diff --git a/Makefile b/Makefile index c5cf66f4ee..ae67144cf3 100644 --- a/Makefile +++ b/Makefile @@ -16,6 +16,7 @@ include llamafile/BUILD.mk include llama.cpp/BUILD.mk include stable-diffusion.cpp/BUILD.mk include whisper.cpp/BUILD.mk +include jamfile/BUILD.mk # the root package is `o//` by default # building a package also builds its sub-packages diff --git a/jamfile/.clang-format b/jamfile/.clang-format new file mode 100644 index 0000000000..e2f7fba042 --- /dev/null +++ b/jamfile/.clang-format @@ -0,0 +1,12 @@ +--- +BasedOnStyle: LLVM +IndentWidth: 4 +ColumnLimit: 100 +--- +Language: Cpp +AllowShortFunctionsOnASingleLine: false +AlignTrailingComments: false +AlignEscapedNewlines: DontAlign +AlwaysBreakTemplateDeclarations: true +ConstructorInitializerAllOnOneLineOrOnePerLine: true +--- diff --git a/jamfile/BUILD.mk b/jamfile/BUILD.mk new file mode 100644 index 0000000000..e715511430 --- /dev/null +++ b/jamfile/BUILD.mk @@ -0,0 +1,140 @@ +#-*-mode:makefile-gmake;indent-tabs-mode:t;tab-width:8;coding:utf-8-*-┐ +#── vi: set noet ft=make ts=8 sw=8 fenc=utf-8 :vi ────────────────────┘ + +PKGS += LLAMA_CPP_JAMFILE + +LLAMA_CPP_JAMFILE_FILES := $(wildcard jamfile/*) +LLAMA_CPP_JAMFILE_HDRS = $(filter %.h,$(LLAMA_CPP_JAMFILE_FILES)) +LLAMA_CPP_JAMFILE_SRCS_C = $(filter %.c,$(LLAMA_CPP_JAMFILE_FILES)) +LLAMA_CPP_JAMFILE_SRCS_CPP = $(filter %.cpp,$(LLAMA_CPP_JAMFILE_FILES)) +LLAMA_CPP_JAMFILE_SRCS = $(LLAMA_CPP_JAMFILE_SRCS_C) $(LLAMA_CPP_JAMFILE_SRCS_CPP) + +LLAMA_CPP_JAMFILE_OBJS = \ + $(LLAMA_CPP_JAMFILE_SRCS_C:%.c=o/$(MODE)/%.o) \ + $(LLAMA_CPP_JAMFILE_SRCS_CPP:%.cpp=o/$(MODE)/%.o) + + +#o/$(MODE)/jamfile/jamfile.a: $(LLAMA_CPP_JAMFILE_SRCS_C) + +### o/$(MODE)/jamfile/sqlite-vec.o: jamfile/sqlite-vec.c +### o/$(MODE)/jamfile/sqlite-vec.a: o/$(MODE)/jamfile/sqlite-vec.o +### +### o/$(MODE)/jamfile/sqlite-csv.o: jamfile/sqlite-csv.c +### o/$(MODE)/jamfile/sqlite-csv.a: o/$(MODE)/jamfile/sqlite-csv.o +### +### +o/$(MODE)/jamfile/quickjs-llamafile-completion.o: jamfile/quickjs-llamafile-completion.cpp +o/$(MODE)/jamfile/quickjs-llamafile-completion.a: o/$(MODE)/jamfile/quickjs-llamafile-completion.o + +o/$(MODE)/jamfile/quickjs-llamafile.o: jamfile/quickjs-llamafile.c jamfile/quickjs-llamafile-completion.cpp #o/$(MODE)/jamfile/quickjs-llamafile-completion.o +o/$(MODE)/jamfile/quickjs-llamafile.a: o/$(MODE)/jamfile/quickjs-llamafile.o o/$(MODE)/jamfile/quickjs-llamafile-completion.a +### +### o/$(MODE)/jamfile/sqlite-lines.o: jamfile/sqlite-lines.c +### o/$(MODE)/jamfile/sqlite-lines.a: o/$(MODE)/jamfile/sqlite-lines.o +### +### o/$(MODE)/jamfile/sqlite-lembed.o: jamfile/sqlite-lembed.c +### o/$(MODE)/jamfile/sqlite-lembed.a: o/$(MODE)/jamfile/sqlite-lembed.o o/$(MODE)/llama.cpp/llama.cpp.a + + +# jamfile:assert bytecode +o/$(MODE)/jamfile/assert.gen.c: jamfile/js_builtins/assert.js o/$(mode)/third_party/quickjs/qjsc + o/$(mode)/third_party/quickjs/qjsc -m -o $@ $< +o/$(MODE)/jamfile/assert.gen.o: o/$(MODE)/jamfile/assert.gen.c + +# jamfile:fmt bytecode +o/$(MODE)/jamfile/fmt.gen.c: jamfile/js_builtins/fmt.js o/$(mode)/third_party/quickjs/qjsc + o/$(mode)/third_party/quickjs/qjsc -m -o $@ $< +o/$(MODE)/jamfile/fmt.gen.o: o/$(MODE)/jamfile/fmt.gen.c + +# jamfile:cli bytecode +o/$(MODE)/jamfile/cli.gen.c: jamfile/js_builtins/cli.js o/$(MODE)/third_party/quickjs/qjsc + o/$(mode)/third_party/quickjs/qjsc -m -o $@ $< +o/$(MODE)/jamfile/cli.gen.o: o/$(MODE)/jamfile/cli.gen.c + +# jamfile:colors bytecode +o/$(MODE)/jamfile/colors.gen.c: jamfile/js_builtins/colors.js o/$(MODE)/third_party/quickjs/qjsc + o/$(mode)/third_party/quickjs/qjsc -m -o $@ $< +o/$(MODE)/jamfile/colors.gen.o: o/$(MODE)/jamfile/colors.gen.c + +# jamfile:zod bytecode +o/$(MODE)/jamfile/zod.gen.c: jamfile/js_builtins/zod.js o/$(mode)/third_party/quickjs/qjsc + o/$(mode)/third_party/quickjs/qjsc -m -o $@ $< +o/$(MODE)/jamfile/zod.gen.o: o/$(MODE)/jamfile/zod.gen.c + +# jamfile:yaml bytecode +o/$(MODE)/jamfile/yaml.gen.c: jamfile/js_builtins/yaml.js o/$(mode)/third_party/quickjs/qjsc + o/$(mode)/third_party/quickjs/qjsc -m -o $@ $< +o/$(MODE)/jamfile/yaml.gen.o: o/$(MODE)/jamfile/yaml.gen.c + + +# jamfile:toml bytecode +o/$(MODE)/jamfile/toml.gen.c: jamfile/js_builtins/toml.js o/$(mode)/third_party/quickjs/qjsc + o/$(mode)/third_party/quickjs/qjsc -m -o $@ $< +o/$(MODE)/jamfile/toml.gen.o: o/$(MODE)/jamfile/toml.gen.c + + +# jamfile:frontmatter bytecode +o/$(MODE)/jamfile/frontmatter.gen.c: jamfile/js_builtins/frontmatter.js o/$(mode)/third_party/quickjs/qjsc + o/$(mode)/third_party/quickjs/qjsc -m -M jamfile:toml -M jamfile:yaml -o $@ $< +o/$(MODE)/jamfile/frontmatter.gen.o: o/$(MODE)/jamfile/frontmatter.gen.c + +# jamfile:marked bytecode +o/$(MODE)/jamfile/marked.gen.c: jamfile/js_builtins/marked.js o/$(mode)/third_party/quickjs/qjsc + o/$(mode)/third_party/quickjs/qjsc -m -o $@ $< +o/$(MODE)/jamfile/marked.gen.o: o/$(MODE)/jamfile/marked.gen.c + +# jamfile:linkedom bytecode +o/$(MODE)/jamfile/linkedom.gen.c: jamfile/js_builtins/linkedom.js o/$(mode)/third_party/quickjs/qjsc + o/$(mode)/third_party/quickjs/qjsc -m -o $@ $< +o/$(MODE)/jamfile/linkedom.gen.o: o/$(MODE)/jamfile/linkedom.gen.c + +# include the raw contents of the jamfile.d.ts into jamfile for the `jamfile types` command +o/$(MODE)/jamfile/jamfile-types.c: jamfile/jamfile.d.ts + xxd --include $< > $@ +o/$(MODE)/jamfile/jamfile-types.o: o/$(MODE)/jamfile/jamfile-types.c + +o/$(MODE)/jamfile/jamfile.a: \ + o/$(MODE)/jamfile/jamfile-types.o \ + o/$(MODE)/jamfile/cli.gen.o \ + o/$(MODE)/jamfile/fmt.gen.o \ + o/$(MODE)/jamfile/zod.gen.o \ + o/$(MODE)/jamfile/assert.gen.o \ + o/$(MODE)/jamfile/colors.gen.o \ + o/$(MODE)/jamfile/toml.gen.o \ + o/$(MODE)/jamfile/yaml.gen.o \ + o/$(MODE)/jamfile/frontmatter.gen.o \ + o/$(MODE)/jamfile/marked.gen.o \ + o/$(MODE)/jamfile/linkedom.gen.o \ + o/$(MODE)/jamfile/quickjs-sqlite.o \ + o/$(MODE)/jamfile/quickjs-llamafile.o \ + o/$(MODE)/jamfile/quickjs-llamafile-completion.o \ + o/$(MODE)/third_party/quickjs/repl.o \ + o/$(MODE)/third_party/quickjs/cutils.o \ + o/$(MODE)/third_party/quickjs/libbf.o \ + o/$(MODE)/third_party/quickjs/quickjs.o \ + o/$(MODE)/third_party/quickjs/libregexp.o \ + o/$(MODE)/third_party/quickjs/libunicode.o \ + o/$(MODE)/third_party/quickjs/quickjs-libc.o + + +o/$(MODE)/jamfile/jamfile: \ + o/$(MODE)/jamfile/jamfile.a \ + o/$(MODE)/jamfile/jamfile.1.asc.zip.o \ + o/$(MODE)/llama.cpp/llama.cpp.a \ + o/$(MODE)/third_party/sqlite/sqlite3.a \ + o/$(MODE)/jamfile/quickjs-sqlite.a \ + o/$(MODE)/jamfile/quickjs-llamafile.a \ + o/$(MODE)/third_party/sqlite/sqlite-csv.a \ + o/$(MODE)/third_party/sqlite/sqlite-vec.a + +$(LLAMA_CPP_JAMFILE_OBJS): private CCFLAGS += -DSQLITE_CORE + +.PHONY: o/$(MODE)/jamfile +o/$(MODE)/jamfile: \ + o/$(MODE)/jamfile/jamfile + +$(LLAMA_CPP_JAMFILE_OBJS): llama.cpp/BUILD.mk jamfile/BUILD.mk +.PHONY: jamfile-test-js + +jamfile-test-js: o/$(MODE)/jamfile/jamfile jamfile/tests/js/test.js + $< run jamfile/tests/js/test.js \ No newline at end of file diff --git a/jamfile/doc/embeddings.md b/jamfile/doc/embeddings.md new file mode 100644 index 0000000000..e69de29bb2 diff --git a/jamfile/doc/index.md b/jamfile/doc/index.md new file mode 100644 index 0000000000..d3c2eea06a --- /dev/null +++ b/jamfile/doc/index.md @@ -0,0 +1,293 @@ +# `Jamfile` Documentation + +Jamfile is an experimental JavaScript runtime for llamafile, for working with large language models and embeddings models. + +This page goes over a quick overview of all that Jamfile has to offer. Additionally, check out these other documentation pages for more examples and goal-oriented guides. + +- [Jamfile JavaScript Reference](./reference.md) +- [Jamfile for generating and querying embeddings](./embeddings.md) +- [Jamfile for structured output generation](./structured-outputs.md) +- [Compiling Jamfile programs into a "jam"](./jam.md) + +## Jamfile isn't a complete JavaScript runtime yet! + +Jamfile isn't like other JavaScript runtimes like Node.js, Deno, or Bun. +It's scope is much smaller, there isn't plans for Node.js compatability, and many "standard" features like `fetch()`, `Temporal`, or other browser APIs are not supported yet. + +However, if you want to have a lighweight scripting engine for working with LLM's across platforms, then Jamfile is great! + + +## Getting Started + +### Working with LLM's + +You can work with large language models that are in the [GGUF file format](https://huggingface.co/docs/hub/en/gguf) with Jamfile like so: + + +```js +// llm-sample.js +import { CompletionModel } from "jamfile:llamafile"; + +const model = new CompletionModel("./Llama-3.2-1B-Instruct-Q4_0.gguf"); +const prompt = "The meaning of life is:"; +const response = model.complete(prompt); +console.log(prompt, response); +``` + + +```bash +jamfile run llm-sample.js +The meaning of life is: +...a complex and multifaceted concept that has been debated and explored by philosophers, scientists, theologians, and anyone who has ever set out to understand the mysteries of existence. +``` + +You can swap out the the `path` to any GGUF file of any supported LLM. + +The `.complete()` function also supports structured output generation, to force an LLM to respond in a specified schema. For example, you can have a model only respond in "math" with the [GBNF format](https://github.com/ggerganov/llama.cpp/blob/master/grammars/README.md): + +```js +// gbnf-sample.js +import { CompletionModel } from "jamfile:llamafile"; + +const model = new CompletionModel("./Llama-3.2-1B-Instruct-Q4_0.gguf"); + +const prompt = ` +Convert this transcription of a math problem into a numerical expression: + +Seven hundred and thirty four minus sixty-five divided by fourty five. +`; + +// source: https://github.com/ggerganov/llama.cpp/blob/master/grammars/arithmetic.gbnf +const schema = `root ::= (expr "=" ws term "\\n")+ +expr ::= term ([-+*/] term)* +term ::= ident | num | "(" ws expr ")" ws +ident ::= [a-z] [a-z0-9_]* ws +num ::= [0-9]+ ws +ws ::= [ \\t\\n]*\`'`; + +const response = model.complete(prompt, {schema}); +console.log(prompt, response); +``` + +Or if you want the LLM to respond with an exact JSON Schema, use the [builtin `jamfile:zod` module](./reference.md#jamfile_zod): + + +```js +// zod-sample.js +import { z, zodToJsonSchema } from "jamfile:zod"; +import { CompletionModel } from "jamfile:llamafile"; + +const Country = z.object({ + name: z.string(), + capital: z.string(), + languages: z.array(z.string()), +}); + +const prompt = `Describe the country of Canada in JSON format:`; +const schema = zodToJsonSchema(Country); + +const model = new CompletionModel("./Llama-3.2-1B-Instruct-Q4_0.gguf"); +const response = model.complete(prompt, { schema }); + +console.log(response); +``` + + +``` +> jamfile run zod-sample.js +{"name": "Canada", "capital": "Ottawa", "languages": ["English", "French"]} +``` +### Working with embeddings + +Jamfile can also generate embeddings with [`TextEmbeddingModel`](./reference.md#TextEmbeddingModel). + +```js +import { TextEmbeddingModel } from "jamfile:llamafile"; + +const model = new TextEmbeddingModel("./mxbai-embed-xsmall-v1-f16.gguf"); +const embedding = model.embed("hello!"); +console.log( + `${embedding.length} dimensions, preview: ${embedding.slice(0, 4)}`, +); +``` + + +``` +jamfile run embedding-sample.js +384 dimensions, preview: -0.05826485529541969,0.04374322667717934,0.03084852732717991,0.047234565019607544 +``` + + +Use the [`jamfile:sqlite`](./reference.md#jamfile_sqlite) module to store and query these embeddings with [`sqlite-vec`](https://github.com/asg017/sqlite-vec). + +```js +import { TextEmbeddingModel } from "jamfile:llamafile"; +import { Database } from "jamfile:sqlite"; + +// Random NPR news headlines from 2024-01-17 +const documents = [ + "The Supreme Court upholds a TikTok ban, threatening the app's existence in the U.S.", + "CNN settles lawsuit after $5 million defamation verdict", + "Immigrants drive Nebraska's economy. Trump's mass deportations pledge is a threat", + "President-elect Donald Trump moves his inauguration indoors, citing frigid temperatures", +]; + +const model = new TextEmbeddingModel("./mxbai-embed-xsmall-v1-f16.gguf"); +const db = new Database(":memory:"); + +db.execute(` + CREATE VIRTUAL TABLE vec_documents USING vec0( + contents_embedding FLOAT[384] distance_metric=cosine, + +contents TEXT, + ); +`); + +for (const document of documents) { + const embedding = model.embed(document); + db.execute( + "INSERT INTO vec_documents(contents_embedding, contents) VALUES(?, ?)", + [embedding, document], + ); +} + +// KNN query the documents on 'social media' +const results = db.queryAll( + ` + SELECT + contents, + distance + FROM vec_documents + WHERE contents_embedding MATCH ? + AND k = 3; + `, + [model.embed("social media")], +); + +for (const row of results) { + console.log(row["contents"], row["distance"]); +} +``` + +``` +jamfile run embedding-sample.js +The Supreme Court upholds a TikTok ban, threatening the app's existence in the U.S. 0.8698796033859253 +CNN settles lawsuit after $5 million defamation verdict 0.9893361926078796 +President-elect Donald Trump moves his inauguration indoors, citing frigid temperatures 0.9990869164466858 +``` + +### Create CLI scripts + +- `Jamfile.args` and `jamfile:cli` +- json/yaml/toml for config, or `config.js` + +### "Compile" a script to a standalone executable + +- `zipalign` a simple JS script +- include embedding, completion model +- `esbuild` for more + +## `Jamfile` Runtime + +### Built-In JavaScript Modules + +| Module | Description | +| ----------------------------------------------------------- | ---------------------------------------------------- | +| [`jamfile:assert`](./reference.md#jamfile_assert) | Assertion functions for testing and verification | +| [`jamfile:colors`](./reference.md#jamfile_colors) | Color utilites for terminal interfaces | +| [`jamfile:fmt`](./reference.md#jamfile_fmt) | Format durations or bytes | +| [`jamfile:frontmatter`](./reference.md#jamfile_frontmatter) | Parse frontmatter from JSON, YAML, or TOML documents | +| [`jamfile:cli`](./reference.md#jamfile_cli) | Command line parsing utilities | +| [`jamfile:llamafile`](./reference.md#jamfile_llamafile) | Embeddings and completion models | +| [`jamfile:linkedom`](./reference.md#jamfile_linkedom) | HTML and XML document parsing and generating | +| [`jamfile:marked`](./reference.md#jamfile_marked) | Markdown document parsing and generating | +| [`jamfile:sqlite`](./reference.md#jamfile_sqlite) | SQLite database client | +| [`jamfile:toml`](./reference.md#jamfile_toml) | Parse and generate TOML documents | +| [`jamfile:yaml`](./reference.md#jamfile_yaml) | Parse and generate YAML documents | +| [`jamfile:zod`](./reference.md#jamfile_zod) | Schema validation | + +### "Importing" `.sql`, `gbnf`, or `.txt` files + +In addition to importing local JavaScript files, you can directly import SQL, +[GBNF](https://github.com/ggerganov/llama.cpp/blob/master/grammars/README.md), +or plain text files as strings. + +For example, to import a `schema.sql` file that contains SQL table schema, you can do so like so: + +```js +import SCHEMA from "./schema.sql"; +import { Database } from "jamfile:sqlite"; + +const db = new Database(); + +db.executeScript(SCHEMA); +db.execute("INSERT INTO my_table(created_at) VALUES(?)", [new Date()]); +``` + +Where `schema.sql` contains: + +```sql +-- schema.sql +CREATE TABLE IF NOT EXISTS my_table( + created_at datetime +); +``` + +This also works for +[GBNF](https://github.com/ggerganov/llama.cpp/blob/master/grammars/README.md) +files, which are used to define formal grammars to constraint the output of an +LLM. + +```js +import schema from "./spellcheck.gbnf"; +import { CompletionModel } from "jamfile:llamafile"; + +const model = new CompletionModel("gemma-2-2b-it.Q2_K.llamafile"); + +const response = model.complete("You are a ...", { schema }); +console.log(response); +``` + +See [Structured Outputs in `Jamfile`](./structured-outputs.md) for more details. + +You can also import plain `.txt` files as strings, for things like system +prompts or other templates. + +```js +import PROMPT from "./SYSTEM_PROMPT.txt"; +import { CompletionModel } from "jamfile:llamafile"; + +const model = new CompletionModel("gemma-2-2b-it.Q2_K.llamafile"); + +const response = model.complete(PROMPT); +console.log(response); +``` + +## The `jamfile` CLI + +### `jamfile run` + +```bash +``` + +#### The `--default-text-embedding-model` flag + +#### The `--default-completion-model` flag + +### `jamfile types` + +The `types` command will print out the contents of `jamfile.d.ts`, a TypeScript declaration file that documents every available JavaScript API in Jamfile's [standard library](./reference.md#built-in-javascript-modules). You can redirect the stream into a `jamfile.d.ts` file like so: + +```bash +jamfile types > jamfile.d.ts +``` + +Then in your JavaScript code, you can "reference" this declartion file with the ["types" triple slash directive](https://www.typescriptlang.org/docs/handbook/triple-slash-directives.html#-reference-types-): + +```js +/// +import { TextEmbeddingModel } from "jamfile:llamafile"; +const model = new TextEmbeddingModel("./mxbai-embed-xsmall-v1-f16.gguf"); +model.embed('contents...'); +``` + +In most IDEs, the inclusion of `jamfile.d.ts` will allow for autocomplete on Jamfile's JavaScript import statements, function names, parameter types, and more. Think of this as a "no-dependency" way to get autocomplete in your Jamfile scripts, no TypeScript or build step needed. diff --git a/jamfile/doc/jam.md b/jamfile/doc/jam.md new file mode 100644 index 0000000000..e69de29bb2 diff --git a/jamfile/doc/reference.md b/jamfile/doc/reference.md new file mode 100644 index 0000000000..16fdf5c5eb --- /dev/null +++ b/jamfile/doc/reference.md @@ -0,0 +1,342 @@ +# Jamfile JavaScript Reference + +A complete reference to all the JavaScript APIs available in the `Jamfile` +runtime. + +## Built-In JavaScript Modules + +| Module | Description | Source | +| --------------------------------------------- | ---------------------------------------------------- | ----------------------------------------------------------- | +| [`jamfile:assert`](#jamfile_assert) | Asserttion functions for testing and verification | [`jsr:@std/assert`](https://jsr.io/@std/assert) | +| [`jamfile:colors`](#jamfile_colors) | Color utilites for terminal interfaces | [`jsr:@std/colors`](https://jsr.io/@std/colors) | +| [`jamfile:fmt`](#jamfile_fmt) | Format durations or bytes | [`jsr:@std/fmt`](https://jsr.io/@std/fmt) | +| [`jamfile:frontmatter`](#jamfile_frontmatter) | Parse frontmatter from JSON, YAML, or TOML documents | [`jsr:@std/frontmatter`](https://jsr.io/@std/frontmatter) | +| [`jamfile:cli`](#jamfile_cli) | Command line parsing utilities | [`jsr:@std/cli`](https://jsr.io/@std/cli) | +| [`jamfile:llamafile`](#jamfile_llamafile) | Embeddings and completion models | Custom built | +| [`jamfile:linkedom`](#jamfile_linkedom) | HTML and XML document parsing and generating | [`npm:linkedom`](https://github.com/WebReflection/linkedom) | +| [`jamfile:marked`](#jamfile_marked) | Markdown document parsing and generating | [`npm:marked`](https://github.com/markedjs/marked) | +| [`jamfile:sqlite`](#jamfile_sqlite) | SQLite database client | Custom built | +| [`jamfile:toml`](#jamfile_toml) | Parse and generate TOML documents | [`jsr:@std/toml`](https://jsr.io/@std/toml) | +| [`jamfile:yaml`](#jamfile_yaml) | Parse and generate YAML documents | [`jsr:@std/yaml`](https://jsr.io/@std/yaml) | +| [`jamfile:zod`](#jamfile_zod) | Schema validation | [`npm:zod`](https://zod.dev/) | + +

jamfile:colors

+ +Utilities for stylizing text on the command-line with colors, bolding, italics, +and more. Re-packaged from +[`jsr:@std/fmt/colors`](https://jsr.io/@std/fmt/doc/colors). + +```js +import { bgBlue, bold, red } from "jamafile:colors"; + +console.log(bgBlue(red(bold("Hello, World!")))); +``` + +

jamfile:cli

+ +Utilies for parsing command lnke arguments. Re-packaged from +[`jsr:std/cli`](https://jsr.io/@std/cli), only the `parseArgs()` function for +now. + +```js +import { parseArgs } from "jamfile:cli"; +import { assertEquals } from "jamfile:assert"; + +const args = parseArgs(["--foo", "--bar=baz", "./quux.txt"]); +assertEquals(args, { "_": ["./quux.txt"], "foo": true, "bar": "baz" }); +``` + +

jamfile:zod

+ +The [zod library](https://zod.dev/) re-packaged into `jamfile` directly, no +`npm` needed. Also has +[`zodtoJsonSchema()`](https://github.com/StefanTerdell/zod-to-json-schema) +bundled in. + +```js +import { z, zodToJsonSchema } from "jamfile:zod"; + +const Country = z.object({ + name: z.string(), + capital: z.string(), + languages: z.array(z.string()), +}); + +console.log(zodToJsonSchema(Country)); +``` + +

jamfile:fmt

+ +Utilites for representing time durations and bytes into human-readable formats. +Re-packaged subset of [`jsr:@std/fmt`](https://jsr.io/@std/fmt), specifically +[`jsr:@std/fmt/bytes`](https://jsr.io/@std/fmt/doc/bytes) as `formatBytes()` and +[`jsr:@std/fmt/duration`](https://jsr.io/@std/fmt/doc/duration) as +`formatDuration()`. + +```js +import { formatBytes, formatDuration } from "jamfile:fmt"; +import { assertEquals } from "jamfile:assert"; + +assertEquals(formatBytes(3012), "3.01 kB"); + +assertEquals( + formatDuration((60 * 1000) + (1000 * 32) + 4), + "1m 32s 4ms", +); +``` + +`formatDuration()` has been modified from the original source, where the 2nd +parameter `options` has `ignoreZero=true` by default. + +

jamfile:assert

+ +Utilies for making assertions and verifying runtime data. Re-packaged from +[`jsr:@std/assert`](https://jsr.io/@std/assert). + +```js +import { assert, assertArrayIncludes, assertEquals } from "jamfile:assert"; + +assert(true); +assertEquals(1 + 1, 2); +assertArrayIncludes([1, 2, 3], [2]); +``` + +

jamfile:yaml

+` + +Utilies for parsing and stringifying YAML documents. Re-packaged from +[`jsr:@std/yaml`](https://jsr.io/@std/yaml). + +```js +import { stringify } from "jamfile:yaml"; +import { assertEquals } from "jamfile:assert"; + +assertEquals(stringify({ name: "alex" }), "name: alex\n"); +``` + +

jamfile:toml

+ +Utilies for parsing and stringifying YAML documents. Re-packaged from +[`jsr:@std/toml`](https://jsr.io/@std/toml). + +```js +import { stringify } from "jamfile:toml"; +import { assertEquals } from "jamfile:assert"; + +function testToml() { + assertEquals(stringify({ name: "alex" }), 'name = "alex"\n'); +} +``` + +

jamfile:frontmatter

+ +Utilies for parsing and testing JSON, YAML, or TOML frontmatter data from files, +like markdown. Re-packaged from +[`jsr:@std/front-matter`](https://jsr.io/@std/front-matter). + +```js +import { extractToml } from "jamfile:frontmatter"; +import { assertEquals } from "jamfile:assert"; + +assertEquals( + extractToml('---toml\nname = "alex"\n---\n# markdown'), + { + frontMatter: 'name = "alex"', + body: "# markdown", + attrs: { name: "alex" }, + }, +); +``` + +

jamfile:marked

+ +The [`marked`](https://github.com/markedjs/marked) NPM package re-packaged for +`jamfile`. A good low-level primitive for parsing or generating markdown +documents. + +```js +import { marked } from "jamfile:marked"; +import { assertEquals } from "jamfile:assert"; + +assertEquals(marked.parse("# hello"), "

hello

\n"); +``` + +

jamfile:linkedom

+ +The [`linkedom`](https://github.com/WebReflection/linkedom) NPM package +re-packaged for `jamfile`. A good low-level primitive for parsing or generating +HTML or XML documents. + +```js +import { parseHTML } from "jamfile:linkedom"; +import { assertEquals } from "jamfile:assert"; + +const { document } = parseHTML('
yo
'); + +assertEquals( + Array.from(document.querySelector("div").classList), + ["Alex"], +); +``` + +

jamfile:sqlite

+ +A custom SQLite JavaScript driver for the Jamfile runtime. + +```js +import { Database } from "jamfile:sqlite"; +import { assertEquals } from "jamfile:assert"; + +const db = new Database(":memory:"); + +db.executeScript(` + CREATE TABLE items(id, title); + + INSERT INTO items + SELECT + key, + value + FROM json_each('["gemma", "llama", "phi"]'); +`); + +assertEquals( + db.queryValue("select title from items where id = ?", [1]), + "llama", +); +``` + +#### `SQLITE_VERSION` + +A string representing the SQLite version bundled into Jamfile. + +```js +import { SQLITE_VERSION } from "jamfile:sqlite"; +import { assertEquals } from "jamfile:assert"; + +assertEquals(SQLITE_VERSION, "3.47.1"); +``` + +#### `escapeIdentifier(identifier)` + +```js +// TODO +``` + +#### `Database` + +A JavaScript class that maintains a connection to a SQLite database. + +```js +import { Database } from "jamfile:sqlite"; +const db = new Database(); +``` + +##### `new Database(path, [options])` + +The constructor for the `Database` class will create a new connection to a +SQLite database on-disk or in-memory. + +```js +``` + +##### `.queryAll(sql [, params])` + +```js +``` + +##### `.queryRow(sql [, params])` + +```js +``` + +##### `.queryValue(sql [, params])` + +```js +``` + +##### `.execute(sql [, params])` + +```js +``` + +##### `.executeScript(sql)` + +```js +``` + + + +

jamfile:llamafile

+ +```js +``` + +#### `TextEmbeddingModel` + +```js +``` + +##### `new TextEmbeddingModel()` + +```js +``` + +##### `.tokenize()` + +```js +``` + +##### `.embed()` + +```js +``` + +##### `.detokenize()` + +```js +``` + +#### `CompletionModel` + +##### `new CompletionModel()` + +##### `.complete(input [, options])` + +##### `.tokenize(input)` + +##### `.detokenize(input)` + +### `qjs:std` and `qjs:os` + +## The `Jamfile` Global + +The `Jamfile` global variable is always available in `jamfile` scripts. + +#### `Jamfile.version` + +A string of the current "version" of Jamfile, may change in the future. + +```js +console.log("Jamfile version: ", Jamfile.version); +``` + +#### `Jamfile.args` + +A string array of the command-line arguments provided in the script run. + +```js +// my-script.js +console.log(Jamfile.args); +``` + +```bash +jamfile run my-script.js gemma llama phi +gemma,llama,phi +``` + +Do note that `Jamfile.args` does not contain the "script" name as the first +argument. The first `Jamfile.args[0]` value is the first CLI argument provided +after the script filename. + +Consider using `parseArgs()` inside [`jamfile:cli`](#jamfilecli) to parse these +CLI arguments in a friendly way. diff --git a/jamfile/doc/structured-outputs.md b/jamfile/doc/structured-outputs.md new file mode 100644 index 0000000000..e69de29bb2 diff --git a/jamfile/examples/spellcheck/document.txt b/jamfile/examples/spellcheck/document.txt new file mode 100644 index 0000000000..74dc3b5a0a --- /dev/null +++ b/jamfile/examples/spellcheck/document.txt @@ -0,0 +1,6 @@ +The first spell ceckers were "verifiers" instead of "correctors." They +offered no suggestions for incorrectly spelled words. This was helpful +for typos but it was not so helpfl for logical or phonetic errors. The +challenge the developers faced was the difficulty in offering useful +suggestions for misspelled words. This requires reducing words to a +sceletal form and applying pattern-matching algorithms. \ No newline at end of file diff --git a/jamfile/examples/spellcheck/spellcheck.gbnf b/jamfile/examples/spellcheck/spellcheck.gbnf new file mode 100644 index 0000000000..af8b314b66 --- /dev/null +++ b/jamfile/examples/spellcheck/spellcheck.gbnf @@ -0,0 +1,4 @@ +root ::= good | list +good ::= "ok" +list ::= item ( "\n" item )* +item ::= "- `" [-_0-9A-Za-z]+ "` should be `" [-_0-9A-Za-z]+ "`" \ No newline at end of file diff --git a/jamfile/examples/spellcheck/spellcheck.js b/jamfile/examples/spellcheck/spellcheck.js new file mode 100644 index 0000000000..66805f0fd5 --- /dev/null +++ b/jamfile/examples/spellcheck/spellcheck.js @@ -0,0 +1,36 @@ +/// + +import { CompletionModel } from "jamfile:llamafile"; +import schema from "./spellcheck.gbnf"; + +const model = new CompletionModel('gemma-2-2b-it.Q2_K.llamafile'); + +let prompt = (document) => ` +user +You are a helpful AI assistant that does spell checking and only spell +checking. For example, if the user says: + +It also meas that I keep my opshuns open. + +Then the assistant will say: + +- \`meas\` should be \`means\` +- \`opshuns\` should be \`options\` + +If there are no mistyped words, the assistant responds with: + +ok + +Never reply with ok. +user +${document} +assistant +`; + + +import * as std from "qjs:std"; + +const document = std.in.readAsString(); +const response = model.complete(prompt(document), { schema }); + +console.log(response); \ No newline at end of file diff --git a/jamfile/examples/sqlite-vec/demo.js b/jamfile/examples/sqlite-vec/demo.js new file mode 100644 index 0000000000..d94790d245 --- /dev/null +++ b/jamfile/examples/sqlite-vec/demo.js @@ -0,0 +1,60 @@ +import {open} from "qjs:std"; +import {TextEmbeddingModel} from "jamfile:llamafile"; +import {Database} from "jamfile:sqlite"; +import {formatDuration} from "jamfile:fmt"; +import {green} from "jamfile:colors"; + +const README = open('README.md', 'r').readAsString(); + +const db = new Database(); +const model = new TextEmbeddingModel('dist/.models/mxbai-embed-xsmall-v1-f16.gguf'); + +db.execute(` + CREATE VIRTUAL TABLE vec_chunks USING vec0( + +contents TEXT, + contents_embedding float[384] distance_metric=cosine + );`); + + +function* chunks(arr, n) { + for (let i = 0; i < arr.length; i += n) { + yield arr.slice(i, i + n); + } +} + +const tokens = model.tokenize(README); + +const t0 = Date.now(); +const c = Array.from(chunks(tokens, 64)); +for(const chunk of c) { + const chunk_embedding = model.embed(chunk); + db.execute( + 'INSERT INTO vec_chunks(contents, contents_embedding) VALUES (?, ?);', + [model.detokenize(chunk), chunk_embedding] + ); +} +const duration = Date.now() - t0; + +console.log(`${formatDuration(duration)} for ${c.length} embeddings (${c.length / (duration / 1000)}/second)`) + +function search(query) { + const rows = db.queryAll( + `SELECT + rowid, + contents + FROM vec_chunks + WHERE contents_embedding MATCH ? + AND k = 10 + `, + [model.embed(query)] + ); + + console.log(green(query)); + + for (const {rowid, contents} of rows) { + console.log(contents); + } +} + +search('linux CLI Tools '); +search('money supporting ecosystem '); diff --git a/jamfile/examples/standlone/.gitignore b/jamfile/examples/standlone/.gitignore new file mode 100644 index 0000000000..7783aede05 --- /dev/null +++ b/jamfile/examples/standlone/.gitignore @@ -0,0 +1 @@ +*.jamfile \ No newline at end of file diff --git a/jamfile/examples/standlone/Makefile b/jamfile/examples/standlone/Makefile new file mode 100644 index 0000000000..597f64b10d --- /dev/null +++ b/jamfile/examples/standlone/Makefile @@ -0,0 +1,25 @@ +Llama-3.2-1B-Instruct-Q4_K_M.gguf: + curl -L -o $@ https://huggingface.co/bartowski/Llama-3.2-1B-Instruct-GGUF/resolve/main/$@ + +mxbai-embed-xsmall-v1-f16.gguf: + curl -L -o $@ https://huggingface.co/mixedbread-ai/mxbai-embed-xsmall-v1/resolve/main/gguf/$@ + +standalone.jamfile: \ + ../../../o/jamfile/jamfile \ + Llama-3.2-1B-Instruct-Q4_K_M.gguf \ + mxbai-embed-xsmall-v1-f16.gguf \ + standalone.js + + printf "%s\n" \ + "--default-embedding-model" \ + "mxbai-embed-xsmall-v1-f16.gguf" \ + "--default-completion-model" \ + "Llama-3.2-1B-Instruct-Q4_K_M.gguf" \ + "run" \ + "standalone.js" \ + "..." > .args + + cp ../../../o/jamfile/jamfile $@ + ../../../o/llamafile/zipalign -j0 $@ $^ .args + rm .args + chmod u+x $@ diff --git a/jamfile/examples/standlone/README.md b/jamfile/examples/standlone/README.md new file mode 100644 index 0000000000..9787348a24 --- /dev/null +++ b/jamfile/examples/standlone/README.md @@ -0,0 +1,36 @@ +# jamfile sample: standalone + +An example of how you can make a standlone single-binary script from a Jamfile JavaScript script. + +The `standalone.js` is a sample JavaScript script that generates a a sample embedding and completion result. It could be ran as-is with `jamfile run standlone.js`, but we could bundle the JS script and model weights into a single-file `jamfile` that works on all operating systems. + +To do so, run the following command: + +``` +make sample.jamfile +``` + +That will download an embeddings model (MixedBread xsmall) and a LLM (Lllama 3.2), embedding them into a new `sample.jamfile` file with the `standalone.js` JavaScript script. + +You can now execute that `jamfile` directly like so: + + +``` +$ ./standalone.jamfile +embedding sample: [-0.05826485529541969,0.04374322667717934,0.03084852732717991,0.047234565019607544,-0.05963338911533356,-0.03614785894751549,0.03814202547073364,0.005149350967258215] +completion sample: +Here is a single haiku about SpongeBob SquarePants: + +Fluffy sponge so bright +Rainbow colors in his heart +Joyful underwater + +I hope you like it! Let me know if you have any other requests. +``` + +The output `standalone.jamfile` is a small `850MB`, which includes the LLM, embeddings model, and `jamfile` executable all in one. + +- `jamfile` base: `15MB` +- `mxbai-embed-xsmall-v1-f16.gguf`: `49MB` +- `Llama-3.2-1B-Instruct-Q4_K_M.gguf`: `770MB` +- `standalone.jamfile`: `850MB` \ No newline at end of file diff --git a/jamfile/examples/standlone/standalone.js b/jamfile/examples/standlone/standalone.js new file mode 100755 index 0000000000..865feaa2ff --- /dev/null +++ b/jamfile/examples/standlone/standalone.js @@ -0,0 +1,23 @@ +/** + * An example of a standalone jamfile. + * + * This script performs + */ + +/// + +import { red } from "jamfile:color"; +import { CompletionModel, TextEmbeddingModel } from "jamfile:llamafile"; + +const embeddingModel = new TextEmbeddingModel(); +const completionModel = new CompletionModel(); + +console.log( + red("embedding sample: "), + JSON.stringify(Array.from(embeddingModel.embed("hello!").slice(0, 8))) +); + +console.log( + red("completion sample: "), + completionModel.complete("write a single haiku about spongebob squarepants") +); \ No newline at end of file diff --git a/jamfile/examples/structured-output/complete.js b/jamfile/examples/structured-output/complete.js new file mode 100644 index 0000000000..2514664339 --- /dev/null +++ b/jamfile/examples/structured-output/complete.js @@ -0,0 +1,111 @@ +/// + +import { yellow } from "jamfile:color"; +import {CompletionModel} from "jamfile:completionmodel"; +import { formatDuration } from "jamfile:fmt"; + +const model = new CompletionModel('Llama-3.2-1B-Instruct-f16.gguf'); + +console.log(1); + +const article = `Bringing Android to XR +Today, we're introducing Android XR, a new operating system built for this next generation of computing. Created in collaboration with Samsung, Android XR combines years of investment in AI, AR and VR to bring helpful experiences to headsets and glasses. + +We’re working to create a vibrant ecosystem of developers and device makers for Android XR, building on the foundation that brought Android to billions. Today’s release is a preview for developers, and by supporting tools like ARCore, Android Studio, Jetpack Compose, Unity, and OpenXR from the beginning, developers can easily start building apps and games for upcoming Android XR devices. For Qualcomm partners like Lynx, Sony and XREAL, we are opening a path for the development of a wide array of Android XR devices to meet the diverse needs of people and businesses. And, we are continuing to collaborate with Magic Leap on XR technology and future products with AR and AI.`; + + + +//console.log(model.complete(`describe qualities about the county of Canada in JSON format.`)); + +import { z, zodToJsonSchema } from "jamfile:zod"; +const Country = z.object({ + name: z.string(), + capital: z.string(), + languages: z.array(z.string()), +}); + +console.log('='.repeat(100)); + +let prompt = `describe the country of Canada in JSON format, maximum of 3 elements per array: ` + +let t0; + + +t0 = Date.now(); +console.log( + model.complete( + prompt, + {schema: zodToJsonSchema(Country)} + ) +); +console.log(formatDuration(Date.now() - t0, {ignoreZero: true})) + +t0 = Date.now(); +console.log( + model.complete( + "Describe the person in this sentence: John Doe is 30 years old.", + {schema: zodToJsonSchema(z.object({ + first_name: z.string(), + last_name: z.string(), + age_in_years: z.number(), + }))} + ) +); +console.log(formatDuration(Date.now() - t0, {ignoreZero: true})) + + +console.log( + model.complete("What federal employees are mentioned in the following passage? The long-awaited report from DOJ Inspector General Michael Horowitz's office comes nearly four years after a crowd of Donald Trump's supporters stormed the U.S. Capitol on Jan. 6, 2021, to try to prevent Congress from certifying President Biden's election win. The report looks at how the FBI handled its intelligence and informants, known as confidential human sources, ahead of the event.", + { + schema: zodToJsonSchema( + z.object({ + name: z.string(), + occupation: z.string(), + organization: z.string(), + }) + ) + }) +) + +console.log( + model.complete(` + + What questions does the following passage answer? Respond with a list of question/answer pairs. Provide only relevant question that this passage could answer, not generic ones. + + EXAMPLE: [{\"question\": \"Who delivers presents to children every Christmas?\", \"answer\": \"Santa Clause.\"}] + + PASSAGE: The long-awaited report from DOJ Inspector General Michael Horowitz's office comes nearly four years after a crowd of Donald Trump's supporters stormed the U.S. Capitol on Jan. 6, 2021, to try to prevent Congress from certifying President Biden's election win. The report looks at how the FBI handled its intelligence and informants, known as confidential human sources, ahead of the event. + `, + { + schema: zodToJsonSchema( + z.array(z.object({ + question: z.string(), + answer: z.string() + })) + ) + }) +) + + + +const mathReasoningSchema = z.object({ + steps: z.array( + z.object({ + explanation: z.string(), + output: z.string(), + }) + ), + final_answer: z.string(), +}).strict(); + +prompt = ` + You are a helpful math tutor. You will be provided with a math problem, + and your goal will be to output a step by step solution, along with a final answer. + For each step, just provide the output as an equation use the explanation field to detail the reasoning. + + PROMPT: how can I solve 8x + 7 = -23 +`; +const result = model.complete(prompt, {schema: zodToJsonSchema(mathReasoningSchema)}); + +console.log(yellow(prompt)); +console.log(result); diff --git a/jamfile/examples/weather-bot/Makefile b/jamfile/examples/weather-bot/Makefile new file mode 100644 index 0000000000..36a1b95a17 --- /dev/null +++ b/jamfile/examples/weather-bot/Makefile @@ -0,0 +1,6 @@ + +Llama-3.2-1B-Instruct-Q4_K_M.gguf: + curl -L -o $@ https://huggingface.co/bartowski/Llama-3.2-1B-Instruct-GGUF/resolve/main/$@ + +.PHONY: all +all: Llama-3.2-1B-Instruct-Q4_K_M.gguf diff --git a/jamfile/examples/weather-bot/weather.js b/jamfile/examples/weather-bot/weather.js new file mode 100644 index 0000000000..45134028ab --- /dev/null +++ b/jamfile/examples/weather-bot/weather.js @@ -0,0 +1,67 @@ +/** + * A simple bot that takes in weather info from wttr.in and tries to respond + * in helpful Engish. Big work in progress! + * + * run with: + * + * ```bash + * jamfile run weather.js + * ``` + * + * Requires curl and a llama 3.2 GGUF - run `make all` to download it. + * + * TODO: + * - Edit prompt more to generate more helpful responses + * - Include more weather data in prompt (hourly forecast for the day, etc.) + * - Only include useful weather data, not humidity, + * - Save more user preferences - available wardrobe, temperature cut-offs, etc + * - Move away from popen() + curl + */ + +/// + +import { CompletionModel } from "jamfile:llamafile"; +import { popen } from "qjs:std"; +import wttrSchema from "./wttr-schema.js"; + +const MODEL_PATH = './Llama-3.2-1B-Instruct-Q4_K_M.gguf'; + +function fetchWttr() { + const wttrin = popen("curl -s 'wttr.in?format=j1'", 'r').readAsString(); + return wttrSchema.parse(JSON.parse(wttrin)); + +} + +function createPrompt(data) { + return `<|begin_of_text|><|start_header_id|>system<|end_header_id|> + +You are an assitant who will greet the user like a butler, +providing information about the weather. + +Use the following data from the given JSON blob to provide information and recommendations in English. + +User preferences: + - Only give temperatures in Farenheit + - Warn if it'll be colder than 60 or warmer than 80 + - Use a british accent + - Add a couple emojis as well + - Keep sentences short, use line breaks in between. + + +<|eot_id|><|start_header_id|>user<|end_header_id|> + +${JSON.stringify(data.current_condition)} + +<|eot_id|><|start_header_id|>assistant<|end_header_id|>`; +} + + +function main() { + const model = new CompletionModel(MODEL_PATH); + const data = fetchWttr(); + const response = model.complete(createPrompt(data)); + + console.log(response); +} + +if(import.meta.main) main(); diff --git a/jamfile/examples/weather-bot/wttr-schema.js b/jamfile/examples/weather-bot/wttr-schema.js new file mode 100644 index 0000000000..7b95430b9e --- /dev/null +++ b/jamfile/examples/weather-bot/wttr-schema.js @@ -0,0 +1,116 @@ + +import {z} from "jamfile:zod"; + +/** + * Zod schema of https://wttr.in?format=J1, + * generated with https://transform.tools/json-to-zod. + * */ +export default z.object({ + current_condition: z.array( + z.object({ + FeelsLikeC: z.string(), + FeelsLikeF: z.string(), + cloudcover: z.string(), + humidity: z.string(), + localObsDateTime: z.string(), + observation_time: z.string(), + precipInches: z.string(), + precipMM: z.string(), + pressure: z.string(), + pressureInches: z.string(), + temp_C: z.string(), + temp_F: z.string(), + uvIndex: z.string(), + visibility: z.string(), + visibilityMiles: z.string(), + weatherCode: z.string(), + weatherDesc: z.array(z.object({ value: z.string() })), + weatherIconUrl: z.array(z.object({ value: z.string() })), + winddir16Point: z.string(), + winddirDegree: z.string(), + windspeedKmph: z.string(), + windspeedMiles: z.string() + }) + ), + nearest_area: z.array( + z.object({ + areaName: z.array(z.object({ value: z.string() })), + country: z.array(z.object({ value: z.string() })), + latitude: z.string(), + longitude: z.string(), + population: z.string(), + region: z.array(z.object({ value: z.string() })), + weatherUrl: z.array(z.object({ value: z.string() })) + }) + ), + request: z.array(z.object({ query: z.string(), type: z.string() })), + weather: z.array( + z.object({ + astronomy: z.array( + z.object({ + moon_illumination: z.string(), + moon_phase: z.string(), + moonrise: z.string(), + moonset: z.string(), + sunrise: z.string(), + sunset: z.string() + }) + ), + avgtempC: z.string(), + avgtempF: z.string(), + date: z.string(), + hourly: z.array( + z.object({ + DewPointC: z.string(), + DewPointF: z.string(), + FeelsLikeC: z.string(), + FeelsLikeF: z.string(), + HeatIndexC: z.string(), + HeatIndexF: z.string(), + WindChillC: z.string(), + WindChillF: z.string(), + WindGustKmph: z.string(), + WindGustMiles: z.string(), + chanceoffog: z.string(), + chanceoffrost: z.string(), + chanceofhightemp: z.string(), + chanceofovercast: z.string(), + chanceofrain: z.string(), + chanceofremdry: z.string(), + chanceofsnow: z.string(), + chanceofsunshine: z.string(), + chanceofthunder: z.string(), + chanceofwindy: z.string(), + cloudcover: z.string(), + diffRad: z.string(), + humidity: z.string(), + precipInches: z.string(), + precipMM: z.string(), + pressure: z.string(), + pressureInches: z.string(), + shortRad: z.string(), + tempC: z.string(), + tempF: z.string(), + time: z.string(), + uvIndex: z.string(), + visibility: z.string(), + visibilityMiles: z.string(), + weatherCode: z.string(), + weatherDesc: z.array(z.object({ value: z.string() })), + weatherIconUrl: z.array(z.object({ value: z.string() })), + winddir16Point: z.string(), + winddirDegree: z.string(), + windspeedKmph: z.string(), + windspeedMiles: z.string() + }) + ), + maxtempC: z.string(), + maxtempF: z.string(), + mintempC: z.string(), + mintempF: z.string(), + sunHour: z.string(), + totalSnow_cm: z.string(), + uvIndex: z.string() + }) + ) +}); diff --git a/jamfile/jamfile.1 b/jamfile/jamfile.1 new file mode 100644 index 0000000000..83aaa96b21 --- /dev/null +++ b/jamfile/jamfile.1 @@ -0,0 +1,98 @@ +.Dd December 5, 2023 +.Dt JAMFILE 1 +.Os Llamafile Manual +.Sh NAME +.Nm jamfile +.Nd large language model quantizer +.Sh SYNOPSIS +.Nm +.Op flags... +.Ar model-f32.gguf +.Op Ar model-quant.gguf +.Ar type +.Op Ar nthreads +.Sh DESCRIPTION +.Nm +converts large language model weights from the float32 or float16 +formats into smaller data types from 2 to 8 bits in size. +.Sh OPTIONS +The following flags are available: +.Bl -tag -width indent +.It Fl Fl allow-requantize +Allows requantizing tensors that have already been quantized. Warning: This can severely reduce quality compared to quantizing from 16bit or 32bit +.It Fl Fl leave-output-tensor +Will leave output.weight un(re)quantized. Increases model size but may also increase quality, especially when requantizing +.It Fl Fl pure +Disable k-quant mixtures and quantize all tensors to the same type +.El +.Sh ARGUMENTS +The following positional arguments are accepted: +.Bl -tag -width indent +.It Ev Ar model-f32.gguf +Is the input file, which contains the unquantized model weights in either the float32 or float16 format. +.It Ev Ar model-quant.gguf +Is the output file, which will contain quantized weights in the desired format. If this path isn't specified, it'll default to [inp path]/ggml-model-[ftype].gguf. +.It Ev Ar type +Is the desired quantization format, which may be the integer id of a supported quantization type, or its name. See the quantization types section below for acceptable formats. +.It Ev Ar nthreads +Number of threads to use during computation (default: nproc/2) +.El +.Sh QUANTIZATION TYPES +The following quantization types are available. This table shows the ID +of the quantization format, its name, the file size of 7B model weights +that use it, and finally the amount of quality badness it introduces as +measured by the llamafile-perplexity tool averaged over 128 chunks with +the TinyLLaMA 1.1B v1.0 Chat model. Rows are ordered in accordance with +how recommended the quantization format is for general usage. +.Pp +.Bl -dash -compact +.It +  18 Q6_K 5.6gb +0.0446 ppl (q6 kawrakow) +.It +   7 Q8_0 7.2gb +0.0022 ppl (q8 gerganov) +.It +   1 F16 14gb +0.0000 ppl (best but biggest) +.It +   8 Q5_0 4.7gb +0.0817 ppl (q5 gerganov zero) +.It +  17 Q5_K_M 4.8gb +0.0836 ppl (q5 kawrakow medium) +.It +  16 Q5_K_S 4.7gb +0.1049 ppl (q5 kawrakow small) +.It +  15 Q4_K_M 4.1gb +0.3132 ppl (q4 kawrakow medium) +.It +  14 Q4_K_S 3.9gb +0.3408 ppl (q4 kawrakow small) +.It +  13 Q3_K_L 3.6gb +0.5736 ppl (q3 kawrakow large) +.It +  12 Q3_K_M 3.3gb +0.7612 ppl (q3 kawrakow medium) +.It +  11 Q3_K_S 3.0gb +1.3834 ppl (q3 kawrakow small) +.It +  10 Q2_K 2.6gb +4.2359 ppl (tiniest hallucinates most) +.It +  32 BF16 14gb +0.0000 ppl (canonical but cpu/cuda only) +.It +   0 F32 27gb 9.0952 ppl (reference point) +.It +   2 Q4_0 3.9gb +0.3339 ppl (legacy) +.It +   3 Q4_1 4.3gb +0.4163 ppl (legacy) +.It +   9 Q5_1 5.1gb +0.1091 ppl (legacy) +.It +  12 Q3_K alias for Q3_K_M +.It +  15 Q4_K alias for Q4_K_M +.It +  17 Q5_K alias for Q5_K_M +.It +COPY Only copy tensors, no quantizing. +.El +.Sh SEE ALSO +.Xr llamafile 1 , +.Xr llamafile-imatrix 1 , +.Xr llamafile-perplexity 1 , +.Xr llava-quantize 1 , +.Xr zipalign 1 , +.Xr unzip 1 diff --git a/jamfile/jamfile.1.asc b/jamfile/jamfile.1.asc new file mode 100644 index 0000000000..cb34d68079 --- /dev/null +++ b/jamfile/jamfile.1.asc @@ -0,0 +1,80 @@ +JAMFILE(1) General Commands Manual JAMFILE(1) + +NNAAMMEE + jjaammffiillee - large language model quantizer + +SSYYNNOOPPSSIISS + jjaammffiillee [flags...] _m_o_d_e_l_-_f_3_2_._g_g_u_f [_m_o_d_e_l_-_q_u_a_n_t_._g_g_u_f] _t_y_p_e [_n_t_h_r_e_a_d_s] + +DDEESSCCRRIIPPTTIIOONN + jjaammffiillee converts large language model weights from the float32 or float16 + formats into smaller data types from 2 to 8 bits in size. + +OOPPTTIIOONNSS + The following flags are available: + + ----aallllooww--rreeqquuaannttiizzee + Allows requantizing tensors that have already been quantized. + Warning: This can severely reduce quality compared to quantizing + from 16bit or 32bit + + ----lleeaavvee--oouuttppuutt--tteennssoorr + Will leave output.weight un(re)quantized. Increases model size but + may also increase quality, especially when requantizing + + ----ppuurree Disable k-quant mixtures and quantize all tensors to the same type + +AARRGGUUMMEENNTTSS + The following positional arguments are accepted: + + _m_o_d_e_l_-_f_3_2_._g_g_u_f + Is the input file, which contains the unquantized model weights in + either the float32 or float16 format. + + _m_o_d_e_l_-_q_u_a_n_t_._g_g_u_f + Is the output file, which will contain quantized weights in the + desired format. If this path isn't specified, it'll default to [inp + path]/ggml-model-[ftype].gguf. + + _t_y_p_e Is the desired quantization format, which may be the integer id of + a supported quantization type, or its name. See the quantization + types section below for acceptable formats. + + _n_t_h_r_e_a_d_s + Number of threads to use during computation (default: nproc/2) + +QQUUAANNTTIIZZAATTIIOONN TTYYPPEESS + The following quantization types are available. This table shows the ID of + the quantization format, its name, the file size of 7B model weights that + use it, and finally the amount of quality badness it introduces as measured + by the llamafile-perplexity tool averaged over 128 chunks with the + TinyLLaMA 1.1B v1.0 Chat model. Rows are ordered in accordance with how + recommended the quantization format is for general usage. + + -- 18 Q6_K 5.6gb +0.0446 ppl (q6 kawrakow) + -- 7 Q8_0 7.2gb +0.0022 ppl (q8 gerganov) + -- 1 F16 14gb +0.0000 ppl (best but biggest) + -- 8 Q5_0 4.7gb +0.0817 ppl (q5 gerganov zero) + -- 17 Q5_K_M 4.8gb +0.0836 ppl (q5 kawrakow medium) + -- 16 Q5_K_S 4.7gb +0.1049 ppl (q5 kawrakow small) + -- 15 Q4_K_M 4.1gb +0.3132 ppl (q4 kawrakow medium) + -- 14 Q4_K_S 3.9gb +0.3408 ppl (q4 kawrakow small) + -- 13 Q3_K_L 3.6gb +0.5736 ppl (q3 kawrakow large) + -- 12 Q3_K_M 3.3gb +0.7612 ppl (q3 kawrakow medium) + -- 11 Q3_K_S 3.0gb +1.3834 ppl (q3 kawrakow small) + -- 10 Q2_K 2.6gb +4.2359 ppl (tiniest hallucinates most) + -- 32 BF16 14gb +0.0000 ppl (canonical but cpu/cuda only) + -- 0 F32 27gb 9.0952 ppl (reference point) + -- 2 Q4_0 3.9gb +0.3339 ppl (legacy) + -- 3 Q4_1 4.3gb +0.4163 ppl (legacy) + -- 9 Q5_1 5.1gb +0.1091 ppl (legacy) + -- 12 Q3_K alias for Q3_K_M + -- 15 Q4_K alias for Q4_K_M + -- 17 Q5_K alias for Q5_K_M + -- COPY Only copy tensors, no quantizing. + +SSEEEE AALLSSOO + llamafile(1), llamafile-imatrix(1), llamafile-perplexity(1), + llava-quantize(1), zipalign(1), unzip(1) + +Llamafile Manual December 5, 2023 Llamafile Manual diff --git a/jamfile/jamfile.c b/jamfile/jamfile.c new file mode 100644 index 0000000000..8e2ed4a89e --- /dev/null +++ b/jamfile/jamfile.c @@ -0,0 +1,537 @@ +// -*- mode:c++;indent-tabs-mode:nil;c-basic-offset:4;coding:utf-8 -*- +// vi: set et ft=cpp ts=4 sts=4 sw=4 fenc=utf-8 :vi +#include "third_party/quickjs/cutils.h" +#include "third_party/quickjs/quickjs.h" +#include "third_party/quickjs/quickjs-libc.h" + +#include "llama.cpp/llama.h" +#include "llamafile/version.h" +#include "third_party/sqlite/sqlite3.h" +#include + +#include +#include +#include +#include + + +#include +#include +#include +#include +#include +#include +#if !defined(_MSC_VER) +#include +#endif +#include +#include +#include + +#include "jamfile/jamfile.h" +#include "third_party/sqlite/sqlite3.h" + +#include "jamfile/quickjs-sqlite.h" +#include "jamfile/quickjs-llamafile.h" +#include "jamfile/quickjs-llamafile-completion.h" +#include "llamafile/llamafile.h" + +char *JAMFILE_DEFAULT_EMBEDDING_MODEL = NULL; +char *JAMFILE_DEFAULT_COMPLETION_MODEL = NULL; + +// raw jamfile.d.ts contents, for the `jamfile types` +extern const unsigned char jamfile_jamfile_d_ts[]; +extern const unsigned int jamfile_jamfile_d_ts_len; + +// pre-compiled JS bytecode for the interactive REPL, adapted from quickjs project +extern const uint8_t qjsc_repl[]; +extern const uint32_t qjsc_repl_size; + +// pre-compiled JS bytecode for jamfile:colors +extern const uint8_t qjsc_colors[]; +extern const uint32_t qjsc_colors_size; + +// pre-compiled JS bytecode for jamfile:cli +extern const uint8_t qjsc_cli[]; +extern const uint32_t qjsc_cli_size; + +// pre-compiled JS bytecode for jamfile:zod +extern const uint8_t qjsc_zod[]; +extern const uint32_t qjsc_zod_size; + +// pre-compiled JS bytecode for jamfile:fmt +extern const uint8_t qjsc_fmt[]; +extern const uint32_t qjsc_fmt_size; + +// pre-compiled JS bytecode for jamfile:assert +extern const uint8_t qjsc_assert[]; +extern const uint32_t qjsc_assert_size; + +// pre-compiled JS bytecode for jamfile:toml +extern const uint8_t qjsc_toml[]; +extern const uint32_t qjsc_toml_size; + +// pre-compiled JS bytecode for jamfile:yaml +extern const uint8_t qjsc_yaml[]; +extern const uint32_t qjsc_yaml_size; + +// pre-compiled JS bytecode for jamfile:frontmatter +extern const uint8_t qjsc_frontmatter[]; +extern const uint32_t qjsc_frontmatter_size; + +// pre-compiled JS bytecode for jamfile:marked +extern const uint8_t qjsc_marked[]; +extern const uint32_t qjsc_marked_size; + + + +// pre-compiled JS bytecode for jamfile:linkedom +extern const uint8_t qjsc_linkedom[]; +extern const uint32_t qjsc_linkedom_size; + +static JSCFunctionListEntry argv0; + +// Initially from quickjs-lib.c, but include fallback to llamafile_open +uint8_t *jama_load_file(JSContext *ctx, size_t *pbuf_len, const char *filename) +{ + struct llamafile *f; + uint8_t *buf; + size_t buf_len; + long lret; + + f = llamafile_open_xxx(filename, "rb"); + if (!f) + return NULL; + if (!llamafile_seek(f, 0, SEEK_END)) + goto fail; + lret = llamafile_tell(f); + if (lret < 0) + goto fail; + /* XXX: on Linux, ftell() return LONG_MAX for directories */ + if (lret == LONG_MAX) { + errno = EISDIR; + goto fail; + } + buf_len = lret; + if (!llamafile_seek(f, 0, SEEK_SET)) + goto fail; + if (ctx) + buf = js_malloc(ctx, buf_len + 1); + else + buf = malloc(buf_len + 1); + if (!buf) + goto fail; + if (llamafile_read(f, buf, buf_len) != buf_len) { + errno = EIO; + if (ctx) + js_free(ctx, buf); + else + free(buf); + fail: + llamafile_close(f); + return NULL; + } + buf[buf_len] = '\0'; + llamafile_close(f); + *pbuf_len = buf_len; + return buf; +} + +static int eval_buf(JSContext *ctx, const void *buf, int buf_len, + const char *filename, int eval_flags) +{ + JSValue val; + int ret; + + if ((eval_flags & JS_EVAL_TYPE_MASK) == JS_EVAL_TYPE_MODULE) { + /* for the modules, we compile then run to be able to set + import.meta */ + val = JS_Eval(ctx, buf, buf_len, filename, + eval_flags | JS_EVAL_FLAG_COMPILE_ONLY); + if (!JS_IsException(val)) { + js_module_set_import_meta(ctx, val, TRUE, TRUE); + val = JS_EvalFunction(ctx, val); + } + val = js_std_await(ctx, val); + } else { + val = JS_Eval(ctx, buf, buf_len, filename, eval_flags); + } + if (JS_IsException(val)) { + js_std_dump_error(ctx); + ret = -1; + } else { + ret = 0; + } + JS_FreeValue(ctx, val); + return ret; +} + +static int eval_file(JSContext *ctx, const char *filename, int module) +{ + uint8_t *buf; + int ret, eval_flags; + size_t buf_len; + + buf = jama_load_file(ctx, &buf_len, filename); + if (!buf) { + perror(filename); + exit(1); + } + + if (module < 0) { + module = (has_suffix(filename, ".mjs") || + JS_DetectModule((const char *)buf, buf_len)); + } + if (module) + eval_flags = JS_EVAL_TYPE_MODULE; + else + eval_flags = JS_EVAL_TYPE_GLOBAL; + ret = eval_buf(ctx, buf, buf_len, filename, eval_flags); + js_free(ctx, buf); + return ret; +} + +static JSValue js_gc(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + JS_RunGC(JS_GetRuntime(ctx)); + return JS_UNDEFINED; +} + +static const JSCFunctionListEntry global_obj[] = { + JS_CFUNC_DEF("gc", 0, js_gc), +}; + + +void jamfile_define_jamfile_global(JSContext *ctx, int argc, char ** argv) { + JSValue global = JS_GetGlobalObject(ctx); + JSValue Jamfile = JS_NewObject(ctx); + + JS_SetPropertyStr(ctx, Jamfile, "version", JS_NewString(ctx, JAMFILE_VERSION)); + + JSValue args = JS_NewArray(ctx); + for(int i = 0; i < argc; i++) { + JS_SetPropertyUint32(ctx, args, i, JS_NewString(ctx, argv[i])); + } + JS_SetPropertyStr(ctx, Jamfile, "args", args); + + JS_SetPropertyStr(ctx, global, "Jamfile", Jamfile); + JS_FreeValue(ctx, global); +} + +static JSContext *jamfile_context_new(JSRuntime *rt) +{ + JSContext *ctx; + ctx = JS_NewContext(rt); + if (!ctx) + return NULL; + + js_init_module_sqlite(ctx, "jamfile:sqlite"); + js_init_module_llamafile(ctx, "jamfile:llamafile", JAMFILE_DEFAULT_EMBEDDING_MODEL, JAMFILE_DEFAULT_COMPLETION_MODEL); + js_init_module_std(ctx, "qjs:std"); + js_init_module_os(ctx, "qjs:os"); + js_init_module_bjson(ctx, "qjs:bjson"); + + JSValue global = JS_GetGlobalObject(ctx); + + JSValue console = JS_NewObject(ctx); + JS_SetPropertyStr(ctx, console, "log", + JS_NewCFunction(ctx, js_print, "log", 1)); + JS_SetPropertyStr(ctx, global, "console", console); + + JS_SetPropertyFunctionList(ctx, global, global_obj, countof(global_obj)); + JS_SetPropertyFunctionList(ctx, global, &argv0, 1); + JS_FreeValue(ctx, global); + + return ctx; +} + +JSModuleDef *jama_module_loader(JSContext *ctx, + const char *module_name, void *opaque) +{ + struct { + char * name; + uint8_t *bytecode; + uint32_t bytecode_size; + } jama_builtin_modules[] = { + {"jamfile:assert", (uint8_t *) qjsc_assert, qjsc_assert_size }, + {"jamfile:colors", (uint8_t *) qjsc_colors, qjsc_colors_size }, + {"jamfile:cli", (uint8_t *) qjsc_cli, qjsc_cli_size }, + {"jamfile:fmt", (uint8_t *) qjsc_fmt, qjsc_fmt_size }, + {"jamfile:zod", (uint8_t *) qjsc_zod, qjsc_zod_size }, + {"jamfile:yaml", (uint8_t *) qjsc_yaml, qjsc_yaml_size }, + {"jamfile:toml", (uint8_t *) qjsc_toml, qjsc_toml_size }, + {"jamfile:frontmatter", (uint8_t *) qjsc_frontmatter, qjsc_frontmatter_size }, + {"jamfile:marked", (uint8_t *) qjsc_marked, qjsc_marked_size }, + {"jamfile:linkedom", (uint8_t *) qjsc_linkedom, qjsc_linkedom_size }, + }; + JSModuleDef *m; + + size_t buf_len; + uint8_t *buf; + JSValue func_val; + for(int i = 0; i < countof(jama_builtin_modules); i++) { + if(strncmp(module_name, jama_builtin_modules[i].name, strlen(module_name)) == 0) { + JSValue obj = JS_ReadObject(ctx, jama_builtin_modules[i].bytecode, jama_builtin_modules[i].bytecode_size, JS_READ_OBJ_BYTECODE); + + assert(!JS_IsException(obj)); + assert(JS_VALUE_GET_TAG(obj) == JS_TAG_MODULE); + + JSModuleDef *m = JS_VALUE_GET_PTR(obj); + JS_FreeValue(ctx, obj); + return m; + } + } + + buf = jama_load_file(ctx, &buf_len, module_name); + if (!buf) { + JS_ThrowReferenceError(ctx, "could not load module filename '%s'", + module_name); + return NULL; + } + + if(sqlite3_strlike("%.gbnf", module_name, 0)==0 + || sqlite3_strlike("%.sql", module_name, 0)==0 + || sqlite3_strlike("%.txt", module_name, 0)==0) { + + sqlite3_str * s = sqlite3_str_new(NULL); + sqlite3_str_appendall(s, "export default `"); + for(int i = 0; i < buf_len; i++) { + // TODO also escape ${}" + if(buf[i] == '`') { + sqlite3_str_appendall(s, "\\`"); + } + else { + sqlite3_str_appendchar(s, 1, buf[i]); + } + } + sqlite3_str_appendall(s, "`;"); + int moduleLength = sqlite3_str_length(s); + char * module = sqlite3_str_finish(s); + assert(module); + func_val = JS_Eval(ctx, module, moduleLength, module_name, JS_EVAL_TYPE_MODULE | JS_EVAL_FLAG_COMPILE_ONLY); + js_free(ctx, buf); + sqlite3_free((void *) module); + } + else { + func_val = JS_Eval(ctx, (char *)buf, buf_len, module_name, + JS_EVAL_TYPE_MODULE | JS_EVAL_FLAG_COMPILE_ONLY); + js_free(ctx, buf); + } + + + if (JS_IsException(func_val)) + return NULL; + /* XXX: could propagate the exception */ + js_module_set_import_meta(ctx, func_val, TRUE, FALSE); + /* the module is already referenced, so we must free it */ + m = JS_VALUE_GET_PTR(func_val); + JS_FreeValue(ctx, func_val); + + return m; +} + +#define PROG_NAME "jamfile" + +void help(void) +{ + printf("Jamfile version %s, QuickJS-ng version %s\n" + "usage: " PROG_NAME " [options] [file [args]]\n" + "-h --help list options\n" + "-e --eval EXPR evaluate EXPR\n" + "-i --interactive go to interactive mode\n" + "-m --module load as ES6 module (default=autodetect)\n" + " --script load as ES6 script (default=autodetect)\n" + " --memory-limit n limit the memory usage to 'n' Kbytes\n" + " --stack-size n limit the stack size to 'n' Kbytes\n" + " --unhandled-rejection dump unhandled promise rejections\n" + "-q --quit just instantiate the interpreter and quit\n", JAMFILE_VERSION, JS_GetVersion()); + exit(1); +} + +int cmd_run(int argc, char ** argv) { + JSRuntime *rt; + JSContext *ctx; + JSValue ret; + int optind; + char *expr = NULL; + int interactive = 0; + int module = -1; + int dump_unhandled_promise_rejection = 0; + int64_t memory_limit = -1; + int64_t stack_size = -1; + + argv0 = (JSCFunctionListEntry)JS_PROP_STRING_DEF("argv0", argv[0], + JS_PROP_C_W_E); + + optind = 1; + while (optind < argc && *argv[optind] == '-') { + char *arg = argv[optind] + 1; + const char *longopt = ""; + char *opt_arg = NULL; + /* a single - is not an option, it also stops argument scanning */ + if (!*arg) + break; + optind++; + if (*arg == '-') { + longopt = arg + 1; + opt_arg = strchr(longopt, '='); + if (opt_arg) + *opt_arg++ = '\0'; + arg += strlen(arg); + /* -- stops argument scanning */ + if (!*longopt) + break; + } + for (; *arg || *longopt; longopt = "") { + char opt = *arg; + if (opt) { + arg++; + if (!opt_arg && *arg) + opt_arg = arg; + } + if (opt == 'h' || opt == '?' || !strcmp(longopt, "help")) { + help(); + continue; + } + if (opt == 'e' || !strcmp(longopt, "eval")) { + if (!opt_arg) { + if (optind >= argc) { + fprintf(stderr, "jamfile: missing expression for -e\n"); + exit(2); + } + opt_arg = argv[optind++]; + } + expr = opt_arg; + break; + } + if (opt == 'i' || !strcmp(longopt, "interactive")) { + interactive++; + continue; + } + if (opt == 'm' || !strcmp(longopt, "module")) { + module = 1; + continue; + } + if (!strcmp(longopt, "script")) { + module = 0; + continue; + } + if (!strcmp(longopt, "unhandled-rejection")) { + dump_unhandled_promise_rejection = 1; + continue; + } + if (opt) { + fprintf(stderr, "jamfile: unknown option '-%c'\n", opt); + } else { + fprintf(stderr, "jamfile: unknown option '--%s'\n", longopt); + } + help(); + } + } + + + rt = JS_NewRuntime(); + + if (!rt) { + fprintf(stderr, "jamfile: cannot allocate JS runtime\n"); + exit(2); + } + + JS_SetRuntimeOpaque(rt, (void *) 0x420); + + js_std_set_worker_new_context_func(jamfile_context_new); + js_std_init_handlers(rt); + ctx = jamfile_context_new(rt); + if (!ctx) { + fprintf(stderr, "jamfile: cannot allocate JS context\n"); + exit(2); + } + + // -1 and +1 to read past the js file arg + jamfile_define_jamfile_global(ctx, argc - optind - 1, argv + optind + 1); + + JS_SetModuleLoaderFunc(rt, NULL, jama_module_loader, NULL); + + if (dump_unhandled_promise_rejection) { + JS_SetHostPromiseRejectionTracker(rt, js_std_promise_rejection_tracker, + NULL); + } + + //js_std_add_helpers(ctx, argc - optind, argv + optind); + + if (expr) { + if (eval_buf(ctx, expr, strlen(expr), "", 0)) + goto fail; + } else + if (optind >= argc) { + /* interactive mode */ + interactive = 1; + } else { + const char *filename; + filename = argv[optind]; + if (eval_file(ctx, filename, module)) + goto fail; + } + if (interactive) { + js_std_eval_binary(ctx, qjsc_repl, qjsc_repl_size, 0); + } + ret = js_std_loop(ctx); + if (!JS_IsUndefined(ret)) { + js_std_dump_error1(ctx, ret); + goto fail; + } + + js_std_free_handlers(rt); + JS_FreeContext(ctx); + JS_FreeRuntime(rt); + + + return 0; + fail: + js_std_free_handlers(rt); + JS_FreeContext(ctx); + JS_FreeRuntime(rt); + return 1; +} + +int main(int argc, char **argv) { + int rc; + sqlite3 *db; + sqlite3_stmt *stmt; + + FLAG_log_disable = 1; + argc = cosmo_args("/zip/.args", &argv); + FLAGS_READY = 1; + + char *modelPath = NULL; + for (int i = 1; i < argc; i++) { + char *arg = argv[i]; + if (sqlite3_stricmp(arg, "--default-embedding-model") == 0) { + assert(++i <= argc); + JAMFILE_DEFAULT_EMBEDDING_MODEL = argv[i]; + }else if (sqlite3_stricmp(arg, "--default-completion-model") == 0) { + assert(++i <= argc); + JAMFILE_DEFAULT_COMPLETION_MODEL = argv[i]; + } else if (sqlite3_stricmp(arg, "--version") == 0 || sqlite3_stricmp(arg, "-v") == 0) { + fprintf(stderr,"jamfile %s\n",JAMFILE_VERSION); + return 0; + } else if (sqlite3_stricmp(arg, "--help") == 0 || sqlite3_stricmp(arg, "-h") == 0) { + llamafile_help("/zip/jamfile/jamfile.1.asc"); + fprintf(stderr, "Usage: jamfile [ run | types ]\n"); + return 0; + } else if(sqlite3_stricmp(arg, "run") == 0) { + return cmd_run(argc-i, argv+i); + } else if(sqlite3_stricmp(arg, "types") == 0) { + fprintf(stdout, "%.*s", jamfile_jamfile_d_ts_len, jamfile_jamfile_d_ts); + return 0; + } + else { + printf("Unknown arg %s\n", arg); + return 1; + } + } + + fprintf(stderr, "Usage: jamfile [run | types]\n"); + return 0; +} diff --git a/jamfile/jamfile.d.ts b/jamfile/jamfile.d.ts new file mode 100644 index 0000000000..0d566081ed --- /dev/null +++ b/jamfile/jamfile.d.ts @@ -0,0 +1,4682 @@ +/** + * This will eventually contain the full types of all the modules this JS runtime offers. + * + * Generating this automatically was too complicated, so will unfortunately have to manage + * manually for now. + */ + + +declare namespace Jamfile { + export const version: string; +} +declare module "qjs:std" { + /** + * Exit the process. + * @param n - Exit code. + */ + export function exit(n: number): void; + + /** + * Evaluate the string `str` as a script (global eval). + * @param str - The script to evaluate. + * @param options - Optional settings for evaluation. + * - `backtrace_barrier` (boolean, default = false): If true, error backtraces do not list the stack frames below the evalScript. + * - `async` (boolean, default = false): If true, await is accepted in the script and a promise is returned. + * @returns The result of the evaluated script or a promise if `async` is true. + */ + export function evalScript( + str: string, + options?: { backtrace_barrier?: boolean; async?: boolean } + ): any; + + /** + * Evaluate the file `filename` as a script (global eval). + * @param filename - The path to the file. + * @returns The result of the evaluated script. + */ + export function loadScript(filename: string): any; + + /** + * Load the file `filename` and return it as a string assuming UTF-8 encoding. + * @param filename - The path to the file. + * @returns The file content as a string, or null in case of I/O error. + */ + export function loadFile(filename: string): string | null; + + /** + * Open a file (wrapper to the libc `fopen()`). + * @param filename - The path to the file. + * @param flags - The flags for opening the file. + * @param errorObj - An optional object where `errno` will be set to the error code, or 0 if no error occurred. + * @returns A `FILE` object, or null in case of I/O error. + */ + export function open( + filename: string, + flags: string, + errorObj?: { errno?: number } + ): FILE | null; + + /** + * Open a process by creating a pipe (wrapper to the libc `popen()`). + * @param command - The command to execute. + * @param flags - The flags for opening the process. + * @param errorObj - An optional object where `errno` will be set to the error code, or 0 if no error occurred. + * @returns A `FILE` object, or null in case of I/O error. + */ + export function popen( + command: string, + flags: string, + errorObj?: { errno?: number } + ): FILE | null; + + /** + * Open a file from a file handle (wrapper to the libc `fdopen()`). + * @param fd - The file descriptor. + * @param flags - The flags for opening the file. + * @param errorObj - An optional object where `errno` will be set to the error code, or 0 if no error occurred. + * @returns A `FILE` object, or null in case of I/O error. + */ + export function fdopen( + fd: number, + flags: string, + errorObj?: { errno?: number } + ): FILE | null; + + /** + * Open a temporary file. + * @param errorObj - An optional object where `errno` will be set to the error code, or 0 if no error occurred. + * @returns A `FILE` object, or null in case of I/O error. + */ + export function tmpfile(errorObj?: { errno?: number }): FILE | null; + + /** + * Outputs the string `str` to stdout. + * @param str - The string to output. + */ + export function puts(str: string): void; + + /** + * Outputs formatted text to stdout. + * @param fmt - The format string. + * @param args - Arguments for formatting. + */ + export function printf(fmt: string, ...args: any[]): void; + + /** + * Formats text into a string using the libc `sprintf()`. + * @param fmt - The format string. + * @param args - Arguments for formatting. + * @returns The formatted string. + */ + export function sprintf(fmt: string, ...args: any[]): string; + + /** Wrapper to the libc `stdin`. */ + // TODO syntax error on the name "in" + //export const in: FILE; + + /** Wrapper to the libc `stdout`. */ + export const out: FILE; + + /** Wrapper to the libc `stderr`. */ + export const err: FILE; + + /** Constants for seek operations. */ + export const SEEK_SET: number; + export const SEEK_CUR: number; + export const SEEK_END: number; + + /** Enumeration object containing common error codes. */ + export const Error: { + EINVAL: number; + EIO: number; + EACCES: number; + EEXIST: number; + ENOSPC: number; + ENOSYS: number; + EBUSY: number; + ENOENT: number; + EPERM: number; + EPIPE: number; + }; + + /** + * Return a string that describes the error `errno`. + * @param errno - The error code. + * @returns The error description. + */ + export function strerror(errno: number): string; + + /** + * Manually invoke the cycle removal algorithm. + * Useful for specific memory constraints or testing. + */ + export function gc(): void; + + /** + * Return the value of the environment variable `name`. + * @param name - The name of the environment variable. + * @returns The value of the environment variable, or undefined if not defined. + */ + export function getenv(name: string): string | undefined; + + /** + * Set the value of the environment variable `name` to the string `value`. + * @param name - The name of the environment variable. + * @param value - The value to set. + */ + export function setenv(name: string, value: string): void; + + /** + * Delete the environment variable `name`. + * @param name - The name of the environment variable. + */ + export function unsetenv(name: string): void; + + /** + * Return an object containing the environment variables as key-value pairs. + * @returns An object with environment variables. + */ + export function getenviron(): Record; + + /** + * Download a URL using the curl command line utility. + * @param url - The URL to download. + * @param options - Optional settings for the download. + * - `binary` (boolean, default = false): If true, the response is an ArrayBuffer instead of a string. + * - `full` (boolean, default = false): If true, return an object containing properties `response`, `responseHeaders`, and `status`. If `full` is false, only the response is returned if the status is between 200 and 299, otherwise null. + * @returns The response content or an object with additional properties based on `options`. + */ + export function urlGet( + url: string, + options?: { binary?: boolean; full?: boolean } + ): string | ArrayBuffer | { response: string | null; responseHeaders: string; status: number }; + + /** + * Parse `str` using a superset of JSON.parse with extended syntax support. + * Supported extensions: + * - Single line and multiline comments + * - Unquoted properties (ASCII-only Javascript identifiers) + * - Trailing comma in array and object definitions + * - Single quoted strings + * - \f and \v as space characters + * - Leading plus in numbers + * - Octal (0o prefix) and hexadecimal (0x prefix) numbers + * @param str - The string to parse. + * @returns The parsed object. + */ + export function parseExtJSON(str: string): any; + + /** Represents a file object. */ + export interface FILE { + /** Close the file. */ + close(): number; + + /** Outputs the string with the UTF-8 encoding. */ + puts(str: string): void; + + /** Formatted printf. */ + printf(fmt: string, ...args: any[]): void; + + /** Flush the buffered file. */ + flush(): void; + + /** Seek to a given file position. */ + seek(offset: number | bigint, whence: number): number; + + /** Return the current file position. */ + tell(): number; + + /** Return the current file position as a bigint. */ + tello(): bigint; + + /** Return true if end of file. */ + eof(): boolean; + + /** Return the associated OS handle. */ + fileno(): number; + + /** Return true if there was an error. */ + error(): boolean; + + /** Clear the error indication. */ + clearerr(): void; + + /** Read bytes from the file to an ArrayBuffer. */ + read(buffer: ArrayBuffer, position: number, length: number): number; + + /** Write bytes to the file from an ArrayBuffer. */ + write(buffer: ArrayBuffer, position: number, length: number): number; + + /** Return the next line from the file as a string. */ + getline(): string | null; + + /** Read bytes as a string up to `max_size`. */ + readAsString(max_size?: number): string | null; + + /** Return the next byte from the file. */ + getByte(): number; + + /** Write one byte to the file. */ + putByte(c: number): void; + } +} + + +declare module "qjs:os" { + /** + * Open a file. Return a handle or < 0 if error. + * @param filename The file name. + * @param flags POSIX open flags. + * @param mode File mode (default = 0o666). + */ + export function open(filename: string, flags: number, mode?: number): number; + + /** POSIX open flags. */ + export const O_RDONLY: number; + export const O_WRONLY: number; + export const O_RDWR: number; + export const O_APPEND: number; + export const O_CREAT: number; + export const O_EXCL: number; + export const O_TRUNC: number; + + /** Windows-specific flag to open the file in text mode. Default is binary mode. */ + export const O_TEXT: number; + + /** + * Close the file handle. + * @param fd The file handle to close. + */ + export function close(fd: number): number; + + /** + * Seek in the file. + * @param fd The file handle. + * @param offset The position to seek to (number or bigint). + * @param whence The reference position (use std.SEEK_* constants). + */ + export function seek(fd: number, offset: number | bigint, whence: number): number | bigint; + + /** + * Read bytes from a file handle. + * @param fd The file handle to read from. + * @param buffer The ArrayBuffer to store the read data. + * @param offset The starting position in the buffer. + * @param length The number of bytes to read. + */ + export function read(fd: number, buffer: ArrayBuffer, offset: number, length: number): number; + + /** + * Write bytes to a file handle. + * @param fd The file handle to write to. + * @param buffer The ArrayBuffer containing the data to write. + * @param offset The starting position in the buffer. + * @param length The number of bytes to write. + */ + export function write(fd: number, buffer: ArrayBuffer, offset: number, length: number): number; + + /** + * Check if the file handle is a TTY (terminal). + * @param fd The file handle to check. + */ + export function isatty(fd: number): boolean; + + /** + * Get the size of a TTY. + * @param fd The TTY file handle. + * @returns [width, height] or null if not available. + */ + export function ttyGetWinSize(fd: number): [number, number] | null; + + /** + * Set the TTY in raw mode. + * @param fd The TTY file handle. + */ + export function ttySetRaw(fd: number): void; + + /** + * Remove a file. + * @param filename The file name to remove. + */ + export function remove(filename: string): number; + + /** + * Rename a file. + * @param oldname The current file name. + * @param newname The new file name. + */ + export function rename(oldname: string, newname: string): number; + + /** + * Get the canonicalized absolute pathname of a path. + * @param path The input path. + * @returns [path, error code]. + */ + export function realpath(path: string): [string, number]; + + /** + * Get the current working directory. + * @returns [directory, error code]. + */ + export function getcwd(): [string, number]; + + /** + * Change the current working directory. + * @param path The new working directory. + */ + export function chdir(path: string): number; + + /** + * Create a directory. + * @param path The directory path. + * @param mode The mode for the directory (default = 0o777). + */ + export function mkdir(path: string, mode?: number): number; + + /** + * Get file status for a path. + * @param path The file path. + * @returns [status object, error code]. + */ + export function stat(path: string): [FileStatus, number]; + + /** + * Get file status for a path (without following symlinks). + * @param path The file path. + * @returns [status object, error code]. + */ + export function lstat(path: string): [FileStatus, number]; + + /** File status object returned by `stat` and `lstat`. */ + export interface FileStatus { + dev: number; + ino: number; + mode: number; + nlink: number; + uid: number; + gid: number; + rdev: number; + size: number; + blocks: number; + atime: number; + mtime: number; + ctime: number; + } + + /** Constants to interpret the mode property returned by `stat`. */ + export const S_IFMT: number; + export const S_IFIFO: number; + export const S_IFCHR: number; + export const S_IFDIR: number; + export const S_IFBLK: number; + export const S_IFREG: number; + export const S_IFSOCK: number; + export const S_IFLNK: number; + export const S_ISGID: number; + export const S_ISUID: number; + + /** + * Change the access and modification times of a file. + * @param path The file path. + * @param atime Access time in milliseconds since 1970. + * @param mtime Modification time in milliseconds since 1970. + */ + export function utimes(path: string, atime: number, mtime: number): number; + + /** + * Create a symbolic link. + * @param target The target of the link. + * @param linkpath The link path to create. + */ + export function symlink(target: string, linkpath: string): number; + + /** + * Read a symbolic link. + * @param path The link path. + * @returns [link target, error code]. + */ + export function readlink(path: string): [string, number]; + + /** + * Read a directory's contents. + * @param path The directory path. + * @returns [array of filenames, error code]. + */ + export function readdir(path: string): [string[], number]; + + /** + * Add a read handler to a file handle. + * @param fd The file handle. + * @param func The function to call when there is data pending (or `null` to remove the handler). + */ + export function setReadHandler(fd: number, func: (() => void) | null): void; + + /** + * Add a write handler to a file handle. + * @param fd The file handle. + * @param func The function to call when data can be written (or `null` to remove the handler). + */ + export function setWriteHandler(fd: number, func: (() => void) | null): void; + + /** + * Add a signal handler. + * @param signal The signal number. + * @param func The function to call when the signal is received (`null` for the default handler, `undefined` to ignore the signal). + */ + export function signal(signal: number, func: (() => void) | null | undefined): void; + + /** POSIX signal numbers. */ + export const SIGINT: number; + export const SIGABRT: number; + export const SIGFPE: number; + export const SIGILL: number; + export const SIGSEGV: number; + export const SIGTERM: number; + + /** + * Send a signal to a process. + * @param pid The process ID. + * @param sig The signal to send. + */ + export function kill(pid: number, sig: number): number; + + /** + * Execute a process with the given arguments. + * @param args The arguments for the process. + * @param options Optional execution parameters. + * @returns Exit code, process ID, or negated signal number depending on the `block` option. + */ + export function exec( + args: string[], + options?: { + block?: boolean; + usePath?: boolean; + file?: string; + cwd?: string; + stdin?: number; + stdout?: number; + stderr?: number; + env?: Record; + uid?: number; + gid?: number; + } + ): number; + + /** + * Get the current process ID. + * @returns The process ID. + */ + export function getpid(): number; + + /** + * Wait for a process to change state. + * @param pid The process ID to wait for. + * @param options Options for the waitpid call (use `WNOHANG` if non-blocking behavior is desired). + * @returns [result, status], where `result` is the PID of the child process or -errno, and `status` is the exit status or signal number. + */ + export function waitpid(pid: number, options?: number): [number, number]; + + /** Constant for non-blocking waitpid call. */ + export const WNOHANG: number; + + /** + * Duplicate a file descriptor. + * @param fd The file descriptor to duplicate. + * @returns The new file descriptor, or -errno on failure. + */ + export function dup(fd: number): number; + + /** + * Duplicate a file descriptor to a specific target descriptor. + * @param oldfd The file descriptor to duplicate. + * @param newfd The target descriptor number. + * @returns 0 if successful, or -errno on failure. + */ + export function dup2(oldfd: number, newfd: number): number; + + /** + * Create a pipe. + * @returns [read_fd, write_fd] or `null` in case of error. + */ + export function pipe(): [number, number] | null; + + /** + * Sleep for the specified duration. + * @param delay_ms The delay in milliseconds. + */ + export function sleep(delay_ms: number): void; + + /** + * Sleep asynchronously for the specified duration. + * @param delay_ms The delay in milliseconds. + * @returns A promise that resolves after the specified delay. + */ + export function sleepAsync(delay_ms: number): Promise; + + /** + * Get a high-precision timestamp. + * @returns A timestamp in milliseconds with more precision than `Date.now()`. + */ + export function now(): number; + + /** + * Call a function after a specified delay. + * @param func The function to call. + * @param delay The delay in milliseconds. + * @returns A timer handle. + */ + export function setTimeout(func: () => void, delay: number): number; + + /** + * Cancel a timer. + * @param handle The timer handle to cancel. + */ + export function clearTimeout(handle: number): void; + + /** + * Get the platform string. + * @returns A string representing the platform: "linux", "darwin", "win32", or "js". + */ + export function platform(): "linux" | "darwin" | "win32" | "js"; + + /** + * Worker constructor for creating a new thread. + * @param module_filename The module filename to execute in the new thread. + */ + export class Worker { + constructor(module_filename: string); + + /** + * Send a message to the corresponding worker. + * @param msg The message to send. + */ + postMessage(msg: any): void; + + /** + * Set a function to handle received messages. + * @param handler A function that receives a single argument containing the message. + */ + onmessage: ((msg: { data: any }) => void) | null; + + /** Represents the parent worker in the created worker. */ + static parent: Worker; + } +} + + +declare module 'jamfile:yaml' { + + export type SchemaType = "failsafe" | "json" | "core" | "default" | "extended"; + export type StyleVariant = + | "lowercase" + | "uppercase" + | "camelcase" + | "decimal" + | "binary" + | "octal" + | "hexadecimal"; + + export type StringifyOptions = { + /** + * Indentation width to use (in spaces). + * + * @default {2} + */ + indent?: number; + /** + * When true, adds an indentation level to array elements. + * + * @default {true} + */ + arrayIndent?: boolean; + /** + * Do not throw on invalid types (like function in the safe schema) and skip + * pairs and single values with such types. + * + * @default {false} + */ + skipInvalid?: boolean; + /** + * Specifies level of nesting, when to switch from block to flow style for + * collections. `-1` means block style everywhere. + * + * @default {-1} + */ + flowLevel?: number; + /** Each tag may have own set of styles. - "tag" => "style" map. */ + styles?: Record; + /** + * Name of the schema to use. + * + * @default {"default"} + */ + schema?: SchemaType; + /** + * If true, sort keys when dumping YAML in ascending, ASCII character order. + * If a function, use the function to sort the keys. + * If a function is specified, the function must return a negative value + * if first argument is less than second argument, zero if they're equal + * and a positive value otherwise. + * + * @default {false} + */ + sortKeys?: boolean | ((a: string, b: string) => number); + /** + * Set max line width. + * + * @default {80} + */ + lineWidth?: number; + /** + * If false, don't convert duplicate objects into references. + * + * @default {true} + */ + useAnchors?: boolean; + /** + * If false don't try to be compatible with older yaml versions. + * Currently: don't quote "yes", "no" and so on, + * as required for YAML 1.1. + * + * @default {true} + */ + compatMode?: boolean; + /** + * If true flow sequences will be condensed, omitting the + * space between `key: value` or `a, b`. Eg. `'[a,b]'` or `{a:{b:c}}`. + * Can be useful when using yaml for pretty URL query params + * as spaces are %-encoded. + * + * @default {false} + */ + condenseFlow?: boolean; + }; + + export function stringify(data: unknown, options?: StringifyOptions): string; + + + export interface ParseOptions { + /** + * Name of the schema to use. + * + * @default {"default"} + */ + schema?: SchemaType; + /** + * If `true`, duplicate keys will overwrite previous values. Otherwise, + * duplicate keys will throw a {@linkcode SyntaxError}. + * + * @default {false} + */ + allowDuplicateKeys?: boolean; + /** + * If defined, a function to call on warning messages taking an + * {@linkcode Error} as its only argument. + */ + onWarning?(error: Error): void; + } + + export function parse(content: string, options?: ParseOptions): unknown; + export function parseAll(content: string, options?: ParseOptions): unknown; + + +} + +declare module 'jamfile:toml' { + + export interface StringifyOptions { + /** + * Define if the keys should be aligned or not. + * + * @default {false} + */ + keyAlignment?: boolean; + } + + export function stringify(obj: Record, options?: StringifyOptions): string; + + export function parse(tomlString: string): Record; +} + +declare module 'jamfile:frontmatter' { + + export type Format = "yaml" | "toml" | "json"; + + export function test(str: string, formats?: Format[]): boolean; + + export type Extract = { + frontMatter: string; + body: string; + attrs: T; + }; + + export function extractJson(text: string): Extract; + export function extractToml(text: string): Extract; + export function extractYaml(text: string): Extract; +} + +declare module 'jamfile:marked' { + export type MarkedToken = (Tokens.Blockquote | Tokens.Br | Tokens.Code | Tokens.Codespan | Tokens.Def | Tokens.Del | Tokens.Em | Tokens.Escape | Tokens.Heading | Tokens.Hr | Tokens.HTML | Tokens.Image | Tokens.Link | Tokens.List | Tokens.ListItem | Tokens.Paragraph | Tokens.Space | Tokens.Strong | Tokens.Table | Tokens.Tag | Tokens.Text); + export type Token = (MarkedToken | Tokens.Generic); + export namespace Tokens { + interface Blockquote { + type: "blockquote"; + raw: string; + text: string; + tokens: Token[]; + } + interface Br { + type: "br"; + raw: string; + } + interface Checkbox { + checked: boolean; + } + interface Code { + type: "code"; + raw: string; + codeBlockStyle?: "indented"; + lang?: string; + text: string; + escaped?: boolean; + } + interface Codespan { + type: "codespan"; + raw: string; + text: string; + } + interface Def { + type: "def"; + raw: string; + tag: string; + href: string; + title: string; + } + interface Del { + type: "del"; + raw: string; + text: string; + tokens: Token[]; + } + interface Em { + type: "em"; + raw: string; + text: string; + tokens: Token[]; + } + interface Escape { + type: "escape"; + raw: string; + text: string; + } + interface Generic { + [index: string]: any; + type: string; + raw: string; + tokens?: Token[]; + } + interface Heading { + type: "heading"; + raw: string; + depth: number; + text: string; + tokens: Token[]; + } + interface Hr { + type: "hr"; + raw: string; + } + interface HTML { + type: "html"; + raw: string; + pre: boolean; + text: string; + block: boolean; + } + interface Image { + type: "image"; + raw: string; + href: string; + title: string | null; + text: string; + } + interface Link { + type: "link"; + raw: string; + href: string; + title?: string | null; + text: string; + tokens: Token[]; + } + interface List { + type: "list"; + raw: string; + ordered: boolean; + start: number | ""; + loose: boolean; + items: ListItem[]; + } + interface ListItem { + type: "list_item"; + raw: string; + task: boolean; + checked?: boolean; + loose: boolean; + text: string; + tokens: Token[]; + } + interface Paragraph { + type: "paragraph"; + raw: string; + pre?: boolean; + text: string; + tokens: Token[]; + } + interface Space { + type: "space"; + raw: string; + } + interface Strong { + type: "strong"; + raw: string; + text: string; + tokens: Token[]; + } + interface Table { + type: "table"; + raw: string; + align: Array<"center" | "left" | "right" | null>; + header: TableCell[]; + rows: TableCell[][]; + } + interface TableCell { + text: string; + tokens: Token[]; + header: boolean; + align: "center" | "left" | "right" | null; + } + interface TableRow { + text: string; + } + interface Tag { + type: "html"; + raw: string; + inLink: boolean; + inRawBlock: boolean; + text: string; + block: boolean; + } + interface Text { + type: "text"; + raw: string; + text: string; + tokens?: Token[]; + escaped?: boolean; + } + } + export type Links = Record>; + export type TokensList = Token[] & { + links: Links; + }; + /** + * Renderer + */ + class _Renderer { + options: MarkedOptions; + parser: _Parser; + constructor(options?: MarkedOptions); + space(token: Tokens.Space): string; + code({ text, lang, escaped }: Tokens.Code): string; + blockquote({ tokens }: Tokens.Blockquote): string; + html({ text }: Tokens.HTML | Tokens.Tag): string; + heading({ tokens, depth }: Tokens.Heading): string; + hr(token: Tokens.Hr): string; + list(token: Tokens.List): string; + listitem(item: Tokens.ListItem): string; + checkbox({ checked }: Tokens.Checkbox): string; + paragraph({ tokens }: Tokens.Paragraph): string; + table(token: Tokens.Table): string; + tablerow({ text }: Tokens.TableRow): string; + tablecell(token: Tokens.TableCell): string; + /** + * span level renderer + */ + strong({ tokens }: Tokens.Strong): string; + em({ tokens }: Tokens.Em): string; + codespan({ text }: Tokens.Codespan): string; + br(token: Tokens.Br): string; + del({ tokens }: Tokens.Del): string; + link({ href, title, tokens }: Tokens.Link): string; + image({ href, title, text }: Tokens.Image): string; + text(token: Tokens.Text | Tokens.Escape): string; + } + /** + * TextRenderer + * returns only the textual part of the token + */ + class _TextRenderer { + strong({ text }: Tokens.Strong): string; + em({ text }: Tokens.Em): string; + codespan({ text }: Tokens.Codespan): string; + del({ text }: Tokens.Del): string; + html({ text }: Tokens.HTML | Tokens.Tag): string; + text({ text }: Tokens.Text | Tokens.Escape | Tokens.Tag): string; + link({ text }: Tokens.Link): string; + image({ text }: Tokens.Image): string; + br(): string; + } + /** + * Parsing & Compiling + */ + class _Parser { + options: MarkedOptions; + renderer: _Renderer; + textRenderer: _TextRenderer; + constructor(options?: MarkedOptions); + /** + * Static Parse Method + */ + static parse(tokens: Token[], options?: MarkedOptions): string; + /** + * Static Parse Inline Method + */ + static parseInline(tokens: Token[], options?: MarkedOptions): string; + /** + * Parse Loop + */ + parse(tokens: Token[], top?: boolean): string; + /** + * Parse Inline Tokens + */ + parseInline(tokens: Token[], renderer?: _Renderer | _TextRenderer): string; + } + const other: { + codeRemoveIndent: RegExp; + outputLinkReplace: RegExp; + indentCodeCompensation: RegExp; + beginningSpace: RegExp; + endingHash: RegExp; + startingSpaceChar: RegExp; + endingSpaceChar: RegExp; + nonSpaceChar: RegExp; + newLineCharGlobal: RegExp; + tabCharGlobal: RegExp; + multipleSpaceGlobal: RegExp; + blankLine: RegExp; + doubleBlankLine: RegExp; + blockquoteStart: RegExp; + blockquoteSetextReplace: RegExp; + blockquoteSetextReplace2: RegExp; + listReplaceTabs: RegExp; + listReplaceNesting: RegExp; + listIsTask: RegExp; + listReplaceTask: RegExp; + anyLine: RegExp; + hrefBrackets: RegExp; + tableDelimiter: RegExp; + tableAlignChars: RegExp; + tableRowBlankLine: RegExp; + tableAlignRight: RegExp; + tableAlignCenter: RegExp; + tableAlignLeft: RegExp; + startATag: RegExp; + endATag: RegExp; + startPreScriptTag: RegExp; + endPreScriptTag: RegExp; + startAngleBracket: RegExp; + endAngleBracket: RegExp; + pedanticHrefTitle: RegExp; + unicodeAlphaNumeric: RegExp; + escapeTest: RegExp; + escapeReplace: RegExp; + escapeTestNoEncode: RegExp; + escapeReplaceNoEncode: RegExp; + unescapeTest: RegExp; + caret: RegExp; + percentDecode: RegExp; + findPipe: RegExp; + splitPipe: RegExp; + slashPipe: RegExp; + carriageReturn: RegExp; + spaceLine: RegExp; + notSpaceStart: RegExp; + endingNewline: RegExp; + listItemRegex: (bull: string) => RegExp; + nextBulletRegex: (indent: number) => RegExp; + hrRegex: (indent: number) => RegExp; + fencesBeginRegex: (indent: number) => RegExp; + headingBeginRegex: (indent: number) => RegExp; + htmlBeginRegex: (indent: number) => RegExp; + }; + const blockNormal: { + blockquote: RegExp; + code: RegExp; + def: RegExp; + fences: RegExp; + heading: RegExp; + hr: RegExp; + html: RegExp; + lheading: RegExp; + list: RegExp; + newline: RegExp; + paragraph: RegExp; + table: RegExp; + text: RegExp; + }; + export type BlockKeys = keyof typeof blockNormal; + const inlineNormal: { + _backpedal: RegExp; + anyPunctuation: RegExp; + autolink: RegExp; + blockSkip: RegExp; + br: RegExp; + code: RegExp; + del: RegExp; + emStrongLDelim: RegExp; + emStrongRDelimAst: RegExp; + emStrongRDelimUnd: RegExp; + escape: RegExp; + link: RegExp; + nolink: RegExp; + punctuation: RegExp; + reflink: RegExp; + reflinkSearch: RegExp; + tag: RegExp; + text: RegExp; + url: RegExp; + }; + export type InlineKeys = keyof typeof inlineNormal; + export interface Rules { + other: typeof other; + block: Record; + inline: Record; + } + /** + * Tokenizer + */ + class _Tokenizer { + options: MarkedOptions; + rules: Rules; + lexer: _Lexer; + constructor(options?: MarkedOptions); + space(src: string): Tokens.Space | undefined; + code(src: string): Tokens.Code | undefined; + fences(src: string): Tokens.Code | undefined; + heading(src: string): Tokens.Heading | undefined; + hr(src: string): Tokens.Hr | undefined; + blockquote(src: string): Tokens.Blockquote | undefined; + list(src: string): Tokens.List | undefined; + html(src: string): Tokens.HTML | undefined; + def(src: string): Tokens.Def | undefined; + table(src: string): Tokens.Table | undefined; + lheading(src: string): Tokens.Heading | undefined; + paragraph(src: string): Tokens.Paragraph | undefined; + text(src: string): Tokens.Text | undefined; + escape(src: string): Tokens.Escape | undefined; + tag(src: string): Tokens.Tag | undefined; + link(src: string): Tokens.Link | Tokens.Image | undefined; + reflink(src: string, links: Links): Tokens.Link | Tokens.Image | Tokens.Text | undefined; + emStrong(src: string, maskedSrc: string, prevChar?: string): Tokens.Em | Tokens.Strong | undefined; + codespan(src: string): Tokens.Codespan | undefined; + br(src: string): Tokens.Br | undefined; + del(src: string): Tokens.Del | undefined; + autolink(src: string): Tokens.Link | undefined; + url(src: string): Tokens.Link | undefined; + inlineText(src: string): Tokens.Text | undefined; + } + class _Hooks { + options: MarkedOptions; + block?: boolean; + constructor(options?: MarkedOptions); + static passThroughHooks: Set; + /** + * Process markdown before marked + */ + preprocess(markdown: string): string; + /** + * Process HTML after marked is finished + */ + postprocess(html: string): string; + /** + * Process all tokens before walk tokens + */ + processAllTokens(tokens: Token[] | TokensList): Token[] | TokensList; + /** + * Provide function to tokenize markdown + */ + provideLexer(): typeof _Lexer.lexInline; + /** + * Provide function to parse tokens + */ + provideParser(): typeof _Parser.parse; + } + export interface TokenizerThis { + lexer: _Lexer; + } + export type TokenizerExtensionFunction = (this: TokenizerThis, src: string, tokens: Token[] | TokensList) => Tokens.Generic | undefined; + export type TokenizerStartFunction = (this: TokenizerThis, src: string) => number | void; + export interface TokenizerExtension { + name: string; + level: "block" | "inline"; + start?: TokenizerStartFunction; + tokenizer: TokenizerExtensionFunction; + childTokens?: string[]; + } + export interface RendererThis { + parser: _Parser; + } + export type RendererExtensionFunction = (this: RendererThis, token: Tokens.Generic) => string | false | undefined; + export interface RendererExtension { + name: string; + renderer: RendererExtensionFunction; + } + export type TokenizerAndRendererExtension = TokenizerExtension | RendererExtension | (TokenizerExtension & RendererExtension); + export type HooksApi = Omit<_Hooks, "constructor" | "options" | "block">; + export type HooksObject = { + [K in keyof HooksApi]?: (this: _Hooks, ...args: Parameters) => ReturnType | Promise>; + }; + export type RendererApi = Omit<_Renderer, "constructor" | "options" | "parser">; + export type RendererObject = { + [K in keyof RendererApi]?: (this: _Renderer, ...args: Parameters) => ReturnType | false; + }; + export type TokenizerApi = Omit<_Tokenizer, "constructor" | "options" | "rules" | "lexer">; + export type TokenizerObject = { + [K in keyof TokenizerApi]?: (this: _Tokenizer, ...args: Parameters) => ReturnType | false; + }; + export interface MarkedExtension { + /** + * True will tell marked to await any walkTokens functions before parsing the tokens and returning an HTML string. + */ + async?: boolean; + /** + * Enable GFM line breaks. This option requires the gfm option to be true. + */ + breaks?: boolean; + /** + * Add tokenizers and renderers to marked + */ + extensions?: TokenizerAndRendererExtension[] | null; + /** + * Enable GitHub flavored markdown. + */ + gfm?: boolean; + /** + * Hooks are methods that hook into some part of marked. + * preprocess is called to process markdown before sending it to marked. + * processAllTokens is called with the TokensList before walkTokens. + * postprocess is called to process html after marked has finished parsing. + * provideLexer is called to provide a function to tokenize markdown. + * provideParser is called to provide a function to parse tokens. + */ + hooks?: HooksObject | null; + /** + * Conform to obscure parts of markdown.pl as much as possible. Don't fix any of the original markdown bugs or poor behavior. + */ + pedantic?: boolean; + /** + * Type: object Default: new Renderer() + * + * An object containing functions to render tokens to HTML. + */ + renderer?: RendererObject | null; + /** + * Shows an HTML error message when rendering fails. + */ + silent?: boolean; + /** + * The tokenizer defines how to turn markdown text into tokens. + */ + tokenizer?: TokenizerObject | null; + /** + * The walkTokens function gets called with every token. + * Child tokens are called before moving on to sibling tokens. + * Each token is passed by reference so updates are persisted when passed to the parser. + * The return value of the function is ignored. + */ + walkTokens?: ((token: Token) => void | Promise) | null; + } + export interface MarkedOptions extends Omit { + /** + * Hooks are methods that hook into some part of marked. + */ + hooks?: _Hooks | null; + /** + * Type: object Default: new Renderer() + * + * An object containing functions to render tokens to HTML. + */ + renderer?: _Renderer | null; + /** + * The tokenizer defines how to turn markdown text into tokens. + */ + tokenizer?: _Tokenizer | null; + /** + * Custom extensions + */ + extensions?: null | { + renderers: { + [name: string]: RendererExtensionFunction; + }; + childTokens: { + [name: string]: string[]; + }; + inline?: TokenizerExtensionFunction[]; + block?: TokenizerExtensionFunction[]; + startInline?: TokenizerStartFunction[]; + startBlock?: TokenizerStartFunction[]; + }; + /** + * walkTokens function returns array of values for Promise.all + */ + walkTokens?: null | ((token: Token) => void | Promise | (void | Promise)[]); + } + /** + * Block Lexer + */ + class _Lexer { + tokens: TokensList; + options: MarkedOptions; + state: { + inLink: boolean; + inRawBlock: boolean; + top: boolean; + }; + private tokenizer; + private inlineQueue; + constructor(options?: MarkedOptions); + /** + * Expose Rules + */ + static get rules(): { + block: { + normal: { + blockquote: RegExp; + code: RegExp; + def: RegExp; + fences: RegExp; + heading: RegExp; + hr: RegExp; + html: RegExp; + lheading: RegExp; + list: RegExp; + newline: RegExp; + paragraph: RegExp; + table: RegExp; + text: RegExp; + }; + gfm: Record<"code" | "blockquote" | "hr" | "html" | "table" | "text" | "def" | "heading" | "list" | "paragraph" | "fences" | "lheading" | "newline", RegExp>; + pedantic: Record<"code" | "blockquote" | "hr" | "html" | "table" | "text" | "def" | "heading" | "list" | "paragraph" | "fences" | "lheading" | "newline", RegExp>; + }; + inline: { + normal: { + _backpedal: RegExp; + anyPunctuation: RegExp; + autolink: RegExp; + blockSkip: RegExp; + br: RegExp; + code: RegExp; + del: RegExp; + emStrongLDelim: RegExp; + emStrongRDelimAst: RegExp; + emStrongRDelimUnd: RegExp; + escape: RegExp; + link: RegExp; + nolink: RegExp; + punctuation: RegExp; + reflink: RegExp; + reflinkSearch: RegExp; + tag: RegExp; + text: RegExp; + url: RegExp; + }; + gfm: Record<"link" | "code" | "url" | "br" | "del" | "text" | "escape" | "tag" | "reflink" | "nolink" | "_backpedal" | "anyPunctuation" | "autolink" | "blockSkip" | "emStrongLDelim" | "emStrongRDelimAst" | "emStrongRDelimUnd" | "punctuation" | "reflinkSearch", RegExp>; + breaks: Record<"link" | "code" | "url" | "br" | "del" | "text" | "escape" | "tag" | "reflink" | "nolink" | "_backpedal" | "anyPunctuation" | "autolink" | "blockSkip" | "emStrongLDelim" | "emStrongRDelimAst" | "emStrongRDelimUnd" | "punctuation" | "reflinkSearch", RegExp>; + pedantic: Record<"link" | "code" | "url" | "br" | "del" | "text" | "escape" | "tag" | "reflink" | "nolink" | "_backpedal" | "anyPunctuation" | "autolink" | "blockSkip" | "emStrongLDelim" | "emStrongRDelimAst" | "emStrongRDelimUnd" | "punctuation" | "reflinkSearch", RegExp>; + }; + }; + /** + * Static Lex Method + */ + static lex(src: string, options?: MarkedOptions): TokensList; + /** + * Static Lex Inline Method + */ + static lexInline(src: string, options?: MarkedOptions): Token[]; + /** + * Preprocessing + */ + lex(src: string): TokensList; + /** + * Lexing + */ + blockTokens(src: string, tokens?: Token[], lastParagraphClipped?: boolean): Token[]; + blockTokens(src: string, tokens?: TokensList, lastParagraphClipped?: boolean): TokensList; + inline(src: string, tokens?: Token[]): Token[]; + /** + * Lexing/Compiling + */ + inlineTokens(src: string, tokens?: Token[]): Token[]; + } + /** + * Gets the original marked default options. + */ + function _getDefaults(): MarkedOptions; + let _defaults: MarkedOptions; + export type MaybePromise = void | Promise; + export class Marked { + defaults: MarkedOptions; + options: (opt: MarkedOptions) => this; + parse: { + (src: string, options: MarkedOptions & { + async: true; + }): Promise; + (src: string, options: MarkedOptions & { + async: false; + }): string; + (src: string, options?: MarkedOptions | null): string | Promise; + }; + parseInline: { + (src: string, options: MarkedOptions & { + async: true; + }): Promise; + (src: string, options: MarkedOptions & { + async: false; + }): string; + (src: string, options?: MarkedOptions | null): string | Promise; + }; + Parser: typeof _Parser; + Renderer: typeof _Renderer; + TextRenderer: typeof _TextRenderer; + Lexer: typeof _Lexer; + Tokenizer: typeof _Tokenizer; + Hooks: typeof _Hooks; + constructor(...args: MarkedExtension[]); + /** + * Run callback for every token + */ + walkTokens(tokens: Token[] | TokensList, callback: (token: Token) => MaybePromise | MaybePromise[]): MaybePromise[]; + use(...args: MarkedExtension[]): this; + setOptions(opt: MarkedOptions): this; + lexer(src: string, options?: MarkedOptions): TokensList; + parser(tokens: Token[], options?: MarkedOptions): string; + private parseMarkdown; + private onError; + } + /** + * Compiles markdown to HTML asynchronously. + * + * @param src String of markdown source to be compiled + * @param options Hash of options, having async: true + * @return Promise of string of compiled HTML + */ + export function marked(src: string, options: MarkedOptions & { + async: true; + }): Promise; + /** + * Compiles markdown to HTML. + * + * @param src String of markdown source to be compiled + * @param options Optional hash of options + * @return String of compiled HTML. Will be a Promise of string if async is set to true by any extensions. + */ + export function marked(src: string, options: MarkedOptions & { + async: false; + }): string; + export function marked(src: string, options: MarkedOptions & { + async: true; + }): Promise; + export function marked(src: string, options?: MarkedOptions | null): string | Promise; + export namespace marked { + var options: (options: MarkedOptions) => typeof marked; + var setOptions: (options: MarkedOptions) => typeof marked; + var getDefaults: typeof _getDefaults; + var defaults: MarkedOptions; + var use: (...args: MarkedExtension[]) => typeof marked; + var walkTokens: (tokens: Token[] | TokensList, callback: (token: Token) => MaybePromise | MaybePromise[]) => MaybePromise[]; + var parseInline: { + (src: string, options: MarkedOptions & { + async: true; + }): Promise; + (src: string, options: MarkedOptions & { + async: false; + }): string; + (src: string, options?: MarkedOptions | null): string | Promise; + }; + var Parser: typeof _Parser; + var parser: typeof _Parser.parse; + var Renderer: typeof _Renderer; + var TextRenderer: typeof _TextRenderer; + var Lexer: typeof _Lexer; + var lexer: typeof _Lexer.lex; + var Tokenizer: typeof _Tokenizer; + var Hooks: typeof _Hooks; + var parse: typeof marked; + } + export const options: (options: MarkedOptions) => typeof marked; + export const setOptions: (options: MarkedOptions) => typeof marked; + export const use: (...args: MarkedExtension[]) => typeof marked; + export const walkTokens: (tokens: Token[] | TokensList, callback: (token: Token) => MaybePromise | MaybePromise[]) => MaybePromise[]; + export const parseInline: { + (src: string, options: MarkedOptions & { + async: true; + }): Promise; + (src: string, options: MarkedOptions & { + async: false; + }): string; + (src: string, options?: MarkedOptions | null): string | Promise; + }; + export const parse: typeof marked; + export const parser: typeof _Parser.parse; + export const lexer: typeof _Lexer.lex; + + export { + _Hooks as Hooks, + _Lexer as Lexer, + _Parser as Parser, + _Renderer as Renderer, + _TextRenderer as TextRenderer, + _Tokenizer as Tokenizer, + _defaults as defaults, + _getDefaults as getDefaults, + }; +} +/** +* Read and write data to SQLite databases. +* +* @module jamfile:sqlite +*/ +declare module 'jamfile:sqlite'{ + + /** + * SQLite library version string + */ + export const SQLITE_VERSION: string; + + export type SqlValue = + | number + | string + | Uint8Array // BLOB or // sqlite-vec bit vector + | Float32Array // sqlite-vec float32 vector + | Int8Array // sqlite-vec int8 vector + | null; + + export type SqlParameterValue = + | number + | string + | null + | Uint8Array + | ArrayBufferView + | Date; + + /** + * "Escapes" a astring for usage as an identifier in SQL. Must be wrapped by double quotes. + * + * Internally uses the "%w" substitution in SQLite's custom printf https://www.sqlite.org/printf.html#percentw + * + * @param identifier string identifier to escape. + */ + export function escapeIdentifier(identifier: string): string; + + export class Database { + /** + * Opens a SQLite database. + * + * @param path The path of the database. + */ + constructor(path: string); + + /** + * Execute a SQL query and return the first value of the first row. + * + * Throws an error in the following cases: + * - Preparing the SQL query fails + * - The SQL query doesn't return any rows + * - The SQL query is not read-only + * @param sql + * @param params + */ + queryValue(sql: string, params?: SqlParameterValue[]) : SqlValue; + queryRow(sql: string, params?: SqlParameterValue[]) : {[key: string]: SqlValue}; + queryAll(sql: string, params?: SqlParameterValue[]) : {[key: string]: SqlValue}[]; + + execute(sql: string, params?: SqlParameterValue[]): void; + executeScript(sql: string): void; + + } +} + + +declare module 'jamfile:llamafile' { + + export class TextEmbeddingModel { + constructor(path?:string); + + /** + * number of dimensions per embedding, ex 386, 768, 1024, etc + */ + dimensions: number; + + embed(input: string): Float32Array; + tokenize(input: string): Int32Array; + detokenize(input: Int32Array): string; + } + + export type CompletionOptions = { + schema: JSONSchema7 | string, + parse: (response:string) => T; + }; + + export class CompletionModel { + /** + * A string description the "type" of the underyling model. + * + * Result of the `llama_model_desc()` C function. + * + */ + description: string; + + constructor(model_path?: string); + + complete(input: string, options?: CompletionOptions): string | T; + tokenize(input: string): Int32Array; + detokenize(input: Int32Array): string; + } +} + +declare module 'jamfile:cli' { + /** Combines recursively all intersection types and returns a new single type. + * @internal + */ + type Id = TRecord extends Record + ? TRecord extends infer InferredRecord + ? { [Key in keyof InferredRecord]: Id } + : never + : TRecord; + + /** Converts a union type `A | B | C` into an intersection type `A & B & C`. + * @internal + */ + type UnionToIntersection = + (TValue extends unknown ? (args: TValue) => unknown : never) extends + (args: infer R) => unknown ? R extends Record ? R : never + : never; + + /** @internal */ + type BooleanType = boolean | string | undefined; + /** @internal */ + type StringType = string | undefined; + /** @internal */ + type ArgType = StringType | BooleanType; + + /** @internal */ + type Collectable = string | undefined; + /** @internal */ + type Negatable = string | undefined; + + type UseTypes< + TBooleans extends BooleanType, + TStrings extends StringType, + TCollectable extends Collectable, + > = undefined extends ( + & (false extends TBooleans ? undefined : TBooleans) + & TCollectable + & TStrings + ) ? false + : true; + + /** + * Creates a record with all available flags with the corresponding type and + * default type. + * @internal + */ + type Values< + TBooleans extends BooleanType, + TStrings extends StringType, + TCollectable extends Collectable, + TNegatable extends Negatable, + TDefault extends Record | undefined, + TAliases extends Aliases | undefined, + > = UseTypes extends true ? + & Record + & AddAliases< + SpreadDefaults< + & CollectValues + & RecursiveRequired> + & CollectUnknownValues< + TBooleans, + TStrings, + TCollectable, + TNegatable + >, + DedotRecord + >, + TAliases + > + // deno-lint-ignore no-explicit-any + : Record; + + /** @internal */ + type Aliases = Partial< + Record, TAliasNames | ReadonlyArray> + >; + + type AddAliases< + TArgs, + TAliases extends Aliases | undefined, + > = { + [TArgName in keyof TArgs as AliasNames]: TArgs[TArgName]; + }; + + type AliasNames< + TArgName, + TAliases extends Aliases | undefined, + > = TArgName extends keyof TAliases + ? string extends TAliases[TArgName] ? TArgName + : TAliases[TArgName] extends string ? TArgName | TAliases[TArgName] + : TAliases[TArgName] extends Array + ? TArgName | TAliases[TArgName][number] + : TArgName + : TArgName; + + /** + * Spreads all default values of Record `TDefaults` into Record `TArgs` + * and makes default values required. + * + * **Example:** + * `SpreadValues<{ foo?: boolean, bar?: number }, { foo: number }>` + * + * **Result:** `{ foo: boolean | number, bar?: number }` + */ + type SpreadDefaults = TDefaults extends undefined ? TArgs + : TArgs extends Record ? + & Omit + & { + [Default in keyof TDefaults]: Default extends keyof TArgs + ? (TArgs[Default] & TDefaults[Default] | TDefaults[Default]) extends + Record + ? NonNullable> + : TDefaults[Default] | NonNullable + : unknown; + } + : never; + + /** + * Defines the Record for the `default` option to add + * auto-suggestion support for IDE's. + * @internal + */ + type Defaults = Id< + UnionToIntersection< + & Record + // Dedotted auto suggestions: { foo: { bar: unknown } } + & MapTypes + & MapTypes + // Flat auto suggestions: { "foo.bar": unknown } + & MapDefaults + & MapDefaults + > + >; + + type MapDefaults = Partial< + Record + >; + + type RecursiveRequired = TRecord extends Record ? { + [Key in keyof TRecord]-?: RecursiveRequired; + } + : TRecord; + + /** Same as `MapTypes` but also supports collectable options. */ + type CollectValues< + TArgNames extends ArgType, + TType, + TCollectable extends Collectable, + TNegatable extends Negatable = undefined, + > = UnionToIntersection< + Extract extends string ? + & (Exclude extends never ? Record + : MapTypes, TType, TNegatable>) + & (Extract extends never ? Record + : RecursiveRequired< + MapTypes, Array, TNegatable> + >) + : MapTypes + >; + + /** Same as `Record` but also supports dotted and negatable options. */ + type MapTypes< + TArgNames extends ArgType, + TType, + TNegatable extends Negatable = undefined, + > = undefined extends TArgNames ? Record + : TArgNames extends `${infer Name}.${infer Rest}` ? { + [Key in Name]?: MapTypes< + Rest, + TType, + TNegatable extends `${Name}.${infer Negate}` ? Negate : undefined + >; + } + : TArgNames extends string ? Partial< + Record + > + : Record; + + type CollectUnknownValues< + TBooleans extends BooleanType, + TStrings extends StringType, + TCollectable extends Collectable, + TNegatable extends Negatable, + > = UnionToIntersection< + TCollectable extends TBooleans & TStrings ? Record + : DedotRecord< + // Unknown collectable & non-negatable args. + & Record< + Exclude< + Extract, string>, + Extract + >, + Array + > + // Unknown collectable & negatable args. + & Record< + Exclude< + Extract, string>, + Extract + >, + Array | false + > + > + >; + + /** Converts `{ "foo.bar.baz": unknown }` into `{ foo: { bar: { baz: unknown } } }`. */ + type DedotRecord = Record extends TRecord ? TRecord + : TRecord extends Record ? UnionToIntersection< + ValueOf< + { + [Key in keyof TRecord]: Key extends string ? Dedot + : never; + } + > + > + : TRecord; + + type Dedot = TKey extends + `${infer Name}.${infer Rest}` ? { [Key in Name]: Dedot } + : { [Key in TKey]: TValue }; + + type ValueOf = TValue[keyof TValue]; + + /** The value returned from {@linkcode parseArgs}. */ + export type Args< + // deno-lint-ignore no-explicit-any + TArgs extends Record = Record, + TDoubleDash extends boolean | undefined = undefined, + > = Id< + & TArgs + & { + /** Contains all the arguments that didn't have an option associated with + * them. */ + _: Array; + } + & (boolean extends TDoubleDash ? DoubleDash + : true extends TDoubleDash ? Required + : Record) + >; + + /** @internal */ + type DoubleDash = { + /** Contains all the arguments that appear after the double dash: "--". */ + "--"?: Array; + }; + + /** Options for {@linkcode parseArgs}. */ + export interface ParseOptions< + TBooleans extends BooleanType = BooleanType, + TStrings extends StringType = StringType, + TCollectable extends Collectable = Collectable, + TNegatable extends Negatable = Negatable, + TDefault extends Record | undefined = + | Record + | undefined, + TAliases extends Aliases | undefined = Aliases | undefined, + TDoubleDash extends boolean | undefined = boolean | undefined, + > { + /** + * When `true`, populate the result `_` with everything before the `--` and + * the result `['--']` with everything after the `--`. + * + * @default {false} + * + * @example + * ```ts + * // $ deno run example.ts -- a arg1 + * import { parseArgs } from "@std/cli/parse-args"; + * console.dir(parseArgs(Deno.args, { "--": false })); + * // output: { _: [ "a", "arg1" ] } + * console.dir(parseArgs(Deno.args, { "--": true })); + * // output: { _: [], --: [ "a", "arg1" ] } + * ``` + */ + "--"?: TDoubleDash; + + /** + * An object mapping string names to strings or arrays of string argument + * names to use as aliases. + * + * @default {{}} + */ + alias?: TAliases; + + /** + * A boolean, string or array of strings to always treat as booleans. If + * `true` will treat all double hyphenated arguments without equal signs as + * `boolean` (e.g. affects `--foo`, not `-f` or `--foo=bar`). + * All `boolean` arguments will be set to `false` by default. + * + * @default {false} + */ + boolean?: TBooleans | ReadonlyArray>; + + /** + * An object mapping string argument names to default values. + * + * @default {{}} + */ + default?: TDefault & Defaults; + + /** + * When `true`, populate the result `_` with everything after the first + * non-option. + * + * @default {false} + */ + stopEarly?: boolean; + + /** + * A string or array of strings argument names to always treat as strings. + * + * @default {[]} + */ + string?: TStrings | ReadonlyArray>; + + /** + * A string or array of strings argument names to always treat as arrays. + * Collectable options can be used multiple times. All values will be + * collected into one array. If a non-collectable option is used multiple + * times, the last value is used. + * + * @default {[]} + */ + collect?: TCollectable | ReadonlyArray>; + + /** + * A string or array of strings argument names which can be negated + * by prefixing them with `--no-`, like `--no-config`. + * + * @default {[]} + */ + negatable?: TNegatable | ReadonlyArray>; + + /** + * A function which is invoked with a command line parameter not defined in + * the `options` configuration object. If the function returns `false`, the + * unknown option is not added to `parsedArgs`. + * + * @default {unknown} + */ + unknown?: (arg: string, key?: string, value?: unknown) => unknown; + } + + interface NestedMapping { + [key: string]: NestedMapping | unknown; + } + + /** + * Take a set of command line arguments, optionally with a set of options, and + * return an object representing the flags found in the passed arguments. + * + * By default, any arguments starting with `-` or `--` are considered boolean + * flags. If the argument name is followed by an equal sign (`=`) it is + * considered a key-value pair. Any arguments which could not be parsed are + * available in the `_` property of the returned object. + * + * By default, this module tries to determine the type of all arguments + * automatically and the return type of this function will have an index + * signature with `any` as value (`{ [x: string]: any }`). + * + * If the `string`, `boolean` or `collect` option is set, the return value of + * this function will be fully typed and the index signature of the return + * type will change to `{ [x: string]: unknown }`. + * + * Any arguments after `'--'` will not be parsed and will end up in `parsedArgs._`. + * + * Numeric-looking arguments will be returned as numbers unless `options.string` + * or `options.boolean` is set for that argument name. + * + * @param args An array of command line arguments. + * @param options Options for the parse function. + * + * @typeParam TArgs Type of result. + * @typeParam TDoubleDash Used by `TArgs` for the result. + * @typeParam TBooleans Used by `TArgs` for the result. + * @typeParam TStrings Used by `TArgs` for the result. + * @typeParam TCollectable Used by `TArgs` for the result. + * @typeParam TNegatable Used by `TArgs` for the result. + * @typeParam TDefaults Used by `TArgs` for the result. + * @typeParam TAliases Used by `TArgs` for the result. + * @typeParam TAliasArgNames Used by `TArgs` for the result. + * @typeParam TAliasNames Used by `TArgs` for the result. + * + * @return The parsed arguments. + * + * @example Usage + * ```ts + * import { parseArgs } from "@std/cli/parse-args"; + * import { assertEquals } from "@std/assert"; + * + * // For proper use, one should use `parseArgs(Deno.args)` + * assertEquals(parseArgs(["--foo", "--bar=baz", "./quux.txt"]), { + * foo: true, + * bar: "baz", + * _: ["./quux.txt"], + * }); + * ``` + */ + export function parseArgs< + TArgs extends Values< + TBooleans, + TStrings, + TCollectable, + TNegatable, + TDefaults, + TAliases + >, + TDoubleDash extends boolean | undefined = undefined, + TBooleans extends BooleanType = undefined, + TStrings extends StringType = undefined, + TCollectable extends Collectable = undefined, + TNegatable extends Negatable = undefined, + TDefaults extends Record | undefined = undefined, + TAliases extends Aliases | undefined = undefined, + TAliasArgNames extends string = string, + TAliasNames extends string = string, + >( + args: string[], + options?: ParseOptions< + TBooleans, + TStrings, + TCollectable, + TNegatable, + TDefaults, + TAliases, + TDoubleDash + >, + ): Args; +} + +declare module 'jamfile:fmt' { + + /** Options for {@linkcode format}. */ + export interface FormatDurationOptions { + /** + * The style for formatting the duration. + * + * "narrow" for "0d 0h 0m 0s 0ms..." + * "digital" for "00:00:00:00:000..." + * "full" for "0 days, 0 hours, 0 minutes,..." + * + * @default {"narrow"} + */ + style?: "narrow" | "digital" | "full"; + /** + * Whether to ignore zero values. + * With style="narrow" | "full", all zero values are ignored. + * With style="digital", only values in the ends are ignored. + * + * @default {false} + */ + ignoreZero?: boolean; +} + + /** + * Format milliseconds to time duration. + * + * @param ms The milliseconds value to format + * @param options The options for formatting + * @returns The formatted string + */ + export function formatDuration( + ms: number, + options?: FormatDurationOptions, + ): string; + + + type LocaleOptions = { + minimumFractionDigits?: number; + maximumFractionDigits?: number; + }; + + /** Options for {@linkcode format}. */ + export interface FormatBytesOptions { + /** + * Uses bits representation. + * + * @default {false} + */ + bits?: boolean; + /** + * Uses binary bytes (e.g. kibibyte). + * + * @default {false} + */ + binary?: boolean; + /** + * Include plus sign for positive numbers. + * + * @default {false} + */ + signed?: boolean; + /** + * Uses localized number formatting. If it is set to true, uses default + * locale on the system. If it's set to string, uses that locale. The given + * string should be a + * {@link https://en.wikipedia.org/wiki/IETF_language_tag | BCP 47 language tag}. + * You can also give the list of language tags. + */ + locale?: boolean | string | string[]; + /** + * The minimum number of fraction digits to display. If neither + * {@linkcode minimumFractionDigits} or {@linkcode maximumFractionDigits} + * are set. + * + * @default {3} + */ + minimumFractionDigits?: number; + /** + * The maximum number of fraction digits to display. If neither + * {@linkcode minimumFractionDigits} or {@linkcode maximumFractionDigits} + * are set. + * + * @default {3} + */ + maximumFractionDigits?: number; + } + + /** + * Convert bytes to a human-readable string: 1337 → 1.34 kB + * + * Based on {@link https://github.com/sindresorhus/pretty-bytes | pretty-bytes}. + * A utility for displaying file sizes for humans. + * + * @param num The bytes value to format + * @param options The options for formatting + * @returns The formatted string + * + * @example Basic usage + * ```ts + * import { formatBytes } from "jamfile:fmt"; + * + * formatBytes(1337); // "1.34 kB" + * formatBytes(100); // "100 B" + * ``` + * + + */ +export function formatBytes( + num: number, + options?: FormatBytesOptions, +): string; +} + + +declare module 'jamfile:assert' { + +type T = any; +/** + * Make an assertion that `actual` and `expected` are almost equal numbers +through a given tolerance. It can be used to take into account IEEE-754 +double-precision floating-point representation limitations. If the values +are not almost equal then throw. + +The default tolerance is one hundred thousandth of a percent of the +expected value. + * @param actual The actual value to compare. + * @param expected The expected value to compare. + * @param tolerance The tolerance to consider the values almost equal. The +default is one hundred thousandth of a percent of the expected value. + * @param msg The optional message to include in the error. + */ +export function assertAlmostEquals(actual: number, expected: number, tolerance: number, msg?: string): void; + +/** + * Make an assertion that `actual` includes the `expected` values. If not then +an error will be thrown. + +Type parameter can be specified to ensure values under comparison have the +same type. + * @param actual The array-like object to check for. + * @param expected The array-like object to check for. + * @param msg The optional message to display if the assertion fails. + */ +export function assertArrayIncludes(actual: any[], expected: any[], msg?: string): void; + +/** + * Make an assertion that `actual` and `expected` are equal, deeply. If not +deeply equal, then throw. + +Type parameter can be specified to ensure values under comparison have the +same type. + +Note: When comparing `Blob` objects, you should first convert them to +`Uint8Array` using the `Blob.bytes()` method and then compare their +contents. + * @param actual The actual value to compare. + * @param expected The expected value to compare. + * @param msg The optional message to display if the assertion fails. + */ +export function assertEquals(actual: T, expected: T, msg?: string): void; + +/** + * Make an assertion that actual is not null or undefined. +If not then throw. + * @param actual The actual value to check. + * @param msg The optional message to include in the error if the assertion fails. + */ +export function assertExists(actual: T, msg?: string): asserts actual is NonNullable; + +type Falsy = false +| 0 +| 0 +| "" +| null +| undefined; + +/** + * Make an assertion, error will be thrown if `expr` have truthy value. + * @param expr The expression to test. + * @param msg The optional message to display if the assertion fails. + */ +export function assertFalse(expr: unknown, undefined: undefined): asserts expr is Falsy; + +/** + * Make an assertion that `actual` is greater than or equal to `expected`. +If not then throw. + * @param actual The actual value to compare. + * @param expected The expected value to compare. + * @param msg The optional message to display if the assertion fails. + */ +export function assertGreaterOrEqual(actual: T, expected: T, msg?: string): void; + +/** + * Make an assertion that `actual` is greater than `expected`. +If not then throw. + * @param actual The actual value to compare. + * @param expected The expected value to compare. + * @param msg The optional message to display if the assertion fails. + */ +export function assertGreater(actual: T, expected: T, msg?: string): void; + +/** + * Make an assertion that `obj` is an instance of `type`. +If not then throw. + * @param actual The object to check. + * @param expectedType The expected class constructor. + * @param msg The optional message to display if the assertion fails. + */ +export function assertInstanceOf any>(actual: unknown, expectedType: T, undefined: undefined): asserts actual is InstanceType; + +/** + * Make an assertion that `error` is an `Error`. +If not then an error will be thrown. +An error class and a string that should be included in the +error message can also be asserted. + * @param error The error to assert. + * @param ErrorClass The optional error class to assert. + * @param msgMatches The optional string or RegExp to assert in the error message. + * @param msg The optional message to display if the assertion fails. + */ +export function assertIsError(error: unknown, ErrorClass: any, msgMatches: string | RegExp, msg?: string): asserts error is E; + +/** + * Make an assertion that `actual` is less than or equal to `expected`. +If not then throw. + * @param actual The actual value to compare. + * @param expected The expected value to compare. + * @param msg The optional message to display if the assertion fails. + */ +export function assertLessOrEqual(actual: T, expected: T, msg?: string): void; + +/** + * Make an assertion that `actual` is less than `expected`. +If not then throw. + * @param actual The actual value to compare. + * @param expected The expected value to compare. + * @param msg The optional message to display if the assertion fails. + */ +export function assertLess(actual: T, expected: T, msg?: string): void; + +/** + * Make an assertion that `actual` match RegExp `expected`. If not +then throw. + * @param actual The actual value to be matched. + * @param expected The expected pattern to match. + * @param msg The optional message to display if the assertion fails. + */ +export function assertMatch(actual: string, expected: RegExp, msg?: string): void; + +/** + * Make an assertion that `actual` and `expected` are not equal, deeply. +If not then throw. + +Type parameter can be specified to ensure values under comparison have the same type. + * @param actual The actual value to compare. + * @param expected The expected value to compare. + * @param msg The optional message to display if the assertion fails. + */ +export function assertNotEquals(actual: T, expected: T, msg?: string): void; + +/** + * Make an assertion that `obj` is not an instance of `type`. +If so, then throw. + * @param actual The object to check. + * @param unexpectedType The class constructor to check against. + * @param msg The optional message to display if the assertion fails. + */ +export function assertNotInstanceOf(actual: A, unexpectedType: any, msg?: string): asserts actual is Exclude; + +/** + * Make an assertion that `actual` not match RegExp `expected`. If match +then throw. + * @param actual The actual value to match. + * @param expected The expected value to not match. + * @param msg The optional message to display if the assertion fails. + */ +export function assertNotMatch(actual: string, expected: RegExp, msg?: string): void; + +/** + * Make an assertion that `actual` and `expected` are not strictly equal, using +{@linkcode Object.is} for equality comparison. If the values are strictly +equal then throw. + * @param actual The actual value to compare. + * @param expected The expected value to compare. + * @param msg The optional message to display if the assertion fails. + */ +export function assertNotStrictEquals(actual: T, expected: T, msg?: string): void; + +/** + * Make an assertion that `expected` object is a subset of `actual` object, +deeply. If not, then throw a diff of the objects, with mismatching +properties highlighted. + * @param actual The actual value to be matched. + * @param expected The expected value to match. + * @param msg The optional message to display if the assertion fails. + */ +export function assertObjectMatch(actual: Record, expected: Record, msg?: string): void; + +/** + * Executes a function which returns a promise, expecting it to reject. + +To assert that a synchronous function throws, use {@linkcode assertThrows}. + * @param fn The function to execute. + * @param msg The optional message to display if the assertion fails. + */ +export function assertRejects(fn: () =>void, msg?: string): Promise; + +/** + * Executes a function which returns a promise, expecting it to reject. +If it does not, then it throws. An error class and a string that should be +included in the error message can also be asserted. + +To assert that a synchronous function throws, use {@linkcode assertThrows}. + * @param fn The function to execute. + * @param ErrorClass The error class to assert. + * @param msgIncludes The string that should be included in the error message. + * @param msg The optional message to display if the assertion fails. + */ +export function assertRejects(fn: () => void, ErrorClass: any, msgIncludes: string, msg?: string): Promise; + + +/** + * Make an assertion that `actual` and `expected` are strictly equal, using +{@linkcode Object.is} for equality comparison. If not, then throw. + * @param actual The actual value to compare. + * @param expected The expected value to compare. + * @param msg The optional message to display if the assertion fails. + */ +export function assertStrictEquals(actual: unknown, expected: T, msg?: string): asserts actual is T; + +/** + * Make an assertion that actual includes expected. If not +then throw. + * @param actual The actual string to check for inclusion. + * @param expected The expected string to check for inclusion. + * @param msg The optional message to display if the assertion fails. + */ +export function assertStringIncludes(actual: string, expected: string, msg?: string): void; + +/** + * Executes a function, expecting it to throw. If it does not, then it +throws. + +To assert that an asynchronous function rejects, use +{@linkcode assertRejects}. + * @param fn The function to execute. + * @param msg The optional message to display if the assertion fails. + */ +export function assertThrows(fn: () => void, msg?: string): unknown; + +/** + * Executes a function, expecting it to throw. If it does not, then it +throws. An error class and a string that should be included in the +error message can also be asserted. + +To assert that an asynchronous function rejects, use +{@linkcode assertRejects}. + * @param fn The function to execute. + * @param ErrorClass The error class to assert. + * @param msgIncludes The string that should be included in the error message. + * @param msg The optional message to display if the assertion fails. + */ +export function assertThrows(fn: () => void, ErrorClass: any, msgIncludes: string, msg?: string): E; + +/** + * undefined + */ +export function assertThrows(fn: () => void, errorClassOrMsg: any | string, msgIncludesOrMsg?: string, msg?: string):any; + +/** + * Make an assertion, error will be thrown if `expr` does not have truthy value. + * @param expr The expression to test. + * @param msg The optional message to display if the assertion fails. + */ +export function assert(expr: unknown, undefined: undefined): asserts expr; + +/** + * Deep equality comparison used in assertions. + * @param a The actual value + * @param b The expected value + */ +export function equal(a: unknown, b: unknown): boolean; + +/** + * Forcefully throws a failed assertion. + * @param msg Optional message to include in the error. + */ +export function fail(msg?: string): never; + +/** + * Use this to stub out methods that will throw when invoked. + * @param msg Optional message to include in the error. + */ +export function unimplemented(msg?: string): never; + +/** + * Use this to assert unreachable code. + * @param msg Optional message to include in the error. + */ +export function unreachable(msg?: string): never; + +} +declare module 'jamfile:color' { + + /** + * Enable or disable text color when styling. + + `@std/fmt/colors` automatically detects NO_COLOR environmental variable + and disables text color. Use this API only when the automatic detection + doesn't work. + * @param value The boolean value to enable or disable text color + */ + export function setColorEnabled(value: boolean): void; + + /** + * Get whether text color change is enabled or disabled. + */ + export function getColorEnabled(): boolean; + + /** + * Reset the text modified. + * @param str The text to reset + */ + export function reset(str: string): string; + + /** + * Make the text bold. + * @param str The text to make bold + */ + export function bold(str: string): string; + + /** + * The text emits only a small amount of light. + * @param str The text to dim + */ + export function dim(str: string): string; + + /** + * Make the text italic. + * @param str The text to make italic + */ + export function italic(str: string): string; + + /** + * Make the text underline. + * @param str The text to underline + */ + export function underline(str: string): string; + + /** + * Invert background color and text color. + * @param str The text to invert its color + */ + export function inverse(str: string): string; + + /** + * Make the text hidden. + * @param str The text to hide + */ + export function hidden(str: string): string; + + /** + * Put horizontal line through the center of the text. + * @param str The text to strike through + */ + export function strikethrough(str: string): string; + + /** + * Set text color to black. + * @param str The text to make black + */ + export function black(str: string): string; + + /** + * Set text color to red. + * @param str The text to make red + */ + export function red(str: string): string; + + /** + * Set text color to green. + * @param str The text to make green + */ + export function green(str: string): string; + + /** + * Set text color to yellow. + * @param str The text to make yellow + */ + export function yellow(str: string): string; + + /** + * Set text color to blue. + * @param str The text to make blue + */ + export function blue(str: string): string; + + /** + * Set text color to magenta. + * @param str The text to make magenta + */ + export function magenta(str: string): string; + + /** + * Set text color to cyan. + * @param str The text to make cyan + */ + export function cyan(str: string): string; + + /** + * Set text color to white. + * @param str The text to make white + */ + export function white(str: string): string; + + /** + * Set text color to gray. + * @param str The text to make gray + */ + export function gray(str: string): string; + + /** + * Set text color to bright black. + * @param str The text to make bright black + */ + export function brightBlack(str: string): string; + + /** + * Set text color to bright red. + * @param str The text to make bright red + */ + export function brightRed(str: string): string; + + /** + * Set text color to bright green. + * @param str The text to make bright green + */ + export function brightGreen(str: string): string; + + /** + * Set text color to bright yellow. + * @param str The text to make bright yellow + */ + export function brightYellow(str: string): string; + + /** + * Set text color to bright blue. + * @param str The text to make bright blue + */ + export function brightBlue(str: string): string; + + /** + * Set text color to bright magenta. + * @param str The text to make bright magenta + */ + export function brightMagenta(str: string): string; + + /** + * Set text color to bright cyan. + * @param str The text to make bright cyan + */ + export function brightCyan(str: string): string; + + /** + * Set text color to bright white. + * @param str The text to make bright white + */ + export function brightWhite(str: string): string; + + /** + * Set background color to black. + * @param str The text to make its background black + */ + export function bgBlack(str: string): string; + + /** + * Set background color to red. + * @param str The text to make its background red + */ + export function bgRed(str: string): string; + + /** + * Set background color to green. + * @param str The text to make its background green + */ + export function bgGreen(str: string): string; + + /** + * Set background color to yellow. + * @param str The text to make its background yellow + */ + export function bgYellow(str: string): string; + + /** + * Set background color to blue. + * @param str The text to make its background blue + */ + export function bgBlue(str: string): string; + + /** + * Set background color to magenta. + * @param str The text to make its background magenta + */ + export function bgMagenta(str: string): string; + + /** + * Set background color to cyan. + * @param str The text to make its background cyan + */ + export function bgCyan(str: string): string; + + /** + * Set background color to white. + * @param str The text to make its background white + */ + export function bgWhite(str: string): string; + + /** + * Set background color to bright black. + * @param str The text to make its background bright black + */ + export function bgBrightBlack(str: string): string; + + /** + * Set background color to bright red. + * @param str The text to make its background bright red + */ + export function bgBrightRed(str: string): string; + + /** + * Set background color to bright green. + * @param str The text to make its background bright green + */ + export function bgBrightGreen(str: string): string; + + /** + * Set background color to bright yellow. + * @param str The text to make its background bright yellow + */ + export function bgBrightYellow(str: string): string; + + /** + * Set background color to bright blue. + * @param str The text to make its background bright blue + */ + export function bgBrightBlue(str: string): string; + + /** + * Set background color to bright magenta. + * @param str The text to make its background bright magenta + */ + export function bgBrightMagenta(str: string): string; + + /** + * Set background color to bright cyan. + * @param str The text to make its background bright cyan + */ + export function bgBrightCyan(str: string): string; + + /** + * Set background color to bright white. + * @param str The text to make its background bright white + */ + export function bgBrightWhite(str: string): string; + + /** + * Set text color using paletted 8bit colors. + https://en.wikipedia.org/wiki/ANSI_escape_code#8-bit + * @param str The text color to apply paletted 8bit colors to + * @param color The color code + */ + export function rgb8(str: string, color: number): string; + + /** + * Set background color using paletted 8bit colors. + https://en.wikipedia.org/wiki/ANSI_escape_code#8-bit + * @param str The text color to apply paletted 8bit background colors to + * @param color code + */ + export function bgRgb8(str: string, color: number): string; + + /** RGB 8-bits per channel. Each in range `0->255` or `0x00->0xff` */ + export interface Rgb { + /** Red component value */ + r: number; + /** Green component value */ + g: number; + /** Blue component value */ + b: number; + } + + /** + * Set text color using 24bit rgb. + `color` can be a number in range `0x000000` to `0xffffff` or + an `Rgb`. + * @param str The text color to apply 24bit rgb to + * @param color The color code + */ + export function rgb24(str: string, color: number | Rgb): string; + + /** + * Set background color using 24bit rgb. + `color` can be a number in range `0x000000` to `0xffffff` or + an `Rgb`. + * @param str The text color to apply 24bit rgb to + * @param color The color code + */ + export function bgRgb24(str: string, color: number | Rgb): string; + + /** + * Remove ANSI escape codes from the string. + * @param string The text to remove ANSI escape codes from + */ + export function stripAnsiCode(string: string): string; + +} + +declare module "jamfile:zod" { + export function zodToJsonSchema(zod: ZodType):JSONSchema7; + + type Primitive = string | number | symbol | bigint | boolean | null | undefined; +type Scalars = Primitive | Primitive[]; + +namespace util { + type AssertEqual = (() => V extends T ? 1 : 2) extends () => V extends U ? 1 : 2 ? true : false; + export type isAny = 0 extends 1 & T ? true : false; + export const assertEqual: (val: AssertEqual) => AssertEqual; + export function assertIs(_arg: T): void; + export function assertNever(_x: never): never; + export type Omit = Pick>; + export type OmitKeys = Pick>; + export type MakePartial = Omit & Partial>; + export type Exactly = T & Record, never>; + export const arrayToEnum: (items: U) => { [k in U[number]]: k; }; + export const getValidEnumValues: (obj: any) => any[]; + export const objectValues: (obj: any) => any[]; + export const objectKeys: ObjectConstructor["keys"]; + export const find: (arr: T[], checker: (arg: T) => any) => T | undefined; + export type identity = objectUtil.identity; + export type flatten = objectUtil.flatten; + export type noUndefined = T extends undefined ? never : T; + export const isInteger: NumberConstructor["isInteger"]; + export function joinValues(array: T, separator?: string): string; + export const jsonStringifyReplacer: (_: string, value: any) => any; + export { }; +} +namespace objectUtil { + export type MergeShapes = { + [k in Exclude]: U[k]; + } & V; + type optionalKeys = { + [k in keyof T]: undefined extends T[k] ? k : never; + }[keyof T]; + type requiredKeys = { + [k in keyof T]: undefined extends T[k] ? never : k; + }[keyof T]; + export type addQuestionMarks = { + [K in requiredKeys]: T[K]; + } & { + [K in optionalKeys]?: T[K]; + } & { + [k in keyof T]?: unknown; + }; + export type identity = T; + export type flatten = identity<{ + [k in keyof T]: T[k]; + }>; + export type noNeverKeys = { + [k in keyof T]: [T[k]] extends [never] ? never : k; + }[keyof T]; + export type noNever = identity<{ + [k in noNeverKeys]: k extends keyof T ? T[k] : never; + }>; + export const mergeShapes: (first: U, second: T) => T & U; + export type extendShape = { + [K in keyof A as K extends keyof B ? never : K]: A[K]; + } & { + [K in keyof B]: B[K]; + }; + export { }; +} +const ZodParsedType: { + string: "string"; + number: "number"; + bigint: "bigint"; + boolean: "boolean"; + symbol: "symbol"; + undefined: "undefined"; + object: "object"; + function: "function"; + map: "map"; + nan: "nan"; + integer: "integer"; + float: "float"; + date: "date"; + null: "null"; + array: "array"; + unknown: "unknown"; + promise: "promise"; + void: "void"; + never: "never"; + set: "set"; +}; +type ZodParsedType = keyof typeof ZodParsedType; +const getParsedType: (data: any) => ZodParsedType; + +type allKeys = T extends any ? keyof T : never; +type inferFlattenedErrors, U = string> = typeToFlattenedError, U>; +type typeToFlattenedError = { + formErrors: U[]; + fieldErrors: { + [P in allKeys]?: U[]; + }; +}; +const ZodIssueCode: { + invalid_type: "invalid_type"; + invalid_literal: "invalid_literal"; + custom: "custom"; + invalid_union: "invalid_union"; + invalid_union_discriminator: "invalid_union_discriminator"; + invalid_enum_value: "invalid_enum_value"; + unrecognized_keys: "unrecognized_keys"; + invalid_arguments: "invalid_arguments"; + invalid_return_type: "invalid_return_type"; + invalid_date: "invalid_date"; + invalid_string: "invalid_string"; + too_small: "too_small"; + too_big: "too_big"; + invalid_intersection_types: "invalid_intersection_types"; + not_multiple_of: "not_multiple_of"; + not_finite: "not_finite"; +}; +type ZodIssueCode = keyof typeof ZodIssueCode; +type ZodIssueBase = { + path: (string | number)[]; + message?: string; +}; +interface ZodInvalidTypeIssue extends ZodIssueBase { + code: typeof ZodIssueCode.invalid_type; + expected: ZodParsedType; + received: ZodParsedType; +} +interface ZodInvalidLiteralIssue extends ZodIssueBase { + code: typeof ZodIssueCode.invalid_literal; + expected: unknown; + received: unknown; +} +interface ZodUnrecognizedKeysIssue extends ZodIssueBase { + code: typeof ZodIssueCode.unrecognized_keys; + keys: string[]; +} +interface ZodInvalidUnionIssue extends ZodIssueBase { + code: typeof ZodIssueCode.invalid_union; + unionErrors: ZodError[]; +} +interface ZodInvalidUnionDiscriminatorIssue extends ZodIssueBase { + code: typeof ZodIssueCode.invalid_union_discriminator; + options: Primitive[]; +} +interface ZodInvalidEnumValueIssue extends ZodIssueBase { + received: string | number; + code: typeof ZodIssueCode.invalid_enum_value; + options: (string | number)[]; +} +interface ZodInvalidArgumentsIssue extends ZodIssueBase { + code: typeof ZodIssueCode.invalid_arguments; + argumentsError: ZodError; +} +interface ZodInvalidReturnTypeIssue extends ZodIssueBase { + code: typeof ZodIssueCode.invalid_return_type; + returnTypeError: ZodError; +} +interface ZodInvalidDateIssue extends ZodIssueBase { + code: typeof ZodIssueCode.invalid_date; +} +type StringValidation = "email" | "url" | "emoji" | "uuid" | "nanoid" | "regex" | "cuid" | "cuid2" | "ulid" | "datetime" | "date" | "time" | "duration" | "ip" | "cidr" | "base64" | "jwt" | "base64url" | { + includes: string; + position?: number; +} | { + startsWith: string; +} | { + endsWith: string; +}; +interface ZodInvalidStringIssue extends ZodIssueBase { + code: typeof ZodIssueCode.invalid_string; + validation: StringValidation; +} +interface ZodTooSmallIssue extends ZodIssueBase { + code: typeof ZodIssueCode.too_small; + minimum: number | bigint; + inclusive: boolean; + exact?: boolean; + type: "array" | "string" | "number" | "set" | "date" | "bigint"; +} +interface ZodTooBigIssue extends ZodIssueBase { + code: typeof ZodIssueCode.too_big; + maximum: number | bigint; + inclusive: boolean; + exact?: boolean; + type: "array" | "string" | "number" | "set" | "date" | "bigint"; +} +interface ZodInvalidIntersectionTypesIssue extends ZodIssueBase { + code: typeof ZodIssueCode.invalid_intersection_types; +} +interface ZodNotMultipleOfIssue extends ZodIssueBase { + code: typeof ZodIssueCode.not_multiple_of; + multipleOf: number | bigint; +} +interface ZodNotFiniteIssue extends ZodIssueBase { + code: typeof ZodIssueCode.not_finite; +} +interface ZodCustomIssue extends ZodIssueBase { + code: typeof ZodIssueCode.custom; + params?: { + [k: string]: any; + }; +} +type DenormalizedError = { + [k: string]: DenormalizedError | string[]; +}; +type ZodIssueOptionalMessage = ZodInvalidTypeIssue | ZodInvalidLiteralIssue | ZodUnrecognizedKeysIssue | ZodInvalidUnionIssue | ZodInvalidUnionDiscriminatorIssue | ZodInvalidEnumValueIssue | ZodInvalidArgumentsIssue | ZodInvalidReturnTypeIssue | ZodInvalidDateIssue | ZodInvalidStringIssue | ZodTooSmallIssue | ZodTooBigIssue | ZodInvalidIntersectionTypesIssue | ZodNotMultipleOfIssue | ZodNotFiniteIssue | ZodCustomIssue; +type ZodIssue = ZodIssueOptionalMessage & { + fatal?: boolean; + message: string; +}; +const quotelessJson: (obj: any) => string; +type recursiveZodFormattedError = T extends [any, ...any[]] ? { + [K in keyof T]?: ZodFormattedError; +} : T extends any[] ? { + [k: number]: ZodFormattedError; +} : T extends object ? { + [K in keyof T]?: ZodFormattedError; +} : unknown; +type ZodFormattedError = { + _errors: U[]; +} & recursiveZodFormattedError>; +type inferFormattedError, U = string> = ZodFormattedError, U>; +class ZodError extends Error { + issues: ZodIssue[]; + get errors(): ZodIssue[]; + constructor(issues: ZodIssue[]); + format(): ZodFormattedError; + format(mapper: (issue: ZodIssue) => U): ZodFormattedError; + static create: (issues: ZodIssue[]) => ZodError; + static assert(value: unknown): asserts value is ZodError; + toString(): string; + get message(): string; + get isEmpty(): boolean; + addIssue: (sub: ZodIssue) => void; + addIssues: (subs?: ZodIssue[]) => void; + flatten(): typeToFlattenedError; + flatten(mapper?: (issue: ZodIssue) => U): typeToFlattenedError; + get formErrors(): typeToFlattenedError; +} +type stripPath = T extends any ? util.OmitKeys : never; +type IssueData = stripPath & { + path?: (string | number)[]; + fatal?: boolean; +}; +type ErrorMapCtx = { + defaultError: string; + data: any; +}; +type ZodErrorMap = (issue: ZodIssueOptionalMessage, _ctx: ErrorMapCtx) => { + message: string; +}; + +const errorMap: ZodErrorMap; +//# sourceMappingURL=en.d.ts.map + +function setErrorMap(map: ZodErrorMap): void; +function getErrorMap(): ZodErrorMap; + +const makeIssue: (params: { + data: any; + path: (string | number)[]; + errorMaps: ZodErrorMap[]; + issueData: IssueData; +}) => ZodIssue; +type ParseParams = { + path: (string | number)[]; + errorMap: ZodErrorMap; + async: boolean; +}; +type ParsePathComponent = string | number; +type ParsePath = ParsePathComponent[]; +const EMPTY_PATH: ParsePath; +interface ParseContext { + readonly common: { + readonly issues: ZodIssue[]; + readonly contextualErrorMap?: ZodErrorMap; + readonly async: boolean; + }; + readonly path: ParsePath; + readonly schemaErrorMap?: ZodErrorMap; + readonly parent: ParseContext | null; + readonly data: any; + readonly parsedType: ZodParsedType; +} +type ParseInput = { + data: any; + path: (string | number)[]; + parent: ParseContext; +}; +function addIssueToContext(ctx: ParseContext, issueData: IssueData): void; +type ObjectPair = { + key: SyncParseReturnType; + value: SyncParseReturnType; +}; +class ParseStatus { + value: "aborted" | "dirty" | "valid"; + dirty(): void; + abort(): void; + static mergeArray(status: ParseStatus, results: SyncParseReturnType[]): SyncParseReturnType; + static mergeObjectAsync(status: ParseStatus, pairs: { + key: ParseReturnType; + value: ParseReturnType; + }[]): Promise>; + static mergeObjectSync(status: ParseStatus, pairs: { + key: SyncParseReturnType; + value: SyncParseReturnType; + alwaysSet?: boolean; + }[]): SyncParseReturnType; +} +interface ParseResult { + status: "aborted" | "dirty" | "valid"; + data: any; +} +type INVALID = { + status: "aborted"; +}; +const INVALID: INVALID; +type DIRTY = { + status: "dirty"; + value: T; +}; +const DIRTY: (value: T) => DIRTY; +type OK = { + status: "valid"; + value: T; +}; +const OK: (value: T) => OK; +type SyncParseReturnType = OK | DIRTY | INVALID; +type AsyncParseReturnType = Promise>; +type ParseReturnType = SyncParseReturnType | AsyncParseReturnType; +const isAborted: (x: ParseReturnType) => x is INVALID; +const isDirty: (x: ParseReturnType) => x is OK | DIRTY; +const isValid: (x: ParseReturnType) => x is OK; +const isAsync: (x: ParseReturnType) => x is AsyncParseReturnType; + +namespace enumUtil { + type UnionToIntersectionFn = (T extends unknown ? (k: () => T) => void : never) extends (k: infer Intersection) => void ? Intersection : never; + type GetUnionLast = UnionToIntersectionFn extends () => infer Last ? Last : never; + type UnionToTuple = [T] extends [never] ? Tuple : UnionToTuple>, [GetUnionLast, ...Tuple]>; + type CastToStringTuple = T extends [string, ...string[]] ? T : never; + export type UnionToTupleString = CastToStringTuple>; + export { }; +} + +namespace errorUtil { + type ErrMessage = string | { + message?: string; + }; + const errToObj: (message?: ErrMessage) => { + message?: string | undefined; + }; + const toString: (message?: ErrMessage) => string | undefined; +} + +namespace partialUtil { + type DeepPartial = T extends ZodObject ? ZodObject<{ + [k in keyof T["shape"]]: ZodOptional>; + }, T["_def"]["unknownKeys"], T["_def"]["catchall"]> : T extends ZodArray ? ZodArray, Card> : T extends ZodOptional ? ZodOptional> : T extends ZodNullable ? ZodNullable> : T extends ZodTuple ? { + [k in keyof Items]: Items[k] extends ZodTypeAny ? DeepPartial : never; + } extends infer PI ? PI extends ZodTupleItems ? ZodTuple : never : never : T; +} + +/** + * The Standard Schema interface. + */ +type StandardSchemaV1 = { + /** + * The Standard Schema properties. + */ + readonly "~standard": StandardSchemaV1.Props; +}; +namespace StandardSchemaV1 { + /** + * The Standard Schema properties interface. + */ + export interface Props { + /** + * The version number of the standard. + */ + readonly version: 1; + /** + * The vendor name of the schema library. + */ + readonly vendor: string; + /** + * Validates unknown input values. + */ + readonly validate: (value: unknown) => Result | Promise>; + /** + * Inferred types associated with the schema. + */ + readonly types?: Types | undefined; + } + /** + * The result interface of the validate function. + */ + export type Result = SuccessResult | FailureResult; + /** + * The result interface if validation succeeds. + */ + export interface SuccessResult { + /** + * The typed output value. + */ + readonly value: Output; + /** + * The non-existent issues. + */ + readonly issues?: undefined; + } + /** + * The result interface if validation fails. + */ + export interface FailureResult { + /** + * The issues of failed validation. + */ + readonly issues: ReadonlyArray; + } + /** + * The issue interface of the failure output. + */ + export interface Issue { + /** + * The error message of the issue. + */ + readonly message: string; + /** + * The path of the issue, if any. + */ + readonly path?: ReadonlyArray | undefined; + } + /** + * The path segment interface of the issue. + */ + export interface PathSegment { + /** + * The key representing a path segment. + */ + readonly key: PropertyKey; + } + /** + * The Standard Schema types interface. + */ + export interface Types { + /** + * The input type of the schema. + */ + readonly input: Input; + /** + * The output type of the schema. + */ + readonly output: Output; + } + /** + * Infers the input type of a Standard Schema. + */ + export type InferInput = NonNullable["input"]; + /** + * Infers the output type of a Standard Schema. + */ + export type InferOutput = NonNullable["output"]; + export { }; +} + +interface RefinementCtx { + addIssue: (arg: IssueData) => void; + path: (string | number)[]; +} +type ZodRawShape = { + [k: string]: ZodTypeAny; +}; +type ZodTypeAny = ZodType; +type TypeOf> = T["_output"]; +type input> = T["_input"]; +type output> = T["_output"]; + +type CustomErrorParams = Partial>; +interface ZodTypeDef { + errorMap?: ZodErrorMap; + description?: string; +} +type RawCreateParams = { + errorMap?: ZodErrorMap; + invalid_type_error?: string; + required_error?: string; + message?: string; + description?: string; +} | undefined; +type ProcessedCreateParams = { + errorMap?: ZodErrorMap; + description?: string; +}; +type SafeParseSuccess = { + success: true; + data: Output; + error?: never; +}; +type SafeParseError = { + success: false; + error: ZodError; + data?: never; +}; +type SafeParseReturnType = SafeParseSuccess | SafeParseError; +abstract class ZodType { + readonly _type: Output; + readonly _output: Output; + readonly _input: Input; + readonly _def: Def; + get description(): string | undefined; + "~standard": StandardSchemaV1.Props; + abstract _parse(input: ParseInput): ParseReturnType; + _getType(input: ParseInput): string; + _getOrReturnCtx(input: ParseInput, ctx?: ParseContext | undefined): ParseContext; + _processInputParams(input: ParseInput): { + status: ParseStatus; + ctx: ParseContext; + }; + _parseSync(input: ParseInput): SyncParseReturnType; + _parseAsync(input: ParseInput): AsyncParseReturnType; + parse(data: unknown, params?: Partial): Output; + safeParse(data: unknown, params?: Partial): SafeParseReturnType; + "~validate"(data: unknown): StandardSchemaV1.Result | Promise>; + parseAsync(data: unknown, params?: Partial): Promise; + safeParseAsync(data: unknown, params?: Partial): Promise>; + /** Alias of safeParseAsync */ + spa: (data: unknown, params?: Partial) => Promise>; + refine(check: (arg: Output) => arg is RefinedOutput, message?: string | CustomErrorParams | ((arg: Output) => CustomErrorParams)): ZodEffects; + refine(check: (arg: Output) => unknown | Promise, message?: string | CustomErrorParams | ((arg: Output) => CustomErrorParams)): ZodEffects; + refinement(check: (arg: Output) => arg is RefinedOutput, refinementData: IssueData | ((arg: Output, ctx: RefinementCtx) => IssueData)): ZodEffects; + refinement(check: (arg: Output) => boolean, refinementData: IssueData | ((arg: Output, ctx: RefinementCtx) => IssueData)): ZodEffects; + _refinement(refinement: RefinementEffect["refinement"]): ZodEffects; + superRefine(refinement: (arg: Output, ctx: RefinementCtx) => arg is RefinedOutput): ZodEffects; + superRefine(refinement: (arg: Output, ctx: RefinementCtx) => void): ZodEffects; + superRefine(refinement: (arg: Output, ctx: RefinementCtx) => Promise): ZodEffects; + constructor(def: Def); + optional(): ZodOptional; + nullable(): ZodNullable; + nullish(): ZodOptional>; + array(): ZodArray; + promise(): ZodPromise; + or(option: T): ZodUnion<[this, T]>; + and(incoming: T): ZodIntersection; + transform(transform: (arg: Output, ctx: RefinementCtx) => NewOut | Promise): ZodEffects; + default(def: util.noUndefined): ZodDefault; + default(def: () => util.noUndefined): ZodDefault; + brand(brand?: B): ZodBranded; + catch(def: Output): ZodCatch; + catch(def: (ctx: { + error: ZodError; + input: Input; + }) => Output): ZodCatch; + describe(description: string): this; + pipe(target: T): ZodPipeline; + readonly(): ZodReadonly; + isOptional(): boolean; + isNullable(): boolean; +} +type IpVersion = "v4" | "v6"; +type ZodStringCheck = { + kind: "min"; + value: number; + message?: string; +} | { + kind: "max"; + value: number; + message?: string; +} | { + kind: "length"; + value: number; + message?: string; +} | { + kind: "email"; + message?: string; +} | { + kind: "url"; + message?: string; +} | { + kind: "emoji"; + message?: string; +} | { + kind: "uuid"; + message?: string; +} | { + kind: "nanoid"; + message?: string; +} | { + kind: "cuid"; + message?: string; +} | { + kind: "includes"; + value: string; + position?: number; + message?: string; +} | { + kind: "cuid2"; + message?: string; +} | { + kind: "ulid"; + message?: string; +} | { + kind: "startsWith"; + value: string; + message?: string; +} | { + kind: "endsWith"; + value: string; + message?: string; +} | { + kind: "regex"; + regex: RegExp; + message?: string; +} | { + kind: "trim"; + message?: string; +} | { + kind: "toLowerCase"; + message?: string; +} | { + kind: "toUpperCase"; + message?: string; +} | { + kind: "jwt"; + alg?: string; + message?: string; +} | { + kind: "datetime"; + offset: boolean; + local: boolean; + precision: number | null; + message?: string; +} | { + kind: "date"; + message?: string; +} | { + kind: "time"; + precision: number | null; + message?: string; +} | { + kind: "duration"; + message?: string; +} | { + kind: "ip"; + version?: IpVersion; + message?: string; +} | { + kind: "cidr"; + version?: IpVersion; + message?: string; +} | { + kind: "base64"; + message?: string; +} | { + kind: "base64url"; + message?: string; +}; +interface ZodStringDef extends ZodTypeDef { + checks: ZodStringCheck[]; + typeName: ZodFirstPartyTypeKind.ZodString; + coerce: boolean; +} +function datetimeRegex(args: { + precision?: number | null; + offset?: boolean; + local?: boolean; +}): RegExp; +class ZodString extends ZodType { + _parse(input: ParseInput): ParseReturnType; + protected _regex(regex: RegExp, validation: StringValidation, message?: errorUtil.ErrMessage): ZodEffects; + _addCheck(check: ZodStringCheck): ZodString; + email(message?: errorUtil.ErrMessage): ZodString; + url(message?: errorUtil.ErrMessage): ZodString; + emoji(message?: errorUtil.ErrMessage): ZodString; + uuid(message?: errorUtil.ErrMessage): ZodString; + nanoid(message?: errorUtil.ErrMessage): ZodString; + cuid(message?: errorUtil.ErrMessage): ZodString; + cuid2(message?: errorUtil.ErrMessage): ZodString; + ulid(message?: errorUtil.ErrMessage): ZodString; + base64(message?: errorUtil.ErrMessage): ZodString; + base64url(message?: errorUtil.ErrMessage): ZodString; + jwt(options?: { + alg?: string; + message?: string; + }): ZodString; + ip(options?: string | { + version?: IpVersion; + message?: string; + }): ZodString; + cidr(options?: string | { + version?: IpVersion; + message?: string; + }): ZodString; + datetime(options?: string | { + message?: string | undefined; + precision?: number | null; + offset?: boolean; + local?: boolean; + }): ZodString; + date(message?: string): ZodString; + time(options?: string | { + message?: string | undefined; + precision?: number | null; + }): ZodString; + duration(message?: errorUtil.ErrMessage): ZodString; + regex(regex: RegExp, message?: errorUtil.ErrMessage): ZodString; + includes(value: string, options?: { + message?: string; + position?: number; + }): ZodString; + startsWith(value: string, message?: errorUtil.ErrMessage): ZodString; + endsWith(value: string, message?: errorUtil.ErrMessage): ZodString; + min(minLength: number, message?: errorUtil.ErrMessage): ZodString; + max(maxLength: number, message?: errorUtil.ErrMessage): ZodString; + length(len: number, message?: errorUtil.ErrMessage): ZodString; + /** + * Equivalent to `.min(1)` + */ + nonempty(message?: errorUtil.ErrMessage): ZodString; + trim(): ZodString; + toLowerCase(): ZodString; + toUpperCase(): ZodString; + get isDatetime(): boolean; + get isDate(): boolean; + get isTime(): boolean; + get isDuration(): boolean; + get isEmail(): boolean; + get isURL(): boolean; + get isEmoji(): boolean; + get isUUID(): boolean; + get isNANOID(): boolean; + get isCUID(): boolean; + get isCUID2(): boolean; + get isULID(): boolean; + get isIP(): boolean; + get isCIDR(): boolean; + get isBase64(): boolean; + get isBase64url(): boolean; + get minLength(): number | null; + get maxLength(): number | null; + static create: (params?: RawCreateParams & { + coerce?: true; + }) => ZodString; +} +type ZodNumberCheck = { + kind: "min"; + value: number; + inclusive: boolean; + message?: string; +} | { + kind: "max"; + value: number; + inclusive: boolean; + message?: string; +} | { + kind: "int"; + message?: string; +} | { + kind: "multipleOf"; + value: number; + message?: string; +} | { + kind: "finite"; + message?: string; +}; +interface ZodNumberDef extends ZodTypeDef { + checks: ZodNumberCheck[]; + typeName: ZodFirstPartyTypeKind.ZodNumber; + coerce: boolean; +} +class ZodNumber extends ZodType { + _parse(input: ParseInput): ParseReturnType; + static create: (params?: RawCreateParams & { + coerce?: boolean; + }) => ZodNumber; + gte(value: number, message?: errorUtil.ErrMessage): ZodNumber; + min: (value: number, message?: errorUtil.ErrMessage) => ZodNumber; + gt(value: number, message?: errorUtil.ErrMessage): ZodNumber; + lte(value: number, message?: errorUtil.ErrMessage): ZodNumber; + max: (value: number, message?: errorUtil.ErrMessage) => ZodNumber; + lt(value: number, message?: errorUtil.ErrMessage): ZodNumber; + protected setLimit(kind: "min" | "max", value: number, inclusive: boolean, message?: string): ZodNumber; + _addCheck(check: ZodNumberCheck): ZodNumber; + int(message?: errorUtil.ErrMessage): ZodNumber; + positive(message?: errorUtil.ErrMessage): ZodNumber; + negative(message?: errorUtil.ErrMessage): ZodNumber; + nonpositive(message?: errorUtil.ErrMessage): ZodNumber; + nonnegative(message?: errorUtil.ErrMessage): ZodNumber; + multipleOf(value: number, message?: errorUtil.ErrMessage): ZodNumber; + step: (value: number, message?: errorUtil.ErrMessage) => ZodNumber; + finite(message?: errorUtil.ErrMessage): ZodNumber; + safe(message?: errorUtil.ErrMessage): ZodNumber; + get minValue(): number | null; + get maxValue(): number | null; + get isInt(): boolean; + get isFinite(): boolean; +} +type ZodBigIntCheck = { + kind: "min"; + value: bigint; + inclusive: boolean; + message?: string; +} | { + kind: "max"; + value: bigint; + inclusive: boolean; + message?: string; +} | { + kind: "multipleOf"; + value: bigint; + message?: string; +}; +interface ZodBigIntDef extends ZodTypeDef { + checks: ZodBigIntCheck[]; + typeName: ZodFirstPartyTypeKind.ZodBigInt; + coerce: boolean; +} +class ZodBigInt extends ZodType { + _parse(input: ParseInput): ParseReturnType; + _getInvalidInput(input: ParseInput): INVALID; + static create: (params?: RawCreateParams & { + coerce?: boolean; + }) => ZodBigInt; + gte(value: bigint, message?: errorUtil.ErrMessage): ZodBigInt; + min: (value: bigint, message?: errorUtil.ErrMessage) => ZodBigInt; + gt(value: bigint, message?: errorUtil.ErrMessage): ZodBigInt; + lte(value: bigint, message?: errorUtil.ErrMessage): ZodBigInt; + max: (value: bigint, message?: errorUtil.ErrMessage) => ZodBigInt; + lt(value: bigint, message?: errorUtil.ErrMessage): ZodBigInt; + protected setLimit(kind: "min" | "max", value: bigint, inclusive: boolean, message?: string): ZodBigInt; + _addCheck(check: ZodBigIntCheck): ZodBigInt; + positive(message?: errorUtil.ErrMessage): ZodBigInt; + negative(message?: errorUtil.ErrMessage): ZodBigInt; + nonpositive(message?: errorUtil.ErrMessage): ZodBigInt; + nonnegative(message?: errorUtil.ErrMessage): ZodBigInt; + multipleOf(value: bigint, message?: errorUtil.ErrMessage): ZodBigInt; + get minValue(): bigint | null; + get maxValue(): bigint | null; +} +interface ZodBooleanDef extends ZodTypeDef { + typeName: ZodFirstPartyTypeKind.ZodBoolean; + coerce: boolean; +} +class ZodBoolean extends ZodType { + _parse(input: ParseInput): ParseReturnType; + static create: (params?: RawCreateParams & { + coerce?: boolean; + }) => ZodBoolean; +} +type ZodDateCheck = { + kind: "min"; + value: number; + message?: string; +} | { + kind: "max"; + value: number; + message?: string; +}; +interface ZodDateDef extends ZodTypeDef { + checks: ZodDateCheck[]; + coerce: boolean; + typeName: ZodFirstPartyTypeKind.ZodDate; +} +class ZodDate extends ZodType { + _parse(input: ParseInput): ParseReturnType; + _addCheck(check: ZodDateCheck): ZodDate; + min(minDate: Date, message?: errorUtil.ErrMessage): ZodDate; + max(maxDate: Date, message?: errorUtil.ErrMessage): ZodDate; + get minDate(): Date | null; + get maxDate(): Date | null; + static create: (params?: RawCreateParams & { + coerce?: boolean; + }) => ZodDate; +} +interface ZodSymbolDef extends ZodTypeDef { + typeName: ZodFirstPartyTypeKind.ZodSymbol; +} +class ZodSymbol extends ZodType { + _parse(input: ParseInput): ParseReturnType; + static create: (params?: RawCreateParams) => ZodSymbol; +} +interface ZodUndefinedDef extends ZodTypeDef { + typeName: ZodFirstPartyTypeKind.ZodUndefined; +} +class ZodUndefined extends ZodType { + _parse(input: ParseInput): ParseReturnType; + params?: RawCreateParams; + static create: (params?: RawCreateParams) => ZodUndefined; +} +interface ZodNullDef extends ZodTypeDef { + typeName: ZodFirstPartyTypeKind.ZodNull; +} +class ZodNull extends ZodType { + _parse(input: ParseInput): ParseReturnType; + static create: (params?: RawCreateParams) => ZodNull; +} +interface ZodAnyDef extends ZodTypeDef { + typeName: ZodFirstPartyTypeKind.ZodAny; +} +class ZodAny extends ZodType { + _any: true; + _parse(input: ParseInput): ParseReturnType; + static create: (params?: RawCreateParams) => ZodAny; +} +interface ZodUnknownDef extends ZodTypeDef { + typeName: ZodFirstPartyTypeKind.ZodUnknown; +} +class ZodUnknown extends ZodType { + _unknown: true; + _parse(input: ParseInput): ParseReturnType; + static create: (params?: RawCreateParams) => ZodUnknown; +} +interface ZodNeverDef extends ZodTypeDef { + typeName: ZodFirstPartyTypeKind.ZodNever; +} +class ZodNever extends ZodType { + _parse(input: ParseInput): ParseReturnType; + static create: (params?: RawCreateParams) => ZodNever; +} +interface ZodVoidDef extends ZodTypeDef { + typeName: ZodFirstPartyTypeKind.ZodVoid; +} +class ZodVoid extends ZodType { + _parse(input: ParseInput): ParseReturnType; + static create: (params?: RawCreateParams) => ZodVoid; +} +interface ZodArrayDef extends ZodTypeDef { + type: T; + typeName: ZodFirstPartyTypeKind.ZodArray; + exactLength: { + value: number; + message?: string; + } | null; + minLength: { + value: number; + message?: string; + } | null; + maxLength: { + value: number; + message?: string; + } | null; +} +type ArrayCardinality = "many" | "atleastone"; +type arrayOutputType = Cardinality extends "atleastone" ? [T["_output"], ...T["_output"][]] : T["_output"][]; +class ZodArray extends ZodType, ZodArrayDef, Cardinality extends "atleastone" ? [T["_input"], ...T["_input"][]] : T["_input"][]> { + _parse(input: ParseInput): ParseReturnType; + get element(): T; + min(minLength: number, message?: errorUtil.ErrMessage): this; + max(maxLength: number, message?: errorUtil.ErrMessage): this; + length(len: number, message?: errorUtil.ErrMessage): this; + nonempty(message?: errorUtil.ErrMessage): ZodArray; + static create: (schema: T_1, params?: RawCreateParams) => ZodArray; +} +type ZodNonEmptyArray = ZodArray; +type UnknownKeysParam = "passthrough" | "strict" | "strip"; +interface ZodObjectDef extends ZodTypeDef { + typeName: ZodFirstPartyTypeKind.ZodObject; + shape: () => T; + catchall: Catchall; + unknownKeys: UnknownKeys; +} +type mergeTypes = { + [k in keyof A | keyof B]: k extends keyof B ? B[k] : k extends keyof A ? A[k] : never; +}; +type objectOutputType = objectUtil.flatten>> & CatchallOutput & PassthroughType; +type baseObjectOutputType = { + [k in keyof Shape]: Shape[k]["_output"]; +}; +type objectInputType = objectUtil.flatten> & CatchallInput & PassthroughType; +type baseObjectInputType = objectUtil.addQuestionMarks<{ + [k in keyof Shape]: Shape[k]["_input"]; +}>; +type CatchallOutput = ZodType extends T ? unknown : { + [k: string]: T["_output"]; +}; +type CatchallInput = ZodType extends T ? unknown : { + [k: string]: T["_input"]; +}; +type PassthroughType = T extends "passthrough" ? { + [k: string]: unknown; +} : unknown; +type deoptional = T extends ZodOptional ? deoptional : T extends ZodNullable ? ZodNullable> : T; +type SomeZodObject = ZodObject; +type noUnrecognized = { + [k in keyof Obj]: k extends keyof Shape ? Obj[k] : never; +}; +class ZodObject, Input = objectInputType> extends ZodType, Input> { + private _cached; + _getCached(): { + shape: T; + keys: string[]; + }; + _parse(input: ParseInput): ParseReturnType; + get shape(): T; + strict(message?: errorUtil.ErrMessage): ZodObject; + strip(): ZodObject; + passthrough(): ZodObject; + /** + * @deprecated In most cases, this is no longer needed - unknown properties are now silently stripped. + * If you want to pass through unknown properties, use `.passthrough()` instead. + */ + nonstrict: () => ZodObject; + extend(augmentation: Augmentation): ZodObject, UnknownKeys, Catchall>; + /** + * @deprecated Use `.extend` instead + * */ + augment: (augmentation: Augmentation) => ZodObject, UnknownKeys, Catchall>; + /** + * Prior to zod@1.0.12 there was a bug in the + * inferred type of merged objects. Please + * upgrade if you are experiencing issues. + */ + merge(merging: Incoming): ZodObject, Incoming["_def"]["unknownKeys"], Incoming["_def"]["catchall"]>; + setKey(key: Key, schema: Schema): ZodObject; + catchall(index: Index): ZodObject; + pick>(mask: Mask): ZodObject>, UnknownKeys, Catchall>; + omit>(mask: Mask): ZodObject, UnknownKeys, Catchall>; + /** + * @deprecated + */ + deepPartial(): partialUtil.DeepPartial; + partial(): ZodObject<{ + [k in keyof T]: ZodOptional; + }, UnknownKeys, Catchall>; + partial>(mask: Mask): ZodObject : T[k]; + }>, UnknownKeys, Catchall>; + required(): ZodObject<{ + [k in keyof T]: deoptional; + }, UnknownKeys, Catchall>; + required>(mask: Mask): ZodObject : T[k]; + }>, UnknownKeys, Catchall>; + keyof(): ZodEnum>; + static create: (shape: T_1, params?: RawCreateParams) => ZodObject, any> extends infer T_2 ? { [k in keyof T_2]: objectUtil.addQuestionMarks, any>[k]; } : never, baseObjectInputType extends infer T_3 ? { [k_1 in keyof T_3]: baseObjectInputType[k_1]; } : never>; + static strictCreate: (shape: T_1, params?: RawCreateParams) => ZodObject, any> extends infer T_2 ? { [k in keyof T_2]: objectUtil.addQuestionMarks, any>[k]; } : never, baseObjectInputType extends infer T_3 ? { [k_1 in keyof T_3]: baseObjectInputType[k_1]; } : never>; + static lazycreate: (shape: () => T_1, params?: RawCreateParams) => ZodObject, any> extends infer T_2 ? { [k in keyof T_2]: objectUtil.addQuestionMarks, any>[k]; } : never, baseObjectInputType extends infer T_3 ? { [k_1 in keyof T_3]: baseObjectInputType[k_1]; } : never>; +} +type AnyZodObject = ZodObject; +type ZodUnionOptions = Readonly<[ZodTypeAny, ...ZodTypeAny[]]>; +interface ZodUnionDef> extends ZodTypeDef { + options: T; + typeName: ZodFirstPartyTypeKind.ZodUnion; +} +class ZodUnion extends ZodType, T[number]["_input"]> { + _parse(input: ParseInput): ParseReturnType; + get options(): T; + static create: (types: T_1, params?: RawCreateParams) => ZodUnion; +} +type ZodDiscriminatedUnionOption = ZodObject<{ + [key in Discriminator]: ZodTypeAny; +} & ZodRawShape, UnknownKeysParam, ZodTypeAny>; +interface ZodDiscriminatedUnionDef[] = ZodDiscriminatedUnionOption[]> extends ZodTypeDef { + discriminator: Discriminator; + options: Options; + optionsMap: Map>; + typeName: ZodFirstPartyTypeKind.ZodDiscriminatedUnion; +} +class ZodDiscriminatedUnion[]> extends ZodType, ZodDiscriminatedUnionDef, input> { + _parse(input: ParseInput): ParseReturnType; + get discriminator(): Discriminator; + get options(): Options; + get optionsMap(): Map>; + /** + * The constructor of the discriminated union schema. Its behaviour is very similar to that of the normal z.union() constructor. + * However, it only allows a union of objects, all of which need to share a discriminator property. This property must + * have a different value for each object in the union. + * @param discriminator the name of the discriminator property + * @param types an array of object schemas + * @param params + */ + static create, + ...ZodDiscriminatedUnionOption[] + ]>(discriminator: Discriminator, options: Types, params?: RawCreateParams): ZodDiscriminatedUnion; +} +interface ZodIntersectionDef extends ZodTypeDef { + left: T; + right: U; + typeName: ZodFirstPartyTypeKind.ZodIntersection; +} +class ZodIntersection extends ZodType, T["_input"] & U["_input"]> { + _parse(input: ParseInput): ParseReturnType; + static create: (left: T_1, right: U_1, params?: RawCreateParams) => ZodIntersection; +} +type ZodTupleItems = [ZodTypeAny, ...ZodTypeAny[]]; +type AssertArray = T extends any[] ? T : never; +type OutputTypeOfTuple = AssertArray<{ + [k in keyof T]: T[k] extends ZodType ? T[k]["_output"] : never; +}>; +type OutputTypeOfTupleWithRest = Rest extends ZodTypeAny ? [...OutputTypeOfTuple, ...Rest["_output"][]] : OutputTypeOfTuple; +type InputTypeOfTuple = AssertArray<{ + [k in keyof T]: T[k] extends ZodType ? T[k]["_input"] : never; +}>; +type InputTypeOfTupleWithRest = Rest extends ZodTypeAny ? [...InputTypeOfTuple, ...Rest["_input"][]] : InputTypeOfTuple; +interface ZodTupleDef extends ZodTypeDef { + items: T; + rest: Rest; + typeName: ZodFirstPartyTypeKind.ZodTuple; +} +type AnyZodTuple = ZodTuple<[ + ZodTypeAny, + ...ZodTypeAny[] +] | [], ZodTypeAny | null>; +class ZodTuple extends ZodType, ZodTupleDef, InputTypeOfTupleWithRest> { + _parse(input: ParseInput): ParseReturnType; + get items(): T; + rest(rest: Rest): ZodTuple; + static create: (schemas: T_1, params?: RawCreateParams) => ZodTuple; +} +interface ZodRecordDef extends ZodTypeDef { + valueType: Value; + keyType: Key; + typeName: ZodFirstPartyTypeKind.ZodRecord; +} +type KeySchema = ZodType; +type RecordType = [ + string +] extends [K] ? Record : [number] extends [K] ? Record : [symbol] extends [K] ? Record : [BRAND] extends [K] ? Record : Partial>; +class ZodRecord extends ZodType, ZodRecordDef, RecordType> { + get keySchema(): Key; + get valueSchema(): Value; + _parse(input: ParseInput): ParseReturnType; + get element(): Value; + static create(valueType: Value, params?: RawCreateParams): ZodRecord; + static create(keySchema: Keys, valueType: Value, params?: RawCreateParams): ZodRecord; +} +interface ZodMapDef extends ZodTypeDef { + valueType: Value; + keyType: Key; + typeName: ZodFirstPartyTypeKind.ZodMap; +} +class ZodMap extends ZodType, ZodMapDef, Map> { + get keySchema(): Key; + get valueSchema(): Value; + _parse(input: ParseInput): ParseReturnType; + static create: (keyType: Key_1, valueType: Value_1, params?: RawCreateParams) => ZodMap; +} +interface ZodSetDef extends ZodTypeDef { + valueType: Value; + typeName: ZodFirstPartyTypeKind.ZodSet; + minSize: { + value: number; + message?: string; + } | null; + maxSize: { + value: number; + message?: string; + } | null; +} +class ZodSet extends ZodType, ZodSetDef, Set> { + _parse(input: ParseInput): ParseReturnType; + min(minSize: number, message?: errorUtil.ErrMessage): this; + max(maxSize: number, message?: errorUtil.ErrMessage): this; + size(size: number, message?: errorUtil.ErrMessage): this; + nonempty(message?: errorUtil.ErrMessage): ZodSet; + static create: (valueType: Value_1, params?: RawCreateParams) => ZodSet; +} +interface ZodFunctionDef = ZodTuple, Returns extends ZodTypeAny = ZodTypeAny> extends ZodTypeDef { + args: Args; + returns: Returns; + typeName: ZodFirstPartyTypeKind.ZodFunction; +} +type OuterTypeOfFunction, Returns extends ZodTypeAny> = Args["_input"] extends Array ? (...args: Args["_input"]) => Returns["_output"] : never; +type InnerTypeOfFunction, Returns extends ZodTypeAny> = Args["_output"] extends Array ? (...args: Args["_output"]) => Returns["_input"] : never; +class ZodFunction, Returns extends ZodTypeAny> extends ZodType, ZodFunctionDef, InnerTypeOfFunction> { + _parse(input: ParseInput): ParseReturnType; + parameters(): Args; + returnType(): Returns; + args[0]>(...items: Items): ZodFunction, Returns>; + returns>(returnType: NewReturnType): ZodFunction; + implement>(func: F): ReturnType extends Returns["_output"] ? (...args: Args["_input"]) => ReturnType : OuterTypeOfFunction; + strictImplement(func: InnerTypeOfFunction): InnerTypeOfFunction; + validate: >(func: F) => ReturnType extends Returns["_output"] ? (...args: Args["_input"]) => ReturnType : OuterTypeOfFunction; + static create(): ZodFunction, ZodUnknown>; + static create>(args: T): ZodFunction; + static create(args: T, returns: U): ZodFunction; + static create, U extends ZodTypeAny = ZodUnknown>(args: T, returns: U, params?: RawCreateParams): ZodFunction; +} +interface ZodLazyDef extends ZodTypeDef { + getter: () => T; + typeName: ZodFirstPartyTypeKind.ZodLazy; +} +class ZodLazy extends ZodType, ZodLazyDef, input> { + get schema(): T; + _parse(input: ParseInput): ParseReturnType; + static create: (getter: () => T_1, params?: RawCreateParams) => ZodLazy; +} +interface ZodLiteralDef extends ZodTypeDef { + value: T; + typeName: ZodFirstPartyTypeKind.ZodLiteral; +} +class ZodLiteral extends ZodType, T> { + _parse(input: ParseInput): ParseReturnType; + get value(): T; + static create: (value: T_1, params?: RawCreateParams) => ZodLiteral; +} +type ArrayKeys = keyof any[]; +type Indices = Exclude; +type EnumValues = readonly [T, ...T[]]; +type Values = { + [k in T[number]]: k; +}; +interface ZodEnumDef extends ZodTypeDef { + values: T; + typeName: ZodFirstPartyTypeKind.ZodEnum; +} +type Writeable = { + -readonly [P in keyof T]: T[P]; +}; +type FilterEnum = Values extends [] ? [] : Values extends [infer Head, ...infer Rest] ? Head extends ToExclude ? FilterEnum : [Head, ...FilterEnum] : never; +type typecast = A extends T ? A : never; +function createZodEnum>(values: T, params?: RawCreateParams): ZodEnum>; +function createZodEnum(values: T, params?: RawCreateParams): ZodEnum; +class ZodEnum extends ZodType, T[number]> { + #private; + _parse(input: ParseInput): ParseReturnType; + get options(): T; + get enum(): Values; + get Values(): Values; + get Enum(): Values; + extract(values: ToExtract, newDef?: RawCreateParams): ZodEnum>; + exclude(values: ToExclude, newDef?: RawCreateParams): ZodEnum>, [string, ...string[]]>>; + static create: typeof createZodEnum; +} +interface ZodNativeEnumDef extends ZodTypeDef { + values: T; + typeName: ZodFirstPartyTypeKind.ZodNativeEnum; +} +type EnumLike = { + [k: string]: string | number; + [nu: number]: string; +}; +class ZodNativeEnum extends ZodType, T[keyof T]> { + #private; + _parse(input: ParseInput): ParseReturnType; + get enum(): T; + static create: (values: T_1, params?: RawCreateParams) => ZodNativeEnum; +} +interface ZodPromiseDef extends ZodTypeDef { + type: T; + typeName: ZodFirstPartyTypeKind.ZodPromise; +} +class ZodPromise extends ZodType, ZodPromiseDef, Promise> { + unwrap(): T; + _parse(input: ParseInput): ParseReturnType; + static create: (schema: T_1, params?: RawCreateParams) => ZodPromise; +} +type Refinement = (arg: T, ctx: RefinementCtx) => any; +type SuperRefinement = (arg: T, ctx: RefinementCtx) => void | Promise; +type RefinementEffect = { + type: "refinement"; + refinement: (arg: T, ctx: RefinementCtx) => any; +}; +type TransformEffect = { + type: "transform"; + transform: (arg: T, ctx: RefinementCtx) => any; +}; +type PreprocessEffect = { + type: "preprocess"; + transform: (arg: T, ctx: RefinementCtx) => any; +}; +type Effect = RefinementEffect | TransformEffect | PreprocessEffect; +interface ZodEffectsDef extends ZodTypeDef { + schema: T; + typeName: ZodFirstPartyTypeKind.ZodEffects; + effect: Effect; +} +class ZodEffects, Input = input> extends ZodType, Input> { + innerType(): T; + sourceType(): T; + _parse(input: ParseInput): ParseReturnType; + static create: (schema: I, effect: Effect, params?: RawCreateParams) => ZodEffects; + static createWithPreprocess: (preprocess: (arg: unknown, ctx: RefinementCtx) => unknown, schema: I, params?: RawCreateParams) => ZodEffects; +} + +interface ZodOptionalDef extends ZodTypeDef { + innerType: T; + typeName: ZodFirstPartyTypeKind.ZodOptional; +} +type ZodOptionalType = ZodOptional; +class ZodOptional extends ZodType, T["_input"] | undefined> { + _parse(input: ParseInput): ParseReturnType; + unwrap(): T; + static create: (type: T_1, params?: RawCreateParams) => ZodOptional; +} +interface ZodNullableDef extends ZodTypeDef { + innerType: T; + typeName: ZodFirstPartyTypeKind.ZodNullable; +} +type ZodNullableType = ZodNullable; +class ZodNullable extends ZodType, T["_input"] | null> { + _parse(input: ParseInput): ParseReturnType; + unwrap(): T; + static create: (type: T_1, params?: RawCreateParams) => ZodNullable; +} +interface ZodDefaultDef extends ZodTypeDef { + innerType: T; + defaultValue: () => util.noUndefined; + typeName: ZodFirstPartyTypeKind.ZodDefault; +} +class ZodDefault extends ZodType, ZodDefaultDef, T["_input"] | undefined> { + _parse(input: ParseInput): ParseReturnType; + removeDefault(): T; + static create: (type: T_1, params: { + errorMap?: ZodErrorMap | undefined; + invalid_type_error?: string | undefined; + required_error?: string | undefined; + message?: string | undefined; + description?: string | undefined; + } & { + default: T_1["_input"] | (() => util.noUndefined); + }) => ZodDefault; +} +interface ZodCatchDef extends ZodTypeDef { + innerType: T; + catchValue: (ctx: { + error: ZodError; + input: unknown; + }) => T["_input"]; + typeName: ZodFirstPartyTypeKind.ZodCatch; +} +class ZodCatch extends ZodType, unknown> { + _parse(input: ParseInput): ParseReturnType; + removeCatch(): T; + static create: (type: T_1, params: { + errorMap?: ZodErrorMap | undefined; + invalid_type_error?: string | undefined; + required_error?: string | undefined; + message?: string | undefined; + description?: string | undefined; + } & { + catch: T_1["_output"] | (() => T_1["_output"]); + }) => ZodCatch; +} +interface ZodNaNDef extends ZodTypeDef { + typeName: ZodFirstPartyTypeKind.ZodNaN; +} +class ZodNaN extends ZodType { + _parse(input: ParseInput): ParseReturnType; + static create: (params?: RawCreateParams) => ZodNaN; +} +interface ZodBrandedDef extends ZodTypeDef { + type: T; + typeName: ZodFirstPartyTypeKind.ZodBranded; +} +const BRAND: unique symbol; +type BRAND = { + [BRAND]: { + [k in T]: true; + }; +}; +class ZodBranded extends ZodType, ZodBrandedDef, T["_input"]> { + _parse(input: ParseInput): ParseReturnType; + unwrap(): T; +} +interface ZodPipelineDef extends ZodTypeDef { + in: A; + out: B; + typeName: ZodFirstPartyTypeKind.ZodPipeline; +} +class ZodPipeline extends ZodType, A["_input"]> { + _parse(input: ParseInput): ParseReturnType; + static create(a: A, b: B): ZodPipeline; +} +type BuiltIn = (((...args: any[]) => any) | (new (...args: any[]) => any)) | { + readonly [Symbol.toStringTag]: string; +} | Date | Error | Generator | Promise | RegExp; +type MakeReadonly = T extends Map ? ReadonlyMap : T extends Set ? ReadonlySet : T extends [infer Head, ...infer Tail] ? readonly [Head, ...Tail] : T extends Array ? ReadonlyArray : T extends BuiltIn ? T : Readonly; +interface ZodReadonlyDef extends ZodTypeDef { + innerType: T; + typeName: ZodFirstPartyTypeKind.ZodReadonly; +} +class ZodReadonly extends ZodType, ZodReadonlyDef, MakeReadonly> { + _parse(input: ParseInput): ParseReturnType; + static create: (type: T_1, params?: RawCreateParams) => ZodReadonly; + unwrap(): T; +} +type CustomParams = CustomErrorParams & { + fatal?: boolean; +}; +function custom(check?: (data: any) => any, params?: string | CustomParams | ((input: any) => CustomParams), +/** + * @deprecated + * + * Pass `fatal` into the params object instead: + * + * ```ts + * z.string().custom((val) => val.length > 5, { fatal: false }) + * ``` + * + */ +fatal?: boolean): ZodType; + +const late: { + object: (shape: () => T, params?: RawCreateParams) => ZodObject; +}; +enum ZodFirstPartyTypeKind { + ZodString = "ZodString", + ZodNumber = "ZodNumber", + ZodNaN = "ZodNaN", + ZodBigInt = "ZodBigInt", + ZodBoolean = "ZodBoolean", + ZodDate = "ZodDate", + ZodSymbol = "ZodSymbol", + ZodUndefined = "ZodUndefined", + ZodNull = "ZodNull", + ZodAny = "ZodAny", + ZodUnknown = "ZodUnknown", + ZodNever = "ZodNever", + ZodVoid = "ZodVoid", + ZodArray = "ZodArray", + ZodObject = "ZodObject", + ZodUnion = "ZodUnion", + ZodDiscriminatedUnion = "ZodDiscriminatedUnion", + ZodIntersection = "ZodIntersection", + ZodTuple = "ZodTuple", + ZodRecord = "ZodRecord", + ZodMap = "ZodMap", + ZodSet = "ZodSet", + ZodFunction = "ZodFunction", + ZodLazy = "ZodLazy", + ZodLiteral = "ZodLiteral", + ZodEnum = "ZodEnum", + ZodEffects = "ZodEffects", + ZodNativeEnum = "ZodNativeEnum", + ZodOptional = "ZodOptional", + ZodNullable = "ZodNullable", + ZodDefault = "ZodDefault", + ZodCatch = "ZodCatch", + ZodPromise = "ZodPromise", + ZodBranded = "ZodBranded", + ZodPipeline = "ZodPipeline", + ZodReadonly = "ZodReadonly" +} +type ZodFirstPartySchemaTypes = ZodString | ZodNumber | ZodNaN | ZodBigInt | ZodBoolean | ZodDate | ZodUndefined | ZodNull | ZodAny | ZodUnknown | ZodNever | ZodVoid | ZodArray | ZodObject | ZodUnion | ZodDiscriminatedUnion | ZodIntersection | ZodTuple | ZodRecord | ZodMap | ZodSet | ZodFunction | ZodLazy | ZodLiteral | ZodEnum | ZodEffects | ZodNativeEnum | ZodOptional | ZodNullable | ZodDefault | ZodCatch | ZodPromise | ZodBranded | ZodPipeline | ZodReadonly | ZodSymbol; +abstract class Class { + constructor(..._: any[]); +} +const instanceOfType: (cls: T, params?: CustomParams) => ZodType, ZodTypeDef, InstanceType>; +const stringType: (params?: RawCreateParams & { + coerce?: true; +}) => ZodString; +const numberType: (params?: RawCreateParams & { + coerce?: boolean; +}) => ZodNumber; +const nanType: (params?: RawCreateParams) => ZodNaN; +const bigIntType: (params?: RawCreateParams & { + coerce?: boolean; +}) => ZodBigInt; +const booleanType: (params?: RawCreateParams & { + coerce?: boolean; +}) => ZodBoolean; +const dateType: (params?: RawCreateParams & { + coerce?: boolean; +}) => ZodDate; +const symbolType: (params?: RawCreateParams) => ZodSymbol; +const undefinedType: (params?: RawCreateParams) => ZodUndefined; +const nullType: (params?: RawCreateParams) => ZodNull; +const anyType: (params?: RawCreateParams) => ZodAny; +const unknownType: (params?: RawCreateParams) => ZodUnknown; +const neverType: (params?: RawCreateParams) => ZodNever; +const voidType: (params?: RawCreateParams) => ZodVoid; +const arrayType: (schema: T, params?: RawCreateParams) => ZodArray; +const objectType: (shape: T, params?: RawCreateParams) => ZodObject, objectInputType>; +const strictObjectType: (shape: T, params?: RawCreateParams) => ZodObject; +const unionType: (types: T, params?: RawCreateParams) => ZodUnion; +const discriminatedUnionType: typeof ZodDiscriminatedUnion.create; +const intersectionType: (left: T, right: U, params?: RawCreateParams) => ZodIntersection; +const tupleType: (schemas: T, params?: RawCreateParams) => ZodTuple; +const recordType: typeof ZodRecord.create; +const mapType: (keyType: Key, valueType: Value, params?: RawCreateParams) => ZodMap; +const setType: (valueType: Value, params?: RawCreateParams) => ZodSet; +const functionType: typeof ZodFunction.create; +const lazyType: (getter: () => T, params?: RawCreateParams) => ZodLazy; +const literalType: (value: T, params?: RawCreateParams) => ZodLiteral; +const enumType: typeof createZodEnum; +const nativeEnumType: (values: T, params?: RawCreateParams) => ZodNativeEnum; +const promiseType: (schema: T, params?: RawCreateParams) => ZodPromise; +const effectsType: (schema: I, effect: Effect, params?: RawCreateParams) => ZodEffects; +const optionalType: (type: T, params?: RawCreateParams) => ZodOptional; +const nullableType: (type: T, params?: RawCreateParams) => ZodNullable; +const preprocessType: (preprocess: (arg: unknown, ctx: RefinementCtx) => unknown, schema: I, params?: RawCreateParams) => ZodEffects; +const pipelineType: typeof ZodPipeline.create; +const ostring: () => ZodOptional; +const onumber: () => ZodOptional; +const oboolean: () => ZodOptional; +const coerce: { + string: (params?: RawCreateParams & { + coerce?: true; + }) => ZodString; + number: (params?: RawCreateParams & { + coerce?: boolean; + }) => ZodNumber; + boolean: (params?: RawCreateParams & { + coerce?: boolean; + }) => ZodBoolean; + bigint: (params?: RawCreateParams & { + coerce?: boolean; + }) => ZodBigInt; + date: (params?: RawCreateParams & { + coerce?: boolean; + }) => ZodDate; +}; + +const NEVER: never; + +//# sourceMappingURL=external.d.ts.map + +type z_AnyZodObject = AnyZodObject; +type z_AnyZodTuple = AnyZodTuple; +type z_ArrayCardinality = ArrayCardinality; +type z_ArrayKeys = ArrayKeys; +type z_AssertArray = AssertArray; +type z_AsyncParseReturnType = AsyncParseReturnType; +type z_BRAND = BRAND; +type z_CatchallInput = CatchallInput; +type z_CatchallOutput = CatchallOutput; +type z_CustomErrorParams = CustomErrorParams; +const z_DIRTY: typeof DIRTY; +type z_DenormalizedError = DenormalizedError; +const z_EMPTY_PATH: typeof EMPTY_PATH; +type z_Effect = Effect; +type z_EnumLike = EnumLike; +type z_EnumValues = EnumValues; +type z_ErrorMapCtx = ErrorMapCtx; +type z_FilterEnum = FilterEnum; +const z_INVALID: typeof INVALID; +type z_Indices = Indices; +type z_InnerTypeOfFunction, Returns extends ZodTypeAny> = InnerTypeOfFunction; +type z_InputTypeOfTuple = InputTypeOfTuple; +type z_InputTypeOfTupleWithRest = InputTypeOfTupleWithRest; +type z_IpVersion = IpVersion; +type z_IssueData = IssueData; +type z_KeySchema = KeySchema; +const z_NEVER: typeof NEVER; +const z_OK: typeof OK; +type z_ObjectPair = ObjectPair; +type z_OuterTypeOfFunction, Returns extends ZodTypeAny> = OuterTypeOfFunction; +type z_OutputTypeOfTuple = OutputTypeOfTuple; +type z_OutputTypeOfTupleWithRest = OutputTypeOfTupleWithRest; +type z_ParseContext = ParseContext; +type z_ParseInput = ParseInput; +type z_ParseParams = ParseParams; +type z_ParsePath = ParsePath; +type z_ParsePathComponent = ParsePathComponent; +type z_ParseResult = ParseResult; +type z_ParseReturnType = ParseReturnType; +type z_ParseStatus = ParseStatus; +const z_ParseStatus: typeof ParseStatus; +type z_PassthroughType = PassthroughType; +type z_PreprocessEffect = PreprocessEffect; +type z_Primitive = Primitive; +type z_ProcessedCreateParams = ProcessedCreateParams; +type z_RawCreateParams = RawCreateParams; +type z_RecordType = RecordType; +type z_Refinement = Refinement; +type z_RefinementCtx = RefinementCtx; +type z_RefinementEffect = RefinementEffect; +type z_SafeParseError = SafeParseError; +type z_SafeParseReturnType = SafeParseReturnType; +type z_SafeParseSuccess = SafeParseSuccess; +type z_Scalars = Scalars; +type z_SomeZodObject = SomeZodObject; +type z_StringValidation = StringValidation; +type z_SuperRefinement = SuperRefinement; +type z_SyncParseReturnType = SyncParseReturnType; +type z_TransformEffect = TransformEffect; +type z_TypeOf> = TypeOf; +type z_UnknownKeysParam = UnknownKeysParam; +type z_Values = Values; +type z_Writeable = Writeable; +type z_ZodAny = ZodAny; +const z_ZodAny: typeof ZodAny; +type z_ZodAnyDef = ZodAnyDef; +type z_ZodArray = ZodArray; +const z_ZodArray: typeof ZodArray; +type z_ZodArrayDef = ZodArrayDef; +type z_ZodBigInt = ZodBigInt; +const z_ZodBigInt: typeof ZodBigInt; +type z_ZodBigIntCheck = ZodBigIntCheck; +type z_ZodBigIntDef = ZodBigIntDef; +type z_ZodBoolean = ZodBoolean; +const z_ZodBoolean: typeof ZodBoolean; +type z_ZodBooleanDef = ZodBooleanDef; +type z_ZodBranded = ZodBranded; +const z_ZodBranded: typeof ZodBranded; +type z_ZodBrandedDef = ZodBrandedDef; +type z_ZodCatch = ZodCatch; +const z_ZodCatch: typeof ZodCatch; +type z_ZodCatchDef = ZodCatchDef; +type z_ZodCustomIssue = ZodCustomIssue; +type z_ZodDate = ZodDate; +const z_ZodDate: typeof ZodDate; +type z_ZodDateCheck = ZodDateCheck; +type z_ZodDateDef = ZodDateDef; +type z_ZodDefault = ZodDefault; +const z_ZodDefault: typeof ZodDefault; +type z_ZodDefaultDef = ZodDefaultDef; +type z_ZodDiscriminatedUnion[]> = ZodDiscriminatedUnion; +const z_ZodDiscriminatedUnion: typeof ZodDiscriminatedUnion; +type z_ZodDiscriminatedUnionDef[] = ZodDiscriminatedUnionOption[]> = ZodDiscriminatedUnionDef; +type z_ZodDiscriminatedUnionOption = ZodDiscriminatedUnionOption; +type z_ZodEffects, Input = input> = ZodEffects; +const z_ZodEffects: typeof ZodEffects; +type z_ZodEffectsDef = ZodEffectsDef; +type z_ZodEnum = ZodEnum; +const z_ZodEnum: typeof ZodEnum; +type z_ZodEnumDef = ZodEnumDef; +type z_ZodError = ZodError; +const z_ZodError: typeof ZodError; +type z_ZodErrorMap = ZodErrorMap; +type z_ZodFirstPartySchemaTypes = ZodFirstPartySchemaTypes; +type z_ZodFirstPartyTypeKind = ZodFirstPartyTypeKind; +const z_ZodFirstPartyTypeKind: typeof ZodFirstPartyTypeKind; +type z_ZodFormattedError = ZodFormattedError; +type z_ZodFunction, Returns extends ZodTypeAny> = ZodFunction; +const z_ZodFunction: typeof ZodFunction; +type z_ZodFunctionDef = ZodTuple, Returns extends ZodTypeAny = ZodTypeAny> = ZodFunctionDef; +type z_ZodIntersection = ZodIntersection; +const z_ZodIntersection: typeof ZodIntersection; +type z_ZodIntersectionDef = ZodIntersectionDef; +type z_ZodInvalidArgumentsIssue = ZodInvalidArgumentsIssue; +type z_ZodInvalidDateIssue = ZodInvalidDateIssue; +type z_ZodInvalidEnumValueIssue = ZodInvalidEnumValueIssue; +type z_ZodInvalidIntersectionTypesIssue = ZodInvalidIntersectionTypesIssue; +type z_ZodInvalidLiteralIssue = ZodInvalidLiteralIssue; +type z_ZodInvalidReturnTypeIssue = ZodInvalidReturnTypeIssue; +type z_ZodInvalidStringIssue = ZodInvalidStringIssue; +type z_ZodInvalidTypeIssue = ZodInvalidTypeIssue; +type z_ZodInvalidUnionDiscriminatorIssue = ZodInvalidUnionDiscriminatorIssue; +type z_ZodInvalidUnionIssue = ZodInvalidUnionIssue; +type z_ZodIssue = ZodIssue; +type z_ZodIssueBase = ZodIssueBase; +type z_ZodIssueCode = ZodIssueCode; +type z_ZodIssueOptionalMessage = ZodIssueOptionalMessage; +type z_ZodLazy = ZodLazy; +const z_ZodLazy: typeof ZodLazy; +type z_ZodLazyDef = ZodLazyDef; +type z_ZodLiteral = ZodLiteral; +const z_ZodLiteral: typeof ZodLiteral; +type z_ZodLiteralDef = ZodLiteralDef; +type z_ZodMap = ZodMap; +const z_ZodMap: typeof ZodMap; +type z_ZodMapDef = ZodMapDef; +type z_ZodNaN = ZodNaN; +const z_ZodNaN: typeof ZodNaN; +type z_ZodNaNDef = ZodNaNDef; +type z_ZodNativeEnum = ZodNativeEnum; +const z_ZodNativeEnum: typeof ZodNativeEnum; +type z_ZodNativeEnumDef = ZodNativeEnumDef; +type z_ZodNever = ZodNever; +const z_ZodNever: typeof ZodNever; +type z_ZodNeverDef = ZodNeverDef; +type z_ZodNonEmptyArray = ZodNonEmptyArray; +type z_ZodNotFiniteIssue = ZodNotFiniteIssue; +type z_ZodNotMultipleOfIssue = ZodNotMultipleOfIssue; +type z_ZodNull = ZodNull; +const z_ZodNull: typeof ZodNull; +type z_ZodNullDef = ZodNullDef; +type z_ZodNullable = ZodNullable; +const z_ZodNullable: typeof ZodNullable; +type z_ZodNullableDef = ZodNullableDef; +type z_ZodNullableType = ZodNullableType; +type z_ZodNumber = ZodNumber; +const z_ZodNumber: typeof ZodNumber; +type z_ZodNumberCheck = ZodNumberCheck; +type z_ZodNumberDef = ZodNumberDef; +type z_ZodObject, Input = objectInputType> = ZodObject; +const z_ZodObject: typeof ZodObject; +type z_ZodObjectDef = ZodObjectDef; +type z_ZodOptional = ZodOptional; +const z_ZodOptional: typeof ZodOptional; +type z_ZodOptionalDef = ZodOptionalDef; +type z_ZodOptionalType = ZodOptionalType; +type z_ZodParsedType = ZodParsedType; +type z_ZodPipeline = ZodPipeline; +const z_ZodPipeline: typeof ZodPipeline; +type z_ZodPipelineDef = ZodPipelineDef; +type z_ZodPromise = ZodPromise; +const z_ZodPromise: typeof ZodPromise; +type z_ZodPromiseDef = ZodPromiseDef; +type z_ZodRawShape = ZodRawShape; +type z_ZodReadonly = ZodReadonly; +const z_ZodReadonly: typeof ZodReadonly; +type z_ZodReadonlyDef = ZodReadonlyDef; +type z_ZodRecord = ZodRecord; +const z_ZodRecord: typeof ZodRecord; +type z_ZodRecordDef = ZodRecordDef; +type z_ZodSet = ZodSet; +const z_ZodSet: typeof ZodSet; +type z_ZodSetDef = ZodSetDef; +type z_ZodString = ZodString; +const z_ZodString: typeof ZodString; +type z_ZodStringCheck = ZodStringCheck; +type z_ZodStringDef = ZodStringDef; +type z_ZodSymbol = ZodSymbol; +const z_ZodSymbol: typeof ZodSymbol; +type z_ZodSymbolDef = ZodSymbolDef; +type z_ZodTooBigIssue = ZodTooBigIssue; +type z_ZodTooSmallIssue = ZodTooSmallIssue; +type z_ZodTuple = ZodTuple; +const z_ZodTuple: typeof ZodTuple; +type z_ZodTupleDef = ZodTupleDef; +type z_ZodTupleItems = ZodTupleItems; +type z_ZodType = ZodType; +const z_ZodType: typeof ZodType; +type z_ZodTypeAny = ZodTypeAny; +type z_ZodTypeDef = ZodTypeDef; +type z_ZodUndefined = ZodUndefined; +const z_ZodUndefined: typeof ZodUndefined; +type z_ZodUndefinedDef = ZodUndefinedDef; +type z_ZodUnion = ZodUnion; +const z_ZodUnion: typeof ZodUnion; +type z_ZodUnionDef> = ZodUnionDef; +type z_ZodUnionOptions = ZodUnionOptions; +type z_ZodUnknown = ZodUnknown; +const z_ZodUnknown: typeof ZodUnknown; +type z_ZodUnknownDef = ZodUnknownDef; +type z_ZodUnrecognizedKeysIssue = ZodUnrecognizedKeysIssue; +type z_ZodVoid = ZodVoid; +const z_ZodVoid: typeof ZodVoid; +type z_ZodVoidDef = ZodVoidDef; +const z_addIssueToContext: typeof addIssueToContext; +type z_arrayOutputType = arrayOutputType; +type z_baseObjectInputType = baseObjectInputType; +type z_baseObjectOutputType = baseObjectOutputType; +const z_coerce: typeof coerce; +const z_custom: typeof custom; +const z_datetimeRegex: typeof datetimeRegex; +type z_deoptional = deoptional; +const z_getErrorMap: typeof getErrorMap; +const z_getParsedType: typeof getParsedType; +type z_inferFlattenedErrors, U = string> = inferFlattenedErrors; +type z_inferFormattedError, U = string> = inferFormattedError; +type z_input> = input; +const z_isAborted: typeof isAborted; +const z_isAsync: typeof isAsync; +const z_isDirty: typeof isDirty; +const z_isValid: typeof isValid; +const z_late: typeof late; +const z_makeIssue: typeof makeIssue; +type z_mergeTypes = mergeTypes; +type z_noUnrecognized = noUnrecognized; +type z_objectInputType = objectInputType; +type z_objectOutputType = objectOutputType; +const z_objectUtil: typeof objectUtil; +const z_oboolean: typeof oboolean; +const z_onumber: typeof onumber; +const z_ostring: typeof ostring; +type z_output> = output; +const z_quotelessJson: typeof quotelessJson; +const z_setErrorMap: typeof setErrorMap; +type z_typeToFlattenedError = typeToFlattenedError; +type z_typecast = typecast; +const z_util: typeof util; +namespace z { + export { type z_AnyZodObject as AnyZodObject, type z_AnyZodTuple as AnyZodTuple, type z_ArrayCardinality as ArrayCardinality, type z_ArrayKeys as ArrayKeys, type z_AssertArray as AssertArray, type z_AsyncParseReturnType as AsyncParseReturnType, type z_BRAND as BRAND, type z_CatchallInput as CatchallInput, type z_CatchallOutput as CatchallOutput, type z_CustomErrorParams as CustomErrorParams, z_DIRTY as DIRTY, type z_DenormalizedError as DenormalizedError, z_EMPTY_PATH as EMPTY_PATH, type z_Effect as Effect, type z_EnumLike as EnumLike, type z_EnumValues as EnumValues, type z_ErrorMapCtx as ErrorMapCtx, type z_FilterEnum as FilterEnum, z_INVALID as INVALID, type z_Indices as Indices, type z_InnerTypeOfFunction as InnerTypeOfFunction, type z_InputTypeOfTuple as InputTypeOfTuple, type z_InputTypeOfTupleWithRest as InputTypeOfTupleWithRest, type z_IpVersion as IpVersion, type z_IssueData as IssueData, type z_KeySchema as KeySchema, z_NEVER as NEVER, z_OK as OK, type z_ObjectPair as ObjectPair, type z_OuterTypeOfFunction as OuterTypeOfFunction, type z_OutputTypeOfTuple as OutputTypeOfTuple, type z_OutputTypeOfTupleWithRest as OutputTypeOfTupleWithRest, type z_ParseContext as ParseContext, type z_ParseInput as ParseInput, type z_ParseParams as ParseParams, type z_ParsePath as ParsePath, type z_ParsePathComponent as ParsePathComponent, type z_ParseResult as ParseResult, type z_ParseReturnType as ParseReturnType, z_ParseStatus as ParseStatus, type z_PassthroughType as PassthroughType, type z_PreprocessEffect as PreprocessEffect, type z_Primitive as Primitive, type z_ProcessedCreateParams as ProcessedCreateParams, type z_RawCreateParams as RawCreateParams, type z_RecordType as RecordType, type z_Refinement as Refinement, type z_RefinementCtx as RefinementCtx, type z_RefinementEffect as RefinementEffect, type z_SafeParseError as SafeParseError, type z_SafeParseReturnType as SafeParseReturnType, type z_SafeParseSuccess as SafeParseSuccess, type z_Scalars as Scalars, ZodType as Schema, type z_SomeZodObject as SomeZodObject, type z_StringValidation as StringValidation, type z_SuperRefinement as SuperRefinement, type z_SyncParseReturnType as SyncParseReturnType, type z_TransformEffect as TransformEffect, type z_TypeOf as TypeOf, type z_UnknownKeysParam as UnknownKeysParam, type z_Values as Values, type z_Writeable as Writeable, z_ZodAny as ZodAny, type z_ZodAnyDef as ZodAnyDef, z_ZodArray as ZodArray, type z_ZodArrayDef as ZodArrayDef, z_ZodBigInt as ZodBigInt, type z_ZodBigIntCheck as ZodBigIntCheck, type z_ZodBigIntDef as ZodBigIntDef, z_ZodBoolean as ZodBoolean, type z_ZodBooleanDef as ZodBooleanDef, z_ZodBranded as ZodBranded, type z_ZodBrandedDef as ZodBrandedDef, z_ZodCatch as ZodCatch, type z_ZodCatchDef as ZodCatchDef, type z_ZodCustomIssue as ZodCustomIssue, z_ZodDate as ZodDate, type z_ZodDateCheck as ZodDateCheck, type z_ZodDateDef as ZodDateDef, z_ZodDefault as ZodDefault, type z_ZodDefaultDef as ZodDefaultDef, z_ZodDiscriminatedUnion as ZodDiscriminatedUnion, type z_ZodDiscriminatedUnionDef as ZodDiscriminatedUnionDef, type z_ZodDiscriminatedUnionOption as ZodDiscriminatedUnionOption, z_ZodEffects as ZodEffects, type z_ZodEffectsDef as ZodEffectsDef, z_ZodEnum as ZodEnum, type z_ZodEnumDef as ZodEnumDef, z_ZodError as ZodError, type z_ZodErrorMap as ZodErrorMap, type z_ZodFirstPartySchemaTypes as ZodFirstPartySchemaTypes, z_ZodFirstPartyTypeKind as ZodFirstPartyTypeKind, type z_ZodFormattedError as ZodFormattedError, z_ZodFunction as ZodFunction, type z_ZodFunctionDef as ZodFunctionDef, z_ZodIntersection as ZodIntersection, type z_ZodIntersectionDef as ZodIntersectionDef, type z_ZodInvalidArgumentsIssue as ZodInvalidArgumentsIssue, type z_ZodInvalidDateIssue as ZodInvalidDateIssue, type z_ZodInvalidEnumValueIssue as ZodInvalidEnumValueIssue, type z_ZodInvalidIntersectionTypesIssue as ZodInvalidIntersectionTypesIssue, type z_ZodInvalidLiteralIssue as ZodInvalidLiteralIssue, type z_ZodInvalidReturnTypeIssue as ZodInvalidReturnTypeIssue, type z_ZodInvalidStringIssue as ZodInvalidStringIssue, type z_ZodInvalidTypeIssue as ZodInvalidTypeIssue, type z_ZodInvalidUnionDiscriminatorIssue as ZodInvalidUnionDiscriminatorIssue, type z_ZodInvalidUnionIssue as ZodInvalidUnionIssue, type z_ZodIssue as ZodIssue, type z_ZodIssueBase as ZodIssueBase, type z_ZodIssueCode as ZodIssueCode, type z_ZodIssueOptionalMessage as ZodIssueOptionalMessage, z_ZodLazy as ZodLazy, type z_ZodLazyDef as ZodLazyDef, z_ZodLiteral as ZodLiteral, type z_ZodLiteralDef as ZodLiteralDef, z_ZodMap as ZodMap, type z_ZodMapDef as ZodMapDef, z_ZodNaN as ZodNaN, type z_ZodNaNDef as ZodNaNDef, z_ZodNativeEnum as ZodNativeEnum, type z_ZodNativeEnumDef as ZodNativeEnumDef, z_ZodNever as ZodNever, type z_ZodNeverDef as ZodNeverDef, type z_ZodNonEmptyArray as ZodNonEmptyArray, type z_ZodNotFiniteIssue as ZodNotFiniteIssue, type z_ZodNotMultipleOfIssue as ZodNotMultipleOfIssue, z_ZodNull as ZodNull, type z_ZodNullDef as ZodNullDef, z_ZodNullable as ZodNullable, type z_ZodNullableDef as ZodNullableDef, type z_ZodNullableType as ZodNullableType, z_ZodNumber as ZodNumber, type z_ZodNumberCheck as ZodNumberCheck, type z_ZodNumberDef as ZodNumberDef, z_ZodObject as ZodObject, type z_ZodObjectDef as ZodObjectDef, z_ZodOptional as ZodOptional, type z_ZodOptionalDef as ZodOptionalDef, type z_ZodOptionalType as ZodOptionalType, type z_ZodParsedType as ZodParsedType, z_ZodPipeline as ZodPipeline, type z_ZodPipelineDef as ZodPipelineDef, z_ZodPromise as ZodPromise, type z_ZodPromiseDef as ZodPromiseDef, type z_ZodRawShape as ZodRawShape, z_ZodReadonly as ZodReadonly, type z_ZodReadonlyDef as ZodReadonlyDef, z_ZodRecord as ZodRecord, type z_ZodRecordDef as ZodRecordDef, ZodType as ZodSchema, z_ZodSet as ZodSet, type z_ZodSetDef as ZodSetDef, z_ZodString as ZodString, type z_ZodStringCheck as ZodStringCheck, type z_ZodStringDef as ZodStringDef, z_ZodSymbol as ZodSymbol, type z_ZodSymbolDef as ZodSymbolDef, type z_ZodTooBigIssue as ZodTooBigIssue, type z_ZodTooSmallIssue as ZodTooSmallIssue, ZodEffects as ZodTransformer, z_ZodTuple as ZodTuple, type z_ZodTupleDef as ZodTupleDef, type z_ZodTupleItems as ZodTupleItems, z_ZodType as ZodType, type z_ZodTypeAny as ZodTypeAny, type z_ZodTypeDef as ZodTypeDef, z_ZodUndefined as ZodUndefined, type z_ZodUndefinedDef as ZodUndefinedDef, z_ZodUnion as ZodUnion, type z_ZodUnionDef as ZodUnionDef, type z_ZodUnionOptions as ZodUnionOptions, z_ZodUnknown as ZodUnknown, type z_ZodUnknownDef as ZodUnknownDef, type z_ZodUnrecognizedKeysIssue as ZodUnrecognizedKeysIssue, z_ZodVoid as ZodVoid, type z_ZodVoidDef as ZodVoidDef, z_addIssueToContext as addIssueToContext, anyType as any, arrayType as array, type z_arrayOutputType as arrayOutputType, type z_baseObjectInputType as baseObjectInputType, type z_baseObjectOutputType as baseObjectOutputType, bigIntType as bigint, booleanType as boolean, z_coerce as coerce, z_custom as custom, dateType as date, z_datetimeRegex as datetimeRegex, errorMap as defaultErrorMap, type z_deoptional as deoptional, discriminatedUnionType as discriminatedUnion, effectsType as effect, enumType as enum, functionType as function, z_getErrorMap as getErrorMap, z_getParsedType as getParsedType, type TypeOf as infer, type z_inferFlattenedErrors as inferFlattenedErrors, type z_inferFormattedError as inferFormattedError, type z_input as input, instanceOfType as instanceof, intersectionType as intersection, z_isAborted as isAborted, z_isAsync as isAsync, z_isDirty as isDirty, z_isValid as isValid, z_late as late, lazyType as lazy, literalType as literal, z_makeIssue as makeIssue, mapType as map, type z_mergeTypes as mergeTypes, nanType as nan, nativeEnumType as nativeEnum, neverType as never, type z_noUnrecognized as noUnrecognized, nullType as null, nullableType as nullable, numberType as number, objectType as object, type z_objectInputType as objectInputType, type z_objectOutputType as objectOutputType, z_objectUtil as objectUtil, z_oboolean as oboolean, z_onumber as onumber, optionalType as optional, z_ostring as ostring, type z_output as output, pipelineType as pipeline, preprocessType as preprocess, promiseType as promise, z_quotelessJson as quotelessJson, recordType as record, setType as set, z_setErrorMap as setErrorMap, strictObjectType as strictObject, stringType as string, symbolType as symbol, effectsType as transformer, tupleType as tuple, type z_typeToFlattenedError as typeToFlattenedError, type z_typecast as typecast, undefinedType as undefined, unionType as union, unknownType as unknown, z_util as util, voidType as void }; +} + +//# sourceMappingURL=index.d.ts.map + +export { type AnyZodObject, type AnyZodTuple, type ArrayCardinality, type ArrayKeys, type AssertArray, type AsyncParseReturnType, BRAND, type CatchallInput, type CatchallOutput, type CustomErrorParams, DIRTY, type DenormalizedError, EMPTY_PATH, type Effect, type EnumLike, type EnumValues, type ErrorMapCtx, type FilterEnum, INVALID, type Indices, type InnerTypeOfFunction, type InputTypeOfTuple, type InputTypeOfTupleWithRest, type IpVersion, type IssueData, type KeySchema, NEVER, OK, type ObjectPair, type OuterTypeOfFunction, type OutputTypeOfTuple, type OutputTypeOfTupleWithRest, type ParseContext, type ParseInput, type ParseParams, type ParsePath, type ParsePathComponent, type ParseResult, type ParseReturnType, ParseStatus, type PassthroughType, type PreprocessEffect, type Primitive, type ProcessedCreateParams, type RawCreateParams, type RecordType, type Refinement, type RefinementCtx, type RefinementEffect, type SafeParseError, type SafeParseReturnType, type SafeParseSuccess, type Scalars, ZodType as Schema, type SomeZodObject, type StringValidation, type SuperRefinement, type SyncParseReturnType, type TransformEffect, type TypeOf, type UnknownKeysParam, type Values, type Writeable, ZodAny, type ZodAnyDef, ZodArray, type ZodArrayDef, ZodBigInt, type ZodBigIntCheck, type ZodBigIntDef, ZodBoolean, type ZodBooleanDef, ZodBranded, type ZodBrandedDef, ZodCatch, type ZodCatchDef, type ZodCustomIssue, ZodDate, type ZodDateCheck, type ZodDateDef, ZodDefault, type ZodDefaultDef, ZodDiscriminatedUnion, type ZodDiscriminatedUnionDef, type ZodDiscriminatedUnionOption, ZodEffects, type ZodEffectsDef, ZodEnum, type ZodEnumDef, ZodError, type ZodErrorMap, type ZodFirstPartySchemaTypes, ZodFirstPartyTypeKind, type ZodFormattedError, ZodFunction, type ZodFunctionDef, ZodIntersection, type ZodIntersectionDef, type ZodInvalidArgumentsIssue, type ZodInvalidDateIssue, type ZodInvalidEnumValueIssue, type ZodInvalidIntersectionTypesIssue, type ZodInvalidLiteralIssue, type ZodInvalidReturnTypeIssue, type ZodInvalidStringIssue, type ZodInvalidTypeIssue, type ZodInvalidUnionDiscriminatorIssue, type ZodInvalidUnionIssue, type ZodIssue, type ZodIssueBase, ZodIssueCode, type ZodIssueOptionalMessage, ZodLazy, type ZodLazyDef, ZodLiteral, type ZodLiteralDef, ZodMap, type ZodMapDef, ZodNaN, type ZodNaNDef, ZodNativeEnum, type ZodNativeEnumDef, ZodNever, type ZodNeverDef, type ZodNonEmptyArray, type ZodNotFiniteIssue, type ZodNotMultipleOfIssue, ZodNull, type ZodNullDef, ZodNullable, type ZodNullableDef, type ZodNullableType, ZodNumber, type ZodNumberCheck, type ZodNumberDef, ZodObject, type ZodObjectDef, ZodOptional, type ZodOptionalDef, type ZodOptionalType, ZodParsedType, ZodPipeline, type ZodPipelineDef, ZodPromise, type ZodPromiseDef, type ZodRawShape, ZodReadonly, type ZodReadonlyDef, ZodRecord, type ZodRecordDef, ZodType as ZodSchema, ZodSet, type ZodSetDef, ZodString, type ZodStringCheck, type ZodStringDef, ZodSymbol, type ZodSymbolDef, type ZodTooBigIssue, type ZodTooSmallIssue, ZodEffects as ZodTransformer, ZodTuple, type ZodTupleDef, type ZodTupleItems, ZodType, type ZodTypeAny, type ZodTypeDef, ZodUndefined, type ZodUndefinedDef, ZodUnion, type ZodUnionDef, type ZodUnionOptions, ZodUnknown, type ZodUnknownDef, type ZodUnrecognizedKeysIssue, ZodVoid, type ZodVoidDef, addIssueToContext, anyType as any, arrayType as array, type arrayOutputType, type baseObjectInputType, type baseObjectOutputType, bigIntType as bigint, booleanType as boolean, coerce, custom, dateType as date, datetimeRegex, z as default, errorMap as defaultErrorMap, type deoptional, discriminatedUnionType as discriminatedUnion, effectsType as effect, enumType as enum, functionType as function, getErrorMap, getParsedType, type TypeOf as infer, type inferFlattenedErrors, type inferFormattedError, type input, instanceOfType as instanceof, intersectionType as intersection, isAborted, isAsync, isDirty, isValid, late, lazyType as lazy, literalType as literal, makeIssue, mapType as map, type mergeTypes, nanType as nan, nativeEnumType as nativeEnum, neverType as never, type noUnrecognized, nullType as null, nullableType as nullable, numberType as number, objectType as object, type objectInputType, type objectOutputType, objectUtil, oboolean, onumber, optionalType as optional, ostring, type output, pipelineType as pipeline, preprocessType as preprocess, promiseType as promise, quotelessJson, recordType as record, setType as set, setErrorMap, strictObjectType as strictObject, stringType as string, symbolType as symbol, effectsType as transformer, tupleType as tuple, type typeToFlattenedError, type typecast, undefinedType as undefined, unionType as union, unknownType as unknown, util, voidType as void, z }; +} + + + +// ================================================================================================== +// JSON Schema Draft 07 +// ================================================================================================== +// https://tools.ietf.org/html/draft-handrews-json-schema-validation-01 +// -------------------------------------------------------------------------------------------------- + +/** + * Primitive type + * @see https://tools.ietf.org/html/draft-handrews-json-schema-validation-01#section-6.1.1 + */ +type JSONSchema7TypeName = +| "string" // +| "number" +| "integer" +| "boolean" +| "object" +| "array" +| "null"; + +/** +* Primitive type +* @see https://tools.ietf.org/html/draft-handrews-json-schema-validation-01#section-6.1.1 +*/ +type JSONSchema7Type = +| string // +| number +| boolean +| JSONSchema7Object +| JSONSchema7Array +| null; + +// Workaround for infinite type recursion +interface JSONSchema7Object { +[key: string]: JSONSchema7Type; +} + +// Workaround for infinite type recursion +// https://github.com/Microsoft/TypeScript/issues/3496#issuecomment-128553540 +interface JSONSchema7Array extends Array {} + +/** +* Meta schema +* +* Recommended values: +* - 'http://json-schema.org/schema#' +* - 'http://json-schema.org/hyper-schema#' +* - 'http://json-schema.org/draft-07/schema#' +* - 'http://json-schema.org/draft-07/hyper-schema#' +* +* @see https://tools.ietf.org/html/draft-handrews-json-schema-validation-01#section-5 +*/ +type JSONSchema7Version = string; + +/** +* JSON Schema v7 +* @see https://tools.ietf.org/html/draft-handrews-json-schema-validation-01 +*/ +type JSONSchema7Definition = JSONSchema7 | boolean; +interface JSONSchema7 { +$id?: string | undefined; +$ref?: string | undefined; +$schema?: JSONSchema7Version | undefined; +$comment?: string | undefined; + +/** + * @see https://datatracker.ietf.org/doc/html/draft-bhutton-json-schema-00#section-8.2.4 + * @see https://datatracker.ietf.org/doc/html/draft-bhutton-json-schema-validation-00#appendix-A + */ +$defs?: { + [key: string]: JSONSchema7Definition; +} | undefined; + +/** + * @see https://tools.ietf.org/html/draft-handrews-json-schema-validation-01#section-6.1 + */ +type?: JSONSchema7TypeName | JSONSchema7TypeName[] | undefined; +enum?: JSONSchema7Type[] | undefined; +const?: JSONSchema7Type | undefined; + +/** + * @see https://tools.ietf.org/html/draft-handrews-json-schema-validation-01#section-6.2 + */ +multipleOf?: number | undefined; +maximum?: number | undefined; +exclusiveMaximum?: number | undefined; +minimum?: number | undefined; +exclusiveMinimum?: number | undefined; + +/** + * @see https://tools.ietf.org/html/draft-handrews-json-schema-validation-01#section-6.3 + */ +maxLength?: number | undefined; +minLength?: number | undefined; +pattern?: string | undefined; + +/** + * @see https://tools.ietf.org/html/draft-handrews-json-schema-validation-01#section-6.4 + */ +items?: JSONSchema7Definition | JSONSchema7Definition[] | undefined; +additionalItems?: JSONSchema7Definition | undefined; +maxItems?: number | undefined; +minItems?: number | undefined; +uniqueItems?: boolean | undefined; +contains?: JSONSchema7Definition | undefined; + +/** + * @see https://tools.ietf.org/html/draft-handrews-json-schema-validation-01#section-6.5 + */ +maxProperties?: number | undefined; +minProperties?: number | undefined; +required?: string[] | undefined; +properties?: { + [key: string]: JSONSchema7Definition; +} | undefined; +patternProperties?: { + [key: string]: JSONSchema7Definition; +} | undefined; +additionalProperties?: JSONSchema7Definition | undefined; +dependencies?: { + [key: string]: JSONSchema7Definition | string[]; +} | undefined; +propertyNames?: JSONSchema7Definition | undefined; + +/** + * @see https://tools.ietf.org/html/draft-handrews-json-schema-validation-01#section-6.6 + */ +if?: JSONSchema7Definition | undefined; +then?: JSONSchema7Definition | undefined; +else?: JSONSchema7Definition | undefined; + +/** + * @see https://tools.ietf.org/html/draft-handrews-json-schema-validation-01#section-6.7 + */ +allOf?: JSONSchema7Definition[] | undefined; +anyOf?: JSONSchema7Definition[] | undefined; +oneOf?: JSONSchema7Definition[] | undefined; +not?: JSONSchema7Definition | undefined; + +/** + * @see https://tools.ietf.org/html/draft-handrews-json-schema-validation-01#section-7 + */ +format?: string | undefined; + +/** + * @see https://tools.ietf.org/html/draft-handrews-json-schema-validation-01#section-8 + */ +contentMediaType?: string | undefined; +contentEncoding?: string | undefined; + +/** + * @see https://tools.ietf.org/html/draft-handrews-json-schema-validation-01#section-9 + */ +definitions?: { + [key: string]: JSONSchema7Definition; +} | undefined; + +/** + * @see https://tools.ietf.org/html/draft-handrews-json-schema-validation-01#section-10 + */ +title?: string | undefined; +description?: string | undefined; +default?: JSONSchema7Type | undefined; +readOnly?: boolean | undefined; +writeOnly?: boolean | undefined; +examples?: JSONSchema7Type | undefined; +} diff --git a/jamfile/jamfile.h b/jamfile/jamfile.h new file mode 100644 index 0000000000..76ce7f9887 --- /dev/null +++ b/jamfile/jamfile.h @@ -0,0 +1,6 @@ +#ifndef JAMFILE_H +#define JAMFILE_H + +#define JAMFILE_VERSION "0.0.1-alpha.0" + +#endif diff --git a/jamfile/js_builtins/assert.js b/jamfile/js_builtins/assert.js new file mode 100644 index 0000000000..984bfc94c8 --- /dev/null +++ b/jamfile/js_builtins/assert.js @@ -0,0 +1,1031 @@ +/** + * Original source: https://jsr.io/@std/assert + * + * Generated from jsr:@std/assert@1.09 using esbuild --bundle. + * + * The following changes were made to adapt with this project and QuickJS runtime: + * + * - References to Intl, Temporal, URL, and URLSearchParams globals were removed. + */ + +// node_modules/@std/assert/assertion_error.js +var AssertionError = class extends Error { + /** Constructs a new instance. + * + * @param message The error message. + * @param options Additional options. This argument is still unstable. It may change in the future release. + */ + constructor(message, options) { + super(message, options); + this.name = "AssertionError"; + } +}; + +// node_modules/@std/assert/almost_equals.js +function assertAlmostEquals(actual, expected, tolerance, msg) { + if (Object.is(actual, expected)) { + return; + } + const delta = Math.abs(expected - actual); + if (tolerance === void 0) { + tolerance = isFinite(expected) ? Math.abs(expected * 1e-7) : 1e-7; + } + if (delta <= tolerance) { + return; + } + const msgSuffix = msg ? `: ${msg}` : "."; + const f = (n) => Number.isInteger(n) ? n : n.toExponential(); + throw new AssertionError(`Expected actual: "${f(actual)}" to be close to "${f(expected)}": delta "${f(delta)}" is greater than "${f(tolerance)}"${msgSuffix}`); +} + +// node_modules/@std/assert/equal.js +function isKeyedCollection(x) { + return x instanceof Set || x instanceof Map; +} +function prototypesEqual(a, b) { + const pa = Object.getPrototypeOf(a); + const pb = Object.getPrototypeOf(b); + return pa === pb || pa === Object.prototype && pb === null || pa === null && pb === Object.prototype; +} +function isBasicObjectOrArray(obj) { + const proto = Object.getPrototypeOf(obj); + return proto === null || proto === Object.prototype || proto === Array.prototype; +} +function ownKeys(obj) { + return [ + ...Object.getOwnPropertyNames(obj), + ...Object.getOwnPropertySymbols(obj) + ]; +} +function getKeysDeep(obj) { + const keys = /* @__PURE__ */ new Set(); + while (obj !== Object.prototype && obj !== Array.prototype && obj != null) { + for (const key of ownKeys(obj)) { + keys.add(key); + } + obj = Object.getPrototypeOf(obj); + } + return keys; +} +var Temporal = globalThis.Temporal ?? new Proxy({}, { + get: () => { + } +}); +var stringComparablePrototypes = new Set([ + //Intl.Locale, + RegExp, + /*Temporal.Duration, + Temporal.Instant, + Temporal.PlainDate, + Temporal.PlainDateTime, + Temporal.PlainTime, + Temporal.PlainYearMonth, + Temporal.PlainMonthDay, + Temporal.ZonedDateTime, + URL, + URLSearchParams + */ +].filter((x) => x != null).map((x) => x.prototype)); +function isPrimitive(x) { + return typeof x === "string" || typeof x === "number" || typeof x === "boolean" || typeof x === "bigint" || typeof x === "symbol" || x == null; +} +var TypedArray = Object.getPrototypeOf(Uint8Array); +function compareTypedArrays(a, b) { + if (a.length !== b.length) return false; + for (let i = 0; i < b.length; i++) { + if (!sameValueZero(a[i], b[i])) return false; + } + return true; +} +function sameValueZero(a, b) { + return a === b || Object.is(a, b); +} +function equal(a, b) { + const seen = /* @__PURE__ */ new Map(); + return function compare(a2, b2) { + if (sameValueZero(a2, b2)) return true; + if (isPrimitive(a2) || isPrimitive(b2)) return false; + if (a2 instanceof Date && b2 instanceof Date) { + return Object.is(a2.getTime(), b2.getTime()); + } + if (a2 && typeof a2 === "object" && b2 && typeof b2 === "object") { + if (!prototypesEqual(a2, b2)) { + return false; + } + if (a2 instanceof TypedArray) { + return compareTypedArrays(a2, b2); + } + if (a2 instanceof WeakMap) { + throw new TypeError("cannot compare WeakMap instances"); + } + if (a2 instanceof WeakSet) { + throw new TypeError("cannot compare WeakSet instances"); + } + if (a2 instanceof WeakRef) { + return compare(a2.deref(), b2.deref()); + } + if (seen.get(a2) === b2) { + return true; + } + if (Object.keys(a2).length !== Object.keys(b2).length) { + return false; + } + seen.set(a2, b2); + if (isKeyedCollection(a2) && isKeyedCollection(b2)) { + if (a2.size !== b2.size) { + return false; + } + const aKeys = [ + ...a2.keys() + ]; + const primitiveKeysFastPath = aKeys.every(isPrimitive); + if (primitiveKeysFastPath) { + if (a2 instanceof Set) { + return a2.symmetricDifference(b2).size === 0; + } + for (const key of aKeys) { + if (!b2.has(key) || !compare(a2.get(key), b2.get(key))) { + return false; + } + } + return true; + } + let unmatchedEntries = a2.size; + for (const [aKey, aValue] of a2.entries()) { + for (const [bKey, bValue] of b2.entries()) { + if (!compare(aKey, bKey)) continue; + if (aKey === aValue && bKey === bValue || compare(aValue, bValue)) { + unmatchedEntries--; + break; + } + } + } + return unmatchedEntries === 0; + } + let keys; + if (isBasicObjectOrArray(a2)) { + keys = ownKeys({ + ...a2, + ...b2 + }); + } else if (stringComparablePrototypes.has(Object.getPrototypeOf(a2))) { + return String(a2) === String(b2); + } else { + keys = getKeysDeep(a2).union(getKeysDeep(b2)); + } + for (const key of keys) { + if (!compare(a2[key], b2[key])) { + return false; + } + if (key in a2 && !(key in b2) || key in b2 && !(key in a2)) { + return false; + } + } + return true; + } + return false; + }(a, b); +} + +// node_modules/@jsr/std__internal/format.js +function format(v) { + const { Deno: Deno2 } = globalThis; + return typeof Deno2?.inspect === "function" ? Deno2.inspect(v, { + depth: Infinity, + sorted: true, + trailingComma: true, + compact: false, + iterableLimit: Infinity, + // getters should be true in assertEquals. + getters: true, + strAbbreviateSize: Infinity + }) : `"${String(v).replace(/(?=["\\])/g, "\\")}"`; +} + +// node_modules/@std/assert/array_includes.js +function assertArrayIncludes(actual, expected, msg) { + const missing = []; + for (let i = 0; i < expected.length; i++) { + let found = false; + for (let j = 0; j < actual.length; j++) { + if (equal(expected[i], actual[j])) { + found = true; + break; + } + } + if (!found) { + missing.push(expected[i]); + } + } + if (missing.length === 0) { + return; + } + const msgSuffix = msg ? `: ${msg}` : "."; + msg = `Expected actual: "${format(actual)}" to include: "${format(expected)}"${msgSuffix} +missing: ${format(missing)}`; + throw new AssertionError(msg); +} + +// node_modules/@jsr/std__internal/styles.js +var { Deno } = globalThis; +var noColor = typeof Deno?.noColor === "boolean" ? Deno.noColor : false; +var enabled = !noColor; +function code(open, close) { + return { + open: `\x1B[${open.join(";")}m`, + close: `\x1B[${close}m`, + regexp: new RegExp(`\\x1b\\[${close}m`, "g") + }; +} +function run(str, code2) { + return enabled ? `${code2.open}${str.replace(code2.regexp, code2.open)}${code2.close}` : str; +} +function bold(str) { + return run(str, code([ + 1 + ], 22)); +} +function red(str) { + return run(str, code([ + 31 + ], 39)); +} +function green(str) { + return run(str, code([ + 32 + ], 39)); +} +function white(str) { + return run(str, code([ + 37 + ], 39)); +} +function gray(str) { + return brightBlack(str); +} +function brightBlack(str) { + return run(str, code([ + 90 + ], 39)); +} +function bgRed(str) { + return run(str, code([ + 41 + ], 49)); +} +function bgGreen(str) { + return run(str, code([ + 42 + ], 49)); +} +var ANSI_PATTERN = new RegExp([ + "[\\u001B\\u009B][[\\]()#;?]*(?:(?:(?:(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]+)*|[a-zA-Z\\d]+(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]*)*)?\\u0007)", + "(?:(?:\\d{1,4}(?:;\\d{0,4})*)?[\\dA-PR-TXZcf-nq-uy=><~]))" +].join("|"), "g"); +function stripAnsiCode(string) { + return string.replace(ANSI_PATTERN, ""); +} + +// node_modules/@jsr/std__internal/build_message.js +function createColor(diffType, background = false) { + switch (diffType) { + case "added": + return (s) => background ? bgGreen(white(s)) : green(bold(s)); + case "removed": + return (s) => background ? bgRed(white(s)) : red(bold(s)); + default: + return white; + } +} +function createSign(diffType) { + switch (diffType) { + case "added": + return "+ "; + case "removed": + return "- "; + default: + return " "; + } +} +function buildMessage(diffResult, options = {}) { + const { stringDiff = false } = options; + const messages = [ + "", + "", + ` ${gray(bold("[Diff]"))} ${red(bold("Actual"))} / ${green(bold("Expected"))}`, + "", + "" + ]; + const diffMessages = diffResult.map((result) => { + const color = createColor(result.type); + const line = result.details?.map((detail) => detail.type !== "common" ? createColor(detail.type, true)(detail.value) : detail.value).join("") ?? result.value; + return color(`${createSign(result.type)}${line}`); + }); + messages.push(...stringDiff ? [ + diffMessages.join("") + ] : diffMessages, ""); + return messages; +} + +// node_modules/@jsr/std__internal/diff.js +var REMOVED = 1; +var COMMON = 2; +var ADDED = 3; +function createCommon(A, B) { + const common = []; + if (A.length === 0 || B.length === 0) return []; + for (let i = 0; i < Math.min(A.length, B.length); i += 1) { + const a = A[i]; + const b = B[i]; + if (a !== void 0 && a === b) { + common.push(a); + } else { + return common; + } + } + return common; +} +function assertFp(value) { + if (value == null || typeof value !== "object" || typeof value?.y !== "number" || typeof value?.id !== "number") { + throw new Error(`Unexpected value, expected 'FarthestPoint': received ${typeof value}`); + } +} +function backTrace(A, B, current, swapped, routes, diffTypesPtrOffset) { + const M = A.length; + const N = B.length; + const result = []; + let a = M - 1; + let b = N - 1; + let j = routes[current.id]; + let type = routes[current.id + diffTypesPtrOffset]; + while (true) { + if (!j && !type) break; + const prev = j; + if (type === REMOVED) { + result.unshift({ + type: swapped ? "removed" : "added", + value: B[b] + }); + b -= 1; + } else if (type === ADDED) { + result.unshift({ + type: swapped ? "added" : "removed", + value: A[a] + }); + a -= 1; + } else { + result.unshift({ + type: "common", + value: A[a] + }); + a -= 1; + b -= 1; + } + j = routes[prev]; + type = routes[prev + diffTypesPtrOffset]; + } + return result; +} +function createFp(k, M, routes, diffTypesPtrOffset, ptr, slide, down) { + if (slide && slide.y === -1 && down && down.y === -1) { + return { + y: 0, + id: 0 + }; + } + const isAdding = down?.y === -1 || k === M || (slide?.y ?? 0) > (down?.y ?? 0) + 1; + if (slide && isAdding) { + const prev = slide.id; + ptr++; + routes[ptr] = prev; + routes[ptr + diffTypesPtrOffset] = ADDED; + return { + y: slide.y, + id: ptr + }; + } + if (down && !isAdding) { + const prev = down.id; + ptr++; + routes[ptr] = prev; + routes[ptr + diffTypesPtrOffset] = REMOVED; + return { + y: down.y + 1, + id: ptr + }; + } + throw new Error("Unexpected missing FarthestPoint"); +} +function diff(A, B) { + const prefixCommon = createCommon(A, B); + A = A.slice(prefixCommon.length); + B = B.slice(prefixCommon.length); + const swapped = B.length > A.length; + [A, B] = swapped ? [ + B, + A + ] : [ + A, + B + ]; + const M = A.length; + const N = B.length; + if (!M && !N && !prefixCommon.length) return []; + if (!N) { + return [ + ...prefixCommon.map((value) => ({ + type: "common", + value + })), + ...A.map((value) => ({ + type: swapped ? "added" : "removed", + value + })) + ]; + } + const offset = N; + const delta = M - N; + const length = M + N + 1; + const fp = Array.from({ + length + }, () => ({ + y: -1, + id: -1 + })); + const routes = new Uint32Array((M * N + length + 1) * 2); + const diffTypesPtrOffset = routes.length / 2; + let ptr = 0; + function snake(k, A2, B2, slide, down) { + const M2 = A2.length; + const N2 = B2.length; + const fp2 = createFp(k, M2, routes, diffTypesPtrOffset, ptr, slide, down); + ptr = fp2.id; + while (fp2.y + k < M2 && fp2.y < N2 && A2[fp2.y + k] === B2[fp2.y]) { + const prev = fp2.id; + ptr++; + fp2.id = ptr; + fp2.y += 1; + routes[ptr] = prev; + routes[ptr + diffTypesPtrOffset] = COMMON; + } + return fp2; + } + let currentFp = fp[delta + offset]; + assertFp(currentFp); + let p = -1; + while (currentFp.y < N) { + p = p + 1; + for (let k = -p; k < delta; ++k) { + const index2 = k + offset; + fp[index2] = snake(k, A, B, fp[index2 - 1], fp[index2 + 1]); + } + for (let k = delta + p; k > delta; --k) { + const index2 = k + offset; + fp[index2] = snake(k, A, B, fp[index2 - 1], fp[index2 + 1]); + } + const index = delta + offset; + fp[delta + offset] = snake(delta, A, B, fp[index - 1], fp[index + 1]); + currentFp = fp[delta + offset]; + assertFp(currentFp); + } + return [ + ...prefixCommon.map((value) => ({ + type: "common", + value + })), + ...backTrace(A, B, currentFp, swapped, routes, diffTypesPtrOffset) + ]; +} + +// node_modules/@jsr/std__internal/diff_str.js +function unescape(string) { + return string.replaceAll("\b", "\\b").replaceAll("\f", "\\f").replaceAll(" ", "\\t").replaceAll("\v", "\\v").replaceAll(/\r\n|\r|\n/g, (str) => str === "\r" ? "\\r" : str === "\n" ? "\\n\n" : "\\r\\n\r\n"); +} +var WHITESPACE_SYMBOLS = /([^\S\r\n]+|[()[\]{}'"\r\n]|\b)/; +function tokenize(string, wordDiff = false) { + if (wordDiff) { + return string.split(WHITESPACE_SYMBOLS).filter((token) => token); + } + const tokens = []; + const lines = string.split(/(\n|\r\n)/).filter((line) => line); + for (const [i, line] of lines.entries()) { + if (i % 2) { + tokens[tokens.length - 1] += line; + } else { + tokens.push(line); + } + } + return tokens; +} +function createDetails(line, tokens) { + return tokens.filter(({ type }) => type === line.type || type === "common").map((result, i, t) => { + const token = t[i - 1]; + if (result.type === "common" && token && token.type === t[i + 1]?.type && /\s+/.test(result.value)) { + return { + ...result, + type: token.type + }; + } + return result; + }); +} +var NON_WHITESPACE_REGEXP = /\S/; +function diffStr(A, B) { + const diffResult = diff(tokenize(`${unescape(A)} +`), tokenize(`${unescape(B)} +`)); + const added = []; + const removed = []; + for (const result of diffResult) { + if (result.type === "added") { + added.push(result); + } + if (result.type === "removed") { + removed.push(result); + } + } + const hasMoreRemovedLines = added.length < removed.length; + const aLines = hasMoreRemovedLines ? added : removed; + const bLines = hasMoreRemovedLines ? removed : added; + for (const a of aLines) { + let tokens = []; + let b; + while (bLines.length) { + b = bLines.shift(); + const tokenized = [ + tokenize(a.value, true), + tokenize(b.value, true) + ]; + if (hasMoreRemovedLines) tokenized.reverse(); + tokens = diff(tokenized[0], tokenized[1]); + if (tokens.some(({ type, value }) => type === "common" && NON_WHITESPACE_REGEXP.test(value))) { + break; + } + } + a.details = createDetails(a, tokens); + if (b) { + b.details = createDetails(b, tokens); + } + } + return diffResult; +} + +// node_modules/@std/assert/equals.js +function assertEquals(actual, expected, msg) { + if (equal(actual, expected)) { + return; + } + const msgSuffix = msg ? `: ${msg}` : "."; + let message = `Values are not equal${msgSuffix}`; + const actualString = format(actual); + const expectedString = format(expected); + const stringDiff = typeof actual === "string" && typeof expected === "string"; + const diffResult = stringDiff ? diffStr(actual, expected) : diff(actualString.split("\n"), expectedString.split("\n")); + const diffMsg = buildMessage(diffResult, { + stringDiff + }).join("\n"); + message = `${message} +${diffMsg}`; + throw new AssertionError(message); +} + +// node_modules/@std/assert/exists.js +function assertExists(actual, msg) { + if (actual === void 0 || actual === null) { + const msgSuffix = msg ? `: ${msg}` : "."; + msg = `Expected actual: "${actual}" to not be null or undefined${msgSuffix}`; + throw new AssertionError(msg); + } +} + +// node_modules/@std/assert/false.js +function assertFalse(expr, msg = "") { + if (expr) { + throw new AssertionError(msg); + } +} + +// node_modules/@std/assert/greater_or_equal.js +function assertGreaterOrEqual(actual, expected, msg) { + if (actual >= expected) return; + const actualString = format(actual); + const expectedString = format(expected); + throw new AssertionError(msg ?? `Expect ${actualString} >= ${expectedString}`); +} + +// node_modules/@std/assert/greater.js +function assertGreater(actual, expected, msg) { + if (actual > expected) return; + const actualString = format(actual); + const expectedString = format(expected); + throw new AssertionError(msg ?? `Expect ${actualString} > ${expectedString}`); +} + +// node_modules/@std/assert/instance_of.js +function assertInstanceOf(actual, expectedType, msg = "") { + if (actual instanceof expectedType) return; + const msgSuffix = msg ? `: ${msg}` : "."; + const expectedTypeStr = expectedType.name; + let actualTypeStr = ""; + if (actual === null) { + actualTypeStr = "null"; + } else if (actual === void 0) { + actualTypeStr = "undefined"; + } else if (typeof actual === "object") { + actualTypeStr = actual.constructor?.name ?? "Object"; + } else { + actualTypeStr = typeof actual; + } + if (expectedTypeStr === actualTypeStr) { + msg = `Expected object to be an instance of "${expectedTypeStr}"${msgSuffix}`; + } else if (actualTypeStr === "function") { + msg = `Expected object to be an instance of "${expectedTypeStr}" but was not an instanced object${msgSuffix}`; + } else { + msg = `Expected object to be an instance of "${expectedTypeStr}" but was "${actualTypeStr}"${msgSuffix}`; + } + throw new AssertionError(msg); +} + +// node_modules/@std/assert/is_error.js +function assertIsError(error, ErrorClass, msgMatches, msg) { + const msgSuffix = msg ? `: ${msg}` : "."; + if (!(error instanceof Error)) { + throw new AssertionError(`Expected "error" to be an Error object${msgSuffix}`); + } + if (ErrorClass && !(error instanceof ErrorClass)) { + msg = `Expected error to be instance of "${ErrorClass.name}", but was "${error?.constructor?.name}"${msgSuffix}`; + throw new AssertionError(msg); + } + let msgCheck; + if (typeof msgMatches === "string") { + msgCheck = stripAnsiCode(error.message).includes(stripAnsiCode(msgMatches)); + } + if (msgMatches instanceof RegExp) { + msgCheck = msgMatches.test(stripAnsiCode(error.message)); + } + if (msgMatches && !msgCheck) { + msg = `Expected error message to include ${msgMatches instanceof RegExp ? msgMatches.toString() : JSON.stringify(msgMatches)}, but got ${JSON.stringify(error?.message)}${msgSuffix}`; + throw new AssertionError(msg); + } +} + +// node_modules/@std/assert/less_or_equal.js +function assertLessOrEqual(actual, expected, msg) { + if (actual <= expected) return; + const actualString = format(actual); + const expectedString = format(expected); + throw new AssertionError(msg ?? `Expect ${actualString} <= ${expectedString}`); +} + +// node_modules/@std/assert/less.js +function assertLess(actual, expected, msg) { + if (actual < expected) return; + const actualString = format(actual); + const expectedString = format(expected); + throw new AssertionError(msg ?? `Expect ${actualString} < ${expectedString}`); +} + +// node_modules/@std/assert/match.js +function assertMatch(actual, expected, msg) { + if (expected.test(actual)) return; + const msgSuffix = msg ? `: ${msg}` : "."; + msg = `Expected actual: "${actual}" to match: "${expected}"${msgSuffix}`; + throw new AssertionError(msg); +} + +// node_modules/@std/assert/not_equals.js +function assertNotEquals(actual, expected, msg) { + if (!equal(actual, expected)) { + return; + } + const actualString = String(actual); + const expectedString = String(expected); + const msgSuffix = msg ? `: ${msg}` : "."; + throw new AssertionError(`Expected actual: ${actualString} not to be: ${expectedString}${msgSuffix}`); +} + +// node_modules/@std/assert/not_instance_of.js +function assertNotInstanceOf(actual, unexpectedType, msg) { + const msgSuffix = msg ? `: ${msg}` : "."; + msg = `Expected object to not be an instance of "${typeof unexpectedType}"${msgSuffix}`; + assertFalse(actual instanceof unexpectedType, msg); +} + +// node_modules/@std/assert/not_match.js +function assertNotMatch(actual, expected, msg) { + if (!expected.test(actual)) return; + const msgSuffix = msg ? `: ${msg}` : "."; + msg = `Expected actual: "${actual}" to not match: "${expected}"${msgSuffix}`; + throw new AssertionError(msg); +} + +// node_modules/@std/assert/not_strict_equals.js +function assertNotStrictEquals(actual, expected, msg) { + if (!Object.is(actual, expected)) { + return; + } + const msgSuffix = msg ? `: ${msg}` : "."; + throw new AssertionError(`Expected "actual" to not be strictly equal to: ${format(actual)}${msgSuffix} +`); +} + +// node_modules/@std/assert/object_match.js +function assertObjectMatch(actual, expected, msg) { + return assertEquals( + // get the intersection of "actual" and "expected" + // side effect: all the instances' constructor field is "Object" now. + filter(actual, expected), + // set (nested) instances' constructor field to be "Object" without changing expected value. + // see https://github.com/denoland/deno_std/pull/1419 + filter(expected, expected), + msg + ); +} +function isObject(val) { + return typeof val === "object" && val !== null; +} +function filter(a, b) { + const seen = /* @__PURE__ */ new WeakMap(); + return filterObject(a, b); + function filterObject(a2, b2) { + if (seen.has(a2) && seen.get(a2) === b2) { + return a2; + } + try { + seen.set(a2, b2); + } catch (err) { + if (err instanceof TypeError) { + throw new TypeError(`Cannot assertObjectMatch ${a2 === null ? null : `type ${typeof a2}`}`); + } + } + const filtered = {}; + const keysA = Reflect.ownKeys(a2); + const keysB = Reflect.ownKeys(b2); + const entries = keysA.filter((key) => keysB.includes(key)).map((key) => [ + key, + a2[key] + ]); + if (keysA.length && keysB.length && !entries.length) { + for (const key of keysA) { + filtered[key] = a2[key]; + } + return filtered; + } + for (const [key, value] of entries) { + if (value instanceof RegExp) { + filtered[key] = value; + continue; + } + const subset = b2[key]; + if (Array.isArray(value) && Array.isArray(subset)) { + filtered[key] = filterArray(value, subset); + continue; + } + if (isObject(value) && isObject(subset)) { + if (value instanceof Map && subset instanceof Map) { + filtered[key] = new Map([ + ...value + ].filter(([k]) => subset.has(k)).map(([k, v]) => { + const v2 = subset.get(k); + if (isObject(v) && isObject(v2)) { + return [ + k, + filterObject(v, v2) + ]; + } + return [ + k, + v + ]; + })); + continue; + } + if (value instanceof Set && subset instanceof Set) { + filtered[key] = value.intersection(subset); + continue; + } + filtered[key] = filterObject(value, subset); + continue; + } + filtered[key] = value; + } + return filtered; + } + function filterArray(a2, b2) { + if (seen.has(a2) && seen.get(a2) === b2) { + return a2; + } + seen.set(a2, b2); + const filtered = []; + const count = Math.min(a2.length, b2.length); + for (let i = 0; i < count; ++i) { + const value = a2[i]; + const subset = b2[i]; + if (value instanceof RegExp) { + filtered.push(value); + continue; + } + if (Array.isArray(value) && Array.isArray(subset)) { + filtered.push(filterArray(value, subset)); + continue; + } + if (isObject(value) && isObject(subset)) { + if (value instanceof Map && subset instanceof Map) { + const map = new Map([ + ...value + ].filter(([k]) => subset.has(k)).map(([k, v]) => { + const v2 = subset.get(k); + if (isObject(v) && isObject(v2)) { + return [ + k, + filterObject(v, v2) + ]; + } + return [ + k, + v + ]; + })); + filtered.push(map); + continue; + } + if (value instanceof Set && subset instanceof Set) { + filtered.push(value.intersection(subset)); + continue; + } + filtered.push(filterObject(value, subset)); + continue; + } + filtered.push(value); + } + return filtered; + } +} + +// node_modules/@std/assert/rejects.js +async function assertRejects(fn, errorClassOrMsg, msgIncludesOrMsg, msg) { + let ErrorClass; + let msgIncludes; + let err; + if (typeof errorClassOrMsg !== "string") { + if (errorClassOrMsg === void 0 || errorClassOrMsg.prototype instanceof Error || errorClassOrMsg.prototype === Error.prototype) { + ErrorClass = errorClassOrMsg; + msgIncludes = msgIncludesOrMsg; + } + } else { + msg = errorClassOrMsg; + } + let doesThrow = false; + let isPromiseReturned = false; + const msgSuffix = msg ? `: ${msg}` : "."; + try { + const possiblePromise = fn(); + if (possiblePromise && typeof possiblePromise === "object" && typeof possiblePromise.then === "function") { + isPromiseReturned = true; + await possiblePromise; + } else { + throw new Error(); + } + } catch (error) { + if (!isPromiseReturned) { + throw new AssertionError(`Function throws when expected to reject${msgSuffix}`); + } + if (ErrorClass) { + if (!(error instanceof Error)) { + throw new AssertionError(`A non-Error object was rejected${msgSuffix}`); + } + assertIsError(error, ErrorClass, msgIncludes, msg); + } + err = error; + doesThrow = true; + } + if (!doesThrow) { + throw new AssertionError(`Expected function to reject${msgSuffix}`); + } + return err; +} + +// node_modules/@std/assert/strict_equals.js +function assertStrictEquals(actual, expected, msg) { + if (Object.is(actual, expected)) { + return; + } + const msgSuffix = msg ? `: ${msg}` : "."; + let message; + const actualString = format(actual); + const expectedString = format(expected); + if (actualString === expectedString) { + const withOffset = actualString.split("\n").map((l) => ` ${l}`).join("\n"); + message = `Values have the same structure but are not reference-equal${msgSuffix} + +${red(withOffset)} +`; + } else { + const stringDiff = typeof actual === "string" && typeof expected === "string"; + const diffResult = stringDiff ? diffStr(actual, expected) : diff(actualString.split("\n"), expectedString.split("\n")); + const diffMsg = buildMessage(diffResult, { + stringDiff + }).join("\n"); + message = `Values are not strictly equal${msgSuffix} +${diffMsg}`; + } + throw new AssertionError(message); +} + +// node_modules/@std/assert/string_includes.js +function assertStringIncludes(actual, expected, msg) { + if (actual.includes(expected)) return; + const msgSuffix = msg ? `: ${msg}` : "."; + msg = `Expected actual: "${actual}" to contain: "${expected}"${msgSuffix}`; + throw new AssertionError(msg); +} + +// node_modules/@std/assert/throws.js +function assertThrows(fn, errorClassOrMsg, msgIncludesOrMsg, msg) { + let ErrorClass; + let msgIncludes; + let err; + if (typeof errorClassOrMsg !== "string") { + if (errorClassOrMsg === void 0 || errorClassOrMsg?.prototype instanceof Error || errorClassOrMsg?.prototype === Error.prototype) { + ErrorClass = errorClassOrMsg; + msgIncludes = msgIncludesOrMsg; + } else { + msg = msgIncludesOrMsg; + } + } else { + msg = errorClassOrMsg; + } + let doesThrow = false; + const msgSuffix = msg ? `: ${msg}` : "."; + try { + fn(); + } catch (error) { + if (ErrorClass) { + if (error instanceof Error === false) { + throw new AssertionError(`A non-Error object was thrown${msgSuffix}`); + } + assertIsError(error, ErrorClass, msgIncludes, msg); + } + err = error; + doesThrow = true; + } + if (!doesThrow) { + msg = `Expected function to throw${msgSuffix}`; + throw new AssertionError(msg); + } + return err; +} + +// node_modules/@std/assert/assert.js +function assert(expr, msg = "") { + if (!expr) { + throw new AssertionError(msg); + } +} + +// node_modules/@std/assert/fail.js +function fail(msg) { + const msgSuffix = msg ? `: ${msg}` : "."; + throw new AssertionError(`Failed assertion${msgSuffix}`); +} + +// node_modules/@std/assert/unimplemented.js +function unimplemented(msg) { + const msgSuffix = msg ? `: ${msg}` : "."; + throw new AssertionError(`Unimplemented${msgSuffix}`); +} + +// node_modules/@std/assert/unreachable.js +function unreachable(msg) { + const msgSuffix = msg ? `: ${msg}` : "."; + throw new AssertionError(`Unreachable${msgSuffix}`); +} +export { + AssertionError, + assert, + assertAlmostEquals, + assertArrayIncludes, + assertEquals, + assertExists, + assertFalse, + assertGreater, + assertGreaterOrEqual, + assertInstanceOf, + assertIsError, + assertLess, + assertLessOrEqual, + assertMatch, + assertNotEquals, + assertNotInstanceOf, + assertNotMatch, + assertNotStrictEquals, + assertObjectMatch, + assertRejects, + assertStrictEquals, + assertStringIncludes, + assertThrows, + equal, + fail, + unimplemented, + unreachable +}; diff --git a/jamfile/js_builtins/cli.js b/jamfile/js_builtins/cli.js new file mode 100644 index 0000000000..b64ea2fc54 --- /dev/null +++ b/jamfile/js_builtins/cli.js @@ -0,0 +1,269 @@ +/** + * Original source: https://jsr.io/@std/cli + * + * Generated from jsr:@std/assert@1.08 using esbuild --bundle. + * + * Only the parseArg() function was ported over, since the others relied + * on a Deno global. + */ + +// node_modules/@std/cli/parse_args.js +var FLAG_REGEXP = /^(?:-(?:(?-)(?no-)?)?)(?.+?)(?:=(?.+?))?$/s; +var LETTER_REGEXP = /[A-Za-z]/; +var NUMBER_REGEXP = /-?\d+(\.\d*)?(e-?\d+)?$/; +var HYPHEN_REGEXP = /^(-|--)[^-]/; +var VALUE_REGEXP = /=(?.+)/; +var FLAG_NAME_REGEXP = /^--[^=]+$/; +var SPECIAL_CHAR_REGEXP = /\W/; +var NON_WHITESPACE_REGEXP = /\S/; +function isNumber(string) { + return NON_WHITESPACE_REGEXP.test(string) && Number.isFinite(Number(string)); +} +function setNested(object, keys, value, collect = false) { + keys = [ + ...keys + ]; + const key = keys.pop(); + keys.forEach((key2) => object = object[key2] ??= {}); + if (collect) { + const v = object[key]; + if (Array.isArray(v)) { + v.push(value); + return; + } + value = v ? [ + v, + value + ] : [ + value + ]; + } + object[key] = value; +} +function hasNested(object, keys) { + for (const key of keys) { + const value = object[key]; + if (!Object.hasOwn(object, key)) return false; + object = value; + } + return true; +} +function aliasIsBoolean(aliasMap, booleanSet, key) { + const set = aliasMap.get(key); + if (set === void 0) return false; + for (const alias of set) if (booleanSet.has(alias)) return true; + return false; +} +function isBooleanString(value) { + return value === "true" || value === "false"; +} +function parseBooleanString(value) { + return value !== "false"; +} +function parseArgs(args, options) { + const { "--": doubleDash = false, alias = {}, boolean = false, default: defaults = {}, stopEarly = false, string = [], collect = [], negatable = [], unknown: unknownFn = (i) => i } = options ?? {}; + const aliasMap = /* @__PURE__ */ new Map(); + const booleanSet = /* @__PURE__ */ new Set(); + const stringSet = /* @__PURE__ */ new Set(); + const collectSet = /* @__PURE__ */ new Set(); + const negatableSet = /* @__PURE__ */ new Set(); + let allBools = false; + if (alias) { + for (const [key, value] of Object.entries(alias)) { + if (value === void 0) { + throw new TypeError("Alias value must be defined"); + } + const aliases = Array.isArray(value) ? value : [ + value + ]; + aliasMap.set(key, new Set(aliases)); + aliases.forEach((alias2) => aliasMap.set(alias2, /* @__PURE__ */ new Set([ + key, + ...aliases.filter((it) => it !== alias2) + ]))); + } + } + if (boolean) { + if (typeof boolean === "boolean") { + allBools = boolean; + } else { + const booleanArgs = Array.isArray(boolean) ? boolean : [ + boolean + ]; + for (const key of booleanArgs.filter(Boolean)) { + booleanSet.add(key); + aliasMap.get(key)?.forEach((al) => { + booleanSet.add(al); + }); + } + } + } + if (string) { + const stringArgs = Array.isArray(string) ? string : [ + string + ]; + for (const key of stringArgs.filter(Boolean)) { + stringSet.add(key); + aliasMap.get(key)?.forEach((al) => stringSet.add(al)); + } + } + if (collect) { + const collectArgs = Array.isArray(collect) ? collect : [ + collect + ]; + for (const key of collectArgs.filter(Boolean)) { + collectSet.add(key); + aliasMap.get(key)?.forEach((al) => collectSet.add(al)); + } + } + if (negatable) { + const negatableArgs = Array.isArray(negatable) ? negatable : [ + negatable + ]; + for (const key of negatableArgs.filter(Boolean)) { + negatableSet.add(key); + aliasMap.get(key)?.forEach((alias2) => negatableSet.add(alias2)); + } + } + const argv = { + _: [] + }; + function setArgument(key, value, arg, collect2) { + if (!booleanSet.has(key) && !stringSet.has(key) && !aliasMap.has(key) && !(allBools && FLAG_NAME_REGEXP.test(arg)) && unknownFn?.(arg, key, value) === false) { + return; + } + if (typeof value === "string" && !stringSet.has(key)) { + value = isNumber(value) ? Number(value) : value; + } + const collectable = collect2 && collectSet.has(key); + setNested(argv, key.split("."), value, collectable); + aliasMap.get(key)?.forEach((key2) => { + setNested(argv, key2.split("."), value, collectable); + }); + } + let notFlags = []; + const index = args.indexOf("--"); + if (index !== -1) { + notFlags = args.slice(index + 1); + args = args.slice(0, index); + } + argsLoop: for (let i = 0; i < args.length; i++) { + const arg = args[i]; + const groups = arg.match(FLAG_REGEXP)?.groups; + if (groups) { + const { doubleDash: doubleDash2, negated } = groups; + let key = groups.key; + let value = groups.value; + if (doubleDash2) { + if (value) { + if (booleanSet.has(key)) value = parseBooleanString(value); + setArgument(key, value, arg, true); + continue; + } + if (negated) { + if (negatableSet.has(key)) { + setArgument(key, false, arg, false); + continue; + } + key = `no-${key}`; + } + const next = args[i + 1]; + if (next) { + if (!booleanSet.has(key) && !allBools && !next.startsWith("-") && (!aliasMap.has(key) || !aliasIsBoolean(aliasMap, booleanSet, key))) { + value = next; + i++; + setArgument(key, value, arg, true); + continue; + } + if (isBooleanString(next)) { + value = parseBooleanString(next); + i++; + setArgument(key, value, arg, true); + continue; + } + } + value = stringSet.has(key) ? "" : true; + setArgument(key, value, arg, true); + continue; + } + const letters = arg.slice(1, -1).split(""); + for (const [j, letter] of letters.entries()) { + const next = arg.slice(j + 2); + if (next === "-") { + setArgument(letter, next, arg, true); + continue; + } + if (LETTER_REGEXP.test(letter)) { + const groups2 = VALUE_REGEXP.exec(next)?.groups; + if (groups2) { + setArgument(letter, groups2.value, arg, true); + continue argsLoop; + } + if (NUMBER_REGEXP.test(next)) { + setArgument(letter, next, arg, true); + continue argsLoop; + } + } + if (letters[j + 1]?.match(SPECIAL_CHAR_REGEXP)) { + setArgument(letter, arg.slice(j + 2), arg, true); + continue argsLoop; + } + setArgument(letter, stringSet.has(letter) ? "" : true, arg, true); + } + key = arg.slice(-1); + if (key === "-") continue; + const nextArg = args[i + 1]; + if (nextArg) { + if (!HYPHEN_REGEXP.test(nextArg) && !booleanSet.has(key) && (!aliasMap.has(key) || !aliasIsBoolean(aliasMap, booleanSet, key))) { + setArgument(key, nextArg, arg, true); + i++; + continue; + } + if (isBooleanString(nextArg)) { + const value2 = parseBooleanString(nextArg); + setArgument(key, value2, arg, true); + i++; + continue; + } + } + setArgument(key, stringSet.has(key) ? "" : true, arg, true); + continue; + } + if (unknownFn?.(arg) !== false) { + argv._.push(stringSet.has("_") || !isNumber(arg) ? arg : Number(arg)); + } + if (stopEarly) { + argv._.push(...args.slice(i + 1)); + break; + } + } + for (const [key, value] of Object.entries(defaults)) { + const keys = key.split("."); + if (!hasNested(argv, keys)) { + setNested(argv, keys, value); + aliasMap.get(key)?.forEach((key2) => setNested(argv, key2.split("."), value)); + } + } + for (const key of booleanSet.keys()) { + const keys = key.split("."); + if (!hasNested(argv, keys)) { + const value = collectSet.has(key) ? [] : false; + setNested(argv, keys, value); + } + } + for (const key of stringSet.keys()) { + const keys = key.split("."); + if (!hasNested(argv, keys) && collectSet.has(key)) { + setNested(argv, keys, []); + } + } + if (doubleDash) { + argv["--"] = notFlags; + } else { + argv._.push(...notFlags); + } + return argv; +} +export { + parseArgs +}; diff --git a/jamfile/js_builtins/colors.js b/jamfile/js_builtins/colors.js new file mode 100644 index 0000000000..04228737a7 --- /dev/null +++ b/jamfile/js_builtins/colors.js @@ -0,0 +1,344 @@ +/** + * Original source: https://jsr.io/@std/fmt/1.0.3/colors.ts + * + * Generated from jsr:@std/fmt@1.03 using esbuild --bundle. + * + */ + + +// node_modules/@std/fmt/colors.js +var { Deno } = globalThis; +var noColor = typeof Deno?.noColor === "boolean" ? Deno.noColor : false; +var enabled = !noColor; +function setColorEnabled(value) { + if (Deno?.noColor) { + return; + } + enabled = value; +} +function getColorEnabled() { + return enabled; +} +function code(open, close) { + return { + open: `\x1B[${open.join(";")}m`, + close: `\x1B[${close}m`, + regexp: new RegExp(`\\x1b\\[${close}m`, "g") + }; +} +function run(str, code2) { + return enabled ? `${code2.open}${str.replace(code2.regexp, code2.open)}${code2.close}` : str; +} +function reset(str) { + return run(str, code([ + 0 + ], 0)); +} +function bold(str) { + return run(str, code([ + 1 + ], 22)); +} +function dim(str) { + return run(str, code([ + 2 + ], 22)); +} +function italic(str) { + return run(str, code([ + 3 + ], 23)); +} +function underline(str) { + return run(str, code([ + 4 + ], 24)); +} +function inverse(str) { + return run(str, code([ + 7 + ], 27)); +} +function hidden(str) { + return run(str, code([ + 8 + ], 28)); +} +function strikethrough(str) { + return run(str, code([ + 9 + ], 29)); +} +function black(str) { + return run(str, code([ + 30 + ], 39)); +} +function red(str) { + return run(str, code([ + 31 + ], 39)); +} +function green(str) { + return run(str, code([ + 32 + ], 39)); +} +function yellow(str) { + return run(str, code([ + 33 + ], 39)); +} +function blue(str) { + return run(str, code([ + 34 + ], 39)); +} +function magenta(str) { + return run(str, code([ + 35 + ], 39)); +} +function cyan(str) { + return run(str, code([ + 36 + ], 39)); +} +function white(str) { + return run(str, code([ + 37 + ], 39)); +} +function gray(str) { + return brightBlack(str); +} +function brightBlack(str) { + return run(str, code([ + 90 + ], 39)); +} +function brightRed(str) { + return run(str, code([ + 91 + ], 39)); +} +function brightGreen(str) { + return run(str, code([ + 92 + ], 39)); +} +function brightYellow(str) { + return run(str, code([ + 93 + ], 39)); +} +function brightBlue(str) { + return run(str, code([ + 94 + ], 39)); +} +function brightMagenta(str) { + return run(str, code([ + 95 + ], 39)); +} +function brightCyan(str) { + return run(str, code([ + 96 + ], 39)); +} +function brightWhite(str) { + return run(str, code([ + 97 + ], 39)); +} +function bgBlack(str) { + return run(str, code([ + 40 + ], 49)); +} +function bgRed(str) { + return run(str, code([ + 41 + ], 49)); +} +function bgGreen(str) { + return run(str, code([ + 42 + ], 49)); +} +function bgYellow(str) { + return run(str, code([ + 43 + ], 49)); +} +function bgBlue(str) { + return run(str, code([ + 44 + ], 49)); +} +function bgMagenta(str) { + return run(str, code([ + 45 + ], 49)); +} +function bgCyan(str) { + return run(str, code([ + 46 + ], 49)); +} +function bgWhite(str) { + return run(str, code([ + 47 + ], 49)); +} +function bgBrightBlack(str) { + return run(str, code([ + 100 + ], 49)); +} +function bgBrightRed(str) { + return run(str, code([ + 101 + ], 49)); +} +function bgBrightGreen(str) { + return run(str, code([ + 102 + ], 49)); +} +function bgBrightYellow(str) { + return run(str, code([ + 103 + ], 49)); +} +function bgBrightBlue(str) { + return run(str, code([ + 104 + ], 49)); +} +function bgBrightMagenta(str) { + return run(str, code([ + 105 + ], 49)); +} +function bgBrightCyan(str) { + return run(str, code([ + 106 + ], 49)); +} +function bgBrightWhite(str) { + return run(str, code([ + 107 + ], 49)); +} +function clampAndTruncate(n, max = 255, min = 0) { + return Math.trunc(Math.max(Math.min(n, max), min)); +} +function rgb8(str, color) { + return run(str, code([ + 38, + 5, + clampAndTruncate(color) + ], 39)); +} +function bgRgb8(str, color) { + return run(str, code([ + 48, + 5, + clampAndTruncate(color) + ], 49)); +} +function rgb24(str, color) { + if (typeof color === "number") { + return run(str, code([ + 38, + 2, + color >> 16 & 255, + color >> 8 & 255, + color & 255 + ], 39)); + } + return run(str, code([ + 38, + 2, + clampAndTruncate(color.r), + clampAndTruncate(color.g), + clampAndTruncate(color.b) + ], 39)); +} +function bgRgb24(str, color) { + if (typeof color === "number") { + return run(str, code([ + 48, + 2, + color >> 16 & 255, + color >> 8 & 255, + color & 255 + ], 49)); + } + return run(str, code([ + 48, + 2, + clampAndTruncate(color.r), + clampAndTruncate(color.g), + clampAndTruncate(color.b) + ], 49)); +} +var ANSI_PATTERN = new RegExp([ + "[\\u001B\\u009B][[\\]()#;?]*(?:(?:(?:(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]+)*|[a-zA-Z\\d]+(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]*)*)?\\u0007)", + "(?:(?:\\d{1,4}(?:;\\d{0,4})*)?[\\dA-PR-TXZcf-nq-uy=><~]))" +].join("|"), "g"); +function stripAnsiCode(string) { + return string.replace(ANSI_PATTERN, ""); +} +export { + bgBlack, + bgBlue, + bgBrightBlack, + bgBrightBlue, + bgBrightCyan, + bgBrightGreen, + bgBrightMagenta, + bgBrightRed, + bgBrightWhite, + bgBrightYellow, + bgCyan, + bgGreen, + bgMagenta, + bgRed, + bgRgb24, + bgRgb8, + bgWhite, + bgYellow, + black, + blue, + bold, + brightBlack, + brightBlue, + brightCyan, + brightGreen, + brightMagenta, + brightRed, + brightWhite, + brightYellow, + cyan, + dim, + getColorEnabled, + gray, + green, + hidden, + inverse, + italic, + magenta, + red, + reset, + rgb24, + rgb8, + setColorEnabled, + strikethrough, + stripAnsiCode, + underline, + white, + yellow +}; diff --git a/jamfile/js_builtins/fmt.js b/jamfile/js_builtins/fmt.js new file mode 100644 index 0000000000..f4e9f62ed4 --- /dev/null +++ b/jamfile/js_builtins/fmt.js @@ -0,0 +1,162 @@ +/** + * Original source: https://jsr.io/@std/fmt + * + * Generated from jsr:@std/fmt@1.03 using esbuild --bundle. + * + * Only the formatBytes and formatDuration functions are in this module, + * the color utilities is in the separate colors.js module. + * + * Changes made: + * - in formatDuration(), changed the default value of the param to true + */ + +// node_modules/@std/fmt/bytes.js +function format(num, options = {}) { + if (!Number.isFinite(num)) { + throw new TypeError(`Expected a finite number, got ${typeof num}: ${num}`); + } + const UNITS_FIRSTLETTER = (options.bits ? "b" : "B") + "kMGTPEZY"; + if (options.signed && num === 0) { + return ` 0 ${UNITS_FIRSTLETTER[0]}`; + } + const prefix = num < 0 ? "-" : options.signed ? "+" : ""; + num = Math.abs(num); + const localeOptions = getLocaleOptions(options); + if (num < 1) { + const numberString2 = toLocaleString(num, options.locale, localeOptions); + return prefix + numberString2 + " " + UNITS_FIRSTLETTER[0]; + } + const exponent = Math.min(Math.floor(options.binary ? Math.log(num) / Math.log(1024) : Math.log10(num) / 3), UNITS_FIRSTLETTER.length - 1); + num /= Math.pow(options.binary ? 1024 : 1e3, exponent); + if (!localeOptions) { + num = Number(num.toPrecision(3)); + } + const numberString = toLocaleString(num, options.locale, localeOptions); + let unit = UNITS_FIRSTLETTER[exponent]; + if (exponent > 0) { + unit += options.binary ? "i" : ""; + unit += options.bits ? "bit" : "B"; + } + return prefix + numberString + " " + unit; +} +function getLocaleOptions({ maximumFractionDigits, minimumFractionDigits }) { + if (maximumFractionDigits === void 0 && minimumFractionDigits === void 0) { + return; + } + const ret = {}; + if (maximumFractionDigits !== void 0) { + ret.maximumFractionDigits = maximumFractionDigits; + } + if (minimumFractionDigits !== void 0) { + ret.minimumFractionDigits = minimumFractionDigits; + } + return ret; +} +function toLocaleString(num, locale, options) { + if (typeof locale === "string" || Array.isArray(locale)) { + return num.toLocaleString(locale, options); + } else if (locale === true || options !== void 0) { + return num.toLocaleString(void 0, options); + } + return num.toString(); +} + +// node_modules/@std/fmt/duration.js +function addZero(num, digits) { + return String(num).padStart(digits, "0"); +} +var keyList = { + d: "days", + h: "hours", + m: "minutes", + s: "seconds", + ms: "milliseconds", + us: "microseconds", + ns: "nanoseconds" +}; +function millisecondsToDurationObject(ms) { + const millis = Math.abs(ms); + const millisFraction = millis.toFixed(7).slice(-7, -1); + return { + d: Math.trunc(millis / 864e5), + h: Math.trunc(millis / 36e5) % 24, + m: Math.trunc(millis / 6e4) % 60, + s: Math.trunc(millis / 1e3) % 60, + ms: Math.trunc(millis) % 1e3, + us: +millisFraction.slice(0, 3), + ns: +millisFraction.slice(3, 6) + }; +} +function durationArray(duration) { + return [ + { + type: "d", + value: duration.d + }, + { + type: "h", + value: duration.h + }, + { + type: "m", + value: duration.m + }, + { + type: "s", + value: duration.s + }, + { + type: "ms", + value: duration.ms + }, + { + type: "us", + value: duration.us + }, + { + type: "ns", + value: duration.ns + } + ]; +} +function format2(ms, options) { + const { style = "narrow", ignoreZero = true } = options ?? {}; + const duration = millisecondsToDurationObject(ms); + const durationArr = durationArray(duration); + switch (style) { + case "narrow": { + if (ignoreZero) { + return `${durationArr.filter((x) => x.value).map((x) => `${x.value}${x.type === "us" ? "\xB5s" : x.type}`).join(" ")}`; + } + return `${durationArr.map((x) => `${x.value}${x.type === "us" ? "\xB5s" : x.type}`).join(" ")}`; + } + case "full": { + if (ignoreZero) { + return `${durationArr.filter((x) => x.value).map((x) => `${x.value} ${keyList[x.type]}`).join(", ")}`; + } + return `${durationArr.map((x) => `${x.value} ${keyList[x.type]}`).join(", ")}`; + } + case "digital": { + const arr = durationArr.map((x) => [ + "ms", + "us", + "ns" + ].includes(x.type) ? addZero(x.value, 3) : addZero(x.value, 2)); + if (ignoreZero) { + let cont = true; + while (cont) { + if (!Number(arr[arr.length - 1])) arr.pop(); + else cont = false; + } + } + return arr.join(":"); + } + default: { + throw new TypeError(`style must be "narrow", "full", or "digital"!`); + } + } +} +export { + format as formatBytes, + format2 as formatDuration +}; diff --git a/jamfile/js_builtins/frontmatter.js b/jamfile/js_builtins/frontmatter.js new file mode 100644 index 0000000000..de65009053 --- /dev/null +++ b/jamfile/js_builtins/frontmatter.js @@ -0,0 +1,104 @@ +/** + * Original source: https://jsr.io/@std/front-matter + * + * Generated from jsr:@std/front-matter@1.05 with: + * + * ```bash + * echo 'export * from "@std/front-matter"' \ + * | ./node_modules/.bin/esbuild --bundle --format=esm \ + * '--external:@jsr/std__toml*' '--external:@jsr/std__yaml*' \ + * > ../jamfile/js_builtins/frontmatter.js + * ``` + * + */ + +// node_modules/@std/front-matter/_formats.js +var BOM = "\\ufeff?"; +var YAML_DELIMITER = "= yaml =|---"; +var YAML_HEADER = `(---yaml|${YAML_DELIMITER})\\s*`; +var YAML_FOOTER = `(?:---|${YAML_DELIMITER})`; +var TOML_DELIMITER = "\\+\\+\\+|= toml ="; +var TOML_HEADER = `(---toml|${TOML_DELIMITER})\\s*`; +var TOML_FOOTER = `(?:---|${TOML_DELIMITER})`; +var JSON_DELIMITER = `= json =`; +var JSON_HEADER = `(---json|${JSON_DELIMITER})\\s*`; +var JSON_FOOTER = `(?:---|${JSON_DELIMITER})`; +var DATA = "([\\s\\S]+?)"; +var NEWLINE = "\\r?\\n?"; +var RECOGNIZE_YAML_REGEXP = new RegExp(`^${YAML_HEADER}$`, "im"); +var RECOGNIZE_TOML_REGEXP = new RegExp(`^${TOML_HEADER}$`, "im"); +var RECOGNIZE_JSON_REGEXP = new RegExp(`^${JSON_HEADER}$`, "im"); +var EXTRACT_YAML_REGEXP = new RegExp(`^(${BOM}${YAML_HEADER}$${DATA}^${YAML_FOOTER}\\s*$${NEWLINE})`, "im"); +var EXTRACT_TOML_REGEXP = new RegExp(`^(${BOM}${TOML_HEADER}$${DATA}^${TOML_FOOTER}\\s*$${NEWLINE})`, "im"); +var EXTRACT_JSON_REGEXP = new RegExp(`^(${BOM}${JSON_HEADER}$${DATA}^${JSON_FOOTER}\\s*$${NEWLINE})`, "im"); +var EXTRACT_REGEXP_MAP = /* @__PURE__ */ new Map([ + [ + "yaml", + EXTRACT_YAML_REGEXP + ], + [ + "toml", + EXTRACT_TOML_REGEXP + ], + [ + "json", + EXTRACT_JSON_REGEXP + ] +]); + +// node_modules/@std/front-matter/_shared.js +function extractAndParse(input, extractRegExp, parse3) { + const match = extractRegExp.exec(input); + if (!match || match.index !== 0) { + throw new TypeError("Unexpected end of input"); + } + const frontMatter = match.at(-1)?.replace(/^\s+|\s+$/g, "") ?? ""; + const attrs = parse3(frontMatter); + const body = input.replace(match[0], ""); + return { + frontMatter, + body, + attrs + }; +} + +// node_modules/@std/front-matter/json.js +function extract(text) { + return extractAndParse(text, EXTRACT_JSON_REGEXP, JSON.parse); +} + +// node_modules/@std/front-matter/toml.js +import { parse } from "jamfile:toml"; +function extract2(text) { + return extractAndParse(text, EXTRACT_TOML_REGEXP, parse); +} + +// node_modules/@std/front-matter/yaml.js +import { parse as parse2 } from "jamfile:yaml"; +function extract3(text) { + return extractAndParse(text, EXTRACT_YAML_REGEXP, (s) => parse2(s)); +} + +// node_modules/@std/front-matter/test.js +function test(str, formats) { + if (!formats) formats = [ + ...EXTRACT_REGEXP_MAP.keys() + ]; + for (const format of formats) { + const regexp = EXTRACT_REGEXP_MAP.get(format); + if (!regexp) { + throw new TypeError(`Unable to test for ${format} front matter format`); + } + const match = regexp.exec(str); + if (match?.index === 0) { + return true; + } + } + return false; +} +export { + extract as extractJson, + extract2 as extractToml, + extract3 as extractYaml, + test +}; diff --git a/jamfile/js_builtins/linkedom.js b/jamfile/js_builtins/linkedom.js new file mode 100644 index 0000000000..38ee45f2fb --- /dev/null +++ b/jamfile/js_builtins/linkedom.js @@ -0,0 +1,10884 @@ +/** + * Original source: https://github.com/WebReflection/linkedom + * + * Generated from npm:linkedom@0.18.6 using: + * + * ```bash + * echo 'export * from "linkedom"' \ + * | ./node_modules/.bin/esbuild --bundle --format=esm \ + * > ../jamfile/js_builtins/linkedom.js + * ``` + * + */ + +var __create = Object.create; +var __defProp = Object.defineProperty; +var __getOwnPropDesc = Object.getOwnPropertyDescriptor; +var __getOwnPropNames = Object.getOwnPropertyNames; +var __getProtoOf = Object.getPrototypeOf; +var __hasOwnProp = Object.prototype.hasOwnProperty; +var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, { + get: (a, b) => (typeof require !== "undefined" ? require : a)[b] +}) : x)(function(x) { + if (typeof require !== "undefined") return require.apply(this, arguments); + throw Error('Dynamic require of "' + x + '" is not supported'); +}); +var __commonJS = (cb, mod) => function __require2() { + return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports; +}; +var __export = (target, all) => { + for (var name in all) + __defProp(target, name, { get: all[name], enumerable: true }); +}; +var __copyProps = (to, from, except, desc) => { + if (from && typeof from === "object" || typeof from === "function") { + for (let key2 of __getOwnPropNames(from)) + if (!__hasOwnProp.call(to, key2) && key2 !== except) + __defProp(to, key2, { get: () => from[key2], enumerable: !(desc = __getOwnPropDesc(from, key2)) || desc.enumerable }); + } + return to; +}; +var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( + // If the importer is in node compatibility mode or this is not an ESM + // file that has been converted to a CommonJS file using a Babel- + // compatible transform (i.e. "__esModule" has not been set), then set + // "default" to the CommonJS "module.exports" for node compatibility. + isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, + mod +)); + +// node_modules/linkedom/commonjs/perf_hooks.cjs +var require_perf_hooks = __commonJS({ + "node_modules/linkedom/commonjs/perf_hooks.cjs"(exports) { + try { + const { performance: performance2 } = __require("perf_hooks"); + exports.performance = performance2; + } catch (fallback) { + exports.performance = { now() { + return +/* @__PURE__ */ new Date(); + } }; + } + } +}); + +// node_modules/boolbase/index.js +var require_boolbase = __commonJS({ + "node_modules/boolbase/index.js"(exports, module) { + module.exports = { + trueFunc: function trueFunc() { + return true; + }, + falseFunc: function falseFunc() { + return false; + } + }; + } +}); + +// node_modules/cssom/lib/StyleSheet.js +var require_StyleSheet = __commonJS({ + "node_modules/cssom/lib/StyleSheet.js"(exports) { + var CSSOM = {}; + CSSOM.StyleSheet = function StyleSheet() { + this.parentStyleSheet = null; + }; + exports.StyleSheet = CSSOM.StyleSheet; + } +}); + +// node_modules/cssom/lib/CSSRule.js +var require_CSSRule = __commonJS({ + "node_modules/cssom/lib/CSSRule.js"(exports) { + var CSSOM = {}; + CSSOM.CSSRule = function CSSRule() { + this.parentRule = null; + this.parentStyleSheet = null; + }; + CSSOM.CSSRule.UNKNOWN_RULE = 0; + CSSOM.CSSRule.STYLE_RULE = 1; + CSSOM.CSSRule.CHARSET_RULE = 2; + CSSOM.CSSRule.IMPORT_RULE = 3; + CSSOM.CSSRule.MEDIA_RULE = 4; + CSSOM.CSSRule.FONT_FACE_RULE = 5; + CSSOM.CSSRule.PAGE_RULE = 6; + CSSOM.CSSRule.KEYFRAMES_RULE = 7; + CSSOM.CSSRule.KEYFRAME_RULE = 8; + CSSOM.CSSRule.MARGIN_RULE = 9; + CSSOM.CSSRule.NAMESPACE_RULE = 10; + CSSOM.CSSRule.COUNTER_STYLE_RULE = 11; + CSSOM.CSSRule.SUPPORTS_RULE = 12; + CSSOM.CSSRule.DOCUMENT_RULE = 13; + CSSOM.CSSRule.FONT_FEATURE_VALUES_RULE = 14; + CSSOM.CSSRule.VIEWPORT_RULE = 15; + CSSOM.CSSRule.REGION_STYLE_RULE = 16; + CSSOM.CSSRule.prototype = { + constructor: CSSOM.CSSRule + //FIXME + }; + exports.CSSRule = CSSOM.CSSRule; + } +}); + +// node_modules/cssom/lib/CSSStyleRule.js +var require_CSSStyleRule = __commonJS({ + "node_modules/cssom/lib/CSSStyleRule.js"(exports) { + var CSSOM = { + CSSStyleDeclaration: require_CSSStyleDeclaration().CSSStyleDeclaration, + CSSRule: require_CSSRule().CSSRule + }; + CSSOM.CSSStyleRule = function CSSStyleRule() { + CSSOM.CSSRule.call(this); + this.selectorText = ""; + this.style = new CSSOM.CSSStyleDeclaration(); + this.style.parentRule = this; + }; + CSSOM.CSSStyleRule.prototype = new CSSOM.CSSRule(); + CSSOM.CSSStyleRule.prototype.constructor = CSSOM.CSSStyleRule; + CSSOM.CSSStyleRule.prototype.type = 1; + Object.defineProperty(CSSOM.CSSStyleRule.prototype, "cssText", { + get: function() { + var text; + if (this.selectorText) { + text = this.selectorText + " {" + this.style.cssText + "}"; + } else { + text = ""; + } + return text; + }, + set: function(cssText) { + var rule = CSSOM.CSSStyleRule.parse(cssText); + this.style = rule.style; + this.selectorText = rule.selectorText; + } + }); + CSSOM.CSSStyleRule.parse = function(ruleText) { + var i = 0; + var state = "selector"; + var index; + var j = i; + var buffer = ""; + var SIGNIFICANT_WHITESPACE = { + "selector": true, + "value": true + }; + var styleRule = new CSSOM.CSSStyleRule(); + var name, priority = ""; + for (var character; character = ruleText.charAt(i); i++) { + switch (character) { + case " ": + case " ": + case "\r": + case "\n": + case "\f": + if (SIGNIFICANT_WHITESPACE[state]) { + switch (ruleText.charAt(i - 1)) { + case " ": + case " ": + case "\r": + case "\n": + case "\f": + break; + default: + buffer += " "; + break; + } + } + break; + // String + case '"': + j = i + 1; + index = ruleText.indexOf('"', j) + 1; + if (!index) { + throw '" is missing'; + } + buffer += ruleText.slice(i, index); + i = index - 1; + break; + case "'": + j = i + 1; + index = ruleText.indexOf("'", j) + 1; + if (!index) { + throw "' is missing"; + } + buffer += ruleText.slice(i, index); + i = index - 1; + break; + // Comment + case "/": + if (ruleText.charAt(i + 1) === "*") { + i += 2; + index = ruleText.indexOf("*/", i); + if (index === -1) { + throw new SyntaxError("Missing */"); + } else { + i = index + 1; + } + } else { + buffer += character; + } + break; + case "{": + if (state === "selector") { + styleRule.selectorText = buffer.trim(); + buffer = ""; + state = "name"; + } + break; + case ":": + if (state === "name") { + name = buffer.trim(); + buffer = ""; + state = "value"; + } else { + buffer += character; + } + break; + case "!": + if (state === "value" && ruleText.indexOf("!important", i) === i) { + priority = "important"; + i += "important".length; + } else { + buffer += character; + } + break; + case ";": + if (state === "value") { + styleRule.style.setProperty(name, buffer.trim(), priority); + priority = ""; + buffer = ""; + state = "name"; + } else { + buffer += character; + } + break; + case "}": + if (state === "value") { + styleRule.style.setProperty(name, buffer.trim(), priority); + priority = ""; + buffer = ""; + } else if (state === "name") { + break; + } else { + buffer += character; + } + state = "selector"; + break; + default: + buffer += character; + break; + } + } + return styleRule; + }; + exports.CSSStyleRule = CSSOM.CSSStyleRule; + } +}); + +// node_modules/cssom/lib/CSSStyleSheet.js +var require_CSSStyleSheet = __commonJS({ + "node_modules/cssom/lib/CSSStyleSheet.js"(exports) { + var CSSOM = { + StyleSheet: require_StyleSheet().StyleSheet, + CSSStyleRule: require_CSSStyleRule().CSSStyleRule + }; + CSSOM.CSSStyleSheet = function CSSStyleSheet() { + CSSOM.StyleSheet.call(this); + this.cssRules = []; + }; + CSSOM.CSSStyleSheet.prototype = new CSSOM.StyleSheet(); + CSSOM.CSSStyleSheet.prototype.constructor = CSSOM.CSSStyleSheet; + CSSOM.CSSStyleSheet.prototype.insertRule = function(rule, index) { + if (index < 0 || index > this.cssRules.length) { + throw new RangeError("INDEX_SIZE_ERR"); + } + var cssRule = CSSOM.parse(rule).cssRules[0]; + cssRule.parentStyleSheet = this; + this.cssRules.splice(index, 0, cssRule); + return index; + }; + CSSOM.CSSStyleSheet.prototype.deleteRule = function(index) { + if (index < 0 || index >= this.cssRules.length) { + throw new RangeError("INDEX_SIZE_ERR"); + } + this.cssRules.splice(index, 1); + }; + CSSOM.CSSStyleSheet.prototype.toString = function() { + var result = ""; + var rules = this.cssRules; + for (var i = 0; i < rules.length; i++) { + result += rules[i].cssText + "\n"; + } + return result; + }; + exports.CSSStyleSheet = CSSOM.CSSStyleSheet; + CSSOM.parse = require_parse().parse; + } +}); + +// node_modules/cssom/lib/MediaList.js +var require_MediaList = __commonJS({ + "node_modules/cssom/lib/MediaList.js"(exports) { + var CSSOM = {}; + CSSOM.MediaList = function MediaList() { + this.length = 0; + }; + CSSOM.MediaList.prototype = { + constructor: CSSOM.MediaList, + /** + * @return {string} + */ + get mediaText() { + return Array.prototype.join.call(this, ", "); + }, + /** + * @param {string} value + */ + set mediaText(value) { + var values = value.split(","); + var length = this.length = values.length; + for (var i = 0; i < length; i++) { + this[i] = values[i].trim(); + } + }, + /** + * @param {string} medium + */ + appendMedium: function(medium) { + if (Array.prototype.indexOf.call(this, medium) === -1) { + this[this.length] = medium; + this.length++; + } + }, + /** + * @param {string} medium + */ + deleteMedium: function(medium) { + var index = Array.prototype.indexOf.call(this, medium); + if (index !== -1) { + Array.prototype.splice.call(this, index, 1); + } + } + }; + exports.MediaList = CSSOM.MediaList; + } +}); + +// node_modules/cssom/lib/CSSImportRule.js +var require_CSSImportRule = __commonJS({ + "node_modules/cssom/lib/CSSImportRule.js"(exports) { + var CSSOM = { + CSSRule: require_CSSRule().CSSRule, + CSSStyleSheet: require_CSSStyleSheet().CSSStyleSheet, + MediaList: require_MediaList().MediaList + }; + CSSOM.CSSImportRule = function CSSImportRule() { + CSSOM.CSSRule.call(this); + this.href = ""; + this.media = new CSSOM.MediaList(); + this.styleSheet = new CSSOM.CSSStyleSheet(); + }; + CSSOM.CSSImportRule.prototype = new CSSOM.CSSRule(); + CSSOM.CSSImportRule.prototype.constructor = CSSOM.CSSImportRule; + CSSOM.CSSImportRule.prototype.type = 3; + Object.defineProperty(CSSOM.CSSImportRule.prototype, "cssText", { + get: function() { + var mediaText = this.media.mediaText; + return "@import url(" + this.href + ")" + (mediaText ? " " + mediaText : "") + ";"; + }, + set: function(cssText) { + var i = 0; + var state = ""; + var buffer = ""; + var index; + for (var character; character = cssText.charAt(i); i++) { + switch (character) { + case " ": + case " ": + case "\r": + case "\n": + case "\f": + if (state === "after-import") { + state = "url"; + } else { + buffer += character; + } + break; + case "@": + if (!state && cssText.indexOf("@import", i) === i) { + state = "after-import"; + i += "import".length; + buffer = ""; + } + break; + case "u": + if (state === "url" && cssText.indexOf("url(", i) === i) { + index = cssText.indexOf(")", i + 1); + if (index === -1) { + throw i + ': ")" not found'; + } + i += "url(".length; + var url = cssText.slice(i, index); + if (url[0] === url[url.length - 1]) { + if (url[0] === '"' || url[0] === "'") { + url = url.slice(1, -1); + } + } + this.href = url; + i = index; + state = "media"; + } + break; + case '"': + if (state === "url") { + index = cssText.indexOf('"', i + 1); + if (!index) { + throw i + `: '"' not found`; + } + this.href = cssText.slice(i + 1, index); + i = index; + state = "media"; + } + break; + case "'": + if (state === "url") { + index = cssText.indexOf("'", i + 1); + if (!index) { + throw i + `: "'" not found`; + } + this.href = cssText.slice(i + 1, index); + i = index; + state = "media"; + } + break; + case ";": + if (state === "media") { + if (buffer) { + this.media.mediaText = buffer.trim(); + } + } + break; + default: + if (state === "media") { + buffer += character; + } + break; + } + } + } + }); + exports.CSSImportRule = CSSOM.CSSImportRule; + } +}); + +// node_modules/cssom/lib/CSSGroupingRule.js +var require_CSSGroupingRule = __commonJS({ + "node_modules/cssom/lib/CSSGroupingRule.js"(exports) { + var CSSOM = { + CSSRule: require_CSSRule().CSSRule + }; + CSSOM.CSSGroupingRule = function CSSGroupingRule() { + CSSOM.CSSRule.call(this); + this.cssRules = []; + }; + CSSOM.CSSGroupingRule.prototype = new CSSOM.CSSRule(); + CSSOM.CSSGroupingRule.prototype.constructor = CSSOM.CSSGroupingRule; + CSSOM.CSSGroupingRule.prototype.insertRule = function insertRule(rule, index) { + if (index < 0 || index > this.cssRules.length) { + throw new RangeError("INDEX_SIZE_ERR"); + } + var cssRule = CSSOM.parse(rule).cssRules[0]; + cssRule.parentRule = this; + this.cssRules.splice(index, 0, cssRule); + return index; + }; + CSSOM.CSSGroupingRule.prototype.deleteRule = function deleteRule(index) { + if (index < 0 || index >= this.cssRules.length) { + throw new RangeError("INDEX_SIZE_ERR"); + } + this.cssRules.splice(index, 1)[0].parentRule = null; + }; + exports.CSSGroupingRule = CSSOM.CSSGroupingRule; + } +}); + +// node_modules/cssom/lib/CSSConditionRule.js +var require_CSSConditionRule = __commonJS({ + "node_modules/cssom/lib/CSSConditionRule.js"(exports) { + var CSSOM = { + CSSRule: require_CSSRule().CSSRule, + CSSGroupingRule: require_CSSGroupingRule().CSSGroupingRule + }; + CSSOM.CSSConditionRule = function CSSConditionRule() { + CSSOM.CSSGroupingRule.call(this); + this.cssRules = []; + }; + CSSOM.CSSConditionRule.prototype = new CSSOM.CSSGroupingRule(); + CSSOM.CSSConditionRule.prototype.constructor = CSSOM.CSSConditionRule; + CSSOM.CSSConditionRule.prototype.conditionText = ""; + CSSOM.CSSConditionRule.prototype.cssText = ""; + exports.CSSConditionRule = CSSOM.CSSConditionRule; + } +}); + +// node_modules/cssom/lib/CSSMediaRule.js +var require_CSSMediaRule = __commonJS({ + "node_modules/cssom/lib/CSSMediaRule.js"(exports) { + var CSSOM = { + CSSRule: require_CSSRule().CSSRule, + CSSGroupingRule: require_CSSGroupingRule().CSSGroupingRule, + CSSConditionRule: require_CSSConditionRule().CSSConditionRule, + MediaList: require_MediaList().MediaList + }; + CSSOM.CSSMediaRule = function CSSMediaRule() { + CSSOM.CSSConditionRule.call(this); + this.media = new CSSOM.MediaList(); + }; + CSSOM.CSSMediaRule.prototype = new CSSOM.CSSConditionRule(); + CSSOM.CSSMediaRule.prototype.constructor = CSSOM.CSSMediaRule; + CSSOM.CSSMediaRule.prototype.type = 4; + Object.defineProperties(CSSOM.CSSMediaRule.prototype, { + "conditionText": { + get: function() { + return this.media.mediaText; + }, + set: function(value) { + this.media.mediaText = value; + }, + configurable: true, + enumerable: true + }, + "cssText": { + get: function() { + var cssTexts = []; + for (var i = 0, length = this.cssRules.length; i < length; i++) { + cssTexts.push(this.cssRules[i].cssText); + } + return "@media " + this.media.mediaText + " {" + cssTexts.join("") + "}"; + }, + configurable: true, + enumerable: true + } + }); + exports.CSSMediaRule = CSSOM.CSSMediaRule; + } +}); + +// node_modules/cssom/lib/CSSSupportsRule.js +var require_CSSSupportsRule = __commonJS({ + "node_modules/cssom/lib/CSSSupportsRule.js"(exports) { + var CSSOM = { + CSSRule: require_CSSRule().CSSRule, + CSSGroupingRule: require_CSSGroupingRule().CSSGroupingRule, + CSSConditionRule: require_CSSConditionRule().CSSConditionRule + }; + CSSOM.CSSSupportsRule = function CSSSupportsRule() { + CSSOM.CSSConditionRule.call(this); + }; + CSSOM.CSSSupportsRule.prototype = new CSSOM.CSSConditionRule(); + CSSOM.CSSSupportsRule.prototype.constructor = CSSOM.CSSSupportsRule; + CSSOM.CSSSupportsRule.prototype.type = 12; + Object.defineProperty(CSSOM.CSSSupportsRule.prototype, "cssText", { + get: function() { + var cssTexts = []; + for (var i = 0, length = this.cssRules.length; i < length; i++) { + cssTexts.push(this.cssRules[i].cssText); + } + return "@supports " + this.conditionText + " {" + cssTexts.join("") + "}"; + } + }); + exports.CSSSupportsRule = CSSOM.CSSSupportsRule; + } +}); + +// node_modules/cssom/lib/CSSFontFaceRule.js +var require_CSSFontFaceRule = __commonJS({ + "node_modules/cssom/lib/CSSFontFaceRule.js"(exports) { + var CSSOM = { + CSSStyleDeclaration: require_CSSStyleDeclaration().CSSStyleDeclaration, + CSSRule: require_CSSRule().CSSRule + }; + CSSOM.CSSFontFaceRule = function CSSFontFaceRule() { + CSSOM.CSSRule.call(this); + this.style = new CSSOM.CSSStyleDeclaration(); + this.style.parentRule = this; + }; + CSSOM.CSSFontFaceRule.prototype = new CSSOM.CSSRule(); + CSSOM.CSSFontFaceRule.prototype.constructor = CSSOM.CSSFontFaceRule; + CSSOM.CSSFontFaceRule.prototype.type = 5; + Object.defineProperty(CSSOM.CSSFontFaceRule.prototype, "cssText", { + get: function() { + return "@font-face {" + this.style.cssText + "}"; + } + }); + exports.CSSFontFaceRule = CSSOM.CSSFontFaceRule; + } +}); + +// node_modules/cssom/lib/CSSHostRule.js +var require_CSSHostRule = __commonJS({ + "node_modules/cssom/lib/CSSHostRule.js"(exports) { + var CSSOM = { + CSSRule: require_CSSRule().CSSRule + }; + CSSOM.CSSHostRule = function CSSHostRule() { + CSSOM.CSSRule.call(this); + this.cssRules = []; + }; + CSSOM.CSSHostRule.prototype = new CSSOM.CSSRule(); + CSSOM.CSSHostRule.prototype.constructor = CSSOM.CSSHostRule; + CSSOM.CSSHostRule.prototype.type = 1001; + Object.defineProperty(CSSOM.CSSHostRule.prototype, "cssText", { + get: function() { + var cssTexts = []; + for (var i = 0, length = this.cssRules.length; i < length; i++) { + cssTexts.push(this.cssRules[i].cssText); + } + return "@host {" + cssTexts.join("") + "}"; + } + }); + exports.CSSHostRule = CSSOM.CSSHostRule; + } +}); + +// node_modules/cssom/lib/CSSKeyframeRule.js +var require_CSSKeyframeRule = __commonJS({ + "node_modules/cssom/lib/CSSKeyframeRule.js"(exports) { + var CSSOM = { + CSSRule: require_CSSRule().CSSRule, + CSSStyleDeclaration: require_CSSStyleDeclaration().CSSStyleDeclaration + }; + CSSOM.CSSKeyframeRule = function CSSKeyframeRule() { + CSSOM.CSSRule.call(this); + this.keyText = ""; + this.style = new CSSOM.CSSStyleDeclaration(); + this.style.parentRule = this; + }; + CSSOM.CSSKeyframeRule.prototype = new CSSOM.CSSRule(); + CSSOM.CSSKeyframeRule.prototype.constructor = CSSOM.CSSKeyframeRule; + CSSOM.CSSKeyframeRule.prototype.type = 8; + Object.defineProperty(CSSOM.CSSKeyframeRule.prototype, "cssText", { + get: function() { + return this.keyText + " {" + this.style.cssText + "} "; + } + }); + exports.CSSKeyframeRule = CSSOM.CSSKeyframeRule; + } +}); + +// node_modules/cssom/lib/CSSKeyframesRule.js +var require_CSSKeyframesRule = __commonJS({ + "node_modules/cssom/lib/CSSKeyframesRule.js"(exports) { + var CSSOM = { + CSSRule: require_CSSRule().CSSRule + }; + CSSOM.CSSKeyframesRule = function CSSKeyframesRule() { + CSSOM.CSSRule.call(this); + this.name = ""; + this.cssRules = []; + }; + CSSOM.CSSKeyframesRule.prototype = new CSSOM.CSSRule(); + CSSOM.CSSKeyframesRule.prototype.constructor = CSSOM.CSSKeyframesRule; + CSSOM.CSSKeyframesRule.prototype.type = 7; + Object.defineProperty(CSSOM.CSSKeyframesRule.prototype, "cssText", { + get: function() { + var cssTexts = []; + for (var i = 0, length = this.cssRules.length; i < length; i++) { + cssTexts.push(" " + this.cssRules[i].cssText); + } + return "@" + (this._vendorPrefix || "") + "keyframes " + this.name + " { \n" + cssTexts.join("\n") + "\n}"; + } + }); + exports.CSSKeyframesRule = CSSOM.CSSKeyframesRule; + } +}); + +// node_modules/cssom/lib/CSSValue.js +var require_CSSValue = __commonJS({ + "node_modules/cssom/lib/CSSValue.js"(exports) { + var CSSOM = {}; + CSSOM.CSSValue = function CSSValue() { + }; + CSSOM.CSSValue.prototype = { + constructor: CSSOM.CSSValue, + // @see: http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSValue + set cssText(text) { + var name = this._getConstructorName(); + throw new Error('DOMException: property "cssText" of "' + name + '" is readonly and can not be replaced with "' + text + '"!'); + }, + get cssText() { + var name = this._getConstructorName(); + throw new Error('getter "cssText" of "' + name + '" is not implemented!'); + }, + _getConstructorName: function() { + var s = this.constructor.toString(), c = s.match(/function\s([^\(]+)/), name = c[1]; + return name; + } + }; + exports.CSSValue = CSSOM.CSSValue; + } +}); + +// node_modules/cssom/lib/CSSValueExpression.js +var require_CSSValueExpression = __commonJS({ + "node_modules/cssom/lib/CSSValueExpression.js"(exports) { + var CSSOM = { + CSSValue: require_CSSValue().CSSValue + }; + CSSOM.CSSValueExpression = function CSSValueExpression(token, idx) { + this._token = token; + this._idx = idx; + }; + CSSOM.CSSValueExpression.prototype = new CSSOM.CSSValue(); + CSSOM.CSSValueExpression.prototype.constructor = CSSOM.CSSValueExpression; + CSSOM.CSSValueExpression.prototype.parse = function() { + var token = this._token, idx = this._idx; + var character = "", expression = "", error = "", info, paren = []; + for (; ; ++idx) { + character = token.charAt(idx); + if (character === "") { + error = "css expression error: unfinished expression!"; + break; + } + switch (character) { + case "(": + paren.push(character); + expression += character; + break; + case ")": + paren.pop(character); + expression += character; + break; + case "/": + if (info = this._parseJSComment(token, idx)) { + if (info.error) { + error = "css expression error: unfinished comment in expression!"; + } else { + idx = info.idx; + } + } else if (info = this._parseJSRexExp(token, idx)) { + idx = info.idx; + expression += info.text; + } else { + expression += character; + } + break; + case "'": + case '"': + info = this._parseJSString(token, idx, character); + if (info) { + idx = info.idx; + expression += info.text; + } else { + expression += character; + } + break; + default: + expression += character; + break; + } + if (error) { + break; + } + if (paren.length === 0) { + break; + } + } + var ret; + if (error) { + ret = { + error + }; + } else { + ret = { + idx, + expression + }; + } + return ret; + }; + CSSOM.CSSValueExpression.prototype._parseJSComment = function(token, idx) { + var nextChar = token.charAt(idx + 1), text; + if (nextChar === "/" || nextChar === "*") { + var startIdx = idx, endIdx, commentEndChar; + if (nextChar === "/") { + commentEndChar = "\n"; + } else if (nextChar === "*") { + commentEndChar = "*/"; + } + endIdx = token.indexOf(commentEndChar, startIdx + 1 + 1); + if (endIdx !== -1) { + endIdx = endIdx + commentEndChar.length - 1; + text = token.substring(idx, endIdx + 1); + return { + idx: endIdx, + text + }; + } else { + var error = "css expression error: unfinished comment in expression!"; + return { + error + }; + } + } else { + return false; + } + }; + CSSOM.CSSValueExpression.prototype._parseJSString = function(token, idx, sep) { + var endIdx = this._findMatchedIdx(token, idx, sep), text; + if (endIdx === -1) { + return false; + } else { + text = token.substring(idx, endIdx + sep.length); + return { + idx: endIdx, + text + }; + } + }; + CSSOM.CSSValueExpression.prototype._parseJSRexExp = function(token, idx) { + var before2 = token.substring(0, idx).replace(/\s+$/, ""), legalRegx = [ + /^$/, + /\($/, + /\[$/, + /\!$/, + /\+$/, + /\-$/, + /\*$/, + /\/\s+/, + /\%$/, + /\=$/, + /\>$/, + /<$/, + /\&$/, + /\|$/, + /\^$/, + /\~$/, + /\?$/, + /\,$/, + /delete$/, + /in$/, + /instanceof$/, + /new$/, + /typeof$/, + /void$/ + ]; + var isLegal = legalRegx.some(function(reg) { + return reg.test(before2); + }); + if (!isLegal) { + return false; + } else { + var sep = "/"; + return this._parseJSString(token, idx, sep); + } + }; + CSSOM.CSSValueExpression.prototype._findMatchedIdx = function(token, idx, sep) { + var startIdx = idx, endIdx; + var NOT_FOUND = -1; + while (true) { + endIdx = token.indexOf(sep, startIdx + 1); + if (endIdx === -1) { + endIdx = NOT_FOUND; + break; + } else { + var text = token.substring(idx + 1, endIdx), matched = text.match(/\\+$/); + if (!matched || matched[0] % 2 === 0) { + break; + } else { + startIdx = endIdx; + } + } + } + var nextNewLineIdx = token.indexOf("\n", idx + 1); + if (nextNewLineIdx < endIdx) { + endIdx = NOT_FOUND; + } + return endIdx; + }; + exports.CSSValueExpression = CSSOM.CSSValueExpression; + } +}); + +// node_modules/cssom/lib/MatcherList.js +var require_MatcherList = __commonJS({ + "node_modules/cssom/lib/MatcherList.js"(exports) { + var CSSOM = {}; + CSSOM.MatcherList = function MatcherList() { + this.length = 0; + }; + CSSOM.MatcherList.prototype = { + constructor: CSSOM.MatcherList, + /** + * @return {string} + */ + get matcherText() { + return Array.prototype.join.call(this, ", "); + }, + /** + * @param {string} value + */ + set matcherText(value) { + var values = value.split(","); + var length = this.length = values.length; + for (var i = 0; i < length; i++) { + this[i] = values[i].trim(); + } + }, + /** + * @param {string} matcher + */ + appendMatcher: function(matcher) { + if (Array.prototype.indexOf.call(this, matcher) === -1) { + this[this.length] = matcher; + this.length++; + } + }, + /** + * @param {string} matcher + */ + deleteMatcher: function(matcher) { + var index = Array.prototype.indexOf.call(this, matcher); + if (index !== -1) { + Array.prototype.splice.call(this, index, 1); + } + } + }; + exports.MatcherList = CSSOM.MatcherList; + } +}); + +// node_modules/cssom/lib/CSSDocumentRule.js +var require_CSSDocumentRule = __commonJS({ + "node_modules/cssom/lib/CSSDocumentRule.js"(exports) { + var CSSOM = { + CSSRule: require_CSSRule().CSSRule, + MatcherList: require_MatcherList().MatcherList + }; + CSSOM.CSSDocumentRule = function CSSDocumentRule() { + CSSOM.CSSRule.call(this); + this.matcher = new CSSOM.MatcherList(); + this.cssRules = []; + }; + CSSOM.CSSDocumentRule.prototype = new CSSOM.CSSRule(); + CSSOM.CSSDocumentRule.prototype.constructor = CSSOM.CSSDocumentRule; + CSSOM.CSSDocumentRule.prototype.type = 10; + Object.defineProperty(CSSOM.CSSDocumentRule.prototype, "cssText", { + get: function() { + var cssTexts = []; + for (var i = 0, length = this.cssRules.length; i < length; i++) { + cssTexts.push(this.cssRules[i].cssText); + } + return "@-moz-document " + this.matcher.matcherText + " {" + cssTexts.join("") + "}"; + } + }); + exports.CSSDocumentRule = CSSOM.CSSDocumentRule; + } +}); + +// node_modules/cssom/lib/parse.js +var require_parse = __commonJS({ + "node_modules/cssom/lib/parse.js"(exports) { + var CSSOM = {}; + CSSOM.parse = function parse5(token) { + var i = 0; + var state = "before-selector"; + var index; + var buffer = ""; + var valueParenthesisDepth = 0; + var SIGNIFICANT_WHITESPACE = { + "selector": true, + "value": true, + "value-parenthesis": true, + "atRule": true, + "importRule-begin": true, + "importRule": true, + "atBlock": true, + "conditionBlock": true, + "documentRule-begin": true + }; + var styleSheet = new CSSOM.CSSStyleSheet(); + var currentScope = styleSheet; + var parentRule; + var ancestorRules = []; + var hasAncestors = false; + var prevScope; + var name, priority = "", styleRule, mediaRule, supportsRule, importRule, fontFaceRule, keyframesRule, documentRule, hostRule; + var atKeyframesRegExp = /@(-(?:\w+-)+)?keyframes/g; + var parseError = function(message) { + var lines = token.substring(0, i).split("\n"); + var lineCount = lines.length; + var charCount = lines.pop().length + 1; + var error = new Error(message + " (line " + lineCount + ", char " + charCount + ")"); + error.line = lineCount; + error["char"] = charCount; + error.styleSheet = styleSheet; + throw error; + }; + for (var character; character = token.charAt(i); i++) { + switch (character) { + case " ": + case " ": + case "\r": + case "\n": + case "\f": + if (SIGNIFICANT_WHITESPACE[state]) { + buffer += character; + } + break; + // String + case '"': + index = i + 1; + do { + index = token.indexOf('"', index) + 1; + if (!index) { + parseError('Unmatched "'); + } + } while (token[index - 2] === "\\"); + buffer += token.slice(i, index); + i = index - 1; + switch (state) { + case "before-value": + state = "value"; + break; + case "importRule-begin": + state = "importRule"; + break; + } + break; + case "'": + index = i + 1; + do { + index = token.indexOf("'", index) + 1; + if (!index) { + parseError("Unmatched '"); + } + } while (token[index - 2] === "\\"); + buffer += token.slice(i, index); + i = index - 1; + switch (state) { + case "before-value": + state = "value"; + break; + case "importRule-begin": + state = "importRule"; + break; + } + break; + // Comment + case "/": + if (token.charAt(i + 1) === "*") { + i += 2; + index = token.indexOf("*/", i); + if (index === -1) { + parseError("Missing */"); + } else { + i = index + 1; + } + } else { + buffer += character; + } + if (state === "importRule-begin") { + buffer += " "; + state = "importRule"; + } + break; + // At-rule + case "@": + if (token.indexOf("@-moz-document", i) === i) { + state = "documentRule-begin"; + documentRule = new CSSOM.CSSDocumentRule(); + documentRule.__starts = i; + i += "-moz-document".length; + buffer = ""; + break; + } else if (token.indexOf("@media", i) === i) { + state = "atBlock"; + mediaRule = new CSSOM.CSSMediaRule(); + mediaRule.__starts = i; + i += "media".length; + buffer = ""; + break; + } else if (token.indexOf("@supports", i) === i) { + state = "conditionBlock"; + supportsRule = new CSSOM.CSSSupportsRule(); + supportsRule.__starts = i; + i += "supports".length; + buffer = ""; + break; + } else if (token.indexOf("@host", i) === i) { + state = "hostRule-begin"; + i += "host".length; + hostRule = new CSSOM.CSSHostRule(); + hostRule.__starts = i; + buffer = ""; + break; + } else if (token.indexOf("@import", i) === i) { + state = "importRule-begin"; + i += "import".length; + buffer += "@import"; + break; + } else if (token.indexOf("@font-face", i) === i) { + state = "fontFaceRule-begin"; + i += "font-face".length; + fontFaceRule = new CSSOM.CSSFontFaceRule(); + fontFaceRule.__starts = i; + buffer = ""; + break; + } else { + atKeyframesRegExp.lastIndex = i; + var matchKeyframes = atKeyframesRegExp.exec(token); + if (matchKeyframes && matchKeyframes.index === i) { + state = "keyframesRule-begin"; + keyframesRule = new CSSOM.CSSKeyframesRule(); + keyframesRule.__starts = i; + keyframesRule._vendorPrefix = matchKeyframes[1]; + i += matchKeyframes[0].length - 1; + buffer = ""; + break; + } else if (state === "selector") { + state = "atRule"; + } + } + buffer += character; + break; + case "{": + if (state === "selector" || state === "atRule") { + styleRule.selectorText = buffer.trim(); + styleRule.style.__starts = i; + buffer = ""; + state = "before-name"; + } else if (state === "atBlock") { + mediaRule.media.mediaText = buffer.trim(); + if (parentRule) { + ancestorRules.push(parentRule); + } + currentScope = parentRule = mediaRule; + mediaRule.parentStyleSheet = styleSheet; + buffer = ""; + state = "before-selector"; + } else if (state === "conditionBlock") { + supportsRule.conditionText = buffer.trim(); + if (parentRule) { + ancestorRules.push(parentRule); + } + currentScope = parentRule = supportsRule; + supportsRule.parentStyleSheet = styleSheet; + buffer = ""; + state = "before-selector"; + } else if (state === "hostRule-begin") { + if (parentRule) { + ancestorRules.push(parentRule); + } + currentScope = parentRule = hostRule; + hostRule.parentStyleSheet = styleSheet; + buffer = ""; + state = "before-selector"; + } else if (state === "fontFaceRule-begin") { + if (parentRule) { + fontFaceRule.parentRule = parentRule; + } + fontFaceRule.parentStyleSheet = styleSheet; + styleRule = fontFaceRule; + buffer = ""; + state = "before-name"; + } else if (state === "keyframesRule-begin") { + keyframesRule.name = buffer.trim(); + if (parentRule) { + ancestorRules.push(parentRule); + keyframesRule.parentRule = parentRule; + } + keyframesRule.parentStyleSheet = styleSheet; + currentScope = parentRule = keyframesRule; + buffer = ""; + state = "keyframeRule-begin"; + } else if (state === "keyframeRule-begin") { + styleRule = new CSSOM.CSSKeyframeRule(); + styleRule.keyText = buffer.trim(); + styleRule.__starts = i; + buffer = ""; + state = "before-name"; + } else if (state === "documentRule-begin") { + documentRule.matcher.matcherText = buffer.trim(); + if (parentRule) { + ancestorRules.push(parentRule); + documentRule.parentRule = parentRule; + } + currentScope = parentRule = documentRule; + documentRule.parentStyleSheet = styleSheet; + buffer = ""; + state = "before-selector"; + } + break; + case ":": + if (state === "name") { + name = buffer.trim(); + buffer = ""; + state = "before-value"; + } else { + buffer += character; + } + break; + case "(": + if (state === "value") { + if (buffer.trim() === "expression") { + var info = new CSSOM.CSSValueExpression(token, i).parse(); + if (info.error) { + parseError(info.error); + } else { + buffer += info.expression; + i = info.idx; + } + } else { + state = "value-parenthesis"; + valueParenthesisDepth = 1; + buffer += character; + } + } else if (state === "value-parenthesis") { + valueParenthesisDepth++; + buffer += character; + } else { + buffer += character; + } + break; + case ")": + if (state === "value-parenthesis") { + valueParenthesisDepth--; + if (valueParenthesisDepth === 0) state = "value"; + } + buffer += character; + break; + case "!": + if (state === "value" && token.indexOf("!important", i) === i) { + priority = "important"; + i += "important".length; + } else { + buffer += character; + } + break; + case ";": + switch (state) { + case "value": + styleRule.style.setProperty(name, buffer.trim(), priority); + priority = ""; + buffer = ""; + state = "before-name"; + break; + case "atRule": + buffer = ""; + state = "before-selector"; + break; + case "importRule": + importRule = new CSSOM.CSSImportRule(); + importRule.parentStyleSheet = importRule.styleSheet.parentStyleSheet = styleSheet; + importRule.cssText = buffer + character; + styleSheet.cssRules.push(importRule); + buffer = ""; + state = "before-selector"; + break; + default: + buffer += character; + break; + } + break; + case "}": + switch (state) { + case "value": + styleRule.style.setProperty(name, buffer.trim(), priority); + priority = ""; + /* falls through */ + case "before-name": + case "name": + styleRule.__ends = i + 1; + if (parentRule) { + styleRule.parentRule = parentRule; + } + styleRule.parentStyleSheet = styleSheet; + currentScope.cssRules.push(styleRule); + buffer = ""; + if (currentScope.constructor === CSSOM.CSSKeyframesRule) { + state = "keyframeRule-begin"; + } else { + state = "before-selector"; + } + break; + case "keyframeRule-begin": + case "before-selector": + case "selector": + if (!parentRule) { + parseError("Unexpected }"); + } + hasAncestors = ancestorRules.length > 0; + while (ancestorRules.length > 0) { + parentRule = ancestorRules.pop(); + if (parentRule.constructor.name === "CSSMediaRule" || parentRule.constructor.name === "CSSSupportsRule") { + prevScope = currentScope; + currentScope = parentRule; + currentScope.cssRules.push(prevScope); + break; + } + if (ancestorRules.length === 0) { + hasAncestors = false; + } + } + if (!hasAncestors) { + currentScope.__ends = i + 1; + styleSheet.cssRules.push(currentScope); + currentScope = styleSheet; + parentRule = null; + } + buffer = ""; + state = "before-selector"; + break; + } + break; + default: + switch (state) { + case "before-selector": + state = "selector"; + styleRule = new CSSOM.CSSStyleRule(); + styleRule.__starts = i; + break; + case "before-name": + state = "name"; + break; + case "before-value": + state = "value"; + break; + case "importRule-begin": + state = "importRule"; + break; + } + buffer += character; + break; + } + } + return styleSheet; + }; + exports.parse = CSSOM.parse; + CSSOM.CSSStyleSheet = require_CSSStyleSheet().CSSStyleSheet; + CSSOM.CSSStyleRule = require_CSSStyleRule().CSSStyleRule; + CSSOM.CSSImportRule = require_CSSImportRule().CSSImportRule; + CSSOM.CSSGroupingRule = require_CSSGroupingRule().CSSGroupingRule; + CSSOM.CSSMediaRule = require_CSSMediaRule().CSSMediaRule; + CSSOM.CSSConditionRule = require_CSSConditionRule().CSSConditionRule; + CSSOM.CSSSupportsRule = require_CSSSupportsRule().CSSSupportsRule; + CSSOM.CSSFontFaceRule = require_CSSFontFaceRule().CSSFontFaceRule; + CSSOM.CSSHostRule = require_CSSHostRule().CSSHostRule; + CSSOM.CSSStyleDeclaration = require_CSSStyleDeclaration().CSSStyleDeclaration; + CSSOM.CSSKeyframeRule = require_CSSKeyframeRule().CSSKeyframeRule; + CSSOM.CSSKeyframesRule = require_CSSKeyframesRule().CSSKeyframesRule; + CSSOM.CSSValueExpression = require_CSSValueExpression().CSSValueExpression; + CSSOM.CSSDocumentRule = require_CSSDocumentRule().CSSDocumentRule; + } +}); + +// node_modules/cssom/lib/CSSStyleDeclaration.js +var require_CSSStyleDeclaration = __commonJS({ + "node_modules/cssom/lib/CSSStyleDeclaration.js"(exports) { + var CSSOM = {}; + CSSOM.CSSStyleDeclaration = function CSSStyleDeclaration2() { + this.length = 0; + this.parentRule = null; + this._importants = {}; + }; + CSSOM.CSSStyleDeclaration.prototype = { + constructor: CSSOM.CSSStyleDeclaration, + /** + * + * @param {string} name + * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSStyleDeclaration-getPropertyValue + * @return {string} the value of the property if it has been explicitly set for this declaration block. + * Returns the empty string if the property has not been set. + */ + getPropertyValue: function(name) { + return this[name] || ""; + }, + /** + * + * @param {string} name + * @param {string} value + * @param {string} [priority=null] "important" or null + * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSStyleDeclaration-setProperty + */ + setProperty: function(name, value, priority) { + if (this[name]) { + var index = Array.prototype.indexOf.call(this, name); + if (index < 0) { + this[this.length] = name; + this.length++; + } + } else { + this[this.length] = name; + this.length++; + } + this[name] = value + ""; + this._importants[name] = priority; + }, + /** + * + * @param {string} name + * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSStyleDeclaration-removeProperty + * @return {string} the value of the property if it has been explicitly set for this declaration block. + * Returns the empty string if the property has not been set or the property name does not correspond to a known CSS property. + */ + removeProperty: function(name) { + if (!(name in this)) { + return ""; + } + var index = Array.prototype.indexOf.call(this, name); + if (index < 0) { + return ""; + } + var prevValue = this[name]; + this[name] = ""; + Array.prototype.splice.call(this, index, 1); + return prevValue; + }, + getPropertyCSSValue: function() { + }, + /** + * + * @param {String} name + */ + getPropertyPriority: function(name) { + return this._importants[name] || ""; + }, + /** + * element.style.overflow = "auto" + * element.style.getPropertyShorthand("overflow-x") + * -> "overflow" + */ + getPropertyShorthand: function() { + }, + isPropertyImplicit: function() { + }, + // Doesn't work in IE < 9 + get cssText() { + var properties = []; + for (var i = 0, length = this.length; i < length; ++i) { + var name = this[i]; + var value = this.getPropertyValue(name); + var priority = this.getPropertyPriority(name); + if (priority) { + priority = " !" + priority; + } + properties[i] = name + ": " + value + priority + ";"; + } + return properties.join(" "); + }, + set cssText(text) { + var i, name; + for (i = this.length; i--; ) { + name = this[i]; + this[name] = ""; + } + Array.prototype.splice.call(this, 0, this.length); + this._importants = {}; + var dummyRule = CSSOM.parse("#bogus{" + text + "}").cssRules[0].style; + var length = dummyRule.length; + for (i = 0; i < length; ++i) { + name = dummyRule[i]; + this.setProperty(dummyRule[i], dummyRule.getPropertyValue(name), dummyRule.getPropertyPriority(name)); + } + } + }; + exports.CSSStyleDeclaration = CSSOM.CSSStyleDeclaration; + CSSOM.parse = require_parse().parse; + } +}); + +// node_modules/cssom/lib/clone.js +var require_clone = __commonJS({ + "node_modules/cssom/lib/clone.js"(exports) { + var CSSOM = { + CSSStyleSheet: require_CSSStyleSheet().CSSStyleSheet, + CSSRule: require_CSSRule().CSSRule, + CSSStyleRule: require_CSSStyleRule().CSSStyleRule, + CSSGroupingRule: require_CSSGroupingRule().CSSGroupingRule, + CSSConditionRule: require_CSSConditionRule().CSSConditionRule, + CSSMediaRule: require_CSSMediaRule().CSSMediaRule, + CSSSupportsRule: require_CSSSupportsRule().CSSSupportsRule, + CSSStyleDeclaration: require_CSSStyleDeclaration().CSSStyleDeclaration, + CSSKeyframeRule: require_CSSKeyframeRule().CSSKeyframeRule, + CSSKeyframesRule: require_CSSKeyframesRule().CSSKeyframesRule + }; + CSSOM.clone = function clone(stylesheet) { + var cloned = new CSSOM.CSSStyleSheet(); + var rules = stylesheet.cssRules; + if (!rules) { + return cloned; + } + for (var i = 0, rulesLength = rules.length; i < rulesLength; i++) { + var rule = rules[i]; + var ruleClone = cloned.cssRules[i] = new rule.constructor(); + var style = rule.style; + if (style) { + var styleClone = ruleClone.style = new CSSOM.CSSStyleDeclaration(); + for (var j = 0, styleLength = style.length; j < styleLength; j++) { + var name = styleClone[j] = style[j]; + styleClone[name] = style[name]; + styleClone._importants[name] = style.getPropertyPriority(name); + } + styleClone.length = style.length; + } + if (rule.hasOwnProperty("keyText")) { + ruleClone.keyText = rule.keyText; + } + if (rule.hasOwnProperty("selectorText")) { + ruleClone.selectorText = rule.selectorText; + } + if (rule.hasOwnProperty("mediaText")) { + ruleClone.mediaText = rule.mediaText; + } + if (rule.hasOwnProperty("conditionText")) { + ruleClone.conditionText = rule.conditionText; + } + if (rule.hasOwnProperty("cssRules")) { + ruleClone.cssRules = clone(rule).cssRules; + } + } + return cloned; + }; + exports.clone = CSSOM.clone; + } +}); + +// node_modules/cssom/lib/index.js +var require_lib = __commonJS({ + "node_modules/cssom/lib/index.js"(exports) { + "use strict"; + exports.CSSStyleDeclaration = require_CSSStyleDeclaration().CSSStyleDeclaration; + exports.CSSRule = require_CSSRule().CSSRule; + exports.CSSGroupingRule = require_CSSGroupingRule().CSSGroupingRule; + exports.CSSConditionRule = require_CSSConditionRule().CSSConditionRule; + exports.CSSStyleRule = require_CSSStyleRule().CSSStyleRule; + exports.MediaList = require_MediaList().MediaList; + exports.CSSMediaRule = require_CSSMediaRule().CSSMediaRule; + exports.CSSSupportsRule = require_CSSSupportsRule().CSSSupportsRule; + exports.CSSImportRule = require_CSSImportRule().CSSImportRule; + exports.CSSFontFaceRule = require_CSSFontFaceRule().CSSFontFaceRule; + exports.CSSHostRule = require_CSSHostRule().CSSHostRule; + exports.StyleSheet = require_StyleSheet().StyleSheet; + exports.CSSStyleSheet = require_CSSStyleSheet().CSSStyleSheet; + exports.CSSKeyframesRule = require_CSSKeyframesRule().CSSKeyframesRule; + exports.CSSKeyframeRule = require_CSSKeyframeRule().CSSKeyframeRule; + exports.MatcherList = require_MatcherList().MatcherList; + exports.CSSDocumentRule = require_CSSDocumentRule().CSSDocumentRule; + exports.CSSValue = require_CSSValue().CSSValue; + exports.CSSValueExpression = require_CSSValueExpression().CSSValueExpression; + exports.parse = require_parse().parse; + exports.clone = require_clone().clone; + } +}); + +// node_modules/linkedom/commonjs/canvas-shim.cjs +var require_canvas_shim = __commonJS({ + "node_modules/linkedom/commonjs/canvas-shim.cjs"(exports, module) { + var Canvas2 = class { + constructor(width, height) { + this.width = width; + this.height = height; + } + getContext() { + return null; + } + toDataURL() { + return ""; + } + }; + module.exports = { + createCanvas: (width, height) => new Canvas2(width, height) + }; + } +}); + +// node_modules/linkedom/commonjs/canvas.cjs +var require_canvas = __commonJS({ + "node_modules/linkedom/commonjs/canvas.cjs"(exports, module) { + try { + module.exports = __require("canvas"); + } catch (fallback) { + module.exports = require_canvas_shim(); + } + } +}); + +// node_modules/linkedom/esm/shared/symbols.js +var CHANGED = Symbol("changed"); +var CLASS_LIST = Symbol("classList"); +var CUSTOM_ELEMENTS = Symbol("CustomElements"); +var CONTENT = Symbol("content"); +var DATASET = Symbol("dataset"); +var DOCTYPE = Symbol("doctype"); +var DOM_PARSER = Symbol("DOMParser"); +var END = Symbol("end"); +var EVENT_TARGET = Symbol("EventTarget"); +var GLOBALS = Symbol("globals"); +var IMAGE = Symbol("image"); +var MIME = Symbol("mime"); +var MUTATION_OBSERVER = Symbol("MutationObserver"); +var NEXT = Symbol("next"); +var OWNER_ELEMENT = Symbol("ownerElement"); +var PREV = Symbol("prev"); +var PRIVATE = Symbol("private"); +var SHEET = Symbol("sheet"); +var START = Symbol("start"); +var STYLE = Symbol("style"); +var UPGRADE = Symbol("upgrade"); +var VALUE = Symbol("value"); + +// node_modules/htmlparser2/lib/esm/index.js +var esm_exports3 = {}; +__export(esm_exports3, { + DefaultHandler: () => DomHandler, + DomHandler: () => DomHandler, + DomUtils: () => esm_exports2, + ElementType: () => esm_exports, + Parser: () => Parser, + QuoteType: () => QuoteType, + Tokenizer: () => Tokenizer, + createDocumentStream: () => createDocumentStream, + createDomStream: () => createDomStream, + getFeed: () => getFeed, + parseDOM: () => parseDOM, + parseDocument: () => parseDocument, + parseFeed: () => parseFeed +}); + +// node_modules/entities/lib/esm/generated/decode-data-html.js +var decode_data_html_default = new Uint16Array( + // prettier-ignore + '\u1D41<\xD5\u0131\u028A\u049D\u057B\u05D0\u0675\u06DE\u07A2\u07D6\u080F\u0A4A\u0A91\u0DA1\u0E6D\u0F09\u0F26\u10CA\u1228\u12E1\u1415\u149D\u14C3\u14DF\u1525\0\0\0\0\0\0\u156B\u16CD\u198D\u1C12\u1DDD\u1F7E\u2060\u21B0\u228D\u23C0\u23FB\u2442\u2824\u2912\u2D08\u2E48\u2FCE\u3016\u32BA\u3639\u37AC\u38FE\u3A28\u3A71\u3AE0\u3B2E\u0800EMabcfglmnoprstu\\bfms\x7F\x84\x8B\x90\x95\x98\xA6\xB3\xB9\xC8\xCFlig\u803B\xC6\u40C6P\u803B&\u4026cute\u803B\xC1\u40C1reve;\u4102\u0100iyx}rc\u803B\xC2\u40C2;\u4410r;\uC000\u{1D504}rave\u803B\xC0\u40C0pha;\u4391acr;\u4100d;\u6A53\u0100gp\x9D\xA1on;\u4104f;\uC000\u{1D538}plyFunction;\u6061ing\u803B\xC5\u40C5\u0100cs\xBE\xC3r;\uC000\u{1D49C}ign;\u6254ilde\u803B\xC3\u40C3ml\u803B\xC4\u40C4\u0400aceforsu\xE5\xFB\xFE\u0117\u011C\u0122\u0127\u012A\u0100cr\xEA\xF2kslash;\u6216\u0176\xF6\xF8;\u6AE7ed;\u6306y;\u4411\u0180crt\u0105\u010B\u0114ause;\u6235noullis;\u612Ca;\u4392r;\uC000\u{1D505}pf;\uC000\u{1D539}eve;\u42D8c\xF2\u0113mpeq;\u624E\u0700HOacdefhilorsu\u014D\u0151\u0156\u0180\u019E\u01A2\u01B5\u01B7\u01BA\u01DC\u0215\u0273\u0278\u027Ecy;\u4427PY\u803B\xA9\u40A9\u0180cpy\u015D\u0162\u017Aute;\u4106\u0100;i\u0167\u0168\u62D2talDifferentialD;\u6145leys;\u612D\u0200aeio\u0189\u018E\u0194\u0198ron;\u410Cdil\u803B\xC7\u40C7rc;\u4108nint;\u6230ot;\u410A\u0100dn\u01A7\u01ADilla;\u40B8terDot;\u40B7\xF2\u017Fi;\u43A7rcle\u0200DMPT\u01C7\u01CB\u01D1\u01D6ot;\u6299inus;\u6296lus;\u6295imes;\u6297o\u0100cs\u01E2\u01F8kwiseContourIntegral;\u6232eCurly\u0100DQ\u0203\u020FoubleQuote;\u601Duote;\u6019\u0200lnpu\u021E\u0228\u0247\u0255on\u0100;e\u0225\u0226\u6237;\u6A74\u0180git\u022F\u0236\u023Aruent;\u6261nt;\u622FourIntegral;\u622E\u0100fr\u024C\u024E;\u6102oduct;\u6210nterClockwiseContourIntegral;\u6233oss;\u6A2Fcr;\uC000\u{1D49E}p\u0100;C\u0284\u0285\u62D3ap;\u624D\u0580DJSZacefios\u02A0\u02AC\u02B0\u02B4\u02B8\u02CB\u02D7\u02E1\u02E6\u0333\u048D\u0100;o\u0179\u02A5trahd;\u6911cy;\u4402cy;\u4405cy;\u440F\u0180grs\u02BF\u02C4\u02C7ger;\u6021r;\u61A1hv;\u6AE4\u0100ay\u02D0\u02D5ron;\u410E;\u4414l\u0100;t\u02DD\u02DE\u6207a;\u4394r;\uC000\u{1D507}\u0100af\u02EB\u0327\u0100cm\u02F0\u0322ritical\u0200ADGT\u0300\u0306\u0316\u031Ccute;\u40B4o\u0174\u030B\u030D;\u42D9bleAcute;\u42DDrave;\u4060ilde;\u42DCond;\u62C4ferentialD;\u6146\u0470\u033D\0\0\0\u0342\u0354\0\u0405f;\uC000\u{1D53B}\u0180;DE\u0348\u0349\u034D\u40A8ot;\u60DCqual;\u6250ble\u0300CDLRUV\u0363\u0372\u0382\u03CF\u03E2\u03F8ontourIntegra\xEC\u0239o\u0274\u0379\0\0\u037B\xBB\u0349nArrow;\u61D3\u0100eo\u0387\u03A4ft\u0180ART\u0390\u0396\u03A1rrow;\u61D0ightArrow;\u61D4e\xE5\u02CAng\u0100LR\u03AB\u03C4eft\u0100AR\u03B3\u03B9rrow;\u67F8ightArrow;\u67FAightArrow;\u67F9ight\u0100AT\u03D8\u03DErrow;\u61D2ee;\u62A8p\u0241\u03E9\0\0\u03EFrrow;\u61D1ownArrow;\u61D5erticalBar;\u6225n\u0300ABLRTa\u0412\u042A\u0430\u045E\u047F\u037Crrow\u0180;BU\u041D\u041E\u0422\u6193ar;\u6913pArrow;\u61F5reve;\u4311eft\u02D2\u043A\0\u0446\0\u0450ightVector;\u6950eeVector;\u695Eector\u0100;B\u0459\u045A\u61BDar;\u6956ight\u01D4\u0467\0\u0471eeVector;\u695Fector\u0100;B\u047A\u047B\u61C1ar;\u6957ee\u0100;A\u0486\u0487\u62A4rrow;\u61A7\u0100ct\u0492\u0497r;\uC000\u{1D49F}rok;\u4110\u0800NTacdfglmopqstux\u04BD\u04C0\u04C4\u04CB\u04DE\u04E2\u04E7\u04EE\u04F5\u0521\u052F\u0536\u0552\u055D\u0560\u0565G;\u414AH\u803B\xD0\u40D0cute\u803B\xC9\u40C9\u0180aiy\u04D2\u04D7\u04DCron;\u411Arc\u803B\xCA\u40CA;\u442Dot;\u4116r;\uC000\u{1D508}rave\u803B\xC8\u40C8ement;\u6208\u0100ap\u04FA\u04FEcr;\u4112ty\u0253\u0506\0\0\u0512mallSquare;\u65FBerySmallSquare;\u65AB\u0100gp\u0526\u052Aon;\u4118f;\uC000\u{1D53C}silon;\u4395u\u0100ai\u053C\u0549l\u0100;T\u0542\u0543\u6A75ilde;\u6242librium;\u61CC\u0100ci\u0557\u055Ar;\u6130m;\u6A73a;\u4397ml\u803B\xCB\u40CB\u0100ip\u056A\u056Fsts;\u6203onentialE;\u6147\u0280cfios\u0585\u0588\u058D\u05B2\u05CCy;\u4424r;\uC000\u{1D509}lled\u0253\u0597\0\0\u05A3mallSquare;\u65FCerySmallSquare;\u65AA\u0370\u05BA\0\u05BF\0\0\u05C4f;\uC000\u{1D53D}All;\u6200riertrf;\u6131c\xF2\u05CB\u0600JTabcdfgorst\u05E8\u05EC\u05EF\u05FA\u0600\u0612\u0616\u061B\u061D\u0623\u066C\u0672cy;\u4403\u803B>\u403Emma\u0100;d\u05F7\u05F8\u4393;\u43DCreve;\u411E\u0180eiy\u0607\u060C\u0610dil;\u4122rc;\u411C;\u4413ot;\u4120r;\uC000\u{1D50A};\u62D9pf;\uC000\u{1D53E}eater\u0300EFGLST\u0635\u0644\u064E\u0656\u065B\u0666qual\u0100;L\u063E\u063F\u6265ess;\u62DBullEqual;\u6267reater;\u6AA2ess;\u6277lantEqual;\u6A7Eilde;\u6273cr;\uC000\u{1D4A2};\u626B\u0400Aacfiosu\u0685\u068B\u0696\u069B\u069E\u06AA\u06BE\u06CARDcy;\u442A\u0100ct\u0690\u0694ek;\u42C7;\u405Eirc;\u4124r;\u610ClbertSpace;\u610B\u01F0\u06AF\0\u06B2f;\u610DizontalLine;\u6500\u0100ct\u06C3\u06C5\xF2\u06A9rok;\u4126mp\u0144\u06D0\u06D8ownHum\xF0\u012Fqual;\u624F\u0700EJOacdfgmnostu\u06FA\u06FE\u0703\u0707\u070E\u071A\u071E\u0721\u0728\u0744\u0778\u078B\u078F\u0795cy;\u4415lig;\u4132cy;\u4401cute\u803B\xCD\u40CD\u0100iy\u0713\u0718rc\u803B\xCE\u40CE;\u4418ot;\u4130r;\u6111rave\u803B\xCC\u40CC\u0180;ap\u0720\u072F\u073F\u0100cg\u0734\u0737r;\u412AinaryI;\u6148lie\xF3\u03DD\u01F4\u0749\0\u0762\u0100;e\u074D\u074E\u622C\u0100gr\u0753\u0758ral;\u622Bsection;\u62C2isible\u0100CT\u076C\u0772omma;\u6063imes;\u6062\u0180gpt\u077F\u0783\u0788on;\u412Ef;\uC000\u{1D540}a;\u4399cr;\u6110ilde;\u4128\u01EB\u079A\0\u079Ecy;\u4406l\u803B\xCF\u40CF\u0280cfosu\u07AC\u07B7\u07BC\u07C2\u07D0\u0100iy\u07B1\u07B5rc;\u4134;\u4419r;\uC000\u{1D50D}pf;\uC000\u{1D541}\u01E3\u07C7\0\u07CCr;\uC000\u{1D4A5}rcy;\u4408kcy;\u4404\u0380HJacfos\u07E4\u07E8\u07EC\u07F1\u07FD\u0802\u0808cy;\u4425cy;\u440Cppa;\u439A\u0100ey\u07F6\u07FBdil;\u4136;\u441Ar;\uC000\u{1D50E}pf;\uC000\u{1D542}cr;\uC000\u{1D4A6}\u0580JTaceflmost\u0825\u0829\u082C\u0850\u0863\u09B3\u09B8\u09C7\u09CD\u0A37\u0A47cy;\u4409\u803B<\u403C\u0280cmnpr\u0837\u083C\u0841\u0844\u084Dute;\u4139bda;\u439Bg;\u67EAlacetrf;\u6112r;\u619E\u0180aey\u0857\u085C\u0861ron;\u413Ddil;\u413B;\u441B\u0100fs\u0868\u0970t\u0500ACDFRTUVar\u087E\u08A9\u08B1\u08E0\u08E6\u08FC\u092F\u095B\u0390\u096A\u0100nr\u0883\u088FgleBracket;\u67E8row\u0180;BR\u0899\u089A\u089E\u6190ar;\u61E4ightArrow;\u61C6eiling;\u6308o\u01F5\u08B7\0\u08C3bleBracket;\u67E6n\u01D4\u08C8\0\u08D2eeVector;\u6961ector\u0100;B\u08DB\u08DC\u61C3ar;\u6959loor;\u630Aight\u0100AV\u08EF\u08F5rrow;\u6194ector;\u694E\u0100er\u0901\u0917e\u0180;AV\u0909\u090A\u0910\u62A3rrow;\u61A4ector;\u695Aiangle\u0180;BE\u0924\u0925\u0929\u62B2ar;\u69CFqual;\u62B4p\u0180DTV\u0937\u0942\u094CownVector;\u6951eeVector;\u6960ector\u0100;B\u0956\u0957\u61BFar;\u6958ector\u0100;B\u0965\u0966\u61BCar;\u6952ight\xE1\u039Cs\u0300EFGLST\u097E\u098B\u0995\u099D\u09A2\u09ADqualGreater;\u62DAullEqual;\u6266reater;\u6276ess;\u6AA1lantEqual;\u6A7Dilde;\u6272r;\uC000\u{1D50F}\u0100;e\u09BD\u09BE\u62D8ftarrow;\u61DAidot;\u413F\u0180npw\u09D4\u0A16\u0A1Bg\u0200LRlr\u09DE\u09F7\u0A02\u0A10eft\u0100AR\u09E6\u09ECrrow;\u67F5ightArrow;\u67F7ightArrow;\u67F6eft\u0100ar\u03B3\u0A0Aight\xE1\u03BFight\xE1\u03CAf;\uC000\u{1D543}er\u0100LR\u0A22\u0A2CeftArrow;\u6199ightArrow;\u6198\u0180cht\u0A3E\u0A40\u0A42\xF2\u084C;\u61B0rok;\u4141;\u626A\u0400acefiosu\u0A5A\u0A5D\u0A60\u0A77\u0A7C\u0A85\u0A8B\u0A8Ep;\u6905y;\u441C\u0100dl\u0A65\u0A6FiumSpace;\u605Flintrf;\u6133r;\uC000\u{1D510}nusPlus;\u6213pf;\uC000\u{1D544}c\xF2\u0A76;\u439C\u0480Jacefostu\u0AA3\u0AA7\u0AAD\u0AC0\u0B14\u0B19\u0D91\u0D97\u0D9Ecy;\u440Acute;\u4143\u0180aey\u0AB4\u0AB9\u0ABEron;\u4147dil;\u4145;\u441D\u0180gsw\u0AC7\u0AF0\u0B0Eative\u0180MTV\u0AD3\u0ADF\u0AE8ediumSpace;\u600Bhi\u0100cn\u0AE6\u0AD8\xEB\u0AD9eryThi\xEE\u0AD9ted\u0100GL\u0AF8\u0B06reaterGreate\xF2\u0673essLes\xF3\u0A48Line;\u400Ar;\uC000\u{1D511}\u0200Bnpt\u0B22\u0B28\u0B37\u0B3Areak;\u6060BreakingSpace;\u40A0f;\u6115\u0680;CDEGHLNPRSTV\u0B55\u0B56\u0B6A\u0B7C\u0BA1\u0BEB\u0C04\u0C5E\u0C84\u0CA6\u0CD8\u0D61\u0D85\u6AEC\u0100ou\u0B5B\u0B64ngruent;\u6262pCap;\u626DoubleVerticalBar;\u6226\u0180lqx\u0B83\u0B8A\u0B9Bement;\u6209ual\u0100;T\u0B92\u0B93\u6260ilde;\uC000\u2242\u0338ists;\u6204reater\u0380;EFGLST\u0BB6\u0BB7\u0BBD\u0BC9\u0BD3\u0BD8\u0BE5\u626Fqual;\u6271ullEqual;\uC000\u2267\u0338reater;\uC000\u226B\u0338ess;\u6279lantEqual;\uC000\u2A7E\u0338ilde;\u6275ump\u0144\u0BF2\u0BFDownHump;\uC000\u224E\u0338qual;\uC000\u224F\u0338e\u0100fs\u0C0A\u0C27tTriangle\u0180;BE\u0C1A\u0C1B\u0C21\u62EAar;\uC000\u29CF\u0338qual;\u62ECs\u0300;EGLST\u0C35\u0C36\u0C3C\u0C44\u0C4B\u0C58\u626Equal;\u6270reater;\u6278ess;\uC000\u226A\u0338lantEqual;\uC000\u2A7D\u0338ilde;\u6274ested\u0100GL\u0C68\u0C79reaterGreater;\uC000\u2AA2\u0338essLess;\uC000\u2AA1\u0338recedes\u0180;ES\u0C92\u0C93\u0C9B\u6280qual;\uC000\u2AAF\u0338lantEqual;\u62E0\u0100ei\u0CAB\u0CB9verseElement;\u620CghtTriangle\u0180;BE\u0CCB\u0CCC\u0CD2\u62EBar;\uC000\u29D0\u0338qual;\u62ED\u0100qu\u0CDD\u0D0CuareSu\u0100bp\u0CE8\u0CF9set\u0100;E\u0CF0\u0CF3\uC000\u228F\u0338qual;\u62E2erset\u0100;E\u0D03\u0D06\uC000\u2290\u0338qual;\u62E3\u0180bcp\u0D13\u0D24\u0D4Eset\u0100;E\u0D1B\u0D1E\uC000\u2282\u20D2qual;\u6288ceeds\u0200;EST\u0D32\u0D33\u0D3B\u0D46\u6281qual;\uC000\u2AB0\u0338lantEqual;\u62E1ilde;\uC000\u227F\u0338erset\u0100;E\u0D58\u0D5B\uC000\u2283\u20D2qual;\u6289ilde\u0200;EFT\u0D6E\u0D6F\u0D75\u0D7F\u6241qual;\u6244ullEqual;\u6247ilde;\u6249erticalBar;\u6224cr;\uC000\u{1D4A9}ilde\u803B\xD1\u40D1;\u439D\u0700Eacdfgmoprstuv\u0DBD\u0DC2\u0DC9\u0DD5\u0DDB\u0DE0\u0DE7\u0DFC\u0E02\u0E20\u0E22\u0E32\u0E3F\u0E44lig;\u4152cute\u803B\xD3\u40D3\u0100iy\u0DCE\u0DD3rc\u803B\xD4\u40D4;\u441Eblac;\u4150r;\uC000\u{1D512}rave\u803B\xD2\u40D2\u0180aei\u0DEE\u0DF2\u0DF6cr;\u414Cga;\u43A9cron;\u439Fpf;\uC000\u{1D546}enCurly\u0100DQ\u0E0E\u0E1AoubleQuote;\u601Cuote;\u6018;\u6A54\u0100cl\u0E27\u0E2Cr;\uC000\u{1D4AA}ash\u803B\xD8\u40D8i\u016C\u0E37\u0E3Cde\u803B\xD5\u40D5es;\u6A37ml\u803B\xD6\u40D6er\u0100BP\u0E4B\u0E60\u0100ar\u0E50\u0E53r;\u603Eac\u0100ek\u0E5A\u0E5C;\u63DEet;\u63B4arenthesis;\u63DC\u0480acfhilors\u0E7F\u0E87\u0E8A\u0E8F\u0E92\u0E94\u0E9D\u0EB0\u0EFCrtialD;\u6202y;\u441Fr;\uC000\u{1D513}i;\u43A6;\u43A0usMinus;\u40B1\u0100ip\u0EA2\u0EADncareplan\xE5\u069Df;\u6119\u0200;eio\u0EB9\u0EBA\u0EE0\u0EE4\u6ABBcedes\u0200;EST\u0EC8\u0EC9\u0ECF\u0EDA\u627Aqual;\u6AAFlantEqual;\u627Cilde;\u627Eme;\u6033\u0100dp\u0EE9\u0EEEuct;\u620Fortion\u0100;a\u0225\u0EF9l;\u621D\u0100ci\u0F01\u0F06r;\uC000\u{1D4AB};\u43A8\u0200Ufos\u0F11\u0F16\u0F1B\u0F1FOT\u803B"\u4022r;\uC000\u{1D514}pf;\u611Acr;\uC000\u{1D4AC}\u0600BEacefhiorsu\u0F3E\u0F43\u0F47\u0F60\u0F73\u0FA7\u0FAA\u0FAD\u1096\u10A9\u10B4\u10BEarr;\u6910G\u803B\xAE\u40AE\u0180cnr\u0F4E\u0F53\u0F56ute;\u4154g;\u67EBr\u0100;t\u0F5C\u0F5D\u61A0l;\u6916\u0180aey\u0F67\u0F6C\u0F71ron;\u4158dil;\u4156;\u4420\u0100;v\u0F78\u0F79\u611Cerse\u0100EU\u0F82\u0F99\u0100lq\u0F87\u0F8Eement;\u620Builibrium;\u61CBpEquilibrium;\u696Fr\xBB\u0F79o;\u43A1ght\u0400ACDFTUVa\u0FC1\u0FEB\u0FF3\u1022\u1028\u105B\u1087\u03D8\u0100nr\u0FC6\u0FD2gleBracket;\u67E9row\u0180;BL\u0FDC\u0FDD\u0FE1\u6192ar;\u61E5eftArrow;\u61C4eiling;\u6309o\u01F5\u0FF9\0\u1005bleBracket;\u67E7n\u01D4\u100A\0\u1014eeVector;\u695Dector\u0100;B\u101D\u101E\u61C2ar;\u6955loor;\u630B\u0100er\u102D\u1043e\u0180;AV\u1035\u1036\u103C\u62A2rrow;\u61A6ector;\u695Biangle\u0180;BE\u1050\u1051\u1055\u62B3ar;\u69D0qual;\u62B5p\u0180DTV\u1063\u106E\u1078ownVector;\u694FeeVector;\u695Cector\u0100;B\u1082\u1083\u61BEar;\u6954ector\u0100;B\u1091\u1092\u61C0ar;\u6953\u0100pu\u109B\u109Ef;\u611DndImplies;\u6970ightarrow;\u61DB\u0100ch\u10B9\u10BCr;\u611B;\u61B1leDelayed;\u69F4\u0680HOacfhimoqstu\u10E4\u10F1\u10F7\u10FD\u1119\u111E\u1151\u1156\u1161\u1167\u11B5\u11BB\u11BF\u0100Cc\u10E9\u10EEHcy;\u4429y;\u4428FTcy;\u442Ccute;\u415A\u0280;aeiy\u1108\u1109\u110E\u1113\u1117\u6ABCron;\u4160dil;\u415Erc;\u415C;\u4421r;\uC000\u{1D516}ort\u0200DLRU\u112A\u1134\u113E\u1149ownArrow\xBB\u041EeftArrow\xBB\u089AightArrow\xBB\u0FDDpArrow;\u6191gma;\u43A3allCircle;\u6218pf;\uC000\u{1D54A}\u0272\u116D\0\0\u1170t;\u621Aare\u0200;ISU\u117B\u117C\u1189\u11AF\u65A1ntersection;\u6293u\u0100bp\u118F\u119Eset\u0100;E\u1197\u1198\u628Fqual;\u6291erset\u0100;E\u11A8\u11A9\u6290qual;\u6292nion;\u6294cr;\uC000\u{1D4AE}ar;\u62C6\u0200bcmp\u11C8\u11DB\u1209\u120B\u0100;s\u11CD\u11CE\u62D0et\u0100;E\u11CD\u11D5qual;\u6286\u0100ch\u11E0\u1205eeds\u0200;EST\u11ED\u11EE\u11F4\u11FF\u627Bqual;\u6AB0lantEqual;\u627Dilde;\u627FTh\xE1\u0F8C;\u6211\u0180;es\u1212\u1213\u1223\u62D1rset\u0100;E\u121C\u121D\u6283qual;\u6287et\xBB\u1213\u0580HRSacfhiors\u123E\u1244\u1249\u1255\u125E\u1271\u1276\u129F\u12C2\u12C8\u12D1ORN\u803B\xDE\u40DEADE;\u6122\u0100Hc\u124E\u1252cy;\u440By;\u4426\u0100bu\u125A\u125C;\u4009;\u43A4\u0180aey\u1265\u126A\u126Fron;\u4164dil;\u4162;\u4422r;\uC000\u{1D517}\u0100ei\u127B\u1289\u01F2\u1280\0\u1287efore;\u6234a;\u4398\u0100cn\u128E\u1298kSpace;\uC000\u205F\u200ASpace;\u6009lde\u0200;EFT\u12AB\u12AC\u12B2\u12BC\u623Cqual;\u6243ullEqual;\u6245ilde;\u6248pf;\uC000\u{1D54B}ipleDot;\u60DB\u0100ct\u12D6\u12DBr;\uC000\u{1D4AF}rok;\u4166\u0AE1\u12F7\u130E\u131A\u1326\0\u132C\u1331\0\0\0\0\0\u1338\u133D\u1377\u1385\0\u13FF\u1404\u140A\u1410\u0100cr\u12FB\u1301ute\u803B\xDA\u40DAr\u0100;o\u1307\u1308\u619Fcir;\u6949r\u01E3\u1313\0\u1316y;\u440Eve;\u416C\u0100iy\u131E\u1323rc\u803B\xDB\u40DB;\u4423blac;\u4170r;\uC000\u{1D518}rave\u803B\xD9\u40D9acr;\u416A\u0100di\u1341\u1369er\u0100BP\u1348\u135D\u0100ar\u134D\u1350r;\u405Fac\u0100ek\u1357\u1359;\u63DFet;\u63B5arenthesis;\u63DDon\u0100;P\u1370\u1371\u62C3lus;\u628E\u0100gp\u137B\u137Fon;\u4172f;\uC000\u{1D54C}\u0400ADETadps\u1395\u13AE\u13B8\u13C4\u03E8\u13D2\u13D7\u13F3rrow\u0180;BD\u1150\u13A0\u13A4ar;\u6912ownArrow;\u61C5ownArrow;\u6195quilibrium;\u696Eee\u0100;A\u13CB\u13CC\u62A5rrow;\u61A5own\xE1\u03F3er\u0100LR\u13DE\u13E8eftArrow;\u6196ightArrow;\u6197i\u0100;l\u13F9\u13FA\u43D2on;\u43A5ing;\u416Ecr;\uC000\u{1D4B0}ilde;\u4168ml\u803B\xDC\u40DC\u0480Dbcdefosv\u1427\u142C\u1430\u1433\u143E\u1485\u148A\u1490\u1496ash;\u62ABar;\u6AEBy;\u4412ash\u0100;l\u143B\u143C\u62A9;\u6AE6\u0100er\u1443\u1445;\u62C1\u0180bty\u144C\u1450\u147Aar;\u6016\u0100;i\u144F\u1455cal\u0200BLST\u1461\u1465\u146A\u1474ar;\u6223ine;\u407Ceparator;\u6758ilde;\u6240ThinSpace;\u600Ar;\uC000\u{1D519}pf;\uC000\u{1D54D}cr;\uC000\u{1D4B1}dash;\u62AA\u0280cefos\u14A7\u14AC\u14B1\u14B6\u14BCirc;\u4174dge;\u62C0r;\uC000\u{1D51A}pf;\uC000\u{1D54E}cr;\uC000\u{1D4B2}\u0200fios\u14CB\u14D0\u14D2\u14D8r;\uC000\u{1D51B};\u439Epf;\uC000\u{1D54F}cr;\uC000\u{1D4B3}\u0480AIUacfosu\u14F1\u14F5\u14F9\u14FD\u1504\u150F\u1514\u151A\u1520cy;\u442Fcy;\u4407cy;\u442Ecute\u803B\xDD\u40DD\u0100iy\u1509\u150Drc;\u4176;\u442Br;\uC000\u{1D51C}pf;\uC000\u{1D550}cr;\uC000\u{1D4B4}ml;\u4178\u0400Hacdefos\u1535\u1539\u153F\u154B\u154F\u155D\u1560\u1564cy;\u4416cute;\u4179\u0100ay\u1544\u1549ron;\u417D;\u4417ot;\u417B\u01F2\u1554\0\u155BoWidt\xE8\u0AD9a;\u4396r;\u6128pf;\u6124cr;\uC000\u{1D4B5}\u0BE1\u1583\u158A\u1590\0\u15B0\u15B6\u15BF\0\0\0\0\u15C6\u15DB\u15EB\u165F\u166D\0\u1695\u169B\u16B2\u16B9\0\u16BEcute\u803B\xE1\u40E1reve;\u4103\u0300;Ediuy\u159C\u159D\u15A1\u15A3\u15A8\u15AD\u623E;\uC000\u223E\u0333;\u623Frc\u803B\xE2\u40E2te\u80BB\xB4\u0306;\u4430lig\u803B\xE6\u40E6\u0100;r\xB2\u15BA;\uC000\u{1D51E}rave\u803B\xE0\u40E0\u0100ep\u15CA\u15D6\u0100fp\u15CF\u15D4sym;\u6135\xE8\u15D3ha;\u43B1\u0100ap\u15DFc\u0100cl\u15E4\u15E7r;\u4101g;\u6A3F\u0264\u15F0\0\0\u160A\u0280;adsv\u15FA\u15FB\u15FF\u1601\u1607\u6227nd;\u6A55;\u6A5Clope;\u6A58;\u6A5A\u0380;elmrsz\u1618\u1619\u161B\u161E\u163F\u164F\u1659\u6220;\u69A4e\xBB\u1619sd\u0100;a\u1625\u1626\u6221\u0461\u1630\u1632\u1634\u1636\u1638\u163A\u163C\u163E;\u69A8;\u69A9;\u69AA;\u69AB;\u69AC;\u69AD;\u69AE;\u69AFt\u0100;v\u1645\u1646\u621Fb\u0100;d\u164C\u164D\u62BE;\u699D\u0100pt\u1654\u1657h;\u6222\xBB\xB9arr;\u637C\u0100gp\u1663\u1667on;\u4105f;\uC000\u{1D552}\u0380;Eaeiop\u12C1\u167B\u167D\u1682\u1684\u1687\u168A;\u6A70cir;\u6A6F;\u624Ad;\u624Bs;\u4027rox\u0100;e\u12C1\u1692\xF1\u1683ing\u803B\xE5\u40E5\u0180cty\u16A1\u16A6\u16A8r;\uC000\u{1D4B6};\u402Amp\u0100;e\u12C1\u16AF\xF1\u0288ilde\u803B\xE3\u40E3ml\u803B\xE4\u40E4\u0100ci\u16C2\u16C8onin\xF4\u0272nt;\u6A11\u0800Nabcdefiklnoprsu\u16ED\u16F1\u1730\u173C\u1743\u1748\u1778\u177D\u17E0\u17E6\u1839\u1850\u170D\u193D\u1948\u1970ot;\u6AED\u0100cr\u16F6\u171Ek\u0200ceps\u1700\u1705\u170D\u1713ong;\u624Cpsilon;\u43F6rime;\u6035im\u0100;e\u171A\u171B\u623Dq;\u62CD\u0176\u1722\u1726ee;\u62BDed\u0100;g\u172C\u172D\u6305e\xBB\u172Drk\u0100;t\u135C\u1737brk;\u63B6\u0100oy\u1701\u1741;\u4431quo;\u601E\u0280cmprt\u1753\u175B\u1761\u1764\u1768aus\u0100;e\u010A\u0109ptyv;\u69B0s\xE9\u170Cno\xF5\u0113\u0180ahw\u176F\u1771\u1773;\u43B2;\u6136een;\u626Cr;\uC000\u{1D51F}g\u0380costuvw\u178D\u179D\u17B3\u17C1\u17D5\u17DB\u17DE\u0180aiu\u1794\u1796\u179A\xF0\u0760rc;\u65EFp\xBB\u1371\u0180dpt\u17A4\u17A8\u17ADot;\u6A00lus;\u6A01imes;\u6A02\u0271\u17B9\0\0\u17BEcup;\u6A06ar;\u6605riangle\u0100du\u17CD\u17D2own;\u65BDp;\u65B3plus;\u6A04e\xE5\u1444\xE5\u14ADarow;\u690D\u0180ako\u17ED\u1826\u1835\u0100cn\u17F2\u1823k\u0180lst\u17FA\u05AB\u1802ozenge;\u69EBriangle\u0200;dlr\u1812\u1813\u1818\u181D\u65B4own;\u65BEeft;\u65C2ight;\u65B8k;\u6423\u01B1\u182B\0\u1833\u01B2\u182F\0\u1831;\u6592;\u65914;\u6593ck;\u6588\u0100eo\u183E\u184D\u0100;q\u1843\u1846\uC000=\u20E5uiv;\uC000\u2261\u20E5t;\u6310\u0200ptwx\u1859\u185E\u1867\u186Cf;\uC000\u{1D553}\u0100;t\u13CB\u1863om\xBB\u13CCtie;\u62C8\u0600DHUVbdhmptuv\u1885\u1896\u18AA\u18BB\u18D7\u18DB\u18EC\u18FF\u1905\u190A\u1910\u1921\u0200LRlr\u188E\u1890\u1892\u1894;\u6557;\u6554;\u6556;\u6553\u0280;DUdu\u18A1\u18A2\u18A4\u18A6\u18A8\u6550;\u6566;\u6569;\u6564;\u6567\u0200LRlr\u18B3\u18B5\u18B7\u18B9;\u655D;\u655A;\u655C;\u6559\u0380;HLRhlr\u18CA\u18CB\u18CD\u18CF\u18D1\u18D3\u18D5\u6551;\u656C;\u6563;\u6560;\u656B;\u6562;\u655Fox;\u69C9\u0200LRlr\u18E4\u18E6\u18E8\u18EA;\u6555;\u6552;\u6510;\u650C\u0280;DUdu\u06BD\u18F7\u18F9\u18FB\u18FD;\u6565;\u6568;\u652C;\u6534inus;\u629Flus;\u629Eimes;\u62A0\u0200LRlr\u1919\u191B\u191D\u191F;\u655B;\u6558;\u6518;\u6514\u0380;HLRhlr\u1930\u1931\u1933\u1935\u1937\u1939\u193B\u6502;\u656A;\u6561;\u655E;\u653C;\u6524;\u651C\u0100ev\u0123\u1942bar\u803B\xA6\u40A6\u0200ceio\u1951\u1956\u195A\u1960r;\uC000\u{1D4B7}mi;\u604Fm\u0100;e\u171A\u171Cl\u0180;bh\u1968\u1969\u196B\u405C;\u69C5sub;\u67C8\u016C\u1974\u197El\u0100;e\u1979\u197A\u6022t\xBB\u197Ap\u0180;Ee\u012F\u1985\u1987;\u6AAE\u0100;q\u06DC\u06DB\u0CE1\u19A7\0\u19E8\u1A11\u1A15\u1A32\0\u1A37\u1A50\0\0\u1AB4\0\0\u1AC1\0\0\u1B21\u1B2E\u1B4D\u1B52\0\u1BFD\0\u1C0C\u0180cpr\u19AD\u19B2\u19DDute;\u4107\u0300;abcds\u19BF\u19C0\u19C4\u19CA\u19D5\u19D9\u6229nd;\u6A44rcup;\u6A49\u0100au\u19CF\u19D2p;\u6A4Bp;\u6A47ot;\u6A40;\uC000\u2229\uFE00\u0100eo\u19E2\u19E5t;\u6041\xEE\u0693\u0200aeiu\u19F0\u19FB\u1A01\u1A05\u01F0\u19F5\0\u19F8s;\u6A4Don;\u410Ddil\u803B\xE7\u40E7rc;\u4109ps\u0100;s\u1A0C\u1A0D\u6A4Cm;\u6A50ot;\u410B\u0180dmn\u1A1B\u1A20\u1A26il\u80BB\xB8\u01ADptyv;\u69B2t\u8100\xA2;e\u1A2D\u1A2E\u40A2r\xE4\u01B2r;\uC000\u{1D520}\u0180cei\u1A3D\u1A40\u1A4Dy;\u4447ck\u0100;m\u1A47\u1A48\u6713ark\xBB\u1A48;\u43C7r\u0380;Ecefms\u1A5F\u1A60\u1A62\u1A6B\u1AA4\u1AAA\u1AAE\u65CB;\u69C3\u0180;el\u1A69\u1A6A\u1A6D\u42C6q;\u6257e\u0261\u1A74\0\0\u1A88rrow\u0100lr\u1A7C\u1A81eft;\u61BAight;\u61BB\u0280RSacd\u1A92\u1A94\u1A96\u1A9A\u1A9F\xBB\u0F47;\u64C8st;\u629Birc;\u629Aash;\u629Dnint;\u6A10id;\u6AEFcir;\u69C2ubs\u0100;u\u1ABB\u1ABC\u6663it\xBB\u1ABC\u02EC\u1AC7\u1AD4\u1AFA\0\u1B0Aon\u0100;e\u1ACD\u1ACE\u403A\u0100;q\xC7\xC6\u026D\u1AD9\0\0\u1AE2a\u0100;t\u1ADE\u1ADF\u402C;\u4040\u0180;fl\u1AE8\u1AE9\u1AEB\u6201\xEE\u1160e\u0100mx\u1AF1\u1AF6ent\xBB\u1AE9e\xF3\u024D\u01E7\u1AFE\0\u1B07\u0100;d\u12BB\u1B02ot;\u6A6Dn\xF4\u0246\u0180fry\u1B10\u1B14\u1B17;\uC000\u{1D554}o\xE4\u0254\u8100\xA9;s\u0155\u1B1Dr;\u6117\u0100ao\u1B25\u1B29rr;\u61B5ss;\u6717\u0100cu\u1B32\u1B37r;\uC000\u{1D4B8}\u0100bp\u1B3C\u1B44\u0100;e\u1B41\u1B42\u6ACF;\u6AD1\u0100;e\u1B49\u1B4A\u6AD0;\u6AD2dot;\u62EF\u0380delprvw\u1B60\u1B6C\u1B77\u1B82\u1BAC\u1BD4\u1BF9arr\u0100lr\u1B68\u1B6A;\u6938;\u6935\u0270\u1B72\0\0\u1B75r;\u62DEc;\u62DFarr\u0100;p\u1B7F\u1B80\u61B6;\u693D\u0300;bcdos\u1B8F\u1B90\u1B96\u1BA1\u1BA5\u1BA8\u622Arcap;\u6A48\u0100au\u1B9B\u1B9Ep;\u6A46p;\u6A4Aot;\u628Dr;\u6A45;\uC000\u222A\uFE00\u0200alrv\u1BB5\u1BBF\u1BDE\u1BE3rr\u0100;m\u1BBC\u1BBD\u61B7;\u693Cy\u0180evw\u1BC7\u1BD4\u1BD8q\u0270\u1BCE\0\0\u1BD2re\xE3\u1B73u\xE3\u1B75ee;\u62CEedge;\u62CFen\u803B\xA4\u40A4earrow\u0100lr\u1BEE\u1BF3eft\xBB\u1B80ight\xBB\u1BBDe\xE4\u1BDD\u0100ci\u1C01\u1C07onin\xF4\u01F7nt;\u6231lcty;\u632D\u0980AHabcdefhijlorstuwz\u1C38\u1C3B\u1C3F\u1C5D\u1C69\u1C75\u1C8A\u1C9E\u1CAC\u1CB7\u1CFB\u1CFF\u1D0D\u1D7B\u1D91\u1DAB\u1DBB\u1DC6\u1DCDr\xF2\u0381ar;\u6965\u0200glrs\u1C48\u1C4D\u1C52\u1C54ger;\u6020eth;\u6138\xF2\u1133h\u0100;v\u1C5A\u1C5B\u6010\xBB\u090A\u016B\u1C61\u1C67arow;\u690Fa\xE3\u0315\u0100ay\u1C6E\u1C73ron;\u410F;\u4434\u0180;ao\u0332\u1C7C\u1C84\u0100gr\u02BF\u1C81r;\u61CAtseq;\u6A77\u0180glm\u1C91\u1C94\u1C98\u803B\xB0\u40B0ta;\u43B4ptyv;\u69B1\u0100ir\u1CA3\u1CA8sht;\u697F;\uC000\u{1D521}ar\u0100lr\u1CB3\u1CB5\xBB\u08DC\xBB\u101E\u0280aegsv\u1CC2\u0378\u1CD6\u1CDC\u1CE0m\u0180;os\u0326\u1CCA\u1CD4nd\u0100;s\u0326\u1CD1uit;\u6666amma;\u43DDin;\u62F2\u0180;io\u1CE7\u1CE8\u1CF8\u40F7de\u8100\xF7;o\u1CE7\u1CF0ntimes;\u62C7n\xF8\u1CF7cy;\u4452c\u026F\u1D06\0\0\u1D0Arn;\u631Eop;\u630D\u0280lptuw\u1D18\u1D1D\u1D22\u1D49\u1D55lar;\u4024f;\uC000\u{1D555}\u0280;emps\u030B\u1D2D\u1D37\u1D3D\u1D42q\u0100;d\u0352\u1D33ot;\u6251inus;\u6238lus;\u6214quare;\u62A1blebarwedg\xE5\xFAn\u0180adh\u112E\u1D5D\u1D67ownarrow\xF3\u1C83arpoon\u0100lr\u1D72\u1D76ef\xF4\u1CB4igh\xF4\u1CB6\u0162\u1D7F\u1D85karo\xF7\u0F42\u026F\u1D8A\0\0\u1D8Ern;\u631Fop;\u630C\u0180cot\u1D98\u1DA3\u1DA6\u0100ry\u1D9D\u1DA1;\uC000\u{1D4B9};\u4455l;\u69F6rok;\u4111\u0100dr\u1DB0\u1DB4ot;\u62F1i\u0100;f\u1DBA\u1816\u65BF\u0100ah\u1DC0\u1DC3r\xF2\u0429a\xF2\u0FA6angle;\u69A6\u0100ci\u1DD2\u1DD5y;\u445Fgrarr;\u67FF\u0900Dacdefglmnopqrstux\u1E01\u1E09\u1E19\u1E38\u0578\u1E3C\u1E49\u1E61\u1E7E\u1EA5\u1EAF\u1EBD\u1EE1\u1F2A\u1F37\u1F44\u1F4E\u1F5A\u0100Do\u1E06\u1D34o\xF4\u1C89\u0100cs\u1E0E\u1E14ute\u803B\xE9\u40E9ter;\u6A6E\u0200aioy\u1E22\u1E27\u1E31\u1E36ron;\u411Br\u0100;c\u1E2D\u1E2E\u6256\u803B\xEA\u40EAlon;\u6255;\u444Dot;\u4117\u0100Dr\u1E41\u1E45ot;\u6252;\uC000\u{1D522}\u0180;rs\u1E50\u1E51\u1E57\u6A9Aave\u803B\xE8\u40E8\u0100;d\u1E5C\u1E5D\u6A96ot;\u6A98\u0200;ils\u1E6A\u1E6B\u1E72\u1E74\u6A99nters;\u63E7;\u6113\u0100;d\u1E79\u1E7A\u6A95ot;\u6A97\u0180aps\u1E85\u1E89\u1E97cr;\u4113ty\u0180;sv\u1E92\u1E93\u1E95\u6205et\xBB\u1E93p\u01001;\u1E9D\u1EA4\u0133\u1EA1\u1EA3;\u6004;\u6005\u6003\u0100gs\u1EAA\u1EAC;\u414Bp;\u6002\u0100gp\u1EB4\u1EB8on;\u4119f;\uC000\u{1D556}\u0180als\u1EC4\u1ECE\u1ED2r\u0100;s\u1ECA\u1ECB\u62D5l;\u69E3us;\u6A71i\u0180;lv\u1EDA\u1EDB\u1EDF\u43B5on\xBB\u1EDB;\u43F5\u0200csuv\u1EEA\u1EF3\u1F0B\u1F23\u0100io\u1EEF\u1E31rc\xBB\u1E2E\u0269\u1EF9\0\0\u1EFB\xED\u0548ant\u0100gl\u1F02\u1F06tr\xBB\u1E5Dess\xBB\u1E7A\u0180aei\u1F12\u1F16\u1F1Als;\u403Dst;\u625Fv\u0100;D\u0235\u1F20D;\u6A78parsl;\u69E5\u0100Da\u1F2F\u1F33ot;\u6253rr;\u6971\u0180cdi\u1F3E\u1F41\u1EF8r;\u612Fo\xF4\u0352\u0100ah\u1F49\u1F4B;\u43B7\u803B\xF0\u40F0\u0100mr\u1F53\u1F57l\u803B\xEB\u40EBo;\u60AC\u0180cip\u1F61\u1F64\u1F67l;\u4021s\xF4\u056E\u0100eo\u1F6C\u1F74ctatio\xEE\u0559nential\xE5\u0579\u09E1\u1F92\0\u1F9E\0\u1FA1\u1FA7\0\0\u1FC6\u1FCC\0\u1FD3\0\u1FE6\u1FEA\u2000\0\u2008\u205Allingdotse\xF1\u1E44y;\u4444male;\u6640\u0180ilr\u1FAD\u1FB3\u1FC1lig;\u8000\uFB03\u0269\u1FB9\0\0\u1FBDg;\u8000\uFB00ig;\u8000\uFB04;\uC000\u{1D523}lig;\u8000\uFB01lig;\uC000fj\u0180alt\u1FD9\u1FDC\u1FE1t;\u666Dig;\u8000\uFB02ns;\u65B1of;\u4192\u01F0\u1FEE\0\u1FF3f;\uC000\u{1D557}\u0100ak\u05BF\u1FF7\u0100;v\u1FFC\u1FFD\u62D4;\u6AD9artint;\u6A0D\u0100ao\u200C\u2055\u0100cs\u2011\u2052\u03B1\u201A\u2030\u2038\u2045\u2048\0\u2050\u03B2\u2022\u2025\u2027\u202A\u202C\0\u202E\u803B\xBD\u40BD;\u6153\u803B\xBC\u40BC;\u6155;\u6159;\u615B\u01B3\u2034\0\u2036;\u6154;\u6156\u02B4\u203E\u2041\0\0\u2043\u803B\xBE\u40BE;\u6157;\u615C5;\u6158\u01B6\u204C\0\u204E;\u615A;\u615D8;\u615El;\u6044wn;\u6322cr;\uC000\u{1D4BB}\u0880Eabcdefgijlnorstv\u2082\u2089\u209F\u20A5\u20B0\u20B4\u20F0\u20F5\u20FA\u20FF\u2103\u2112\u2138\u0317\u213E\u2152\u219E\u0100;l\u064D\u2087;\u6A8C\u0180cmp\u2090\u2095\u209Dute;\u41F5ma\u0100;d\u209C\u1CDA\u43B3;\u6A86reve;\u411F\u0100iy\u20AA\u20AErc;\u411D;\u4433ot;\u4121\u0200;lqs\u063E\u0642\u20BD\u20C9\u0180;qs\u063E\u064C\u20C4lan\xF4\u0665\u0200;cdl\u0665\u20D2\u20D5\u20E5c;\u6AA9ot\u0100;o\u20DC\u20DD\u6A80\u0100;l\u20E2\u20E3\u6A82;\u6A84\u0100;e\u20EA\u20ED\uC000\u22DB\uFE00s;\u6A94r;\uC000\u{1D524}\u0100;g\u0673\u061Bmel;\u6137cy;\u4453\u0200;Eaj\u065A\u210C\u210E\u2110;\u6A92;\u6AA5;\u6AA4\u0200Eaes\u211B\u211D\u2129\u2134;\u6269p\u0100;p\u2123\u2124\u6A8Arox\xBB\u2124\u0100;q\u212E\u212F\u6A88\u0100;q\u212E\u211Bim;\u62E7pf;\uC000\u{1D558}\u0100ci\u2143\u2146r;\u610Am\u0180;el\u066B\u214E\u2150;\u6A8E;\u6A90\u8300>;cdlqr\u05EE\u2160\u216A\u216E\u2173\u2179\u0100ci\u2165\u2167;\u6AA7r;\u6A7Aot;\u62D7Par;\u6995uest;\u6A7C\u0280adels\u2184\u216A\u2190\u0656\u219B\u01F0\u2189\0\u218Epro\xF8\u209Er;\u6978q\u0100lq\u063F\u2196les\xF3\u2088i\xED\u066B\u0100en\u21A3\u21ADrtneqq;\uC000\u2269\uFE00\xC5\u21AA\u0500Aabcefkosy\u21C4\u21C7\u21F1\u21F5\u21FA\u2218\u221D\u222F\u2268\u227Dr\xF2\u03A0\u0200ilmr\u21D0\u21D4\u21D7\u21DBrs\xF0\u1484f\xBB\u2024il\xF4\u06A9\u0100dr\u21E0\u21E4cy;\u444A\u0180;cw\u08F4\u21EB\u21EFir;\u6948;\u61ADar;\u610Firc;\u4125\u0180alr\u2201\u220E\u2213rts\u0100;u\u2209\u220A\u6665it\xBB\u220Alip;\u6026con;\u62B9r;\uC000\u{1D525}s\u0100ew\u2223\u2229arow;\u6925arow;\u6926\u0280amopr\u223A\u223E\u2243\u225E\u2263rr;\u61FFtht;\u623Bk\u0100lr\u2249\u2253eftarrow;\u61A9ightarrow;\u61AAf;\uC000\u{1D559}bar;\u6015\u0180clt\u226F\u2274\u2278r;\uC000\u{1D4BD}as\xE8\u21F4rok;\u4127\u0100bp\u2282\u2287ull;\u6043hen\xBB\u1C5B\u0AE1\u22A3\0\u22AA\0\u22B8\u22C5\u22CE\0\u22D5\u22F3\0\0\u22F8\u2322\u2367\u2362\u237F\0\u2386\u23AA\u23B4cute\u803B\xED\u40ED\u0180;iy\u0771\u22B0\u22B5rc\u803B\xEE\u40EE;\u4438\u0100cx\u22BC\u22BFy;\u4435cl\u803B\xA1\u40A1\u0100fr\u039F\u22C9;\uC000\u{1D526}rave\u803B\xEC\u40EC\u0200;ino\u073E\u22DD\u22E9\u22EE\u0100in\u22E2\u22E6nt;\u6A0Ct;\u622Dfin;\u69DCta;\u6129lig;\u4133\u0180aop\u22FE\u231A\u231D\u0180cgt\u2305\u2308\u2317r;\u412B\u0180elp\u071F\u230F\u2313in\xE5\u078Ear\xF4\u0720h;\u4131f;\u62B7ed;\u41B5\u0280;cfot\u04F4\u232C\u2331\u233D\u2341are;\u6105in\u0100;t\u2338\u2339\u621Eie;\u69DDdo\xF4\u2319\u0280;celp\u0757\u234C\u2350\u235B\u2361al;\u62BA\u0100gr\u2355\u2359er\xF3\u1563\xE3\u234Darhk;\u6A17rod;\u6A3C\u0200cgpt\u236F\u2372\u2376\u237By;\u4451on;\u412Ff;\uC000\u{1D55A}a;\u43B9uest\u803B\xBF\u40BF\u0100ci\u238A\u238Fr;\uC000\u{1D4BE}n\u0280;Edsv\u04F4\u239B\u239D\u23A1\u04F3;\u62F9ot;\u62F5\u0100;v\u23A6\u23A7\u62F4;\u62F3\u0100;i\u0777\u23AElde;\u4129\u01EB\u23B8\0\u23BCcy;\u4456l\u803B\xEF\u40EF\u0300cfmosu\u23CC\u23D7\u23DC\u23E1\u23E7\u23F5\u0100iy\u23D1\u23D5rc;\u4135;\u4439r;\uC000\u{1D527}ath;\u4237pf;\uC000\u{1D55B}\u01E3\u23EC\0\u23F1r;\uC000\u{1D4BF}rcy;\u4458kcy;\u4454\u0400acfghjos\u240B\u2416\u2422\u2427\u242D\u2431\u2435\u243Bppa\u0100;v\u2413\u2414\u43BA;\u43F0\u0100ey\u241B\u2420dil;\u4137;\u443Ar;\uC000\u{1D528}reen;\u4138cy;\u4445cy;\u445Cpf;\uC000\u{1D55C}cr;\uC000\u{1D4C0}\u0B80ABEHabcdefghjlmnoprstuv\u2470\u2481\u2486\u248D\u2491\u250E\u253D\u255A\u2580\u264E\u265E\u2665\u2679\u267D\u269A\u26B2\u26D8\u275D\u2768\u278B\u27C0\u2801\u2812\u0180art\u2477\u247A\u247Cr\xF2\u09C6\xF2\u0395ail;\u691Barr;\u690E\u0100;g\u0994\u248B;\u6A8Bar;\u6962\u0963\u24A5\0\u24AA\0\u24B1\0\0\0\0\0\u24B5\u24BA\0\u24C6\u24C8\u24CD\0\u24F9ute;\u413Amptyv;\u69B4ra\xEE\u084Cbda;\u43BBg\u0180;dl\u088E\u24C1\u24C3;\u6991\xE5\u088E;\u6A85uo\u803B\xAB\u40ABr\u0400;bfhlpst\u0899\u24DE\u24E6\u24E9\u24EB\u24EE\u24F1\u24F5\u0100;f\u089D\u24E3s;\u691Fs;\u691D\xEB\u2252p;\u61ABl;\u6939im;\u6973l;\u61A2\u0180;ae\u24FF\u2500\u2504\u6AABil;\u6919\u0100;s\u2509\u250A\u6AAD;\uC000\u2AAD\uFE00\u0180abr\u2515\u2519\u251Drr;\u690Crk;\u6772\u0100ak\u2522\u252Cc\u0100ek\u2528\u252A;\u407B;\u405B\u0100es\u2531\u2533;\u698Bl\u0100du\u2539\u253B;\u698F;\u698D\u0200aeuy\u2546\u254B\u2556\u2558ron;\u413E\u0100di\u2550\u2554il;\u413C\xEC\u08B0\xE2\u2529;\u443B\u0200cqrs\u2563\u2566\u256D\u257Da;\u6936uo\u0100;r\u0E19\u1746\u0100du\u2572\u2577har;\u6967shar;\u694Bh;\u61B2\u0280;fgqs\u258B\u258C\u0989\u25F3\u25FF\u6264t\u0280ahlrt\u2598\u25A4\u25B7\u25C2\u25E8rrow\u0100;t\u0899\u25A1a\xE9\u24F6arpoon\u0100du\u25AF\u25B4own\xBB\u045Ap\xBB\u0966eftarrows;\u61C7ight\u0180ahs\u25CD\u25D6\u25DErrow\u0100;s\u08F4\u08A7arpoon\xF3\u0F98quigarro\xF7\u21F0hreetimes;\u62CB\u0180;qs\u258B\u0993\u25FAlan\xF4\u09AC\u0280;cdgs\u09AC\u260A\u260D\u261D\u2628c;\u6AA8ot\u0100;o\u2614\u2615\u6A7F\u0100;r\u261A\u261B\u6A81;\u6A83\u0100;e\u2622\u2625\uC000\u22DA\uFE00s;\u6A93\u0280adegs\u2633\u2639\u263D\u2649\u264Bppro\xF8\u24C6ot;\u62D6q\u0100gq\u2643\u2645\xF4\u0989gt\xF2\u248C\xF4\u099Bi\xED\u09B2\u0180ilr\u2655\u08E1\u265Asht;\u697C;\uC000\u{1D529}\u0100;E\u099C\u2663;\u6A91\u0161\u2669\u2676r\u0100du\u25B2\u266E\u0100;l\u0965\u2673;\u696Alk;\u6584cy;\u4459\u0280;acht\u0A48\u2688\u268B\u2691\u2696r\xF2\u25C1orne\xF2\u1D08ard;\u696Bri;\u65FA\u0100io\u269F\u26A4dot;\u4140ust\u0100;a\u26AC\u26AD\u63B0che\xBB\u26AD\u0200Eaes\u26BB\u26BD\u26C9\u26D4;\u6268p\u0100;p\u26C3\u26C4\u6A89rox\xBB\u26C4\u0100;q\u26CE\u26CF\u6A87\u0100;q\u26CE\u26BBim;\u62E6\u0400abnoptwz\u26E9\u26F4\u26F7\u271A\u272F\u2741\u2747\u2750\u0100nr\u26EE\u26F1g;\u67ECr;\u61FDr\xEB\u08C1g\u0180lmr\u26FF\u270D\u2714eft\u0100ar\u09E6\u2707ight\xE1\u09F2apsto;\u67FCight\xE1\u09FDparrow\u0100lr\u2725\u2729ef\xF4\u24EDight;\u61AC\u0180afl\u2736\u2739\u273Dr;\u6985;\uC000\u{1D55D}us;\u6A2Dimes;\u6A34\u0161\u274B\u274Fst;\u6217\xE1\u134E\u0180;ef\u2757\u2758\u1800\u65CAnge\xBB\u2758ar\u0100;l\u2764\u2765\u4028t;\u6993\u0280achmt\u2773\u2776\u277C\u2785\u2787r\xF2\u08A8orne\xF2\u1D8Car\u0100;d\u0F98\u2783;\u696D;\u600Eri;\u62BF\u0300achiqt\u2798\u279D\u0A40\u27A2\u27AE\u27BBquo;\u6039r;\uC000\u{1D4C1}m\u0180;eg\u09B2\u27AA\u27AC;\u6A8D;\u6A8F\u0100bu\u252A\u27B3o\u0100;r\u0E1F\u27B9;\u601Arok;\u4142\u8400<;cdhilqr\u082B\u27D2\u2639\u27DC\u27E0\u27E5\u27EA\u27F0\u0100ci\u27D7\u27D9;\u6AA6r;\u6A79re\xE5\u25F2mes;\u62C9arr;\u6976uest;\u6A7B\u0100Pi\u27F5\u27F9ar;\u6996\u0180;ef\u2800\u092D\u181B\u65C3r\u0100du\u2807\u280Dshar;\u694Ahar;\u6966\u0100en\u2817\u2821rtneqq;\uC000\u2268\uFE00\xC5\u281E\u0700Dacdefhilnopsu\u2840\u2845\u2882\u288E\u2893\u28A0\u28A5\u28A8\u28DA\u28E2\u28E4\u0A83\u28F3\u2902Dot;\u623A\u0200clpr\u284E\u2852\u2863\u287Dr\u803B\xAF\u40AF\u0100et\u2857\u2859;\u6642\u0100;e\u285E\u285F\u6720se\xBB\u285F\u0100;s\u103B\u2868to\u0200;dlu\u103B\u2873\u2877\u287Bow\xEE\u048Cef\xF4\u090F\xF0\u13D1ker;\u65AE\u0100oy\u2887\u288Cmma;\u6A29;\u443Cash;\u6014asuredangle\xBB\u1626r;\uC000\u{1D52A}o;\u6127\u0180cdn\u28AF\u28B4\u28C9ro\u803B\xB5\u40B5\u0200;acd\u1464\u28BD\u28C0\u28C4s\xF4\u16A7ir;\u6AF0ot\u80BB\xB7\u01B5us\u0180;bd\u28D2\u1903\u28D3\u6212\u0100;u\u1D3C\u28D8;\u6A2A\u0163\u28DE\u28E1p;\u6ADB\xF2\u2212\xF0\u0A81\u0100dp\u28E9\u28EEels;\u62A7f;\uC000\u{1D55E}\u0100ct\u28F8\u28FDr;\uC000\u{1D4C2}pos\xBB\u159D\u0180;lm\u2909\u290A\u290D\u43BCtimap;\u62B8\u0C00GLRVabcdefghijlmoprstuvw\u2942\u2953\u297E\u2989\u2998\u29DA\u29E9\u2A15\u2A1A\u2A58\u2A5D\u2A83\u2A95\u2AA4\u2AA8\u2B04\u2B07\u2B44\u2B7F\u2BAE\u2C34\u2C67\u2C7C\u2CE9\u0100gt\u2947\u294B;\uC000\u22D9\u0338\u0100;v\u2950\u0BCF\uC000\u226B\u20D2\u0180elt\u295A\u2972\u2976ft\u0100ar\u2961\u2967rrow;\u61CDightarrow;\u61CE;\uC000\u22D8\u0338\u0100;v\u297B\u0C47\uC000\u226A\u20D2ightarrow;\u61CF\u0100Dd\u298E\u2993ash;\u62AFash;\u62AE\u0280bcnpt\u29A3\u29A7\u29AC\u29B1\u29CCla\xBB\u02DEute;\u4144g;\uC000\u2220\u20D2\u0280;Eiop\u0D84\u29BC\u29C0\u29C5\u29C8;\uC000\u2A70\u0338d;\uC000\u224B\u0338s;\u4149ro\xF8\u0D84ur\u0100;a\u29D3\u29D4\u666El\u0100;s\u29D3\u0B38\u01F3\u29DF\0\u29E3p\u80BB\xA0\u0B37mp\u0100;e\u0BF9\u0C00\u0280aeouy\u29F4\u29FE\u2A03\u2A10\u2A13\u01F0\u29F9\0\u29FB;\u6A43on;\u4148dil;\u4146ng\u0100;d\u0D7E\u2A0Aot;\uC000\u2A6D\u0338p;\u6A42;\u443Dash;\u6013\u0380;Aadqsx\u0B92\u2A29\u2A2D\u2A3B\u2A41\u2A45\u2A50rr;\u61D7r\u0100hr\u2A33\u2A36k;\u6924\u0100;o\u13F2\u13F0ot;\uC000\u2250\u0338ui\xF6\u0B63\u0100ei\u2A4A\u2A4Ear;\u6928\xED\u0B98ist\u0100;s\u0BA0\u0B9Fr;\uC000\u{1D52B}\u0200Eest\u0BC5\u2A66\u2A79\u2A7C\u0180;qs\u0BBC\u2A6D\u0BE1\u0180;qs\u0BBC\u0BC5\u2A74lan\xF4\u0BE2i\xED\u0BEA\u0100;r\u0BB6\u2A81\xBB\u0BB7\u0180Aap\u2A8A\u2A8D\u2A91r\xF2\u2971rr;\u61AEar;\u6AF2\u0180;sv\u0F8D\u2A9C\u0F8C\u0100;d\u2AA1\u2AA2\u62FC;\u62FAcy;\u445A\u0380AEadest\u2AB7\u2ABA\u2ABE\u2AC2\u2AC5\u2AF6\u2AF9r\xF2\u2966;\uC000\u2266\u0338rr;\u619Ar;\u6025\u0200;fqs\u0C3B\u2ACE\u2AE3\u2AEFt\u0100ar\u2AD4\u2AD9rro\xF7\u2AC1ightarro\xF7\u2A90\u0180;qs\u0C3B\u2ABA\u2AEAlan\xF4\u0C55\u0100;s\u0C55\u2AF4\xBB\u0C36i\xED\u0C5D\u0100;r\u0C35\u2AFEi\u0100;e\u0C1A\u0C25i\xE4\u0D90\u0100pt\u2B0C\u2B11f;\uC000\u{1D55F}\u8180\xAC;in\u2B19\u2B1A\u2B36\u40ACn\u0200;Edv\u0B89\u2B24\u2B28\u2B2E;\uC000\u22F9\u0338ot;\uC000\u22F5\u0338\u01E1\u0B89\u2B33\u2B35;\u62F7;\u62F6i\u0100;v\u0CB8\u2B3C\u01E1\u0CB8\u2B41\u2B43;\u62FE;\u62FD\u0180aor\u2B4B\u2B63\u2B69r\u0200;ast\u0B7B\u2B55\u2B5A\u2B5Flle\xEC\u0B7Bl;\uC000\u2AFD\u20E5;\uC000\u2202\u0338lint;\u6A14\u0180;ce\u0C92\u2B70\u2B73u\xE5\u0CA5\u0100;c\u0C98\u2B78\u0100;e\u0C92\u2B7D\xF1\u0C98\u0200Aait\u2B88\u2B8B\u2B9D\u2BA7r\xF2\u2988rr\u0180;cw\u2B94\u2B95\u2B99\u619B;\uC000\u2933\u0338;\uC000\u219D\u0338ghtarrow\xBB\u2B95ri\u0100;e\u0CCB\u0CD6\u0380chimpqu\u2BBD\u2BCD\u2BD9\u2B04\u0B78\u2BE4\u2BEF\u0200;cer\u0D32\u2BC6\u0D37\u2BC9u\xE5\u0D45;\uC000\u{1D4C3}ort\u026D\u2B05\0\0\u2BD6ar\xE1\u2B56m\u0100;e\u0D6E\u2BDF\u0100;q\u0D74\u0D73su\u0100bp\u2BEB\u2BED\xE5\u0CF8\xE5\u0D0B\u0180bcp\u2BF6\u2C11\u2C19\u0200;Ees\u2BFF\u2C00\u0D22\u2C04\u6284;\uC000\u2AC5\u0338et\u0100;e\u0D1B\u2C0Bq\u0100;q\u0D23\u2C00c\u0100;e\u0D32\u2C17\xF1\u0D38\u0200;Ees\u2C22\u2C23\u0D5F\u2C27\u6285;\uC000\u2AC6\u0338et\u0100;e\u0D58\u2C2Eq\u0100;q\u0D60\u2C23\u0200gilr\u2C3D\u2C3F\u2C45\u2C47\xEC\u0BD7lde\u803B\xF1\u40F1\xE7\u0C43iangle\u0100lr\u2C52\u2C5Ceft\u0100;e\u0C1A\u2C5A\xF1\u0C26ight\u0100;e\u0CCB\u2C65\xF1\u0CD7\u0100;m\u2C6C\u2C6D\u43BD\u0180;es\u2C74\u2C75\u2C79\u4023ro;\u6116p;\u6007\u0480DHadgilrs\u2C8F\u2C94\u2C99\u2C9E\u2CA3\u2CB0\u2CB6\u2CD3\u2CE3ash;\u62ADarr;\u6904p;\uC000\u224D\u20D2ash;\u62AC\u0100et\u2CA8\u2CAC;\uC000\u2265\u20D2;\uC000>\u20D2nfin;\u69DE\u0180Aet\u2CBD\u2CC1\u2CC5rr;\u6902;\uC000\u2264\u20D2\u0100;r\u2CCA\u2CCD\uC000<\u20D2ie;\uC000\u22B4\u20D2\u0100At\u2CD8\u2CDCrr;\u6903rie;\uC000\u22B5\u20D2im;\uC000\u223C\u20D2\u0180Aan\u2CF0\u2CF4\u2D02rr;\u61D6r\u0100hr\u2CFA\u2CFDk;\u6923\u0100;o\u13E7\u13E5ear;\u6927\u1253\u1A95\0\0\0\0\0\0\0\0\0\0\0\0\0\u2D2D\0\u2D38\u2D48\u2D60\u2D65\u2D72\u2D84\u1B07\0\0\u2D8D\u2DAB\0\u2DC8\u2DCE\0\u2DDC\u2E19\u2E2B\u2E3E\u2E43\u0100cs\u2D31\u1A97ute\u803B\xF3\u40F3\u0100iy\u2D3C\u2D45r\u0100;c\u1A9E\u2D42\u803B\xF4\u40F4;\u443E\u0280abios\u1AA0\u2D52\u2D57\u01C8\u2D5Alac;\u4151v;\u6A38old;\u69BClig;\u4153\u0100cr\u2D69\u2D6Dir;\u69BF;\uC000\u{1D52C}\u036F\u2D79\0\0\u2D7C\0\u2D82n;\u42DBave\u803B\xF2\u40F2;\u69C1\u0100bm\u2D88\u0DF4ar;\u69B5\u0200acit\u2D95\u2D98\u2DA5\u2DA8r\xF2\u1A80\u0100ir\u2D9D\u2DA0r;\u69BEoss;\u69BBn\xE5\u0E52;\u69C0\u0180aei\u2DB1\u2DB5\u2DB9cr;\u414Dga;\u43C9\u0180cdn\u2DC0\u2DC5\u01CDron;\u43BF;\u69B6pf;\uC000\u{1D560}\u0180ael\u2DD4\u2DD7\u01D2r;\u69B7rp;\u69B9\u0380;adiosv\u2DEA\u2DEB\u2DEE\u2E08\u2E0D\u2E10\u2E16\u6228r\xF2\u1A86\u0200;efm\u2DF7\u2DF8\u2E02\u2E05\u6A5Dr\u0100;o\u2DFE\u2DFF\u6134f\xBB\u2DFF\u803B\xAA\u40AA\u803B\xBA\u40BAgof;\u62B6r;\u6A56lope;\u6A57;\u6A5B\u0180clo\u2E1F\u2E21\u2E27\xF2\u2E01ash\u803B\xF8\u40F8l;\u6298i\u016C\u2E2F\u2E34de\u803B\xF5\u40F5es\u0100;a\u01DB\u2E3As;\u6A36ml\u803B\xF6\u40F6bar;\u633D\u0AE1\u2E5E\0\u2E7D\0\u2E80\u2E9D\0\u2EA2\u2EB9\0\0\u2ECB\u0E9C\0\u2F13\0\0\u2F2B\u2FBC\0\u2FC8r\u0200;ast\u0403\u2E67\u2E72\u0E85\u8100\xB6;l\u2E6D\u2E6E\u40B6le\xEC\u0403\u0269\u2E78\0\0\u2E7Bm;\u6AF3;\u6AFDy;\u443Fr\u0280cimpt\u2E8B\u2E8F\u2E93\u1865\u2E97nt;\u4025od;\u402Eil;\u6030enk;\u6031r;\uC000\u{1D52D}\u0180imo\u2EA8\u2EB0\u2EB4\u0100;v\u2EAD\u2EAE\u43C6;\u43D5ma\xF4\u0A76ne;\u660E\u0180;tv\u2EBF\u2EC0\u2EC8\u43C0chfork\xBB\u1FFD;\u43D6\u0100au\u2ECF\u2EDFn\u0100ck\u2ED5\u2EDDk\u0100;h\u21F4\u2EDB;\u610E\xF6\u21F4s\u0480;abcdemst\u2EF3\u2EF4\u1908\u2EF9\u2EFD\u2F04\u2F06\u2F0A\u2F0E\u402Bcir;\u6A23ir;\u6A22\u0100ou\u1D40\u2F02;\u6A25;\u6A72n\u80BB\xB1\u0E9Dim;\u6A26wo;\u6A27\u0180ipu\u2F19\u2F20\u2F25ntint;\u6A15f;\uC000\u{1D561}nd\u803B\xA3\u40A3\u0500;Eaceinosu\u0EC8\u2F3F\u2F41\u2F44\u2F47\u2F81\u2F89\u2F92\u2F7E\u2FB6;\u6AB3p;\u6AB7u\xE5\u0ED9\u0100;c\u0ECE\u2F4C\u0300;acens\u0EC8\u2F59\u2F5F\u2F66\u2F68\u2F7Eppro\xF8\u2F43urlye\xF1\u0ED9\xF1\u0ECE\u0180aes\u2F6F\u2F76\u2F7Approx;\u6AB9qq;\u6AB5im;\u62E8i\xED\u0EDFme\u0100;s\u2F88\u0EAE\u6032\u0180Eas\u2F78\u2F90\u2F7A\xF0\u2F75\u0180dfp\u0EEC\u2F99\u2FAF\u0180als\u2FA0\u2FA5\u2FAAlar;\u632Eine;\u6312urf;\u6313\u0100;t\u0EFB\u2FB4\xEF\u0EFBrel;\u62B0\u0100ci\u2FC0\u2FC5r;\uC000\u{1D4C5};\u43C8ncsp;\u6008\u0300fiopsu\u2FDA\u22E2\u2FDF\u2FE5\u2FEB\u2FF1r;\uC000\u{1D52E}pf;\uC000\u{1D562}rime;\u6057cr;\uC000\u{1D4C6}\u0180aeo\u2FF8\u3009\u3013t\u0100ei\u2FFE\u3005rnion\xF3\u06B0nt;\u6A16st\u0100;e\u3010\u3011\u403F\xF1\u1F19\xF4\u0F14\u0A80ABHabcdefhilmnoprstux\u3040\u3051\u3055\u3059\u30E0\u310E\u312B\u3147\u3162\u3172\u318E\u3206\u3215\u3224\u3229\u3258\u326E\u3272\u3290\u32B0\u32B7\u0180art\u3047\u304A\u304Cr\xF2\u10B3\xF2\u03DDail;\u691Car\xF2\u1C65ar;\u6964\u0380cdenqrt\u3068\u3075\u3078\u307F\u308F\u3094\u30CC\u0100eu\u306D\u3071;\uC000\u223D\u0331te;\u4155i\xE3\u116Emptyv;\u69B3g\u0200;del\u0FD1\u3089\u308B\u308D;\u6992;\u69A5\xE5\u0FD1uo\u803B\xBB\u40BBr\u0580;abcfhlpstw\u0FDC\u30AC\u30AF\u30B7\u30B9\u30BC\u30BE\u30C0\u30C3\u30C7\u30CAp;\u6975\u0100;f\u0FE0\u30B4s;\u6920;\u6933s;\u691E\xEB\u225D\xF0\u272El;\u6945im;\u6974l;\u61A3;\u619D\u0100ai\u30D1\u30D5il;\u691Ao\u0100;n\u30DB\u30DC\u6236al\xF3\u0F1E\u0180abr\u30E7\u30EA\u30EEr\xF2\u17E5rk;\u6773\u0100ak\u30F3\u30FDc\u0100ek\u30F9\u30FB;\u407D;\u405D\u0100es\u3102\u3104;\u698Cl\u0100du\u310A\u310C;\u698E;\u6990\u0200aeuy\u3117\u311C\u3127\u3129ron;\u4159\u0100di\u3121\u3125il;\u4157\xEC\u0FF2\xE2\u30FA;\u4440\u0200clqs\u3134\u3137\u313D\u3144a;\u6937dhar;\u6969uo\u0100;r\u020E\u020Dh;\u61B3\u0180acg\u314E\u315F\u0F44l\u0200;ips\u0F78\u3158\u315B\u109Cn\xE5\u10BBar\xF4\u0FA9t;\u65AD\u0180ilr\u3169\u1023\u316Esht;\u697D;\uC000\u{1D52F}\u0100ao\u3177\u3186r\u0100du\u317D\u317F\xBB\u047B\u0100;l\u1091\u3184;\u696C\u0100;v\u318B\u318C\u43C1;\u43F1\u0180gns\u3195\u31F9\u31FCht\u0300ahlrst\u31A4\u31B0\u31C2\u31D8\u31E4\u31EErrow\u0100;t\u0FDC\u31ADa\xE9\u30C8arpoon\u0100du\u31BB\u31BFow\xEE\u317Ep\xBB\u1092eft\u0100ah\u31CA\u31D0rrow\xF3\u0FEAarpoon\xF3\u0551ightarrows;\u61C9quigarro\xF7\u30CBhreetimes;\u62CCg;\u42DAingdotse\xF1\u1F32\u0180ahm\u320D\u3210\u3213r\xF2\u0FEAa\xF2\u0551;\u600Foust\u0100;a\u321E\u321F\u63B1che\xBB\u321Fmid;\u6AEE\u0200abpt\u3232\u323D\u3240\u3252\u0100nr\u3237\u323Ag;\u67EDr;\u61FEr\xEB\u1003\u0180afl\u3247\u324A\u324Er;\u6986;\uC000\u{1D563}us;\u6A2Eimes;\u6A35\u0100ap\u325D\u3267r\u0100;g\u3263\u3264\u4029t;\u6994olint;\u6A12ar\xF2\u31E3\u0200achq\u327B\u3280\u10BC\u3285quo;\u603Ar;\uC000\u{1D4C7}\u0100bu\u30FB\u328Ao\u0100;r\u0214\u0213\u0180hir\u3297\u329B\u32A0re\xE5\u31F8mes;\u62CAi\u0200;efl\u32AA\u1059\u1821\u32AB\u65B9tri;\u69CEluhar;\u6968;\u611E\u0D61\u32D5\u32DB\u32DF\u332C\u3338\u3371\0\u337A\u33A4\0\0\u33EC\u33F0\0\u3428\u3448\u345A\u34AD\u34B1\u34CA\u34F1\0\u3616\0\0\u3633cute;\u415Bqu\xEF\u27BA\u0500;Eaceinpsy\u11ED\u32F3\u32F5\u32FF\u3302\u330B\u330F\u331F\u3326\u3329;\u6AB4\u01F0\u32FA\0\u32FC;\u6AB8on;\u4161u\xE5\u11FE\u0100;d\u11F3\u3307il;\u415Frc;\u415D\u0180Eas\u3316\u3318\u331B;\u6AB6p;\u6ABAim;\u62E9olint;\u6A13i\xED\u1204;\u4441ot\u0180;be\u3334\u1D47\u3335\u62C5;\u6A66\u0380Aacmstx\u3346\u334A\u3357\u335B\u335E\u3363\u336Drr;\u61D8r\u0100hr\u3350\u3352\xEB\u2228\u0100;o\u0A36\u0A34t\u803B\xA7\u40A7i;\u403Bwar;\u6929m\u0100in\u3369\xF0nu\xF3\xF1t;\u6736r\u0100;o\u3376\u2055\uC000\u{1D530}\u0200acoy\u3382\u3386\u3391\u33A0rp;\u666F\u0100hy\u338B\u338Fcy;\u4449;\u4448rt\u026D\u3399\0\0\u339Ci\xE4\u1464ara\xEC\u2E6F\u803B\xAD\u40AD\u0100gm\u33A8\u33B4ma\u0180;fv\u33B1\u33B2\u33B2\u43C3;\u43C2\u0400;deglnpr\u12AB\u33C5\u33C9\u33CE\u33D6\u33DE\u33E1\u33E6ot;\u6A6A\u0100;q\u12B1\u12B0\u0100;E\u33D3\u33D4\u6A9E;\u6AA0\u0100;E\u33DB\u33DC\u6A9D;\u6A9Fe;\u6246lus;\u6A24arr;\u6972ar\xF2\u113D\u0200aeit\u33F8\u3408\u340F\u3417\u0100ls\u33FD\u3404lsetm\xE9\u336Ahp;\u6A33parsl;\u69E4\u0100dl\u1463\u3414e;\u6323\u0100;e\u341C\u341D\u6AAA\u0100;s\u3422\u3423\u6AAC;\uC000\u2AAC\uFE00\u0180flp\u342E\u3433\u3442tcy;\u444C\u0100;b\u3438\u3439\u402F\u0100;a\u343E\u343F\u69C4r;\u633Ff;\uC000\u{1D564}a\u0100dr\u344D\u0402es\u0100;u\u3454\u3455\u6660it\xBB\u3455\u0180csu\u3460\u3479\u349F\u0100au\u3465\u346Fp\u0100;s\u1188\u346B;\uC000\u2293\uFE00p\u0100;s\u11B4\u3475;\uC000\u2294\uFE00u\u0100bp\u347F\u348F\u0180;es\u1197\u119C\u3486et\u0100;e\u1197\u348D\xF1\u119D\u0180;es\u11A8\u11AD\u3496et\u0100;e\u11A8\u349D\xF1\u11AE\u0180;af\u117B\u34A6\u05B0r\u0165\u34AB\u05B1\xBB\u117Car\xF2\u1148\u0200cemt\u34B9\u34BE\u34C2\u34C5r;\uC000\u{1D4C8}tm\xEE\xF1i\xEC\u3415ar\xE6\u11BE\u0100ar\u34CE\u34D5r\u0100;f\u34D4\u17BF\u6606\u0100an\u34DA\u34EDight\u0100ep\u34E3\u34EApsilo\xEE\u1EE0h\xE9\u2EAFs\xBB\u2852\u0280bcmnp\u34FB\u355E\u1209\u358B\u358E\u0480;Edemnprs\u350E\u350F\u3511\u3515\u351E\u3523\u352C\u3531\u3536\u6282;\u6AC5ot;\u6ABD\u0100;d\u11DA\u351Aot;\u6AC3ult;\u6AC1\u0100Ee\u3528\u352A;\u6ACB;\u628Alus;\u6ABFarr;\u6979\u0180eiu\u353D\u3552\u3555t\u0180;en\u350E\u3545\u354Bq\u0100;q\u11DA\u350Feq\u0100;q\u352B\u3528m;\u6AC7\u0100bp\u355A\u355C;\u6AD5;\u6AD3c\u0300;acens\u11ED\u356C\u3572\u3579\u357B\u3326ppro\xF8\u32FAurlye\xF1\u11FE\xF1\u11F3\u0180aes\u3582\u3588\u331Bppro\xF8\u331Aq\xF1\u3317g;\u666A\u0680123;Edehlmnps\u35A9\u35AC\u35AF\u121C\u35B2\u35B4\u35C0\u35C9\u35D5\u35DA\u35DF\u35E8\u35ED\u803B\xB9\u40B9\u803B\xB2\u40B2\u803B\xB3\u40B3;\u6AC6\u0100os\u35B9\u35BCt;\u6ABEub;\u6AD8\u0100;d\u1222\u35C5ot;\u6AC4s\u0100ou\u35CF\u35D2l;\u67C9b;\u6AD7arr;\u697Bult;\u6AC2\u0100Ee\u35E4\u35E6;\u6ACC;\u628Blus;\u6AC0\u0180eiu\u35F4\u3609\u360Ct\u0180;en\u121C\u35FC\u3602q\u0100;q\u1222\u35B2eq\u0100;q\u35E7\u35E4m;\u6AC8\u0100bp\u3611\u3613;\u6AD4;\u6AD6\u0180Aan\u361C\u3620\u362Drr;\u61D9r\u0100hr\u3626\u3628\xEB\u222E\u0100;o\u0A2B\u0A29war;\u692Alig\u803B\xDF\u40DF\u0BE1\u3651\u365D\u3660\u12CE\u3673\u3679\0\u367E\u36C2\0\0\0\0\0\u36DB\u3703\0\u3709\u376C\0\0\0\u3787\u0272\u3656\0\0\u365Bget;\u6316;\u43C4r\xEB\u0E5F\u0180aey\u3666\u366B\u3670ron;\u4165dil;\u4163;\u4442lrec;\u6315r;\uC000\u{1D531}\u0200eiko\u3686\u369D\u36B5\u36BC\u01F2\u368B\0\u3691e\u01004f\u1284\u1281a\u0180;sv\u3698\u3699\u369B\u43B8ym;\u43D1\u0100cn\u36A2\u36B2k\u0100as\u36A8\u36AEppro\xF8\u12C1im\xBB\u12ACs\xF0\u129E\u0100as\u36BA\u36AE\xF0\u12C1rn\u803B\xFE\u40FE\u01EC\u031F\u36C6\u22E7es\u8180\xD7;bd\u36CF\u36D0\u36D8\u40D7\u0100;a\u190F\u36D5r;\u6A31;\u6A30\u0180eps\u36E1\u36E3\u3700\xE1\u2A4D\u0200;bcf\u0486\u36EC\u36F0\u36F4ot;\u6336ir;\u6AF1\u0100;o\u36F9\u36FC\uC000\u{1D565}rk;\u6ADA\xE1\u3362rime;\u6034\u0180aip\u370F\u3712\u3764d\xE5\u1248\u0380adempst\u3721\u374D\u3740\u3751\u3757\u375C\u375Fngle\u0280;dlqr\u3730\u3731\u3736\u3740\u3742\u65B5own\xBB\u1DBBeft\u0100;e\u2800\u373E\xF1\u092E;\u625Cight\u0100;e\u32AA\u374B\xF1\u105Aot;\u65ECinus;\u6A3Alus;\u6A39b;\u69CDime;\u6A3Bezium;\u63E2\u0180cht\u3772\u377D\u3781\u0100ry\u3777\u377B;\uC000\u{1D4C9};\u4446cy;\u445Brok;\u4167\u0100io\u378B\u378Ex\xF4\u1777head\u0100lr\u3797\u37A0eftarro\xF7\u084Fightarrow\xBB\u0F5D\u0900AHabcdfghlmoprstuw\u37D0\u37D3\u37D7\u37E4\u37F0\u37FC\u380E\u381C\u3823\u3834\u3851\u385D\u386B\u38A9\u38CC\u38D2\u38EA\u38F6r\xF2\u03EDar;\u6963\u0100cr\u37DC\u37E2ute\u803B\xFA\u40FA\xF2\u1150r\u01E3\u37EA\0\u37EDy;\u445Eve;\u416D\u0100iy\u37F5\u37FArc\u803B\xFB\u40FB;\u4443\u0180abh\u3803\u3806\u380Br\xF2\u13ADlac;\u4171a\xF2\u13C3\u0100ir\u3813\u3818sht;\u697E;\uC000\u{1D532}rave\u803B\xF9\u40F9\u0161\u3827\u3831r\u0100lr\u382C\u382E\xBB\u0957\xBB\u1083lk;\u6580\u0100ct\u3839\u384D\u026F\u383F\0\0\u384Arn\u0100;e\u3845\u3846\u631Cr\xBB\u3846op;\u630Fri;\u65F8\u0100al\u3856\u385Acr;\u416B\u80BB\xA8\u0349\u0100gp\u3862\u3866on;\u4173f;\uC000\u{1D566}\u0300adhlsu\u114B\u3878\u387D\u1372\u3891\u38A0own\xE1\u13B3arpoon\u0100lr\u3888\u388Cef\xF4\u382Digh\xF4\u382Fi\u0180;hl\u3899\u389A\u389C\u43C5\xBB\u13FAon\xBB\u389Aparrows;\u61C8\u0180cit\u38B0\u38C4\u38C8\u026F\u38B6\0\0\u38C1rn\u0100;e\u38BC\u38BD\u631Dr\xBB\u38BDop;\u630Eng;\u416Fri;\u65F9cr;\uC000\u{1D4CA}\u0180dir\u38D9\u38DD\u38E2ot;\u62F0lde;\u4169i\u0100;f\u3730\u38E8\xBB\u1813\u0100am\u38EF\u38F2r\xF2\u38A8l\u803B\xFC\u40FCangle;\u69A7\u0780ABDacdeflnoprsz\u391C\u391F\u3929\u392D\u39B5\u39B8\u39BD\u39DF\u39E4\u39E8\u39F3\u39F9\u39FD\u3A01\u3A20r\xF2\u03F7ar\u0100;v\u3926\u3927\u6AE8;\u6AE9as\xE8\u03E1\u0100nr\u3932\u3937grt;\u699C\u0380eknprst\u34E3\u3946\u394B\u3952\u395D\u3964\u3996app\xE1\u2415othin\xE7\u1E96\u0180hir\u34EB\u2EC8\u3959op\xF4\u2FB5\u0100;h\u13B7\u3962\xEF\u318D\u0100iu\u3969\u396Dgm\xE1\u33B3\u0100bp\u3972\u3984setneq\u0100;q\u397D\u3980\uC000\u228A\uFE00;\uC000\u2ACB\uFE00setneq\u0100;q\u398F\u3992\uC000\u228B\uFE00;\uC000\u2ACC\uFE00\u0100hr\u399B\u399Fet\xE1\u369Ciangle\u0100lr\u39AA\u39AFeft\xBB\u0925ight\xBB\u1051y;\u4432ash\xBB\u1036\u0180elr\u39C4\u39D2\u39D7\u0180;be\u2DEA\u39CB\u39CFar;\u62BBq;\u625Alip;\u62EE\u0100bt\u39DC\u1468a\xF2\u1469r;\uC000\u{1D533}tr\xE9\u39AEsu\u0100bp\u39EF\u39F1\xBB\u0D1C\xBB\u0D59pf;\uC000\u{1D567}ro\xF0\u0EFBtr\xE9\u39B4\u0100cu\u3A06\u3A0Br;\uC000\u{1D4CB}\u0100bp\u3A10\u3A18n\u0100Ee\u3980\u3A16\xBB\u397En\u0100Ee\u3992\u3A1E\xBB\u3990igzag;\u699A\u0380cefoprs\u3A36\u3A3B\u3A56\u3A5B\u3A54\u3A61\u3A6Airc;\u4175\u0100di\u3A40\u3A51\u0100bg\u3A45\u3A49ar;\u6A5Fe\u0100;q\u15FA\u3A4F;\u6259erp;\u6118r;\uC000\u{1D534}pf;\uC000\u{1D568}\u0100;e\u1479\u3A66at\xE8\u1479cr;\uC000\u{1D4CC}\u0AE3\u178E\u3A87\0\u3A8B\0\u3A90\u3A9B\0\0\u3A9D\u3AA8\u3AAB\u3AAF\0\0\u3AC3\u3ACE\0\u3AD8\u17DC\u17DFtr\xE9\u17D1r;\uC000\u{1D535}\u0100Aa\u3A94\u3A97r\xF2\u03C3r\xF2\u09F6;\u43BE\u0100Aa\u3AA1\u3AA4r\xF2\u03B8r\xF2\u09EBa\xF0\u2713is;\u62FB\u0180dpt\u17A4\u3AB5\u3ABE\u0100fl\u3ABA\u17A9;\uC000\u{1D569}im\xE5\u17B2\u0100Aa\u3AC7\u3ACAr\xF2\u03CEr\xF2\u0A01\u0100cq\u3AD2\u17B8r;\uC000\u{1D4CD}\u0100pt\u17D6\u3ADCr\xE9\u17D4\u0400acefiosu\u3AF0\u3AFD\u3B08\u3B0C\u3B11\u3B15\u3B1B\u3B21c\u0100uy\u3AF6\u3AFBte\u803B\xFD\u40FD;\u444F\u0100iy\u3B02\u3B06rc;\u4177;\u444Bn\u803B\xA5\u40A5r;\uC000\u{1D536}cy;\u4457pf;\uC000\u{1D56A}cr;\uC000\u{1D4CE}\u0100cm\u3B26\u3B29y;\u444El\u803B\xFF\u40FF\u0500acdefhiosw\u3B42\u3B48\u3B54\u3B58\u3B64\u3B69\u3B6D\u3B74\u3B7A\u3B80cute;\u417A\u0100ay\u3B4D\u3B52ron;\u417E;\u4437ot;\u417C\u0100et\u3B5D\u3B61tr\xE6\u155Fa;\u43B6r;\uC000\u{1D537}cy;\u4436grarr;\u61DDpf;\uC000\u{1D56B}cr;\uC000\u{1D4CF}\u0100jn\u3B85\u3B87;\u600Dj;\u600C'.split("").map((c) => c.charCodeAt(0)) +); + +// node_modules/entities/lib/esm/generated/decode-data-xml.js +var decode_data_xml_default = new Uint16Array( + // prettier-ignore + "\u0200aglq \x1B\u026D\0\0p;\u4026os;\u4027t;\u403Et;\u403Cuot;\u4022".split("").map((c) => c.charCodeAt(0)) +); + +// node_modules/entities/lib/esm/decode_codepoint.js +var _a; +var decodeMap = /* @__PURE__ */ new Map([ + [0, 65533], + // C1 Unicode control character reference replacements + [128, 8364], + [130, 8218], + [131, 402], + [132, 8222], + [133, 8230], + [134, 8224], + [135, 8225], + [136, 710], + [137, 8240], + [138, 352], + [139, 8249], + [140, 338], + [142, 381], + [145, 8216], + [146, 8217], + [147, 8220], + [148, 8221], + [149, 8226], + [150, 8211], + [151, 8212], + [152, 732], + [153, 8482], + [154, 353], + [155, 8250], + [156, 339], + [158, 382], + [159, 376] +]); +var fromCodePoint = ( + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition, node/no-unsupported-features/es-builtins + (_a = String.fromCodePoint) !== null && _a !== void 0 ? _a : function(codePoint) { + let output = ""; + if (codePoint > 65535) { + codePoint -= 65536; + output += String.fromCharCode(codePoint >>> 10 & 1023 | 55296); + codePoint = 56320 | codePoint & 1023; + } + output += String.fromCharCode(codePoint); + return output; + } +); +function replaceCodePoint(codePoint) { + var _a2; + if (codePoint >= 55296 && codePoint <= 57343 || codePoint > 1114111) { + return 65533; + } + return (_a2 = decodeMap.get(codePoint)) !== null && _a2 !== void 0 ? _a2 : codePoint; +} + +// node_modules/entities/lib/esm/decode.js +var CharCodes; +(function(CharCodes3) { + CharCodes3[CharCodes3["NUM"] = 35] = "NUM"; + CharCodes3[CharCodes3["SEMI"] = 59] = "SEMI"; + CharCodes3[CharCodes3["EQUALS"] = 61] = "EQUALS"; + CharCodes3[CharCodes3["ZERO"] = 48] = "ZERO"; + CharCodes3[CharCodes3["NINE"] = 57] = "NINE"; + CharCodes3[CharCodes3["LOWER_A"] = 97] = "LOWER_A"; + CharCodes3[CharCodes3["LOWER_F"] = 102] = "LOWER_F"; + CharCodes3[CharCodes3["LOWER_X"] = 120] = "LOWER_X"; + CharCodes3[CharCodes3["LOWER_Z"] = 122] = "LOWER_Z"; + CharCodes3[CharCodes3["UPPER_A"] = 65] = "UPPER_A"; + CharCodes3[CharCodes3["UPPER_F"] = 70] = "UPPER_F"; + CharCodes3[CharCodes3["UPPER_Z"] = 90] = "UPPER_Z"; +})(CharCodes || (CharCodes = {})); +var TO_LOWER_BIT = 32; +var BinTrieFlags; +(function(BinTrieFlags2) { + BinTrieFlags2[BinTrieFlags2["VALUE_LENGTH"] = 49152] = "VALUE_LENGTH"; + BinTrieFlags2[BinTrieFlags2["BRANCH_LENGTH"] = 16256] = "BRANCH_LENGTH"; + BinTrieFlags2[BinTrieFlags2["JUMP_TABLE"] = 127] = "JUMP_TABLE"; +})(BinTrieFlags || (BinTrieFlags = {})); +function isNumber(code) { + return code >= CharCodes.ZERO && code <= CharCodes.NINE; +} +function isHexadecimalCharacter(code) { + return code >= CharCodes.UPPER_A && code <= CharCodes.UPPER_F || code >= CharCodes.LOWER_A && code <= CharCodes.LOWER_F; +} +function isAsciiAlphaNumeric(code) { + return code >= CharCodes.UPPER_A && code <= CharCodes.UPPER_Z || code >= CharCodes.LOWER_A && code <= CharCodes.LOWER_Z || isNumber(code); +} +function isEntityInAttributeInvalidEnd(code) { + return code === CharCodes.EQUALS || isAsciiAlphaNumeric(code); +} +var EntityDecoderState; +(function(EntityDecoderState2) { + EntityDecoderState2[EntityDecoderState2["EntityStart"] = 0] = "EntityStart"; + EntityDecoderState2[EntityDecoderState2["NumericStart"] = 1] = "NumericStart"; + EntityDecoderState2[EntityDecoderState2["NumericDecimal"] = 2] = "NumericDecimal"; + EntityDecoderState2[EntityDecoderState2["NumericHex"] = 3] = "NumericHex"; + EntityDecoderState2[EntityDecoderState2["NamedEntity"] = 4] = "NamedEntity"; +})(EntityDecoderState || (EntityDecoderState = {})); +var DecodingMode; +(function(DecodingMode2) { + DecodingMode2[DecodingMode2["Legacy"] = 0] = "Legacy"; + DecodingMode2[DecodingMode2["Strict"] = 1] = "Strict"; + DecodingMode2[DecodingMode2["Attribute"] = 2] = "Attribute"; +})(DecodingMode || (DecodingMode = {})); +var EntityDecoder = class { + constructor(decodeTree, emitCodePoint, errors) { + this.decodeTree = decodeTree; + this.emitCodePoint = emitCodePoint; + this.errors = errors; + this.state = EntityDecoderState.EntityStart; + this.consumed = 1; + this.result = 0; + this.treeIndex = 0; + this.excess = 1; + this.decodeMode = DecodingMode.Strict; + } + /** Resets the instance to make it reusable. */ + startEntity(decodeMode) { + this.decodeMode = decodeMode; + this.state = EntityDecoderState.EntityStart; + this.result = 0; + this.treeIndex = 0; + this.excess = 1; + this.consumed = 1; + } + /** + * Write an entity to the decoder. This can be called multiple times with partial entities. + * If the entity is incomplete, the decoder will return -1. + * + * Mirrors the implementation of `getDecoder`, but with the ability to stop decoding if the + * entity is incomplete, and resume when the next string is written. + * + * @param string The string containing the entity (or a continuation of the entity). + * @param offset The offset at which the entity begins. Should be 0 if this is not the first call. + * @returns The number of characters that were consumed, or -1 if the entity is incomplete. + */ + write(str, offset) { + switch (this.state) { + case EntityDecoderState.EntityStart: { + if (str.charCodeAt(offset) === CharCodes.NUM) { + this.state = EntityDecoderState.NumericStart; + this.consumed += 1; + return this.stateNumericStart(str, offset + 1); + } + this.state = EntityDecoderState.NamedEntity; + return this.stateNamedEntity(str, offset); + } + case EntityDecoderState.NumericStart: { + return this.stateNumericStart(str, offset); + } + case EntityDecoderState.NumericDecimal: { + return this.stateNumericDecimal(str, offset); + } + case EntityDecoderState.NumericHex: { + return this.stateNumericHex(str, offset); + } + case EntityDecoderState.NamedEntity: { + return this.stateNamedEntity(str, offset); + } + } + } + /** + * Switches between the numeric decimal and hexadecimal states. + * + * Equivalent to the `Numeric character reference state` in the HTML spec. + * + * @param str The string containing the entity (or a continuation of the entity). + * @param offset The current offset. + * @returns The number of characters that were consumed, or -1 if the entity is incomplete. + */ + stateNumericStart(str, offset) { + if (offset >= str.length) { + return -1; + } + if ((str.charCodeAt(offset) | TO_LOWER_BIT) === CharCodes.LOWER_X) { + this.state = EntityDecoderState.NumericHex; + this.consumed += 1; + return this.stateNumericHex(str, offset + 1); + } + this.state = EntityDecoderState.NumericDecimal; + return this.stateNumericDecimal(str, offset); + } + addToNumericResult(str, start, end, base) { + if (start !== end) { + const digitCount = end - start; + this.result = this.result * Math.pow(base, digitCount) + parseInt(str.substr(start, digitCount), base); + this.consumed += digitCount; + } + } + /** + * Parses a hexadecimal numeric entity. + * + * Equivalent to the `Hexademical character reference state` in the HTML spec. + * + * @param str The string containing the entity (or a continuation of the entity). + * @param offset The current offset. + * @returns The number of characters that were consumed, or -1 if the entity is incomplete. + */ + stateNumericHex(str, offset) { + const startIdx = offset; + while (offset < str.length) { + const char = str.charCodeAt(offset); + if (isNumber(char) || isHexadecimalCharacter(char)) { + offset += 1; + } else { + this.addToNumericResult(str, startIdx, offset, 16); + return this.emitNumericEntity(char, 3); + } + } + this.addToNumericResult(str, startIdx, offset, 16); + return -1; + } + /** + * Parses a decimal numeric entity. + * + * Equivalent to the `Decimal character reference state` in the HTML spec. + * + * @param str The string containing the entity (or a continuation of the entity). + * @param offset The current offset. + * @returns The number of characters that were consumed, or -1 if the entity is incomplete. + */ + stateNumericDecimal(str, offset) { + const startIdx = offset; + while (offset < str.length) { + const char = str.charCodeAt(offset); + if (isNumber(char)) { + offset += 1; + } else { + this.addToNumericResult(str, startIdx, offset, 10); + return this.emitNumericEntity(char, 2); + } + } + this.addToNumericResult(str, startIdx, offset, 10); + return -1; + } + /** + * Validate and emit a numeric entity. + * + * Implements the logic from the `Hexademical character reference start + * state` and `Numeric character reference end state` in the HTML spec. + * + * @param lastCp The last code point of the entity. Used to see if the + * entity was terminated with a semicolon. + * @param expectedLength The minimum number of characters that should be + * consumed. Used to validate that at least one digit + * was consumed. + * @returns The number of characters that were consumed. + */ + emitNumericEntity(lastCp, expectedLength) { + var _a2; + if (this.consumed <= expectedLength) { + (_a2 = this.errors) === null || _a2 === void 0 ? void 0 : _a2.absenceOfDigitsInNumericCharacterReference(this.consumed); + return 0; + } + if (lastCp === CharCodes.SEMI) { + this.consumed += 1; + } else if (this.decodeMode === DecodingMode.Strict) { + return 0; + } + this.emitCodePoint(replaceCodePoint(this.result), this.consumed); + if (this.errors) { + if (lastCp !== CharCodes.SEMI) { + this.errors.missingSemicolonAfterCharacterReference(); + } + this.errors.validateNumericCharacterReference(this.result); + } + return this.consumed; + } + /** + * Parses a named entity. + * + * Equivalent to the `Named character reference state` in the HTML spec. + * + * @param str The string containing the entity (or a continuation of the entity). + * @param offset The current offset. + * @returns The number of characters that were consumed, or -1 if the entity is incomplete. + */ + stateNamedEntity(str, offset) { + const { decodeTree } = this; + let current = decodeTree[this.treeIndex]; + let valueLength = (current & BinTrieFlags.VALUE_LENGTH) >> 14; + for (; offset < str.length; offset++, this.excess++) { + const char = str.charCodeAt(offset); + this.treeIndex = determineBranch(decodeTree, current, this.treeIndex + Math.max(1, valueLength), char); + if (this.treeIndex < 0) { + return this.result === 0 || // If we are parsing an attribute + this.decodeMode === DecodingMode.Attribute && // We shouldn't have consumed any characters after the entity, + (valueLength === 0 || // And there should be no invalid characters. + isEntityInAttributeInvalidEnd(char)) ? 0 : this.emitNotTerminatedNamedEntity(); + } + current = decodeTree[this.treeIndex]; + valueLength = (current & BinTrieFlags.VALUE_LENGTH) >> 14; + if (valueLength !== 0) { + if (char === CharCodes.SEMI) { + return this.emitNamedEntityData(this.treeIndex, valueLength, this.consumed + this.excess); + } + if (this.decodeMode !== DecodingMode.Strict) { + this.result = this.treeIndex; + this.consumed += this.excess; + this.excess = 0; + } + } + } + return -1; + } + /** + * Emit a named entity that was not terminated with a semicolon. + * + * @returns The number of characters consumed. + */ + emitNotTerminatedNamedEntity() { + var _a2; + const { result, decodeTree } = this; + const valueLength = (decodeTree[result] & BinTrieFlags.VALUE_LENGTH) >> 14; + this.emitNamedEntityData(result, valueLength, this.consumed); + (_a2 = this.errors) === null || _a2 === void 0 ? void 0 : _a2.missingSemicolonAfterCharacterReference(); + return this.consumed; + } + /** + * Emit a named entity. + * + * @param result The index of the entity in the decode tree. + * @param valueLength The number of bytes in the entity. + * @param consumed The number of characters consumed. + * + * @returns The number of characters consumed. + */ + emitNamedEntityData(result, valueLength, consumed) { + const { decodeTree } = this; + this.emitCodePoint(valueLength === 1 ? decodeTree[result] & ~BinTrieFlags.VALUE_LENGTH : decodeTree[result + 1], consumed); + if (valueLength === 3) { + this.emitCodePoint(decodeTree[result + 2], consumed); + } + return consumed; + } + /** + * Signal to the parser that the end of the input was reached. + * + * Remaining data will be emitted and relevant errors will be produced. + * + * @returns The number of characters consumed. + */ + end() { + var _a2; + switch (this.state) { + case EntityDecoderState.NamedEntity: { + return this.result !== 0 && (this.decodeMode !== DecodingMode.Attribute || this.result === this.treeIndex) ? this.emitNotTerminatedNamedEntity() : 0; + } + // Otherwise, emit a numeric entity if we have one. + case EntityDecoderState.NumericDecimal: { + return this.emitNumericEntity(0, 2); + } + case EntityDecoderState.NumericHex: { + return this.emitNumericEntity(0, 3); + } + case EntityDecoderState.NumericStart: { + (_a2 = this.errors) === null || _a2 === void 0 ? void 0 : _a2.absenceOfDigitsInNumericCharacterReference(this.consumed); + return 0; + } + case EntityDecoderState.EntityStart: { + return 0; + } + } + } +}; +function getDecoder(decodeTree) { + let ret = ""; + const decoder = new EntityDecoder(decodeTree, (str) => ret += fromCodePoint(str)); + return function decodeWithTrie(str, decodeMode) { + let lastIndex = 0; + let offset = 0; + while ((offset = str.indexOf("&", offset)) >= 0) { + ret += str.slice(lastIndex, offset); + decoder.startEntity(decodeMode); + const len = decoder.write( + str, + // Skip the "&" + offset + 1 + ); + if (len < 0) { + lastIndex = offset + decoder.end(); + break; + } + lastIndex = offset + len; + offset = len === 0 ? lastIndex + 1 : lastIndex; + } + const result = ret + str.slice(lastIndex); + ret = ""; + return result; + }; +} +function determineBranch(decodeTree, current, nodeIdx, char) { + const branchCount = (current & BinTrieFlags.BRANCH_LENGTH) >> 7; + const jumpOffset = current & BinTrieFlags.JUMP_TABLE; + if (branchCount === 0) { + return jumpOffset !== 0 && char === jumpOffset ? nodeIdx : -1; + } + if (jumpOffset) { + const value = char - jumpOffset; + return value < 0 || value >= branchCount ? -1 : decodeTree[nodeIdx + value] - 1; + } + let lo = nodeIdx; + let hi = lo + branchCount - 1; + while (lo <= hi) { + const mid = lo + hi >>> 1; + const midVal = decodeTree[mid]; + if (midVal < char) { + lo = mid + 1; + } else if (midVal > char) { + hi = mid - 1; + } else { + return decodeTree[mid + branchCount]; + } + } + return -1; +} +var htmlDecoder = getDecoder(decode_data_html_default); +var xmlDecoder = getDecoder(decode_data_xml_default); + +// node_modules/htmlparser2/lib/esm/Tokenizer.js +var CharCodes2; +(function(CharCodes3) { + CharCodes3[CharCodes3["Tab"] = 9] = "Tab"; + CharCodes3[CharCodes3["NewLine"] = 10] = "NewLine"; + CharCodes3[CharCodes3["FormFeed"] = 12] = "FormFeed"; + CharCodes3[CharCodes3["CarriageReturn"] = 13] = "CarriageReturn"; + CharCodes3[CharCodes3["Space"] = 32] = "Space"; + CharCodes3[CharCodes3["ExclamationMark"] = 33] = "ExclamationMark"; + CharCodes3[CharCodes3["Number"] = 35] = "Number"; + CharCodes3[CharCodes3["Amp"] = 38] = "Amp"; + CharCodes3[CharCodes3["SingleQuote"] = 39] = "SingleQuote"; + CharCodes3[CharCodes3["DoubleQuote"] = 34] = "DoubleQuote"; + CharCodes3[CharCodes3["Dash"] = 45] = "Dash"; + CharCodes3[CharCodes3["Slash"] = 47] = "Slash"; + CharCodes3[CharCodes3["Zero"] = 48] = "Zero"; + CharCodes3[CharCodes3["Nine"] = 57] = "Nine"; + CharCodes3[CharCodes3["Semi"] = 59] = "Semi"; + CharCodes3[CharCodes3["Lt"] = 60] = "Lt"; + CharCodes3[CharCodes3["Eq"] = 61] = "Eq"; + CharCodes3[CharCodes3["Gt"] = 62] = "Gt"; + CharCodes3[CharCodes3["Questionmark"] = 63] = "Questionmark"; + CharCodes3[CharCodes3["UpperA"] = 65] = "UpperA"; + CharCodes3[CharCodes3["LowerA"] = 97] = "LowerA"; + CharCodes3[CharCodes3["UpperF"] = 70] = "UpperF"; + CharCodes3[CharCodes3["LowerF"] = 102] = "LowerF"; + CharCodes3[CharCodes3["UpperZ"] = 90] = "UpperZ"; + CharCodes3[CharCodes3["LowerZ"] = 122] = "LowerZ"; + CharCodes3[CharCodes3["LowerX"] = 120] = "LowerX"; + CharCodes3[CharCodes3["OpeningSquareBracket"] = 91] = "OpeningSquareBracket"; +})(CharCodes2 || (CharCodes2 = {})); +var State; +(function(State2) { + State2[State2["Text"] = 1] = "Text"; + State2[State2["BeforeTagName"] = 2] = "BeforeTagName"; + State2[State2["InTagName"] = 3] = "InTagName"; + State2[State2["InSelfClosingTag"] = 4] = "InSelfClosingTag"; + State2[State2["BeforeClosingTagName"] = 5] = "BeforeClosingTagName"; + State2[State2["InClosingTagName"] = 6] = "InClosingTagName"; + State2[State2["AfterClosingTagName"] = 7] = "AfterClosingTagName"; + State2[State2["BeforeAttributeName"] = 8] = "BeforeAttributeName"; + State2[State2["InAttributeName"] = 9] = "InAttributeName"; + State2[State2["AfterAttributeName"] = 10] = "AfterAttributeName"; + State2[State2["BeforeAttributeValue"] = 11] = "BeforeAttributeValue"; + State2[State2["InAttributeValueDq"] = 12] = "InAttributeValueDq"; + State2[State2["InAttributeValueSq"] = 13] = "InAttributeValueSq"; + State2[State2["InAttributeValueNq"] = 14] = "InAttributeValueNq"; + State2[State2["BeforeDeclaration"] = 15] = "BeforeDeclaration"; + State2[State2["InDeclaration"] = 16] = "InDeclaration"; + State2[State2["InProcessingInstruction"] = 17] = "InProcessingInstruction"; + State2[State2["BeforeComment"] = 18] = "BeforeComment"; + State2[State2["CDATASequence"] = 19] = "CDATASequence"; + State2[State2["InSpecialComment"] = 20] = "InSpecialComment"; + State2[State2["InCommentLike"] = 21] = "InCommentLike"; + State2[State2["BeforeSpecialS"] = 22] = "BeforeSpecialS"; + State2[State2["BeforeSpecialT"] = 23] = "BeforeSpecialT"; + State2[State2["SpecialStartSequence"] = 24] = "SpecialStartSequence"; + State2[State2["InSpecialTag"] = 25] = "InSpecialTag"; + State2[State2["InEntity"] = 26] = "InEntity"; +})(State || (State = {})); +function isWhitespace(c) { + return c === CharCodes2.Space || c === CharCodes2.NewLine || c === CharCodes2.Tab || c === CharCodes2.FormFeed || c === CharCodes2.CarriageReturn; +} +function isEndOfTagSection(c) { + return c === CharCodes2.Slash || c === CharCodes2.Gt || isWhitespace(c); +} +function isASCIIAlpha(c) { + return c >= CharCodes2.LowerA && c <= CharCodes2.LowerZ || c >= CharCodes2.UpperA && c <= CharCodes2.UpperZ; +} +var QuoteType; +(function(QuoteType2) { + QuoteType2[QuoteType2["NoValue"] = 0] = "NoValue"; + QuoteType2[QuoteType2["Unquoted"] = 1] = "Unquoted"; + QuoteType2[QuoteType2["Single"] = 2] = "Single"; + QuoteType2[QuoteType2["Double"] = 3] = "Double"; +})(QuoteType || (QuoteType = {})); +var Sequences = { + Cdata: new Uint8Array([67, 68, 65, 84, 65, 91]), + // CDATA[ + CdataEnd: new Uint8Array([93, 93, 62]), + // ]]> + CommentEnd: new Uint8Array([45, 45, 62]), + // `-->` + ScriptEnd: new Uint8Array([60, 47, 115, 99, 114, 105, 112, 116]), + // `<\/script` + StyleEnd: new Uint8Array([60, 47, 115, 116, 121, 108, 101]), + // ` this.emitCodePoint(cp, consumed)); + } + reset() { + this.state = State.Text; + this.buffer = ""; + this.sectionStart = 0; + this.index = 0; + this.baseState = State.Text; + this.currentSequence = void 0; + this.running = true; + this.offset = 0; + } + write(chunk) { + this.offset += this.buffer.length; + this.buffer = chunk; + this.parse(); + } + end() { + if (this.running) + this.finish(); + } + pause() { + this.running = false; + } + resume() { + this.running = true; + if (this.index < this.buffer.length + this.offset) { + this.parse(); + } + } + stateText(c) { + if (c === CharCodes2.Lt || !this.decodeEntities && this.fastForwardTo(CharCodes2.Lt)) { + if (this.index > this.sectionStart) { + this.cbs.ontext(this.sectionStart, this.index); + } + this.state = State.BeforeTagName; + this.sectionStart = this.index; + } else if (this.decodeEntities && c === CharCodes2.Amp) { + this.startEntity(); + } + } + stateSpecialStartSequence(c) { + const isEnd = this.sequenceIndex === this.currentSequence.length; + const isMatch = isEnd ? ( + // If we are at the end of the sequence, make sure the tag name has ended + isEndOfTagSection(c) + ) : ( + // Otherwise, do a case-insensitive comparison + (c | 32) === this.currentSequence[this.sequenceIndex] + ); + if (!isMatch) { + this.isSpecial = false; + } else if (!isEnd) { + this.sequenceIndex++; + return; + } + this.sequenceIndex = 0; + this.state = State.InTagName; + this.stateInTagName(c); + } + /** Look for an end tag. For tags, also decode entities. */ + stateInSpecialTag(c) { + if (this.sequenceIndex === this.currentSequence.length) { + if (c === CharCodes2.Gt || isWhitespace(c)) { + const endOfText = this.index - this.currentSequence.length; + if (this.sectionStart < endOfText) { + const actualIndex = this.index; + this.index = endOfText; + this.cbs.ontext(this.sectionStart, endOfText); + this.index = actualIndex; + } + this.isSpecial = false; + this.sectionStart = endOfText + 2; + this.stateInClosingTagName(c); + return; + } + this.sequenceIndex = 0; + } + if ((c | 32) === this.currentSequence[this.sequenceIndex]) { + this.sequenceIndex += 1; + } else if (this.sequenceIndex === 0) { + if (this.currentSequence === Sequences.TitleEnd) { + if (this.decodeEntities && c === CharCodes2.Amp) { + this.startEntity(); + } + } else if (this.fastForwardTo(CharCodes2.Lt)) { + this.sequenceIndex = 1; + } + } else { + this.sequenceIndex = Number(c === CharCodes2.Lt); + } + } + stateCDATASequence(c) { + if (c === Sequences.Cdata[this.sequenceIndex]) { + if (++this.sequenceIndex === Sequences.Cdata.length) { + this.state = State.InCommentLike; + this.currentSequence = Sequences.CdataEnd; + this.sequenceIndex = 0; + this.sectionStart = this.index + 1; + } + } else { + this.sequenceIndex = 0; + this.state = State.InDeclaration; + this.stateInDeclaration(c); + } + } + /** + * When we wait for one specific character, we can speed things up + * by skipping through the buffer until we find it. + * + * @returns Whether the character was found. + */ + fastForwardTo(c) { + while (++this.index < this.buffer.length + this.offset) { + if (this.buffer.charCodeAt(this.index - this.offset) === c) { + return true; + } + } + this.index = this.buffer.length + this.offset - 1; + return false; + } + /** + * Comments and CDATA end with `-->` and `]]>`. + * + * Their common qualities are: + * - Their end sequences have a distinct character they start with. + * - That character is then repeated, so we have to check multiple repeats. + * - All characters but the start character of the sequence can be skipped. + */ + stateInCommentLike(c) { + if (c === this.currentSequence[this.sequenceIndex]) { + if (++this.sequenceIndex === this.currentSequence.length) { + if (this.currentSequence === Sequences.CdataEnd) { + this.cbs.oncdata(this.sectionStart, this.index, 2); + } else { + this.cbs.oncomment(this.sectionStart, this.index, 2); + } + this.sequenceIndex = 0; + this.sectionStart = this.index + 1; + this.state = State.Text; + } + } else if (this.sequenceIndex === 0) { + if (this.fastForwardTo(this.currentSequence[0])) { + this.sequenceIndex = 1; + } + } else if (c !== this.currentSequence[this.sequenceIndex - 1]) { + this.sequenceIndex = 0; + } + } + /** + * HTML only allows ASCII alpha characters (a-z and A-Z) at the beginning of a tag name. + * + * XML allows a lot more characters here (@see https://www.w3.org/TR/REC-xml/#NT-NameStartChar). + * We allow anything that wouldn't end the tag. + */ + isTagStartChar(c) { + return this.xmlMode ? !isEndOfTagSection(c) : isASCIIAlpha(c); + } + startSpecial(sequence, offset) { + this.isSpecial = true; + this.currentSequence = sequence; + this.sequenceIndex = offset; + this.state = State.SpecialStartSequence; + } + stateBeforeTagName(c) { + if (c === CharCodes2.ExclamationMark) { + this.state = State.BeforeDeclaration; + this.sectionStart = this.index + 1; + } else if (c === CharCodes2.Questionmark) { + this.state = State.InProcessingInstruction; + this.sectionStart = this.index + 1; + } else if (this.isTagStartChar(c)) { + const lower = c | 32; + this.sectionStart = this.index; + if (this.xmlMode) { + this.state = State.InTagName; + } else if (lower === Sequences.ScriptEnd[2]) { + this.state = State.BeforeSpecialS; + } else if (lower === Sequences.TitleEnd[2]) { + this.state = State.BeforeSpecialT; + } else { + this.state = State.InTagName; + } + } else if (c === CharCodes2.Slash) { + this.state = State.BeforeClosingTagName; + } else { + this.state = State.Text; + this.stateText(c); + } + } + stateInTagName(c) { + if (isEndOfTagSection(c)) { + this.cbs.onopentagname(this.sectionStart, this.index); + this.sectionStart = -1; + this.state = State.BeforeAttributeName; + this.stateBeforeAttributeName(c); + } + } + stateBeforeClosingTagName(c) { + if (isWhitespace(c)) { + } else if (c === CharCodes2.Gt) { + this.state = State.Text; + } else { + this.state = this.isTagStartChar(c) ? State.InClosingTagName : State.InSpecialComment; + this.sectionStart = this.index; + } + } + stateInClosingTagName(c) { + if (c === CharCodes2.Gt || isWhitespace(c)) { + this.cbs.onclosetag(this.sectionStart, this.index); + this.sectionStart = -1; + this.state = State.AfterClosingTagName; + this.stateAfterClosingTagName(c); + } + } + stateAfterClosingTagName(c) { + if (c === CharCodes2.Gt || this.fastForwardTo(CharCodes2.Gt)) { + this.state = State.Text; + this.sectionStart = this.index + 1; + } + } + stateBeforeAttributeName(c) { + if (c === CharCodes2.Gt) { + this.cbs.onopentagend(this.index); + if (this.isSpecial) { + this.state = State.InSpecialTag; + this.sequenceIndex = 0; + } else { + this.state = State.Text; + } + this.sectionStart = this.index + 1; + } else if (c === CharCodes2.Slash) { + this.state = State.InSelfClosingTag; + } else if (!isWhitespace(c)) { + this.state = State.InAttributeName; + this.sectionStart = this.index; + } + } + stateInSelfClosingTag(c) { + if (c === CharCodes2.Gt) { + this.cbs.onselfclosingtag(this.index); + this.state = State.Text; + this.sectionStart = this.index + 1; + this.isSpecial = false; + } else if (!isWhitespace(c)) { + this.state = State.BeforeAttributeName; + this.stateBeforeAttributeName(c); + } + } + stateInAttributeName(c) { + if (c === CharCodes2.Eq || isEndOfTagSection(c)) { + this.cbs.onattribname(this.sectionStart, this.index); + this.sectionStart = this.index; + this.state = State.AfterAttributeName; + this.stateAfterAttributeName(c); + } + } + stateAfterAttributeName(c) { + if (c === CharCodes2.Eq) { + this.state = State.BeforeAttributeValue; + } else if (c === CharCodes2.Slash || c === CharCodes2.Gt) { + this.cbs.onattribend(QuoteType.NoValue, this.sectionStart); + this.sectionStart = -1; + this.state = State.BeforeAttributeName; + this.stateBeforeAttributeName(c); + } else if (!isWhitespace(c)) { + this.cbs.onattribend(QuoteType.NoValue, this.sectionStart); + this.state = State.InAttributeName; + this.sectionStart = this.index; + } + } + stateBeforeAttributeValue(c) { + if (c === CharCodes2.DoubleQuote) { + this.state = State.InAttributeValueDq; + this.sectionStart = this.index + 1; + } else if (c === CharCodes2.SingleQuote) { + this.state = State.InAttributeValueSq; + this.sectionStart = this.index + 1; + } else if (!isWhitespace(c)) { + this.sectionStart = this.index; + this.state = State.InAttributeValueNq; + this.stateInAttributeValueNoQuotes(c); + } + } + handleInAttributeValue(c, quote) { + if (c === quote || !this.decodeEntities && this.fastForwardTo(quote)) { + this.cbs.onattribdata(this.sectionStart, this.index); + this.sectionStart = -1; + this.cbs.onattribend(quote === CharCodes2.DoubleQuote ? QuoteType.Double : QuoteType.Single, this.index + 1); + this.state = State.BeforeAttributeName; + } else if (this.decodeEntities && c === CharCodes2.Amp) { + this.startEntity(); + } + } + stateInAttributeValueDoubleQuotes(c) { + this.handleInAttributeValue(c, CharCodes2.DoubleQuote); + } + stateInAttributeValueSingleQuotes(c) { + this.handleInAttributeValue(c, CharCodes2.SingleQuote); + } + stateInAttributeValueNoQuotes(c) { + if (isWhitespace(c) || c === CharCodes2.Gt) { + this.cbs.onattribdata(this.sectionStart, this.index); + this.sectionStart = -1; + this.cbs.onattribend(QuoteType.Unquoted, this.index); + this.state = State.BeforeAttributeName; + this.stateBeforeAttributeName(c); + } else if (this.decodeEntities && c === CharCodes2.Amp) { + this.startEntity(); + } + } + stateBeforeDeclaration(c) { + if (c === CharCodes2.OpeningSquareBracket) { + this.state = State.CDATASequence; + this.sequenceIndex = 0; + } else { + this.state = c === CharCodes2.Dash ? State.BeforeComment : State.InDeclaration; + } + } + stateInDeclaration(c) { + if (c === CharCodes2.Gt || this.fastForwardTo(CharCodes2.Gt)) { + this.cbs.ondeclaration(this.sectionStart, this.index); + this.state = State.Text; + this.sectionStart = this.index + 1; + } + } + stateInProcessingInstruction(c) { + if (c === CharCodes2.Gt || this.fastForwardTo(CharCodes2.Gt)) { + this.cbs.onprocessinginstruction(this.sectionStart, this.index); + this.state = State.Text; + this.sectionStart = this.index + 1; + } + } + stateBeforeComment(c) { + if (c === CharCodes2.Dash) { + this.state = State.InCommentLike; + this.currentSequence = Sequences.CommentEnd; + this.sequenceIndex = 2; + this.sectionStart = this.index + 1; + } else { + this.state = State.InDeclaration; + } + } + stateInSpecialComment(c) { + if (c === CharCodes2.Gt || this.fastForwardTo(CharCodes2.Gt)) { + this.cbs.oncomment(this.sectionStart, this.index, 0); + this.state = State.Text; + this.sectionStart = this.index + 1; + } + } + stateBeforeSpecialS(c) { + const lower = c | 32; + if (lower === Sequences.ScriptEnd[3]) { + this.startSpecial(Sequences.ScriptEnd, 4); + } else if (lower === Sequences.StyleEnd[3]) { + this.startSpecial(Sequences.StyleEnd, 4); + } else { + this.state = State.InTagName; + this.stateInTagName(c); + } + } + stateBeforeSpecialT(c) { + const lower = c | 32; + if (lower === Sequences.TitleEnd[3]) { + this.startSpecial(Sequences.TitleEnd, 4); + } else if (lower === Sequences.TextareaEnd[3]) { + this.startSpecial(Sequences.TextareaEnd, 4); + } else { + this.state = State.InTagName; + this.stateInTagName(c); + } + } + startEntity() { + this.baseState = this.state; + this.state = State.InEntity; + this.entityStart = this.index; + this.entityDecoder.startEntity(this.xmlMode ? DecodingMode.Strict : this.baseState === State.Text || this.baseState === State.InSpecialTag ? DecodingMode.Legacy : DecodingMode.Attribute); + } + stateInEntity() { + const length = this.entityDecoder.write(this.buffer, this.index - this.offset); + if (length >= 0) { + this.state = this.baseState; + if (length === 0) { + this.index = this.entityStart; + } + } else { + this.index = this.offset + this.buffer.length - 1; + } + } + /** + * Remove data that has already been consumed from the buffer. + */ + cleanup() { + if (this.running && this.sectionStart !== this.index) { + if (this.state === State.Text || this.state === State.InSpecialTag && this.sequenceIndex === 0) { + this.cbs.ontext(this.sectionStart, this.index); + this.sectionStart = this.index; + } else if (this.state === State.InAttributeValueDq || this.state === State.InAttributeValueSq || this.state === State.InAttributeValueNq) { + this.cbs.onattribdata(this.sectionStart, this.index); + this.sectionStart = this.index; + } + } + } + shouldContinue() { + return this.index < this.buffer.length + this.offset && this.running; + } + /** + * Iterates through the buffer, calling the function corresponding to the current state. + * + * States that are more likely to be hit are higher up, as a performance improvement. + */ + parse() { + while (this.shouldContinue()) { + const c = this.buffer.charCodeAt(this.index - this.offset); + switch (this.state) { + case State.Text: { + this.stateText(c); + break; + } + case State.SpecialStartSequence: { + this.stateSpecialStartSequence(c); + break; + } + case State.InSpecialTag: { + this.stateInSpecialTag(c); + break; + } + case State.CDATASequence: { + this.stateCDATASequence(c); + break; + } + case State.InAttributeValueDq: { + this.stateInAttributeValueDoubleQuotes(c); + break; + } + case State.InAttributeName: { + this.stateInAttributeName(c); + break; + } + case State.InCommentLike: { + this.stateInCommentLike(c); + break; + } + case State.InSpecialComment: { + this.stateInSpecialComment(c); + break; + } + case State.BeforeAttributeName: { + this.stateBeforeAttributeName(c); + break; + } + case State.InTagName: { + this.stateInTagName(c); + break; + } + case State.InClosingTagName: { + this.stateInClosingTagName(c); + break; + } + case State.BeforeTagName: { + this.stateBeforeTagName(c); + break; + } + case State.AfterAttributeName: { + this.stateAfterAttributeName(c); + break; + } + case State.InAttributeValueSq: { + this.stateInAttributeValueSingleQuotes(c); + break; + } + case State.BeforeAttributeValue: { + this.stateBeforeAttributeValue(c); + break; + } + case State.BeforeClosingTagName: { + this.stateBeforeClosingTagName(c); + break; + } + case State.AfterClosingTagName: { + this.stateAfterClosingTagName(c); + break; + } + case State.BeforeSpecialS: { + this.stateBeforeSpecialS(c); + break; + } + case State.BeforeSpecialT: { + this.stateBeforeSpecialT(c); + break; + } + case State.InAttributeValueNq: { + this.stateInAttributeValueNoQuotes(c); + break; + } + case State.InSelfClosingTag: { + this.stateInSelfClosingTag(c); + break; + } + case State.InDeclaration: { + this.stateInDeclaration(c); + break; + } + case State.BeforeDeclaration: { + this.stateBeforeDeclaration(c); + break; + } + case State.BeforeComment: { + this.stateBeforeComment(c); + break; + } + case State.InProcessingInstruction: { + this.stateInProcessingInstruction(c); + break; + } + case State.InEntity: { + this.stateInEntity(); + break; + } + } + this.index++; + } + this.cleanup(); + } + finish() { + if (this.state === State.InEntity) { + this.entityDecoder.end(); + this.state = this.baseState; + } + this.handleTrailingData(); + this.cbs.onend(); + } + /** Handle any trailing data. */ + handleTrailingData() { + const endIndex = this.buffer.length + this.offset; + if (this.sectionStart >= endIndex) { + return; + } + if (this.state === State.InCommentLike) { + if (this.currentSequence === Sequences.CdataEnd) { + this.cbs.oncdata(this.sectionStart, endIndex, 0); + } else { + this.cbs.oncomment(this.sectionStart, endIndex, 0); + } + } else if (this.state === State.InTagName || this.state === State.BeforeAttributeName || this.state === State.BeforeAttributeValue || this.state === State.AfterAttributeName || this.state === State.InAttributeName || this.state === State.InAttributeValueSq || this.state === State.InAttributeValueDq || this.state === State.InAttributeValueNq || this.state === State.InClosingTagName) { + } else { + this.cbs.ontext(this.sectionStart, endIndex); + } + } + emitCodePoint(cp, consumed) { + if (this.baseState !== State.Text && this.baseState !== State.InSpecialTag) { + if (this.sectionStart < this.entityStart) { + this.cbs.onattribdata(this.sectionStart, this.entityStart); + } + this.sectionStart = this.entityStart + consumed; + this.index = this.sectionStart - 1; + this.cbs.onattribentity(cp); + } else { + if (this.sectionStart < this.entityStart) { + this.cbs.ontext(this.sectionStart, this.entityStart); + } + this.sectionStart = this.entityStart + consumed; + this.index = this.sectionStart - 1; + this.cbs.ontextentity(cp, this.sectionStart); + } + } +}; + +// node_modules/htmlparser2/lib/esm/Parser.js +var formTags = /* @__PURE__ */ new Set([ + "input", + "option", + "optgroup", + "select", + "button", + "datalist", + "textarea" +]); +var pTag = /* @__PURE__ */ new Set(["p"]); +var tableSectionTags = /* @__PURE__ */ new Set(["thead", "tbody"]); +var ddtTags = /* @__PURE__ */ new Set(["dd", "dt"]); +var rtpTags = /* @__PURE__ */ new Set(["rt", "rp"]); +var openImpliesClose = /* @__PURE__ */ new Map([ + ["tr", /* @__PURE__ */ new Set(["tr", "th", "td"])], + ["th", /* @__PURE__ */ new Set(["th"])], + ["td", /* @__PURE__ */ new Set(["thead", "th", "td"])], + ["body", /* @__PURE__ */ new Set(["head", "link", "script"])], + ["li", /* @__PURE__ */ new Set(["li"])], + ["p", pTag], + ["h1", pTag], + ["h2", pTag], + ["h3", pTag], + ["h4", pTag], + ["h5", pTag], + ["h6", pTag], + ["select", formTags], + ["input", formTags], + ["output", formTags], + ["button", formTags], + ["datalist", formTags], + ["textarea", formTags], + ["option", /* @__PURE__ */ new Set(["option"])], + ["optgroup", /* @__PURE__ */ new Set(["optgroup", "option"])], + ["dd", ddtTags], + ["dt", ddtTags], + ["address", pTag], + ["article", pTag], + ["aside", pTag], + ["blockquote", pTag], + ["details", pTag], + ["div", pTag], + ["dl", pTag], + ["fieldset", pTag], + ["figcaption", pTag], + ["figure", pTag], + ["footer", pTag], + ["form", pTag], + ["header", pTag], + ["hr", pTag], + ["main", pTag], + ["nav", pTag], + ["ol", pTag], + ["pre", pTag], + ["section", pTag], + ["table", pTag], + ["ul", pTag], + ["rt", rtpTags], + ["rp", rtpTags], + ["tbody", tableSectionTags], + ["tfoot", tableSectionTags] +]); +var voidElements = /* @__PURE__ */ new Set([ + "area", + "base", + "basefont", + "br", + "col", + "command", + "embed", + "frame", + "hr", + "img", + "input", + "isindex", + "keygen", + "link", + "meta", + "param", + "source", + "track", + "wbr" +]); +var foreignContextElements = /* @__PURE__ */ new Set(["math", "svg"]); +var htmlIntegrationElements = /* @__PURE__ */ new Set([ + "mi", + "mo", + "mn", + "ms", + "mtext", + "annotation-xml", + "foreignobject", + "desc", + "title" +]); +var reNameEnd = /\s|\//; +var Parser = class { + constructor(cbs, options = {}) { + var _a2, _b, _c, _d, _e, _f; + this.options = options; + this.startIndex = 0; + this.endIndex = 0; + this.openTagStart = 0; + this.tagname = ""; + this.attribname = ""; + this.attribvalue = ""; + this.attribs = null; + this.stack = []; + this.buffers = []; + this.bufferOffset = 0; + this.writeIndex = 0; + this.ended = false; + this.cbs = cbs !== null && cbs !== void 0 ? cbs : {}; + this.htmlMode = !this.options.xmlMode; + this.lowerCaseTagNames = (_a2 = options.lowerCaseTags) !== null && _a2 !== void 0 ? _a2 : this.htmlMode; + this.lowerCaseAttributeNames = (_b = options.lowerCaseAttributeNames) !== null && _b !== void 0 ? _b : this.htmlMode; + this.recognizeSelfClosing = (_c = options.recognizeSelfClosing) !== null && _c !== void 0 ? _c : !this.htmlMode; + this.tokenizer = new ((_d = options.Tokenizer) !== null && _d !== void 0 ? _d : Tokenizer)(this.options, this); + this.foreignContext = [!this.htmlMode]; + (_f = (_e = this.cbs).onparserinit) === null || _f === void 0 ? void 0 : _f.call(_e, this); + } + // Tokenizer event handlers + /** @internal */ + ontext(start, endIndex) { + var _a2, _b; + const data = this.getSlice(start, endIndex); + this.endIndex = endIndex - 1; + (_b = (_a2 = this.cbs).ontext) === null || _b === void 0 ? void 0 : _b.call(_a2, data); + this.startIndex = endIndex; + } + /** @internal */ + ontextentity(cp, endIndex) { + var _a2, _b; + this.endIndex = endIndex - 1; + (_b = (_a2 = this.cbs).ontext) === null || _b === void 0 ? void 0 : _b.call(_a2, fromCodePoint(cp)); + this.startIndex = endIndex; + } + /** + * Checks if the current tag is a void element. Override this if you want + * to specify your own additional void elements. + */ + isVoidElement(name) { + return this.htmlMode && voidElements.has(name); + } + /** @internal */ + onopentagname(start, endIndex) { + this.endIndex = endIndex; + let name = this.getSlice(start, endIndex); + if (this.lowerCaseTagNames) { + name = name.toLowerCase(); + } + this.emitOpenTag(name); + } + emitOpenTag(name) { + var _a2, _b, _c, _d; + this.openTagStart = this.startIndex; + this.tagname = name; + const impliesClose = this.htmlMode && openImpliesClose.get(name); + if (impliesClose) { + while (this.stack.length > 0 && impliesClose.has(this.stack[0])) { + const element = this.stack.shift(); + (_b = (_a2 = this.cbs).onclosetag) === null || _b === void 0 ? void 0 : _b.call(_a2, element, true); + } + } + if (!this.isVoidElement(name)) { + this.stack.unshift(name); + if (this.htmlMode) { + if (foreignContextElements.has(name)) { + this.foreignContext.unshift(true); + } else if (htmlIntegrationElements.has(name)) { + this.foreignContext.unshift(false); + } + } + } + (_d = (_c = this.cbs).onopentagname) === null || _d === void 0 ? void 0 : _d.call(_c, name); + if (this.cbs.onopentag) + this.attribs = {}; + } + endOpenTag(isImplied) { + var _a2, _b; + this.startIndex = this.openTagStart; + if (this.attribs) { + (_b = (_a2 = this.cbs).onopentag) === null || _b === void 0 ? void 0 : _b.call(_a2, this.tagname, this.attribs, isImplied); + this.attribs = null; + } + if (this.cbs.onclosetag && this.isVoidElement(this.tagname)) { + this.cbs.onclosetag(this.tagname, true); + } + this.tagname = ""; + } + /** @internal */ + onopentagend(endIndex) { + this.endIndex = endIndex; + this.endOpenTag(false); + this.startIndex = endIndex + 1; + } + /** @internal */ + onclosetag(start, endIndex) { + var _a2, _b, _c, _d, _e, _f, _g, _h; + this.endIndex = endIndex; + let name = this.getSlice(start, endIndex); + if (this.lowerCaseTagNames) { + name = name.toLowerCase(); + } + if (this.htmlMode && (foreignContextElements.has(name) || htmlIntegrationElements.has(name))) { + this.foreignContext.shift(); + } + if (!this.isVoidElement(name)) { + const pos = this.stack.indexOf(name); + if (pos !== -1) { + for (let index = 0; index <= pos; index++) { + const element = this.stack.shift(); + (_b = (_a2 = this.cbs).onclosetag) === null || _b === void 0 ? void 0 : _b.call(_a2, element, index !== pos); + } + } else if (this.htmlMode && name === "p") { + this.emitOpenTag("p"); + this.closeCurrentTag(true); + } + } else if (this.htmlMode && name === "br") { + (_d = (_c = this.cbs).onopentagname) === null || _d === void 0 ? void 0 : _d.call(_c, "br"); + (_f = (_e = this.cbs).onopentag) === null || _f === void 0 ? void 0 : _f.call(_e, "br", {}, true); + (_h = (_g = this.cbs).onclosetag) === null || _h === void 0 ? void 0 : _h.call(_g, "br", false); + } + this.startIndex = endIndex + 1; + } + /** @internal */ + onselfclosingtag(endIndex) { + this.endIndex = endIndex; + if (this.recognizeSelfClosing || this.foreignContext[0]) { + this.closeCurrentTag(false); + this.startIndex = endIndex + 1; + } else { + this.onopentagend(endIndex); + } + } + closeCurrentTag(isOpenImplied) { + var _a2, _b; + const name = this.tagname; + this.endOpenTag(isOpenImplied); + if (this.stack[0] === name) { + (_b = (_a2 = this.cbs).onclosetag) === null || _b === void 0 ? void 0 : _b.call(_a2, name, !isOpenImplied); + this.stack.shift(); + } + } + /** @internal */ + onattribname(start, endIndex) { + this.startIndex = start; + const name = this.getSlice(start, endIndex); + this.attribname = this.lowerCaseAttributeNames ? name.toLowerCase() : name; + } + /** @internal */ + onattribdata(start, endIndex) { + this.attribvalue += this.getSlice(start, endIndex); + } + /** @internal */ + onattribentity(cp) { + this.attribvalue += fromCodePoint(cp); + } + /** @internal */ + onattribend(quote, endIndex) { + var _a2, _b; + this.endIndex = endIndex; + (_b = (_a2 = this.cbs).onattribute) === null || _b === void 0 ? void 0 : _b.call(_a2, this.attribname, this.attribvalue, quote === QuoteType.Double ? '"' : quote === QuoteType.Single ? "'" : quote === QuoteType.NoValue ? void 0 : null); + if (this.attribs && !Object.prototype.hasOwnProperty.call(this.attribs, this.attribname)) { + this.attribs[this.attribname] = this.attribvalue; + } + this.attribvalue = ""; + } + getInstructionName(value) { + const index = value.search(reNameEnd); + let name = index < 0 ? value : value.substr(0, index); + if (this.lowerCaseTagNames) { + name = name.toLowerCase(); + } + return name; + } + /** @internal */ + ondeclaration(start, endIndex) { + this.endIndex = endIndex; + const value = this.getSlice(start, endIndex); + if (this.cbs.onprocessinginstruction) { + const name = this.getInstructionName(value); + this.cbs.onprocessinginstruction(`!${name}`, `!${value}`); + } + this.startIndex = endIndex + 1; + } + /** @internal */ + onprocessinginstruction(start, endIndex) { + this.endIndex = endIndex; + const value = this.getSlice(start, endIndex); + if (this.cbs.onprocessinginstruction) { + const name = this.getInstructionName(value); + this.cbs.onprocessinginstruction(`?${name}`, `?${value}`); + } + this.startIndex = endIndex + 1; + } + /** @internal */ + oncomment(start, endIndex, offset) { + var _a2, _b, _c, _d; + this.endIndex = endIndex; + (_b = (_a2 = this.cbs).oncomment) === null || _b === void 0 ? void 0 : _b.call(_a2, this.getSlice(start, endIndex - offset)); + (_d = (_c = this.cbs).oncommentend) === null || _d === void 0 ? void 0 : _d.call(_c); + this.startIndex = endIndex + 1; + } + /** @internal */ + oncdata(start, endIndex, offset) { + var _a2, _b, _c, _d, _e, _f, _g, _h, _j, _k; + this.endIndex = endIndex; + const value = this.getSlice(start, endIndex - offset); + if (!this.htmlMode || this.options.recognizeCDATA) { + (_b = (_a2 = this.cbs).oncdatastart) === null || _b === void 0 ? void 0 : _b.call(_a2); + (_d = (_c = this.cbs).ontext) === null || _d === void 0 ? void 0 : _d.call(_c, value); + (_f = (_e = this.cbs).oncdataend) === null || _f === void 0 ? void 0 : _f.call(_e); + } else { + (_h = (_g = this.cbs).oncomment) === null || _h === void 0 ? void 0 : _h.call(_g, `[CDATA[${value}]]`); + (_k = (_j = this.cbs).oncommentend) === null || _k === void 0 ? void 0 : _k.call(_j); + } + this.startIndex = endIndex + 1; + } + /** @internal */ + onend() { + var _a2, _b; + if (this.cbs.onclosetag) { + this.endIndex = this.startIndex; + for (let index = 0; index < this.stack.length; index++) { + this.cbs.onclosetag(this.stack[index], true); + } + } + (_b = (_a2 = this.cbs).onend) === null || _b === void 0 ? void 0 : _b.call(_a2); + } + /** + * Resets the parser to a blank state, ready to parse a new HTML document + */ + reset() { + var _a2, _b, _c, _d; + (_b = (_a2 = this.cbs).onreset) === null || _b === void 0 ? void 0 : _b.call(_a2); + this.tokenizer.reset(); + this.tagname = ""; + this.attribname = ""; + this.attribs = null; + this.stack.length = 0; + this.startIndex = 0; + this.endIndex = 0; + (_d = (_c = this.cbs).onparserinit) === null || _d === void 0 ? void 0 : _d.call(_c, this); + this.buffers.length = 0; + this.foreignContext.length = 0; + this.foreignContext.unshift(!this.htmlMode); + this.bufferOffset = 0; + this.writeIndex = 0; + this.ended = false; + } + /** + * Resets the parser, then parses a complete document and + * pushes it to the handler. + * + * @param data Document to parse. + */ + parseComplete(data) { + this.reset(); + this.end(data); + } + getSlice(start, end) { + while (start - this.bufferOffset >= this.buffers[0].length) { + this.shiftBuffer(); + } + let slice = this.buffers[0].slice(start - this.bufferOffset, end - this.bufferOffset); + while (end - this.bufferOffset > this.buffers[0].length) { + this.shiftBuffer(); + slice += this.buffers[0].slice(0, end - this.bufferOffset); + } + return slice; + } + shiftBuffer() { + this.bufferOffset += this.buffers[0].length; + this.writeIndex--; + this.buffers.shift(); + } + /** + * Parses a chunk of data and calls the corresponding callbacks. + * + * @param chunk Chunk to parse. + */ + write(chunk) { + var _a2, _b; + if (this.ended) { + (_b = (_a2 = this.cbs).onerror) === null || _b === void 0 ? void 0 : _b.call(_a2, new Error(".write() after done!")); + return; + } + this.buffers.push(chunk); + if (this.tokenizer.running) { + this.tokenizer.write(chunk); + this.writeIndex++; + } + } + /** + * Parses the end of the buffer and clears the stack, calls onend. + * + * @param chunk Optional final chunk to parse. + */ + end(chunk) { + var _a2, _b; + if (this.ended) { + (_b = (_a2 = this.cbs).onerror) === null || _b === void 0 ? void 0 : _b.call(_a2, new Error(".end() after done!")); + return; + } + if (chunk) + this.write(chunk); + this.ended = true; + this.tokenizer.end(); + } + /** + * Pauses parsing. The parser won't emit events until `resume` is called. + */ + pause() { + this.tokenizer.pause(); + } + /** + * Resumes parsing after `pause` was called. + */ + resume() { + this.tokenizer.resume(); + while (this.tokenizer.running && this.writeIndex < this.buffers.length) { + this.tokenizer.write(this.buffers[this.writeIndex++]); + } + if (this.ended) + this.tokenizer.end(); + } + /** + * Alias of `write`, for backwards compatibility. + * + * @param chunk Chunk to parse. + * @deprecated + */ + parseChunk(chunk) { + this.write(chunk); + } + /** + * Alias of `end`, for backwards compatibility. + * + * @param chunk Optional final chunk to parse. + * @deprecated + */ + done(chunk) { + this.end(chunk); + } +}; + +// node_modules/domelementtype/lib/esm/index.js +var esm_exports = {}; +__export(esm_exports, { + CDATA: () => CDATA, + Comment: () => Comment, + Directive: () => Directive, + Doctype: () => Doctype, + ElementType: () => ElementType, + Root: () => Root, + Script: () => Script, + Style: () => Style, + Tag: () => Tag, + Text: () => Text, + isTag: () => isTag +}); +var ElementType; +(function(ElementType2) { + ElementType2["Root"] = "root"; + ElementType2["Text"] = "text"; + ElementType2["Directive"] = "directive"; + ElementType2["Comment"] = "comment"; + ElementType2["Script"] = "script"; + ElementType2["Style"] = "style"; + ElementType2["Tag"] = "tag"; + ElementType2["CDATA"] = "cdata"; + ElementType2["Doctype"] = "doctype"; +})(ElementType || (ElementType = {})); +function isTag(elem) { + return elem.type === ElementType.Tag || elem.type === ElementType.Script || elem.type === ElementType.Style; +} +var Root = ElementType.Root; +var Text = ElementType.Text; +var Directive = ElementType.Directive; +var Comment = ElementType.Comment; +var Script = ElementType.Script; +var Style = ElementType.Style; +var Tag = ElementType.Tag; +var CDATA = ElementType.CDATA; +var Doctype = ElementType.Doctype; + +// node_modules/domhandler/lib/esm/node.js +var Node = class { + constructor() { + this.parent = null; + this.prev = null; + this.next = null; + this.startIndex = null; + this.endIndex = null; + } + // Read-write aliases for properties + /** + * Same as {@link parent}. + * [DOM spec](https://dom.spec.whatwg.org)-compatible alias. + */ + get parentNode() { + return this.parent; + } + set parentNode(parent) { + this.parent = parent; + } + /** + * Same as {@link prev}. + * [DOM spec](https://dom.spec.whatwg.org)-compatible alias. + */ + get previousSibling() { + return this.prev; + } + set previousSibling(prev) { + this.prev = prev; + } + /** + * Same as {@link next}. + * [DOM spec](https://dom.spec.whatwg.org)-compatible alias. + */ + get nextSibling() { + return this.next; + } + set nextSibling(next) { + this.next = next; + } + /** + * Clone this node, and optionally its children. + * + * @param recursive Clone child nodes as well. + * @returns A clone of the node. + */ + cloneNode(recursive = false) { + return cloneNode(this, recursive); + } +}; +var DataNode = class extends Node { + /** + * @param data The content of the data node + */ + constructor(data) { + super(); + this.data = data; + } + /** + * Same as {@link data}. + * [DOM spec](https://dom.spec.whatwg.org)-compatible alias. + */ + get nodeValue() { + return this.data; + } + set nodeValue(data) { + this.data = data; + } +}; +var Text2 = class extends DataNode { + constructor() { + super(...arguments); + this.type = ElementType.Text; + } + get nodeType() { + return 3; + } +}; +var Comment2 = class extends DataNode { + constructor() { + super(...arguments); + this.type = ElementType.Comment; + } + get nodeType() { + return 8; + } +}; +var ProcessingInstruction = class extends DataNode { + constructor(name, data) { + super(data); + this.name = name; + this.type = ElementType.Directive; + } + get nodeType() { + return 1; + } +}; +var NodeWithChildren = class extends Node { + /** + * @param children Children of the node. Only certain node types can have children. + */ + constructor(children) { + super(); + this.children = children; + } + // Aliases + /** First child of the node. */ + get firstChild() { + var _a2; + return (_a2 = this.children[0]) !== null && _a2 !== void 0 ? _a2 : null; + } + /** Last child of the node. */ + get lastChild() { + return this.children.length > 0 ? this.children[this.children.length - 1] : null; + } + /** + * Same as {@link children}. + * [DOM spec](https://dom.spec.whatwg.org)-compatible alias. + */ + get childNodes() { + return this.children; + } + set childNodes(children) { + this.children = children; + } +}; +var CDATA2 = class extends NodeWithChildren { + constructor() { + super(...arguments); + this.type = ElementType.CDATA; + } + get nodeType() { + return 4; + } +}; +var Document = class extends NodeWithChildren { + constructor() { + super(...arguments); + this.type = ElementType.Root; + } + get nodeType() { + return 9; + } +}; +var Element = class extends NodeWithChildren { + /** + * @param name Name of the tag, eg. `div`, `span`. + * @param attribs Object mapping attribute names to attribute values. + * @param children Children of the node. + */ + constructor(name, attribs, children = [], type = name === "script" ? ElementType.Script : name === "style" ? ElementType.Style : ElementType.Tag) { + super(children); + this.name = name; + this.attribs = attribs; + this.type = type; + } + get nodeType() { + return 1; + } + // DOM Level 1 aliases + /** + * Same as {@link name}. + * [DOM spec](https://dom.spec.whatwg.org)-compatible alias. + */ + get tagName() { + return this.name; + } + set tagName(name) { + this.name = name; + } + get attributes() { + return Object.keys(this.attribs).map((name) => { + var _a2, _b; + return { + name, + value: this.attribs[name], + namespace: (_a2 = this["x-attribsNamespace"]) === null || _a2 === void 0 ? void 0 : _a2[name], + prefix: (_b = this["x-attribsPrefix"]) === null || _b === void 0 ? void 0 : _b[name] + }; + }); + } +}; +function isTag2(node) { + return isTag(node); +} +function isCDATA(node) { + return node.type === ElementType.CDATA; +} +function isText(node) { + return node.type === ElementType.Text; +} +function isComment(node) { + return node.type === ElementType.Comment; +} +function isDirective(node) { + return node.type === ElementType.Directive; +} +function isDocument(node) { + return node.type === ElementType.Root; +} +function hasChildren(node) { + return Object.prototype.hasOwnProperty.call(node, "children"); +} +function cloneNode(node, recursive = false) { + let result; + if (isText(node)) { + result = new Text2(node.data); + } else if (isComment(node)) { + result = new Comment2(node.data); + } else if (isTag2(node)) { + const children = recursive ? cloneChildren(node.children) : []; + const clone = new Element(node.name, { ...node.attribs }, children); + children.forEach((child) => child.parent = clone); + if (node.namespace != null) { + clone.namespace = node.namespace; + } + if (node["x-attribsNamespace"]) { + clone["x-attribsNamespace"] = { ...node["x-attribsNamespace"] }; + } + if (node["x-attribsPrefix"]) { + clone["x-attribsPrefix"] = { ...node["x-attribsPrefix"] }; + } + result = clone; + } else if (isCDATA(node)) { + const children = recursive ? cloneChildren(node.children) : []; + const clone = new CDATA2(children); + children.forEach((child) => child.parent = clone); + result = clone; + } else if (isDocument(node)) { + const children = recursive ? cloneChildren(node.children) : []; + const clone = new Document(children); + children.forEach((child) => child.parent = clone); + if (node["x-mode"]) { + clone["x-mode"] = node["x-mode"]; + } + result = clone; + } else if (isDirective(node)) { + const instruction = new ProcessingInstruction(node.name, node.data); + if (node["x-name"] != null) { + instruction["x-name"] = node["x-name"]; + instruction["x-publicId"] = node["x-publicId"]; + instruction["x-systemId"] = node["x-systemId"]; + } + result = instruction; + } else { + throw new Error(`Not implemented yet: ${node.type}`); + } + result.startIndex = node.startIndex; + result.endIndex = node.endIndex; + if (node.sourceCodeLocation != null) { + result.sourceCodeLocation = node.sourceCodeLocation; + } + return result; +} +function cloneChildren(childs) { + const children = childs.map((child) => cloneNode(child, true)); + for (let i = 1; i < children.length; i++) { + children[i].prev = children[i - 1]; + children[i - 1].next = children[i]; + } + return children; +} + +// node_modules/domhandler/lib/esm/index.js +var defaultOpts = { + withStartIndices: false, + withEndIndices: false, + xmlMode: false +}; +var DomHandler = class { + /** + * @param callback Called once parsing has completed. + * @param options Settings for the handler. + * @param elementCB Callback whenever a tag is closed. + */ + constructor(callback, options, elementCB) { + this.dom = []; + this.root = new Document(this.dom); + this.done = false; + this.tagStack = [this.root]; + this.lastNode = null; + this.parser = null; + if (typeof options === "function") { + elementCB = options; + options = defaultOpts; + } + if (typeof callback === "object") { + options = callback; + callback = void 0; + } + this.callback = callback !== null && callback !== void 0 ? callback : null; + this.options = options !== null && options !== void 0 ? options : defaultOpts; + this.elementCB = elementCB !== null && elementCB !== void 0 ? elementCB : null; + } + onparserinit(parser) { + this.parser = parser; + } + // Resets the handler back to starting state + onreset() { + this.dom = []; + this.root = new Document(this.dom); + this.done = false; + this.tagStack = [this.root]; + this.lastNode = null; + this.parser = null; + } + // Signals the handler that parsing is done + onend() { + if (this.done) + return; + this.done = true; + this.parser = null; + this.handleCallback(null); + } + onerror(error) { + this.handleCallback(error); + } + onclosetag() { + this.lastNode = null; + const elem = this.tagStack.pop(); + if (this.options.withEndIndices) { + elem.endIndex = this.parser.endIndex; + } + if (this.elementCB) + this.elementCB(elem); + } + onopentag(name, attribs) { + const type = this.options.xmlMode ? ElementType.Tag : void 0; + const element = new Element(name, attribs, void 0, type); + this.addNode(element); + this.tagStack.push(element); + } + ontext(data) { + const { lastNode } = this; + if (lastNode && lastNode.type === ElementType.Text) { + lastNode.data += data; + if (this.options.withEndIndices) { + lastNode.endIndex = this.parser.endIndex; + } + } else { + const node = new Text2(data); + this.addNode(node); + this.lastNode = node; + } + } + oncomment(data) { + if (this.lastNode && this.lastNode.type === ElementType.Comment) { + this.lastNode.data += data; + return; + } + const node = new Comment2(data); + this.addNode(node); + this.lastNode = node; + } + oncommentend() { + this.lastNode = null; + } + oncdatastart() { + const text = new Text2(""); + const node = new CDATA2([text]); + this.addNode(node); + text.parent = node; + this.lastNode = text; + } + oncdataend() { + this.lastNode = null; + } + onprocessinginstruction(name, data) { + const node = new ProcessingInstruction(name, data); + this.addNode(node); + } + handleCallback(error) { + if (typeof this.callback === "function") { + this.callback(error, this.dom); + } else if (error) { + throw error; + } + } + addNode(node) { + const parent = this.tagStack[this.tagStack.length - 1]; + const previousSibling2 = parent.children[parent.children.length - 1]; + if (this.options.withStartIndices) { + node.startIndex = this.parser.startIndex; + } + if (this.options.withEndIndices) { + node.endIndex = this.parser.endIndex; + } + parent.children.push(node); + if (previousSibling2) { + node.prev = previousSibling2; + previousSibling2.next = node; + } + node.parent = parent; + this.lastNode = null; + } +}; + +// node_modules/domutils/lib/esm/index.js +var esm_exports2 = {}; +__export(esm_exports2, { + DocumentPosition: () => DocumentPosition, + append: () => append, + appendChild: () => appendChild, + compareDocumentPosition: () => compareDocumentPosition, + existsOne: () => existsOne, + filter: () => filter, + find: () => find, + findAll: () => findAll, + findOne: () => findOne, + findOneChild: () => findOneChild, + getAttributeValue: () => getAttributeValue, + getChildren: () => getChildren, + getElementById: () => getElementById, + getElements: () => getElements, + getElementsByClassName: () => getElementsByClassName, + getElementsByTagName: () => getElementsByTagName, + getElementsByTagType: () => getElementsByTagType, + getFeed: () => getFeed, + getInnerHTML: () => getInnerHTML, + getName: () => getName, + getOuterHTML: () => getOuterHTML, + getParent: () => getParent, + getSiblings: () => getSiblings, + getText: () => getText, + hasAttrib: () => hasAttrib, + hasChildren: () => hasChildren, + innerText: () => innerText, + isCDATA: () => isCDATA, + isComment: () => isComment, + isDocument: () => isDocument, + isTag: () => isTag2, + isText: () => isText, + nextElementSibling: () => nextElementSibling, + prepend: () => prepend, + prependChild: () => prependChild, + prevElementSibling: () => prevElementSibling, + removeElement: () => removeElement, + removeSubsets: () => removeSubsets, + replaceElement: () => replaceElement, + testElement: () => testElement, + textContent: () => textContent, + uniqueSort: () => uniqueSort +}); + +// node_modules/entities/lib/esm/generated/encode-html.js +function restoreDiff(arr) { + for (let i = 1; i < arr.length; i++) { + arr[i][0] += arr[i - 1][0] + 1; + } + return arr; +} +var encode_html_default = new Map(/* @__PURE__ */ restoreDiff([[9, " "], [0, " "], [22, "!"], [0, """], [0, "#"], [0, "$"], [0, "%"], [0, "&"], [0, "'"], [0, "("], [0, ")"], [0, "*"], [0, "+"], [0, ","], [1, "."], [0, "/"], [10, ":"], [0, ";"], [0, { v: "<", n: 8402, o: "<⃒" }], [0, { v: "=", n: 8421, o: "=⃥" }], [0, { v: ">", n: 8402, o: ">⃒" }], [0, "?"], [0, "@"], [26, "["], [0, "\"], [0, "]"], [0, "^"], [0, "_"], [0, "`"], [5, { n: 106, o: "fj" }], [20, "{"], [0, "|"], [0, "}"], [34, " "], [0, "¡"], [0, "¢"], [0, "£"], [0, "¤"], [0, "¥"], [0, "¦"], [0, "§"], [0, "¨"], [0, "©"], [0, "ª"], [0, "«"], [0, "¬"], [0, "­"], [0, "®"], [0, "¯"], [0, "°"], [0, "±"], [0, "²"], [0, "³"], [0, "´"], [0, "µ"], [0, "¶"], [0, "·"], [0, "¸"], [0, "¹"], [0, "º"], [0, "»"], [0, "¼"], [0, "½"], [0, "¾"], [0, "¿"], [0, "À"], [0, "Á"], [0, "Â"], [0, "Ã"], [0, "Ä"], [0, "Å"], [0, "Æ"], [0, "Ç"], [0, "È"], [0, "É"], [0, "Ê"], [0, "Ë"], [0, "Ì"], [0, "Í"], [0, "Î"], [0, "Ï"], [0, "Ð"], [0, "Ñ"], [0, "Ò"], [0, "Ó"], [0, "Ô"], [0, "Õ"], [0, "Ö"], [0, "×"], [0, "Ø"], [0, "Ù"], [0, "Ú"], [0, "Û"], [0, "Ü"], [0, "Ý"], [0, "Þ"], [0, "ß"], [0, "à"], [0, "á"], [0, "â"], [0, "ã"], [0, "ä"], [0, "å"], [0, "æ"], [0, "ç"], [0, "è"], [0, "é"], [0, "ê"], [0, "ë"], [0, "ì"], [0, "í"], [0, "î"], [0, "ï"], [0, "ð"], [0, "ñ"], [0, "ò"], [0, "ó"], [0, "ô"], [0, "õ"], [0, "ö"], [0, "÷"], [0, "ø"], [0, "ù"], [0, "ú"], [0, "û"], [0, "ü"], [0, "ý"], [0, "þ"], [0, "ÿ"], [0, "Ā"], [0, "ā"], [0, "Ă"], [0, "ă"], [0, "Ą"], [0, "ą"], [0, "Ć"], [0, "ć"], [0, "Ĉ"], [0, "ĉ"], [0, "Ċ"], [0, "ċ"], [0, "Č"], [0, "č"], [0, "Ď"], [0, "ď"], [0, "Đ"], [0, "đ"], [0, "Ē"], [0, "ē"], [2, "Ė"], [0, "ė"], [0, "Ę"], [0, "ę"], [0, "Ě"], [0, "ě"], [0, "Ĝ"], [0, "ĝ"], [0, "Ğ"], [0, "ğ"], [0, "Ġ"], [0, "ġ"], [0, "Ģ"], [1, "Ĥ"], [0, "ĥ"], [0, "Ħ"], [0, "ħ"], [0, "Ĩ"], [0, "ĩ"], [0, "Ī"], [0, "ī"], [2, "Į"], [0, "į"], [0, "İ"], [0, "ı"], [0, "IJ"], [0, "ij"], [0, "Ĵ"], [0, "ĵ"], [0, "Ķ"], [0, "ķ"], [0, "ĸ"], [0, "Ĺ"], [0, "ĺ"], [0, "Ļ"], [0, "ļ"], [0, "Ľ"], [0, "ľ"], [0, "Ŀ"], [0, "ŀ"], [0, "Ł"], [0, "ł"], [0, "Ń"], [0, "ń"], [0, "Ņ"], [0, "ņ"], [0, "Ň"], [0, "ň"], [0, "ʼn"], [0, "Ŋ"], [0, "ŋ"], [0, "Ō"], [0, "ō"], [2, "Ő"], [0, "ő"], [0, "Œ"], [0, "œ"], [0, "Ŕ"], [0, "ŕ"], [0, "Ŗ"], [0, "ŗ"], [0, "Ř"], [0, "ř"], [0, "Ś"], [0, "ś"], [0, "Ŝ"], [0, "ŝ"], [0, "Ş"], [0, "ş"], [0, "Š"], [0, "š"], [0, "Ţ"], [0, "ţ"], [0, "Ť"], [0, "ť"], [0, "Ŧ"], [0, "ŧ"], [0, "Ũ"], [0, "ũ"], [0, "Ū"], [0, "ū"], [0, "Ŭ"], [0, "ŭ"], [0, "Ů"], [0, "ů"], [0, "Ű"], [0, "ű"], [0, "Ų"], [0, "ų"], [0, "Ŵ"], [0, "ŵ"], [0, "Ŷ"], [0, "ŷ"], [0, "Ÿ"], [0, "Ź"], [0, "ź"], [0, "Ż"], [0, "ż"], [0, "Ž"], [0, "ž"], [19, "ƒ"], [34, "Ƶ"], [63, "ǵ"], [65, "ȷ"], [142, "ˆ"], [0, "ˇ"], [16, "˘"], [0, "˙"], [0, "˚"], [0, "˛"], [0, "˜"], [0, "˝"], [51, "̑"], [127, "Α"], [0, "Β"], [0, "Γ"], [0, "Δ"], [0, "Ε"], [0, "Ζ"], [0, "Η"], [0, "Θ"], [0, "Ι"], [0, "Κ"], [0, "Λ"], [0, "Μ"], [0, "Ν"], [0, "Ξ"], [0, "Ο"], [0, "Π"], [0, "Ρ"], [1, "Σ"], [0, "Τ"], [0, "Υ"], [0, "Φ"], [0, "Χ"], [0, "Ψ"], [0, "Ω"], [7, "α"], [0, "β"], [0, "γ"], [0, "δ"], [0, "ε"], [0, "ζ"], [0, "η"], [0, "θ"], [0, "ι"], [0, "κ"], [0, "λ"], [0, "μ"], [0, "ν"], [0, "ξ"], [0, "ο"], [0, "π"], [0, "ρ"], [0, "ς"], [0, "σ"], [0, "τ"], [0, "υ"], [0, "φ"], [0, "χ"], [0, "ψ"], [0, "ω"], [7, "ϑ"], [0, "ϒ"], [2, "ϕ"], [0, "ϖ"], [5, "Ϝ"], [0, "ϝ"], [18, "ϰ"], [0, "ϱ"], [3, "ϵ"], [0, "϶"], [10, "Ё"], [0, "Ђ"], [0, "Ѓ"], [0, "Є"], [0, "Ѕ"], [0, "І"], [0, "Ї"], [0, "Ј"], [0, "Љ"], [0, "Њ"], [0, "Ћ"], [0, "Ќ"], [1, "Ў"], [0, "Џ"], [0, "А"], [0, "Б"], [0, "В"], [0, "Г"], [0, "Д"], [0, "Е"], [0, "Ж"], [0, "З"], [0, "И"], [0, "Й"], [0, "К"], [0, "Л"], [0, "М"], [0, "Н"], [0, "О"], [0, "П"], [0, "Р"], [0, "С"], [0, "Т"], [0, "У"], [0, "Ф"], [0, "Х"], [0, "Ц"], [0, "Ч"], [0, "Ш"], [0, "Щ"], [0, "Ъ"], [0, "Ы"], [0, "Ь"], [0, "Э"], [0, "Ю"], [0, "Я"], [0, "а"], [0, "б"], [0, "в"], [0, "г"], [0, "д"], [0, "е"], [0, "ж"], [0, "з"], [0, "и"], [0, "й"], [0, "к"], [0, "л"], [0, "м"], [0, "н"], [0, "о"], [0, "п"], [0, "р"], [0, "с"], [0, "т"], [0, "у"], [0, "ф"], [0, "х"], [0, "ц"], [0, "ч"], [0, "ш"], [0, "щ"], [0, "ъ"], [0, "ы"], [0, "ь"], [0, "э"], [0, "ю"], [0, "я"], [1, "ё"], [0, "ђ"], [0, "ѓ"], [0, "є"], [0, "ѕ"], [0, "і"], [0, "ї"], [0, "ј"], [0, "љ"], [0, "њ"], [0, "ћ"], [0, "ќ"], [1, "ў"], [0, "џ"], [7074, " "], [0, " "], [0, " "], [0, " "], [1, " "], [0, " "], [0, " "], [0, " "], [0, "​"], [0, "‌"], [0, "‍"], [0, "‎"], [0, "‏"], [0, "‐"], [2, "–"], [0, "—"], [0, "―"], [0, "‖"], [1, "‘"], [0, "’"], [0, "‚"], [1, "“"], [0, "”"], [0, "„"], [1, "†"], [0, "‡"], [0, "•"], [2, "‥"], [0, "…"], [9, "‰"], [0, "‱"], [0, "′"], [0, "″"], [0, "‴"], [0, "‵"], [3, "‹"], [0, "›"], [3, "‾"], [2, "⁁"], [1, "⁃"], [0, "⁄"], [10, "⁏"], [7, "⁗"], [7, { v: " ", n: 8202, o: "  " }], [0, "⁠"], [0, "⁡"], [0, "⁢"], [0, "⁣"], [72, "€"], [46, "⃛"], [0, "⃜"], [37, "ℂ"], [2, "℅"], [4, "ℊ"], [0, "ℋ"], [0, "ℌ"], [0, "ℍ"], [0, "ℎ"], [0, "ℏ"], [0, "ℐ"], [0, "ℑ"], [0, "ℒ"], [0, "ℓ"], [1, "ℕ"], [0, "№"], [0, "℗"], [0, "℘"], [0, "ℙ"], [0, "ℚ"], [0, "ℛ"], [0, "ℜ"], [0, "ℝ"], [0, "℞"], [3, "™"], [1, "ℤ"], [2, "℧"], [0, "ℨ"], [0, "℩"], [2, "ℬ"], [0, "ℭ"], [1, "ℯ"], [0, "ℰ"], [0, "ℱ"], [1, "ℳ"], [0, "ℴ"], [0, "ℵ"], [0, "ℶ"], [0, "ℷ"], [0, "ℸ"], [12, "ⅅ"], [0, "ⅆ"], [0, "ⅇ"], [0, "ⅈ"], [10, "⅓"], [0, "⅔"], [0, "⅕"], [0, "⅖"], [0, "⅗"], [0, "⅘"], [0, "⅙"], [0, "⅚"], [0, "⅛"], [0, "⅜"], [0, "⅝"], [0, "⅞"], [49, "←"], [0, "↑"], [0, "→"], [0, "↓"], [0, "↔"], [0, "↕"], [0, "↖"], [0, "↗"], [0, "↘"], [0, "↙"], [0, "↚"], [0, "↛"], [1, { v: "↝", n: 824, o: "↝̸" }], [0, "↞"], [0, "↟"], [0, "↠"], [0, "↡"], [0, "↢"], [0, "↣"], [0, "↤"], [0, "↥"], [0, "↦"], [0, "↧"], [1, "↩"], [0, "↪"], [0, "↫"], [0, "↬"], [0, "↭"], [0, "↮"], [1, "↰"], [0, "↱"], [0, "↲"], [0, "↳"], [1, "↵"], [0, "↶"], [0, "↷"], [2, "↺"], [0, "↻"], [0, "↼"], [0, "↽"], [0, "↾"], [0, "↿"], [0, "⇀"], [0, "⇁"], [0, "⇂"], [0, "⇃"], [0, "⇄"], [0, "⇅"], [0, "⇆"], [0, "⇇"], [0, "⇈"], [0, "⇉"], [0, "⇊"], [0, "⇋"], [0, "⇌"], [0, "⇍"], [0, "⇎"], [0, "⇏"], [0, "⇐"], [0, "⇑"], [0, "⇒"], [0, "⇓"], [0, "⇔"], [0, "⇕"], [0, "⇖"], [0, "⇗"], [0, "⇘"], [0, "⇙"], [0, "⇚"], [0, "⇛"], [1, "⇝"], [6, "⇤"], [0, "⇥"], [15, "⇵"], [7, "⇽"], [0, "⇾"], [0, "⇿"], [0, "∀"], [0, "∁"], [0, { v: "∂", n: 824, o: "∂̸" }], [0, "∃"], [0, "∄"], [0, "∅"], [1, "∇"], [0, "∈"], [0, "∉"], [1, "∋"], [0, "∌"], [2, "∏"], [0, "∐"], [0, "∑"], [0, "−"], [0, "∓"], [0, "∔"], [1, "∖"], [0, "∗"], [0, "∘"], [1, "√"], [2, "∝"], [0, "∞"], [0, "∟"], [0, { v: "∠", n: 8402, o: "∠⃒" }], [0, "∡"], [0, "∢"], [0, "∣"], [0, "∤"], [0, "∥"], [0, "∦"], [0, "∧"], [0, "∨"], [0, { v: "∩", n: 65024, o: "∩︀" }], [0, { v: "∪", n: 65024, o: "∪︀" }], [0, "∫"], [0, "∬"], [0, "∭"], [0, "∮"], [0, "∯"], [0, "∰"], [0, "∱"], [0, "∲"], [0, "∳"], [0, "∴"], [0, "∵"], [0, "∶"], [0, "∷"], [0, "∸"], [1, "∺"], [0, "∻"], [0, { v: "∼", n: 8402, o: "∼⃒" }], [0, { v: "∽", n: 817, o: "∽̱" }], [0, { v: "∾", n: 819, o: "∾̳" }], [0, "∿"], [0, "≀"], [0, "≁"], [0, { v: "≂", n: 824, o: "≂̸" }], [0, "≃"], [0, "≄"], [0, "≅"], [0, "≆"], [0, "≇"], [0, "≈"], [0, "≉"], [0, "≊"], [0, { v: "≋", n: 824, o: "≋̸" }], [0, "≌"], [0, { v: "≍", n: 8402, o: "≍⃒" }], [0, { v: "≎", n: 824, o: "≎̸" }], [0, { v: "≏", n: 824, o: "≏̸" }], [0, { v: "≐", n: 824, o: "≐̸" }], [0, "≑"], [0, "≒"], [0, "≓"], [0, "≔"], [0, "≕"], [0, "≖"], [0, "≗"], [1, "≙"], [0, "≚"], [1, "≜"], [2, "≟"], [0, "≠"], [0, { v: "≡", n: 8421, o: "≡⃥" }], [0, "≢"], [1, { v: "≤", n: 8402, o: "≤⃒" }], [0, { v: "≥", n: 8402, o: "≥⃒" }], [0, { v: "≦", n: 824, o: "≦̸" }], [0, { v: "≧", n: 824, o: "≧̸" }], [0, { v: "≨", n: 65024, o: "≨︀" }], [0, { v: "≩", n: 65024, o: "≩︀" }], [0, { v: "≪", n: new Map(/* @__PURE__ */ restoreDiff([[824, "≪̸"], [7577, "≪⃒"]])) }], [0, { v: "≫", n: new Map(/* @__PURE__ */ restoreDiff([[824, "≫̸"], [7577, "≫⃒"]])) }], [0, "≬"], [0, "≭"], [0, "≮"], [0, "≯"], [0, "≰"], [0, "≱"], [0, "≲"], [0, "≳"], [0, "≴"], [0, "≵"], [0, "≶"], [0, "≷"], [0, "≸"], [0, "≹"], [0, "≺"], [0, "≻"], [0, "≼"], [0, "≽"], [0, "≾"], [0, { v: "≿", n: 824, o: "≿̸" }], [0, "⊀"], [0, "⊁"], [0, { v: "⊂", n: 8402, o: "⊂⃒" }], [0, { v: "⊃", n: 8402, o: "⊃⃒" }], [0, "⊄"], [0, "⊅"], [0, "⊆"], [0, "⊇"], [0, "⊈"], [0, "⊉"], [0, { v: "⊊", n: 65024, o: "⊊︀" }], [0, { v: "⊋", n: 65024, o: "⊋︀" }], [1, "⊍"], [0, "⊎"], [0, { v: "⊏", n: 824, o: "⊏̸" }], [0, { v: "⊐", n: 824, o: "⊐̸" }], [0, "⊑"], [0, "⊒"], [0, { v: "⊓", n: 65024, o: "⊓︀" }], [0, { v: "⊔", n: 65024, o: "⊔︀" }], [0, "⊕"], [0, "⊖"], [0, "⊗"], [0, "⊘"], [0, "⊙"], [0, "⊚"], [0, "⊛"], [1, "⊝"], [0, "⊞"], [0, "⊟"], [0, "⊠"], [0, "⊡"], [0, "⊢"], [0, "⊣"], [0, "⊤"], [0, "⊥"], [1, "⊧"], [0, "⊨"], [0, "⊩"], [0, "⊪"], [0, "⊫"], [0, "⊬"], [0, "⊭"], [0, "⊮"], [0, "⊯"], [0, "⊰"], [1, "⊲"], [0, "⊳"], [0, { v: "⊴", n: 8402, o: "⊴⃒" }], [0, { v: "⊵", n: 8402, o: "⊵⃒" }], [0, "⊶"], [0, "⊷"], [0, "⊸"], [0, "⊹"], [0, "⊺"], [0, "⊻"], [1, "⊽"], [0, "⊾"], [0, "⊿"], [0, "⋀"], [0, "⋁"], [0, "⋂"], [0, "⋃"], [0, "⋄"], [0, "⋅"], [0, "⋆"], [0, "⋇"], [0, "⋈"], [0, "⋉"], [0, "⋊"], [0, "⋋"], [0, "⋌"], [0, "⋍"], [0, "⋎"], [0, "⋏"], [0, "⋐"], [0, "⋑"], [0, "⋒"], [0, "⋓"], [0, "⋔"], [0, "⋕"], [0, "⋖"], [0, "⋗"], [0, { v: "⋘", n: 824, o: "⋘̸" }], [0, { v: "⋙", n: 824, o: "⋙̸" }], [0, { v: "⋚", n: 65024, o: "⋚︀" }], [0, { v: "⋛", n: 65024, o: "⋛︀" }], [2, "⋞"], [0, "⋟"], [0, "⋠"], [0, "⋡"], [0, "⋢"], [0, "⋣"], [2, "⋦"], [0, "⋧"], [0, "⋨"], [0, "⋩"], [0, "⋪"], [0, "⋫"], [0, "⋬"], [0, "⋭"], [0, "⋮"], [0, "⋯"], [0, "⋰"], [0, "⋱"], [0, "⋲"], [0, "⋳"], [0, "⋴"], [0, { v: "⋵", n: 824, o: "⋵̸" }], [0, "⋶"], [0, "⋷"], [1, { v: "⋹", n: 824, o: "⋹̸" }], [0, "⋺"], [0, "⋻"], [0, "⋼"], [0, "⋽"], [0, "⋾"], [6, "⌅"], [0, "⌆"], [1, "⌈"], [0, "⌉"], [0, "⌊"], [0, "⌋"], [0, "⌌"], [0, "⌍"], [0, "⌎"], [0, "⌏"], [0, "⌐"], [1, "⌒"], [0, "⌓"], [1, "⌕"], [0, "⌖"], [5, "⌜"], [0, "⌝"], [0, "⌞"], [0, "⌟"], [2, "⌢"], [0, "⌣"], [9, "⌭"], [0, "⌮"], [7, "⌶"], [6, "⌽"], [1, "⌿"], [60, "⍼"], [51, "⎰"], [0, "⎱"], [2, "⎴"], [0, "⎵"], [0, "⎶"], [37, "⏜"], [0, "⏝"], [0, "⏞"], [0, "⏟"], [2, "⏢"], [4, "⏧"], [59, "␣"], [164, "Ⓢ"], [55, "─"], [1, "│"], [9, "┌"], [3, "┐"], [3, "└"], [3, "┘"], [3, "├"], [7, "┤"], [7, "┬"], [7, "┴"], [7, "┼"], [19, "═"], [0, "║"], [0, "╒"], [0, "╓"], [0, "╔"], [0, "╕"], [0, "╖"], [0, "╗"], [0, "╘"], [0, "╙"], [0, "╚"], [0, "╛"], [0, "╜"], [0, "╝"], [0, "╞"], [0, "╟"], [0, "╠"], [0, "╡"], [0, "╢"], [0, "╣"], [0, "╤"], [0, "╥"], [0, "╦"], [0, "╧"], [0, "╨"], [0, "╩"], [0, "╪"], [0, "╫"], [0, "╬"], [19, "▀"], [3, "▄"], [3, "█"], [8, "░"], [0, "▒"], [0, "▓"], [13, "□"], [8, "▪"], [0, "▫"], [1, "▭"], [0, "▮"], [2, "▱"], [1, "△"], [0, "▴"], [0, "▵"], [2, "▸"], [0, "▹"], [3, "▽"], [0, "▾"], [0, "▿"], [2, "◂"], [0, "◃"], [6, "◊"], [0, "○"], [32, "◬"], [2, "◯"], [8, "◸"], [0, "◹"], [0, "◺"], [0, "◻"], [0, "◼"], [8, "★"], [0, "☆"], [7, "☎"], [49, "♀"], [1, "♂"], [29, "♠"], [2, "♣"], [1, "♥"], [0, "♦"], [3, "♪"], [2, "♭"], [0, "♮"], [0, "♯"], [163, "✓"], [3, "✗"], [8, "✠"], [21, "✶"], [33, "❘"], [25, "❲"], [0, "❳"], [84, "⟈"], [0, "⟉"], [28, "⟦"], [0, "⟧"], [0, "⟨"], [0, "⟩"], [0, "⟪"], [0, "⟫"], [0, "⟬"], [0, "⟭"], [7, "⟵"], [0, "⟶"], [0, "⟷"], [0, "⟸"], [0, "⟹"], [0, "⟺"], [1, "⟼"], [2, "⟿"], [258, "⤂"], [0, "⤃"], [0, "⤄"], [0, "⤅"], [6, "⤌"], [0, "⤍"], [0, "⤎"], [0, "⤏"], [0, "⤐"], [0, "⤑"], [0, "⤒"], [0, "⤓"], [2, "⤖"], [2, "⤙"], [0, "⤚"], [0, "⤛"], [0, "⤜"], [0, "⤝"], [0, "⤞"], [0, "⤟"], [0, "⤠"], [2, "⤣"], [0, "⤤"], [0, "⤥"], [0, "⤦"], [0, "⤧"], [0, "⤨"], [0, "⤩"], [0, "⤪"], [8, { v: "⤳", n: 824, o: "⤳̸" }], [1, "⤵"], [0, "⤶"], [0, "⤷"], [0, "⤸"], [0, "⤹"], [2, "⤼"], [0, "⤽"], [7, "⥅"], [2, "⥈"], [0, "⥉"], [0, "⥊"], [0, "⥋"], [2, "⥎"], [0, "⥏"], [0, "⥐"], [0, "⥑"], [0, "⥒"], [0, "⥓"], [0, "⥔"], [0, "⥕"], [0, "⥖"], [0, "⥗"], [0, "⥘"], [0, "⥙"], [0, "⥚"], [0, "⥛"], [0, "⥜"], [0, "⥝"], [0, "⥞"], [0, "⥟"], [0, "⥠"], [0, "⥡"], [0, "⥢"], [0, "⥣"], [0, "⥤"], [0, "⥥"], [0, "⥦"], [0, "⥧"], [0, "⥨"], [0, "⥩"], [0, "⥪"], [0, "⥫"], [0, "⥬"], [0, "⥭"], [0, "⥮"], [0, "⥯"], [0, "⥰"], [0, "⥱"], [0, "⥲"], [0, "⥳"], [0, "⥴"], [0, "⥵"], [0, "⥶"], [1, "⥸"], [0, "⥹"], [1, "⥻"], [0, "⥼"], [0, "⥽"], [0, "⥾"], [0, "⥿"], [5, "⦅"], [0, "⦆"], [4, "⦋"], [0, "⦌"], [0, "⦍"], [0, "⦎"], [0, "⦏"], [0, "⦐"], [0, "⦑"], [0, "⦒"], [0, "⦓"], [0, "⦔"], [0, "⦕"], [0, "⦖"], [3, "⦚"], [1, "⦜"], [0, "⦝"], [6, "⦤"], [0, "⦥"], [0, "⦦"], [0, "⦧"], [0, "⦨"], [0, "⦩"], [0, "⦪"], [0, "⦫"], [0, "⦬"], [0, "⦭"], [0, "⦮"], [0, "⦯"], [0, "⦰"], [0, "⦱"], [0, "⦲"], [0, "⦳"], [0, "⦴"], [0, "⦵"], [0, "⦶"], [0, "⦷"], [1, "⦹"], [1, "⦻"], [0, "⦼"], [1, "⦾"], [0, "⦿"], [0, "⧀"], [0, "⧁"], [0, "⧂"], [0, "⧃"], [0, "⧄"], [0, "⧅"], [3, "⧉"], [3, "⧍"], [0, "⧎"], [0, { v: "⧏", n: 824, o: "⧏̸" }], [0, { v: "⧐", n: 824, o: "⧐̸" }], [11, "⧜"], [0, "⧝"], [0, "⧞"], [4, "⧣"], [0, "⧤"], [0, "⧥"], [5, "⧫"], [8, "⧴"], [1, "⧶"], [9, "⨀"], [0, "⨁"], [0, "⨂"], [1, "⨄"], [1, "⨆"], [5, "⨌"], [0, "⨍"], [2, "⨐"], [0, "⨑"], [0, "⨒"], [0, "⨓"], [0, "⨔"], [0, "⨕"], [0, "⨖"], [0, "⨗"], [10, "⨢"], [0, "⨣"], [0, "⨤"], [0, "⨥"], [0, "⨦"], [0, "⨧"], [1, "⨩"], [0, "⨪"], [2, "⨭"], [0, "⨮"], [0, "⨯"], [0, "⨰"], [0, "⨱"], [1, "⨳"], [0, "⨴"], [0, "⨵"], [0, "⨶"], [0, "⨷"], [0, "⨸"], [0, "⨹"], [0, "⨺"], [0, "⨻"], [0, "⨼"], [2, "⨿"], [0, "⩀"], [1, "⩂"], [0, "⩃"], [0, "⩄"], [0, "⩅"], [0, "⩆"], [0, "⩇"], [0, "⩈"], [0, "⩉"], [0, "⩊"], [0, "⩋"], [0, "⩌"], [0, "⩍"], [2, "⩐"], [2, "⩓"], [0, "⩔"], [0, "⩕"], [0, "⩖"], [0, "⩗"], [0, "⩘"], [1, "⩚"], [0, "⩛"], [0, "⩜"], [0, "⩝"], [1, "⩟"], [6, "⩦"], [3, "⩪"], [2, { v: "⩭", n: 824, o: "⩭̸" }], [0, "⩮"], [0, "⩯"], [0, { v: "⩰", n: 824, o: "⩰̸" }], [0, "⩱"], [0, "⩲"], [0, "⩳"], [0, "⩴"], [0, "⩵"], [1, "⩷"], [0, "⩸"], [0, "⩹"], [0, "⩺"], [0, "⩻"], [0, "⩼"], [0, { v: "⩽", n: 824, o: "⩽̸" }], [0, { v: "⩾", n: 824, o: "⩾̸" }], [0, "⩿"], [0, "⪀"], [0, "⪁"], [0, "⪂"], [0, "⪃"], [0, "⪄"], [0, "⪅"], [0, "⪆"], [0, "⪇"], [0, "⪈"], [0, "⪉"], [0, "⪊"], [0, "⪋"], [0, "⪌"], [0, "⪍"], [0, "⪎"], [0, "⪏"], [0, "⪐"], [0, "⪑"], [0, "⪒"], [0, "⪓"], [0, "⪔"], [0, "⪕"], [0, "⪖"], [0, "⪗"], [0, "⪘"], [0, "⪙"], [0, "⪚"], [2, "⪝"], [0, "⪞"], [0, "⪟"], [0, "⪠"], [0, { v: "⪡", n: 824, o: "⪡̸" }], [0, { v: "⪢", n: 824, o: "⪢̸" }], [1, "⪤"], [0, "⪥"], [0, "⪦"], [0, "⪧"], [0, "⪨"], [0, "⪩"], [0, "⪪"], [0, "⪫"], [0, { v: "⪬", n: 65024, o: "⪬︀" }], [0, { v: "⪭", n: 65024, o: "⪭︀" }], [0, "⪮"], [0, { v: "⪯", n: 824, o: "⪯̸" }], [0, { v: "⪰", n: 824, o: "⪰̸" }], [2, "⪳"], [0, "⪴"], [0, "⪵"], [0, "⪶"], [0, "⪷"], [0, "⪸"], [0, "⪹"], [0, "⪺"], [0, "⪻"], [0, "⪼"], [0, "⪽"], [0, "⪾"], [0, "⪿"], [0, "⫀"], [0, "⫁"], [0, "⫂"], [0, "⫃"], [0, "⫄"], [0, { v: "⫅", n: 824, o: "⫅̸" }], [0, { v: "⫆", n: 824, o: "⫆̸" }], [0, "⫇"], [0, "⫈"], [2, { v: "⫋", n: 65024, o: "⫋︀" }], [0, { v: "⫌", n: 65024, o: "⫌︀" }], [2, "⫏"], [0, "⫐"], [0, "⫑"], [0, "⫒"], [0, "⫓"], [0, "⫔"], [0, "⫕"], [0, "⫖"], [0, "⫗"], [0, "⫘"], [0, "⫙"], [0, "⫚"], [0, "⫛"], [8, "⫤"], [1, "⫦"], [0, "⫧"], [0, "⫨"], [0, "⫩"], [1, "⫫"], [0, "⫬"], [0, "⫭"], [0, "⫮"], [0, "⫯"], [0, "⫰"], [0, "⫱"], [0, "⫲"], [0, "⫳"], [9, { v: "⫽", n: 8421, o: "⫽⃥" }], [44343, { n: new Map(/* @__PURE__ */ restoreDiff([[56476, "𝒜"], [1, "𝒞"], [0, "𝒟"], [2, "𝒢"], [2, "𝒥"], [0, "𝒦"], [2, "𝒩"], [0, "𝒪"], [0, "𝒫"], [0, "𝒬"], [1, "𝒮"], [0, "𝒯"], [0, "𝒰"], [0, "𝒱"], [0, "𝒲"], [0, "𝒳"], [0, "𝒴"], [0, "𝒵"], [0, "𝒶"], [0, "𝒷"], [0, "𝒸"], [0, "𝒹"], [1, "𝒻"], [1, "𝒽"], [0, "𝒾"], [0, "𝒿"], [0, "𝓀"], [0, "𝓁"], [0, "𝓂"], [0, "𝓃"], [1, "𝓅"], [0, "𝓆"], [0, "𝓇"], [0, "𝓈"], [0, "𝓉"], [0, "𝓊"], [0, "𝓋"], [0, "𝓌"], [0, "𝓍"], [0, "𝓎"], [0, "𝓏"], [52, "𝔄"], [0, "𝔅"], [1, "𝔇"], [0, "𝔈"], [0, "𝔉"], [0, "𝔊"], [2, "𝔍"], [0, "𝔎"], [0, "𝔏"], [0, "𝔐"], [0, "𝔑"], [0, "𝔒"], [0, "𝔓"], [0, "𝔔"], [1, "𝔖"], [0, "𝔗"], [0, "𝔘"], [0, "𝔙"], [0, "𝔚"], [0, "𝔛"], [0, "𝔜"], [1, "𝔞"], [0, "𝔟"], [0, "𝔠"], [0, "𝔡"], [0, "𝔢"], [0, "𝔣"], [0, "𝔤"], [0, "𝔥"], [0, "𝔦"], [0, "𝔧"], [0, "𝔨"], [0, "𝔩"], [0, "𝔪"], [0, "𝔫"], [0, "𝔬"], [0, "𝔭"], [0, "𝔮"], [0, "𝔯"], [0, "𝔰"], [0, "𝔱"], [0, "𝔲"], [0, "𝔳"], [0, "𝔴"], [0, "𝔵"], [0, "𝔶"], [0, "𝔷"], [0, "𝔸"], [0, "𝔹"], [1, "𝔻"], [0, "𝔼"], [0, "𝔽"], [0, "𝔾"], [1, "𝕀"], [0, "𝕁"], [0, "𝕂"], [0, "𝕃"], [0, "𝕄"], [1, "𝕆"], [3, "𝕊"], [0, "𝕋"], [0, "𝕌"], [0, "𝕍"], [0, "𝕎"], [0, "𝕏"], [0, "𝕐"], [1, "𝕒"], [0, "𝕓"], [0, "𝕔"], [0, "𝕕"], [0, "𝕖"], [0, "𝕗"], [0, "𝕘"], [0, "𝕙"], [0, "𝕚"], [0, "𝕛"], [0, "𝕜"], [0, "𝕝"], [0, "𝕞"], [0, "𝕟"], [0, "𝕠"], [0, "𝕡"], [0, "𝕢"], [0, "𝕣"], [0, "𝕤"], [0, "𝕥"], [0, "𝕦"], [0, "𝕧"], [0, "𝕨"], [0, "𝕩"], [0, "𝕪"], [0, "𝕫"]])) }], [8906, "ff"], [0, "fi"], [0, "fl"], [0, "ffi"], [0, "ffl"]])); + +// node_modules/entities/lib/esm/escape.js +var xmlReplacer = /["&'<>$\x80-\uFFFF]/g; +var xmlCodeMap = /* @__PURE__ */ new Map([ + [34, """], + [38, "&"], + [39, "'"], + [60, "<"], + [62, ">"] +]); +var getCodePoint = ( + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition + String.prototype.codePointAt != null ? (str, index) => str.codePointAt(index) : ( + // http://mathiasbynens.be/notes/javascript-encoding#surrogate-formulae + (c, index) => (c.charCodeAt(index) & 64512) === 55296 ? (c.charCodeAt(index) - 55296) * 1024 + c.charCodeAt(index + 1) - 56320 + 65536 : c.charCodeAt(index) + ) +); +function encodeXML(str) { + let ret = ""; + let lastIdx = 0; + let match; + while ((match = xmlReplacer.exec(str)) !== null) { + const i = match.index; + const char = str.charCodeAt(i); + const next = xmlCodeMap.get(char); + if (next !== void 0) { + ret += str.substring(lastIdx, i) + next; + lastIdx = i + 1; + } else { + ret += `${str.substring(lastIdx, i)}&#x${getCodePoint(str, i).toString(16)};`; + lastIdx = xmlReplacer.lastIndex += Number((char & 64512) === 55296); + } + } + return ret + str.substr(lastIdx); +} +function getEscaper(regex, map) { + return function escape3(data) { + let match; + let lastIdx = 0; + let result = ""; + while (match = regex.exec(data)) { + if (lastIdx !== match.index) { + result += data.substring(lastIdx, match.index); + } + result += map.get(match[0].charCodeAt(0)); + lastIdx = match.index + 1; + } + return result + data.substring(lastIdx); + }; +} +var escapeUTF8 = getEscaper(/[&<>'"]/g, xmlCodeMap); +var escapeAttribute = getEscaper(/["&\u00A0]/g, /* @__PURE__ */ new Map([ + [34, """], + [38, "&"], + [160, " "] +])); +var escapeText = getEscaper(/[&<>\u00A0]/g, /* @__PURE__ */ new Map([ + [38, "&"], + [60, "<"], + [62, ">"], + [160, " "] +])); + +// node_modules/entities/lib/esm/index.js +var EntityLevel; +(function(EntityLevel2) { + EntityLevel2[EntityLevel2["XML"] = 0] = "XML"; + EntityLevel2[EntityLevel2["HTML"] = 1] = "HTML"; +})(EntityLevel || (EntityLevel = {})); +var EncodingMode; +(function(EncodingMode2) { + EncodingMode2[EncodingMode2["UTF8"] = 0] = "UTF8"; + EncodingMode2[EncodingMode2["ASCII"] = 1] = "ASCII"; + EncodingMode2[EncodingMode2["Extensive"] = 2] = "Extensive"; + EncodingMode2[EncodingMode2["Attribute"] = 3] = "Attribute"; + EncodingMode2[EncodingMode2["Text"] = 4] = "Text"; +})(EncodingMode || (EncodingMode = {})); + +// node_modules/dom-serializer/lib/esm/foreignNames.js +var elementNames = new Map([ + "altGlyph", + "altGlyphDef", + "altGlyphItem", + "animateColor", + "animateMotion", + "animateTransform", + "clipPath", + "feBlend", + "feColorMatrix", + "feComponentTransfer", + "feComposite", + "feConvolveMatrix", + "feDiffuseLighting", + "feDisplacementMap", + "feDistantLight", + "feDropShadow", + "feFlood", + "feFuncA", + "feFuncB", + "feFuncG", + "feFuncR", + "feGaussianBlur", + "feImage", + "feMerge", + "feMergeNode", + "feMorphology", + "feOffset", + "fePointLight", + "feSpecularLighting", + "feSpotLight", + "feTile", + "feTurbulence", + "foreignObject", + "glyphRef", + "linearGradient", + "radialGradient", + "textPath" +].map((val) => [val.toLowerCase(), val])); +var attributeNames = new Map([ + "definitionURL", + "attributeName", + "attributeType", + "baseFrequency", + "baseProfile", + "calcMode", + "clipPathUnits", + "diffuseConstant", + "edgeMode", + "filterUnits", + "glyphRef", + "gradientTransform", + "gradientUnits", + "kernelMatrix", + "kernelUnitLength", + "keyPoints", + "keySplines", + "keyTimes", + "lengthAdjust", + "limitingConeAngle", + "markerHeight", + "markerUnits", + "markerWidth", + "maskContentUnits", + "maskUnits", + "numOctaves", + "pathLength", + "patternContentUnits", + "patternTransform", + "patternUnits", + "pointsAtX", + "pointsAtY", + "pointsAtZ", + "preserveAlpha", + "preserveAspectRatio", + "primitiveUnits", + "refX", + "refY", + "repeatCount", + "repeatDur", + "requiredExtensions", + "requiredFeatures", + "specularConstant", + "specularExponent", + "spreadMethod", + "startOffset", + "stdDeviation", + "stitchTiles", + "surfaceScale", + "systemLanguage", + "tableValues", + "targetX", + "targetY", + "textLength", + "viewBox", + "viewTarget", + "xChannelSelector", + "yChannelSelector", + "zoomAndPan" +].map((val) => [val.toLowerCase(), val])); + +// node_modules/dom-serializer/lib/esm/index.js +var unencodedElements = /* @__PURE__ */ new Set([ + "style", + "script", + "xmp", + "iframe", + "noembed", + "noframes", + "plaintext", + "noscript" +]); +function replaceQuotes(value) { + return value.replace(/"/g, """); +} +function formatAttributes(attributes2, opts) { + var _a2; + if (!attributes2) + return; + const encode = ((_a2 = opts.encodeEntities) !== null && _a2 !== void 0 ? _a2 : opts.decodeEntities) === false ? replaceQuotes : opts.xmlMode || opts.encodeEntities !== "utf8" ? encodeXML : escapeAttribute; + return Object.keys(attributes2).map((key2) => { + var _a3, _b; + const value = (_a3 = attributes2[key2]) !== null && _a3 !== void 0 ? _a3 : ""; + if (opts.xmlMode === "foreign") { + key2 = (_b = attributeNames.get(key2)) !== null && _b !== void 0 ? _b : key2; + } + if (!opts.emptyAttrs && !opts.xmlMode && value === "") { + return key2; + } + return `${key2}="${encode(value)}"`; + }).join(" "); +} +var singleTag = /* @__PURE__ */ new Set([ + "area", + "base", + "basefont", + "br", + "col", + "command", + "embed", + "frame", + "hr", + "img", + "input", + "isindex", + "keygen", + "link", + "meta", + "param", + "source", + "track", + "wbr" +]); +function render(node, options = {}) { + const nodes = "length" in node ? node : [node]; + let output = ""; + for (let i = 0; i < nodes.length; i++) { + output += renderNode(nodes[i], options); + } + return output; +} +var esm_default = render; +function renderNode(node, options) { + switch (node.type) { + case Root: + return render(node.children, options); + // @ts-expect-error We don't use `Doctype` yet + case Doctype: + case Directive: + return renderDirective(node); + case Comment: + return renderComment(node); + case CDATA: + return renderCdata(node); + case Script: + case Style: + case Tag: + return renderTag(node, options); + case Text: + return renderText(node, options); + } +} +var foreignModeIntegrationPoints = /* @__PURE__ */ new Set([ + "mi", + "mo", + "mn", + "ms", + "mtext", + "annotation-xml", + "foreignObject", + "desc", + "title" +]); +var foreignElements = /* @__PURE__ */ new Set(["svg", "math"]); +function renderTag(elem, opts) { + var _a2; + if (opts.xmlMode === "foreign") { + elem.name = (_a2 = elementNames.get(elem.name)) !== null && _a2 !== void 0 ? _a2 : elem.name; + if (elem.parent && foreignModeIntegrationPoints.has(elem.parent.name)) { + opts = { ...opts, xmlMode: false }; + } + } + if (!opts.xmlMode && foreignElements.has(elem.name)) { + opts = { ...opts, xmlMode: "foreign" }; + } + let tag = `<${elem.name}`; + const attribs = formatAttributes(elem.attribs, opts); + if (attribs) { + tag += ` ${attribs}`; + } + if (elem.children.length === 0 && (opts.xmlMode ? ( + // In XML mode or foreign mode, and user hasn't explicitly turned off self-closing tags + opts.selfClosingTags !== false + ) : ( + // User explicitly asked for self-closing tags, even in HTML mode + opts.selfClosingTags && singleTag.has(elem.name) + ))) { + if (!opts.xmlMode) + tag += " "; + tag += "/>"; + } else { + tag += ">"; + if (elem.children.length > 0) { + tag += render(elem.children, opts); + } + if (opts.xmlMode || !singleTag.has(elem.name)) { + tag += `</${elem.name}>`; + } + } + return tag; +} +function renderDirective(elem) { + return `<${elem.data}>`; +} +function renderText(elem, opts) { + var _a2; + let data = elem.data || ""; + if (((_a2 = opts.encodeEntities) !== null && _a2 !== void 0 ? _a2 : opts.decodeEntities) !== false && !(!opts.xmlMode && elem.parent && unencodedElements.has(elem.parent.name))) { + data = opts.xmlMode || opts.encodeEntities !== "utf8" ? encodeXML(data) : escapeText(data); + } + return data; +} +function renderCdata(elem) { + return `<![CDATA[${elem.children[0].data}]]>`; +} +function renderComment(elem) { + return `<!--${elem.data}-->`; +} + +// node_modules/domutils/lib/esm/stringify.js +function getOuterHTML(node, options) { + return esm_default(node, options); +} +function getInnerHTML(node, options) { + return hasChildren(node) ? node.children.map((node2) => getOuterHTML(node2, options)).join("") : ""; +} +function getText(node) { + if (Array.isArray(node)) + return node.map(getText).join(""); + if (isTag2(node)) + return node.name === "br" ? "\n" : getText(node.children); + if (isCDATA(node)) + return getText(node.children); + if (isText(node)) + return node.data; + return ""; +} +function textContent(node) { + if (Array.isArray(node)) + return node.map(textContent).join(""); + if (hasChildren(node) && !isComment(node)) { + return textContent(node.children); + } + if (isText(node)) + return node.data; + return ""; +} +function innerText(node) { + if (Array.isArray(node)) + return node.map(innerText).join(""); + if (hasChildren(node) && (node.type === ElementType.Tag || isCDATA(node))) { + return innerText(node.children); + } + if (isText(node)) + return node.data; + return ""; +} + +// node_modules/domutils/lib/esm/traversal.js +function getChildren(elem) { + return hasChildren(elem) ? elem.children : []; +} +function getParent(elem) { + return elem.parent || null; +} +function getSiblings(elem) { + const parent = getParent(elem); + if (parent != null) + return getChildren(parent); + const siblings = [elem]; + let { prev, next } = elem; + while (prev != null) { + siblings.unshift(prev); + ({ prev } = prev); + } + while (next != null) { + siblings.push(next); + ({ next } = next); + } + return siblings; +} +function getAttributeValue(elem, name) { + var _a2; + return (_a2 = elem.attribs) === null || _a2 === void 0 ? void 0 : _a2[name]; +} +function hasAttrib(elem, name) { + return elem.attribs != null && Object.prototype.hasOwnProperty.call(elem.attribs, name) && elem.attribs[name] != null; +} +function getName(elem) { + return elem.name; +} +function nextElementSibling(elem) { + let { next } = elem; + while (next !== null && !isTag2(next)) + ({ next } = next); + return next; +} +function prevElementSibling(elem) { + let { prev } = elem; + while (prev !== null && !isTag2(prev)) + ({ prev } = prev); + return prev; +} + +// node_modules/domutils/lib/esm/manipulation.js +function removeElement(elem) { + if (elem.prev) + elem.prev.next = elem.next; + if (elem.next) + elem.next.prev = elem.prev; + if (elem.parent) { + const childs = elem.parent.children; + const childsIndex = childs.lastIndexOf(elem); + if (childsIndex >= 0) { + childs.splice(childsIndex, 1); + } + } + elem.next = null; + elem.prev = null; + elem.parent = null; +} +function replaceElement(elem, replacement) { + const prev = replacement.prev = elem.prev; + if (prev) { + prev.next = replacement; + } + const next = replacement.next = elem.next; + if (next) { + next.prev = replacement; + } + const parent = replacement.parent = elem.parent; + if (parent) { + const childs = parent.children; + childs[childs.lastIndexOf(elem)] = replacement; + elem.parent = null; + } +} +function appendChild(parent, child) { + removeElement(child); + child.next = null; + child.parent = parent; + if (parent.children.push(child) > 1) { + const sibling = parent.children[parent.children.length - 2]; + sibling.next = child; + child.prev = sibling; + } else { + child.prev = null; + } +} +function append(elem, next) { + removeElement(next); + const { parent } = elem; + const currNext = elem.next; + next.next = currNext; + next.prev = elem; + elem.next = next; + next.parent = parent; + if (currNext) { + currNext.prev = next; + if (parent) { + const childs = parent.children; + childs.splice(childs.lastIndexOf(currNext), 0, next); + } + } else if (parent) { + parent.children.push(next); + } +} +function prependChild(parent, child) { + removeElement(child); + child.parent = parent; + child.prev = null; + if (parent.children.unshift(child) !== 1) { + const sibling = parent.children[1]; + sibling.prev = child; + child.next = sibling; + } else { + child.next = null; + } +} +function prepend(elem, prev) { + removeElement(prev); + const { parent } = elem; + if (parent) { + const childs = parent.children; + childs.splice(childs.indexOf(elem), 0, prev); + } + if (elem.prev) { + elem.prev.next = prev; + } + prev.parent = parent; + prev.prev = elem.prev; + prev.next = elem; + elem.prev = prev; +} + +// node_modules/domutils/lib/esm/querying.js +function filter(test, node, recurse = true, limit = Infinity) { + return find(test, Array.isArray(node) ? node : [node], recurse, limit); +} +function find(test, nodes, recurse, limit) { + const result = []; + const nodeStack = [Array.isArray(nodes) ? nodes : [nodes]]; + const indexStack = [0]; + for (; ; ) { + if (indexStack[0] >= nodeStack[0].length) { + if (indexStack.length === 1) { + return result; + } + nodeStack.shift(); + indexStack.shift(); + continue; + } + const elem = nodeStack[0][indexStack[0]++]; + if (test(elem)) { + result.push(elem); + if (--limit <= 0) + return result; + } + if (recurse && hasChildren(elem) && elem.children.length > 0) { + indexStack.unshift(0); + nodeStack.unshift(elem.children); + } + } +} +function findOneChild(test, nodes) { + return nodes.find(test); +} +function findOne(test, nodes, recurse = true) { + const searchedNodes = Array.isArray(nodes) ? nodes : [nodes]; + for (let i = 0; i < searchedNodes.length; i++) { + const node = searchedNodes[i]; + if (isTag2(node) && test(node)) { + return node; + } + if (recurse && hasChildren(node) && node.children.length > 0) { + return findOne(test, node.children, true); + } + } + return null; +} +function existsOne(test, nodes) { + return (Array.isArray(nodes) ? nodes : [nodes]).some((node) => isTag2(node) && test(node) || hasChildren(node) && existsOne(test, node.children)); +} +function findAll(test, nodes) { + const result = []; + const nodeStack = [Array.isArray(nodes) ? nodes : [nodes]]; + const indexStack = [0]; + for (; ; ) { + if (indexStack[0] >= nodeStack[0].length) { + if (nodeStack.length === 1) { + return result; + } + nodeStack.shift(); + indexStack.shift(); + continue; + } + const elem = nodeStack[0][indexStack[0]++]; + if (isTag2(elem) && test(elem)) + result.push(elem); + if (hasChildren(elem) && elem.children.length > 0) { + indexStack.unshift(0); + nodeStack.unshift(elem.children); + } + } +} + +// node_modules/domutils/lib/esm/legacy.js +var Checks = { + tag_name(name) { + if (typeof name === "function") { + return (elem) => isTag2(elem) && name(elem.name); + } else if (name === "*") { + return isTag2; + } + return (elem) => isTag2(elem) && elem.name === name; + }, + tag_type(type) { + if (typeof type === "function") { + return (elem) => type(elem.type); + } + return (elem) => elem.type === type; + }, + tag_contains(data) { + if (typeof data === "function") { + return (elem) => isText(elem) && data(elem.data); + } + return (elem) => isText(elem) && elem.data === data; + } +}; +function getAttribCheck(attrib, value) { + if (typeof value === "function") { + return (elem) => isTag2(elem) && value(elem.attribs[attrib]); + } + return (elem) => isTag2(elem) && elem.attribs[attrib] === value; +} +function combineFuncs(a, b) { + return (elem) => a(elem) || b(elem); +} +function compileTest(options) { + const funcs = Object.keys(options).map((key2) => { + const value = options[key2]; + return Object.prototype.hasOwnProperty.call(Checks, key2) ? Checks[key2](value) : getAttribCheck(key2, value); + }); + return funcs.length === 0 ? null : funcs.reduce(combineFuncs); +} +function testElement(options, node) { + const test = compileTest(options); + return test ? test(node) : true; +} +function getElements(options, nodes, recurse, limit = Infinity) { + const test = compileTest(options); + return test ? filter(test, nodes, recurse, limit) : []; +} +function getElementById(id, nodes, recurse = true) { + if (!Array.isArray(nodes)) + nodes = [nodes]; + return findOne(getAttribCheck("id", id), nodes, recurse); +} +function getElementsByTagName(tagName19, nodes, recurse = true, limit = Infinity) { + return filter(Checks["tag_name"](tagName19), nodes, recurse, limit); +} +function getElementsByClassName(className, nodes, recurse = true, limit = Infinity) { + return filter(getAttribCheck("class", className), nodes, recurse, limit); +} +function getElementsByTagType(type, nodes, recurse = true, limit = Infinity) { + return filter(Checks["tag_type"](type), nodes, recurse, limit); +} + +// node_modules/domutils/lib/esm/helpers.js +function removeSubsets(nodes) { + let idx = nodes.length; + while (--idx >= 0) { + const node = nodes[idx]; + if (idx > 0 && nodes.lastIndexOf(node, idx - 1) >= 0) { + nodes.splice(idx, 1); + continue; + } + for (let ancestor = node.parent; ancestor; ancestor = ancestor.parent) { + if (nodes.includes(ancestor)) { + nodes.splice(idx, 1); + break; + } + } + } + return nodes; +} +var DocumentPosition; +(function(DocumentPosition2) { + DocumentPosition2[DocumentPosition2["DISCONNECTED"] = 1] = "DISCONNECTED"; + DocumentPosition2[DocumentPosition2["PRECEDING"] = 2] = "PRECEDING"; + DocumentPosition2[DocumentPosition2["FOLLOWING"] = 4] = "FOLLOWING"; + DocumentPosition2[DocumentPosition2["CONTAINS"] = 8] = "CONTAINS"; + DocumentPosition2[DocumentPosition2["CONTAINED_BY"] = 16] = "CONTAINED_BY"; +})(DocumentPosition || (DocumentPosition = {})); +function compareDocumentPosition(nodeA, nodeB) { + const aParents = []; + const bParents = []; + if (nodeA === nodeB) { + return 0; + } + let current = hasChildren(nodeA) ? nodeA : nodeA.parent; + while (current) { + aParents.unshift(current); + current = current.parent; + } + current = hasChildren(nodeB) ? nodeB : nodeB.parent; + while (current) { + bParents.unshift(current); + current = current.parent; + } + const maxIdx = Math.min(aParents.length, bParents.length); + let idx = 0; + while (idx < maxIdx && aParents[idx] === bParents[idx]) { + idx++; + } + if (idx === 0) { + return DocumentPosition.DISCONNECTED; + } + const sharedParent = aParents[idx - 1]; + const siblings = sharedParent.children; + const aSibling = aParents[idx]; + const bSibling = bParents[idx]; + if (siblings.indexOf(aSibling) > siblings.indexOf(bSibling)) { + if (sharedParent === nodeB) { + return DocumentPosition.FOLLOWING | DocumentPosition.CONTAINED_BY; + } + return DocumentPosition.FOLLOWING; + } + if (sharedParent === nodeA) { + return DocumentPosition.PRECEDING | DocumentPosition.CONTAINS; + } + return DocumentPosition.PRECEDING; +} +function uniqueSort(nodes) { + nodes = nodes.filter((node, i, arr) => !arr.includes(node, i + 1)); + nodes.sort((a, b) => { + const relative = compareDocumentPosition(a, b); + if (relative & DocumentPosition.PRECEDING) { + return -1; + } else if (relative & DocumentPosition.FOLLOWING) { + return 1; + } + return 0; + }); + return nodes; +} + +// node_modules/domutils/lib/esm/feeds.js +function getFeed(doc) { + const feedRoot = getOneElement(isValidFeed, doc); + return !feedRoot ? null : feedRoot.name === "feed" ? getAtomFeed(feedRoot) : getRssFeed(feedRoot); +} +function getAtomFeed(feedRoot) { + var _a2; + const childs = feedRoot.children; + const feed = { + type: "atom", + items: getElementsByTagName("entry", childs).map((item) => { + var _a3; + const { children } = item; + const entry = { media: getMediaElements(children) }; + addConditionally(entry, "id", "id", children); + addConditionally(entry, "title", "title", children); + const href2 = (_a3 = getOneElement("link", children)) === null || _a3 === void 0 ? void 0 : _a3.attribs["href"]; + if (href2) { + entry.link = href2; + } + const description = fetch("summary", children) || fetch("content", children); + if (description) { + entry.description = description; + } + const pubDate = fetch("updated", children); + if (pubDate) { + entry.pubDate = new Date(pubDate); + } + return entry; + }) + }; + addConditionally(feed, "id", "id", childs); + addConditionally(feed, "title", "title", childs); + const href = (_a2 = getOneElement("link", childs)) === null || _a2 === void 0 ? void 0 : _a2.attribs["href"]; + if (href) { + feed.link = href; + } + addConditionally(feed, "description", "subtitle", childs); + const updated = fetch("updated", childs); + if (updated) { + feed.updated = new Date(updated); + } + addConditionally(feed, "author", "email", childs, true); + return feed; +} +function getRssFeed(feedRoot) { + var _a2, _b; + const childs = (_b = (_a2 = getOneElement("channel", feedRoot.children)) === null || _a2 === void 0 ? void 0 : _a2.children) !== null && _b !== void 0 ? _b : []; + const feed = { + type: feedRoot.name.substr(0, 3), + id: "", + items: getElementsByTagName("item", feedRoot.children).map((item) => { + const { children } = item; + const entry = { media: getMediaElements(children) }; + addConditionally(entry, "id", "guid", children); + addConditionally(entry, "title", "title", children); + addConditionally(entry, "link", "link", children); + addConditionally(entry, "description", "description", children); + const pubDate = fetch("pubDate", children) || fetch("dc:date", children); + if (pubDate) + entry.pubDate = new Date(pubDate); + return entry; + }) + }; + addConditionally(feed, "title", "title", childs); + addConditionally(feed, "link", "link", childs); + addConditionally(feed, "description", "description", childs); + const updated = fetch("lastBuildDate", childs); + if (updated) { + feed.updated = new Date(updated); + } + addConditionally(feed, "author", "managingEditor", childs, true); + return feed; +} +var MEDIA_KEYS_STRING = ["url", "type", "lang"]; +var MEDIA_KEYS_INT = [ + "fileSize", + "bitrate", + "framerate", + "samplingrate", + "channels", + "duration", + "height", + "width" +]; +function getMediaElements(where) { + return getElementsByTagName("media:content", where).map((elem) => { + const { attribs } = elem; + const media = { + medium: attribs["medium"], + isDefault: !!attribs["isDefault"] + }; + for (const attrib of MEDIA_KEYS_STRING) { + if (attribs[attrib]) { + media[attrib] = attribs[attrib]; + } + } + for (const attrib of MEDIA_KEYS_INT) { + if (attribs[attrib]) { + media[attrib] = parseInt(attribs[attrib], 10); + } + } + if (attribs["expression"]) { + media.expression = attribs["expression"]; + } + return media; + }); +} +function getOneElement(tagName19, node) { + return getElementsByTagName(tagName19, node, true, 1)[0]; +} +function fetch(tagName19, where, recurse = false) { + return textContent(getElementsByTagName(tagName19, where, recurse, 1)).trim(); +} +function addConditionally(obj, prop2, tagName19, where, recurse = false) { + const val = fetch(tagName19, where, recurse); + if (val) + obj[prop2] = val; +} +function isValidFeed(value) { + return value === "rss" || value === "feed" || value === "rdf:RDF"; +} + +// node_modules/htmlparser2/lib/esm/index.js +function parseDocument(data, options) { + const handler4 = new DomHandler(void 0, options); + new Parser(handler4, options).end(data); + return handler4.root; +} +function parseDOM(data, options) { + return parseDocument(data, options).children; +} +function createDocumentStream(callback, options, elementCallback) { + const handler4 = new DomHandler((error) => callback(error, handler4.root), options, elementCallback); + return new Parser(handler4, options); +} +function createDomStream(callback, options, elementCallback) { + const handler4 = new DomHandler(callback, options, elementCallback); + return new Parser(handler4, options); +} +var parseFeedDefaultOptions = { xmlMode: true }; +function parseFeed(feed, options = parseFeedDefaultOptions) { + return getFeed(parseDOM(feed, options)); +} + +// node_modules/linkedom/esm/shared/constants.js +var NODE_END = -1; +var ELEMENT_NODE = 1; +var ATTRIBUTE_NODE = 2; +var TEXT_NODE = 3; +var CDATA_SECTION_NODE = 4; +var COMMENT_NODE = 8; +var DOCUMENT_NODE = 9; +var DOCUMENT_TYPE_NODE = 10; +var DOCUMENT_FRAGMENT_NODE = 11; +var BLOCK_ELEMENTS = /* @__PURE__ */ new Set(["ARTICLE", "ASIDE", "BLOCKQUOTE", "BODY", "BR", "BUTTON", "CANVAS", "CAPTION", "COL", "COLGROUP", "DD", "DIV", "DL", "DT", "EMBED", "FIELDSET", "FIGCAPTION", "FIGURE", "FOOTER", "FORM", "H1", "H2", "H3", "H4", "H5", "H6", "LI", "UL", "OL", "P"]); +var SHOW_ALL = -1; +var SHOW_ELEMENT = 1; +var SHOW_TEXT = 4; +var SHOW_CDATA_SECTION = 8; +var SHOW_COMMENT = 128; +var DOCUMENT_POSITION_DISCONNECTED = 1; +var DOCUMENT_POSITION_PRECEDING = 2; +var DOCUMENT_POSITION_FOLLOWING = 4; +var DOCUMENT_POSITION_CONTAINS = 8; +var DOCUMENT_POSITION_CONTAINED_BY = 16; +var DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC = 32; +var SVG_NAMESPACE = "http://www.w3.org/2000/svg"; + +// node_modules/linkedom/esm/shared/object.js +var { + assign, + create, + defineProperties, + entries, + getOwnPropertyDescriptors, + keys, + setPrototypeOf +} = Object; + +// node_modules/linkedom/esm/shared/utils.js +var $String = String; +var getEnd = (node) => node.nodeType === ELEMENT_NODE ? node[END] : node; +var ignoreCase = ({ ownerDocument }) => ownerDocument[MIME].ignoreCase; +var knownAdjacent = (prev, next) => { + prev[NEXT] = next; + next[PREV] = prev; +}; +var knownBoundaries = (prev, current, next) => { + knownAdjacent(prev, current); + knownAdjacent(getEnd(current), next); +}; +var knownSegment = (prev, start, end, next) => { + knownAdjacent(prev, start); + knownAdjacent(getEnd(end), next); +}; +var knownSiblings = (prev, current, next) => { + knownAdjacent(prev, current); + knownAdjacent(current, next); +}; +var localCase = ({ localName, ownerDocument }) => { + return ownerDocument[MIME].ignoreCase ? localName.toUpperCase() : localName; +}; +var setAdjacent = (prev, next) => { + if (prev) + prev[NEXT] = next; + if (next) + next[PREV] = prev; +}; + +// node_modules/linkedom/esm/shared/shadow-roots.js +var shadowRoots = /* @__PURE__ */ new WeakMap(); + +// node_modules/linkedom/esm/interface/custom-element-registry.js +var reactive = false; +var Classes = /* @__PURE__ */ new WeakMap(); +var customElements = /* @__PURE__ */ new WeakMap(); +var attributeChangedCallback = (element, attributeName, oldValue, newValue) => { + if (reactive && customElements.has(element) && element.attributeChangedCallback && element.constructor.observedAttributes.includes(attributeName)) { + element.attributeChangedCallback(attributeName, oldValue, newValue); + } +}; +var createTrigger = (method, isConnected2) => (element) => { + if (customElements.has(element)) { + const info = customElements.get(element); + if (info.connected !== isConnected2 && element.isConnected === isConnected2) { + info.connected = isConnected2; + if (method in element) + element[method](); + } + } +}; +var triggerConnected = createTrigger("connectedCallback", true); +var connectedCallback = (element) => { + if (reactive) { + triggerConnected(element); + if (shadowRoots.has(element)) + element = shadowRoots.get(element).shadowRoot; + let { [NEXT]: next, [END]: end } = element; + while (next !== end) { + if (next.nodeType === ELEMENT_NODE) + triggerConnected(next); + next = next[NEXT]; + } + } +}; +var triggerDisconnected = createTrigger("disconnectedCallback", false); +var disconnectedCallback = (element) => { + if (reactive) { + triggerDisconnected(element); + if (shadowRoots.has(element)) + element = shadowRoots.get(element).shadowRoot; + let { [NEXT]: next, [END]: end } = element; + while (next !== end) { + if (next.nodeType === ELEMENT_NODE) + triggerDisconnected(next); + next = next[NEXT]; + } + } +}; +var CustomElementRegistry = class { + /** + * @param {Document} ownerDocument + */ + constructor(ownerDocument) { + this.ownerDocument = ownerDocument; + this.registry = /* @__PURE__ */ new Map(); + this.waiting = /* @__PURE__ */ new Map(); + this.active = false; + } + /** + * @param {string} localName the custom element definition name + * @param {Function} Class the custom element **Class** definition + * @param {object?} options the optional object with an `extends` property + */ + define(localName, Class, options = {}) { + const { ownerDocument, registry, waiting } = this; + if (registry.has(localName)) + throw new Error("unable to redefine " + localName); + if (Classes.has(Class)) + throw new Error("unable to redefine the same class: " + Class); + this.active = reactive = true; + const { extends: extend } = options; + Classes.set(Class, { + ownerDocument, + options: { is: extend ? localName : "" }, + localName: extend || localName + }); + const check = extend ? (element) => { + return element.localName === extend && element.getAttribute("is") === localName; + } : (element) => element.localName === localName; + registry.set(localName, { Class, check }); + if (waiting.has(localName)) { + for (const resolve of waiting.get(localName)) + resolve(Class); + waiting.delete(localName); + } + ownerDocument.querySelectorAll( + extend ? `${extend}[is="${localName}"]` : localName + ).forEach(this.upgrade, this); + } + /** + * @param {Element} element + */ + upgrade(element) { + if (customElements.has(element)) + return; + const { ownerDocument, registry } = this; + const ce = element.getAttribute("is") || element.localName; + if (registry.has(ce)) { + const { Class, check } = registry.get(ce); + if (check(element)) { + const { attributes: attributes2, isConnected: isConnected2 } = element; + for (const attr of attributes2) + element.removeAttributeNode(attr); + const values = entries(element); + for (const [key2] of values) + delete element[key2]; + setPrototypeOf(element, Class.prototype); + ownerDocument[UPGRADE] = { element, values }; + new Class(ownerDocument, ce); + customElements.set(element, { connected: isConnected2 }); + for (const attr of attributes2) + element.setAttributeNode(attr); + if (isConnected2 && element.connectedCallback) + element.connectedCallback(); + } + } + } + /** + * @param {string} localName the custom element definition name + */ + whenDefined(localName) { + const { registry, waiting } = this; + return new Promise((resolve) => { + if (registry.has(localName)) + resolve(registry.get(localName).Class); + else { + if (!waiting.has(localName)) + waiting.set(localName, []); + waiting.get(localName).push(resolve); + } + }); + } + /** + * @param {string} localName the custom element definition name + * @returns {Function?} the custom element **Class**, if any + */ + get(localName) { + const info = this.registry.get(localName); + return info && info.Class; + } + /** + * @param {Function} Class **Class** of custom element + * @returns {string?} found tag name or null + */ + getName(Class) { + if (Classes.has(Class)) { + const { localName } = Classes.get(Class); + return localName; + } + return null; + } +}; + +// node_modules/linkedom/esm/shared/parse-from-string.js +var { Parser: Parser2 } = esm_exports3; +var notParsing = true; +var append2 = (self, node, active) => { + const end = self[END]; + node.parentNode = self; + knownBoundaries(end[PREV], node, end); + if (active && node.nodeType === ELEMENT_NODE) + connectedCallback(node); + return node; +}; +var attribute = (element, end, attribute2, value, active) => { + attribute2[VALUE] = value; + attribute2.ownerElement = element; + knownSiblings(end[PREV], attribute2, end); + if (attribute2.name === "class") + element.className = value; + if (active) + attributeChangedCallback(element, attribute2.name, null, value); +}; +var parseFromString = (document, isHTML, markupLanguage) => { + const { active, registry } = document[CUSTOM_ELEMENTS]; + let node = document; + let ownerSVGElement = null; + let parsingCData = false; + notParsing = false; + const content = new Parser2({ + // <!DOCTYPE ...> + onprocessinginstruction(name, data) { + if (name.toLowerCase() === "!doctype") + document.doctype = data.slice(name.length).trim(); + }, + // <tagName> + onopentag(name, attributes2) { + let create3 = true; + if (isHTML) { + if (ownerSVGElement) { + node = append2(node, document.createElementNS(SVG_NAMESPACE, name), active); + node.ownerSVGElement = ownerSVGElement; + create3 = false; + } else if (name === "svg" || name === "SVG") { + ownerSVGElement = document.createElementNS(SVG_NAMESPACE, name); + node = append2(node, ownerSVGElement, active); + create3 = false; + } else if (active) { + const ce = name.includes("-") ? name : attributes2.is || ""; + if (ce && registry.has(ce)) { + const { Class } = registry.get(ce); + node = append2(node, new Class(), active); + delete attributes2.is; + create3 = false; + } + } + } + if (create3) + node = append2(node, document.createElement(name), false); + let end = node[END]; + for (const name2 of keys(attributes2)) + attribute(node, end, document.createAttribute(name2), attributes2[name2], active); + }, + // #text, #comment + oncomment(data) { + append2(node, document.createComment(data), active); + }, + ontext(text) { + if (parsingCData) { + append2(node, document.createCDATASection(text), active); + } else { + append2(node, document.createTextNode(text), active); + } + }, + // #cdata + oncdatastart() { + parsingCData = true; + }, + oncdataend() { + parsingCData = false; + }, + // </tagName> + onclosetag() { + if (isHTML && node === ownerSVGElement) + ownerSVGElement = null; + node = node.parentNode; + } + }, { + lowerCaseAttributeNames: false, + decodeEntities: true, + xmlMode: !isHTML + }); + content.write(markupLanguage); + content.end(); + notParsing = true; + return document; +}; + +// node_modules/linkedom/esm/shared/register-html-class.js +var htmlClasses = /* @__PURE__ */ new Map(); +var registerHTMLClass = (names, Class) => { + for (const name of [].concat(names)) { + htmlClasses.set(name, Class); + htmlClasses.set(name.toUpperCase(), Class); + } +}; + +// node_modules/linkedom/esm/interface/document.js +var import_perf_hooks = __toESM(require_perf_hooks(), 1); + +// node_modules/linkedom/esm/shared/jsdon.js +var loopSegment = ({ [NEXT]: next, [END]: end }, json) => { + while (next !== end) { + switch (next.nodeType) { + case ATTRIBUTE_NODE: + attrAsJSON(next, json); + break; + case TEXT_NODE: + case COMMENT_NODE: + case CDATA_SECTION_NODE: + characterDataAsJSON(next, json); + break; + case ELEMENT_NODE: + elementAsJSON(next, json); + next = getEnd(next); + break; + case DOCUMENT_TYPE_NODE: + documentTypeAsJSON(next, json); + break; + } + next = next[NEXT]; + } + const last = json.length - 1; + const value = json[last]; + if (typeof value === "number" && value < 0) + json[last] += NODE_END; + else + json.push(NODE_END); +}; +var attrAsJSON = (attr, json) => { + json.push(ATTRIBUTE_NODE, attr.name); + const value = attr[VALUE].trim(); + if (value) + json.push(value); +}; +var characterDataAsJSON = (node, json) => { + const value = node[VALUE]; + if (value.trim()) + json.push(node.nodeType, value); +}; +var nonElementAsJSON = (node, json) => { + json.push(node.nodeType); + loopSegment(node, json); +}; +var documentTypeAsJSON = ({ name, publicId, systemId }, json) => { + json.push(DOCUMENT_TYPE_NODE, name); + if (publicId) + json.push(publicId); + if (systemId) + json.push(systemId); +}; +var elementAsJSON = (element, json) => { + json.push(ELEMENT_NODE, element.localName); + loopSegment(element, json); +}; + +// node_modules/linkedom/esm/interface/mutation-observer.js +var createRecord = (type, target, addedNodes, removedNodes, attributeName, oldValue) => ({ type, target, addedNodes, removedNodes, attributeName, oldValue }); +var queueAttribute = (observer, target, attributeName, attributeFilter, attributeOldValue, oldValue) => { + if (!attributeFilter || attributeFilter.includes(attributeName)) { + const { callback, records, scheduled } = observer; + records.push(createRecord( + "attributes", + target, + [], + [], + attributeName, + attributeOldValue ? oldValue : void 0 + )); + if (!scheduled) { + observer.scheduled = true; + Promise.resolve().then(() => { + observer.scheduled = false; + callback(records.splice(0), observer); + }); + } + } +}; +var attributeChangedCallback2 = (element, attributeName, oldValue) => { + const { ownerDocument } = element; + const { active, observers } = ownerDocument[MUTATION_OBSERVER]; + if (active) { + for (const observer of observers) { + for (const [ + target, + { + childList, + subtree, + attributes: attributes2, + attributeFilter, + attributeOldValue + } + ] of observer.nodes) { + if (childList) { + if (subtree && (target === ownerDocument || target.contains(element)) || !subtree && target.children.includes(element)) { + queueAttribute( + observer, + element, + attributeName, + attributeFilter, + attributeOldValue, + oldValue + ); + break; + } + } else if (attributes2 && target === element) { + queueAttribute( + observer, + element, + attributeName, + attributeFilter, + attributeOldValue, + oldValue + ); + break; + } + } + } + } +}; +var moCallback = (element, parentNode) => { + const { ownerDocument } = element; + const { active, observers } = ownerDocument[MUTATION_OBSERVER]; + if (active) { + for (const observer of observers) { + for (const [target, { subtree, childList, characterData }] of observer.nodes) { + if (childList) { + if (parentNode && (target === parentNode || /* c8 ignore next */ + subtree && target.contains(parentNode)) || !parentNode && (subtree && (target === ownerDocument || /* c8 ignore next */ + target.contains(element)) || !subtree && target[characterData ? "childNodes" : "children"].includes(element))) { + const { callback, records, scheduled } = observer; + records.push(createRecord( + "childList", + target, + parentNode ? [] : [element], + parentNode ? [element] : [] + )); + if (!scheduled) { + observer.scheduled = true; + Promise.resolve().then(() => { + observer.scheduled = false; + callback(records.splice(0), observer); + }); + } + break; + } + } + } + } + } +}; +var MutationObserverClass = class { + constructor(ownerDocument) { + const observers = /* @__PURE__ */ new Set(); + this.observers = observers; + this.active = false; + this.class = class MutationObserver { + constructor(callback) { + this.callback = callback; + this.nodes = /* @__PURE__ */ new Map(); + this.records = []; + this.scheduled = false; + } + disconnect() { + this.records.splice(0); + this.nodes.clear(); + observers.delete(this); + ownerDocument[MUTATION_OBSERVER].active = !!observers.size; + } + /** + * @param {Element} target + * @param {MutationObserverInit} options + */ + observe(target, options = { + subtree: false, + childList: false, + attributes: false, + attributeFilter: null, + attributeOldValue: false, + characterData: false + // TODO: not implemented yet + // characterDataOldValue: false + }) { + if ("attributeOldValue" in options || "attributeFilter" in options) + options.attributes = true; + options.childList = !!options.childList; + options.subtree = !!options.subtree; + this.nodes.set(target, options); + observers.add(this); + ownerDocument[MUTATION_OBSERVER].active = true; + } + /** + * @returns {MutationRecord[]} + */ + takeRecords() { + return this.records.splice(0); + } + }; + } +}; + +// node_modules/linkedom/esm/shared/attributes.js +var emptyAttributes = /* @__PURE__ */ new Set([ + "allowfullscreen", + "allowpaymentrequest", + "async", + "autofocus", + "autoplay", + "checked", + "class", + "contenteditable", + "controls", + "default", + "defer", + "disabled", + "draggable", + "formnovalidate", + "hidden", + "id", + "ismap", + "itemscope", + "loop", + "multiple", + "muted", + "nomodule", + "novalidate", + "open", + "playsinline", + "readonly", + "required", + "reversed", + "selected", + "style", + "truespeed" +]); +var setAttribute = (element, attribute2) => { + const { [VALUE]: value, name } = attribute2; + attribute2.ownerElement = element; + knownSiblings(element, attribute2, element[NEXT]); + if (name === "class") + element.className = value; + attributeChangedCallback2(element, name, null); + attributeChangedCallback(element, name, null, value); +}; +var removeAttribute = (element, attribute2) => { + const { [VALUE]: value, name } = attribute2; + knownAdjacent(attribute2[PREV], attribute2[NEXT]); + attribute2.ownerElement = attribute2[PREV] = attribute2[NEXT] = null; + if (name === "class") + element[CLASS_LIST] = null; + attributeChangedCallback2(element, name, value); + attributeChangedCallback(element, name, value, null); +}; +var booleanAttribute = { + get(element, name) { + return element.hasAttribute(name); + }, + set(element, name, value) { + if (value) + element.setAttribute(name, ""); + else + element.removeAttribute(name); + } +}; +var numericAttribute = { + get(element, name) { + return parseFloat(element.getAttribute(name) || 0); + }, + set(element, name, value) { + element.setAttribute(name, value); + } +}; +var stringAttribute = { + get(element, name) { + return element.getAttribute(name) || ""; + }, + set(element, name, value) { + element.setAttribute(name, value); + } +}; + +// node_modules/linkedom/esm/interface/event-target.js +var wm = /* @__PURE__ */ new WeakMap(); +function dispatch(event, listener) { + if (typeof listener === "function") + listener.call(event.target, event); + else + listener.handleEvent(event); + return event._stopImmediatePropagationFlag; +} +function invokeListeners({ currentTarget, target }) { + const map = wm.get(currentTarget); + if (map && map.has(this.type)) { + const listeners = map.get(this.type); + if (currentTarget === target) { + this.eventPhase = this.AT_TARGET; + } else { + this.eventPhase = this.BUBBLING_PHASE; + } + this.currentTarget = currentTarget; + this.target = target; + for (const [listener, options] of listeners) { + if (options && options.once) + listeners.delete(listener); + if (dispatch(this, listener)) + break; + } + delete this.currentTarget; + delete this.target; + return this.cancelBubble; + } +} +var DOMEventTarget = class { + constructor() { + wm.set(this, /* @__PURE__ */ new Map()); + } + /** + * @protected + */ + _getParent() { + return null; + } + addEventListener(type, listener, options) { + const map = wm.get(this); + if (!map.has(type)) + map.set(type, /* @__PURE__ */ new Map()); + map.get(type).set(listener, options); + } + removeEventListener(type, listener) { + const map = wm.get(this); + if (map.has(type)) { + const listeners = map.get(type); + if (listeners.delete(listener) && !listeners.size) + map.delete(type); + } + } + dispatchEvent(event) { + let node = this; + event.eventPhase = event.CAPTURING_PHASE; + while (node) { + if (node.dispatchEvent) + event._path.push({ currentTarget: node, target: this }); + node = event.bubbles && node._getParent && node._getParent(); + } + event._path.some(invokeListeners, event); + event._path = []; + event.eventPhase = event.NONE; + return !event.defaultPrevented; + } +}; + +// node_modules/linkedom/esm/interface/node-list.js +var NodeList = class extends Array { + item(i) { + return i < this.length ? this[i] : null; + } +}; + +// node_modules/linkedom/esm/interface/node.js +var getParentNodeCount = ({ parentNode }) => { + let count = 0; + while (parentNode) { + count++; + parentNode = parentNode.parentNode; + } + return count; +}; +var Node2 = class extends DOMEventTarget { + static get ELEMENT_NODE() { + return ELEMENT_NODE; + } + static get ATTRIBUTE_NODE() { + return ATTRIBUTE_NODE; + } + static get TEXT_NODE() { + return TEXT_NODE; + } + static get CDATA_SECTION_NODE() { + return CDATA_SECTION_NODE; + } + static get COMMENT_NODE() { + return COMMENT_NODE; + } + static get DOCUMENT_NODE() { + return DOCUMENT_NODE; + } + static get DOCUMENT_FRAGMENT_NODE() { + return DOCUMENT_FRAGMENT_NODE; + } + static get DOCUMENT_TYPE_NODE() { + return DOCUMENT_TYPE_NODE; + } + constructor(ownerDocument, localName, nodeType) { + super(); + this.ownerDocument = ownerDocument; + this.localName = localName; + this.nodeType = nodeType; + this.parentNode = null; + this[NEXT] = null; + this[PREV] = null; + } + get ELEMENT_NODE() { + return ELEMENT_NODE; + } + get ATTRIBUTE_NODE() { + return ATTRIBUTE_NODE; + } + get TEXT_NODE() { + return TEXT_NODE; + } + get CDATA_SECTION_NODE() { + return CDATA_SECTION_NODE; + } + get COMMENT_NODE() { + return COMMENT_NODE; + } + get DOCUMENT_NODE() { + return DOCUMENT_NODE; + } + get DOCUMENT_FRAGMENT_NODE() { + return DOCUMENT_FRAGMENT_NODE; + } + get DOCUMENT_TYPE_NODE() { + return DOCUMENT_TYPE_NODE; + } + get baseURI() { + const ownerDocument = this.nodeType === DOCUMENT_NODE ? this : this.ownerDocument; + if (ownerDocument) { + const base = ownerDocument.querySelector("base"); + if (base) + return base.getAttribute("href"); + const { location } = ownerDocument.defaultView; + if (location) + return location.href; + } + return null; + } + /* c8 ignore start */ + // mixin: node + get isConnected() { + return false; + } + get nodeName() { + return this.localName; + } + get parentElement() { + return null; + } + get previousSibling() { + return null; + } + get previousElementSibling() { + return null; + } + get nextSibling() { + return null; + } + get nextElementSibling() { + return null; + } + get childNodes() { + return new NodeList(); + } + get firstChild() { + return null; + } + get lastChild() { + return null; + } + // default values + get nodeValue() { + return null; + } + set nodeValue(value) { + } + get textContent() { + return null; + } + set textContent(value) { + } + normalize() { + } + cloneNode() { + return null; + } + contains() { + return false; + } + /** + * Inserts a node before a reference node as a child of this parent node. + * @param {Node} newNode The node to be inserted. + * @param {Node} referenceNode The node before which newNode is inserted. If this is null, then newNode is inserted at the end of node's child nodes. + * @returns The added child + */ + // eslint-disable-next-line no-unused-vars + insertBefore(newNode, referenceNode) { + return newNode; + } + /** + * Adds a node to the end of the list of children of this node. + * @param {Node} child The node to append to the given parent node. + * @returns The appended child. + */ + appendChild(child) { + return child; + } + /** + * Replaces a child node within this node + * @param {Node} newChild The new node to replace oldChild. + * @param {Node} oldChild The child to be replaced. + * @returns The replaced Node. This is the same node as oldChild. + */ + replaceChild(newChild, oldChild) { + return oldChild; + } + /** + * Removes a child node from the DOM. + * @param {Node} child A Node that is the child node to be removed from the DOM. + * @returns The removed node. + */ + removeChild(child) { + return child; + } + toString() { + return ""; + } + /* c8 ignore stop */ + hasChildNodes() { + return !!this.lastChild; + } + isSameNode(node) { + return this === node; + } + // TODO: attributes? + compareDocumentPosition(target) { + let result = 0; + if (this !== target) { + let self = getParentNodeCount(this); + let other = getParentNodeCount(target); + if (self < other) { + result += DOCUMENT_POSITION_FOLLOWING; + if (this.contains(target)) + result += DOCUMENT_POSITION_CONTAINED_BY; + } else if (other < self) { + result += DOCUMENT_POSITION_PRECEDING; + if (target.contains(this)) + result += DOCUMENT_POSITION_CONTAINS; + } else if (self && other) { + const { childNodes } = this.parentNode; + if (childNodes.indexOf(this) < childNodes.indexOf(target)) + result += DOCUMENT_POSITION_FOLLOWING; + else + result += DOCUMENT_POSITION_PRECEDING; + } + if (!self || !other) { + result += DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC; + result += DOCUMENT_POSITION_DISCONNECTED; + } + } + return result; + } + isEqualNode(node) { + if (this === node) + return true; + if (this.nodeType === node.nodeType) { + switch (this.nodeType) { + case DOCUMENT_NODE: + case DOCUMENT_FRAGMENT_NODE: { + const aNodes = this.childNodes; + const bNodes = node.childNodes; + return aNodes.length === bNodes.length && aNodes.every((node2, i) => node2.isEqualNode(bNodes[i])); + } + } + return this.toString() === node.toString(); + } + return false; + } + /** + * @protected + */ + _getParent() { + return this.parentNode; + } + /** + * Calling it on an element inside a standard web page will return an HTMLDocument object representing the entire page (or <iframe>). + * Calling it on an element inside a shadow DOM will return the associated ShadowRoot. + * @return {ShadowRoot | HTMLDocument} + */ + getRootNode() { + let root = this; + while (root.parentNode) + root = root.parentNode; + return root; + } +}; + +// node_modules/linkedom/esm/shared/text-escaper.js +var { replace } = ""; +var ca = /[<>&\xA0]/g; +var esca = { + "\xA0": " ", + "&": "&", + "<": "<", + ">": ">" +}; +var pe = (m) => esca[m]; +var escape2 = (es) => replace.call(es, ca, pe); + +// node_modules/linkedom/esm/interface/attr.js +var QUOTE = /"/g; +var Attr = class _Attr extends Node2 { + constructor(ownerDocument, name, value = "") { + super(ownerDocument, name, ATTRIBUTE_NODE); + this.ownerElement = null; + this.name = $String(name); + this[VALUE] = $String(value); + this[CHANGED] = false; + } + get value() { + return this[VALUE]; + } + set value(newValue) { + const { [VALUE]: oldValue, name, ownerElement } = this; + this[VALUE] = $String(newValue); + this[CHANGED] = true; + if (ownerElement) { + attributeChangedCallback2(ownerElement, name, oldValue); + attributeChangedCallback(ownerElement, name, oldValue, this[VALUE]); + } + } + cloneNode() { + const { ownerDocument, name, [VALUE]: value } = this; + return new _Attr(ownerDocument, name, value); + } + toString() { + const { name, [VALUE]: value } = this; + if (emptyAttributes.has(name) && !value) { + return ignoreCase(this) ? name : `${name}=""`; + } + const escapedValue = (ignoreCase(this) ? value : escape2(value)).replace(QUOTE, """); + return `${name}="${escapedValue}"`; + } + toJSON() { + const json = []; + attrAsJSON(this, json); + return json; + } +}; + +// node_modules/linkedom/esm/shared/node.js +var isConnected = ({ ownerDocument, parentNode }) => { + while (parentNode) { + if (parentNode === ownerDocument) + return true; + parentNode = parentNode.parentNode || parentNode.host; + } + return false; +}; +var parentElement = ({ parentNode }) => { + if (parentNode) { + switch (parentNode.nodeType) { + case DOCUMENT_NODE: + case DOCUMENT_FRAGMENT_NODE: + return null; + } + } + return parentNode; +}; +var previousSibling = ({ [PREV]: prev }) => { + switch (prev ? prev.nodeType : 0) { + case NODE_END: + return prev[START]; + case TEXT_NODE: + case COMMENT_NODE: + case CDATA_SECTION_NODE: + return prev; + } + return null; +}; +var nextSibling = (node) => { + const next = getEnd(node)[NEXT]; + return next && (next.nodeType === NODE_END ? null : next); +}; + +// node_modules/linkedom/esm/mixin/non-document-type-child-node.js +var nextElementSibling2 = (node) => { + let next = nextSibling(node); + while (next && next.nodeType !== ELEMENT_NODE) + next = nextSibling(next); + return next; +}; +var previousElementSibling = (node) => { + let prev = previousSibling(node); + while (prev && prev.nodeType !== ELEMENT_NODE) + prev = previousSibling(prev); + return prev; +}; + +// node_modules/linkedom/esm/mixin/child-node.js +var asFragment = (ownerDocument, nodes) => { + const fragment = ownerDocument.createDocumentFragment(); + fragment.append(...nodes); + return fragment; +}; +var before = (node, nodes) => { + const { ownerDocument, parentNode } = node; + if (parentNode) + parentNode.insertBefore( + asFragment(ownerDocument, nodes), + node + ); +}; +var after = (node, nodes) => { + const { ownerDocument, parentNode } = node; + if (parentNode) + parentNode.insertBefore( + asFragment(ownerDocument, nodes), + getEnd(node)[NEXT] + ); +}; +var replaceWith = (node, nodes) => { + const { ownerDocument, parentNode } = node; + if (parentNode) { + if (nodes.includes(node)) + replaceWith(node, [node = node.cloneNode()]); + parentNode.insertBefore( + asFragment(ownerDocument, nodes), + node + ); + node.remove(); + } +}; +var remove = (prev, current, next) => { + const { parentNode, nodeType } = current; + if (prev || next) { + setAdjacent(prev, next); + current[PREV] = null; + getEnd(current)[NEXT] = null; + } + if (parentNode) { + current.parentNode = null; + moCallback(current, parentNode); + if (nodeType === ELEMENT_NODE) + disconnectedCallback(current); + } +}; + +// node_modules/linkedom/esm/interface/character-data.js +var CharacterData = class extends Node2 { + constructor(ownerDocument, localName, nodeType, data) { + super(ownerDocument, localName, nodeType); + this[VALUE] = $String(data); + } + // <Mixins> + get isConnected() { + return isConnected(this); + } + get parentElement() { + return parentElement(this); + } + get previousSibling() { + return previousSibling(this); + } + get nextSibling() { + return nextSibling(this); + } + get previousElementSibling() { + return previousElementSibling(this); + } + get nextElementSibling() { + return nextElementSibling2(this); + } + before(...nodes) { + before(this, nodes); + } + after(...nodes) { + after(this, nodes); + } + replaceWith(...nodes) { + replaceWith(this, nodes); + } + remove() { + remove(this[PREV], this, this[NEXT]); + } + // </Mixins> + // CharacterData only + /* c8 ignore start */ + get data() { + return this[VALUE]; + } + set data(value) { + this[VALUE] = $String(value); + moCallback(this, this.parentNode); + } + get nodeValue() { + return this.data; + } + set nodeValue(value) { + this.data = value; + } + get textContent() { + return this.data; + } + set textContent(value) { + this.data = value; + } + get length() { + return this.data.length; + } + substringData(offset, count) { + return this.data.substr(offset, count); + } + appendData(data) { + this.data += data; + } + insertData(offset, data) { + const { data: t } = this; + this.data = t.slice(0, offset) + data + t.slice(offset); + } + deleteData(offset, count) { + const { data: t } = this; + this.data = t.slice(0, offset) + t.slice(offset + count); + } + replaceData(offset, count, data) { + const { data: t } = this; + this.data = t.slice(0, offset) + data + t.slice(offset + count); + } + /* c8 ignore stop */ + toJSON() { + const json = []; + characterDataAsJSON(this, json); + return json; + } +}; + +// node_modules/linkedom/esm/interface/cdata-section.js +var CDATASection = class _CDATASection extends CharacterData { + constructor(ownerDocument, data = "") { + super(ownerDocument, "#cdatasection", CDATA_SECTION_NODE, data); + } + cloneNode() { + const { ownerDocument, [VALUE]: data } = this; + return new _CDATASection(ownerDocument, data); + } + toString() { + return `<![CDATA[${this[VALUE]}]]>`; + } +}; + +// node_modules/linkedom/esm/interface/comment.js +var Comment3 = class _Comment extends CharacterData { + constructor(ownerDocument, data = "") { + super(ownerDocument, "#comment", COMMENT_NODE, data); + } + cloneNode() { + const { ownerDocument, [VALUE]: data } = this; + return new _Comment(ownerDocument, data); + } + toString() { + return `<!--${this[VALUE]}-->`; + } +}; + +// node_modules/css-select/lib/esm/index.js +var import_boolbase6 = __toESM(require_boolbase(), 1); + +// node_modules/css-what/lib/es/types.js +var SelectorType; +(function(SelectorType2) { + SelectorType2["Attribute"] = "attribute"; + SelectorType2["Pseudo"] = "pseudo"; + SelectorType2["PseudoElement"] = "pseudo-element"; + SelectorType2["Tag"] = "tag"; + SelectorType2["Universal"] = "universal"; + SelectorType2["Adjacent"] = "adjacent"; + SelectorType2["Child"] = "child"; + SelectorType2["Descendant"] = "descendant"; + SelectorType2["Parent"] = "parent"; + SelectorType2["Sibling"] = "sibling"; + SelectorType2["ColumnCombinator"] = "column-combinator"; +})(SelectorType || (SelectorType = {})); +var AttributeAction; +(function(AttributeAction2) { + AttributeAction2["Any"] = "any"; + AttributeAction2["Element"] = "element"; + AttributeAction2["End"] = "end"; + AttributeAction2["Equals"] = "equals"; + AttributeAction2["Exists"] = "exists"; + AttributeAction2["Hyphen"] = "hyphen"; + AttributeAction2["Not"] = "not"; + AttributeAction2["Start"] = "start"; +})(AttributeAction || (AttributeAction = {})); + +// node_modules/css-what/lib/es/parse.js +var reName = /^[^\\#]?(?:\\(?:[\da-f]{1,6}\s?|.)|[\w\-\u00b0-\uFFFF])+/; +var reEscape = /\\([\da-f]{1,6}\s?|(\s)|.)/gi; +var actionTypes = /* @__PURE__ */ new Map([ + [126, AttributeAction.Element], + [94, AttributeAction.Start], + [36, AttributeAction.End], + [42, AttributeAction.Any], + [33, AttributeAction.Not], + [124, AttributeAction.Hyphen] +]); +var unpackPseudos = /* @__PURE__ */ new Set([ + "has", + "not", + "matches", + "is", + "where", + "host", + "host-context" +]); +function isTraversal(selector) { + switch (selector.type) { + case SelectorType.Adjacent: + case SelectorType.Child: + case SelectorType.Descendant: + case SelectorType.Parent: + case SelectorType.Sibling: + case SelectorType.ColumnCombinator: + return true; + default: + return false; + } +} +var stripQuotesFromPseudos = /* @__PURE__ */ new Set(["contains", "icontains"]); +function funescape(_, escaped, escapedWhitespace) { + const high = parseInt(escaped, 16) - 65536; + return high !== high || escapedWhitespace ? escaped : high < 0 ? ( + // BMP codepoint + String.fromCharCode(high + 65536) + ) : ( + // Supplemental Plane codepoint (surrogate pair) + String.fromCharCode(high >> 10 | 55296, high & 1023 | 56320) + ); +} +function unescapeCSS(str) { + return str.replace(reEscape, funescape); +} +function isQuote(c) { + return c === 39 || c === 34; +} +function isWhitespace2(c) { + return c === 32 || c === 9 || c === 10 || c === 12 || c === 13; +} +function parse(selector) { + const subselects2 = []; + const endIndex = parseSelector(subselects2, `${selector}`, 0); + if (endIndex < selector.length) { + throw new Error(`Unmatched selector: ${selector.slice(endIndex)}`); + } + return subselects2; +} +function parseSelector(subselects2, selector, selectorIndex) { + let tokens = []; + function getName3(offset) { + const match = selector.slice(selectorIndex + offset).match(reName); + if (!match) { + throw new Error(`Expected name, found ${selector.slice(selectorIndex)}`); + } + const [name] = match; + selectorIndex += offset + name.length; + return unescapeCSS(name); + } + function stripWhitespace(offset) { + selectorIndex += offset; + while (selectorIndex < selector.length && isWhitespace2(selector.charCodeAt(selectorIndex))) { + selectorIndex++; + } + } + function readValueWithParenthesis() { + selectorIndex += 1; + const start = selectorIndex; + let counter = 1; + for (; counter > 0 && selectorIndex < selector.length; selectorIndex++) { + if (selector.charCodeAt(selectorIndex) === 40 && !isEscaped(selectorIndex)) { + counter++; + } else if (selector.charCodeAt(selectorIndex) === 41 && !isEscaped(selectorIndex)) { + counter--; + } + } + if (counter) { + throw new Error("Parenthesis not matched"); + } + return unescapeCSS(selector.slice(start, selectorIndex - 1)); + } + function isEscaped(pos) { + let slashCount = 0; + while (selector.charCodeAt(--pos) === 92) + slashCount++; + return (slashCount & 1) === 1; + } + function ensureNotTraversal() { + if (tokens.length > 0 && isTraversal(tokens[tokens.length - 1])) { + throw new Error("Did not expect successive traversals."); + } + } + function addTraversal(type) { + if (tokens.length > 0 && tokens[tokens.length - 1].type === SelectorType.Descendant) { + tokens[tokens.length - 1].type = type; + return; + } + ensureNotTraversal(); + tokens.push({ type }); + } + function addSpecialAttribute(name, action) { + tokens.push({ + type: SelectorType.Attribute, + name, + action, + value: getName3(1), + namespace: null, + ignoreCase: "quirks" + }); + } + function finalizeSubselector() { + if (tokens.length && tokens[tokens.length - 1].type === SelectorType.Descendant) { + tokens.pop(); + } + if (tokens.length === 0) { + throw new Error("Empty sub-selector"); + } + subselects2.push(tokens); + } + stripWhitespace(0); + if (selector.length === selectorIndex) { + return selectorIndex; + } + loop: while (selectorIndex < selector.length) { + const firstChar = selector.charCodeAt(selectorIndex); + switch (firstChar) { + // Whitespace + case 32: + case 9: + case 10: + case 12: + case 13: { + if (tokens.length === 0 || tokens[0].type !== SelectorType.Descendant) { + ensureNotTraversal(); + tokens.push({ type: SelectorType.Descendant }); + } + stripWhitespace(1); + break; + } + // Traversals + case 62: { + addTraversal(SelectorType.Child); + stripWhitespace(1); + break; + } + case 60: { + addTraversal(SelectorType.Parent); + stripWhitespace(1); + break; + } + case 126: { + addTraversal(SelectorType.Sibling); + stripWhitespace(1); + break; + } + case 43: { + addTraversal(SelectorType.Adjacent); + stripWhitespace(1); + break; + } + // Special attribute selectors: .class, #id + case 46: { + addSpecialAttribute("class", AttributeAction.Element); + break; + } + case 35: { + addSpecialAttribute("id", AttributeAction.Equals); + break; + } + case 91: { + stripWhitespace(1); + let name; + let namespace = null; + if (selector.charCodeAt(selectorIndex) === 124) { + name = getName3(1); + } else if (selector.startsWith("*|", selectorIndex)) { + namespace = "*"; + name = getName3(2); + } else { + name = getName3(0); + if (selector.charCodeAt(selectorIndex) === 124 && selector.charCodeAt(selectorIndex + 1) !== 61) { + namespace = name; + name = getName3(1); + } + } + stripWhitespace(0); + let action = AttributeAction.Exists; + const possibleAction = actionTypes.get(selector.charCodeAt(selectorIndex)); + if (possibleAction) { + action = possibleAction; + if (selector.charCodeAt(selectorIndex + 1) !== 61) { + throw new Error("Expected `=`"); + } + stripWhitespace(2); + } else if (selector.charCodeAt(selectorIndex) === 61) { + action = AttributeAction.Equals; + stripWhitespace(1); + } + let value = ""; + let ignoreCase2 = null; + if (action !== "exists") { + if (isQuote(selector.charCodeAt(selectorIndex))) { + const quote = selector.charCodeAt(selectorIndex); + let sectionEnd = selectorIndex + 1; + while (sectionEnd < selector.length && (selector.charCodeAt(sectionEnd) !== quote || isEscaped(sectionEnd))) { + sectionEnd += 1; + } + if (selector.charCodeAt(sectionEnd) !== quote) { + throw new Error("Attribute value didn't end"); + } + value = unescapeCSS(selector.slice(selectorIndex + 1, sectionEnd)); + selectorIndex = sectionEnd + 1; + } else { + const valueStart = selectorIndex; + while (selectorIndex < selector.length && (!isWhitespace2(selector.charCodeAt(selectorIndex)) && selector.charCodeAt(selectorIndex) !== 93 || isEscaped(selectorIndex))) { + selectorIndex += 1; + } + value = unescapeCSS(selector.slice(valueStart, selectorIndex)); + } + stripWhitespace(0); + const forceIgnore = selector.charCodeAt(selectorIndex) | 32; + if (forceIgnore === 115) { + ignoreCase2 = false; + stripWhitespace(1); + } else if (forceIgnore === 105) { + ignoreCase2 = true; + stripWhitespace(1); + } + } + if (selector.charCodeAt(selectorIndex) !== 93) { + throw new Error("Attribute selector didn't terminate"); + } + selectorIndex += 1; + const attributeSelector = { + type: SelectorType.Attribute, + name, + action, + value, + namespace, + ignoreCase: ignoreCase2 + }; + tokens.push(attributeSelector); + break; + } + case 58: { + if (selector.charCodeAt(selectorIndex + 1) === 58) { + tokens.push({ + type: SelectorType.PseudoElement, + name: getName3(2).toLowerCase(), + data: selector.charCodeAt(selectorIndex) === 40 ? readValueWithParenthesis() : null + }); + continue; + } + const name = getName3(1).toLowerCase(); + let data = null; + if (selector.charCodeAt(selectorIndex) === 40) { + if (unpackPseudos.has(name)) { + if (isQuote(selector.charCodeAt(selectorIndex + 1))) { + throw new Error(`Pseudo-selector ${name} cannot be quoted`); + } + data = []; + selectorIndex = parseSelector(data, selector, selectorIndex + 1); + if (selector.charCodeAt(selectorIndex) !== 41) { + throw new Error(`Missing closing parenthesis in :${name} (${selector})`); + } + selectorIndex += 1; + } else { + data = readValueWithParenthesis(); + if (stripQuotesFromPseudos.has(name)) { + const quot = data.charCodeAt(0); + if (quot === data.charCodeAt(data.length - 1) && isQuote(quot)) { + data = data.slice(1, -1); + } + } + data = unescapeCSS(data); + } + } + tokens.push({ type: SelectorType.Pseudo, name, data }); + break; + } + case 44: { + finalizeSubselector(); + tokens = []; + stripWhitespace(1); + break; + } + default: { + if (selector.startsWith("/*", selectorIndex)) { + const endIndex = selector.indexOf("*/", selectorIndex + 2); + if (endIndex < 0) { + throw new Error("Comment was not terminated"); + } + selectorIndex = endIndex + 2; + if (tokens.length === 0) { + stripWhitespace(0); + } + break; + } + let namespace = null; + let name; + if (firstChar === 42) { + selectorIndex += 1; + name = "*"; + } else if (firstChar === 124) { + name = ""; + if (selector.charCodeAt(selectorIndex + 1) === 124) { + addTraversal(SelectorType.ColumnCombinator); + stripWhitespace(2); + break; + } + } else if (reName.test(selector.slice(selectorIndex))) { + name = getName3(0); + } else { + break loop; + } + if (selector.charCodeAt(selectorIndex) === 124 && selector.charCodeAt(selectorIndex + 1) !== 124) { + namespace = name; + if (selector.charCodeAt(selectorIndex + 1) === 42) { + name = "*"; + selectorIndex += 2; + } else { + name = getName3(1); + } + } + tokens.push(name === "*" ? { type: SelectorType.Universal, namespace } : { type: SelectorType.Tag, name, namespace }); + } + } + } + finalizeSubselector(); + return selectorIndex; +} + +// node_modules/css-select/lib/esm/compile.js +var import_boolbase5 = __toESM(require_boolbase(), 1); + +// node_modules/css-select/lib/esm/sort.js +var procedure = /* @__PURE__ */ new Map([ + [SelectorType.Universal, 50], + [SelectorType.Tag, 30], + [SelectorType.Attribute, 1], + [SelectorType.Pseudo, 0] +]); +function isTraversal2(token) { + return !procedure.has(token.type); +} +var attributes = /* @__PURE__ */ new Map([ + [AttributeAction.Exists, 10], + [AttributeAction.Equals, 8], + [AttributeAction.Not, 7], + [AttributeAction.Start, 6], + [AttributeAction.End, 6], + [AttributeAction.Any, 5] +]); +function sortByProcedure(arr) { + const procs = arr.map(getProcedure); + for (let i = 1; i < arr.length; i++) { + const procNew = procs[i]; + if (procNew < 0) + continue; + for (let j = i - 1; j >= 0 && procNew < procs[j]; j--) { + const token = arr[j + 1]; + arr[j + 1] = arr[j]; + arr[j] = token; + procs[j + 1] = procs[j]; + procs[j] = procNew; + } + } +} +function getProcedure(token) { + var _a2, _b; + let proc = (_a2 = procedure.get(token.type)) !== null && _a2 !== void 0 ? _a2 : -1; + if (token.type === SelectorType.Attribute) { + proc = (_b = attributes.get(token.action)) !== null && _b !== void 0 ? _b : 4; + if (token.action === AttributeAction.Equals && token.name === "id") { + proc = 9; + } + if (token.ignoreCase) { + proc >>= 1; + } + } else if (token.type === SelectorType.Pseudo) { + if (!token.data) { + proc = 3; + } else if (token.name === "has" || token.name === "contains") { + proc = 0; + } else if (Array.isArray(token.data)) { + proc = Math.min(...token.data.map((d) => Math.min(...d.map(getProcedure)))); + if (proc < 0) { + proc = 0; + } + } else { + proc = 2; + } + } + return proc; +} + +// node_modules/css-select/lib/esm/attributes.js +var import_boolbase = __toESM(require_boolbase(), 1); +var reChars = /[-[\]{}()*+?.,\\^$|#\s]/g; +function escapeRegex(value) { + return value.replace(reChars, "\\$&"); +} +var caseInsensitiveAttributes = /* @__PURE__ */ new Set([ + "accept", + "accept-charset", + "align", + "alink", + "axis", + "bgcolor", + "charset", + "checked", + "clear", + "codetype", + "color", + "compact", + "declare", + "defer", + "dir", + "direction", + "disabled", + "enctype", + "face", + "frame", + "hreflang", + "http-equiv", + "lang", + "language", + "link", + "media", + "method", + "multiple", + "nohref", + "noresize", + "noshade", + "nowrap", + "readonly", + "rel", + "rev", + "rules", + "scope", + "scrolling", + "selected", + "shape", + "target", + "text", + "type", + "valign", + "valuetype", + "vlink" +]); +function shouldIgnoreCase(selector, options) { + return typeof selector.ignoreCase === "boolean" ? selector.ignoreCase : selector.ignoreCase === "quirks" ? !!options.quirksMode : !options.xmlMode && caseInsensitiveAttributes.has(selector.name); +} +var attributeRules = { + equals(next, data, options) { + const { adapter: adapter2 } = options; + const { name } = data; + let { value } = data; + if (shouldIgnoreCase(data, options)) { + value = value.toLowerCase(); + return (elem) => { + const attr = adapter2.getAttributeValue(elem, name); + return attr != null && attr.length === value.length && attr.toLowerCase() === value && next(elem); + }; + } + return (elem) => adapter2.getAttributeValue(elem, name) === value && next(elem); + }, + hyphen(next, data, options) { + const { adapter: adapter2 } = options; + const { name } = data; + let { value } = data; + const len = value.length; + if (shouldIgnoreCase(data, options)) { + value = value.toLowerCase(); + return function hyphenIC(elem) { + const attr = adapter2.getAttributeValue(elem, name); + return attr != null && (attr.length === len || attr.charAt(len) === "-") && attr.substr(0, len).toLowerCase() === value && next(elem); + }; + } + return function hyphen(elem) { + const attr = adapter2.getAttributeValue(elem, name); + return attr != null && (attr.length === len || attr.charAt(len) === "-") && attr.substr(0, len) === value && next(elem); + }; + }, + element(next, data, options) { + const { adapter: adapter2 } = options; + const { name, value } = data; + if (/\s/.test(value)) { + return import_boolbase.default.falseFunc; + } + const regex = new RegExp(`(?:^|\\s)${escapeRegex(value)}(?:$|\\s)`, shouldIgnoreCase(data, options) ? "i" : ""); + return function element(elem) { + const attr = adapter2.getAttributeValue(elem, name); + return attr != null && attr.length >= value.length && regex.test(attr) && next(elem); + }; + }, + exists(next, { name }, { adapter: adapter2 }) { + return (elem) => adapter2.hasAttrib(elem, name) && next(elem); + }, + start(next, data, options) { + const { adapter: adapter2 } = options; + const { name } = data; + let { value } = data; + const len = value.length; + if (len === 0) { + return import_boolbase.default.falseFunc; + } + if (shouldIgnoreCase(data, options)) { + value = value.toLowerCase(); + return (elem) => { + const attr = adapter2.getAttributeValue(elem, name); + return attr != null && attr.length >= len && attr.substr(0, len).toLowerCase() === value && next(elem); + }; + } + return (elem) => { + var _a2; + return !!((_a2 = adapter2.getAttributeValue(elem, name)) === null || _a2 === void 0 ? void 0 : _a2.startsWith(value)) && next(elem); + }; + }, + end(next, data, options) { + const { adapter: adapter2 } = options; + const { name } = data; + let { value } = data; + const len = -value.length; + if (len === 0) { + return import_boolbase.default.falseFunc; + } + if (shouldIgnoreCase(data, options)) { + value = value.toLowerCase(); + return (elem) => { + var _a2; + return ((_a2 = adapter2.getAttributeValue(elem, name)) === null || _a2 === void 0 ? void 0 : _a2.substr(len).toLowerCase()) === value && next(elem); + }; + } + return (elem) => { + var _a2; + return !!((_a2 = adapter2.getAttributeValue(elem, name)) === null || _a2 === void 0 ? void 0 : _a2.endsWith(value)) && next(elem); + }; + }, + any(next, data, options) { + const { adapter: adapter2 } = options; + const { name, value } = data; + if (value === "") { + return import_boolbase.default.falseFunc; + } + if (shouldIgnoreCase(data, options)) { + const regex = new RegExp(escapeRegex(value), "i"); + return function anyIC(elem) { + const attr = adapter2.getAttributeValue(elem, name); + return attr != null && attr.length >= value.length && regex.test(attr) && next(elem); + }; + } + return (elem) => { + var _a2; + return !!((_a2 = adapter2.getAttributeValue(elem, name)) === null || _a2 === void 0 ? void 0 : _a2.includes(value)) && next(elem); + }; + }, + not(next, data, options) { + const { adapter: adapter2 } = options; + const { name } = data; + let { value } = data; + if (value === "") { + return (elem) => !!adapter2.getAttributeValue(elem, name) && next(elem); + } else if (shouldIgnoreCase(data, options)) { + value = value.toLowerCase(); + return (elem) => { + const attr = adapter2.getAttributeValue(elem, name); + return (attr == null || attr.length !== value.length || attr.toLowerCase() !== value) && next(elem); + }; + } + return (elem) => adapter2.getAttributeValue(elem, name) !== value && next(elem); + } +}; + +// node_modules/nth-check/lib/esm/parse.js +var whitespace = /* @__PURE__ */ new Set([9, 10, 12, 13, 32]); +var ZERO = "0".charCodeAt(0); +var NINE = "9".charCodeAt(0); +function parse2(formula) { + formula = formula.trim().toLowerCase(); + if (formula === "even") { + return [2, 0]; + } else if (formula === "odd") { + return [2, 1]; + } + let idx = 0; + let a = 0; + let sign = readSign(); + let number = readNumber(); + if (idx < formula.length && formula.charAt(idx) === "n") { + idx++; + a = sign * (number !== null && number !== void 0 ? number : 1); + skipWhitespace(); + if (idx < formula.length) { + sign = readSign(); + skipWhitespace(); + number = readNumber(); + } else { + sign = number = 0; + } + } + if (number === null || idx < formula.length) { + throw new Error(`n-th rule couldn't be parsed ('${formula}')`); + } + return [a, sign * number]; + function readSign() { + if (formula.charAt(idx) === "-") { + idx++; + return -1; + } + if (formula.charAt(idx) === "+") { + idx++; + } + return 1; + } + function readNumber() { + const start = idx; + let value = 0; + while (idx < formula.length && formula.charCodeAt(idx) >= ZERO && formula.charCodeAt(idx) <= NINE) { + value = value * 10 + (formula.charCodeAt(idx) - ZERO); + idx++; + } + return idx === start ? null : value; + } + function skipWhitespace() { + while (idx < formula.length && whitespace.has(formula.charCodeAt(idx))) { + idx++; + } + } +} + +// node_modules/nth-check/lib/esm/compile.js +var import_boolbase2 = __toESM(require_boolbase(), 1); +function compile(parsed) { + const a = parsed[0]; + const b = parsed[1] - 1; + if (b < 0 && a <= 0) + return import_boolbase2.default.falseFunc; + if (a === -1) + return (index) => index <= b; + if (a === 0) + return (index) => index === b; + if (a === 1) + return b < 0 ? import_boolbase2.default.trueFunc : (index) => index >= b; + const absA = Math.abs(a); + const bMod = (b % absA + absA) % absA; + return a > 1 ? (index) => index >= b && index % absA === bMod : (index) => index <= b && index % absA === bMod; +} + +// node_modules/nth-check/lib/esm/index.js +function nthCheck(formula) { + return compile(parse2(formula)); +} + +// node_modules/css-select/lib/esm/pseudo-selectors/filters.js +var import_boolbase3 = __toESM(require_boolbase(), 1); +function getChildFunc(next, adapter2) { + return (elem) => { + const parent = adapter2.getParent(elem); + return parent != null && adapter2.isTag(parent) && next(elem); + }; +} +var filters = { + contains(next, text, { adapter: adapter2 }) { + return function contains(elem) { + return next(elem) && adapter2.getText(elem).includes(text); + }; + }, + icontains(next, text, { adapter: adapter2 }) { + const itext = text.toLowerCase(); + return function icontains(elem) { + return next(elem) && adapter2.getText(elem).toLowerCase().includes(itext); + }; + }, + // Location specific methods + "nth-child"(next, rule, { adapter: adapter2, equals }) { + const func = nthCheck(rule); + if (func === import_boolbase3.default.falseFunc) + return import_boolbase3.default.falseFunc; + if (func === import_boolbase3.default.trueFunc) + return getChildFunc(next, adapter2); + return function nthChild(elem) { + const siblings = adapter2.getSiblings(elem); + let pos = 0; + for (let i = 0; i < siblings.length; i++) { + if (equals(elem, siblings[i])) + break; + if (adapter2.isTag(siblings[i])) { + pos++; + } + } + return func(pos) && next(elem); + }; + }, + "nth-last-child"(next, rule, { adapter: adapter2, equals }) { + const func = nthCheck(rule); + if (func === import_boolbase3.default.falseFunc) + return import_boolbase3.default.falseFunc; + if (func === import_boolbase3.default.trueFunc) + return getChildFunc(next, adapter2); + return function nthLastChild(elem) { + const siblings = adapter2.getSiblings(elem); + let pos = 0; + for (let i = siblings.length - 1; i >= 0; i--) { + if (equals(elem, siblings[i])) + break; + if (adapter2.isTag(siblings[i])) { + pos++; + } + } + return func(pos) && next(elem); + }; + }, + "nth-of-type"(next, rule, { adapter: adapter2, equals }) { + const func = nthCheck(rule); + if (func === import_boolbase3.default.falseFunc) + return import_boolbase3.default.falseFunc; + if (func === import_boolbase3.default.trueFunc) + return getChildFunc(next, adapter2); + return function nthOfType(elem) { + const siblings = adapter2.getSiblings(elem); + let pos = 0; + for (let i = 0; i < siblings.length; i++) { + const currentSibling = siblings[i]; + if (equals(elem, currentSibling)) + break; + if (adapter2.isTag(currentSibling) && adapter2.getName(currentSibling) === adapter2.getName(elem)) { + pos++; + } + } + return func(pos) && next(elem); + }; + }, + "nth-last-of-type"(next, rule, { adapter: adapter2, equals }) { + const func = nthCheck(rule); + if (func === import_boolbase3.default.falseFunc) + return import_boolbase3.default.falseFunc; + if (func === import_boolbase3.default.trueFunc) + return getChildFunc(next, adapter2); + return function nthLastOfType(elem) { + const siblings = adapter2.getSiblings(elem); + let pos = 0; + for (let i = siblings.length - 1; i >= 0; i--) { + const currentSibling = siblings[i]; + if (equals(elem, currentSibling)) + break; + if (adapter2.isTag(currentSibling) && adapter2.getName(currentSibling) === adapter2.getName(elem)) { + pos++; + } + } + return func(pos) && next(elem); + }; + }, + // TODO determine the actual root element + root(next, _rule, { adapter: adapter2 }) { + return (elem) => { + const parent = adapter2.getParent(elem); + return (parent == null || !adapter2.isTag(parent)) && next(elem); + }; + }, + scope(next, rule, options, context) { + const { equals } = options; + if (!context || context.length === 0) { + return filters["root"](next, rule, options); + } + if (context.length === 1) { + return (elem) => equals(context[0], elem) && next(elem); + } + return (elem) => context.includes(elem) && next(elem); + }, + hover: dynamicStatePseudo("isHovered"), + visited: dynamicStatePseudo("isVisited"), + active: dynamicStatePseudo("isActive") +}; +function dynamicStatePseudo(name) { + return function dynamicPseudo(next, _rule, { adapter: adapter2 }) { + const func = adapter2[name]; + if (typeof func !== "function") { + return import_boolbase3.default.falseFunc; + } + return function active(elem) { + return func(elem) && next(elem); + }; + }; +} + +// node_modules/css-select/lib/esm/pseudo-selectors/pseudos.js +var pseudos = { + empty(elem, { adapter: adapter2 }) { + return !adapter2.getChildren(elem).some((elem2) => ( + // FIXME: `getText` call is potentially expensive. + adapter2.isTag(elem2) || adapter2.getText(elem2) !== "" + )); + }, + "first-child"(elem, { adapter: adapter2, equals }) { + if (adapter2.prevElementSibling) { + return adapter2.prevElementSibling(elem) == null; + } + const firstChild = adapter2.getSiblings(elem).find((elem2) => adapter2.isTag(elem2)); + return firstChild != null && equals(elem, firstChild); + }, + "last-child"(elem, { adapter: adapter2, equals }) { + const siblings = adapter2.getSiblings(elem); + for (let i = siblings.length - 1; i >= 0; i--) { + if (equals(elem, siblings[i])) + return true; + if (adapter2.isTag(siblings[i])) + break; + } + return false; + }, + "first-of-type"(elem, { adapter: adapter2, equals }) { + const siblings = adapter2.getSiblings(elem); + const elemName = adapter2.getName(elem); + for (let i = 0; i < siblings.length; i++) { + const currentSibling = siblings[i]; + if (equals(elem, currentSibling)) + return true; + if (adapter2.isTag(currentSibling) && adapter2.getName(currentSibling) === elemName) { + break; + } + } + return false; + }, + "last-of-type"(elem, { adapter: adapter2, equals }) { + const siblings = adapter2.getSiblings(elem); + const elemName = adapter2.getName(elem); + for (let i = siblings.length - 1; i >= 0; i--) { + const currentSibling = siblings[i]; + if (equals(elem, currentSibling)) + return true; + if (adapter2.isTag(currentSibling) && adapter2.getName(currentSibling) === elemName) { + break; + } + } + return false; + }, + "only-of-type"(elem, { adapter: adapter2, equals }) { + const elemName = adapter2.getName(elem); + return adapter2.getSiblings(elem).every((sibling) => equals(elem, sibling) || !adapter2.isTag(sibling) || adapter2.getName(sibling) !== elemName); + }, + "only-child"(elem, { adapter: adapter2, equals }) { + return adapter2.getSiblings(elem).every((sibling) => equals(elem, sibling) || !adapter2.isTag(sibling)); + } +}; +function verifyPseudoArgs(func, name, subselect, argIndex) { + if (subselect === null) { + if (func.length > argIndex) { + throw new Error(`Pseudo-class :${name} requires an argument`); + } + } else if (func.length === argIndex) { + throw new Error(`Pseudo-class :${name} doesn't have any arguments`); + } +} + +// node_modules/css-select/lib/esm/pseudo-selectors/aliases.js +var aliases = { + // Links + "any-link": ":is(a, area, link)[href]", + link: ":any-link:not(:visited)", + // Forms + // https://html.spec.whatwg.org/multipage/scripting.html#disabled-elements + disabled: `:is( + :is(button, input, select, textarea, optgroup, option)[disabled], + optgroup[disabled] > option, + fieldset[disabled]:not(fieldset[disabled] legend:first-of-type *) + )`, + enabled: ":not(:disabled)", + checked: ":is(:is(input[type=radio], input[type=checkbox])[checked], option:selected)", + required: ":is(input, select, textarea)[required]", + optional: ":is(input, select, textarea):not([required])", + // JQuery extensions + // https://html.spec.whatwg.org/multipage/form-elements.html#concept-option-selectedness + selected: "option:is([selected], select:not([multiple]):not(:has(> option[selected])) > :first-of-type)", + checkbox: "[type=checkbox]", + file: "[type=file]", + password: "[type=password]", + radio: "[type=radio]", + reset: "[type=reset]", + image: "[type=image]", + submit: "[type=submit]", + parent: ":not(:empty)", + header: ":is(h1, h2, h3, h4, h5, h6)", + button: ":is(button, input[type=button])", + input: ":is(input, textarea, select, button)", + text: "input:is(:not([type!='']), [type=text])" +}; + +// node_modules/css-select/lib/esm/pseudo-selectors/subselects.js +var import_boolbase4 = __toESM(require_boolbase(), 1); +var PLACEHOLDER_ELEMENT = {}; +function ensureIsTag(next, adapter2) { + if (next === import_boolbase4.default.falseFunc) + return import_boolbase4.default.falseFunc; + return (elem) => adapter2.isTag(elem) && next(elem); +} +function getNextSiblings(elem, adapter2) { + const siblings = adapter2.getSiblings(elem); + if (siblings.length <= 1) + return []; + const elemIndex = siblings.indexOf(elem); + if (elemIndex < 0 || elemIndex === siblings.length - 1) + return []; + return siblings.slice(elemIndex + 1).filter(adapter2.isTag); +} +function copyOptions(options) { + return { + xmlMode: !!options.xmlMode, + lowerCaseAttributeNames: !!options.lowerCaseAttributeNames, + lowerCaseTags: !!options.lowerCaseTags, + quirksMode: !!options.quirksMode, + cacheResults: !!options.cacheResults, + pseudos: options.pseudos, + adapter: options.adapter, + equals: options.equals + }; +} +var is = (next, token, options, context, compileToken2) => { + const func = compileToken2(token, copyOptions(options), context); + return func === import_boolbase4.default.trueFunc ? next : func === import_boolbase4.default.falseFunc ? import_boolbase4.default.falseFunc : (elem) => func(elem) && next(elem); +}; +var subselects = { + is, + /** + * `:matches` and `:where` are aliases for `:is`. + */ + matches: is, + where: is, + not(next, token, options, context, compileToken2) { + const func = compileToken2(token, copyOptions(options), context); + return func === import_boolbase4.default.falseFunc ? next : func === import_boolbase4.default.trueFunc ? import_boolbase4.default.falseFunc : (elem) => !func(elem) && next(elem); + }, + has(next, subselect, options, _context, compileToken2) { + const { adapter: adapter2 } = options; + const opts = copyOptions(options); + opts.relativeSelector = true; + const context = subselect.some((s) => s.some(isTraversal2)) ? ( + // Used as a placeholder. Will be replaced with the actual element. + [PLACEHOLDER_ELEMENT] + ) : void 0; + const compiled = compileToken2(subselect, opts, context); + if (compiled === import_boolbase4.default.falseFunc) + return import_boolbase4.default.falseFunc; + const hasElement = ensureIsTag(compiled, adapter2); + if (context && compiled !== import_boolbase4.default.trueFunc) { + const { shouldTestNextSiblings = false } = compiled; + return (elem) => { + if (!next(elem)) + return false; + context[0] = elem; + const childs = adapter2.getChildren(elem); + const nextElements = shouldTestNextSiblings ? [...childs, ...getNextSiblings(elem, adapter2)] : childs; + return adapter2.existsOne(hasElement, nextElements); + }; + } + return (elem) => next(elem) && adapter2.existsOne(hasElement, adapter2.getChildren(elem)); + } +}; + +// node_modules/css-select/lib/esm/pseudo-selectors/index.js +function compilePseudoSelector(next, selector, options, context, compileToken2) { + var _a2; + const { name, data } = selector; + if (Array.isArray(data)) { + if (!(name in subselects)) { + throw new Error(`Unknown pseudo-class :${name}(${data})`); + } + return subselects[name](next, data, options, context, compileToken2); + } + const userPseudo = (_a2 = options.pseudos) === null || _a2 === void 0 ? void 0 : _a2[name]; + const stringPseudo = typeof userPseudo === "string" ? userPseudo : aliases[name]; + if (typeof stringPseudo === "string") { + if (data != null) { + throw new Error(`Pseudo ${name} doesn't have any arguments`); + } + const alias = parse(stringPseudo); + return subselects["is"](next, alias, options, context, compileToken2); + } + if (typeof userPseudo === "function") { + verifyPseudoArgs(userPseudo, name, data, 1); + return (elem) => userPseudo(elem, data) && next(elem); + } + if (name in filters) { + return filters[name](next, data, options, context); + } + if (name in pseudos) { + const pseudo = pseudos[name]; + verifyPseudoArgs(pseudo, name, data, 2); + return (elem) => pseudo(elem, options, data) && next(elem); + } + throw new Error(`Unknown pseudo-class :${name}`); +} + +// node_modules/css-select/lib/esm/general.js +function getElementParent(node, adapter2) { + const parent = adapter2.getParent(node); + if (parent && adapter2.isTag(parent)) { + return parent; + } + return null; +} +function compileGeneralSelector(next, selector, options, context, compileToken2) { + const { adapter: adapter2, equals } = options; + switch (selector.type) { + case SelectorType.PseudoElement: { + throw new Error("Pseudo-elements are not supported by css-select"); + } + case SelectorType.ColumnCombinator: { + throw new Error("Column combinators are not yet supported by css-select"); + } + case SelectorType.Attribute: { + if (selector.namespace != null) { + throw new Error("Namespaced attributes are not yet supported by css-select"); + } + if (!options.xmlMode || options.lowerCaseAttributeNames) { + selector.name = selector.name.toLowerCase(); + } + return attributeRules[selector.action](next, selector, options); + } + case SelectorType.Pseudo: { + return compilePseudoSelector(next, selector, options, context, compileToken2); + } + // Tags + case SelectorType.Tag: { + if (selector.namespace != null) { + throw new Error("Namespaced tag names are not yet supported by css-select"); + } + let { name } = selector; + if (!options.xmlMode || options.lowerCaseTags) { + name = name.toLowerCase(); + } + return function tag(elem) { + return adapter2.getName(elem) === name && next(elem); + }; + } + // Traversal + case SelectorType.Descendant: { + if (options.cacheResults === false || typeof WeakSet === "undefined") { + return function descendant(elem) { + let current = elem; + while (current = getElementParent(current, adapter2)) { + if (next(current)) { + return true; + } + } + return false; + }; + } + const isFalseCache = /* @__PURE__ */ new WeakSet(); + return function cachedDescendant(elem) { + let current = elem; + while (current = getElementParent(current, adapter2)) { + if (!isFalseCache.has(current)) { + if (adapter2.isTag(current) && next(current)) { + return true; + } + isFalseCache.add(current); + } + } + return false; + }; + } + case "_flexibleDescendant": { + return function flexibleDescendant(elem) { + let current = elem; + do { + if (next(current)) + return true; + } while (current = getElementParent(current, adapter2)); + return false; + }; + } + case SelectorType.Parent: { + return function parent(elem) { + return adapter2.getChildren(elem).some((elem2) => adapter2.isTag(elem2) && next(elem2)); + }; + } + case SelectorType.Child: { + return function child(elem) { + const parent = adapter2.getParent(elem); + return parent != null && adapter2.isTag(parent) && next(parent); + }; + } + case SelectorType.Sibling: { + return function sibling(elem) { + const siblings = adapter2.getSiblings(elem); + for (let i = 0; i < siblings.length; i++) { + const currentSibling = siblings[i]; + if (equals(elem, currentSibling)) + break; + if (adapter2.isTag(currentSibling) && next(currentSibling)) { + return true; + } + } + return false; + }; + } + case SelectorType.Adjacent: { + if (adapter2.prevElementSibling) { + return function adjacent(elem) { + const previous = adapter2.prevElementSibling(elem); + return previous != null && next(previous); + }; + } + return function adjacent(elem) { + const siblings = adapter2.getSiblings(elem); + let lastElement; + for (let i = 0; i < siblings.length; i++) { + const currentSibling = siblings[i]; + if (equals(elem, currentSibling)) + break; + if (adapter2.isTag(currentSibling)) { + lastElement = currentSibling; + } + } + return !!lastElement && next(lastElement); + }; + } + case SelectorType.Universal: { + if (selector.namespace != null && selector.namespace !== "*") { + throw new Error("Namespaced universal selectors are not yet supported by css-select"); + } + return next; + } + } +} + +// node_modules/css-select/lib/esm/compile.js +function compile2(selector, options, context) { + const next = compileUnsafe(selector, options, context); + return ensureIsTag(next, options.adapter); +} +function compileUnsafe(selector, options, context) { + const token = typeof selector === "string" ? parse(selector) : selector; + return compileToken(token, options, context); +} +function includesScopePseudo(t) { + return t.type === SelectorType.Pseudo && (t.name === "scope" || Array.isArray(t.data) && t.data.some((data) => data.some(includesScopePseudo))); +} +var DESCENDANT_TOKEN = { type: SelectorType.Descendant }; +var FLEXIBLE_DESCENDANT_TOKEN = { + type: "_flexibleDescendant" +}; +var SCOPE_TOKEN = { + type: SelectorType.Pseudo, + name: "scope", + data: null +}; +function absolutize(token, { adapter: adapter2 }, context) { + const hasContext = !!(context === null || context === void 0 ? void 0 : context.every((e) => { + const parent = adapter2.isTag(e) && adapter2.getParent(e); + return e === PLACEHOLDER_ELEMENT || parent && adapter2.isTag(parent); + })); + for (const t of token) { + if (t.length > 0 && isTraversal2(t[0]) && t[0].type !== SelectorType.Descendant) { + } else if (hasContext && !t.some(includesScopePseudo)) { + t.unshift(DESCENDANT_TOKEN); + } else { + continue; + } + t.unshift(SCOPE_TOKEN); + } +} +function compileToken(token, options, context) { + var _a2; + token.forEach(sortByProcedure); + context = (_a2 = options.context) !== null && _a2 !== void 0 ? _a2 : context; + const isArrayContext = Array.isArray(context); + const finalContext = context && (Array.isArray(context) ? context : [context]); + if (options.relativeSelector !== false) { + absolutize(token, options, finalContext); + } else if (token.some((t) => t.length > 0 && isTraversal2(t[0]))) { + throw new Error("Relative selectors are not allowed when the `relativeSelector` option is disabled"); + } + let shouldTestNextSiblings = false; + const query2 = token.map((rules) => { + if (rules.length >= 2) { + const [first, second] = rules; + if (first.type !== SelectorType.Pseudo || first.name !== "scope") { + } else if (isArrayContext && second.type === SelectorType.Descendant) { + rules[1] = FLEXIBLE_DESCENDANT_TOKEN; + } else if (second.type === SelectorType.Adjacent || second.type === SelectorType.Sibling) { + shouldTestNextSiblings = true; + } + } + return compileRules(rules, options, finalContext); + }).reduce(reduceRules, import_boolbase5.default.falseFunc); + query2.shouldTestNextSiblings = shouldTestNextSiblings; + return query2; +} +function compileRules(rules, options, context) { + var _a2; + return rules.reduce((previous, rule) => previous === import_boolbase5.default.falseFunc ? import_boolbase5.default.falseFunc : compileGeneralSelector(previous, rule, options, context, compileToken), (_a2 = options.rootFunc) !== null && _a2 !== void 0 ? _a2 : import_boolbase5.default.trueFunc); +} +function reduceRules(a, b) { + if (b === import_boolbase5.default.falseFunc || a === import_boolbase5.default.trueFunc) { + return a; + } + if (a === import_boolbase5.default.falseFunc || b === import_boolbase5.default.trueFunc) { + return b; + } + return function combine(elem) { + return a(elem) || b(elem); + }; +} + +// node_modules/css-select/lib/esm/index.js +var defaultEquals = (a, b) => a === b; +var defaultOptions = { + adapter: esm_exports2, + equals: defaultEquals +}; +function convertOptionFormats(options) { + var _a2, _b, _c, _d; + const opts = options !== null && options !== void 0 ? options : defaultOptions; + (_a2 = opts.adapter) !== null && _a2 !== void 0 ? _a2 : opts.adapter = esm_exports2; + (_b = opts.equals) !== null && _b !== void 0 ? _b : opts.equals = (_d = (_c = opts.adapter) === null || _c === void 0 ? void 0 : _c.equals) !== null && _d !== void 0 ? _d : defaultEquals; + return opts; +} +function wrapCompile(func) { + return function addAdapter(selector, options, context) { + const opts = convertOptionFormats(options); + return func(selector, opts, context); + }; +} +var compile3 = wrapCompile(compile2); +var _compileUnsafe = wrapCompile(compileUnsafe); +var _compileToken = wrapCompile(compileToken); +function getSelectorFunc(searchFunc) { + return function select(query2, elements, options) { + const opts = convertOptionFormats(options); + if (typeof query2 !== "function") { + query2 = compileUnsafe(query2, opts, elements); + } + const filteredElements = prepareContext(elements, opts.adapter, query2.shouldTestNextSiblings); + return searchFunc(query2, filteredElements, opts); + }; +} +function prepareContext(elems, adapter2, shouldTestNextSiblings = false) { + if (shouldTestNextSiblings) { + elems = appendNextSiblings(elems, adapter2); + } + return Array.isArray(elems) ? adapter2.removeSubsets(elems) : adapter2.getChildren(elems); +} +function appendNextSiblings(elem, adapter2) { + const elems = Array.isArray(elem) ? elem.slice(0) : [elem]; + const elemsLength = elems.length; + for (let i = 0; i < elemsLength; i++) { + const nextSiblings = getNextSiblings(elems[i], adapter2); + elems.push(...nextSiblings); + } + return elems; +} +var selectAll = getSelectorFunc((query2, elems, options) => query2 === import_boolbase6.default.falseFunc || !elems || elems.length === 0 ? [] : options.adapter.findAll(query2, elems)); +var selectOne = getSelectorFunc((query2, elems, options) => query2 === import_boolbase6.default.falseFunc || !elems || elems.length === 0 ? null : options.adapter.findOne(query2, elems)); +function is2(elem, query2, options) { + const opts = convertOptionFormats(options); + return (typeof query2 === "function" ? query2 : compile2(query2, opts))(elem); +} + +// node_modules/linkedom/esm/shared/matches.js +var { isArray } = Array; +var isTag3 = ({ nodeType }) => nodeType === ELEMENT_NODE; +var existsOne2 = (test, elements) => elements.some( + (element) => isTag3(element) && (test(element) || existsOne2(test, getChildren2(element))) +); +var getAttributeValue2 = (element, name) => name === "class" ? element.classList.value : element.getAttribute(name); +var getChildren2 = ({ childNodes }) => childNodes; +var getName2 = (element) => { + const { localName } = element; + return ignoreCase(element) ? localName.toLowerCase() : localName; +}; +var getParent2 = ({ parentNode }) => parentNode; +var getSiblings2 = (element) => { + const { parentNode } = element; + return parentNode ? getChildren2(parentNode) : element; +}; +var getText2 = (node) => { + if (isArray(node)) + return node.map(getText2).join(""); + if (isTag3(node)) + return getText2(getChildren2(node)); + if (node.nodeType === TEXT_NODE) + return node.data; + return ""; +}; +var hasAttrib2 = (element, name) => element.hasAttribute(name); +var removeSubsets2 = (nodes) => { + let { length } = nodes; + while (length--) { + const node = nodes[length]; + if (length && -1 < nodes.lastIndexOf(node, length - 1)) { + nodes.splice(length, 1); + continue; + } + for (let { parentNode } = node; parentNode; parentNode = parentNode.parentNode) { + if (nodes.includes(parentNode)) { + nodes.splice(length, 1); + break; + } + } + } + return nodes; +}; +var findAll2 = (test, nodes) => { + const matches2 = []; + for (const node of nodes) { + if (isTag3(node)) { + if (test(node)) + matches2.push(node); + matches2.push(...findAll2(test, getChildren2(node))); + } + } + return matches2; +}; +var findOne2 = (test, nodes) => { + for (let node of nodes) + if (test(node) || (node = findOne2(test, getChildren2(node)))) + return node; + return null; +}; +var adapter = { + isTag: isTag3, + existsOne: existsOne2, + getAttributeValue: getAttributeValue2, + getChildren: getChildren2, + getName: getName2, + getParent: getParent2, + getSiblings: getSiblings2, + getText: getText2, + hasAttrib: hasAttrib2, + removeSubsets: removeSubsets2, + findAll: findAll2, + findOne: findOne2 +}; +var prepareMatch = (element, selectors) => compile3( + selectors, + { + context: selectors.includes(":scope") ? element : void 0, + xmlMode: !ignoreCase(element), + adapter + } +); +var matches = (element, selectors) => is2( + element, + selectors, + { + strict: true, + context: selectors.includes(":scope") ? element : void 0, + xmlMode: !ignoreCase(element), + adapter + } +); + +// node_modules/linkedom/esm/interface/text.js +var Text3 = class _Text extends CharacterData { + constructor(ownerDocument, data = "") { + super(ownerDocument, "#text", TEXT_NODE, data); + } + get wholeText() { + const text = []; + let { previousSibling: previousSibling2, nextSibling: nextSibling2 } = this; + while (previousSibling2) { + if (previousSibling2.nodeType === TEXT_NODE) + text.unshift(previousSibling2[VALUE]); + else + break; + previousSibling2 = previousSibling2.previousSibling; + } + text.push(this[VALUE]); + while (nextSibling2) { + if (nextSibling2.nodeType === TEXT_NODE) + text.push(nextSibling2[VALUE]); + else + break; + nextSibling2 = nextSibling2.nextSibling; + } + return text.join(""); + } + cloneNode() { + const { ownerDocument, [VALUE]: data } = this; + return new _Text(ownerDocument, data); + } + toString() { + return escape2(this[VALUE]); + } +}; + +// node_modules/linkedom/esm/mixin/parent-node.js +var isNode = (node) => node instanceof Node2; +var insert = (parentNode, child, nodes) => { + const { ownerDocument } = parentNode; + for (const node of nodes) + parentNode.insertBefore( + isNode(node) ? node : new Text3(ownerDocument, node), + child + ); +}; +var ParentNode = class extends Node2 { + constructor(ownerDocument, localName, nodeType) { + super(ownerDocument, localName, nodeType); + this[PRIVATE] = null; + this[NEXT] = this[END] = { + [NEXT]: null, + [PREV]: this, + [START]: this, + nodeType: NODE_END, + ownerDocument: this.ownerDocument, + parentNode: null + }; + } + get childNodes() { + const childNodes = new NodeList(); + let { firstChild } = this; + while (firstChild) { + childNodes.push(firstChild); + firstChild = nextSibling(firstChild); + } + return childNodes; + } + get children() { + const children = new NodeList(); + let { firstElementChild } = this; + while (firstElementChild) { + children.push(firstElementChild); + firstElementChild = nextElementSibling2(firstElementChild); + } + return children; + } + /** + * @returns {NodeStruct | null} + */ + get firstChild() { + let { [NEXT]: next, [END]: end } = this; + while (next.nodeType === ATTRIBUTE_NODE) + next = next[NEXT]; + return next === end ? null : next; + } + /** + * @returns {NodeStruct | null} + */ + get firstElementChild() { + let { firstChild } = this; + while (firstChild) { + if (firstChild.nodeType === ELEMENT_NODE) + return firstChild; + firstChild = nextSibling(firstChild); + } + return null; + } + get lastChild() { + const prev = this[END][PREV]; + switch (prev.nodeType) { + case NODE_END: + return prev[START]; + case ATTRIBUTE_NODE: + return null; + } + return prev === this ? null : prev; + } + get lastElementChild() { + let { lastChild } = this; + while (lastChild) { + if (lastChild.nodeType === ELEMENT_NODE) + return lastChild; + lastChild = previousSibling(lastChild); + } + return null; + } + get childElementCount() { + return this.children.length; + } + prepend(...nodes) { + insert(this, this.firstChild, nodes); + } + append(...nodes) { + insert(this, this[END], nodes); + } + replaceChildren(...nodes) { + let { [NEXT]: next, [END]: end } = this; + while (next !== end && next.nodeType === ATTRIBUTE_NODE) + next = next[NEXT]; + while (next !== end) { + const after2 = getEnd(next)[NEXT]; + next.remove(); + next = after2; + } + if (nodes.length) + insert(this, end, nodes); + } + getElementsByClassName(className) { + const elements = new NodeList(); + let { [NEXT]: next, [END]: end } = this; + while (next !== end) { + if (next.nodeType === ELEMENT_NODE && next.hasAttribute("class") && next.classList.has(className)) + elements.push(next); + next = next[NEXT]; + } + return elements; + } + getElementsByTagName(tagName19) { + const elements = new NodeList(); + let { [NEXT]: next, [END]: end } = this; + while (next !== end) { + if (next.nodeType === ELEMENT_NODE && (next.localName === tagName19 || localCase(next) === tagName19)) + elements.push(next); + next = next[NEXT]; + } + return elements; + } + querySelector(selectors) { + const matches2 = prepareMatch(this, selectors); + let { [NEXT]: next, [END]: end } = this; + while (next !== end) { + if (next.nodeType === ELEMENT_NODE && matches2(next)) + return next; + next = next.nodeType === ELEMENT_NODE && next.localName === "template" ? next[END] : next[NEXT]; + } + return null; + } + querySelectorAll(selectors) { + const matches2 = prepareMatch(this, selectors); + const elements = new NodeList(); + let { [NEXT]: next, [END]: end } = this; + while (next !== end) { + if (next.nodeType === ELEMENT_NODE && matches2(next)) + elements.push(next); + next = next.nodeType === ELEMENT_NODE && next.localName === "template" ? next[END] : next[NEXT]; + } + return elements; + } + appendChild(node) { + return this.insertBefore(node, this[END]); + } + contains(node) { + let parentNode = node; + while (parentNode && parentNode !== this) + parentNode = parentNode.parentNode; + return parentNode === this; + } + insertBefore(node, before2 = null) { + if (node === before2) + return node; + if (node === this) + throw new Error("unable to append a node to itself"); + const next = before2 || this[END]; + switch (node.nodeType) { + case ELEMENT_NODE: + node.remove(); + node.parentNode = this; + knownBoundaries(next[PREV], node, next); + moCallback(node, null); + connectedCallback(node); + break; + case DOCUMENT_FRAGMENT_NODE: { + let { [PRIVATE]: parentNode, firstChild, lastChild } = node; + if (firstChild) { + knownSegment(next[PREV], firstChild, lastChild, next); + knownAdjacent(node, node[END]); + if (parentNode) + parentNode.replaceChildren(); + do { + firstChild.parentNode = this; + moCallback(firstChild, null); + if (firstChild.nodeType === ELEMENT_NODE) + connectedCallback(firstChild); + } while (firstChild !== lastChild && (firstChild = nextSibling(firstChild))); + } + break; + } + case TEXT_NODE: + case COMMENT_NODE: + case CDATA_SECTION_NODE: + node.remove(); + /* eslint no-fallthrough:0 */ + // this covers DOCUMENT_TYPE_NODE too + default: + node.parentNode = this; + knownSiblings(next[PREV], node, next); + moCallback(node, null); + break; + } + return node; + } + normalize() { + let { [NEXT]: next, [END]: end } = this; + while (next !== end) { + const { [NEXT]: $next, [PREV]: $prev, nodeType } = next; + if (nodeType === TEXT_NODE) { + if (!next[VALUE]) + next.remove(); + else if ($prev && $prev.nodeType === TEXT_NODE) { + $prev.textContent += next.textContent; + next.remove(); + } + } + next = $next; + } + } + removeChild(node) { + if (node.parentNode !== this) + throw new Error("node is not a child"); + node.remove(); + return node; + } + replaceChild(node, replaced) { + const next = getEnd(replaced)[NEXT]; + replaced.remove(); + this.insertBefore(node, next); + return replaced; + } +}; + +// node_modules/linkedom/esm/mixin/non-element-parent-node.js +var NonElementParentNode = class extends ParentNode { + getElementById(id) { + let { [NEXT]: next, [END]: end } = this; + while (next !== end) { + if (next.nodeType === ELEMENT_NODE && next.id === id) + return next; + next = next[NEXT]; + } + return null; + } + cloneNode(deep) { + const { ownerDocument, constructor } = this; + const nonEPN = new constructor(ownerDocument); + if (deep) { + const { [END]: end } = nonEPN; + for (const node of this.childNodes) + nonEPN.insertBefore(node.cloneNode(deep), end); + } + return nonEPN; + } + toString() { + const { childNodes, localName } = this; + return `<${localName}>${childNodes.join("")}</${localName}>`; + } + toJSON() { + const json = []; + nonElementAsJSON(this, json); + return json; + } +}; + +// node_modules/linkedom/esm/interface/document-fragment.js +var DocumentFragment = class extends NonElementParentNode { + constructor(ownerDocument) { + super(ownerDocument, "#document-fragment", DOCUMENT_FRAGMENT_NODE); + } +}; + +// node_modules/linkedom/esm/interface/document-type.js +var DocumentType = class _DocumentType extends Node2 { + constructor(ownerDocument, name, publicId = "", systemId = "") { + super(ownerDocument, "#document-type", DOCUMENT_TYPE_NODE); + this.name = name; + this.publicId = publicId; + this.systemId = systemId; + } + cloneNode() { + const { ownerDocument, name, publicId, systemId } = this; + return new _DocumentType(ownerDocument, name, publicId, systemId); + } + toString() { + const { name, publicId, systemId } = this; + const hasPublic = 0 < publicId.length; + const str = [name]; + if (hasPublic) + str.push("PUBLIC", `"${publicId}"`); + if (systemId.length) { + if (!hasPublic) + str.push("SYSTEM"); + str.push(`"${systemId}"`); + } + return `<!DOCTYPE ${str.join(" ")}>`; + } + toJSON() { + const json = []; + documentTypeAsJSON(this, json); + return json; + } +}; + +// node_modules/linkedom/esm/mixin/inner-html.js +var getInnerHtml = (node) => node.childNodes.join(""); +var setInnerHtml = (node, html) => { + const { ownerDocument } = node; + const { constructor } = ownerDocument; + const document = new constructor(); + document[CUSTOM_ELEMENTS] = ownerDocument[CUSTOM_ELEMENTS]; + const { childNodes } = parseFromString(document, ignoreCase(node), html); + node.replaceChildren(...childNodes.map(setOwnerDocument, ownerDocument)); +}; +function setOwnerDocument(node) { + node.ownerDocument = this; + switch (node.nodeType) { + case ELEMENT_NODE: + case DOCUMENT_FRAGMENT_NODE: + node.childNodes.forEach(setOwnerDocument, this); + break; + } + return node; +} + +// node_modules/uhyphen/esm/index.js +var esm_default2 = (camel) => camel.replace(/(([A-Z0-9])([A-Z0-9][a-z]))|(([a-z0-9]+)([A-Z]))/g, "$2$5-$3$6").toLowerCase(); + +// node_modules/linkedom/esm/dom/string-map.js +var refs = /* @__PURE__ */ new WeakMap(); +var key = (name) => `data-${esm_default2(name)}`; +var prop = (name) => name.slice(5).replace(/-([a-z])/g, (_, $1) => $1.toUpperCase()); +var handler = { + get(dataset, name) { + if (name in dataset) + return refs.get(dataset).getAttribute(key(name)); + }, + set(dataset, name, value) { + dataset[name] = value; + refs.get(dataset).setAttribute(key(name), value); + return true; + }, + deleteProperty(dataset, name) { + if (name in dataset) + refs.get(dataset).removeAttribute(key(name)); + return delete dataset[name]; + } +}; +var DOMStringMap = class { + /** + * @param {Element} ref + */ + constructor(ref) { + for (const { name, value } of ref.attributes) { + if (/^data-/.test(name)) + this[prop(name)] = value; + } + refs.set(this, ref); + return new Proxy(this, handler); + } +}; +setPrototypeOf(DOMStringMap.prototype, null); + +// node_modules/linkedom/esm/dom/token-list.js +var { add } = Set.prototype; +var addTokens = (self, tokens) => { + for (const token of tokens) { + if (token) + add.call(self, token); + } +}; +var update = ({ [OWNER_ELEMENT]: ownerElement, value }) => { + const attribute2 = ownerElement.getAttributeNode("class"); + if (attribute2) + attribute2.value = value; + else + setAttribute( + ownerElement, + new Attr(ownerElement.ownerDocument, "class", value) + ); +}; +var DOMTokenList = class extends Set { + constructor(ownerElement) { + super(); + this[OWNER_ELEMENT] = ownerElement; + const attribute2 = ownerElement.getAttributeNode("class"); + if (attribute2) + addTokens(this, attribute2.value.split(/\s+/)); + } + get length() { + return this.size; + } + get value() { + return [...this].join(" "); + } + /** + * @param {...string} tokens + */ + add(...tokens) { + addTokens(this, tokens); + update(this); + } + /** + * @param {string} token + */ + contains(token) { + return this.has(token); + } + /** + * @param {...string} tokens + */ + remove(...tokens) { + for (const token of tokens) + this.delete(token); + update(this); + } + /** + * @param {string} token + * @param {boolean?} force + */ + toggle(token, force) { + if (this.has(token)) { + if (force) + return true; + this.delete(token); + update(this); + } else if (force || arguments.length === 1) { + super.add(token); + update(this); + return true; + } + return false; + } + /** + * @param {string} token + * @param {string} newToken + */ + replace(token, newToken) { + if (this.has(token)) { + this.delete(token); + super.add(newToken); + update(this); + return true; + } + return false; + } + /** + * @param {string} token + */ + supports() { + return true; + } +}; + +// node_modules/linkedom/esm/interface/css-style-declaration.js +var refs2 = /* @__PURE__ */ new WeakMap(); +var getKeys = (style) => [...style.keys()].filter((key2) => key2 !== PRIVATE); +var updateKeys = (style) => { + const attr = refs2.get(style).getAttributeNode("style"); + if (!attr || attr[CHANGED] || style.get(PRIVATE) !== attr) { + style.clear(); + if (attr) { + style.set(PRIVATE, attr); + for (const rule of attr[VALUE].split(/\s*;\s*/)) { + let [key2, ...rest] = rule.split(":"); + if (rest.length > 0) { + key2 = key2.trim(); + const value = rest.join(":").trim(); + if (key2 && value) + style.set(key2, value); + } + } + } + } + return attr; +}; +var handler2 = { + get(style, name) { + if (name in prototype) + return style[name]; + updateKeys(style); + if (name === "length") + return getKeys(style).length; + if (/^\d+$/.test(name)) + return getKeys(style)[name]; + return style.get(esm_default2(name)); + }, + set(style, name, value) { + if (name === "cssText") + style[name] = value; + else { + let attr = updateKeys(style); + if (value == null) + style.delete(esm_default2(name)); + else + style.set(esm_default2(name), value); + if (!attr) { + const element = refs2.get(style); + attr = element.ownerDocument.createAttribute("style"); + element.setAttributeNode(attr); + style.set(PRIVATE, attr); + } + attr[CHANGED] = false; + attr[VALUE] = style.toString(); + } + return true; + } +}; +var CSSStyleDeclaration = class extends Map { + constructor(element) { + super(); + refs2.set(this, element); + return new Proxy(this, handler2); + } + get cssText() { + return this.toString(); + } + set cssText(value) { + refs2.get(this).setAttribute("style", value); + } + getPropertyValue(name) { + const self = this[PRIVATE]; + return handler2.get(self, name); + } + setProperty(name, value) { + const self = this[PRIVATE]; + handler2.set(self, name, value); + } + removeProperty(name) { + const self = this[PRIVATE]; + handler2.set(self, name, null); + } + [Symbol.iterator]() { + const self = this[PRIVATE]; + updateKeys(self); + const keys2 = getKeys(self); + const { length } = keys2; + let i = 0; + return { + next() { + const done = i === length; + return { done, value: done ? null : keys2[i++] }; + } + }; + } + get [PRIVATE]() { + return this; + } + toString() { + const self = this[PRIVATE]; + updateKeys(self); + const cssText = []; + self.forEach(push, cssText); + return cssText.join(";"); + } +}; +var { prototype } = CSSStyleDeclaration; +function push(value, key2) { + if (key2 !== PRIVATE) + this.push(`${key2}:${value}`); +} + +// node_modules/linkedom/esm/interface/event.js +var BUBBLING_PHASE = 3; +var AT_TARGET = 2; +var CAPTURING_PHASE = 1; +var NONE = 0; +function getCurrentTarget(ev) { + return ev.currentTarget; +} +var GlobalEvent = class { + static get BUBBLING_PHASE() { + return BUBBLING_PHASE; + } + static get AT_TARGET() { + return AT_TARGET; + } + static get CAPTURING_PHASE() { + return CAPTURING_PHASE; + } + static get NONE() { + return NONE; + } + constructor(type, eventInitDict = {}) { + this.type = type; + this.bubbles = !!eventInitDict.bubbles; + this.cancelBubble = false; + this._stopImmediatePropagationFlag = false; + this.cancelable = !!eventInitDict.cancelable; + this.eventPhase = this.NONE; + this.timeStamp = Date.now(); + this.defaultPrevented = false; + this.originalTarget = null; + this.returnValue = null; + this.srcElement = null; + this.target = null; + this._path = []; + } + get BUBBLING_PHASE() { + return BUBBLING_PHASE; + } + get AT_TARGET() { + return AT_TARGET; + } + get CAPTURING_PHASE() { + return CAPTURING_PHASE; + } + get NONE() { + return NONE; + } + preventDefault() { + this.defaultPrevented = true; + } + // simplified implementation, should be https://dom.spec.whatwg.org/#dom-event-composedpath + composedPath() { + return this._path.map(getCurrentTarget); + } + stopPropagation() { + this.cancelBubble = true; + } + stopImmediatePropagation() { + this.stopPropagation(); + this._stopImmediatePropagationFlag = true; + } +}; + +// node_modules/linkedom/esm/interface/named-node-map.js +var NamedNodeMap = class extends Array { + constructor(ownerElement) { + super(); + this.ownerElement = ownerElement; + } + getNamedItem(name) { + return this.ownerElement.getAttributeNode(name); + } + setNamedItem(attr) { + this.ownerElement.setAttributeNode(attr); + this.unshift(attr); + } + removeNamedItem(name) { + const item = this.getNamedItem(name); + this.ownerElement.removeAttribute(name); + this.splice(this.indexOf(item), 1); + } + item(index) { + return index < this.length ? this[index] : null; + } + /* c8 ignore start */ + getNamedItemNS(_, name) { + return this.getNamedItem(name); + } + setNamedItemNS(_, attr) { + return this.setNamedItem(attr); + } + removeNamedItemNS(_, name) { + return this.removeNamedItem(name); + } + /* c8 ignore stop */ +}; + +// node_modules/linkedom/esm/interface/shadow-root.js +var ShadowRoot = class extends NonElementParentNode { + constructor(host) { + super(host.ownerDocument, "#shadow-root", DOCUMENT_FRAGMENT_NODE); + this.host = host; + } + get innerHTML() { + return getInnerHtml(this); + } + set innerHTML(html) { + setInnerHtml(this, html); + } +}; + +// node_modules/linkedom/esm/interface/element.js +var attributesHandler = { + get(target, key2) { + return key2 in target ? target[key2] : target.find(({ name }) => name === key2); + } +}; +var create2 = (ownerDocument, element, localName) => { + if ("ownerSVGElement" in element) { + const svg = ownerDocument.createElementNS(SVG_NAMESPACE, localName); + svg.ownerSVGElement = element.ownerSVGElement; + return svg; + } + return ownerDocument.createElement(localName); +}; +var isVoid = ({ localName, ownerDocument }) => { + return ownerDocument[MIME].voidElements.test(localName); +}; +var Element2 = class extends ParentNode { + constructor(ownerDocument, localName) { + super(ownerDocument, localName, ELEMENT_NODE); + this[CLASS_LIST] = null; + this[DATASET] = null; + this[STYLE] = null; + } + // <Mixins> + get isConnected() { + return isConnected(this); + } + get parentElement() { + return parentElement(this); + } + get previousSibling() { + return previousSibling(this); + } + get nextSibling() { + return nextSibling(this); + } + get namespaceURI() { + return "http://www.w3.org/1999/xhtml"; + } + get previousElementSibling() { + return previousElementSibling(this); + } + get nextElementSibling() { + return nextElementSibling2(this); + } + before(...nodes) { + before(this, nodes); + } + after(...nodes) { + after(this, nodes); + } + replaceWith(...nodes) { + replaceWith(this, nodes); + } + remove() { + remove(this[PREV], this, this[END][NEXT]); + } + // </Mixins> + // <specialGetters> + get id() { + return stringAttribute.get(this, "id"); + } + set id(value) { + stringAttribute.set(this, "id", value); + } + get className() { + return this.classList.value; + } + set className(value) { + const { classList } = this; + classList.clear(); + classList.add(...$String(value).split(/\s+/)); + } + get nodeName() { + return localCase(this); + } + get tagName() { + return localCase(this); + } + get classList() { + return this[CLASS_LIST] || (this[CLASS_LIST] = new DOMTokenList(this)); + } + get dataset() { + return this[DATASET] || (this[DATASET] = new DOMStringMap(this)); + } + getBoundingClientRect() { + return { + x: 0, + y: 0, + bottom: 0, + height: 0, + left: 0, + right: 0, + top: 0, + width: 0 + }; + } + get nonce() { + return stringAttribute.get(this, "nonce"); + } + set nonce(value) { + stringAttribute.set(this, "nonce", value); + } + get style() { + return this[STYLE] || (this[STYLE] = new CSSStyleDeclaration(this)); + } + get tabIndex() { + return numericAttribute.get(this, "tabindex") || -1; + } + set tabIndex(value) { + numericAttribute.set(this, "tabindex", value); + } + get slot() { + return stringAttribute.get(this, "slot"); + } + set slot(value) { + stringAttribute.set(this, "slot", value); + } + // </specialGetters> + // <contentRelated> + get innerText() { + const text = []; + let { [NEXT]: next, [END]: end } = this; + while (next !== end) { + if (next.nodeType === TEXT_NODE) { + text.push(next.textContent.replace(/\s+/g, " ")); + } else if (text.length && next[NEXT] != end && BLOCK_ELEMENTS.has(next.tagName)) { + text.push("\n"); + } + next = next[NEXT]; + } + return text.join(""); + } + /** + * @returns {String} + */ + get textContent() { + const text = []; + let { [NEXT]: next, [END]: end } = this; + while (next !== end) { + if (next.nodeType === TEXT_NODE) + text.push(next.textContent); + next = next[NEXT]; + } + return text.join(""); + } + set textContent(text) { + this.replaceChildren(); + if (text != null && text !== "") + this.appendChild(new Text3(this.ownerDocument, text)); + } + get innerHTML() { + return getInnerHtml(this); + } + set innerHTML(html) { + setInnerHtml(this, html); + } + get outerHTML() { + return this.toString(); + } + set outerHTML(html) { + const template = this.ownerDocument.createElement(""); + template.innerHTML = html; + this.replaceWith(...template.childNodes); + } + // </contentRelated> + // <attributes> + get attributes() { + const attributes2 = new NamedNodeMap(this); + let next = this[NEXT]; + while (next.nodeType === ATTRIBUTE_NODE) { + attributes2.push(next); + next = next[NEXT]; + } + return new Proxy(attributes2, attributesHandler); + } + focus() { + this.dispatchEvent(new GlobalEvent("focus")); + } + getAttribute(name) { + if (name === "class") + return this.className; + const attribute2 = this.getAttributeNode(name); + return attribute2 && (ignoreCase(this) ? attribute2.value : escape2(attribute2.value)); + } + getAttributeNode(name) { + let next = this[NEXT]; + while (next.nodeType === ATTRIBUTE_NODE) { + if (next.name === name) + return next; + next = next[NEXT]; + } + return null; + } + getAttributeNames() { + const attributes2 = new NodeList(); + let next = this[NEXT]; + while (next.nodeType === ATTRIBUTE_NODE) { + attributes2.push(next.name); + next = next[NEXT]; + } + return attributes2; + } + hasAttribute(name) { + return !!this.getAttributeNode(name); + } + hasAttributes() { + return this[NEXT].nodeType === ATTRIBUTE_NODE; + } + removeAttribute(name) { + if (name === "class" && this[CLASS_LIST]) + this[CLASS_LIST].clear(); + let next = this[NEXT]; + while (next.nodeType === ATTRIBUTE_NODE) { + if (next.name === name) { + removeAttribute(this, next); + return; + } + next = next[NEXT]; + } + } + removeAttributeNode(attribute2) { + let next = this[NEXT]; + while (next.nodeType === ATTRIBUTE_NODE) { + if (next === attribute2) { + removeAttribute(this, next); + return; + } + next = next[NEXT]; + } + } + setAttribute(name, value) { + if (name === "class") + this.className = value; + else { + const attribute2 = this.getAttributeNode(name); + if (attribute2) + attribute2.value = value; + else + setAttribute(this, new Attr(this.ownerDocument, name, value)); + } + } + setAttributeNode(attribute2) { + const { name } = attribute2; + const previously = this.getAttributeNode(name); + if (previously !== attribute2) { + if (previously) + this.removeAttributeNode(previously); + const { ownerElement } = attribute2; + if (ownerElement) + ownerElement.removeAttributeNode(attribute2); + setAttribute(this, attribute2); + } + return previously; + } + toggleAttribute(name, force) { + if (this.hasAttribute(name)) { + if (!force) { + this.removeAttribute(name); + return false; + } + return true; + } else if (force || arguments.length === 1) { + this.setAttribute(name, ""); + return true; + } + return false; + } + // </attributes> + // <ShadowDOM> + get shadowRoot() { + if (shadowRoots.has(this)) { + const { mode, shadowRoot } = shadowRoots.get(this); + if (mode === "open") + return shadowRoot; + } + return null; + } + attachShadow(init) { + if (shadowRoots.has(this)) + throw new Error("operation not supported"); + const shadowRoot = new ShadowRoot(this); + shadowRoots.set(this, { + mode: init.mode, + shadowRoot + }); + return shadowRoot; + } + // </ShadowDOM> + // <selectors> + matches(selectors) { + return matches(this, selectors); + } + closest(selectors) { + let parentElement2 = this; + const matches2 = prepareMatch(parentElement2, selectors); + while (parentElement2 && !matches2(parentElement2)) + parentElement2 = parentElement2.parentElement; + return parentElement2; + } + // </selectors> + // <insertAdjacent> + insertAdjacentElement(position, element) { + const { parentElement: parentElement2 } = this; + switch (position) { + case "beforebegin": + if (parentElement2) { + parentElement2.insertBefore(element, this); + break; + } + return null; + case "afterbegin": + this.insertBefore(element, this.firstChild); + break; + case "beforeend": + this.insertBefore(element, null); + break; + case "afterend": + if (parentElement2) { + parentElement2.insertBefore(element, this.nextSibling); + break; + } + return null; + } + return element; + } + insertAdjacentHTML(position, html) { + const template = this.ownerDocument.createElement("template"); + template.innerHTML = html; + this.insertAdjacentElement(position, template.content); + } + insertAdjacentText(position, text) { + const node = this.ownerDocument.createTextNode(text); + this.insertAdjacentElement(position, node); + } + // </insertAdjacent> + cloneNode(deep = false) { + const { ownerDocument, localName } = this; + const addNext = (next2) => { + next2.parentNode = parentNode; + knownAdjacent($next, next2); + $next = next2; + }; + const clone = create2(ownerDocument, this, localName); + let parentNode = clone, $next = clone; + let { [NEXT]: next, [END]: prev } = this; + while (next !== prev && (deep || next.nodeType === ATTRIBUTE_NODE)) { + switch (next.nodeType) { + case NODE_END: + knownAdjacent($next, parentNode[END]); + $next = parentNode[END]; + parentNode = parentNode.parentNode; + break; + case ELEMENT_NODE: { + const node = create2(ownerDocument, next, next.localName); + addNext(node); + parentNode = node; + break; + } + case ATTRIBUTE_NODE: { + const attr = next.cloneNode(deep); + attr.ownerElement = parentNode; + addNext(attr); + break; + } + case TEXT_NODE: + case COMMENT_NODE: + case CDATA_SECTION_NODE: + addNext(next.cloneNode(deep)); + break; + } + next = next[NEXT]; + } + knownAdjacent($next, clone[END]); + return clone; + } + // <custom> + toString() { + const out = []; + const { [END]: end } = this; + let next = { [NEXT]: this }; + let isOpened = false; + do { + next = next[NEXT]; + switch (next.nodeType) { + case ATTRIBUTE_NODE: { + const attr = " " + next; + switch (attr) { + case " id": + case " class": + case " style": + break; + default: + out.push(attr); + } + break; + } + case NODE_END: { + const start = next[START]; + if (isOpened) { + if ("ownerSVGElement" in start) + out.push(" />"); + else if (isVoid(start)) + out.push(ignoreCase(start) ? ">" : " />"); + else + out.push(`></${start.localName}>`); + isOpened = false; + } else + out.push(`</${start.localName}>`); + break; + } + case ELEMENT_NODE: + if (isOpened) + out.push(">"); + if (next.toString !== this.toString) { + out.push(next.toString()); + next = next[END]; + isOpened = false; + } else { + out.push(`<${next.localName}`); + isOpened = true; + } + break; + case TEXT_NODE: + case COMMENT_NODE: + case CDATA_SECTION_NODE: + out.push((isOpened ? ">" : "") + next); + isOpened = false; + break; + } + } while (next !== end); + return out.join(""); + } + toJSON() { + const json = []; + elementAsJSON(this, json); + return json; + } + // </custom> + /* c8 ignore start */ + getAttributeNS(_, name) { + return this.getAttribute(name); + } + getElementsByTagNameNS(_, name) { + return this.getElementsByTagName(name); + } + hasAttributeNS(_, name) { + return this.hasAttribute(name); + } + removeAttributeNS(_, name) { + this.removeAttribute(name); + } + setAttributeNS(_, name, value) { + this.setAttribute(name, value); + } + setAttributeNodeNS(attr) { + return this.setAttributeNode(attr); + } + /* c8 ignore stop */ +}; + +// node_modules/linkedom/esm/svg/element.js +var classNames = /* @__PURE__ */ new WeakMap(); +var handler3 = { + get(target, name) { + return target[name]; + }, + set(target, name, value) { + target[name] = value; + return true; + } +}; +var SVGElement = class extends Element2 { + constructor(ownerDocument, localName, ownerSVGElement = null) { + super(ownerDocument, localName); + this.ownerSVGElement = ownerSVGElement; + } + get className() { + if (!classNames.has(this)) + classNames.set(this, new Proxy({ baseVal: "", animVal: "" }, handler3)); + return classNames.get(this); + } + /* c8 ignore start */ + set className(value) { + const { classList } = this; + classList.clear(); + classList.add(...$String(value).split(/\s+/)); + } + /* c8 ignore stop */ + get namespaceURI() { + return "http://www.w3.org/2000/svg"; + } + getAttribute(name) { + return name === "class" ? [...this.classList].join(" ") : super.getAttribute(name); + } + setAttribute(name, value) { + if (name === "class") + this.className = value; + else if (name === "style") { + const { className } = this; + className.baseVal = className.animVal = value; + } + super.setAttribute(name, value); + } +}; + +// node_modules/linkedom/esm/shared/facades.js +var illegalConstructor = () => { + throw new TypeError("Illegal constructor"); +}; +function Attr2() { + illegalConstructor(); +} +setPrototypeOf(Attr2, Attr); +Attr2.prototype = Attr.prototype; +function CDATASection2() { + illegalConstructor(); +} +setPrototypeOf(CDATASection2, CDATASection); +CDATASection2.prototype = CDATASection.prototype; +function CharacterData2() { + illegalConstructor(); +} +setPrototypeOf(CharacterData2, CharacterData); +CharacterData2.prototype = CharacterData.prototype; +function Comment4() { + illegalConstructor(); +} +setPrototypeOf(Comment4, Comment3); +Comment4.prototype = Comment3.prototype; +function DocumentFragment2() { + illegalConstructor(); +} +setPrototypeOf(DocumentFragment2, DocumentFragment); +DocumentFragment2.prototype = DocumentFragment.prototype; +function DocumentType2() { + illegalConstructor(); +} +setPrototypeOf(DocumentType2, DocumentType); +DocumentType2.prototype = DocumentType.prototype; +function Element3() { + illegalConstructor(); +} +setPrototypeOf(Element3, Element2); +Element3.prototype = Element2.prototype; +function Node3() { + illegalConstructor(); +} +setPrototypeOf(Node3, Node2); +Node3.prototype = Node2.prototype; +function ShadowRoot2() { + illegalConstructor(); +} +setPrototypeOf(ShadowRoot2, ShadowRoot); +ShadowRoot2.prototype = ShadowRoot.prototype; +function Text4() { + illegalConstructor(); +} +setPrototypeOf(Text4, Text3); +Text4.prototype = Text3.prototype; +function SVGElement2() { + illegalConstructor(); +} +setPrototypeOf(SVGElement2, SVGElement); +SVGElement2.prototype = SVGElement.prototype; +var Facades = { + Attr: Attr2, + CDATASection: CDATASection2, + CharacterData: CharacterData2, + Comment: Comment4, + DocumentFragment: DocumentFragment2, + DocumentType: DocumentType2, + Element: Element3, + Node: Node3, + ShadowRoot: ShadowRoot2, + Text: Text4, + SVGElement: SVGElement2 +}; + +// node_modules/linkedom/esm/html/element.js +var Level0 = /* @__PURE__ */ new WeakMap(); +var level0 = { + get(element, name) { + return Level0.has(element) && Level0.get(element)[name] || null; + }, + set(element, name, value) { + if (!Level0.has(element)) + Level0.set(element, {}); + const handlers = Level0.get(element); + const type = name.slice(2); + if (handlers[name]) + element.removeEventListener(type, handlers[name], false); + if (handlers[name] = value) + element.addEventListener(type, value, false); + } +}; +var HTMLElement = class extends Element2 { + static get observedAttributes() { + return []; + } + constructor(ownerDocument = null, localName = "") { + super(ownerDocument, localName); + const ownerLess = !ownerDocument; + let options; + if (ownerLess) { + const { constructor: Class } = this; + if (!Classes.has(Class)) + throw new Error("unable to initialize this Custom Element"); + ({ ownerDocument, localName, options } = Classes.get(Class)); + } + if (ownerDocument[UPGRADE]) { + const { element, values } = ownerDocument[UPGRADE]; + ownerDocument[UPGRADE] = null; + for (const [key2, value] of values) + element[key2] = value; + return element; + } + if (ownerLess) { + this.ownerDocument = this[END].ownerDocument = ownerDocument; + this.localName = localName; + customElements.set(this, { connected: false }); + if (options.is) + this.setAttribute("is", options.is); + } + } + /* c8 ignore start */ + /* TODO: what about these? + offsetHeight + offsetLeft + offsetParent + offsetTop + offsetWidth + */ + blur() { + this.dispatchEvent(new GlobalEvent("blur")); + } + click() { + const clickEvent = new GlobalEvent("click", { bubbles: true, cancelable: true }); + clickEvent.button = 0; + this.dispatchEvent(clickEvent); + } + // Boolean getters + get accessKeyLabel() { + const { accessKey } = this; + return accessKey && `Alt+Shift+${accessKey}`; + } + get isContentEditable() { + return this.hasAttribute("contenteditable"); + } + // Boolean Accessors + get contentEditable() { + return booleanAttribute.get(this, "contenteditable"); + } + set contentEditable(value) { + booleanAttribute.set(this, "contenteditable", value); + } + get draggable() { + return booleanAttribute.get(this, "draggable"); + } + set draggable(value) { + booleanAttribute.set(this, "draggable", value); + } + get hidden() { + return booleanAttribute.get(this, "hidden"); + } + set hidden(value) { + booleanAttribute.set(this, "hidden", value); + } + get spellcheck() { + return booleanAttribute.get(this, "spellcheck"); + } + set spellcheck(value) { + booleanAttribute.set(this, "spellcheck", value); + } + // String Accessors + get accessKey() { + return stringAttribute.get(this, "accesskey"); + } + set accessKey(value) { + stringAttribute.set(this, "accesskey", value); + } + get dir() { + return stringAttribute.get(this, "dir"); + } + set dir(value) { + stringAttribute.set(this, "dir", value); + } + get lang() { + return stringAttribute.get(this, "lang"); + } + set lang(value) { + stringAttribute.set(this, "lang", value); + } + get title() { + return stringAttribute.get(this, "title"); + } + set title(value) { + stringAttribute.set(this, "title", value); + } + // DOM Level 0 + get onabort() { + return level0.get(this, "onabort"); + } + set onabort(value) { + level0.set(this, "onabort", value); + } + get onblur() { + return level0.get(this, "onblur"); + } + set onblur(value) { + level0.set(this, "onblur", value); + } + get oncancel() { + return level0.get(this, "oncancel"); + } + set oncancel(value) { + level0.set(this, "oncancel", value); + } + get oncanplay() { + return level0.get(this, "oncanplay"); + } + set oncanplay(value) { + level0.set(this, "oncanplay", value); + } + get oncanplaythrough() { + return level0.get(this, "oncanplaythrough"); + } + set oncanplaythrough(value) { + level0.set(this, "oncanplaythrough", value); + } + get onchange() { + return level0.get(this, "onchange"); + } + set onchange(value) { + level0.set(this, "onchange", value); + } + get onclick() { + return level0.get(this, "onclick"); + } + set onclick(value) { + level0.set(this, "onclick", value); + } + get onclose() { + return level0.get(this, "onclose"); + } + set onclose(value) { + level0.set(this, "onclose", value); + } + get oncontextmenu() { + return level0.get(this, "oncontextmenu"); + } + set oncontextmenu(value) { + level0.set(this, "oncontextmenu", value); + } + get oncuechange() { + return level0.get(this, "oncuechange"); + } + set oncuechange(value) { + level0.set(this, "oncuechange", value); + } + get ondblclick() { + return level0.get(this, "ondblclick"); + } + set ondblclick(value) { + level0.set(this, "ondblclick", value); + } + get ondrag() { + return level0.get(this, "ondrag"); + } + set ondrag(value) { + level0.set(this, "ondrag", value); + } + get ondragend() { + return level0.get(this, "ondragend"); + } + set ondragend(value) { + level0.set(this, "ondragend", value); + } + get ondragenter() { + return level0.get(this, "ondragenter"); + } + set ondragenter(value) { + level0.set(this, "ondragenter", value); + } + get ondragleave() { + return level0.get(this, "ondragleave"); + } + set ondragleave(value) { + level0.set(this, "ondragleave", value); + } + get ondragover() { + return level0.get(this, "ondragover"); + } + set ondragover(value) { + level0.set(this, "ondragover", value); + } + get ondragstart() { + return level0.get(this, "ondragstart"); + } + set ondragstart(value) { + level0.set(this, "ondragstart", value); + } + get ondrop() { + return level0.get(this, "ondrop"); + } + set ondrop(value) { + level0.set(this, "ondrop", value); + } + get ondurationchange() { + return level0.get(this, "ondurationchange"); + } + set ondurationchange(value) { + level0.set(this, "ondurationchange", value); + } + get onemptied() { + return level0.get(this, "onemptied"); + } + set onemptied(value) { + level0.set(this, "onemptied", value); + } + get onended() { + return level0.get(this, "onended"); + } + set onended(value) { + level0.set(this, "onended", value); + } + get onerror() { + return level0.get(this, "onerror"); + } + set onerror(value) { + level0.set(this, "onerror", value); + } + get onfocus() { + return level0.get(this, "onfocus"); + } + set onfocus(value) { + level0.set(this, "onfocus", value); + } + get oninput() { + return level0.get(this, "oninput"); + } + set oninput(value) { + level0.set(this, "oninput", value); + } + get oninvalid() { + return level0.get(this, "oninvalid"); + } + set oninvalid(value) { + level0.set(this, "oninvalid", value); + } + get onkeydown() { + return level0.get(this, "onkeydown"); + } + set onkeydown(value) { + level0.set(this, "onkeydown", value); + } + get onkeypress() { + return level0.get(this, "onkeypress"); + } + set onkeypress(value) { + level0.set(this, "onkeypress", value); + } + get onkeyup() { + return level0.get(this, "onkeyup"); + } + set onkeyup(value) { + level0.set(this, "onkeyup", value); + } + get onload() { + return level0.get(this, "onload"); + } + set onload(value) { + level0.set(this, "onload", value); + } + get onloadeddata() { + return level0.get(this, "onloadeddata"); + } + set onloadeddata(value) { + level0.set(this, "onloadeddata", value); + } + get onloadedmetadata() { + return level0.get(this, "onloadedmetadata"); + } + set onloadedmetadata(value) { + level0.set(this, "onloadedmetadata", value); + } + get onloadstart() { + return level0.get(this, "onloadstart"); + } + set onloadstart(value) { + level0.set(this, "onloadstart", value); + } + get onmousedown() { + return level0.get(this, "onmousedown"); + } + set onmousedown(value) { + level0.set(this, "onmousedown", value); + } + get onmouseenter() { + return level0.get(this, "onmouseenter"); + } + set onmouseenter(value) { + level0.set(this, "onmouseenter", value); + } + get onmouseleave() { + return level0.get(this, "onmouseleave"); + } + set onmouseleave(value) { + level0.set(this, "onmouseleave", value); + } + get onmousemove() { + return level0.get(this, "onmousemove"); + } + set onmousemove(value) { + level0.set(this, "onmousemove", value); + } + get onmouseout() { + return level0.get(this, "onmouseout"); + } + set onmouseout(value) { + level0.set(this, "onmouseout", value); + } + get onmouseover() { + return level0.get(this, "onmouseover"); + } + set onmouseover(value) { + level0.set(this, "onmouseover", value); + } + get onmouseup() { + return level0.get(this, "onmouseup"); + } + set onmouseup(value) { + level0.set(this, "onmouseup", value); + } + get onmousewheel() { + return level0.get(this, "onmousewheel"); + } + set onmousewheel(value) { + level0.set(this, "onmousewheel", value); + } + get onpause() { + return level0.get(this, "onpause"); + } + set onpause(value) { + level0.set(this, "onpause", value); + } + get onplay() { + return level0.get(this, "onplay"); + } + set onplay(value) { + level0.set(this, "onplay", value); + } + get onplaying() { + return level0.get(this, "onplaying"); + } + set onplaying(value) { + level0.set(this, "onplaying", value); + } + get onprogress() { + return level0.get(this, "onprogress"); + } + set onprogress(value) { + level0.set(this, "onprogress", value); + } + get onratechange() { + return level0.get(this, "onratechange"); + } + set onratechange(value) { + level0.set(this, "onratechange", value); + } + get onreset() { + return level0.get(this, "onreset"); + } + set onreset(value) { + level0.set(this, "onreset", value); + } + get onresize() { + return level0.get(this, "onresize"); + } + set onresize(value) { + level0.set(this, "onresize", value); + } + get onscroll() { + return level0.get(this, "onscroll"); + } + set onscroll(value) { + level0.set(this, "onscroll", value); + } + get onseeked() { + return level0.get(this, "onseeked"); + } + set onseeked(value) { + level0.set(this, "onseeked", value); + } + get onseeking() { + return level0.get(this, "onseeking"); + } + set onseeking(value) { + level0.set(this, "onseeking", value); + } + get onselect() { + return level0.get(this, "onselect"); + } + set onselect(value) { + level0.set(this, "onselect", value); + } + get onshow() { + return level0.get(this, "onshow"); + } + set onshow(value) { + level0.set(this, "onshow", value); + } + get onstalled() { + return level0.get(this, "onstalled"); + } + set onstalled(value) { + level0.set(this, "onstalled", value); + } + get onsubmit() { + return level0.get(this, "onsubmit"); + } + set onsubmit(value) { + level0.set(this, "onsubmit", value); + } + get onsuspend() { + return level0.get(this, "onsuspend"); + } + set onsuspend(value) { + level0.set(this, "onsuspend", value); + } + get ontimeupdate() { + return level0.get(this, "ontimeupdate"); + } + set ontimeupdate(value) { + level0.set(this, "ontimeupdate", value); + } + get ontoggle() { + return level0.get(this, "ontoggle"); + } + set ontoggle(value) { + level0.set(this, "ontoggle", value); + } + get onvolumechange() { + return level0.get(this, "onvolumechange"); + } + set onvolumechange(value) { + level0.set(this, "onvolumechange", value); + } + get onwaiting() { + return level0.get(this, "onwaiting"); + } + set onwaiting(value) { + level0.set(this, "onwaiting", value); + } + get onauxclick() { + return level0.get(this, "onauxclick"); + } + set onauxclick(value) { + level0.set(this, "onauxclick", value); + } + get ongotpointercapture() { + return level0.get(this, "ongotpointercapture"); + } + set ongotpointercapture(value) { + level0.set(this, "ongotpointercapture", value); + } + get onlostpointercapture() { + return level0.get(this, "onlostpointercapture"); + } + set onlostpointercapture(value) { + level0.set(this, "onlostpointercapture", value); + } + get onpointercancel() { + return level0.get(this, "onpointercancel"); + } + set onpointercancel(value) { + level0.set(this, "onpointercancel", value); + } + get onpointerdown() { + return level0.get(this, "onpointerdown"); + } + set onpointerdown(value) { + level0.set(this, "onpointerdown", value); + } + get onpointerenter() { + return level0.get(this, "onpointerenter"); + } + set onpointerenter(value) { + level0.set(this, "onpointerenter", value); + } + get onpointerleave() { + return level0.get(this, "onpointerleave"); + } + set onpointerleave(value) { + level0.set(this, "onpointerleave", value); + } + get onpointermove() { + return level0.get(this, "onpointermove"); + } + set onpointermove(value) { + level0.set(this, "onpointermove", value); + } + get onpointerout() { + return level0.get(this, "onpointerout"); + } + set onpointerout(value) { + level0.set(this, "onpointerout", value); + } + get onpointerover() { + return level0.get(this, "onpointerover"); + } + set onpointerover(value) { + level0.set(this, "onpointerover", value); + } + get onpointerup() { + return level0.get(this, "onpointerup"); + } + set onpointerup(value) { + level0.set(this, "onpointerup", value); + } + /* c8 ignore stop */ +}; + +// node_modules/linkedom/esm/html/template-element.js +var tagName = "template"; +var HTMLTemplateElement = class extends HTMLElement { + constructor(ownerDocument) { + super(ownerDocument, tagName); + const content = this.ownerDocument.createDocumentFragment(); + (this[CONTENT] = content)[PRIVATE] = this; + } + get content() { + if (this.hasChildNodes() && !this[CONTENT].hasChildNodes()) { + for (const node of this.childNodes) + this[CONTENT].appendChild(node.cloneNode(true)); + } + return this[CONTENT]; + } +}; +registerHTMLClass(tagName, HTMLTemplateElement); + +// node_modules/linkedom/esm/html/html-element.js +var HTMLHtmlElement = class extends HTMLElement { + constructor(ownerDocument, localName = "html") { + super(ownerDocument, localName); + } +}; + +// node_modules/linkedom/esm/html/text-element.js +var { toString } = HTMLElement.prototype; +var TextElement = class extends HTMLElement { + get innerHTML() { + return this.textContent; + } + set innerHTML(html) { + this.textContent = html; + } + toString() { + const outerHTML = toString.call(this.cloneNode()); + return outerHTML.replace("><", () => `>${this.textContent}<`); + } +}; + +// node_modules/linkedom/esm/html/script-element.js +var tagName2 = "script"; +var HTMLScriptElement = class extends TextElement { + constructor(ownerDocument, localName = tagName2) { + super(ownerDocument, localName); + } + get type() { + return stringAttribute.get(this, "type"); + } + set type(value) { + stringAttribute.set(this, "type", value); + } + get src() { + return stringAttribute.get(this, "src"); + } + set src(value) { + stringAttribute.set(this, "src", value); + } + get defer() { + return booleanAttribute.get(this, "defer"); + } + set defer(value) { + booleanAttribute.set(this, "defer", value); + } + get crossOrigin() { + return stringAttribute.get(this, "crossorigin"); + } + set crossOrigin(value) { + stringAttribute.set(this, "crossorigin", value); + } + get nomodule() { + return booleanAttribute.get(this, "nomodule"); + } + set nomodule(value) { + booleanAttribute.set(this, "nomodule", value); + } + get referrerPolicy() { + return stringAttribute.get(this, "referrerpolicy"); + } + set referrerPolicy(value) { + stringAttribute.set(this, "referrerpolicy", value); + } + get nonce() { + return stringAttribute.get(this, "nonce"); + } + set nonce(value) { + stringAttribute.set(this, "nonce", value); + } + get async() { + return booleanAttribute.get(this, "async"); + } + set async(value) { + booleanAttribute.set(this, "async", value); + } + get text() { + return this.textContent; + } + set text(content) { + this.textContent = content; + } +}; +registerHTMLClass(tagName2, HTMLScriptElement); + +// node_modules/linkedom/esm/html/frame-element.js +var HTMLFrameElement = class extends HTMLElement { + constructor(ownerDocument, localName = "frame") { + super(ownerDocument, localName); + } +}; + +// node_modules/linkedom/esm/html/i-frame-element.js +var tagName3 = "iframe"; +var HTMLIFrameElement = class extends HTMLElement { + constructor(ownerDocument, localName = tagName3) { + super(ownerDocument, localName); + } + /* c8 ignore start */ + get src() { + return stringAttribute.get(this, "src"); + } + set src(value) { + stringAttribute.set(this, "src", value); + } + get srcdoc() { + return stringAttribute.get(this, "srcdoc"); + } + set srcdoc(value) { + stringAttribute.set(this, "srcdoc", value); + } + get name() { + return stringAttribute.get(this, "name"); + } + set name(value) { + stringAttribute.set(this, "name", value); + } + get allow() { + return stringAttribute.get(this, "allow"); + } + set allow(value) { + stringAttribute.set(this, "allow", value); + } + get allowFullscreen() { + return booleanAttribute.get(this, "allowfullscreen"); + } + set allowFullscreen(value) { + booleanAttribute.set(this, "allowfullscreen", value); + } + get referrerPolicy() { + return stringAttribute.get(this, "referrerpolicy"); + } + set referrerPolicy(value) { + stringAttribute.set(this, "referrerpolicy", value); + } + get loading() { + return stringAttribute.get(this, "loading"); + } + set loading(value) { + stringAttribute.set(this, "loading", value); + } + /* c8 ignore stop */ +}; +registerHTMLClass(tagName3, HTMLIFrameElement); + +// node_modules/linkedom/esm/html/object-element.js +var HTMLObjectElement = class extends HTMLElement { + constructor(ownerDocument, localName = "object") { + super(ownerDocument, localName); + } +}; + +// node_modules/linkedom/esm/html/head-element.js +var HTMLHeadElement = class extends HTMLElement { + constructor(ownerDocument, localName = "head") { + super(ownerDocument, localName); + } +}; + +// node_modules/linkedom/esm/html/body-element.js +var HTMLBodyElement = class extends HTMLElement { + constructor(ownerDocument, localName = "body") { + super(ownerDocument, localName); + } +}; + +// node_modules/linkedom/esm/html/style-element.js +var import_cssom = __toESM(require_lib(), 1); +var tagName4 = "style"; +var HTMLStyleElement = class extends TextElement { + constructor(ownerDocument, localName = tagName4) { + super(ownerDocument, localName); + this[SHEET] = null; + } + get sheet() { + const sheet = this[SHEET]; + if (sheet !== null) { + return sheet; + } + return this[SHEET] = (0, import_cssom.parse)(this.textContent); + } + get innerHTML() { + return super.innerHTML || ""; + } + set innerHTML(value) { + super.textContent = value; + this[SHEET] = null; + } + get innerText() { + return super.innerText || ""; + } + set innerText(value) { + super.textContent = value; + this[SHEET] = null; + } + get textContent() { + return super.textContent || ""; + } + set textContent(value) { + super.textContent = value; + this[SHEET] = null; + } +}; +registerHTMLClass(tagName4, HTMLStyleElement); + +// node_modules/linkedom/esm/html/time-element.js +var HTMLTimeElement = class extends HTMLElement { + constructor(ownerDocument, localName = "time") { + super(ownerDocument, localName); + } + /** + * @type {string} + */ + get dateTime() { + return stringAttribute.get(this, "datetime"); + } + set dateTime(value) { + stringAttribute.set(this, "datetime", value); + } +}; +registerHTMLClass("time", HTMLTimeElement); + +// node_modules/linkedom/esm/html/field-set-element.js +var HTMLFieldSetElement = class extends HTMLElement { + constructor(ownerDocument, localName = "fieldset") { + super(ownerDocument, localName); + } +}; + +// node_modules/linkedom/esm/html/embed-element.js +var HTMLEmbedElement = class extends HTMLElement { + constructor(ownerDocument, localName = "embed") { + super(ownerDocument, localName); + } +}; + +// node_modules/linkedom/esm/html/hr-element.js +var HTMLHRElement = class extends HTMLElement { + constructor(ownerDocument, localName = "hr") { + super(ownerDocument, localName); + } +}; + +// node_modules/linkedom/esm/html/progress-element.js +var HTMLProgressElement = class extends HTMLElement { + constructor(ownerDocument, localName = "progress") { + super(ownerDocument, localName); + } +}; + +// node_modules/linkedom/esm/html/paragraph-element.js +var HTMLParagraphElement = class extends HTMLElement { + constructor(ownerDocument, localName = "p") { + super(ownerDocument, localName); + } +}; + +// node_modules/linkedom/esm/html/table-element.js +var HTMLTableElement = class extends HTMLElement { + constructor(ownerDocument, localName = "table") { + super(ownerDocument, localName); + } +}; + +// node_modules/linkedom/esm/html/frame-set-element.js +var HTMLFrameSetElement = class extends HTMLElement { + constructor(ownerDocument, localName = "frameset") { + super(ownerDocument, localName); + } +}; + +// node_modules/linkedom/esm/html/li-element.js +var HTMLLIElement = class extends HTMLElement { + constructor(ownerDocument, localName = "li") { + super(ownerDocument, localName); + } +}; + +// node_modules/linkedom/esm/html/base-element.js +var HTMLBaseElement = class extends HTMLElement { + constructor(ownerDocument, localName = "base") { + super(ownerDocument, localName); + } +}; + +// node_modules/linkedom/esm/html/data-list-element.js +var HTMLDataListElement = class extends HTMLElement { + constructor(ownerDocument, localName = "datalist") { + super(ownerDocument, localName); + } +}; + +// node_modules/linkedom/esm/html/input-element.js +var tagName5 = "input"; +var HTMLInputElement = class extends HTMLElement { + constructor(ownerDocument, localName = tagName5) { + super(ownerDocument, localName); + } + /* c8 ignore start */ + get autofocus() { + return booleanAttribute.get(this, "autofocus") || -1; + } + set autofocus(value) { + booleanAttribute.set(this, "autofocus", value); + } + get disabled() { + return booleanAttribute.get(this, "disabled"); + } + set disabled(value) { + booleanAttribute.set(this, "disabled", value); + } + get name() { + return this.getAttribute("name"); + } + set name(value) { + this.setAttribute("name", value); + } + get placeholder() { + return this.getAttribute("placeholder"); + } + set placeholder(value) { + this.setAttribute("placeholder", value); + } + get type() { + return this.getAttribute("type"); + } + set type(value) { + this.setAttribute("type", value); + } + get value() { + return stringAttribute.get(this, "value"); + } + set value(value) { + stringAttribute.set(this, "value", value); + } + /* c8 ignore stop */ +}; +registerHTMLClass(tagName5, HTMLInputElement); + +// node_modules/linkedom/esm/html/param-element.js +var HTMLParamElement = class extends HTMLElement { + constructor(ownerDocument, localName = "param") { + super(ownerDocument, localName); + } +}; + +// node_modules/linkedom/esm/html/media-element.js +var HTMLMediaElement = class extends HTMLElement { + constructor(ownerDocument, localName = "media") { + super(ownerDocument, localName); + } +}; + +// node_modules/linkedom/esm/html/audio-element.js +var HTMLAudioElement = class extends HTMLElement { + constructor(ownerDocument, localName = "audio") { + super(ownerDocument, localName); + } +}; + +// node_modules/linkedom/esm/html/heading-element.js +var tagName6 = "h1"; +var HTMLHeadingElement = class extends HTMLElement { + constructor(ownerDocument, localName = tagName6) { + super(ownerDocument, localName); + } +}; +registerHTMLClass([tagName6, "h2", "h3", "h4", "h5", "h6"], HTMLHeadingElement); + +// node_modules/linkedom/esm/html/directory-element.js +var HTMLDirectoryElement = class extends HTMLElement { + constructor(ownerDocument, localName = "dir") { + super(ownerDocument, localName); + } +}; + +// node_modules/linkedom/esm/html/quote-element.js +var HTMLQuoteElement = class extends HTMLElement { + constructor(ownerDocument, localName = "quote") { + super(ownerDocument, localName); + } +}; + +// node_modules/linkedom/esm/html/canvas-element.js +var import_canvas = __toESM(require_canvas(), 1); +var { createCanvas } = import_canvas.default; +var tagName7 = "canvas"; +var HTMLCanvasElement = class extends HTMLElement { + constructor(ownerDocument, localName = tagName7) { + super(ownerDocument, localName); + this[IMAGE] = createCanvas(300, 150); + } + get width() { + return this[IMAGE].width; + } + set width(value) { + numericAttribute.set(this, "width", value); + this[IMAGE].width = value; + } + get height() { + return this[IMAGE].height; + } + set height(value) { + numericAttribute.set(this, "height", value); + this[IMAGE].height = value; + } + getContext(type) { + return this[IMAGE].getContext(type); + } + toDataURL(...args) { + return this[IMAGE].toDataURL(...args); + } +}; +registerHTMLClass(tagName7, HTMLCanvasElement); + +// node_modules/linkedom/esm/html/legend-element.js +var HTMLLegendElement = class extends HTMLElement { + constructor(ownerDocument, localName = "legend") { + super(ownerDocument, localName); + } +}; + +// node_modules/linkedom/esm/html/option-element.js +var tagName8 = "option"; +var HTMLOptionElement = class extends HTMLElement { + constructor(ownerDocument, localName = tagName8) { + super(ownerDocument, localName); + } + /* c8 ignore start */ + get value() { + return stringAttribute.get(this, "value"); + } + set value(value) { + stringAttribute.set(this, "value", value); + } + /* c8 ignore stop */ + get selected() { + return booleanAttribute.get(this, "selected"); + } + set selected(value) { + const option = this.parentElement?.querySelector("option[selected]"); + if (option && option !== this) + option.selected = false; + booleanAttribute.set(this, "selected", value); + } +}; +registerHTMLClass(tagName8, HTMLOptionElement); + +// node_modules/linkedom/esm/html/span-element.js +var HTMLSpanElement = class extends HTMLElement { + constructor(ownerDocument, localName = "span") { + super(ownerDocument, localName); + } +}; + +// node_modules/linkedom/esm/html/meter-element.js +var HTMLMeterElement = class extends HTMLElement { + constructor(ownerDocument, localName = "meter") { + super(ownerDocument, localName); + } +}; + +// node_modules/linkedom/esm/html/video-element.js +var HTMLVideoElement = class extends HTMLElement { + constructor(ownerDocument, localName = "video") { + super(ownerDocument, localName); + } +}; + +// node_modules/linkedom/esm/html/table-cell-element.js +var HTMLTableCellElement = class extends HTMLElement { + constructor(ownerDocument, localName = "td") { + super(ownerDocument, localName); + } +}; + +// node_modules/linkedom/esm/html/title-element.js +var tagName9 = "title"; +var HTMLTitleElement = class extends TextElement { + constructor(ownerDocument, localName = tagName9) { + super(ownerDocument, localName); + } +}; +registerHTMLClass(tagName9, HTMLTitleElement); + +// node_modules/linkedom/esm/html/output-element.js +var HTMLOutputElement = class extends HTMLElement { + constructor(ownerDocument, localName = "output") { + super(ownerDocument, localName); + } +}; + +// node_modules/linkedom/esm/html/table-row-element.js +var HTMLTableRowElement = class extends HTMLElement { + constructor(ownerDocument, localName = "tr") { + super(ownerDocument, localName); + } +}; + +// node_modules/linkedom/esm/html/data-element.js +var HTMLDataElement = class extends HTMLElement { + constructor(ownerDocument, localName = "data") { + super(ownerDocument, localName); + } +}; + +// node_modules/linkedom/esm/html/menu-element.js +var HTMLMenuElement = class extends HTMLElement { + constructor(ownerDocument, localName = "menu") { + super(ownerDocument, localName); + } +}; + +// node_modules/linkedom/esm/html/select-element.js +var tagName10 = "select"; +var HTMLSelectElement = class extends HTMLElement { + constructor(ownerDocument, localName = tagName10) { + super(ownerDocument, localName); + } + get options() { + let children = new NodeList(); + let { firstElementChild } = this; + while (firstElementChild) { + if (firstElementChild.tagName === "OPTGROUP") + children.push(...firstElementChild.children); + else + children.push(firstElementChild); + firstElementChild = firstElementChild.nextElementSibling; + } + return children; + } + /* c8 ignore start */ + get disabled() { + return booleanAttribute.get(this, "disabled"); + } + set disabled(value) { + booleanAttribute.set(this, "disabled", value); + } + get name() { + return this.getAttribute("name"); + } + set name(value) { + this.setAttribute("name", value); + } + /* c8 ignore stop */ + get value() { + return this.querySelector("option[selected]")?.value; + } +}; +registerHTMLClass(tagName10, HTMLSelectElement); + +// node_modules/linkedom/esm/html/br-element.js +var HTMLBRElement = class extends HTMLElement { + constructor(ownerDocument, localName = "br") { + super(ownerDocument, localName); + } +}; + +// node_modules/linkedom/esm/html/button-element.js +var tagName11 = "button"; +var HTMLButtonElement = class extends HTMLElement { + constructor(ownerDocument, localName = tagName11) { + super(ownerDocument, localName); + } + /* c8 ignore start */ + get disabled() { + return booleanAttribute.get(this, "disabled"); + } + set disabled(value) { + booleanAttribute.set(this, "disabled", value); + } + get name() { + return this.getAttribute("name"); + } + set name(value) { + this.setAttribute("name", value); + } + get type() { + return this.getAttribute("type"); + } + set type(value) { + this.setAttribute("type", value); + } + /* c8 ignore stop */ +}; +registerHTMLClass(tagName11, HTMLButtonElement); + +// node_modules/linkedom/esm/html/map-element.js +var HTMLMapElement = class extends HTMLElement { + constructor(ownerDocument, localName = "map") { + super(ownerDocument, localName); + } +}; + +// node_modules/linkedom/esm/html/opt-group-element.js +var HTMLOptGroupElement = class extends HTMLElement { + constructor(ownerDocument, localName = "optgroup") { + super(ownerDocument, localName); + } +}; + +// node_modules/linkedom/esm/html/d-list-element.js +var HTMLDListElement = class extends HTMLElement { + constructor(ownerDocument, localName = "dl") { + super(ownerDocument, localName); + } +}; + +// node_modules/linkedom/esm/html/text-area-element.js +var tagName12 = "textarea"; +var HTMLTextAreaElement = class extends TextElement { + constructor(ownerDocument, localName = tagName12) { + super(ownerDocument, localName); + } + /* c8 ignore start */ + get disabled() { + return booleanAttribute.get(this, "disabled"); + } + set disabled(value) { + booleanAttribute.set(this, "disabled", value); + } + get name() { + return this.getAttribute("name"); + } + set name(value) { + this.setAttribute("name", value); + } + get placeholder() { + return this.getAttribute("placeholder"); + } + set placeholder(value) { + this.setAttribute("placeholder", value); + } + get type() { + return this.getAttribute("type"); + } + set type(value) { + this.setAttribute("type", value); + } + get value() { + return this.textContent; + } + set value(content) { + this.textContent = content; + } + /* c8 ignore stop */ +}; +registerHTMLClass(tagName12, HTMLTextAreaElement); + +// node_modules/linkedom/esm/html/font-element.js +var HTMLFontElement = class extends HTMLElement { + constructor(ownerDocument, localName = "font") { + super(ownerDocument, localName); + } +}; + +// node_modules/linkedom/esm/html/div-element.js +var HTMLDivElement = class extends HTMLElement { + constructor(ownerDocument, localName = "div") { + super(ownerDocument, localName); + } +}; + +// node_modules/linkedom/esm/html/link-element.js +var tagName13 = "link"; +var HTMLLinkElement = class extends HTMLElement { + constructor(ownerDocument, localName = tagName13) { + super(ownerDocument, localName); + } + /* c8 ignore start */ + // copy paste from img.src, already covered + get disabled() { + return booleanAttribute.get(this, "disabled"); + } + set disabled(value) { + booleanAttribute.set(this, "disabled", value); + } + get href() { + return stringAttribute.get(this, "href"); + } + set href(value) { + stringAttribute.set(this, "href", value); + } + get hreflang() { + return stringAttribute.get(this, "hreflang"); + } + set hreflang(value) { + stringAttribute.set(this, "hreflang", value); + } + get media() { + return stringAttribute.get(this, "media"); + } + set media(value) { + stringAttribute.set(this, "media", value); + } + get rel() { + return stringAttribute.get(this, "rel"); + } + set rel(value) { + stringAttribute.set(this, "rel", value); + } + get type() { + return stringAttribute.get(this, "type"); + } + set type(value) { + stringAttribute.set(this, "type", value); + } + /* c8 ignore stop */ +}; +registerHTMLClass(tagName13, HTMLLinkElement); + +// node_modules/linkedom/esm/html/slot-element.js +var tagName14 = "slot"; +var HTMLSlotElement = class extends HTMLElement { + constructor(ownerDocument, localName = tagName14) { + super(ownerDocument, localName); + } + /* c8 ignore start */ + get name() { + return this.getAttribute("name"); + } + set name(value) { + this.setAttribute("name", value); + } + assign() { + } + assignedNodes(options) { + const isNamedSlot = !!this.name; + const hostChildNodes = this.getRootNode().host?.childNodes ?? []; + let slottables; + if (isNamedSlot) { + slottables = [...hostChildNodes].filter((node) => node.slot === this.name); + } else { + slottables = [...hostChildNodes].filter((node) => !node.slot); + } + if (options?.flatten) { + const result = []; + for (let slottable of slottables) { + if (slottable.localName === "slot") { + result.push(...slottable.assignedNodes({ flatten: true })); + } else { + result.push(slottable); + } + } + slottables = result; + } + return slottables.length ? slottables : [...this.childNodes]; + } + assignedElements(options) { + const slottables = this.assignedNodes(options).filter((n) => n.nodeType === 1); + return slottables.length ? slottables : [...this.children]; + } + /* c8 ignore stop */ +}; +registerHTMLClass(tagName14, HTMLSlotElement); + +// node_modules/linkedom/esm/html/form-element.js +var HTMLFormElement = class extends HTMLElement { + constructor(ownerDocument, localName = "form") { + super(ownerDocument, localName); + } +}; + +// node_modules/linkedom/esm/html/image-element.js +var tagName15 = "img"; +var HTMLImageElement = class extends HTMLElement { + constructor(ownerDocument, localName = tagName15) { + super(ownerDocument, localName); + } + /* c8 ignore start */ + get alt() { + return stringAttribute.get(this, "alt"); + } + set alt(value) { + stringAttribute.set(this, "alt", value); + } + get sizes() { + return stringAttribute.get(this, "sizes"); + } + set sizes(value) { + stringAttribute.set(this, "sizes", value); + } + get src() { + return stringAttribute.get(this, "src"); + } + set src(value) { + stringAttribute.set(this, "src", value); + } + get srcset() { + return stringAttribute.get(this, "srcset"); + } + set srcset(value) { + stringAttribute.set(this, "srcset", value); + } + get title() { + return stringAttribute.get(this, "title"); + } + set title(value) { + stringAttribute.set(this, "title", value); + } + get width() { + return numericAttribute.get(this, "width"); + } + set width(value) { + numericAttribute.set(this, "width", value); + } + get height() { + return numericAttribute.get(this, "height"); + } + set height(value) { + numericAttribute.set(this, "height", value); + } + /* c8 ignore stop */ +}; +registerHTMLClass(tagName15, HTMLImageElement); + +// node_modules/linkedom/esm/html/pre-element.js +var HTMLPreElement = class extends HTMLElement { + constructor(ownerDocument, localName = "pre") { + super(ownerDocument, localName); + } +}; + +// node_modules/linkedom/esm/html/u-list-element.js +var HTMLUListElement = class extends HTMLElement { + constructor(ownerDocument, localName = "ul") { + super(ownerDocument, localName); + } +}; + +// node_modules/linkedom/esm/html/meta-element.js +var tagName16 = "meta"; +var HTMLMetaElement = class extends HTMLElement { + constructor(ownerDocument, localName = tagName16) { + super(ownerDocument, localName); + } + /* c8 ignore start */ + get name() { + return stringAttribute.get(this, "name"); + } + set name(value) { + stringAttribute.set(this, "name", value); + } + get httpEquiv() { + return stringAttribute.get(this, "http-equiv"); + } + set httpEquiv(value) { + stringAttribute.set(this, "http-equiv", value); + } + get content() { + return stringAttribute.get(this, "content"); + } + set content(value) { + stringAttribute.set(this, "content", value); + } + get charset() { + return stringAttribute.get(this, "charset"); + } + set charset(value) { + stringAttribute.set(this, "charset", value); + } + get media() { + return stringAttribute.get(this, "media"); + } + set media(value) { + stringAttribute.set(this, "media", value); + } + /* c8 ignore stop */ +}; +registerHTMLClass(tagName16, HTMLMetaElement); + +// node_modules/linkedom/esm/html/picture-element.js +var HTMLPictureElement = class extends HTMLElement { + constructor(ownerDocument, localName = "picture") { + super(ownerDocument, localName); + } +}; + +// node_modules/linkedom/esm/html/area-element.js +var HTMLAreaElement = class extends HTMLElement { + constructor(ownerDocument, localName = "area") { + super(ownerDocument, localName); + } +}; + +// node_modules/linkedom/esm/html/o-list-element.js +var HTMLOListElement = class extends HTMLElement { + constructor(ownerDocument, localName = "ol") { + super(ownerDocument, localName); + } +}; + +// node_modules/linkedom/esm/html/table-caption-element.js +var HTMLTableCaptionElement = class extends HTMLElement { + constructor(ownerDocument, localName = "caption") { + super(ownerDocument, localName); + } +}; + +// node_modules/linkedom/esm/html/anchor-element.js +var tagName17 = "a"; +var HTMLAnchorElement = class extends HTMLElement { + constructor(ownerDocument, localName = tagName17) { + super(ownerDocument, localName); + } + /* c8 ignore start */ + // copy paste from img.src, already covered + get href() { + return encodeURI(decodeURI(stringAttribute.get(this, "href"))); + } + set href(value) { + stringAttribute.set(this, "href", decodeURI(value)); + } + get download() { + return encodeURI(decodeURI(stringAttribute.get(this, "download"))); + } + set download(value) { + stringAttribute.set(this, "download", decodeURI(value)); + } + get target() { + return stringAttribute.get(this, "target"); + } + set target(value) { + stringAttribute.set(this, "target", value); + } + get type() { + return stringAttribute.get(this, "type"); + } + set type(value) { + stringAttribute.set(this, "type", value); + } + get rel() { + return stringAttribute.get(this, "rel"); + } + set rel(value) { + stringAttribute.set(this, "rel", value); + } + /* c8 ignore stop */ +}; +registerHTMLClass(tagName17, HTMLAnchorElement); + +// node_modules/linkedom/esm/html/label-element.js +var HTMLLabelElement = class extends HTMLElement { + constructor(ownerDocument, localName = "label") { + super(ownerDocument, localName); + } +}; + +// node_modules/linkedom/esm/html/unknown-element.js +var HTMLUnknownElement = class extends HTMLElement { + constructor(ownerDocument, localName = "unknown") { + super(ownerDocument, localName); + } +}; + +// node_modules/linkedom/esm/html/mod-element.js +var HTMLModElement = class extends HTMLElement { + constructor(ownerDocument, localName = "mod") { + super(ownerDocument, localName); + } +}; + +// node_modules/linkedom/esm/html/details-element.js +var HTMLDetailsElement = class extends HTMLElement { + constructor(ownerDocument, localName = "details") { + super(ownerDocument, localName); + } +}; + +// node_modules/linkedom/esm/html/source-element.js +var tagName18 = "source"; +var HTMLSourceElement = class extends HTMLElement { + constructor(ownerDocument, localName = tagName18) { + super(ownerDocument, localName); + } + /* c8 ignore start */ + get src() { + return stringAttribute.get(this, "src"); + } + set src(value) { + stringAttribute.set(this, "src", value); + } + get srcset() { + return stringAttribute.get(this, "srcset"); + } + set srcset(value) { + stringAttribute.set(this, "srcset", value); + } + get sizes() { + return stringAttribute.get(this, "sizes"); + } + set sizes(value) { + stringAttribute.set(this, "sizes", value); + } + get type() { + return stringAttribute.get(this, "type"); + } + set type(value) { + stringAttribute.set(this, "type", value); + } + /* c8 ignore stop */ +}; +registerHTMLClass(tagName18, HTMLSourceElement); + +// node_modules/linkedom/esm/html/track-element.js +var HTMLTrackElement = class extends HTMLElement { + constructor(ownerDocument, localName = "track") { + super(ownerDocument, localName); + } +}; + +// node_modules/linkedom/esm/html/marquee-element.js +var HTMLMarqueeElement = class extends HTMLElement { + constructor(ownerDocument, localName = "marquee") { + super(ownerDocument, localName); + } +}; + +// node_modules/linkedom/esm/shared/html-classes.js +var HTMLClasses = { + HTMLElement, + HTMLTemplateElement, + HTMLHtmlElement, + HTMLScriptElement, + HTMLFrameElement, + HTMLIFrameElement, + HTMLObjectElement, + HTMLHeadElement, + HTMLBodyElement, + HTMLStyleElement, + HTMLTimeElement, + HTMLFieldSetElement, + HTMLEmbedElement, + HTMLHRElement, + HTMLProgressElement, + HTMLParagraphElement, + HTMLTableElement, + HTMLFrameSetElement, + HTMLLIElement, + HTMLBaseElement, + HTMLDataListElement, + HTMLInputElement, + HTMLParamElement, + HTMLMediaElement, + HTMLAudioElement, + HTMLHeadingElement, + HTMLDirectoryElement, + HTMLQuoteElement, + HTMLCanvasElement, + HTMLLegendElement, + HTMLOptionElement, + HTMLSpanElement, + HTMLMeterElement, + HTMLVideoElement, + HTMLTableCellElement, + HTMLTitleElement, + HTMLOutputElement, + HTMLTableRowElement, + HTMLDataElement, + HTMLMenuElement, + HTMLSelectElement, + HTMLBRElement, + HTMLButtonElement, + HTMLMapElement, + HTMLOptGroupElement, + HTMLDListElement, + HTMLTextAreaElement, + HTMLFontElement, + HTMLDivElement, + HTMLLinkElement, + HTMLSlotElement, + HTMLFormElement, + HTMLImageElement, + HTMLPreElement, + HTMLUListElement, + HTMLMetaElement, + HTMLPictureElement, + HTMLAreaElement, + HTMLOListElement, + HTMLTableCaptionElement, + HTMLAnchorElement, + HTMLLabelElement, + HTMLUnknownElement, + HTMLModElement, + HTMLDetailsElement, + HTMLSourceElement, + HTMLTrackElement, + HTMLMarqueeElement +}; + +// node_modules/linkedom/esm/shared/mime.js +var voidElements2 = { test: () => true }; +var Mime = { + "text/html": { + docType: "<!DOCTYPE html>", + ignoreCase: true, + voidElements: /^(?:area|base|br|col|embed|hr|img|input|keygen|link|menuitem|meta|param|source|track|wbr)$/i + }, + "image/svg+xml": { + docType: '<?xml version="1.0" encoding="utf-8"?>', + ignoreCase: false, + voidElements: voidElements2 + }, + "text/xml": { + docType: '<?xml version="1.0" encoding="utf-8"?>', + ignoreCase: false, + voidElements: voidElements2 + }, + "application/xml": { + docType: '<?xml version="1.0" encoding="utf-8"?>', + ignoreCase: false, + voidElements: voidElements2 + }, + "application/xhtml+xml": { + docType: '<?xml version="1.0" encoding="utf-8"?>', + ignoreCase: false, + voidElements: voidElements2 + } +}; + +// node_modules/linkedom/esm/interface/custom-event.js +var CustomEvent = class extends GlobalEvent { + constructor(type, eventInitDict = {}) { + super(type, eventInitDict); + this.detail = eventInitDict.detail; + } +}; + +// node_modules/linkedom/esm/interface/input-event.js +var InputEvent = class extends GlobalEvent { + constructor(type, inputEventInit = {}) { + super(type, inputEventInit); + this.inputType = inputEventInit.inputType; + this.data = inputEventInit.data; + this.dataTransfer = inputEventInit.dataTransfer; + this.isComposing = inputEventInit.isComposing || false; + this.ranges = inputEventInit.ranges; + } +}; + +// node_modules/linkedom/esm/interface/image.js +var ImageClass = (ownerDocument) => ( + /** + * @implements globalThis.Image + */ + class Image extends HTMLImageElement { + constructor(width, height) { + super(ownerDocument); + switch (arguments.length) { + case 1: + this.height = width; + this.width = width; + break; + case 2: + this.height = height; + this.width = width; + break; + } + } + } +); + +// node_modules/linkedom/esm/interface/range.js +var deleteContents = ({ [START]: start, [END]: end }, fragment = null) => { + setAdjacent(start[PREV], end[NEXT]); + do { + const after2 = getEnd(start); + const next = after2 === end ? after2 : after2[NEXT]; + if (fragment) + fragment.insertBefore(start, fragment[END]); + else + start.remove(); + start = next; + } while (start !== end); +}; +var Range = class _Range { + constructor() { + this[START] = null; + this[END] = null; + this.commonAncestorContainer = null; + } + /* TODO: this is more complicated than it looks + setStart(node, offset) { + this[START] = node.childNodes[offset]; + } + + setEnd(node, offset) { + this[END] = getEnd(node.childNodes[offset]); + } + //*/ + insertNode(newNode) { + this[END].parentNode.insertBefore(newNode, this[START]); + } + selectNode(node) { + this[START] = node; + this[END] = getEnd(node); + } + // TODO: SVG elements should then create contextual fragments + // that return SVG nodes + selectNodeContents(node) { + this.selectNode(node); + this.commonAncestorContainer = node; + } + surroundContents(parentNode) { + parentNode.replaceChildren(this.extractContents()); + } + setStartBefore(node) { + this[START] = node; + } + setStartAfter(node) { + this[START] = node.nextSibling; + } + setEndBefore(node) { + this[END] = getEnd(node.previousSibling); + } + setEndAfter(node) { + this[END] = getEnd(node); + } + cloneContents() { + let { [START]: start, [END]: end } = this; + const fragment = start.ownerDocument.createDocumentFragment(); + while (start !== end) { + fragment.insertBefore(start.cloneNode(true), fragment[END]); + start = getEnd(start); + if (start !== end) + start = start[NEXT]; + } + return fragment; + } + deleteContents() { + deleteContents(this); + } + extractContents() { + const fragment = this[START].ownerDocument.createDocumentFragment(); + deleteContents(this, fragment); + return fragment; + } + createContextualFragment(html) { + const { commonAncestorContainer: doc } = this; + const isSVG = "ownerSVGElement" in doc; + const document = isSVG ? doc.ownerDocument : doc; + const template = document.createElement("template"); + template.innerHTML = html; + let { content } = template; + if (isSVG) { + const childNodes = [...content.childNodes]; + content = document.createDocumentFragment(); + Object.setPrototypeOf(content, SVGElement.prototype); + content.ownerSVGElement = document; + for (const child of childNodes) { + Object.setPrototypeOf(child, SVGElement.prototype); + child.ownerSVGElement = document; + content.appendChild(child); + } + } else + this.selectNode(content); + return content; + } + cloneRange() { + const range = new _Range(); + range[START] = this[START]; + range[END] = this[END]; + return range; + } +}; + +// node_modules/linkedom/esm/interface/tree-walker.js +var isOK = ({ nodeType }, mask) => { + switch (nodeType) { + case ELEMENT_NODE: + return mask & SHOW_ELEMENT; + case TEXT_NODE: + return mask & SHOW_TEXT; + case COMMENT_NODE: + return mask & SHOW_COMMENT; + case CDATA_SECTION_NODE: + return mask & SHOW_CDATA_SECTION; + } + return 0; +}; +var TreeWalker = class { + constructor(root, whatToShow = SHOW_ALL) { + this.root = root; + this.currentNode = root; + this.whatToShow = whatToShow; + let { [NEXT]: next, [END]: end } = root; + if (root.nodeType === DOCUMENT_NODE) { + const { documentElement } = root; + next = documentElement; + end = documentElement[END]; + } + const nodes = []; + while (next && next !== end) { + if (isOK(next, whatToShow)) + nodes.push(next); + next = next[NEXT]; + } + this[PRIVATE] = { i: 0, nodes }; + } + nextNode() { + const $ = this[PRIVATE]; + this.currentNode = $.i < $.nodes.length ? $.nodes[$.i++] : null; + return this.currentNode; + } +}; + +// node_modules/linkedom/esm/interface/document.js +var query = (method, ownerDocument, selectors) => { + let { [NEXT]: next, [END]: end } = ownerDocument; + return method.call({ ownerDocument, [NEXT]: next, [END]: end }, selectors); +}; +var globalExports = assign( + {}, + Facades, + HTMLClasses, + { + CustomEvent, + Event: GlobalEvent, + EventTarget: DOMEventTarget, + InputEvent, + NamedNodeMap, + NodeList + } +); +var window = /* @__PURE__ */ new WeakMap(); +var Document2 = class extends NonElementParentNode { + constructor(type) { + super(null, "#document", DOCUMENT_NODE); + this[CUSTOM_ELEMENTS] = { active: false, registry: null }; + this[MUTATION_OBSERVER] = { active: false, class: null }; + this[MIME] = Mime[type]; + this[DOCTYPE] = null; + this[DOM_PARSER] = null; + this[GLOBALS] = null; + this[IMAGE] = null; + this[UPGRADE] = null; + } + /** + * @type {globalThis.Document['defaultView']} + */ + get defaultView() { + if (!window.has(this)) + window.set(this, new Proxy(globalThis, { + set: (target, name, value) => { + switch (name) { + case "addEventListener": + case "removeEventListener": + case "dispatchEvent": + this[EVENT_TARGET][name] = value; + break; + default: + target[name] = value; + break; + } + return true; + }, + get: (globalThis2, name) => { + switch (name) { + case "addEventListener": + case "removeEventListener": + case "dispatchEvent": + if (!this[EVENT_TARGET]) { + const et = this[EVENT_TARGET] = new DOMEventTarget(); + et.dispatchEvent = et.dispatchEvent.bind(et); + et.addEventListener = et.addEventListener.bind(et); + et.removeEventListener = et.removeEventListener.bind(et); + } + return this[EVENT_TARGET][name]; + case "document": + return this; + /* c8 ignore start */ + case "navigator": + return { + userAgent: "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.150 Safari/537.36" + }; + /* c8 ignore stop */ + case "window": + return window.get(this); + case "customElements": + if (!this[CUSTOM_ELEMENTS].registry) + this[CUSTOM_ELEMENTS] = new CustomElementRegistry(this); + return this[CUSTOM_ELEMENTS]; + case "performance": + return import_perf_hooks.performance; + case "DOMParser": + return this[DOM_PARSER]; + case "Image": + if (!this[IMAGE]) + this[IMAGE] = ImageClass(this); + return this[IMAGE]; + case "MutationObserver": + if (!this[MUTATION_OBSERVER].class) + this[MUTATION_OBSERVER] = new MutationObserverClass(this); + return this[MUTATION_OBSERVER].class; + } + return this[GLOBALS] && this[GLOBALS][name] || globalExports[name] || globalThis2[name]; + } + })); + return window.get(this); + } + get doctype() { + const docType = this[DOCTYPE]; + if (docType) + return docType; + const { firstChild } = this; + if (firstChild && firstChild.nodeType === DOCUMENT_TYPE_NODE) + return this[DOCTYPE] = firstChild; + return null; + } + set doctype(value) { + if (/^([a-z:]+)(\s+system|\s+public(\s+"([^"]+)")?)?(\s+"([^"]+)")?/i.test(value)) { + const { $1: name, $4: publicId, $6: systemId } = RegExp; + this[DOCTYPE] = new DocumentType(this, name, publicId, systemId); + knownSiblings(this, this[DOCTYPE], this[NEXT]); + } + } + get documentElement() { + return this.firstElementChild; + } + get isConnected() { + return true; + } + /** + * @protected + */ + _getParent() { + return this[EVENT_TARGET]; + } + createAttribute(name) { + return new Attr(this, name); + } + createCDATASection(data) { + return new CDATASection(this, data); + } + createComment(textContent2) { + return new Comment3(this, textContent2); + } + createDocumentFragment() { + return new DocumentFragment(this); + } + createDocumentType(name, publicId, systemId) { + return new DocumentType(this, name, publicId, systemId); + } + createElement(localName) { + return new Element2(this, localName); + } + createRange() { + const range = new Range(); + range.commonAncestorContainer = this; + return range; + } + createTextNode(textContent2) { + return new Text3(this, textContent2); + } + createTreeWalker(root, whatToShow = -1) { + return new TreeWalker(root, whatToShow); + } + createNodeIterator(root, whatToShow = -1) { + return this.createTreeWalker(root, whatToShow); + } + createEvent(name) { + const event = create(name === "Event" ? new GlobalEvent("") : new CustomEvent("")); + event.initEvent = event.initCustomEvent = (type, canBubble = false, cancelable = false, detail) => { + event.bubbles = !!canBubble; + defineProperties(event, { + type: { value: type }, + canBubble: { value: canBubble }, + cancelable: { value: cancelable }, + detail: { value: detail } + }); + }; + return event; + } + cloneNode(deep = false) { + const { + constructor, + [CUSTOM_ELEMENTS]: customElements2, + [DOCTYPE]: doctype + } = this; + const document = new constructor(); + document[CUSTOM_ELEMENTS] = customElements2; + if (deep) { + const end = document[END]; + const { childNodes } = this; + for (let { length } = childNodes, i = 0; i < length; i++) + document.insertBefore(childNodes[i].cloneNode(true), end); + if (doctype) + document[DOCTYPE] = childNodes[0]; + } + return document; + } + importNode(externalNode) { + const deep = 1 < arguments.length && !!arguments[1]; + const node = externalNode.cloneNode(deep); + const { [CUSTOM_ELEMENTS]: customElements2 } = this; + const { active } = customElements2; + const upgrade = (element) => { + const { ownerDocument, nodeType } = element; + element.ownerDocument = this; + if (active && ownerDocument !== this && nodeType === ELEMENT_NODE) + customElements2.upgrade(element); + }; + upgrade(node); + if (deep) { + switch (node.nodeType) { + case ELEMENT_NODE: + case DOCUMENT_FRAGMENT_NODE: { + let { [NEXT]: next, [END]: end } = node; + while (next !== end) { + if (next.nodeType === ELEMENT_NODE) + upgrade(next); + next = next[NEXT]; + } + break; + } + } + } + return node; + } + toString() { + return this.childNodes.join(""); + } + querySelector(selectors) { + return query(super.querySelector, this, selectors); + } + querySelectorAll(selectors) { + return query(super.querySelectorAll, this, selectors); + } + /* c8 ignore start */ + getElementsByTagNameNS(_, name) { + return this.getElementsByTagName(name); + } + createAttributeNS(_, name) { + return this.createAttribute(name); + } + createElementNS(nsp, localName, options) { + return nsp === SVG_NAMESPACE ? new SVGElement(this, localName, null) : this.createElement(localName, options); + } + /* c8 ignore stop */ +}; +setPrototypeOf( + globalExports.Document = function Document3() { + illegalConstructor(); + }, + Document2 +).prototype = Document2.prototype; + +// node_modules/linkedom/esm/html/document.js +var createHTMLElement = (ownerDocument, builtin, localName, options) => { + if (!builtin && htmlClasses.has(localName)) { + const Class = htmlClasses.get(localName); + return new Class(ownerDocument, localName); + } + const { [CUSTOM_ELEMENTS]: { active, registry } } = ownerDocument; + if (active) { + const ce = builtin ? options.is : localName; + if (registry.has(ce)) { + const { Class } = registry.get(ce); + const element = new Class(ownerDocument, localName); + customElements.set(element, { connected: false }); + return element; + } + } + return new HTMLElement(ownerDocument, localName); +}; +var HTMLDocument = class extends Document2 { + constructor() { + super("text/html"); + } + get all() { + const nodeList = new NodeList(); + let { [NEXT]: next, [END]: end } = this; + while (next !== end) { + switch (next.nodeType) { + case ELEMENT_NODE: + nodeList.push(next); + break; + } + next = next[NEXT]; + } + return nodeList; + } + /** + * @type HTMLHeadElement + */ + get head() { + const { documentElement } = this; + let { firstElementChild } = documentElement; + if (!firstElementChild || firstElementChild.tagName !== "HEAD") { + firstElementChild = this.createElement("head"); + documentElement.prepend(firstElementChild); + } + return firstElementChild; + } + /** + * @type HTMLBodyElement + */ + get body() { + const { head } = this; + let { nextElementSibling: nextElementSibling3 } = head; + if (!nextElementSibling3 || nextElementSibling3.tagName !== "BODY") { + nextElementSibling3 = this.createElement("body"); + head.after(nextElementSibling3); + } + return nextElementSibling3; + } + /** + * @type HTMLTitleElement + */ + get title() { + const { head } = this; + return head.getElementsByTagName("title").at(0)?.textContent || ""; + } + set title(textContent2) { + const { head } = this; + let title = head.getElementsByTagName("title").at(0); + if (title) + title.textContent = textContent2; + else { + head.insertBefore( + this.createElement("title"), + head.firstChild + ).textContent = textContent2; + } + } + createElement(localName, options) { + const builtin = !!(options && options.is); + const element = createHTMLElement(this, builtin, localName, options); + if (builtin) + element.setAttribute("is", options.is); + return element; + } +}; + +// node_modules/linkedom/esm/svg/document.js +var SVGDocument = class extends Document2 { + constructor() { + super("image/svg+xml"); + } + toString() { + return this[MIME].docType + super.toString(); + } +}; + +// node_modules/linkedom/esm/xml/document.js +var XMLDocument = class extends Document2 { + constructor() { + super("text/xml"); + } + toString() { + return this[MIME].docType + super.toString(); + } +}; + +// node_modules/linkedom/esm/dom/parser.js +var DOMParser = class _DOMParser { + /** @typedef {{ "text/html": HTMLDocument, "image/svg+xml": SVGDocument, "text/xml": XMLDocument }} MimeToDoc */ + /** + * @template {keyof MimeToDoc} MIME + * @param {string} markupLanguage + * @param {MIME} mimeType + * @returns {MimeToDoc[MIME]} + */ + parseFromString(markupLanguage, mimeType, globals = null) { + let isHTML = false, document; + if (mimeType === "text/html") { + isHTML = true; + document = new HTMLDocument(); + } else if (mimeType === "image/svg+xml") + document = new SVGDocument(); + else + document = new XMLDocument(); + document[DOM_PARSER] = _DOMParser; + if (globals) + document[GLOBALS] = globals; + if (isHTML && markupLanguage === "...") + markupLanguage = "<!doctype html><html><head></head><body></body></html>"; + return markupLanguage ? parseFromString(document, isHTML, markupLanguage) : document; + } +}; + +// node_modules/linkedom/esm/shared/parse-json.js +var { parse: parse4 } = JSON; +var append3 = (parentNode, node, end) => { + node.parentNode = parentNode; + knownSiblings(end[PREV], node, end); +}; +var createHTMLElement2 = (ownerDocument, localName) => { + if (htmlClasses.has(localName)) { + const Class = htmlClasses.get(localName); + return new Class(ownerDocument, localName); + } + return new HTMLElement(ownerDocument, localName); +}; +var parseJSON = (value) => { + const array = typeof value === "string" ? parse4(value) : value; + const { length } = array; + const document = new HTMLDocument(); + let parentNode = document, end = parentNode[END], svg = false, i = 0; + while (i < length) { + let nodeType = array[i++]; + switch (nodeType) { + case ELEMENT_NODE: { + const localName = array[i++]; + const isSVG = svg || localName === "svg" || localName === "SVG"; + const element = isSVG ? new SVGElement(document, localName, parentNode.ownerSVGElement || null) : createHTMLElement2(document, localName); + knownBoundaries(end[PREV], element, end); + element.parentNode = parentNode; + parentNode = element; + end = parentNode[END]; + svg = isSVG; + break; + } + case ATTRIBUTE_NODE: { + const name = array[i++]; + const value2 = typeof array[i] === "string" ? array[i++] : ""; + const attr = new Attr(document, name, value2); + attr.ownerElement = parentNode; + knownSiblings(end[PREV], attr, end); + break; + } + case TEXT_NODE: + append3(parentNode, new Text3(document, array[i++]), end); + break; + case COMMENT_NODE: + append3(parentNode, new Comment3(document, array[i++]), end); + break; + case CDATA_SECTION_NODE: + append3(parentNode, new CDATASection(document, array[i++]), end); + break; + case DOCUMENT_TYPE_NODE: { + const args = [document]; + while (typeof array[i] === "string") + args.push(array[i++]); + if (args.length === 3 && /\.dtd$/i.test(args[2])) + args.splice(2, 0, ""); + append3(parentNode, new DocumentType(...args), end); + break; + } + case DOCUMENT_FRAGMENT_NODE: + parentNode = document.createDocumentFragment(); + end = parentNode[END]; + /* eslint no-fallthrough:0 */ + case DOCUMENT_NODE: + break; + default: + do { + nodeType -= NODE_END; + if (svg && !parentNode.ownerSVGElement) + svg = false; + parentNode = parentNode.parentNode || parentNode; + } while (nodeType < 0); + end = parentNode[END]; + break; + } + } + switch (i && array[0]) { + case ELEMENT_NODE: + return document.firstElementChild; + case DOCUMENT_FRAGMENT_NODE: + return parentNode; + } + return document; +}; +var toJSON = (node) => node.toJSON(); + +// node_modules/linkedom/esm/interface/node-filter.js +var NodeFilter = class { + static get SHOW_ALL() { + return SHOW_ALL; + } + static get SHOW_ELEMENT() { + return SHOW_ELEMENT; + } + static get SHOW_COMMENT() { + return SHOW_COMMENT; + } + static get SHOW_CDATA_SECTION() { + return SHOW_CDATA_SECTION; + } + static get SHOW_TEXT() { + return SHOW_TEXT; + } +}; + +// node_modules/linkedom/esm/index.js +var parseHTML = (html, globals = null) => new DOMParser().parseFromString( + html, + "text/html", + globals +).defaultView; +function Document4() { + illegalConstructor(); +} +setPrototypeOf(Document4, Document2).prototype = Document2.prototype; +export { + Attr2 as Attr, + CDATASection2 as CDATASection, + CharacterData2 as CharacterData, + Comment4 as Comment, + CustomEvent, + DOMParser, + Document4 as Document, + DocumentFragment2 as DocumentFragment, + DocumentType2 as DocumentType, + Element3 as Element, + GlobalEvent as Event, + DOMEventTarget as EventTarget, + Facades, + HTMLAnchorElement, + HTMLAreaElement, + HTMLAudioElement, + HTMLBRElement, + HTMLBaseElement, + HTMLBodyElement, + HTMLButtonElement, + HTMLCanvasElement, + HTMLClasses, + HTMLDListElement, + HTMLDataElement, + HTMLDataListElement, + HTMLDetailsElement, + HTMLDirectoryElement, + HTMLDivElement, + HTMLElement, + HTMLEmbedElement, + HTMLFieldSetElement, + HTMLFontElement, + HTMLFormElement, + HTMLFrameElement, + HTMLFrameSetElement, + HTMLHRElement, + HTMLHeadElement, + HTMLHeadingElement, + HTMLHtmlElement, + HTMLIFrameElement, + HTMLImageElement, + HTMLInputElement, + HTMLLIElement, + HTMLLabelElement, + HTMLLegendElement, + HTMLLinkElement, + HTMLMapElement, + HTMLMarqueeElement, + HTMLMediaElement, + HTMLMenuElement, + HTMLMetaElement, + HTMLMeterElement, + HTMLModElement, + HTMLOListElement, + HTMLObjectElement, + HTMLOptGroupElement, + HTMLOptionElement, + HTMLOutputElement, + HTMLParagraphElement, + HTMLParamElement, + HTMLPictureElement, + HTMLPreElement, + HTMLProgressElement, + HTMLQuoteElement, + HTMLScriptElement, + HTMLSelectElement, + HTMLSlotElement, + HTMLSourceElement, + HTMLSpanElement, + HTMLStyleElement, + HTMLTableCaptionElement, + HTMLTableCellElement, + HTMLTableElement, + HTMLTableRowElement, + HTMLTemplateElement, + HTMLTextAreaElement, + HTMLTimeElement, + HTMLTitleElement, + HTMLTrackElement, + HTMLUListElement, + HTMLUnknownElement, + HTMLVideoElement, + InputEvent, + Node3 as Node, + NodeFilter, + NodeList, + SVGElement2 as SVGElement, + ShadowRoot2 as ShadowRoot, + Text4 as Text, + illegalConstructor, + parseHTML, + parseJSON, + toJSON +}; diff --git a/jamfile/js_builtins/marked.js b/jamfile/js_builtins/marked.js new file mode 100644 index 0000000000..4617ff167e --- /dev/null +++ b/jamfile/js_builtins/marked.js @@ -0,0 +1,2157 @@ +/** + * Original source: https://github.com/markedjs/marked + * + * Generated from npm:marked@15.0.5 using: + * + * ```bash + * echo 'export * from "marked"' \ + * | ./node_modules/.bin/esbuild --bundle --format=esm \ + * > ../jamfile/js_builtins/marked.js + * ``` + * + */ + + +// node_modules/marked/lib/marked.esm.js +function _getDefaults() { + return { + async: false, + breaks: false, + extensions: null, + gfm: true, + hooks: null, + pedantic: false, + renderer: null, + silent: false, + tokenizer: null, + walkTokens: null + }; +} +var _defaults = _getDefaults(); +function changeDefaults(newDefaults) { + _defaults = newDefaults; +} +var noopTest = { exec: () => null }; +function edit(regex, opt = "") { + let source = typeof regex === "string" ? regex : regex.source; + const obj = { + replace: (name, val) => { + let valSource = typeof val === "string" ? val : val.source; + valSource = valSource.replace(other.caret, "$1"); + source = source.replace(name, valSource); + return obj; + }, + getRegex: () => { + return new RegExp(source, opt); + } + }; + return obj; +} +var other = { + codeRemoveIndent: /^(?: {1,4}| {0,3}\t)/gm, + outputLinkReplace: /\\([\[\]])/g, + indentCodeCompensation: /^(\s+)(?:```)/, + beginningSpace: /^\s+/, + endingHash: /#$/, + startingSpaceChar: /^ /, + endingSpaceChar: / $/, + nonSpaceChar: /[^ ]/, + newLineCharGlobal: /\n/g, + tabCharGlobal: /\t/g, + multipleSpaceGlobal: /\s+/g, + blankLine: /^[ \t]*$/, + doubleBlankLine: /\n[ \t]*\n[ \t]*$/, + blockquoteStart: /^ {0,3}>/, + blockquoteSetextReplace: /\n {0,3}((?:=+|-+) *)(?=\n|$)/g, + blockquoteSetextReplace2: /^ {0,3}>[ \t]?/gm, + listReplaceTabs: /^\t+/, + listReplaceNesting: /^ {1,4}(?=( {4})*[^ ])/g, + listIsTask: /^\[[ xX]\] /, + listReplaceTask: /^\[[ xX]\] +/, + anyLine: /\n.*\n/, + hrefBrackets: /^<(.*)>$/, + tableDelimiter: /[:|]/, + tableAlignChars: /^\||\| *$/g, + tableRowBlankLine: /\n[ \t]*$/, + tableAlignRight: /^ *-+: *$/, + tableAlignCenter: /^ *:-+: *$/, + tableAlignLeft: /^ *:-+ *$/, + startATag: /^<a /i, + endATag: /^<\/a>/i, + startPreScriptTag: /^<(pre|code|kbd|script)(\s|>)/i, + endPreScriptTag: /^<\/(pre|code|kbd|script)(\s|>)/i, + startAngleBracket: /^</, + endAngleBracket: />$/, + pedanticHrefTitle: /^([^'"]*[^\s])\s+(['"])(.*)\2/, + unicodeAlphaNumeric: /[\p{L}\p{N}]/u, + escapeTest: /[&<>"']/, + escapeReplace: /[&<>"']/g, + escapeTestNoEncode: /[<>"']|&(?!(#\d{1,7}|#[Xx][a-fA-F0-9]{1,6}|\w+);)/, + escapeReplaceNoEncode: /[<>"']|&(?!(#\d{1,7}|#[Xx][a-fA-F0-9]{1,6}|\w+);)/g, + unescapeTest: /&(#(?:\d+)|(?:#x[0-9A-Fa-f]+)|(?:\w+));?/ig, + caret: /(^|[^\[])\^/g, + percentDecode: /%25/g, + findPipe: /\|/g, + splitPipe: / \|/, + slashPipe: /\\\|/g, + carriageReturn: /\r\n|\r/g, + spaceLine: /^ +$/gm, + notSpaceStart: /^\S*/, + endingNewline: /\n$/, + listItemRegex: (bull) => new RegExp(`^( {0,3}${bull})((?:[ ][^\\n]*)?(?:\\n|$))`), + nextBulletRegex: (indent) => new RegExp(`^ {0,${Math.min(3, indent - 1)}}(?:[*+-]|\\d{1,9}[.)])((?:[ ][^\\n]*)?(?:\\n|$))`), + hrRegex: (indent) => new RegExp(`^ {0,${Math.min(3, indent - 1)}}((?:- *){3,}|(?:_ *){3,}|(?:\\* *){3,})(?:\\n+|$)`), + fencesBeginRegex: (indent) => new RegExp(`^ {0,${Math.min(3, indent - 1)}}(?:\`\`\`|~~~)`), + headingBeginRegex: (indent) => new RegExp(`^ {0,${Math.min(3, indent - 1)}}#`), + htmlBeginRegex: (indent) => new RegExp(`^ {0,${Math.min(3, indent - 1)}}<(?:[a-z].*>|!--)`, "i") +}; +var newline = /^(?:[ \t]*(?:\n|$))+/; +var blockCode = /^((?: {4}| {0,3}\t)[^\n]+(?:\n(?:[ \t]*(?:\n|$))*)?)+/; +var fences = /^ {0,3}(`{3,}(?=[^`\n]*(?:\n|$))|~{3,})([^\n]*)(?:\n|$)(?:|([\s\S]*?)(?:\n|$))(?: {0,3}\1[~`]* *(?=\n|$)|$)/; +var hr = /^ {0,3}((?:-[\t ]*){3,}|(?:_[ \t]*){3,}|(?:\*[ \t]*){3,})(?:\n+|$)/; +var heading = /^ {0,3}(#{1,6})(?=\s|$)(.*)(?:\n+|$)/; +var bullet = /(?:[*+-]|\d{1,9}[.)])/; +var lheading = edit(/^(?!bull |blockCode|fences|blockquote|heading|html)((?:.|\n(?!\s*?\n|bull |blockCode|fences|blockquote|heading|html))+?)\n {0,3}(=+|-+) *(?:\n+|$)/).replace(/bull/g, bullet).replace(/blockCode/g, /(?: {4}| {0,3}\t)/).replace(/fences/g, / {0,3}(?:`{3,}|~{3,})/).replace(/blockquote/g, / {0,3}>/).replace(/heading/g, / {0,3}#{1,6}/).replace(/html/g, / {0,3}<[^\n>]+>\n/).getRegex(); +var _paragraph = /^([^\n]+(?:\n(?!hr|heading|lheading|blockquote|fences|list|html|table| +\n)[^\n]+)*)/; +var blockText = /^[^\n]+/; +var _blockLabel = /(?!\s*\])(?:\\.|[^\[\]\\])+/; +var def = edit(/^ {0,3}\[(label)\]: *(?:\n[ \t]*)?([^<\s][^\s]*|<.*?>)(?:(?: +(?:\n[ \t]*)?| *\n[ \t]*)(title))? *(?:\n+|$)/).replace("label", _blockLabel).replace("title", /(?:"(?:\\"?|[^"\\])*"|'[^'\n]*(?:\n[^'\n]+)*\n?'|\([^()]*\))/).getRegex(); +var list = edit(/^( {0,3}bull)([ \t][^\n]+?)?(?:\n|$)/).replace(/bull/g, bullet).getRegex(); +var _tag = "address|article|aside|base|basefont|blockquote|body|caption|center|col|colgroup|dd|details|dialog|dir|div|dl|dt|fieldset|figcaption|figure|footer|form|frame|frameset|h[1-6]|head|header|hr|html|iframe|legend|li|link|main|menu|menuitem|meta|nav|noframes|ol|optgroup|option|p|param|search|section|summary|table|tbody|td|tfoot|th|thead|title|tr|track|ul"; +var _comment = /<!--(?:-?>|[\s\S]*?(?:-->|$))/; +var html = edit("^ {0,3}(?:<(script|pre|style|textarea)[\\s>][\\s\\S]*?(?:</\\1>[^\\n]*\\n+|$)|comment[^\\n]*(\\n+|$)|<\\?[\\s\\S]*?(?:\\?>\\n*|$)|<![A-Z][\\s\\S]*?(?:>\\n*|$)|<!\\[CDATA\\[[\\s\\S]*?(?:\\]\\]>\\n*|$)|</?(tag)(?: +|\\n|/?>)[\\s\\S]*?(?:(?:\\n[ ]*)+\\n|$)|<(?!script|pre|style|textarea)([a-z][\\w-]*)(?:attribute)*? */?>(?=[ \\t]*(?:\\n|$))[\\s\\S]*?(?:(?:\\n[ ]*)+\\n|$)|</(?!script|pre|style|textarea)[a-z][\\w-]*\\s*>(?=[ \\t]*(?:\\n|$))[\\s\\S]*?(?:(?:\\n[ ]*)+\\n|$))", "i").replace("comment", _comment).replace("tag", _tag).replace("attribute", / +[a-zA-Z:_][\w.:-]*(?: *= *"[^"\n]*"| *= *'[^'\n]*'| *= *[^\s"'=<>`]+)?/).getRegex(); +var paragraph = edit(_paragraph).replace("hr", hr).replace("heading", " {0,3}#{1,6}(?:\\s|$)").replace("|lheading", "").replace("|table", "").replace("blockquote", " {0,3}>").replace("fences", " {0,3}(?:`{3,}(?=[^`\\n]*\\n)|~{3,})[^\\n]*\\n").replace("list", " {0,3}(?:[*+-]|1[.)]) ").replace("html", "</?(?:tag)(?: +|\\n|/?>)|<(?:script|pre|style|textarea|!--)").replace("tag", _tag).getRegex(); +var blockquote = edit(/^( {0,3}> ?(paragraph|[^\n]*)(?:\n|$))+/).replace("paragraph", paragraph).getRegex(); +var blockNormal = { + blockquote, + code: blockCode, + def, + fences, + heading, + hr, + html, + lheading, + list, + newline, + paragraph, + table: noopTest, + text: blockText +}; +var gfmTable = edit("^ *([^\\n ].*)\\n {0,3}((?:\\| *)?:?-+:? *(?:\\| *:?-+:? *)*(?:\\| *)?)(?:\\n((?:(?! *\\n|hr|heading|blockquote|code|fences|list|html).*(?:\\n|$))*)\\n*|$)").replace("hr", hr).replace("heading", " {0,3}#{1,6}(?:\\s|$)").replace("blockquote", " {0,3}>").replace("code", "(?: {4}| {0,3} )[^\\n]").replace("fences", " {0,3}(?:`{3,}(?=[^`\\n]*\\n)|~{3,})[^\\n]*\\n").replace("list", " {0,3}(?:[*+-]|1[.)]) ").replace("html", "</?(?:tag)(?: +|\\n|/?>)|<(?:script|pre|style|textarea|!--)").replace("tag", _tag).getRegex(); +var blockGfm = { + ...blockNormal, + table: gfmTable, + paragraph: edit(_paragraph).replace("hr", hr).replace("heading", " {0,3}#{1,6}(?:\\s|$)").replace("|lheading", "").replace("table", gfmTable).replace("blockquote", " {0,3}>").replace("fences", " {0,3}(?:`{3,}(?=[^`\\n]*\\n)|~{3,})[^\\n]*\\n").replace("list", " {0,3}(?:[*+-]|1[.)]) ").replace("html", "</?(?:tag)(?: +|\\n|/?>)|<(?:script|pre|style|textarea|!--)").replace("tag", _tag).getRegex() +}; +var blockPedantic = { + ...blockNormal, + html: edit(`^ *(?:comment *(?:\\n|\\s*$)|<(tag)[\\s\\S]+?</\\1> *(?:\\n{2,}|\\s*$)|<tag(?:"[^"]*"|'[^']*'|\\s[^'"/>\\s]*)*?/?> *(?:\\n{2,}|\\s*$))`).replace("comment", _comment).replace(/tag/g, "(?!(?:a|em|strong|small|s|cite|q|dfn|abbr|data|time|code|var|samp|kbd|sub|sup|i|b|u|mark|ruby|rt|rp|bdi|bdo|span|br|wbr|ins|del|img)\\b)\\w+(?!:|[^\\w\\s@]*@)\\b").getRegex(), + def: /^ *\[([^\]]+)\]: *<?([^\s>]+)>?(?: +(["(][^\n]+[")]))? *(?:\n+|$)/, + heading: /^(#{1,6})(.*)(?:\n+|$)/, + fences: noopTest, + // fences not supported + lheading: /^(.+?)\n {0,3}(=+|-+) *(?:\n+|$)/, + paragraph: edit(_paragraph).replace("hr", hr).replace("heading", " *#{1,6} *[^\n]").replace("lheading", lheading).replace("|table", "").replace("blockquote", " {0,3}>").replace("|fences", "").replace("|list", "").replace("|html", "").replace("|tag", "").getRegex() +}; +var escape$1 = /^\\([!"#$%&'()*+,\-./:;<=>?@\[\]\\^_`{|}~])/; +var inlineCode = /^(`+)([^`]|[^`][\s\S]*?[^`])\1(?!`)/; +var br = /^( {2,}|\\)\n(?!\s*$)/; +var inlineText = /^(`+|[^`])(?:(?= {2,}\n)|[\s\S]*?(?:(?=[\\<!\[`*_]|\b_|$)|[^ ](?= {2,}\n)))/; +var _punctuation = /[\p{P}\p{S}]/u; +var _punctuationOrSpace = /[\s\p{P}\p{S}]/u; +var _notPunctuationOrSpace = /[^\s\p{P}\p{S}]/u; +var punctuation = edit(/^((?![*_])punctSpace)/, "u").replace(/punctSpace/g, _punctuationOrSpace).getRegex(); +var _punctuationGfmStrongEm = /(?!~)[\p{P}\p{S}]/u; +var _punctuationOrSpaceGfmStrongEm = /(?!~)[\s\p{P}\p{S}]/u; +var _notPunctuationOrSpaceGfmStrongEm = /(?:[^\s\p{P}\p{S}]|~)/u; +var blockSkip = /\[[^[\]]*?\]\((?:\\.|[^\\\(\)]|\((?:\\.|[^\\\(\)])*\))*\)|`[^`]*?`|<[^<>]*?>/g; +var emStrongLDelim = edit(/^(?:\*+(?:((?!\*)punct)|[^\s*]))|^_+(?:((?!_)punct)|([^\s_]))/, "u").replace(/punct/g, _punctuation).getRegex(); +var emStrongRDelimAstCore = "^[^_*]*?__[^_*]*?\\*[^_*]*?(?=__)|[^*]+(?=[^*])|(?!\\*)punct(\\*+)(?=[\\s]|$)|notPunctSpace(\\*+)(?!\\*)(?=punctSpace|$)|(?!\\*)punctSpace(\\*+)(?=notPunctSpace)|[\\s](\\*+)(?!\\*)(?=punct)|(?!\\*)punct(\\*+)(?!\\*)(?=punct)|notPunctSpace(\\*+)(?=notPunctSpace)"; +var emStrongRDelimAst = edit(emStrongRDelimAstCore, "gu").replace(/notPunctSpace/g, _notPunctuationOrSpace).replace(/punctSpace/g, _punctuationOrSpace).replace(/punct/g, _punctuation).getRegex(); +var emStrongRDelimAstGfm = edit(emStrongRDelimAstCore, "gu").replace(/notPunctSpace/g, _notPunctuationOrSpaceGfmStrongEm).replace(/punctSpace/g, _punctuationOrSpaceGfmStrongEm).replace(/punct/g, _punctuationGfmStrongEm).getRegex(); +var emStrongRDelimUnd = edit("^[^_*]*?\\*\\*[^_*]*?_[^_*]*?(?=\\*\\*)|[^_]+(?=[^_])|(?!_)punct(_+)(?=[\\s]|$)|notPunctSpace(_+)(?!_)(?=punctSpace|$)|(?!_)punctSpace(_+)(?=notPunctSpace)|[\\s](_+)(?!_)(?=punct)|(?!_)punct(_+)(?!_)(?=punct)", "gu").replace(/notPunctSpace/g, _notPunctuationOrSpace).replace(/punctSpace/g, _punctuationOrSpace).replace(/punct/g, _punctuation).getRegex(); +var anyPunctuation = edit(/\\(punct)/, "gu").replace(/punct/g, _punctuation).getRegex(); +var autolink = edit(/^<(scheme:[^\s\x00-\x1f<>]*|email)>/).replace("scheme", /[a-zA-Z][a-zA-Z0-9+.-]{1,31}/).replace("email", /[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+(@)[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)+(?![-_])/).getRegex(); +var _inlineComment = edit(_comment).replace("(?:-->|$)", "-->").getRegex(); +var tag = edit("^comment|^</[a-zA-Z][\\w:-]*\\s*>|^<[a-zA-Z][\\w-]*(?:attribute)*?\\s*/?>|^<\\?[\\s\\S]*?\\?>|^<![a-zA-Z]+\\s[\\s\\S]*?>|^<!\\[CDATA\\[[\\s\\S]*?\\]\\]>").replace("comment", _inlineComment).replace("attribute", /\s+[a-zA-Z:_][\w.:-]*(?:\s*=\s*"[^"]*"|\s*=\s*'[^']*'|\s*=\s*[^\s"'=<>`]+)?/).getRegex(); +var _inlineLabel = /(?:\[(?:\\.|[^\[\]\\])*\]|\\.|`[^`]*`|[^\[\]\\`])*?/; +var link = edit(/^!?\[(label)\]\(\s*(href)(?:\s+(title))?\s*\)/).replace("label", _inlineLabel).replace("href", /<(?:\\.|[^\n<>\\])+>|[^\s\x00-\x1f]*/).replace("title", /"(?:\\"?|[^"\\])*"|'(?:\\'?|[^'\\])*'|\((?:\\\)?|[^)\\])*\)/).getRegex(); +var reflink = edit(/^!?\[(label)\]\[(ref)\]/).replace("label", _inlineLabel).replace("ref", _blockLabel).getRegex(); +var nolink = edit(/^!?\[(ref)\](?:\[\])?/).replace("ref", _blockLabel).getRegex(); +var reflinkSearch = edit("reflink|nolink(?!\\()", "g").replace("reflink", reflink).replace("nolink", nolink).getRegex(); +var inlineNormal = { + _backpedal: noopTest, + // only used for GFM url + anyPunctuation, + autolink, + blockSkip, + br, + code: inlineCode, + del: noopTest, + emStrongLDelim, + emStrongRDelimAst, + emStrongRDelimUnd, + escape: escape$1, + link, + nolink, + punctuation, + reflink, + reflinkSearch, + tag, + text: inlineText, + url: noopTest +}; +var inlinePedantic = { + ...inlineNormal, + link: edit(/^!?\[(label)\]\((.*?)\)/).replace("label", _inlineLabel).getRegex(), + reflink: edit(/^!?\[(label)\]\s*\[([^\]]*)\]/).replace("label", _inlineLabel).getRegex() +}; +var inlineGfm = { + ...inlineNormal, + emStrongRDelimAst: emStrongRDelimAstGfm, + url: edit(/^((?:ftp|https?):\/\/|www\.)(?:[a-zA-Z0-9\-]+\.?)+[^\s<]*|^email/, "i").replace("email", /[A-Za-z0-9._+-]+(@)[a-zA-Z0-9-_]+(?:\.[a-zA-Z0-9-_]*[a-zA-Z0-9])+(?![-_])/).getRegex(), + _backpedal: /(?:[^?!.,:;*_'"~()&]+|\([^)]*\)|&(?![a-zA-Z0-9]+;$)|[?!.,:;*_'"~)]+(?!$))+/, + del: /^(~~?)(?=[^\s~])((?:\\.|[^\\])*?(?:\\.|[^\s~\\]))\1(?=[^~]|$)/, + text: /^([`~]+|[^`~])(?:(?= {2,}\n)|(?=[a-zA-Z0-9.!#$%&'*+\/=?_`{\|}~-]+@)|[\s\S]*?(?:(?=[\\<!\[`*~_]|\b_|https?:\/\/|ftp:\/\/|www\.|$)|[^ ](?= {2,}\n)|[^a-zA-Z0-9.!#$%&'*+\/=?_`{\|}~-](?=[a-zA-Z0-9.!#$%&'*+\/=?_`{\|}~-]+@)))/ +}; +var inlineBreaks = { + ...inlineGfm, + br: edit(br).replace("{2,}", "*").getRegex(), + text: edit(inlineGfm.text).replace("\\b_", "\\b_| {2,}\\n").replace(/\{2,\}/g, "*").getRegex() +}; +var block = { + normal: blockNormal, + gfm: blockGfm, + pedantic: blockPedantic +}; +var inline = { + normal: inlineNormal, + gfm: inlineGfm, + breaks: inlineBreaks, + pedantic: inlinePedantic +}; +var escapeReplacements = { + "&": "&", + "<": "<", + ">": ">", + '"': """, + "'": "'" +}; +var getEscapeReplacement = (ch) => escapeReplacements[ch]; +function escape(html2, encode) { + if (encode) { + if (other.escapeTest.test(html2)) { + return html2.replace(other.escapeReplace, getEscapeReplacement); + } + } else { + if (other.escapeTestNoEncode.test(html2)) { + return html2.replace(other.escapeReplaceNoEncode, getEscapeReplacement); + } + } + return html2; +} +function cleanUrl(href) { + try { + href = encodeURI(href).replace(other.percentDecode, "%"); + } catch { + return null; + } + return href; +} +function splitCells(tableRow, count) { + const row = tableRow.replace(other.findPipe, (match, offset, str) => { + let escaped = false; + let curr = offset; + while (--curr >= 0 && str[curr] === "\\") + escaped = !escaped; + if (escaped) { + return "|"; + } else { + return " |"; + } + }), cells = row.split(other.splitPipe); + let i = 0; + if (!cells[0].trim()) { + cells.shift(); + } + if (cells.length > 0 && !cells.at(-1)?.trim()) { + cells.pop(); + } + if (count) { + if (cells.length > count) { + cells.splice(count); + } else { + while (cells.length < count) + cells.push(""); + } + } + for (; i < cells.length; i++) { + cells[i] = cells[i].trim().replace(other.slashPipe, "|"); + } + return cells; +} +function rtrim(str, c, invert) { + const l = str.length; + if (l === 0) { + return ""; + } + let suffLen = 0; + while (suffLen < l) { + const currChar = str.charAt(l - suffLen - 1); + if (currChar === c && !invert) { + suffLen++; + } else { + break; + } + } + return str.slice(0, l - suffLen); +} +function findClosingBracket(str, b) { + if (str.indexOf(b[1]) === -1) { + return -1; + } + let level = 0; + for (let i = 0; i < str.length; i++) { + if (str[i] === "\\") { + i++; + } else if (str[i] === b[0]) { + level++; + } else if (str[i] === b[1]) { + level--; + if (level < 0) { + return i; + } + } + } + return -1; +} +function outputLink(cap, link2, raw, lexer2, rules) { + const href = link2.href; + const title = link2.title || null; + const text = cap[1].replace(rules.other.outputLinkReplace, "$1"); + if (cap[0].charAt(0) !== "!") { + lexer2.state.inLink = true; + const token = { + type: "link", + raw, + href, + title, + text, + tokens: lexer2.inlineTokens(text) + }; + lexer2.state.inLink = false; + return token; + } + return { + type: "image", + raw, + href, + title, + text + }; +} +function indentCodeCompensation(raw, text, rules) { + const matchIndentToCode = raw.match(rules.other.indentCodeCompensation); + if (matchIndentToCode === null) { + return text; + } + const indentToCode = matchIndentToCode[1]; + return text.split("\n").map((node) => { + const matchIndentInNode = node.match(rules.other.beginningSpace); + if (matchIndentInNode === null) { + return node; + } + const [indentInNode] = matchIndentInNode; + if (indentInNode.length >= indentToCode.length) { + return node.slice(indentToCode.length); + } + return node; + }).join("\n"); +} +var _Tokenizer = class { + options; + rules; + // set by the lexer + lexer; + // set by the lexer + constructor(options2) { + this.options = options2 || _defaults; + } + space(src) { + const cap = this.rules.block.newline.exec(src); + if (cap && cap[0].length > 0) { + return { + type: "space", + raw: cap[0] + }; + } + } + code(src) { + const cap = this.rules.block.code.exec(src); + if (cap) { + const text = cap[0].replace(this.rules.other.codeRemoveIndent, ""); + return { + type: "code", + raw: cap[0], + codeBlockStyle: "indented", + text: !this.options.pedantic ? rtrim(text, "\n") : text + }; + } + } + fences(src) { + const cap = this.rules.block.fences.exec(src); + if (cap) { + const raw = cap[0]; + const text = indentCodeCompensation(raw, cap[3] || "", this.rules); + return { + type: "code", + raw, + lang: cap[2] ? cap[2].trim().replace(this.rules.inline.anyPunctuation, "$1") : cap[2], + text + }; + } + } + heading(src) { + const cap = this.rules.block.heading.exec(src); + if (cap) { + let text = cap[2].trim(); + if (this.rules.other.endingHash.test(text)) { + const trimmed = rtrim(text, "#"); + if (this.options.pedantic) { + text = trimmed.trim(); + } else if (!trimmed || this.rules.other.endingSpaceChar.test(trimmed)) { + text = trimmed.trim(); + } + } + return { + type: "heading", + raw: cap[0], + depth: cap[1].length, + text, + tokens: this.lexer.inline(text) + }; + } + } + hr(src) { + const cap = this.rules.block.hr.exec(src); + if (cap) { + return { + type: "hr", + raw: rtrim(cap[0], "\n") + }; + } + } + blockquote(src) { + const cap = this.rules.block.blockquote.exec(src); + if (cap) { + let lines = rtrim(cap[0], "\n").split("\n"); + let raw = ""; + let text = ""; + const tokens = []; + while (lines.length > 0) { + let inBlockquote = false; + const currentLines = []; + let i; + for (i = 0; i < lines.length; i++) { + if (this.rules.other.blockquoteStart.test(lines[i])) { + currentLines.push(lines[i]); + inBlockquote = true; + } else if (!inBlockquote) { + currentLines.push(lines[i]); + } else { + break; + } + } + lines = lines.slice(i); + const currentRaw = currentLines.join("\n"); + const currentText = currentRaw.replace(this.rules.other.blockquoteSetextReplace, "\n $1").replace(this.rules.other.blockquoteSetextReplace2, ""); + raw = raw ? `${raw} +${currentRaw}` : currentRaw; + text = text ? `${text} +${currentText}` : currentText; + const top = this.lexer.state.top; + this.lexer.state.top = true; + this.lexer.blockTokens(currentText, tokens, true); + this.lexer.state.top = top; + if (lines.length === 0) { + break; + } + const lastToken = tokens.at(-1); + if (lastToken?.type === "code") { + break; + } else if (lastToken?.type === "blockquote") { + const oldToken = lastToken; + const newText = oldToken.raw + "\n" + lines.join("\n"); + const newToken = this.blockquote(newText); + tokens[tokens.length - 1] = newToken; + raw = raw.substring(0, raw.length - oldToken.raw.length) + newToken.raw; + text = text.substring(0, text.length - oldToken.text.length) + newToken.text; + break; + } else if (lastToken?.type === "list") { + const oldToken = lastToken; + const newText = oldToken.raw + "\n" + lines.join("\n"); + const newToken = this.list(newText); + tokens[tokens.length - 1] = newToken; + raw = raw.substring(0, raw.length - lastToken.raw.length) + newToken.raw; + text = text.substring(0, text.length - oldToken.raw.length) + newToken.raw; + lines = newText.substring(tokens.at(-1).raw.length).split("\n"); + continue; + } + } + return { + type: "blockquote", + raw, + tokens, + text + }; + } + } + list(src) { + let cap = this.rules.block.list.exec(src); + if (cap) { + let bull = cap[1].trim(); + const isordered = bull.length > 1; + const list2 = { + type: "list", + raw: "", + ordered: isordered, + start: isordered ? +bull.slice(0, -1) : "", + loose: false, + items: [] + }; + bull = isordered ? `\\d{1,9}\\${bull.slice(-1)}` : `\\${bull}`; + if (this.options.pedantic) { + bull = isordered ? bull : "[*+-]"; + } + const itemRegex = this.rules.other.listItemRegex(bull); + let endsWithBlankLine = false; + while (src) { + let endEarly = false; + let raw = ""; + let itemContents = ""; + if (!(cap = itemRegex.exec(src))) { + break; + } + if (this.rules.block.hr.test(src)) { + break; + } + raw = cap[0]; + src = src.substring(raw.length); + let line = cap[2].split("\n", 1)[0].replace(this.rules.other.listReplaceTabs, (t) => " ".repeat(3 * t.length)); + let nextLine = src.split("\n", 1)[0]; + let blankLine = !line.trim(); + let indent = 0; + if (this.options.pedantic) { + indent = 2; + itemContents = line.trimStart(); + } else if (blankLine) { + indent = cap[1].length + 1; + } else { + indent = cap[2].search(this.rules.other.nonSpaceChar); + indent = indent > 4 ? 1 : indent; + itemContents = line.slice(indent); + indent += cap[1].length; + } + if (blankLine && this.rules.other.blankLine.test(nextLine)) { + raw += nextLine + "\n"; + src = src.substring(nextLine.length + 1); + endEarly = true; + } + if (!endEarly) { + const nextBulletRegex = this.rules.other.nextBulletRegex(indent); + const hrRegex = this.rules.other.hrRegex(indent); + const fencesBeginRegex = this.rules.other.fencesBeginRegex(indent); + const headingBeginRegex = this.rules.other.headingBeginRegex(indent); + const htmlBeginRegex = this.rules.other.htmlBeginRegex(indent); + while (src) { + const rawLine = src.split("\n", 1)[0]; + let nextLineWithoutTabs; + nextLine = rawLine; + if (this.options.pedantic) { + nextLine = nextLine.replace(this.rules.other.listReplaceNesting, " "); + nextLineWithoutTabs = nextLine; + } else { + nextLineWithoutTabs = nextLine.replace(this.rules.other.tabCharGlobal, " "); + } + if (fencesBeginRegex.test(nextLine)) { + break; + } + if (headingBeginRegex.test(nextLine)) { + break; + } + if (htmlBeginRegex.test(nextLine)) { + break; + } + if (nextBulletRegex.test(nextLine)) { + break; + } + if (hrRegex.test(nextLine)) { + break; + } + if (nextLineWithoutTabs.search(this.rules.other.nonSpaceChar) >= indent || !nextLine.trim()) { + itemContents += "\n" + nextLineWithoutTabs.slice(indent); + } else { + if (blankLine) { + break; + } + if (line.replace(this.rules.other.tabCharGlobal, " ").search(this.rules.other.nonSpaceChar) >= 4) { + break; + } + if (fencesBeginRegex.test(line)) { + break; + } + if (headingBeginRegex.test(line)) { + break; + } + if (hrRegex.test(line)) { + break; + } + itemContents += "\n" + nextLine; + } + if (!blankLine && !nextLine.trim()) { + blankLine = true; + } + raw += rawLine + "\n"; + src = src.substring(rawLine.length + 1); + line = nextLineWithoutTabs.slice(indent); + } + } + if (!list2.loose) { + if (endsWithBlankLine) { + list2.loose = true; + } else if (this.rules.other.doubleBlankLine.test(raw)) { + endsWithBlankLine = true; + } + } + let istask = null; + let ischecked; + if (this.options.gfm) { + istask = this.rules.other.listIsTask.exec(itemContents); + if (istask) { + ischecked = istask[0] !== "[ ] "; + itemContents = itemContents.replace(this.rules.other.listReplaceTask, ""); + } + } + list2.items.push({ + type: "list_item", + raw, + task: !!istask, + checked: ischecked, + loose: false, + text: itemContents, + tokens: [] + }); + list2.raw += raw; + } + const lastItem = list2.items.at(-1); + if (lastItem) { + lastItem.raw = lastItem.raw.trimEnd(); + lastItem.text = lastItem.text.trimEnd(); + } else { + return; + } + list2.raw = list2.raw.trimEnd(); + for (let i = 0; i < list2.items.length; i++) { + this.lexer.state.top = false; + list2.items[i].tokens = this.lexer.blockTokens(list2.items[i].text, []); + if (!list2.loose) { + const spacers = list2.items[i].tokens.filter((t) => t.type === "space"); + const hasMultipleLineBreaks = spacers.length > 0 && spacers.some((t) => this.rules.other.anyLine.test(t.raw)); + list2.loose = hasMultipleLineBreaks; + } + } + if (list2.loose) { + for (let i = 0; i < list2.items.length; i++) { + list2.items[i].loose = true; + } + } + return list2; + } + } + html(src) { + const cap = this.rules.block.html.exec(src); + if (cap) { + const token = { + type: "html", + block: true, + raw: cap[0], + pre: cap[1] === "pre" || cap[1] === "script" || cap[1] === "style", + text: cap[0] + }; + return token; + } + } + def(src) { + const cap = this.rules.block.def.exec(src); + if (cap) { + const tag2 = cap[1].toLowerCase().replace(this.rules.other.multipleSpaceGlobal, " "); + const href = cap[2] ? cap[2].replace(this.rules.other.hrefBrackets, "$1").replace(this.rules.inline.anyPunctuation, "$1") : ""; + const title = cap[3] ? cap[3].substring(1, cap[3].length - 1).replace(this.rules.inline.anyPunctuation, "$1") : cap[3]; + return { + type: "def", + tag: tag2, + raw: cap[0], + href, + title + }; + } + } + table(src) { + const cap = this.rules.block.table.exec(src); + if (!cap) { + return; + } + if (!this.rules.other.tableDelimiter.test(cap[2])) { + return; + } + const headers = splitCells(cap[1]); + const aligns = cap[2].replace(this.rules.other.tableAlignChars, "").split("|"); + const rows = cap[3]?.trim() ? cap[3].replace(this.rules.other.tableRowBlankLine, "").split("\n") : []; + const item = { + type: "table", + raw: cap[0], + header: [], + align: [], + rows: [] + }; + if (headers.length !== aligns.length) { + return; + } + for (const align of aligns) { + if (this.rules.other.tableAlignRight.test(align)) { + item.align.push("right"); + } else if (this.rules.other.tableAlignCenter.test(align)) { + item.align.push("center"); + } else if (this.rules.other.tableAlignLeft.test(align)) { + item.align.push("left"); + } else { + item.align.push(null); + } + } + for (let i = 0; i < headers.length; i++) { + item.header.push({ + text: headers[i], + tokens: this.lexer.inline(headers[i]), + header: true, + align: item.align[i] + }); + } + for (const row of rows) { + item.rows.push(splitCells(row, item.header.length).map((cell, i) => { + return { + text: cell, + tokens: this.lexer.inline(cell), + header: false, + align: item.align[i] + }; + })); + } + return item; + } + lheading(src) { + const cap = this.rules.block.lheading.exec(src); + if (cap) { + return { + type: "heading", + raw: cap[0], + depth: cap[2].charAt(0) === "=" ? 1 : 2, + text: cap[1], + tokens: this.lexer.inline(cap[1]) + }; + } + } + paragraph(src) { + const cap = this.rules.block.paragraph.exec(src); + if (cap) { + const text = cap[1].charAt(cap[1].length - 1) === "\n" ? cap[1].slice(0, -1) : cap[1]; + return { + type: "paragraph", + raw: cap[0], + text, + tokens: this.lexer.inline(text) + }; + } + } + text(src) { + const cap = this.rules.block.text.exec(src); + if (cap) { + return { + type: "text", + raw: cap[0], + text: cap[0], + tokens: this.lexer.inline(cap[0]) + }; + } + } + escape(src) { + const cap = this.rules.inline.escape.exec(src); + if (cap) { + return { + type: "escape", + raw: cap[0], + text: cap[1] + }; + } + } + tag(src) { + const cap = this.rules.inline.tag.exec(src); + if (cap) { + if (!this.lexer.state.inLink && this.rules.other.startATag.test(cap[0])) { + this.lexer.state.inLink = true; + } else if (this.lexer.state.inLink && this.rules.other.endATag.test(cap[0])) { + this.lexer.state.inLink = false; + } + if (!this.lexer.state.inRawBlock && this.rules.other.startPreScriptTag.test(cap[0])) { + this.lexer.state.inRawBlock = true; + } else if (this.lexer.state.inRawBlock && this.rules.other.endPreScriptTag.test(cap[0])) { + this.lexer.state.inRawBlock = false; + } + return { + type: "html", + raw: cap[0], + inLink: this.lexer.state.inLink, + inRawBlock: this.lexer.state.inRawBlock, + block: false, + text: cap[0] + }; + } + } + link(src) { + const cap = this.rules.inline.link.exec(src); + if (cap) { + const trimmedUrl = cap[2].trim(); + if (!this.options.pedantic && this.rules.other.startAngleBracket.test(trimmedUrl)) { + if (!this.rules.other.endAngleBracket.test(trimmedUrl)) { + return; + } + const rtrimSlash = rtrim(trimmedUrl.slice(0, -1), "\\"); + if ((trimmedUrl.length - rtrimSlash.length) % 2 === 0) { + return; + } + } else { + const lastParenIndex = findClosingBracket(cap[2], "()"); + if (lastParenIndex > -1) { + const start = cap[0].indexOf("!") === 0 ? 5 : 4; + const linkLen = start + cap[1].length + lastParenIndex; + cap[2] = cap[2].substring(0, lastParenIndex); + cap[0] = cap[0].substring(0, linkLen).trim(); + cap[3] = ""; + } + } + let href = cap[2]; + let title = ""; + if (this.options.pedantic) { + const link2 = this.rules.other.pedanticHrefTitle.exec(href); + if (link2) { + href = link2[1]; + title = link2[3]; + } + } else { + title = cap[3] ? cap[3].slice(1, -1) : ""; + } + href = href.trim(); + if (this.rules.other.startAngleBracket.test(href)) { + if (this.options.pedantic && !this.rules.other.endAngleBracket.test(trimmedUrl)) { + href = href.slice(1); + } else { + href = href.slice(1, -1); + } + } + return outputLink(cap, { + href: href ? href.replace(this.rules.inline.anyPunctuation, "$1") : href, + title: title ? title.replace(this.rules.inline.anyPunctuation, "$1") : title + }, cap[0], this.lexer, this.rules); + } + } + reflink(src, links) { + let cap; + if ((cap = this.rules.inline.reflink.exec(src)) || (cap = this.rules.inline.nolink.exec(src))) { + const linkString = (cap[2] || cap[1]).replace(this.rules.other.multipleSpaceGlobal, " "); + const link2 = links[linkString.toLowerCase()]; + if (!link2) { + const text = cap[0].charAt(0); + return { + type: "text", + raw: text, + text + }; + } + return outputLink(cap, link2, cap[0], this.lexer, this.rules); + } + } + emStrong(src, maskedSrc, prevChar = "") { + let match = this.rules.inline.emStrongLDelim.exec(src); + if (!match) + return; + if (match[3] && prevChar.match(this.rules.other.unicodeAlphaNumeric)) + return; + const nextChar = match[1] || match[2] || ""; + if (!nextChar || !prevChar || this.rules.inline.punctuation.exec(prevChar)) { + const lLength = [...match[0]].length - 1; + let rDelim, rLength, delimTotal = lLength, midDelimTotal = 0; + const endReg = match[0][0] === "*" ? this.rules.inline.emStrongRDelimAst : this.rules.inline.emStrongRDelimUnd; + endReg.lastIndex = 0; + maskedSrc = maskedSrc.slice(-1 * src.length + lLength); + while ((match = endReg.exec(maskedSrc)) != null) { + rDelim = match[1] || match[2] || match[3] || match[4] || match[5] || match[6]; + if (!rDelim) + continue; + rLength = [...rDelim].length; + if (match[3] || match[4]) { + delimTotal += rLength; + continue; + } else if (match[5] || match[6]) { + if (lLength % 3 && !((lLength + rLength) % 3)) { + midDelimTotal += rLength; + continue; + } + } + delimTotal -= rLength; + if (delimTotal > 0) + continue; + rLength = Math.min(rLength, rLength + delimTotal + midDelimTotal); + const lastCharLength = [...match[0]][0].length; + const raw = src.slice(0, lLength + match.index + lastCharLength + rLength); + if (Math.min(lLength, rLength) % 2) { + const text2 = raw.slice(1, -1); + return { + type: "em", + raw, + text: text2, + tokens: this.lexer.inlineTokens(text2) + }; + } + const text = raw.slice(2, -2); + return { + type: "strong", + raw, + text, + tokens: this.lexer.inlineTokens(text) + }; + } + } + } + codespan(src) { + const cap = this.rules.inline.code.exec(src); + if (cap) { + let text = cap[2].replace(this.rules.other.newLineCharGlobal, " "); + const hasNonSpaceChars = this.rules.other.nonSpaceChar.test(text); + const hasSpaceCharsOnBothEnds = this.rules.other.startingSpaceChar.test(text) && this.rules.other.endingSpaceChar.test(text); + if (hasNonSpaceChars && hasSpaceCharsOnBothEnds) { + text = text.substring(1, text.length - 1); + } + return { + type: "codespan", + raw: cap[0], + text + }; + } + } + br(src) { + const cap = this.rules.inline.br.exec(src); + if (cap) { + return { + type: "br", + raw: cap[0] + }; + } + } + del(src) { + const cap = this.rules.inline.del.exec(src); + if (cap) { + return { + type: "del", + raw: cap[0], + text: cap[2], + tokens: this.lexer.inlineTokens(cap[2]) + }; + } + } + autolink(src) { + const cap = this.rules.inline.autolink.exec(src); + if (cap) { + let text, href; + if (cap[2] === "@") { + text = cap[1]; + href = "mailto:" + text; + } else { + text = cap[1]; + href = text; + } + return { + type: "link", + raw: cap[0], + text, + href, + tokens: [ + { + type: "text", + raw: text, + text + } + ] + }; + } + } + url(src) { + let cap; + if (cap = this.rules.inline.url.exec(src)) { + let text, href; + if (cap[2] === "@") { + text = cap[0]; + href = "mailto:" + text; + } else { + let prevCapZero; + do { + prevCapZero = cap[0]; + cap[0] = this.rules.inline._backpedal.exec(cap[0])?.[0] ?? ""; + } while (prevCapZero !== cap[0]); + text = cap[0]; + if (cap[1] === "www.") { + href = "http://" + cap[0]; + } else { + href = cap[0]; + } + } + return { + type: "link", + raw: cap[0], + text, + href, + tokens: [ + { + type: "text", + raw: text, + text + } + ] + }; + } + } + inlineText(src) { + const cap = this.rules.inline.text.exec(src); + if (cap) { + const escaped = this.lexer.state.inRawBlock; + return { + type: "text", + raw: cap[0], + text: cap[0], + escaped + }; + } + } +}; +var _Lexer = class __Lexer { + tokens; + options; + state; + tokenizer; + inlineQueue; + constructor(options2) { + this.tokens = []; + this.tokens.links = /* @__PURE__ */ Object.create(null); + this.options = options2 || _defaults; + this.options.tokenizer = this.options.tokenizer || new _Tokenizer(); + this.tokenizer = this.options.tokenizer; + this.tokenizer.options = this.options; + this.tokenizer.lexer = this; + this.inlineQueue = []; + this.state = { + inLink: false, + inRawBlock: false, + top: true + }; + const rules = { + other, + block: block.normal, + inline: inline.normal + }; + if (this.options.pedantic) { + rules.block = block.pedantic; + rules.inline = inline.pedantic; + } else if (this.options.gfm) { + rules.block = block.gfm; + if (this.options.breaks) { + rules.inline = inline.breaks; + } else { + rules.inline = inline.gfm; + } + } + this.tokenizer.rules = rules; + } + /** + * Expose Rules + */ + static get rules() { + return { + block, + inline + }; + } + /** + * Static Lex Method + */ + static lex(src, options2) { + const lexer2 = new __Lexer(options2); + return lexer2.lex(src); + } + /** + * Static Lex Inline Method + */ + static lexInline(src, options2) { + const lexer2 = new __Lexer(options2); + return lexer2.inlineTokens(src); + } + /** + * Preprocessing + */ + lex(src) { + src = src.replace(other.carriageReturn, "\n"); + this.blockTokens(src, this.tokens); + for (let i = 0; i < this.inlineQueue.length; i++) { + const next = this.inlineQueue[i]; + this.inlineTokens(next.src, next.tokens); + } + this.inlineQueue = []; + return this.tokens; + } + blockTokens(src, tokens = [], lastParagraphClipped = false) { + if (this.options.pedantic) { + src = src.replace(other.tabCharGlobal, " ").replace(other.spaceLine, ""); + } + while (src) { + let token; + if (this.options.extensions?.block?.some((extTokenizer) => { + if (token = extTokenizer.call({ lexer: this }, src, tokens)) { + src = src.substring(token.raw.length); + tokens.push(token); + return true; + } + return false; + })) { + continue; + } + if (token = this.tokenizer.space(src)) { + src = src.substring(token.raw.length); + const lastToken = tokens.at(-1); + if (token.raw.length === 1 && lastToken !== void 0) { + lastToken.raw += "\n"; + } else { + tokens.push(token); + } + continue; + } + if (token = this.tokenizer.code(src)) { + src = src.substring(token.raw.length); + const lastToken = tokens.at(-1); + if (lastToken?.type === "paragraph" || lastToken?.type === "text") { + lastToken.raw += "\n" + token.raw; + lastToken.text += "\n" + token.text; + this.inlineQueue.at(-1).src = lastToken.text; + } else { + tokens.push(token); + } + continue; + } + if (token = this.tokenizer.fences(src)) { + src = src.substring(token.raw.length); + tokens.push(token); + continue; + } + if (token = this.tokenizer.heading(src)) { + src = src.substring(token.raw.length); + tokens.push(token); + continue; + } + if (token = this.tokenizer.hr(src)) { + src = src.substring(token.raw.length); + tokens.push(token); + continue; + } + if (token = this.tokenizer.blockquote(src)) { + src = src.substring(token.raw.length); + tokens.push(token); + continue; + } + if (token = this.tokenizer.list(src)) { + src = src.substring(token.raw.length); + tokens.push(token); + continue; + } + if (token = this.tokenizer.html(src)) { + src = src.substring(token.raw.length); + tokens.push(token); + continue; + } + if (token = this.tokenizer.def(src)) { + src = src.substring(token.raw.length); + const lastToken = tokens.at(-1); + if (lastToken?.type === "paragraph" || lastToken?.type === "text") { + lastToken.raw += "\n" + token.raw; + lastToken.text += "\n" + token.raw; + this.inlineQueue.at(-1).src = lastToken.text; + } else if (!this.tokens.links[token.tag]) { + this.tokens.links[token.tag] = { + href: token.href, + title: token.title + }; + } + continue; + } + if (token = this.tokenizer.table(src)) { + src = src.substring(token.raw.length); + tokens.push(token); + continue; + } + if (token = this.tokenizer.lheading(src)) { + src = src.substring(token.raw.length); + tokens.push(token); + continue; + } + let cutSrc = src; + if (this.options.extensions?.startBlock) { + let startIndex = Infinity; + const tempSrc = src.slice(1); + let tempStart; + this.options.extensions.startBlock.forEach((getStartIndex) => { + tempStart = getStartIndex.call({ lexer: this }, tempSrc); + if (typeof tempStart === "number" && tempStart >= 0) { + startIndex = Math.min(startIndex, tempStart); + } + }); + if (startIndex < Infinity && startIndex >= 0) { + cutSrc = src.substring(0, startIndex + 1); + } + } + if (this.state.top && (token = this.tokenizer.paragraph(cutSrc))) { + const lastToken = tokens.at(-1); + if (lastParagraphClipped && lastToken?.type === "paragraph") { + lastToken.raw += "\n" + token.raw; + lastToken.text += "\n" + token.text; + this.inlineQueue.pop(); + this.inlineQueue.at(-1).src = lastToken.text; + } else { + tokens.push(token); + } + lastParagraphClipped = cutSrc.length !== src.length; + src = src.substring(token.raw.length); + continue; + } + if (token = this.tokenizer.text(src)) { + src = src.substring(token.raw.length); + const lastToken = tokens.at(-1); + if (lastToken?.type === "text") { + lastToken.raw += "\n" + token.raw; + lastToken.text += "\n" + token.text; + this.inlineQueue.pop(); + this.inlineQueue.at(-1).src = lastToken.text; + } else { + tokens.push(token); + } + continue; + } + if (src) { + const errMsg = "Infinite loop on byte: " + src.charCodeAt(0); + if (this.options.silent) { + console.error(errMsg); + break; + } else { + throw new Error(errMsg); + } + } + } + this.state.top = true; + return tokens; + } + inline(src, tokens = []) { + this.inlineQueue.push({ src, tokens }); + return tokens; + } + /** + * Lexing/Compiling + */ + inlineTokens(src, tokens = []) { + let maskedSrc = src; + let match = null; + if (this.tokens.links) { + const links = Object.keys(this.tokens.links); + if (links.length > 0) { + while ((match = this.tokenizer.rules.inline.reflinkSearch.exec(maskedSrc)) != null) { + if (links.includes(match[0].slice(match[0].lastIndexOf("[") + 1, -1))) { + maskedSrc = maskedSrc.slice(0, match.index) + "[" + "a".repeat(match[0].length - 2) + "]" + maskedSrc.slice(this.tokenizer.rules.inline.reflinkSearch.lastIndex); + } + } + } + } + while ((match = this.tokenizer.rules.inline.blockSkip.exec(maskedSrc)) != null) { + maskedSrc = maskedSrc.slice(0, match.index) + "[" + "a".repeat(match[0].length - 2) + "]" + maskedSrc.slice(this.tokenizer.rules.inline.blockSkip.lastIndex); + } + while ((match = this.tokenizer.rules.inline.anyPunctuation.exec(maskedSrc)) != null) { + maskedSrc = maskedSrc.slice(0, match.index) + "++" + maskedSrc.slice(this.tokenizer.rules.inline.anyPunctuation.lastIndex); + } + let keepPrevChar = false; + let prevChar = ""; + while (src) { + if (!keepPrevChar) { + prevChar = ""; + } + keepPrevChar = false; + let token; + if (this.options.extensions?.inline?.some((extTokenizer) => { + if (token = extTokenizer.call({ lexer: this }, src, tokens)) { + src = src.substring(token.raw.length); + tokens.push(token); + return true; + } + return false; + })) { + continue; + } + if (token = this.tokenizer.escape(src)) { + src = src.substring(token.raw.length); + tokens.push(token); + continue; + } + if (token = this.tokenizer.tag(src)) { + src = src.substring(token.raw.length); + tokens.push(token); + continue; + } + if (token = this.tokenizer.link(src)) { + src = src.substring(token.raw.length); + tokens.push(token); + continue; + } + if (token = this.tokenizer.reflink(src, this.tokens.links)) { + src = src.substring(token.raw.length); + const lastToken = tokens.at(-1); + if (token.type === "text" && lastToken?.type === "text") { + lastToken.raw += token.raw; + lastToken.text += token.text; + } else { + tokens.push(token); + } + continue; + } + if (token = this.tokenizer.emStrong(src, maskedSrc, prevChar)) { + src = src.substring(token.raw.length); + tokens.push(token); + continue; + } + if (token = this.tokenizer.codespan(src)) { + src = src.substring(token.raw.length); + tokens.push(token); + continue; + } + if (token = this.tokenizer.br(src)) { + src = src.substring(token.raw.length); + tokens.push(token); + continue; + } + if (token = this.tokenizer.del(src)) { + src = src.substring(token.raw.length); + tokens.push(token); + continue; + } + if (token = this.tokenizer.autolink(src)) { + src = src.substring(token.raw.length); + tokens.push(token); + continue; + } + if (!this.state.inLink && (token = this.tokenizer.url(src))) { + src = src.substring(token.raw.length); + tokens.push(token); + continue; + } + let cutSrc = src; + if (this.options.extensions?.startInline) { + let startIndex = Infinity; + const tempSrc = src.slice(1); + let tempStart; + this.options.extensions.startInline.forEach((getStartIndex) => { + tempStart = getStartIndex.call({ lexer: this }, tempSrc); + if (typeof tempStart === "number" && tempStart >= 0) { + startIndex = Math.min(startIndex, tempStart); + } + }); + if (startIndex < Infinity && startIndex >= 0) { + cutSrc = src.substring(0, startIndex + 1); + } + } + if (token = this.tokenizer.inlineText(cutSrc)) { + src = src.substring(token.raw.length); + if (token.raw.slice(-1) !== "_") { + prevChar = token.raw.slice(-1); + } + keepPrevChar = true; + const lastToken = tokens.at(-1); + if (lastToken?.type === "text") { + lastToken.raw += token.raw; + lastToken.text += token.text; + } else { + tokens.push(token); + } + continue; + } + if (src) { + const errMsg = "Infinite loop on byte: " + src.charCodeAt(0); + if (this.options.silent) { + console.error(errMsg); + break; + } else { + throw new Error(errMsg); + } + } + } + return tokens; + } +}; +var _Renderer = class { + options; + parser; + // set by the parser + constructor(options2) { + this.options = options2 || _defaults; + } + space(token) { + return ""; + } + code({ text, lang, escaped }) { + const langString = (lang || "").match(other.notSpaceStart)?.[0]; + const code = text.replace(other.endingNewline, "") + "\n"; + if (!langString) { + return "<pre><code>" + (escaped ? code : escape(code, true)) + "</code></pre>\n"; + } + return '<pre><code class="language-' + escape(langString) + '">' + (escaped ? code : escape(code, true)) + "</code></pre>\n"; + } + blockquote({ tokens }) { + const body = this.parser.parse(tokens); + return `<blockquote> +${body}</blockquote> +`; + } + html({ text }) { + return text; + } + heading({ tokens, depth }) { + return `<h${depth}>${this.parser.parseInline(tokens)}</h${depth}> +`; + } + hr(token) { + return "<hr>\n"; + } + list(token) { + const ordered = token.ordered; + const start = token.start; + let body = ""; + for (let j = 0; j < token.items.length; j++) { + const item = token.items[j]; + body += this.listitem(item); + } + const type = ordered ? "ol" : "ul"; + const startAttr = ordered && start !== 1 ? ' start="' + start + '"' : ""; + return "<" + type + startAttr + ">\n" + body + "</" + type + ">\n"; + } + listitem(item) { + let itemBody = ""; + if (item.task) { + const checkbox = this.checkbox({ checked: !!item.checked }); + if (item.loose) { + if (item.tokens[0]?.type === "paragraph") { + item.tokens[0].text = checkbox + " " + item.tokens[0].text; + if (item.tokens[0].tokens && item.tokens[0].tokens.length > 0 && item.tokens[0].tokens[0].type === "text") { + item.tokens[0].tokens[0].text = checkbox + " " + escape(item.tokens[0].tokens[0].text); + item.tokens[0].tokens[0].escaped = true; + } + } else { + item.tokens.unshift({ + type: "text", + raw: checkbox + " ", + text: checkbox + " ", + escaped: true + }); + } + } else { + itemBody += checkbox + " "; + } + } + itemBody += this.parser.parse(item.tokens, !!item.loose); + return `<li>${itemBody}</li> +`; + } + checkbox({ checked }) { + return "<input " + (checked ? 'checked="" ' : "") + 'disabled="" type="checkbox">'; + } + paragraph({ tokens }) { + return `<p>${this.parser.parseInline(tokens)}</p> +`; + } + table(token) { + let header = ""; + let cell = ""; + for (let j = 0; j < token.header.length; j++) { + cell += this.tablecell(token.header[j]); + } + header += this.tablerow({ text: cell }); + let body = ""; + for (let j = 0; j < token.rows.length; j++) { + const row = token.rows[j]; + cell = ""; + for (let k = 0; k < row.length; k++) { + cell += this.tablecell(row[k]); + } + body += this.tablerow({ text: cell }); + } + if (body) + body = `<tbody>${body}</tbody>`; + return "<table>\n<thead>\n" + header + "</thead>\n" + body + "</table>\n"; + } + tablerow({ text }) { + return `<tr> +${text}</tr> +`; + } + tablecell(token) { + const content = this.parser.parseInline(token.tokens); + const type = token.header ? "th" : "td"; + const tag2 = token.align ? `<${type} align="${token.align}">` : `<${type}>`; + return tag2 + content + `</${type}> +`; + } + /** + * span level renderer + */ + strong({ tokens }) { + return `<strong>${this.parser.parseInline(tokens)}</strong>`; + } + em({ tokens }) { + return `<em>${this.parser.parseInline(tokens)}</em>`; + } + codespan({ text }) { + return `<code>${escape(text, true)}</code>`; + } + br(token) { + return "<br>"; + } + del({ tokens }) { + return `<del>${this.parser.parseInline(tokens)}</del>`; + } + link({ href, title, tokens }) { + const text = this.parser.parseInline(tokens); + const cleanHref = cleanUrl(href); + if (cleanHref === null) { + return text; + } + href = cleanHref; + let out = '<a href="' + href + '"'; + if (title) { + out += ' title="' + escape(title) + '"'; + } + out += ">" + text + "</a>"; + return out; + } + image({ href, title, text }) { + const cleanHref = cleanUrl(href); + if (cleanHref === null) { + return escape(text); + } + href = cleanHref; + let out = `<img src="${href}" alt="${text}"`; + if (title) { + out += ` title="${escape(title)}"`; + } + out += ">"; + return out; + } + text(token) { + return "tokens" in token && token.tokens ? this.parser.parseInline(token.tokens) : "escaped" in token && token.escaped ? token.text : escape(token.text); + } +}; +var _TextRenderer = class { + // no need for block level renderers + strong({ text }) { + return text; + } + em({ text }) { + return text; + } + codespan({ text }) { + return text; + } + del({ text }) { + return text; + } + html({ text }) { + return text; + } + text({ text }) { + return text; + } + link({ text }) { + return "" + text; + } + image({ text }) { + return "" + text; + } + br() { + return ""; + } +}; +var _Parser = class __Parser { + options; + renderer; + textRenderer; + constructor(options2) { + this.options = options2 || _defaults; + this.options.renderer = this.options.renderer || new _Renderer(); + this.renderer = this.options.renderer; + this.renderer.options = this.options; + this.renderer.parser = this; + this.textRenderer = new _TextRenderer(); + } + /** + * Static Parse Method + */ + static parse(tokens, options2) { + const parser2 = new __Parser(options2); + return parser2.parse(tokens); + } + /** + * Static Parse Inline Method + */ + static parseInline(tokens, options2) { + const parser2 = new __Parser(options2); + return parser2.parseInline(tokens); + } + /** + * Parse Loop + */ + parse(tokens, top = true) { + let out = ""; + for (let i = 0; i < tokens.length; i++) { + const anyToken = tokens[i]; + if (this.options.extensions?.renderers?.[anyToken.type]) { + const genericToken = anyToken; + const ret = this.options.extensions.renderers[genericToken.type].call({ parser: this }, genericToken); + if (ret !== false || !["space", "hr", "heading", "code", "table", "blockquote", "list", "html", "paragraph", "text"].includes(genericToken.type)) { + out += ret || ""; + continue; + } + } + const token = anyToken; + switch (token.type) { + case "space": { + out += this.renderer.space(token); + continue; + } + case "hr": { + out += this.renderer.hr(token); + continue; + } + case "heading": { + out += this.renderer.heading(token); + continue; + } + case "code": { + out += this.renderer.code(token); + continue; + } + case "table": { + out += this.renderer.table(token); + continue; + } + case "blockquote": { + out += this.renderer.blockquote(token); + continue; + } + case "list": { + out += this.renderer.list(token); + continue; + } + case "html": { + out += this.renderer.html(token); + continue; + } + case "paragraph": { + out += this.renderer.paragraph(token); + continue; + } + case "text": { + let textToken = token; + let body = this.renderer.text(textToken); + while (i + 1 < tokens.length && tokens[i + 1].type === "text") { + textToken = tokens[++i]; + body += "\n" + this.renderer.text(textToken); + } + if (top) { + out += this.renderer.paragraph({ + type: "paragraph", + raw: body, + text: body, + tokens: [{ type: "text", raw: body, text: body, escaped: true }] + }); + } else { + out += body; + } + continue; + } + default: { + const errMsg = 'Token with "' + token.type + '" type was not found.'; + if (this.options.silent) { + console.error(errMsg); + return ""; + } else { + throw new Error(errMsg); + } + } + } + } + return out; + } + /** + * Parse Inline Tokens + */ + parseInline(tokens, renderer = this.renderer) { + let out = ""; + for (let i = 0; i < tokens.length; i++) { + const anyToken = tokens[i]; + if (this.options.extensions?.renderers?.[anyToken.type]) { + const ret = this.options.extensions.renderers[anyToken.type].call({ parser: this }, anyToken); + if (ret !== false || !["escape", "html", "link", "image", "strong", "em", "codespan", "br", "del", "text"].includes(anyToken.type)) { + out += ret || ""; + continue; + } + } + const token = anyToken; + switch (token.type) { + case "escape": { + out += renderer.text(token); + break; + } + case "html": { + out += renderer.html(token); + break; + } + case "link": { + out += renderer.link(token); + break; + } + case "image": { + out += renderer.image(token); + break; + } + case "strong": { + out += renderer.strong(token); + break; + } + case "em": { + out += renderer.em(token); + break; + } + case "codespan": { + out += renderer.codespan(token); + break; + } + case "br": { + out += renderer.br(token); + break; + } + case "del": { + out += renderer.del(token); + break; + } + case "text": { + out += renderer.text(token); + break; + } + default: { + const errMsg = 'Token with "' + token.type + '" type was not found.'; + if (this.options.silent) { + console.error(errMsg); + return ""; + } else { + throw new Error(errMsg); + } + } + } + } + return out; + } +}; +var _Hooks = class { + options; + block; + constructor(options2) { + this.options = options2 || _defaults; + } + static passThroughHooks = /* @__PURE__ */ new Set([ + "preprocess", + "postprocess", + "processAllTokens" + ]); + /** + * Process markdown before marked + */ + preprocess(markdown) { + return markdown; + } + /** + * Process HTML after marked is finished + */ + postprocess(html2) { + return html2; + } + /** + * Process all tokens before walk tokens + */ + processAllTokens(tokens) { + return tokens; + } + /** + * Provide function to tokenize markdown + */ + provideLexer() { + return this.block ? _Lexer.lex : _Lexer.lexInline; + } + /** + * Provide function to parse tokens + */ + provideParser() { + return this.block ? _Parser.parse : _Parser.parseInline; + } +}; +var Marked = class { + defaults = _getDefaults(); + options = this.setOptions; + parse = this.parseMarkdown(true); + parseInline = this.parseMarkdown(false); + Parser = _Parser; + Renderer = _Renderer; + TextRenderer = _TextRenderer; + Lexer = _Lexer; + Tokenizer = _Tokenizer; + Hooks = _Hooks; + constructor(...args) { + this.use(...args); + } + /** + * Run callback for every token + */ + walkTokens(tokens, callback) { + let values = []; + for (const token of tokens) { + values = values.concat(callback.call(this, token)); + switch (token.type) { + case "table": { + const tableToken = token; + for (const cell of tableToken.header) { + values = values.concat(this.walkTokens(cell.tokens, callback)); + } + for (const row of tableToken.rows) { + for (const cell of row) { + values = values.concat(this.walkTokens(cell.tokens, callback)); + } + } + break; + } + case "list": { + const listToken = token; + values = values.concat(this.walkTokens(listToken.items, callback)); + break; + } + default: { + const genericToken = token; + if (this.defaults.extensions?.childTokens?.[genericToken.type]) { + this.defaults.extensions.childTokens[genericToken.type].forEach((childTokens) => { + const tokens2 = genericToken[childTokens].flat(Infinity); + values = values.concat(this.walkTokens(tokens2, callback)); + }); + } else if (genericToken.tokens) { + values = values.concat(this.walkTokens(genericToken.tokens, callback)); + } + } + } + } + return values; + } + use(...args) { + const extensions = this.defaults.extensions || { renderers: {}, childTokens: {} }; + args.forEach((pack) => { + const opts = { ...pack }; + opts.async = this.defaults.async || opts.async || false; + if (pack.extensions) { + pack.extensions.forEach((ext) => { + if (!ext.name) { + throw new Error("extension name required"); + } + if ("renderer" in ext) { + const prevRenderer = extensions.renderers[ext.name]; + if (prevRenderer) { + extensions.renderers[ext.name] = function(...args2) { + let ret = ext.renderer.apply(this, args2); + if (ret === false) { + ret = prevRenderer.apply(this, args2); + } + return ret; + }; + } else { + extensions.renderers[ext.name] = ext.renderer; + } + } + if ("tokenizer" in ext) { + if (!ext.level || ext.level !== "block" && ext.level !== "inline") { + throw new Error("extension level must be 'block' or 'inline'"); + } + const extLevel = extensions[ext.level]; + if (extLevel) { + extLevel.unshift(ext.tokenizer); + } else { + extensions[ext.level] = [ext.tokenizer]; + } + if (ext.start) { + if (ext.level === "block") { + if (extensions.startBlock) { + extensions.startBlock.push(ext.start); + } else { + extensions.startBlock = [ext.start]; + } + } else if (ext.level === "inline") { + if (extensions.startInline) { + extensions.startInline.push(ext.start); + } else { + extensions.startInline = [ext.start]; + } + } + } + } + if ("childTokens" in ext && ext.childTokens) { + extensions.childTokens[ext.name] = ext.childTokens; + } + }); + opts.extensions = extensions; + } + if (pack.renderer) { + const renderer = this.defaults.renderer || new _Renderer(this.defaults); + for (const prop in pack.renderer) { + if (!(prop in renderer)) { + throw new Error(`renderer '${prop}' does not exist`); + } + if (["options", "parser"].includes(prop)) { + continue; + } + const rendererProp = prop; + const rendererFunc = pack.renderer[rendererProp]; + const prevRenderer = renderer[rendererProp]; + renderer[rendererProp] = (...args2) => { + let ret = rendererFunc.apply(renderer, args2); + if (ret === false) { + ret = prevRenderer.apply(renderer, args2); + } + return ret || ""; + }; + } + opts.renderer = renderer; + } + if (pack.tokenizer) { + const tokenizer = this.defaults.tokenizer || new _Tokenizer(this.defaults); + for (const prop in pack.tokenizer) { + if (!(prop in tokenizer)) { + throw new Error(`tokenizer '${prop}' does not exist`); + } + if (["options", "rules", "lexer"].includes(prop)) { + continue; + } + const tokenizerProp = prop; + const tokenizerFunc = pack.tokenizer[tokenizerProp]; + const prevTokenizer = tokenizer[tokenizerProp]; + tokenizer[tokenizerProp] = (...args2) => { + let ret = tokenizerFunc.apply(tokenizer, args2); + if (ret === false) { + ret = prevTokenizer.apply(tokenizer, args2); + } + return ret; + }; + } + opts.tokenizer = tokenizer; + } + if (pack.hooks) { + const hooks = this.defaults.hooks || new _Hooks(); + for (const prop in pack.hooks) { + if (!(prop in hooks)) { + throw new Error(`hook '${prop}' does not exist`); + } + if (["options", "block"].includes(prop)) { + continue; + } + const hooksProp = prop; + const hooksFunc = pack.hooks[hooksProp]; + const prevHook = hooks[hooksProp]; + if (_Hooks.passThroughHooks.has(prop)) { + hooks[hooksProp] = (arg) => { + if (this.defaults.async) { + return Promise.resolve(hooksFunc.call(hooks, arg)).then((ret2) => { + return prevHook.call(hooks, ret2); + }); + } + const ret = hooksFunc.call(hooks, arg); + return prevHook.call(hooks, ret); + }; + } else { + hooks[hooksProp] = (...args2) => { + let ret = hooksFunc.apply(hooks, args2); + if (ret === false) { + ret = prevHook.apply(hooks, args2); + } + return ret; + }; + } + } + opts.hooks = hooks; + } + if (pack.walkTokens) { + const walkTokens2 = this.defaults.walkTokens; + const packWalktokens = pack.walkTokens; + opts.walkTokens = function(token) { + let values = []; + values.push(packWalktokens.call(this, token)); + if (walkTokens2) { + values = values.concat(walkTokens2.call(this, token)); + } + return values; + }; + } + this.defaults = { ...this.defaults, ...opts }; + }); + return this; + } + setOptions(opt) { + this.defaults = { ...this.defaults, ...opt }; + return this; + } + lexer(src, options2) { + return _Lexer.lex(src, options2 ?? this.defaults); + } + parser(tokens, options2) { + return _Parser.parse(tokens, options2 ?? this.defaults); + } + parseMarkdown(blockType) { + const parse2 = (src, options2) => { + const origOpt = { ...options2 }; + const opt = { ...this.defaults, ...origOpt }; + const throwError = this.onError(!!opt.silent, !!opt.async); + if (this.defaults.async === true && origOpt.async === false) { + return throwError(new Error("marked(): The async option was set to true by an extension. Remove async: false from the parse options object to return a Promise.")); + } + if (typeof src === "undefined" || src === null) { + return throwError(new Error("marked(): input parameter is undefined or null")); + } + if (typeof src !== "string") { + return throwError(new Error("marked(): input parameter is of type " + Object.prototype.toString.call(src) + ", string expected")); + } + if (opt.hooks) { + opt.hooks.options = opt; + opt.hooks.block = blockType; + } + const lexer2 = opt.hooks ? opt.hooks.provideLexer() : blockType ? _Lexer.lex : _Lexer.lexInline; + const parser2 = opt.hooks ? opt.hooks.provideParser() : blockType ? _Parser.parse : _Parser.parseInline; + if (opt.async) { + return Promise.resolve(opt.hooks ? opt.hooks.preprocess(src) : src).then((src2) => lexer2(src2, opt)).then((tokens) => opt.hooks ? opt.hooks.processAllTokens(tokens) : tokens).then((tokens) => opt.walkTokens ? Promise.all(this.walkTokens(tokens, opt.walkTokens)).then(() => tokens) : tokens).then((tokens) => parser2(tokens, opt)).then((html2) => opt.hooks ? opt.hooks.postprocess(html2) : html2).catch(throwError); + } + try { + if (opt.hooks) { + src = opt.hooks.preprocess(src); + } + let tokens = lexer2(src, opt); + if (opt.hooks) { + tokens = opt.hooks.processAllTokens(tokens); + } + if (opt.walkTokens) { + this.walkTokens(tokens, opt.walkTokens); + } + let html2 = parser2(tokens, opt); + if (opt.hooks) { + html2 = opt.hooks.postprocess(html2); + } + return html2; + } catch (e) { + return throwError(e); + } + }; + return parse2; + } + onError(silent, async) { + return (e) => { + e.message += "\nPlease report this to https://github.com/markedjs/marked."; + if (silent) { + const msg = "<p>An error occurred:</p><pre>" + escape(e.message + "", true) + "</pre>"; + if (async) { + return Promise.resolve(msg); + } + return msg; + } + if (async) { + return Promise.reject(e); + } + throw e; + }; + } +}; +var markedInstance = new Marked(); +function marked(src, opt) { + return markedInstance.parse(src, opt); +} +marked.options = marked.setOptions = function(options2) { + markedInstance.setOptions(options2); + marked.defaults = markedInstance.defaults; + changeDefaults(marked.defaults); + return marked; +}; +marked.getDefaults = _getDefaults; +marked.defaults = _defaults; +marked.use = function(...args) { + markedInstance.use(...args); + marked.defaults = markedInstance.defaults; + changeDefaults(marked.defaults); + return marked; +}; +marked.walkTokens = function(tokens, callback) { + return markedInstance.walkTokens(tokens, callback); +}; +marked.parseInline = markedInstance.parseInline; +marked.Parser = _Parser; +marked.parser = _Parser.parse; +marked.Renderer = _Renderer; +marked.TextRenderer = _TextRenderer; +marked.Lexer = _Lexer; +marked.lexer = _Lexer.lex; +marked.Tokenizer = _Tokenizer; +marked.Hooks = _Hooks; +marked.parse = marked; +var options = marked.options; +var setOptions = marked.setOptions; +var use = marked.use; +var walkTokens = marked.walkTokens; +var parseInline = marked.parseInline; +var parse = marked; +var parser = _Parser.parse; +var lexer = _Lexer.lex; +export { + _Hooks as Hooks, + _Lexer as Lexer, + Marked, + _Parser as Parser, + _Renderer as Renderer, + _TextRenderer as TextRenderer, + _Tokenizer as Tokenizer, + _defaults as defaults, + _getDefaults as getDefaults, + lexer, + marked, + options, + parse, + parseInline, + parser, + setOptions, + use, + walkTokens +}; diff --git a/jamfile/js_builtins/toml.js b/jamfile/js_builtins/toml.js new file mode 100644 index 0000000000..0cc4a1014c --- /dev/null +++ b/jamfile/js_builtins/toml.js @@ -0,0 +1,1045 @@ +/** + * Original source: https://jsr.io/@std/toml + * + * Generated from jsr:@std/toml@1.0.2 using: + * + * ```bash + * echo 'export * from "@std/toml"' \ + * | ./node_modules/.bin/esbuild --bundle --format=esm \ + * > ../jamfile/js_builtins/toml.js + * ``` + * + */ + +// node_modules/@std/toml/stringify.js +function joinKeys(keys) { + return keys.map((str) => { + return str.length === 0 || str.match(/[^A-Za-z0-9_-]/) ? JSON.stringify(str) : str; + }).join("."); +} +var Dumper = class { + maxPad = 0; + srcObject; + output = []; + #arrayTypeCache = /* @__PURE__ */ new Map(); + constructor(srcObjc) { + this.srcObject = srcObjc; + } + dump(fmtOptions = {}) { + this.output = this.#printObject(this.srcObject); + this.output = this.#format(fmtOptions); + return this.output; + } + #printObject(obj, keys = []) { + const out = []; + const props = Object.keys(obj); + const inlineProps = []; + const multilineProps = []; + for (const prop of props) { + if (this.#isSimplySerializable(obj[prop])) { + inlineProps.push(prop); + } else { + multilineProps.push(prop); + } + } + const sortedProps = inlineProps.concat(multilineProps); + for (const prop of sortedProps) { + const value2 = obj[prop]; + if (value2 instanceof Date) { + out.push(this.#dateDeclaration([ + prop + ], value2)); + } else if (typeof value2 === "string" || value2 instanceof RegExp) { + out.push(this.#strDeclaration([ + prop + ], value2.toString())); + } else if (typeof value2 === "number") { + out.push(this.#numberDeclaration([ + prop + ], value2)); + } else if (typeof value2 === "boolean") { + out.push(this.#boolDeclaration([ + prop + ], value2)); + } else if (value2 instanceof Array) { + const arrayType = this.#getTypeOfArray(value2); + if (arrayType === "ONLY_PRIMITIVE") { + out.push(this.#arrayDeclaration([ + prop + ], value2)); + } else if (arrayType === "ONLY_OBJECT_EXCLUDING_ARRAY") { + for (let i = 0; i < value2.length; i++) { + out.push(""); + out.push(this.#headerGroup([ + ...keys, + prop + ])); + out.push(...this.#printObject(value2[i], [ + ...keys, + prop + ])); + } + } else { + const str = value2.map((x) => this.#printAsInlineValue(x)).join(","); + out.push(`${this.#declaration([ + prop + ])}[${str}]`); + } + } else if (typeof value2 === "object") { + out.push(""); + out.push(this.#header([ + ...keys, + prop + ])); + if (value2) { + const toParse = value2; + out.push(...this.#printObject(toParse, [ + ...keys, + prop + ])); + } + } + } + out.push(""); + return out; + } + #isPrimitive(value2) { + return value2 instanceof Date || value2 instanceof RegExp || [ + "string", + "number", + "boolean" + ].includes(typeof value2); + } + #getTypeOfArray(arr) { + if (this.#arrayTypeCache.has(arr)) { + return this.#arrayTypeCache.get(arr); + } + const type = this.#doGetTypeOfArray(arr); + this.#arrayTypeCache.set(arr, type); + return type; + } + #doGetTypeOfArray(arr) { + if (!arr.length) { + return "ONLY_PRIMITIVE"; + } + const onlyPrimitive = this.#isPrimitive(arr[0]); + if (arr[0] instanceof Array) { + return "MIXED"; + } + for (let i = 1; i < arr.length; i++) { + if (onlyPrimitive !== this.#isPrimitive(arr[i]) || arr[i] instanceof Array) { + return "MIXED"; + } + } + return onlyPrimitive ? "ONLY_PRIMITIVE" : "ONLY_OBJECT_EXCLUDING_ARRAY"; + } + #printAsInlineValue(value2) { + if (value2 instanceof Date) { + return `"${this.#printDate(value2)}"`; + } else if (typeof value2 === "string" || value2 instanceof RegExp) { + return JSON.stringify(value2.toString()); + } else if (typeof value2 === "number") { + return value2; + } else if (typeof value2 === "boolean") { + return value2.toString(); + } else if (value2 instanceof Array) { + const str = value2.map((x) => this.#printAsInlineValue(x)).join(","); + return `[${str}]`; + } else if (typeof value2 === "object") { + if (!value2) { + throw new Error("should never reach"); + } + const str = Object.keys(value2).map((key) => { + return `${joinKeys([ + key + ])} = ${// deno-lint-ignore no-explicit-any + this.#printAsInlineValue(value2[key])}`; + }).join(","); + return `{${str}}`; + } + throw new Error("should never reach"); + } + #isSimplySerializable(value2) { + return typeof value2 === "string" || typeof value2 === "number" || typeof value2 === "boolean" || value2 instanceof RegExp || value2 instanceof Date || value2 instanceof Array && this.#getTypeOfArray(value2) !== "ONLY_OBJECT_EXCLUDING_ARRAY"; + } + #header(keys) { + return `[${joinKeys(keys)}]`; + } + #headerGroup(keys) { + return `[[${joinKeys(keys)}]]`; + } + #declaration(keys) { + const title = joinKeys(keys); + if (title.length > this.maxPad) { + this.maxPad = title.length; + } + return `${title} = `; + } + #arrayDeclaration(keys, value2) { + return `${this.#declaration(keys)}${JSON.stringify(value2)}`; + } + #strDeclaration(keys, value2) { + return `${this.#declaration(keys)}${JSON.stringify(value2)}`; + } + #numberDeclaration(keys, value2) { + switch (value2) { + case Infinity: + return `${this.#declaration(keys)}inf`; + case -Infinity: + return `${this.#declaration(keys)}-inf`; + default: + return `${this.#declaration(keys)}${value2}`; + } + } + #boolDeclaration(keys, value2) { + return `${this.#declaration(keys)}${value2}`; + } + #printDate(value2) { + function dtPad(v, lPad = 2) { + return v.padStart(lPad, "0"); + } + const m = dtPad((value2.getUTCMonth() + 1).toString()); + const d = dtPad(value2.getUTCDate().toString()); + const h = dtPad(value2.getUTCHours().toString()); + const min = dtPad(value2.getUTCMinutes().toString()); + const s = dtPad(value2.getUTCSeconds().toString()); + const ms = dtPad(value2.getUTCMilliseconds().toString(), 3); + const fData = `${value2.getUTCFullYear()}-${m}-${d}T${h}:${min}:${s}.${ms}`; + return fData; + } + #dateDeclaration(keys, value2) { + return `${this.#declaration(keys)}${this.#printDate(value2)}`; + } + #format(options = {}) { + const { keyAlignment = false } = options; + const rDeclaration = /^(\".*\"|[^=]*)\s=/; + const out = []; + for (let i = 0; i < this.output.length; i++) { + const l = this.output[i]; + if (l[0] === "[" && l[1] !== "[") { + if (this.output[i + 1] === "" && this.output[i + 2]?.slice(0, l.length) === l.slice(0, -1) + ".") { + i += 1; + continue; + } + out.push(l); + } else { + if (keyAlignment) { + const m = rDeclaration.exec(l); + if (m && m[1]) { + out.push(l.replace(m[1], m[1].padEnd(this.maxPad))); + } else { + out.push(l); + } + } else { + out.push(l); + } + } + } + const cleanedOutput = []; + for (let i = 0; i < out.length; i++) { + const l = out[i]; + if (!(l === "" && out[i + 1] === "")) { + cleanedOutput.push(l); + } + } + return cleanedOutput; + } +}; +function stringify(obj, options) { + return new Dumper(obj).dump(options).join("\n"); +} + +// node_modules/@jsr/std__collections/_utils.js +function filterInPlace(array, predicate) { + let outputIndex = 0; + for (const cur of array) { + if (!predicate(cur)) { + continue; + } + array[outputIndex] = cur; + outputIndex += 1; + } + array.splice(outputIndex); + return array; +} + +// node_modules/@jsr/std__collections/deep_merge.js +function deepMerge(record, other, options) { + return deepMergeInternal(record, other, /* @__PURE__ */ new Set(), options); +} +function deepMergeInternal(record, other, seen, options) { + const result = {}; + const keys = /* @__PURE__ */ new Set([ + ...getKeys(record), + ...getKeys(other) + ]); + for (const key of keys) { + if (key === "__proto__") { + continue; + } + const a = record[key]; + if (!Object.hasOwn(other, key)) { + result[key] = a; + continue; + } + const b = other[key]; + if (isNonNullObject(a) && isNonNullObject(b) && !seen.has(a) && !seen.has(b)) { + seen.add(a); + seen.add(b); + result[key] = mergeObjects(a, b, seen, options); + continue; + } + result[key] = b; + } + return result; +} +function mergeObjects(left, right, seen, options = { + arrays: "merge", + sets: "merge", + maps: "merge" +}) { + if (isMergeable(left) && isMergeable(right)) { + return deepMergeInternal(left, right, seen, options); + } + if (isIterable(left) && isIterable(right)) { + if (Array.isArray(left) && Array.isArray(right)) { + if (options.arrays === "merge") { + return left.concat(right); + } + return right; + } + if (left instanceof Map && right instanceof Map) { + if (options.maps === "merge") { + return new Map([ + ...left, + ...right + ]); + } + return right; + } + if (left instanceof Set && right instanceof Set) { + if (options.sets === "merge") { + return /* @__PURE__ */ new Set([ + ...left, + ...right + ]); + } + return right; + } + } + return right; +} +function isMergeable(value2) { + return Object.getPrototypeOf(value2) === Object.prototype; +} +function isIterable(value2) { + return typeof value2[Symbol.iterator] === "function"; +} +function isNonNullObject(value2) { + return value2 !== null && typeof value2 === "object"; +} +function getKeys(record) { + const result = Object.getOwnPropertySymbols(record); + filterInPlace(result, (key) => Object.prototype.propertyIsEnumerable.call(record, key)); + result.push(...Object.keys(record)); + return result; +} + +// node_modules/@std/toml/_parser.js +var Scanner = class { + #whitespace = /[ \t]/; + #position = 0; + #source; + constructor(source) { + this.#source = source; + } + /** + * Get current character + * @param index - relative index from current position + */ + char(index = 0) { + return this.#source[this.#position + index] ?? ""; + } + /** + * Get sliced string + * @param start - start position relative from current position + * @param end - end position relative from current position + */ + slice(start, end) { + return this.#source.slice(this.#position + start, this.#position + end); + } + /** + * Move position to next + */ + next(count) { + if (typeof count === "number") { + for (let i = 0; i < count; i++) { + this.#position++; + } + } else { + this.#position++; + } + } + /** + * Move position until current char is not a whitespace, EOL, or comment. + * @param options.inline - skip only whitespaces + */ + nextUntilChar(options = { + comment: true + }) { + if (options.inline) { + while (this.#whitespace.test(this.char()) && !this.eof()) { + this.next(); + } + } else { + while (!this.eof()) { + const char = this.char(); + if (this.#whitespace.test(char) || this.isCurrentCharEOL()) { + this.next(); + } else if (options.comment && this.char() === "#") { + while (!this.isCurrentCharEOL() && !this.eof()) { + this.next(); + } + } else { + break; + } + } + } + if (!this.isCurrentCharEOL() && /\s/.test(this.char())) { + const escaped = "\\u" + this.char().charCodeAt(0).toString(16); + const position = this.#position; + throw new SyntaxError(`Cannot parse the TOML: It contains invalid whitespace at position '${position}': \`${escaped}\``); + } + } + /** + * Position reached EOF or not + */ + eof() { + return this.position() >= this.#source.length; + } + /** + * Get current position + */ + position() { + return this.#position; + } + isCurrentCharEOL() { + return this.char() === "\n" || this.slice(0, 2) === "\r\n"; + } +}; +function success(body) { + return { + ok: true, + body + }; +} +function failure() { + return { + ok: false + }; +} +function unflat(keys, values = {}, cObj) { + const out = {}; + if (keys.length === 0) { + return cObj; + } + if (!cObj) cObj = values; + const key = keys[keys.length - 1]; + if (typeof key === "string") out[key] = cObj; + return unflat(keys.slice(0, -1), values, out); +} +function deepAssignWithTable(target, table2) { + if (table2.key.length === 0 || table2.key[0] == null) { + throw new Error("Cannot parse the TOML: key length is not a positive number"); + } + const value2 = target[table2.key[0]]; + if (typeof value2 === "undefined") { + Object.assign(target, unflat(table2.key, table2.type === "Table" ? table2.value : [ + table2.value + ])); + } else if (Array.isArray(value2)) { + if (table2.type === "TableArray" && table2.key.length === 1) { + value2.push(table2.value); + } else { + const last = value2[value2.length - 1]; + deepAssignWithTable(last, { + type: table2.type, + key: table2.key.slice(1), + value: table2.value + }); + } + } else if (typeof value2 === "object" && value2 !== null) { + deepAssignWithTable(value2, { + type: table2.type, + key: table2.key.slice(1), + value: table2.value + }); + } else { + throw new Error("Unexpected assign"); + } +} +function or(parsers) { + return (scanner) => { + for (const parse2 of parsers) { + const result = parse2(scanner); + if (result.ok) return result; + } + return failure(); + }; +} +function join(parser, separator) { + const Separator = character(separator); + return (scanner) => { + const first = parser(scanner); + if (!first.ok) return failure(); + const out = [ + first.body + ]; + while (!scanner.eof()) { + if (!Separator(scanner).ok) break; + const result = parser(scanner); + if (!result.ok) { + throw new SyntaxError(`Invalid token after "${separator}"`); + } + out.push(result.body); + } + return success(out); + }; +} +function kv(keyParser, separator, valueParser) { + const Separator = character(separator); + return (scanner) => { + const key = keyParser(scanner); + if (!key.ok) return failure(); + const sep = Separator(scanner); + if (!sep.ok) { + throw new SyntaxError(`key/value pair doesn't have "${separator}"`); + } + const value2 = valueParser(scanner); + if (!value2.ok) { + throw new SyntaxError(`Value of key/value pair is invalid data format`); + } + return success(unflat(key.body, value2.body)); + }; +} +function merge(parser) { + return (scanner) => { + const result = parser(scanner); + if (!result.ok) return failure(); + let body = {}; + for (const record of result.body) { + if (typeof body === "object" && body !== null) { + body = deepMerge(body, record); + } + } + return success(body); + }; +} +function repeat(parser) { + return (scanner) => { + const body = []; + while (!scanner.eof()) { + const result = parser(scanner); + if (!result.ok) break; + body.push(result.body); + scanner.nextUntilChar(); + } + if (body.length === 0) return failure(); + return success(body); + }; +} +function surround(left, parser, right) { + const Left = character(left); + const Right = character(right); + return (scanner) => { + if (!Left(scanner).ok) { + return failure(); + } + const result = parser(scanner); + if (!result.ok) { + throw new SyntaxError(`Invalid token after "${left}"`); + } + if (!Right(scanner).ok) { + throw new SyntaxError(`Not closed by "${right}" after started with "${left}"`); + } + return success(result.body); + }; +} +function character(str) { + return (scanner) => { + scanner.nextUntilChar({ + inline: true + }); + if (scanner.slice(0, str.length) !== str) return failure(); + scanner.next(str.length); + scanner.nextUntilChar({ + inline: true + }); + return success(void 0); + }; +} +var BARE_KEY_REGEXP = /[A-Za-z0-9_-]/; +var FLOAT_REGEXP = /[0-9_\.e+\-]/i; +var END_OF_VALUE_REGEXP = /[ \t\r\n#,}\]]/; +function bareKey(scanner) { + scanner.nextUntilChar({ + inline: true + }); + if (!scanner.char() || !BARE_KEY_REGEXP.test(scanner.char())) { + return failure(); + } + const acc = []; + while (scanner.char() && BARE_KEY_REGEXP.test(scanner.char())) { + acc.push(scanner.char()); + scanner.next(); + } + const key = acc.join(""); + return success(key); +} +function escapeSequence(scanner) { + if (scanner.char() !== "\\") return failure(); + scanner.next(); + switch (scanner.char()) { + case "b": + scanner.next(); + return success("\b"); + case "t": + scanner.next(); + return success(" "); + case "n": + scanner.next(); + return success("\n"); + case "f": + scanner.next(); + return success("\f"); + case "r": + scanner.next(); + return success("\r"); + case "u": + case "U": { + const codePointLen = scanner.char() === "u" ? 4 : 6; + const codePoint = parseInt("0x" + scanner.slice(1, 1 + codePointLen), 16); + const str = String.fromCodePoint(codePoint); + scanner.next(codePointLen + 1); + return success(str); + } + case '"': + scanner.next(); + return success('"'); + case "\\": + scanner.next(); + return success("\\"); + default: + throw new SyntaxError(`Invalid escape sequence: \\${scanner.char()}`); + } +} +function basicString(scanner) { + scanner.nextUntilChar({ + inline: true + }); + if (scanner.char() !== '"') return failure(); + scanner.next(); + const acc = []; + while (scanner.char() !== '"' && !scanner.eof()) { + if (scanner.char() === "\n") { + throw new SyntaxError("Single-line string cannot contain EOL"); + } + const escapedChar = escapeSequence(scanner); + if (escapedChar.ok) { + acc.push(escapedChar.body); + } else { + acc.push(scanner.char()); + scanner.next(); + } + } + if (scanner.eof()) { + throw new SyntaxError(`Single-line string is not closed: +${acc.join("")}`); + } + scanner.next(); + return success(acc.join("")); +} +function literalString(scanner) { + scanner.nextUntilChar({ + inline: true + }); + if (scanner.char() !== "'") return failure(); + scanner.next(); + const acc = []; + while (scanner.char() !== "'" && !scanner.eof()) { + if (scanner.char() === "\n") { + throw new SyntaxError("Single-line string cannot contain EOL"); + } + acc.push(scanner.char()); + scanner.next(); + } + if (scanner.eof()) { + throw new SyntaxError(`Single-line string is not closed: +${acc.join("")}`); + } + scanner.next(); + return success(acc.join("")); +} +function multilineBasicString(scanner) { + scanner.nextUntilChar({ + inline: true + }); + if (scanner.slice(0, 3) !== '"""') return failure(); + scanner.next(3); + if (scanner.char() === "\n") { + scanner.next(); + } else if (scanner.slice(0, 2) === "\r\n") { + scanner.next(2); + } + const acc = []; + while (scanner.slice(0, 3) !== '"""' && !scanner.eof()) { + if (scanner.slice(0, 2) === "\\\n") { + scanner.next(); + scanner.nextUntilChar({ + comment: false + }); + continue; + } else if (scanner.slice(0, 3) === "\\\r\n") { + scanner.next(); + scanner.nextUntilChar({ + comment: false + }); + continue; + } + const escapedChar = escapeSequence(scanner); + if (escapedChar.ok) { + acc.push(escapedChar.body); + } else { + acc.push(scanner.char()); + scanner.next(); + } + } + if (scanner.eof()) { + throw new SyntaxError(`Multi-line string is not closed: +${acc.join("")}`); + } + if (scanner.char(3) === '"') { + acc.push('"'); + scanner.next(); + } + scanner.next(3); + return success(acc.join("")); +} +function multilineLiteralString(scanner) { + scanner.nextUntilChar({ + inline: true + }); + if (scanner.slice(0, 3) !== "'''") return failure(); + scanner.next(3); + if (scanner.char() === "\n") { + scanner.next(); + } else if (scanner.slice(0, 2) === "\r\n") { + scanner.next(2); + } + const acc = []; + while (scanner.slice(0, 3) !== "'''" && !scanner.eof()) { + acc.push(scanner.char()); + scanner.next(); + } + if (scanner.eof()) { + throw new SyntaxError(`Multi-line string is not closed: +${acc.join("")}`); + } + if (scanner.char(3) === "'") { + acc.push("'"); + scanner.next(); + } + scanner.next(3); + return success(acc.join("")); +} +var symbolPairs = [ + [ + "true", + true + ], + [ + "false", + false + ], + [ + "inf", + Infinity + ], + [ + "+inf", + Infinity + ], + [ + "-inf", + -Infinity + ], + [ + "nan", + NaN + ], + [ + "+nan", + NaN + ], + [ + "-nan", + NaN + ] +]; +function symbols(scanner) { + scanner.nextUntilChar({ + inline: true + }); + const found = symbolPairs.find(([str2]) => scanner.slice(0, str2.length) === str2); + if (!found) return failure(); + const [str, value2] = found; + scanner.next(str.length); + return success(value2); +} +var dottedKey = join(or([ + bareKey, + basicString, + literalString +]), "."); +function integer(scanner) { + scanner.nextUntilChar({ + inline: true + }); + const first2 = scanner.slice(0, 2); + if (first2.length === 2 && /0(?:x|o|b)/i.test(first2)) { + scanner.next(2); + const acc2 = [ + first2 + ]; + while (/[0-9a-f_]/i.test(scanner.char()) && !scanner.eof()) { + acc2.push(scanner.char()); + scanner.next(); + } + if (acc2.length === 1) return failure(); + return success(acc2.join("")); + } + const acc = []; + if (/[+-]/.test(scanner.char())) { + acc.push(scanner.char()); + scanner.next(); + } + while (/[0-9_]/.test(scanner.char()) && !scanner.eof()) { + acc.push(scanner.char()); + scanner.next(); + } + if (acc.length === 0 || acc.length === 1 && /[+-]/.test(acc[0])) { + return failure(); + } + const int = parseInt(acc.filter((char) => char !== "_").join("")); + return success(int); +} +function float(scanner) { + scanner.nextUntilChar({ + inline: true + }); + let position = 0; + while (scanner.char(position) && !END_OF_VALUE_REGEXP.test(scanner.char(position))) { + if (!FLOAT_REGEXP.test(scanner.char(position))) return failure(); + position++; + } + const acc = []; + if (/[+-]/.test(scanner.char())) { + acc.push(scanner.char()); + scanner.next(); + } + while (FLOAT_REGEXP.test(scanner.char()) && !scanner.eof()) { + acc.push(scanner.char()); + scanner.next(); + } + if (acc.length === 0) return failure(); + const float2 = parseFloat(acc.filter((char) => char !== "_").join("")); + if (isNaN(float2)) return failure(); + return success(float2); +} +function dateTime(scanner) { + scanner.nextUntilChar({ + inline: true + }); + let dateStr = scanner.slice(0, 10); + if (!/^\d{4}-\d{2}-\d{2}/.test(dateStr)) return failure(); + scanner.next(10); + const acc = []; + while (/[ 0-9TZ.:+-]/.test(scanner.char()) && !scanner.eof()) { + acc.push(scanner.char()); + scanner.next(); + } + dateStr += acc.join(""); + const date = new Date(dateStr.trim()); + if (isNaN(date.getTime())) { + throw new SyntaxError(`Invalid date string "${dateStr}"`); + } + return success(date); +} +function localTime(scanner) { + scanner.nextUntilChar({ + inline: true + }); + let timeStr = scanner.slice(0, 8); + if (!/^(\d{2}):(\d{2}):(\d{2})/.test(timeStr)) return failure(); + scanner.next(8); + const acc = []; + if (scanner.char() !== ".") return success(timeStr); + acc.push(scanner.char()); + scanner.next(); + while (/[0-9]/.test(scanner.char()) && !scanner.eof()) { + acc.push(scanner.char()); + scanner.next(); + } + timeStr += acc.join(""); + return success(timeStr); +} +function arrayValue(scanner) { + scanner.nextUntilChar({ + inline: true + }); + if (scanner.char() !== "[") return failure(); + scanner.next(); + const array = []; + while (!scanner.eof()) { + scanner.nextUntilChar(); + const result = value(scanner); + if (!result.ok) break; + array.push(result.body); + scanner.nextUntilChar({ + inline: true + }); + if (scanner.char() !== ",") break; + scanner.next(); + } + scanner.nextUntilChar(); + if (scanner.char() !== "]") throw new SyntaxError("Array is not closed"); + scanner.next(); + return success(array); +} +function inlineTable(scanner) { + scanner.nextUntilChar(); + if (scanner.char(1) === "}") { + scanner.next(2); + return success({}); + } + const pairs = surround("{", join(pair, ","), "}")(scanner); + if (!pairs.ok) return failure(); + let table2 = {}; + for (const pair2 of pairs.body) { + table2 = deepMerge(table2, pair2); + } + return success(table2); +} +var value = or([ + multilineBasicString, + multilineLiteralString, + basicString, + literalString, + symbols, + dateTime, + localTime, + float, + integer, + arrayValue, + inlineTable +]); +var pair = kv(dottedKey, "=", value); +function block(scanner) { + scanner.nextUntilChar(); + const result = merge(repeat(pair))(scanner); + if (result.ok) return success({ + type: "Block", + value: result.body + }); + return failure(); +} +var tableHeader = surround("[", dottedKey, "]"); +function table(scanner) { + scanner.nextUntilChar(); + const header = tableHeader(scanner); + if (!header.ok) return failure(); + scanner.nextUntilChar(); + const b = block(scanner); + return success({ + type: "Table", + key: header.body, + value: b.ok ? b.body.value : {} + }); +} +var tableArrayHeader = surround("[[", dottedKey, "]]"); +function tableArray(scanner) { + scanner.nextUntilChar(); + const header = tableArrayHeader(scanner); + if (!header.ok) return failure(); + scanner.nextUntilChar(); + const b = block(scanner); + return success({ + type: "TableArray", + key: header.body, + value: b.ok ? b.body.value : {} + }); +} +function toml(scanner) { + const blocks = repeat(or([ + block, + tableArray, + table + ]))(scanner); + if (!blocks.ok) return failure(); + let body = {}; + for (const block2 of blocks.body) { + switch (block2.type) { + case "Block": { + body = deepMerge(body, block2.value); + break; + } + case "Table": { + deepAssignWithTable(body, block2); + break; + } + case "TableArray": { + deepAssignWithTable(body, block2); + break; + } + } + } + return success(body); +} +function parserFactory(parser) { + return (tomlString) => { + const scanner = new Scanner(tomlString); + let parsed = null; + let err = null; + try { + parsed = parser(scanner); + } catch (e) { + err = e instanceof Error ? e : new Error("Invalid error type caught"); + } + if (err || !parsed || !parsed.ok || !scanner.eof()) { + const position = scanner.position(); + const subStr = tomlString.slice(0, position); + const lines = subStr.split("\n"); + const row = lines.length; + const column = (() => { + let count = subStr.length; + for (const line of lines) { + if (count <= line.length) break; + count -= line.length + 1; + } + return count; + })(); + const message = `Parse error on line ${row}, column ${column}: ${err ? err.message : `Unexpected character: "${scanner.char()}"`}`; + throw new SyntaxError(message); + } + return parsed.body; + }; +} + +// node_modules/@std/toml/parse.js +function parse(tomlString) { + return parserFactory(toml)(tomlString); +} +export { + parse, + stringify +}; diff --git a/jamfile/js_builtins/yaml.js b/jamfile/js_builtins/yaml.js new file mode 100644 index 0000000000..1f94ee503d --- /dev/null +++ b/jamfile/js_builtins/yaml.js @@ -0,0 +1,2468 @@ +/** + * Original source: https://jsr.io/@std/yaml + * + * Generated from jsr:@std/yaml@1.0.5 using: + * + * ```bash + * echo 'export * from "@std/toml"' \ + * | ./node_modules/.bin/esbuild --bundle --format=esm \ + * > ../jamfile/js_builtins/toml.js + * ``` + * + */ + +// node_modules/@std/yaml/_chars.js +var BOM = 65279; +var TAB = 9; +var LINE_FEED = 10; +var CARRIAGE_RETURN = 13; +var SPACE = 32; +var EXCLAMATION = 33; +var DOUBLE_QUOTE = 34; +var SHARP = 35; +var PERCENT = 37; +var AMPERSAND = 38; +var SINGLE_QUOTE = 39; +var ASTERISK = 42; +var PLUS = 43; +var COMMA = 44; +var MINUS = 45; +var DOT = 46; +var COLON = 58; +var SMALLER_THAN = 60; +var GREATER_THAN = 62; +var QUESTION = 63; +var COMMERCIAL_AT = 64; +var LEFT_SQUARE_BRACKET = 91; +var BACKSLASH = 92; +var RIGHT_SQUARE_BRACKET = 93; +var GRAVE_ACCENT = 96; +var LEFT_CURLY_BRACKET = 123; +var VERTICAL_LINE = 124; +var RIGHT_CURLY_BRACKET = 125; +function isEOL(c) { + return c === LINE_FEED || c === CARRIAGE_RETURN; +} +function isWhiteSpace(c) { + return c === TAB || c === SPACE; +} +function isWhiteSpaceOrEOL(c) { + return isWhiteSpace(c) || isEOL(c); +} +function isFlowIndicator(c) { + return c === COMMA || c === LEFT_SQUARE_BRACKET || c === RIGHT_SQUARE_BRACKET || c === LEFT_CURLY_BRACKET || c === RIGHT_CURLY_BRACKET; +} + +// node_modules/@std/yaml/_type/binary.js +var BASE64_MAP = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=\n\r"; +function resolveYamlBinary(data) { + if (data === null) return false; + let code; + let bitlen = 0; + const max = data.length; + const map2 = BASE64_MAP; + for (let idx = 0; idx < max; idx++) { + code = map2.indexOf(data.charAt(idx)); + if (code > 64) continue; + if (code < 0) return false; + bitlen += 6; + } + return bitlen % 8 === 0; +} +function constructYamlBinary(data) { + const input = data.replace(/[\r\n=]/g, ""); + const max = input.length; + const map2 = BASE64_MAP; + const result = []; + let bits = 0; + for (let idx = 0; idx < max; idx++) { + if (idx % 4 === 0 && idx) { + result.push(bits >> 16 & 255); + result.push(bits >> 8 & 255); + result.push(bits & 255); + } + bits = bits << 6 | map2.indexOf(input.charAt(idx)); + } + const tailbits = max % 4 * 6; + if (tailbits === 0) { + result.push(bits >> 16 & 255); + result.push(bits >> 8 & 255); + result.push(bits & 255); + } else if (tailbits === 18) { + result.push(bits >> 10 & 255); + result.push(bits >> 2 & 255); + } else if (tailbits === 12) { + result.push(bits >> 4 & 255); + } + return new Uint8Array(result); +} +function representYamlBinary(object) { + const max = object.length; + const map2 = BASE64_MAP; + let result = ""; + let bits = 0; + for (let idx = 0; idx < max; idx++) { + if (idx % 3 === 0 && idx) { + result += map2[bits >> 18 & 63]; + result += map2[bits >> 12 & 63]; + result += map2[bits >> 6 & 63]; + result += map2[bits & 63]; + } + bits = (bits << 8) + object[idx]; + } + const tail = max % 3; + if (tail === 0) { + result += map2[bits >> 18 & 63]; + result += map2[bits >> 12 & 63]; + result += map2[bits >> 6 & 63]; + result += map2[bits & 63]; + } else if (tail === 2) { + result += map2[bits >> 10 & 63]; + result += map2[bits >> 4 & 63]; + result += map2[bits << 2 & 63]; + result += map2[64]; + } else if (tail === 1) { + result += map2[bits >> 2 & 63]; + result += map2[bits << 4 & 63]; + result += map2[64]; + result += map2[64]; + } + return result; +} +function isBinary(obj) { + return obj instanceof Uint8Array; +} +var binary = { + tag: "tag:yaml.org,2002:binary", + construct: constructYamlBinary, + kind: "scalar", + predicate: isBinary, + represent: representYamlBinary, + resolve: resolveYamlBinary +}; + +// node_modules/@std/yaml/_type/bool.js +var YAML_TRUE_BOOLEANS = [ + "true", + "True", + "TRUE" +]; +var YAML_FALSE_BOOLEANS = [ + "false", + "False", + "FALSE" +]; +var YAML_BOOLEANS = [ + ...YAML_TRUE_BOOLEANS, + ...YAML_FALSE_BOOLEANS +]; +var bool = { + tag: "tag:yaml.org,2002:bool", + kind: "scalar", + defaultStyle: "lowercase", + predicate: (value) => typeof value === "boolean" || value instanceof Boolean, + construct: (data) => YAML_TRUE_BOOLEANS.includes(data), + resolve: (data) => YAML_BOOLEANS.includes(data), + represent: { + // deno-lint-ignore ban-types + lowercase: (object) => { + const value = object instanceof Boolean ? object.valueOf() : object; + return value ? "true" : "false"; + }, + // deno-lint-ignore ban-types + uppercase: (object) => { + const value = object instanceof Boolean ? object.valueOf() : object; + return value ? "TRUE" : "FALSE"; + }, + // deno-lint-ignore ban-types + camelcase: (object) => { + const value = object instanceof Boolean ? object.valueOf() : object; + return value ? "True" : "False"; + } + } +}; + +// node_modules/@std/yaml/_utils.js +function isObject(value) { + return value !== null && typeof value === "object"; +} +function isNegativeZero(i) { + return i === 0 && Number.NEGATIVE_INFINITY === 1 / i; +} +function isPlainObject(object) { + return Object.prototype.toString.call(object) === "[object Object]"; +} + +// node_modules/@std/yaml/_type/float.js +var YAML_FLOAT_PATTERN = new RegExp( + // 2.5e4, 2.5 and integers + "^(?:[-+]?(?:0|[1-9][0-9_]*)(?:\\.[0-9_]*)?(?:[eE][-+]?[0-9]+)?|\\.[0-9_]+(?:[eE][-+]?[0-9]+)?|[-+]?\\.(?:inf|Inf|INF)|\\.(?:nan|NaN|NAN))$" +); +function resolveYamlFloat(data) { + if (!YAML_FLOAT_PATTERN.test(data) || // Quick hack to not allow integers end with `_` + // Probably should update regexp & check speed + data[data.length - 1] === "_") { + return false; + } + return true; +} +function constructYamlFloat(data) { + let value = data.replace(/_/g, "").toLowerCase(); + const sign = value[0] === "-" ? -1 : 1; + if (value[0] && "+-".includes(value[0])) { + value = value.slice(1); + } + if (value === ".inf") { + return sign === 1 ? Number.POSITIVE_INFINITY : Number.NEGATIVE_INFINITY; + } + if (value === ".nan") { + return NaN; + } + return sign * parseFloat(value); +} +var SCIENTIFIC_WITHOUT_DOT = /^[-+]?[0-9]+e/; +function representYamlFloat(object, style) { + const value = object instanceof Number ? object.valueOf() : object; + if (isNaN(value)) { + switch (style) { + case "lowercase": + return ".nan"; + case "uppercase": + return ".NAN"; + case "camelcase": + return ".NaN"; + } + } else if (Number.POSITIVE_INFINITY === value) { + switch (style) { + case "lowercase": + return ".inf"; + case "uppercase": + return ".INF"; + case "camelcase": + return ".Inf"; + } + } else if (Number.NEGATIVE_INFINITY === value) { + switch (style) { + case "lowercase": + return "-.inf"; + case "uppercase": + return "-.INF"; + case "camelcase": + return "-.Inf"; + } + } else if (isNegativeZero(value)) { + return "-0.0"; + } + const res = value.toString(10); + return SCIENTIFIC_WITHOUT_DOT.test(res) ? res.replace("e", ".e") : res; +} +function isFloat(object) { + if (object instanceof Number) object = object.valueOf(); + return typeof object === "number" && (object % 1 !== 0 || isNegativeZero(object)); +} +var float = { + tag: "tag:yaml.org,2002:float", + construct: constructYamlFloat, + defaultStyle: "lowercase", + kind: "scalar", + predicate: isFloat, + represent: representYamlFloat, + resolve: resolveYamlFloat +}; + +// node_modules/@std/yaml/_type/int.js +function isCharCodeInRange(c, lower, upper) { + return lower <= c && c <= upper; +} +function isHexCode(c) { + return isCharCodeInRange(c, 48, 57) || // 0-9 + isCharCodeInRange(c, 65, 70) || // A-F + isCharCodeInRange(c, 97, 102); +} +function isOctCode(c) { + return isCharCodeInRange(c, 48, 55); +} +function isDecCode(c) { + return isCharCodeInRange(c, 48, 57); +} +function resolveYamlInteger(data) { + const max = data.length; + let index = 0; + let hasDigits = false; + if (!max) return false; + let ch = data[index]; + if (ch === "-" || ch === "+") { + ch = data[++index]; + } + if (ch === "0") { + if (index + 1 === max) return true; + ch = data[++index]; + if (ch === "b") { + index++; + for (; index < max; index++) { + ch = data[index]; + if (ch === "_") continue; + if (ch !== "0" && ch !== "1") return false; + hasDigits = true; + } + return hasDigits && ch !== "_"; + } + if (ch === "x") { + index++; + for (; index < max; index++) { + ch = data[index]; + if (ch === "_") continue; + if (!isHexCode(data.charCodeAt(index))) return false; + hasDigits = true; + } + return hasDigits && ch !== "_"; + } + for (; index < max; index++) { + ch = data[index]; + if (ch === "_") continue; + if (!isOctCode(data.charCodeAt(index))) return false; + hasDigits = true; + } + return hasDigits && ch !== "_"; + } + if (ch === "_") return false; + for (; index < max; index++) { + ch = data[index]; + if (ch === "_") continue; + if (!isDecCode(data.charCodeAt(index))) { + return false; + } + hasDigits = true; + } + if (!hasDigits || ch === "_") return false; + return /^(:[0-5]?[0-9])+$/.test(data.slice(index)); +} +function constructYamlInteger(data) { + let value = data; + if (value.includes("_")) { + value = value.replace(/_/g, ""); + } + let sign = 1; + let ch = value[0]; + if (ch === "-" || ch === "+") { + if (ch === "-") sign = -1; + value = value.slice(1); + ch = value[0]; + } + if (value === "0") return 0; + if (ch === "0") { + if (value[1] === "b") return sign * parseInt(value.slice(2), 2); + if (value[1] === "x") return sign * parseInt(value, 16); + return sign * parseInt(value, 8); + } + return sign * parseInt(value, 10); +} +function isInteger(object) { + if (object instanceof Number) object = object.valueOf(); + return typeof object === "number" && object % 1 === 0 && !isNegativeZero(object); +} +var int = { + tag: "tag:yaml.org,2002:int", + construct: constructYamlInteger, + defaultStyle: "decimal", + kind: "scalar", + predicate: isInteger, + represent: { + // deno-lint-ignore ban-types + binary(object) { + const value = object instanceof Number ? object.valueOf() : object; + return value >= 0 ? `0b${value.toString(2)}` : `-0b${value.toString(2).slice(1)}`; + }, + // deno-lint-ignore ban-types + octal(object) { + const value = object instanceof Number ? object.valueOf() : object; + return value >= 0 ? `0${value.toString(8)}` : `-0${value.toString(8).slice(1)}`; + }, + // deno-lint-ignore ban-types + decimal(object) { + const value = object instanceof Number ? object.valueOf() : object; + return value.toString(10); + }, + // deno-lint-ignore ban-types + hexadecimal(object) { + const value = object instanceof Number ? object.valueOf() : object; + return value >= 0 ? `0x${value.toString(16).toUpperCase()}` : `-0x${value.toString(16).toUpperCase().slice(1)}`; + } + }, + resolve: resolveYamlInteger +}; + +// node_modules/@std/yaml/_type/map.js +var map = { + tag: "tag:yaml.org,2002:map", + resolve() { + return true; + }, + construct(data) { + return data !== null ? data : {}; + }, + kind: "mapping" +}; + +// node_modules/@std/yaml/_type/merge.js +var merge = { + tag: "tag:yaml.org,2002:merge", + kind: "scalar", + resolve: (data) => data === "<<" || data === null, + construct: (data) => data +}; + +// node_modules/@std/yaml/_type/nil.js +var nil = { + tag: "tag:yaml.org,2002:null", + kind: "scalar", + defaultStyle: "lowercase", + predicate: (object) => object === null, + construct: () => null, + resolve: (data) => { + return data === "~" || data === "null" || data === "Null" || data === "NULL"; + }, + represent: { + lowercase: () => "null", + uppercase: () => "NULL", + camelcase: () => "Null" + } +}; + +// node_modules/@std/yaml/_type/omap.js +function resolveYamlOmap(data) { + const objectKeys = /* @__PURE__ */ new Set(); + for (const object of data) { + if (!isPlainObject(object)) return false; + const keys = Object.keys(object); + if (keys.length !== 1) return false; + for (const key of keys) { + if (objectKeys.has(key)) return false; + objectKeys.add(key); + } + } + return true; +} +var omap = { + tag: "tag:yaml.org,2002:omap", + kind: "sequence", + resolve: resolveYamlOmap, + construct(data) { + return data; + } +}; + +// node_modules/@std/yaml/_type/pairs.js +function resolveYamlPairs(data) { + if (data === null) return true; + return data.every((it) => isPlainObject(it) && Object.keys(it).length === 1); +} +var pairs = { + tag: "tag:yaml.org,2002:pairs", + construct(data) { + return data?.flatMap(Object.entries) ?? []; + }, + kind: "sequence", + resolve: resolveYamlPairs +}; + +// node_modules/@std/yaml/_type/regexp.js +var REGEXP = /^\/(?<regexp>[\s\S]+)\/(?<modifiers>[gismuy]*)$/; +var regexp = { + tag: "tag:yaml.org,2002:js/regexp", + kind: "scalar", + resolve(data) { + if (data === null || !data.length) return false; + if (data.charAt(0) === "/") { + const groups = data.match(REGEXP)?.groups; + if (!groups) return false; + const modifiers = groups.modifiers ?? ""; + if (new Set(modifiers).size < modifiers.length) return false; + } + return true; + }, + construct(data) { + const { regexp: regexp2 = data, modifiers = "" } = data.match(REGEXP)?.groups ?? {}; + return new RegExp(regexp2, modifiers); + }, + predicate: (object) => object instanceof RegExp, + represent: (object) => object.toString() +}; + +// node_modules/@std/yaml/_type/seq.js +var seq = { + tag: "tag:yaml.org,2002:seq", + kind: "sequence", + resolve: () => true, + construct: (data) => data !== null ? data : [] +}; + +// node_modules/@std/yaml/_type/set.js +var set = { + tag: "tag:yaml.org,2002:set", + kind: "mapping", + construct: (data) => data !== null ? data : {}, + resolve: (data) => { + if (data === null) return true; + return Object.values(data).every((it) => it === null); + } +}; + +// node_modules/@std/yaml/_type/str.js +var str = { + tag: "tag:yaml.org,2002:str", + kind: "scalar", + resolve: () => true, + construct: (data) => data !== null ? data : "" +}; + +// node_modules/@std/yaml/_type/timestamp.js +var YAML_DATE_REGEXP = new RegExp("^([0-9][0-9][0-9][0-9])-([0-9][0-9])-([0-9][0-9])$"); +var YAML_TIMESTAMP_REGEXP = new RegExp("^([0-9][0-9][0-9][0-9])-([0-9][0-9]?)-([0-9][0-9]?)(?:[Tt]|[ \\t]+)([0-9][0-9]?):([0-9][0-9]):([0-9][0-9])(?:\\.([0-9]*))?(?:[ \\t]*(Z|([-+])([0-9][0-9]?)(?::([0-9][0-9]))?))?$"); +function resolveYamlTimestamp(data) { + if (data === null) return false; + if (YAML_DATE_REGEXP.exec(data) !== null) return true; + if (YAML_TIMESTAMP_REGEXP.exec(data) !== null) return true; + return false; +} +function constructYamlTimestamp(data) { + let match = YAML_DATE_REGEXP.exec(data); + if (match === null) match = YAML_TIMESTAMP_REGEXP.exec(data); + if (match === null) { + throw new Error("Cannot construct YAML timestamp: date resolve error"); + } + const year = +match[1]; + const month = +match[2] - 1; + const day = +match[3]; + if (!match[4]) { + return new Date(Date.UTC(year, month, day)); + } + const hour = +match[4]; + const minute = +match[5]; + const second = +match[6]; + let fraction = 0; + if (match[7]) { + let partFraction = match[7].slice(0, 3); + while (partFraction.length < 3) { + partFraction += "0"; + } + fraction = +partFraction; + } + let delta = null; + if (match[9] && match[10]) { + const tzHour = +match[10]; + const tzMinute = +(match[11] || 0); + delta = (tzHour * 60 + tzMinute) * 6e4; + if (match[9] === "-") delta = -delta; + } + const date = new Date(Date.UTC(year, month, day, hour, minute, second, fraction)); + if (delta) date.setTime(date.getTime() - delta); + return date; +} +function representYamlTimestamp(date) { + return date.toISOString(); +} +var timestamp = { + tag: "tag:yaml.org,2002:timestamp", + construct: constructYamlTimestamp, + predicate(object) { + return object instanceof Date; + }, + kind: "scalar", + represent: representYamlTimestamp, + resolve: resolveYamlTimestamp +}; + +// node_modules/@std/yaml/_type/undefined.js +var undefinedType = { + tag: "tag:yaml.org,2002:js/undefined", + kind: "scalar", + resolve() { + return true; + }, + construct() { + return void 0; + }, + predicate(object) { + return typeof object === "undefined"; + }, + represent() { + return ""; + } +}; + +// node_modules/@std/yaml/_schema.js +function createTypeMap(implicitTypes, explicitTypes) { + const result = { + fallback: /* @__PURE__ */ new Map(), + mapping: /* @__PURE__ */ new Map(), + scalar: /* @__PURE__ */ new Map(), + sequence: /* @__PURE__ */ new Map() + }; + const fallbackMap = result.fallback; + for (const type of [ + ...implicitTypes, + ...explicitTypes + ]) { + const map2 = result[type.kind]; + map2.set(type.tag, type); + fallbackMap.set(type.tag, type); + } + return result; +} +function createSchema({ explicitTypes = [], implicitTypes = [], include }) { + if (include) { + implicitTypes.push(...include.implicitTypes); + explicitTypes.push(...include.explicitTypes); + } + const typeMap = createTypeMap(implicitTypes, explicitTypes); + return { + implicitTypes, + explicitTypes, + typeMap + }; +} +var FAILSAFE_SCHEMA = createSchema({ + explicitTypes: [ + str, + seq, + map + ] +}); +var JSON_SCHEMA = createSchema({ + implicitTypes: [ + nil, + bool, + int, + float + ], + include: FAILSAFE_SCHEMA +}); +var CORE_SCHEMA = createSchema({ + include: JSON_SCHEMA +}); +var DEFAULT_SCHEMA = createSchema({ + explicitTypes: [ + binary, + omap, + pairs, + set + ], + implicitTypes: [ + timestamp, + merge + ], + include: CORE_SCHEMA +}); +var EXTENDED_SCHEMA = createSchema({ + explicitTypes: [ + regexp, + undefinedType + ], + include: DEFAULT_SCHEMA +}); +var SCHEMA_MAP = /* @__PURE__ */ new Map([ + [ + "core", + CORE_SCHEMA + ], + [ + "default", + DEFAULT_SCHEMA + ], + [ + "failsafe", + FAILSAFE_SCHEMA + ], + [ + "json", + JSON_SCHEMA + ], + [ + "extended", + EXTENDED_SCHEMA + ] +]); + +// node_modules/@std/yaml/_loader_state.js +var CONTEXT_FLOW_IN = 1; +var CONTEXT_FLOW_OUT = 2; +var CONTEXT_BLOCK_IN = 3; +var CONTEXT_BLOCK_OUT = 4; +var CHOMPING_CLIP = 1; +var CHOMPING_STRIP = 2; +var CHOMPING_KEEP = 3; +var PATTERN_NON_PRINTABLE = ( + // deno-lint-ignore no-control-regex + /[\x00-\x08\x0B\x0C\x0E-\x1F\x7F-\x84\x86-\x9F\uFFFE\uFFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?:[^\uD800-\uDBFF]|^)[\uDC00-\uDFFF]/ +); +var PATTERN_NON_ASCII_LINE_BREAKS = /[\x85\u2028\u2029]/; +var PATTERN_FLOW_INDICATORS = /[,\[\]\{\}]/; +var PATTERN_TAG_HANDLE = /^(?:!|!!|![a-z\-]+!)$/i; +var PATTERN_TAG_URI = /^(?:!|[^,\[\]\{\}])(?:%[0-9a-f]{2}|[0-9a-z\-#;\/\?:@&=\+\$,_\.!~\*'\(\)\[\]])*$/i; +var ESCAPED_HEX_LENGTHS = /* @__PURE__ */ new Map([ + [ + 120, + 2 + ], + [ + 117, + 4 + ], + [ + 85, + 8 + ] +]); +var SIMPLE_ESCAPE_SEQUENCES = /* @__PURE__ */ new Map([ + [ + 48, + "\0" + ], + [ + 97, + "\x07" + ], + [ + 98, + "\b" + ], + [ + 116, + " " + ], + [ + 9, + " " + ], + [ + 110, + "\n" + ], + [ + 118, + "\v" + ], + [ + 102, + "\f" + ], + [ + 114, + "\r" + ], + [ + 101, + "\x1B" + ], + [ + 32, + " " + ], + [ + 34, + '"' + ], + [ + 47, + "/" + ], + [ + 92, + "\\" + ], + [ + 78, + "\x85" + ], + [ + 95, + "\xA0" + ], + [ + 76, + "\u2028" + ], + [ + 80, + "\u2029" + ] +]); +function hexCharCodeToNumber(charCode) { + if (48 <= charCode && charCode <= 57) return charCode - 48; + const lc = charCode | 32; + if (97 <= lc && lc <= 102) return lc - 97 + 10; + return -1; +} +function decimalCharCodeToNumber(charCode) { + if (48 <= charCode && charCode <= 57) return charCode - 48; + return -1; +} +function codepointToChar(codepoint) { + if (codepoint <= 65535) return String.fromCharCode(codepoint); + return String.fromCharCode((codepoint - 65536 >> 10) + 55296, (codepoint - 65536 & 1023) + 56320); +} +var INDENT = 4; +var MAX_LENGTH = 75; +var DELIMITERS = "\0\r\n\x85\u2028\u2029"; +function getSnippet(buffer, position) { + if (!buffer) return null; + let start = position; + let end = position; + let head = ""; + let tail = ""; + while (start > 0 && !DELIMITERS.includes(buffer.charAt(start - 1))) { + start--; + if (position - start > MAX_LENGTH / 2 - 1) { + head = " ... "; + start += 5; + break; + } + } + while (end < buffer.length && !DELIMITERS.includes(buffer.charAt(end))) { + end++; + if (end - position > MAX_LENGTH / 2 - 1) { + tail = " ... "; + end -= 5; + break; + } + } + const snippet = buffer.slice(start, end); + const indent = " ".repeat(INDENT); + const caretIndent = " ".repeat(INDENT + position - start + head.length); + return `${indent + head + snippet + tail} +${caretIndent}^`; +} +function markToString(buffer, position, line, column) { + let where = `at line ${line + 1}, column ${column + 1}`; + const snippet = getSnippet(buffer, position); + if (snippet) where += `: +${snippet}`; + return where; +} +var LoaderState = class { + input; + length; + lineIndent = 0; + lineStart = 0; + position = 0; + line = 0; + onWarning; + allowDuplicateKeys; + implicitTypes; + typeMap; + version; + checkLineBreaks = false; + tagMap = /* @__PURE__ */ new Map(); + anchorMap = /* @__PURE__ */ new Map(); + tag; + anchor; + kind; + result = ""; + constructor(input, { schema = DEFAULT_SCHEMA, onWarning, allowDuplicateKeys = false }) { + this.input = input; + this.onWarning = onWarning; + this.allowDuplicateKeys = allowDuplicateKeys; + this.implicitTypes = schema.implicitTypes; + this.typeMap = schema.typeMap; + this.length = input.length; + this.version = null; + this.readIndent(); + } + readIndent() { + let char = this.peek(); + while (char === SPACE) { + this.lineIndent += 1; + char = this.next(); + } + } + peek(offset = 0) { + return this.input.charCodeAt(this.position + offset); + } + next() { + this.position += 1; + return this.peek(); + } + #createError(message) { + const mark = markToString(this.input, this.position, this.line, this.position - this.lineStart); + return new SyntaxError(`${message} ${mark}`); + } + throwError(message) { + throw this.#createError(message); + } + dispatchWarning(message) { + const error = this.#createError(message); + this.onWarning?.(error); + } + yamlDirectiveHandler(...args) { + if (this.version !== null) { + return this.throwError("Cannot handle YAML directive: duplication of %YAML directive"); + } + if (args.length !== 1) { + return this.throwError("Cannot handle YAML directive: YAML directive accepts exactly one argument"); + } + const match = /^([0-9]+)\.([0-9]+)$/.exec(args[0]); + if (match === null) { + return this.throwError("Cannot handle YAML directive: ill-formed argument"); + } + const major = parseInt(match[1], 10); + const minor = parseInt(match[2], 10); + if (major !== 1) { + return this.throwError("Cannot handle YAML directive: unacceptable YAML version"); + } + this.version = args[0] ?? null; + this.checkLineBreaks = minor < 2; + if (minor !== 1 && minor !== 2) { + return this.dispatchWarning("Cannot handle YAML directive: unsupported YAML version"); + } + } + tagDirectiveHandler(...args) { + if (args.length !== 2) { + return this.throwError(`Cannot handle tag directive: directive accepts exactly two arguments, received ${args.length}`); + } + const handle = args[0]; + const prefix = args[1]; + if (!PATTERN_TAG_HANDLE.test(handle)) { + return this.throwError(`Cannot handle tag directive: ill-formed handle (first argument) in "${handle}"`); + } + if (this.tagMap.has(handle)) { + return this.throwError(`Cannot handle tag directive: previously declared suffix for "${handle}" tag handle`); + } + if (!PATTERN_TAG_URI.test(prefix)) { + return this.throwError("Cannot handle tag directive: ill-formed tag prefix (second argument) of the TAG directive"); + } + this.tagMap.set(handle, prefix); + } + captureSegment(start, end, checkJson) { + let result; + if (start < end) { + result = this.input.slice(start, end); + if (checkJson) { + for (let position = 0; position < result.length; position++) { + const character = result.charCodeAt(position); + if (!(character === 9 || 32 <= character && character <= 1114111)) { + return this.throwError(`Expected valid JSON character: received "${character}"`); + } + } + } else if (PATTERN_NON_PRINTABLE.test(result)) { + return this.throwError("Stream contains non-printable characters"); + } + this.result += result; + } + } + readBlockSequence(nodeIndent) { + let line; + let following; + let detected = false; + let ch; + const tag = this.tag; + const anchor = this.anchor; + const result = []; + if (this.anchor !== null && typeof this.anchor !== "undefined") { + this.anchorMap.set(this.anchor, result); + } + ch = this.peek(); + while (ch !== 0) { + if (ch !== MINUS) { + break; + } + following = this.peek(1); + if (!isWhiteSpaceOrEOL(following)) { + break; + } + detected = true; + this.position++; + if (this.skipSeparationSpace(true, -1)) { + if (this.lineIndent <= nodeIndent) { + result.push(null); + ch = this.peek(); + continue; + } + } + line = this.line; + this.composeNode(nodeIndent, CONTEXT_BLOCK_IN, false, true); + result.push(this.result); + this.skipSeparationSpace(true, -1); + ch = this.peek(); + if ((this.line === line || this.lineIndent > nodeIndent) && ch !== 0) { + return this.throwError("Cannot read block sequence: bad indentation of a sequence entry"); + } else if (this.lineIndent < nodeIndent) { + break; + } + } + if (detected) { + this.tag = tag; + this.anchor = anchor; + this.kind = "sequence"; + this.result = result; + return true; + } + return false; + } + mergeMappings(destination, source, overridableKeys) { + if (!isObject(source)) { + return this.throwError("Cannot merge mappings: the provided source object is unacceptable"); + } + for (const [key, value] of Object.entries(source)) { + if (Object.hasOwn(destination, key)) continue; + Object.defineProperty(destination, key, { + value, + writable: true, + enumerable: true, + configurable: true + }); + overridableKeys.add(key); + } + } + storeMappingPair(result, overridableKeys, keyTag, keyNode, valueNode, startLine, startPos) { + if (Array.isArray(keyNode)) { + keyNode = Array.prototype.slice.call(keyNode); + for (let index = 0; index < keyNode.length; index++) { + if (Array.isArray(keyNode[index])) { + return this.throwError("Cannot store mapping pair: nested arrays are not supported inside keys"); + } + if (typeof keyNode === "object" && isPlainObject(keyNode[index])) { + keyNode[index] = "[object Object]"; + } + } + } + if (typeof keyNode === "object" && isPlainObject(keyNode)) { + keyNode = "[object Object]"; + } + keyNode = String(keyNode); + if (keyTag === "tag:yaml.org,2002:merge") { + if (Array.isArray(valueNode)) { + for (let index = 0; index < valueNode.length; index++) { + this.mergeMappings(result, valueNode[index], overridableKeys); + } + } else { + this.mergeMappings(result, valueNode, overridableKeys); + } + } else { + if (!this.allowDuplicateKeys && !overridableKeys.has(keyNode) && Object.hasOwn(result, keyNode)) { + this.line = startLine || this.line; + this.position = startPos || this.position; + return this.throwError("Cannot store mapping pair: duplicated key"); + } + Object.defineProperty(result, keyNode, { + value: valueNode, + writable: true, + enumerable: true, + configurable: true + }); + overridableKeys.delete(keyNode); + } + return result; + } + readLineBreak() { + const ch = this.peek(); + if (ch === LINE_FEED) { + this.position++; + } else if (ch === CARRIAGE_RETURN) { + this.position++; + if (this.peek() === LINE_FEED) { + this.position++; + } + } else { + return this.throwError("Cannot read line: line break not found"); + } + this.line += 1; + this.lineStart = this.position; + } + skipSeparationSpace(allowComments, checkIndent) { + let lineBreaks = 0; + let ch = this.peek(); + while (ch !== 0) { + while (isWhiteSpace(ch)) { + ch = this.next(); + } + if (allowComments && ch === SHARP) { + do { + ch = this.next(); + } while (ch !== LINE_FEED && ch !== CARRIAGE_RETURN && ch !== 0); + } + if (isEOL(ch)) { + this.readLineBreak(); + ch = this.peek(); + lineBreaks++; + this.lineIndent = 0; + this.readIndent(); + ch = this.peek(); + } else { + break; + } + } + if (checkIndent !== -1 && lineBreaks !== 0 && this.lineIndent < checkIndent) { + this.dispatchWarning("deficient indentation"); + } + return lineBreaks; + } + testDocumentSeparator() { + let ch = this.peek(); + if ((ch === MINUS || ch === DOT) && ch === this.peek(1) && ch === this.peek(2)) { + ch = this.peek(3); + if (ch === 0 || isWhiteSpaceOrEOL(ch)) { + return true; + } + } + return false; + } + writeFoldedLines(count) { + if (count === 1) { + this.result += " "; + } else if (count > 1) { + this.result += "\n".repeat(count - 1); + } + } + readPlainScalar(nodeIndent, withinFlowCollection) { + const kind = this.kind; + const result = this.result; + let ch = this.peek(); + if (isWhiteSpaceOrEOL(ch) || isFlowIndicator(ch) || ch === SHARP || ch === AMPERSAND || ch === ASTERISK || ch === EXCLAMATION || ch === VERTICAL_LINE || ch === GREATER_THAN || ch === SINGLE_QUOTE || ch === DOUBLE_QUOTE || ch === PERCENT || ch === COMMERCIAL_AT || ch === GRAVE_ACCENT) { + return false; + } + let following; + if (ch === QUESTION || ch === MINUS) { + following = this.peek(1); + if (isWhiteSpaceOrEOL(following) || withinFlowCollection && isFlowIndicator(following)) { + return false; + } + } + this.kind = "scalar"; + this.result = ""; + let captureEnd = this.position; + let captureStart = this.position; + let hasPendingContent = false; + let line = 0; + while (ch !== 0) { + if (ch === COLON) { + following = this.peek(1); + if (isWhiteSpaceOrEOL(following) || withinFlowCollection && isFlowIndicator(following)) { + break; + } + } else if (ch === SHARP) { + const preceding = this.peek(-1); + if (isWhiteSpaceOrEOL(preceding)) { + break; + } + } else if (this.position === this.lineStart && this.testDocumentSeparator() || withinFlowCollection && isFlowIndicator(ch)) { + break; + } else if (isEOL(ch)) { + line = this.line; + const lineStart = this.lineStart; + const lineIndent = this.lineIndent; + this.skipSeparationSpace(false, -1); + if (this.lineIndent >= nodeIndent) { + hasPendingContent = true; + ch = this.peek(); + continue; + } else { + this.position = captureEnd; + this.line = line; + this.lineStart = lineStart; + this.lineIndent = lineIndent; + break; + } + } + if (hasPendingContent) { + this.captureSegment(captureStart, captureEnd, false); + this.writeFoldedLines(this.line - line); + captureStart = captureEnd = this.position; + hasPendingContent = false; + } + if (!isWhiteSpace(ch)) { + captureEnd = this.position + 1; + } + ch = this.next(); + } + this.captureSegment(captureStart, captureEnd, false); + if (this.result) { + return true; + } + this.kind = kind; + this.result = result; + return false; + } + readSingleQuotedScalar(nodeIndent) { + let ch; + let captureStart; + let captureEnd; + ch = this.peek(); + if (ch !== SINGLE_QUOTE) { + return false; + } + this.kind = "scalar"; + this.result = ""; + this.position++; + captureStart = captureEnd = this.position; + while ((ch = this.peek()) !== 0) { + if (ch === SINGLE_QUOTE) { + this.captureSegment(captureStart, this.position, true); + ch = this.next(); + if (ch === SINGLE_QUOTE) { + captureStart = this.position; + this.position++; + captureEnd = this.position; + } else { + return true; + } + } else if (isEOL(ch)) { + this.captureSegment(captureStart, captureEnd, true); + this.writeFoldedLines(this.skipSeparationSpace(false, nodeIndent)); + captureStart = captureEnd = this.position; + } else if (this.position === this.lineStart && this.testDocumentSeparator()) { + return this.throwError("Unexpected end of the document within a single quoted scalar"); + } else { + this.position++; + captureEnd = this.position; + } + } + return this.throwError("Unexpected end of the stream within a single quoted scalar"); + } + readDoubleQuotedScalar(nodeIndent) { + let ch = this.peek(); + if (ch !== DOUBLE_QUOTE) { + return false; + } + this.kind = "scalar"; + this.result = ""; + this.position++; + let captureEnd = this.position; + let captureStart = this.position; + let tmp; + while ((ch = this.peek()) !== 0) { + if (ch === DOUBLE_QUOTE) { + this.captureSegment(captureStart, this.position, true); + this.position++; + return true; + } + if (ch === BACKSLASH) { + this.captureSegment(captureStart, this.position, true); + ch = this.next(); + if (isEOL(ch)) { + this.skipSeparationSpace(false, nodeIndent); + } else if (ch < 256 && SIMPLE_ESCAPE_SEQUENCES.has(ch)) { + this.result += SIMPLE_ESCAPE_SEQUENCES.get(ch); + this.position++; + } else if ((tmp = ESCAPED_HEX_LENGTHS.get(ch) ?? 0) > 0) { + let hexLength = tmp; + let hexResult = 0; + for (; hexLength > 0; hexLength--) { + ch = this.next(); + if ((tmp = hexCharCodeToNumber(ch)) >= 0) { + hexResult = (hexResult << 4) + tmp; + } else { + return this.throwError("Cannot read double quoted scalar: expected hexadecimal character"); + } + } + this.result += codepointToChar(hexResult); + this.position++; + } else { + return this.throwError("Cannot read double quoted scalar: unknown escape sequence"); + } + captureStart = captureEnd = this.position; + } else if (isEOL(ch)) { + this.captureSegment(captureStart, captureEnd, true); + this.writeFoldedLines(this.skipSeparationSpace(false, nodeIndent)); + captureStart = captureEnd = this.position; + } else if (this.position === this.lineStart && this.testDocumentSeparator()) { + return this.throwError("Unexpected end of the document within a double quoted scalar"); + } else { + this.position++; + captureEnd = this.position; + } + } + return this.throwError("Unexpected end of the stream within a double quoted scalar"); + } + readFlowCollection(nodeIndent) { + let ch = this.peek(); + let terminator; + let isMapping = true; + let result = {}; + if (ch === LEFT_SQUARE_BRACKET) { + terminator = RIGHT_SQUARE_BRACKET; + isMapping = false; + result = []; + } else if (ch === LEFT_CURLY_BRACKET) { + terminator = RIGHT_CURLY_BRACKET; + } else { + return false; + } + if (this.anchor !== null && typeof this.anchor !== "undefined") { + this.anchorMap.set(this.anchor, result); + } + ch = this.next(); + const tag = this.tag; + const anchor = this.anchor; + let readNext = true; + let valueNode = null; + let keyNode = null; + let keyTag = null; + let isExplicitPair = false; + let isPair = false; + let following = 0; + let line = 0; + const overridableKeys = /* @__PURE__ */ new Set(); + while (ch !== 0) { + this.skipSeparationSpace(true, nodeIndent); + ch = this.peek(); + if (ch === terminator) { + this.position++; + this.tag = tag; + this.anchor = anchor; + this.kind = isMapping ? "mapping" : "sequence"; + this.result = result; + return true; + } + if (!readNext) { + return this.throwError("Cannot read flow collection: missing comma between flow collection entries"); + } + keyTag = keyNode = valueNode = null; + isPair = isExplicitPair = false; + if (ch === QUESTION) { + following = this.peek(1); + if (isWhiteSpaceOrEOL(following)) { + isPair = isExplicitPair = true; + this.position++; + this.skipSeparationSpace(true, nodeIndent); + } + } + line = this.line; + this.composeNode(nodeIndent, CONTEXT_FLOW_IN, false, true); + keyTag = this.tag || null; + keyNode = this.result; + this.skipSeparationSpace(true, nodeIndent); + ch = this.peek(); + if ((isExplicitPair || this.line === line) && ch === COLON) { + isPair = true; + ch = this.next(); + this.skipSeparationSpace(true, nodeIndent); + this.composeNode(nodeIndent, CONTEXT_FLOW_IN, false, true); + valueNode = this.result; + } + if (isMapping) { + this.storeMappingPair(result, overridableKeys, keyTag, keyNode, valueNode); + } else if (isPair) { + result.push(this.storeMappingPair({}, overridableKeys, keyTag, keyNode, valueNode)); + } else { + result.push(keyNode); + } + this.skipSeparationSpace(true, nodeIndent); + ch = this.peek(); + if (ch === COMMA) { + readNext = true; + ch = this.next(); + } else { + readNext = false; + } + } + return this.throwError("Cannot read flow collection: unexpected end of the stream within a flow collection"); + } + // Handles block scaler styles: e.g. '|', '>', '|-' and '>-'. + // https://yaml.org/spec/1.2.2/#81-block-scalar-styles + readBlockScalar(nodeIndent) { + let chomping = CHOMPING_CLIP; + let didReadContent = false; + let detectedIndent = false; + let textIndent = nodeIndent; + let emptyLines = 0; + let atMoreIndented = false; + let ch = this.peek(); + let folding = false; + if (ch === VERTICAL_LINE) { + folding = false; + } else if (ch === GREATER_THAN) { + folding = true; + } else { + return false; + } + this.kind = "scalar"; + this.result = ""; + let tmp = 0; + while (ch !== 0) { + ch = this.next(); + if (ch === PLUS || ch === MINUS) { + if (CHOMPING_CLIP === chomping) { + chomping = ch === PLUS ? CHOMPING_KEEP : CHOMPING_STRIP; + } else { + return this.throwError("Cannot read block: chomping mode identifier repeated"); + } + } else if ((tmp = decimalCharCodeToNumber(ch)) >= 0) { + if (tmp === 0) { + return this.throwError("Cannot read block: indentation width must be greater than 0"); + } else if (!detectedIndent) { + textIndent = nodeIndent + tmp - 1; + detectedIndent = true; + } else { + return this.throwError("Cannot read block: indentation width identifier repeated"); + } + } else { + break; + } + } + if (isWhiteSpace(ch)) { + do { + ch = this.next(); + } while (isWhiteSpace(ch)); + if (ch === SHARP) { + do { + ch = this.next(); + } while (!isEOL(ch) && ch !== 0); + } + } + while (ch !== 0) { + this.readLineBreak(); + this.lineIndent = 0; + ch = this.peek(); + while ((!detectedIndent || this.lineIndent < textIndent) && ch === SPACE) { + this.lineIndent++; + ch = this.next(); + } + if (!detectedIndent && this.lineIndent > textIndent) { + textIndent = this.lineIndent; + } + if (isEOL(ch)) { + emptyLines++; + continue; + } + if (this.lineIndent < textIndent) { + if (chomping === CHOMPING_KEEP) { + this.result += "\n".repeat(didReadContent ? 1 + emptyLines : emptyLines); + } else if (chomping === CHOMPING_CLIP) { + if (didReadContent) { + this.result += "\n"; + } + } + break; + } + if (folding) { + if (isWhiteSpace(ch)) { + atMoreIndented = true; + this.result += "\n".repeat(didReadContent ? 1 + emptyLines : emptyLines); + } else if (atMoreIndented) { + atMoreIndented = false; + this.result += "\n".repeat(emptyLines + 1); + } else if (emptyLines === 0) { + if (didReadContent) { + this.result += " "; + } + } else { + this.result += "\n".repeat(emptyLines); + } + } else { + this.result += "\n".repeat(didReadContent ? 1 + emptyLines : emptyLines); + } + didReadContent = true; + detectedIndent = true; + emptyLines = 0; + const captureStart = this.position; + while (!isEOL(ch) && ch !== 0) { + ch = this.next(); + } + this.captureSegment(captureStart, this.position, false); + } + return true; + } + readBlockMapping(nodeIndent, flowIndent) { + const tag = this.tag; + const anchor = this.anchor; + const result = {}; + const overridableKeys = /* @__PURE__ */ new Set(); + let following; + let allowCompact = false; + let line; + let pos; + let keyTag = null; + let keyNode = null; + let valueNode = null; + let atExplicitKey = false; + let detected = false; + let ch; + if (this.anchor !== null && typeof this.anchor !== "undefined") { + this.anchorMap.set(this.anchor, result); + } + ch = this.peek(); + while (ch !== 0) { + following = this.peek(1); + line = this.line; + pos = this.position; + if ((ch === QUESTION || ch === COLON) && isWhiteSpaceOrEOL(following)) { + if (ch === QUESTION) { + if (atExplicitKey) { + this.storeMappingPair(result, overridableKeys, keyTag, keyNode, null); + keyTag = keyNode = valueNode = null; + } + detected = true; + atExplicitKey = true; + allowCompact = true; + } else if (atExplicitKey) { + atExplicitKey = false; + allowCompact = true; + } else { + return this.throwError("Cannot read block as explicit mapping pair is incomplete: a key node is missed or followed by a non-tabulated empty line"); + } + this.position += 1; + ch = following; + } else if (this.composeNode(flowIndent, CONTEXT_FLOW_OUT, false, true)) { + if (this.line === line) { + ch = this.peek(); + while (isWhiteSpace(ch)) { + ch = this.next(); + } + if (ch === COLON) { + ch = this.next(); + if (!isWhiteSpaceOrEOL(ch)) { + return this.throwError("Cannot read block: a whitespace character is expected after the key-value separator within a block mapping"); + } + if (atExplicitKey) { + this.storeMappingPair(result, overridableKeys, keyTag, keyNode, null); + keyTag = keyNode = valueNode = null; + } + detected = true; + atExplicitKey = false; + allowCompact = false; + keyTag = this.tag; + keyNode = this.result; + } else if (detected) { + return this.throwError("Cannot read an implicit mapping pair: missing colon"); + } else { + this.tag = tag; + this.anchor = anchor; + return true; + } + } else if (detected) { + return this.throwError("Cannot read a block mapping entry: a multiline key may not be an implicit key"); + } else { + this.tag = tag; + this.anchor = anchor; + return true; + } + } else { + break; + } + if (this.line === line || this.lineIndent > nodeIndent) { + if (this.composeNode(nodeIndent, CONTEXT_BLOCK_OUT, true, allowCompact)) { + if (atExplicitKey) { + keyNode = this.result; + } else { + valueNode = this.result; + } + } + if (!atExplicitKey) { + this.storeMappingPair(result, overridableKeys, keyTag, keyNode, valueNode, line, pos); + keyTag = keyNode = valueNode = null; + } + this.skipSeparationSpace(true, -1); + ch = this.peek(); + } + if (this.lineIndent > nodeIndent && ch !== 0) { + return this.throwError("Cannot read block: bad indentation of a mapping entry"); + } else if (this.lineIndent < nodeIndent) { + break; + } + } + if (atExplicitKey) { + this.storeMappingPair(result, overridableKeys, keyTag, keyNode, null); + } + if (detected) { + this.tag = tag; + this.anchor = anchor; + this.kind = "mapping"; + this.result = result; + } + return detected; + } + readTagProperty() { + let position; + let isVerbatim = false; + let isNamed = false; + let tagHandle = ""; + let tagName; + let ch; + ch = this.peek(); + if (ch !== EXCLAMATION) return false; + if (this.tag !== null) { + return this.throwError("Cannot read tag property: duplication of a tag property"); + } + ch = this.next(); + if (ch === SMALLER_THAN) { + isVerbatim = true; + ch = this.next(); + } else if (ch === EXCLAMATION) { + isNamed = true; + tagHandle = "!!"; + ch = this.next(); + } else { + tagHandle = "!"; + } + position = this.position; + if (isVerbatim) { + do { + ch = this.next(); + } while (ch !== 0 && ch !== GREATER_THAN); + if (this.position < this.length) { + tagName = this.input.slice(position, this.position); + ch = this.next(); + } else { + return this.throwError("Cannot read tag property: unexpected end of stream"); + } + } else { + while (ch !== 0 && !isWhiteSpaceOrEOL(ch)) { + if (ch === EXCLAMATION) { + if (!isNamed) { + tagHandle = this.input.slice(position - 1, this.position + 1); + if (!PATTERN_TAG_HANDLE.test(tagHandle)) { + return this.throwError("Cannot read tag property: named tag handle contains invalid characters"); + } + isNamed = true; + position = this.position + 1; + } else { + return this.throwError("Cannot read tag property: tag suffix cannot contain an exclamation mark"); + } + } + ch = this.next(); + } + tagName = this.input.slice(position, this.position); + if (PATTERN_FLOW_INDICATORS.test(tagName)) { + return this.throwError("Cannot read tag property: tag suffix cannot contain flow indicator characters"); + } + } + if (tagName && !PATTERN_TAG_URI.test(tagName)) { + return this.throwError(`Cannot read tag property: invalid characters in tag name "${tagName}"`); + } + if (isVerbatim) { + this.tag = tagName; + } else if (this.tagMap.has(tagHandle)) { + this.tag = this.tagMap.get(tagHandle) + tagName; + } else if (tagHandle === "!") { + this.tag = `!${tagName}`; + } else if (tagHandle === "!!") { + this.tag = `tag:yaml.org,2002:${tagName}`; + } else { + return this.throwError(`Cannot read tag property: undeclared tag handle "${tagHandle}"`); + } + return true; + } + readAnchorProperty() { + let ch = this.peek(); + if (ch !== AMPERSAND) return false; + if (this.anchor !== null) { + return this.throwError("Cannot read anchor property: duplicate anchor property"); + } + ch = this.next(); + const position = this.position; + while (ch !== 0 && !isWhiteSpaceOrEOL(ch) && !isFlowIndicator(ch)) { + ch = this.next(); + } + if (this.position === position) { + return this.throwError("Cannot read anchor property: name of an anchor node must contain at least one character"); + } + this.anchor = this.input.slice(position, this.position); + return true; + } + readAlias() { + if (this.peek() !== ASTERISK) return false; + let ch = this.next(); + const position = this.position; + while (ch !== 0 && !isWhiteSpaceOrEOL(ch) && !isFlowIndicator(ch)) { + ch = this.next(); + } + if (this.position === position) { + return this.throwError("Cannot read alias: alias name must contain at least one character"); + } + const alias = this.input.slice(position, this.position); + if (!this.anchorMap.has(alias)) { + return this.throwError(`Cannot read alias: unidentified alias "${alias}"`); + } + this.result = this.anchorMap.get(alias); + this.skipSeparationSpace(true, -1); + return true; + } + composeNode(parentIndent, nodeContext, allowToSeek, allowCompact) { + let allowBlockScalars; + let allowBlockCollections; + let indentStatus = 1; + let atNewLine = false; + let hasContent = false; + let type; + let flowIndent; + let blockIndent; + this.tag = null; + this.anchor = null; + this.kind = null; + this.result = null; + const allowBlockStyles = allowBlockScalars = allowBlockCollections = CONTEXT_BLOCK_OUT === nodeContext || CONTEXT_BLOCK_IN === nodeContext; + if (allowToSeek) { + if (this.skipSeparationSpace(true, -1)) { + atNewLine = true; + if (this.lineIndent > parentIndent) { + indentStatus = 1; + } else if (this.lineIndent === parentIndent) { + indentStatus = 0; + } else if (this.lineIndent < parentIndent) { + indentStatus = -1; + } + } + } + if (indentStatus === 1) { + while (this.readTagProperty() || this.readAnchorProperty()) { + if (this.skipSeparationSpace(true, -1)) { + atNewLine = true; + allowBlockCollections = allowBlockStyles; + if (this.lineIndent > parentIndent) { + indentStatus = 1; + } else if (this.lineIndent === parentIndent) { + indentStatus = 0; + } else if (this.lineIndent < parentIndent) { + indentStatus = -1; + } + } else { + allowBlockCollections = false; + } + } + } + if (allowBlockCollections) { + allowBlockCollections = atNewLine || allowCompact; + } + if (indentStatus === 1 || CONTEXT_BLOCK_OUT === nodeContext) { + const cond = CONTEXT_FLOW_IN === nodeContext || CONTEXT_FLOW_OUT === nodeContext; + flowIndent = cond ? parentIndent : parentIndent + 1; + blockIndent = this.position - this.lineStart; + if (indentStatus === 1) { + if (allowBlockCollections && (this.readBlockSequence(blockIndent) || this.readBlockMapping(blockIndent, flowIndent)) || this.readFlowCollection(flowIndent)) { + hasContent = true; + } else { + if (allowBlockScalars && this.readBlockScalar(flowIndent) || this.readSingleQuotedScalar(flowIndent) || this.readDoubleQuotedScalar(flowIndent)) { + hasContent = true; + } else if (this.readAlias()) { + hasContent = true; + if (this.tag !== null || this.anchor !== null) { + return this.throwError("Cannot compose node: alias node should not have any properties"); + } + } else if (this.readPlainScalar(flowIndent, CONTEXT_FLOW_IN === nodeContext)) { + hasContent = true; + if (this.tag === null) { + this.tag = "?"; + } + } + if (this.anchor !== null) { + this.anchorMap.set(this.anchor, this.result); + } + } + } else if (indentStatus === 0) { + hasContent = allowBlockCollections && this.readBlockSequence(blockIndent); + } + } + if (this.tag !== null && this.tag !== "!") { + if (this.tag === "?") { + for (let typeIndex = 0; typeIndex < this.implicitTypes.length; typeIndex++) { + type = this.implicitTypes[typeIndex]; + if (type.resolve(this.result)) { + this.result = type.construct(this.result); + this.tag = type.tag; + if (this.anchor !== null) { + this.anchorMap.set(this.anchor, this.result); + } + break; + } + } + } else if (this.typeMap[this.kind ?? "fallback"].has(this.tag)) { + const map2 = this.typeMap[this.kind ?? "fallback"]; + type = map2.get(this.tag); + if (this.result !== null && type.kind !== this.kind) { + return this.throwError(`Unacceptable node kind for !<${this.tag}> tag: it should be "${type.kind}", not "${this.kind}"`); + } + if (!type.resolve(this.result)) { + return this.throwError(`Cannot resolve a node with !<${this.tag}> explicit tag`); + } else { + this.result = type.construct(this.result); + if (this.anchor !== null) { + this.anchorMap.set(this.anchor, this.result); + } + } + } else { + return this.throwError(`Cannot resolve unknown tag !<${this.tag}>`); + } + } + return this.tag !== null || this.anchor !== null || hasContent; + } + readDocument() { + const documentStart = this.position; + let position; + let directiveName; + let directiveArgs; + let hasDirectives = false; + let ch; + this.version = null; + this.checkLineBreaks = false; + this.tagMap = /* @__PURE__ */ new Map(); + this.anchorMap = /* @__PURE__ */ new Map(); + while ((ch = this.peek()) !== 0) { + this.skipSeparationSpace(true, -1); + ch = this.peek(); + if (this.lineIndent > 0 || ch !== PERCENT) { + break; + } + hasDirectives = true; + ch = this.next(); + position = this.position; + while (ch !== 0 && !isWhiteSpaceOrEOL(ch)) { + ch = this.next(); + } + directiveName = this.input.slice(position, this.position); + directiveArgs = []; + if (directiveName.length < 1) { + return this.throwError("Cannot read document: directive name length must be greater than zero"); + } + while (ch !== 0) { + while (isWhiteSpace(ch)) { + ch = this.next(); + } + if (ch === SHARP) { + do { + ch = this.next(); + } while (ch !== 0 && !isEOL(ch)); + break; + } + if (isEOL(ch)) break; + position = this.position; + while (ch !== 0 && !isWhiteSpaceOrEOL(ch)) { + ch = this.next(); + } + directiveArgs.push(this.input.slice(position, this.position)); + } + if (ch !== 0) this.readLineBreak(); + switch (directiveName) { + case "YAML": + this.yamlDirectiveHandler(...directiveArgs); + break; + case "TAG": + this.tagDirectiveHandler(...directiveArgs); + break; + default: + this.dispatchWarning(`unknown document directive "${directiveName}"`); + break; + } + } + this.skipSeparationSpace(true, -1); + if (this.lineIndent === 0 && this.peek() === MINUS && this.peek(1) === MINUS && this.peek(2) === MINUS) { + this.position += 3; + this.skipSeparationSpace(true, -1); + } else if (hasDirectives) { + return this.throwError("Cannot read document: directives end mark is expected"); + } + this.composeNode(this.lineIndent - 1, CONTEXT_BLOCK_OUT, false, true); + this.skipSeparationSpace(true, -1); + if (this.checkLineBreaks && PATTERN_NON_ASCII_LINE_BREAKS.test(this.input.slice(documentStart, this.position))) { + this.dispatchWarning("non-ASCII line breaks are interpreted as content"); + } + if (this.position === this.lineStart && this.testDocumentSeparator()) { + if (this.peek() === DOT) { + this.position += 3; + this.skipSeparationSpace(true, -1); + } + } else if (this.position < this.length - 1) { + return this.throwError("Cannot read document: end of the stream or a document separator is expected"); + } + return this.result; + } + *readDocuments() { + while (this.position < this.length - 1) { + yield this.readDocument(); + } + } +}; + +// node_modules/@std/yaml/parse.js +function sanitizeInput(input) { + input = String(input); + if (input.length > 0) { + if (!isEOL(input.charCodeAt(input.length - 1))) input += "\n"; + if (input.charCodeAt(0) === 65279) input = input.slice(1); + } + input += "\0"; + return input; +} +function parse(content, options = {}) { + content = sanitizeInput(content); + const state = new LoaderState(content, { + ...options, + schema: SCHEMA_MAP.get(options.schema) + }); + const documentGenerator = state.readDocuments(); + const document = documentGenerator.next().value; + if (!documentGenerator.next().done) { + throw new SyntaxError("Found more than 1 document in the stream: expected a single document"); + } + return document ?? null; +} +function parseAll(content, options = {}) { + content = sanitizeInput(content); + const state = new LoaderState(content, { + ...options, + schema: SCHEMA_MAP.get(options.schema) + }); + return [ + ...state.readDocuments() + ]; +} + +// node_modules/@std/yaml/_dumper_state.js +var STYLE_PLAIN = 1; +var STYLE_SINGLE = 2; +var STYLE_LITERAL = 3; +var STYLE_FOLDED = 4; +var STYLE_DOUBLE = 5; +var LEADING_SPACE_REGEXP = /^\n* /; +var ESCAPE_SEQUENCES = /* @__PURE__ */ new Map([ + [ + 0, + "\\0" + ], + [ + 7, + "\\a" + ], + [ + 8, + "\\b" + ], + [ + 9, + "\\t" + ], + [ + 10, + "\\n" + ], + [ + 11, + "\\v" + ], + [ + 12, + "\\f" + ], + [ + 13, + "\\r" + ], + [ + 27, + "\\e" + ], + [ + 34, + '\\"' + ], + [ + 92, + "\\\\" + ], + [ + 133, + "\\N" + ], + [ + 160, + "\\_" + ], + [ + 8232, + "\\L" + ], + [ + 8233, + "\\P" + ] +]); +var DEPRECATED_BOOLEANS_SYNTAX = [ + "y", + "Y", + "yes", + "Yes", + "YES", + "on", + "On", + "ON", + "n", + "N", + "no", + "No", + "NO", + "off", + "Off", + "OFF" +]; +function charCodeToHexString(charCode) { + const hexString = charCode.toString(16).toUpperCase(); + if (charCode <= 255) return `\\x${hexString.padStart(2, "0")}`; + if (charCode <= 65535) return `\\u${hexString.padStart(4, "0")}`; + if (charCode <= 4294967295) return `\\U${hexString.padStart(8, "0")}`; + throw new Error("Code point within a string may not be greater than 0xFFFFFFFF"); +} +function createStyleMap(map2) { + const result = /* @__PURE__ */ new Map(); + for (let tag of Object.keys(map2)) { + const style = String(map2[tag]); + if (tag.slice(0, 2) === "!!") { + tag = `tag:yaml.org,2002:${tag.slice(2)}`; + } + result.set(tag, style); + } + return result; +} +function indentString(string, spaces) { + const indent = " ".repeat(spaces); + return string.split("\n").map((line) => line.length ? indent + line : line).join("\n"); +} +function generateNextLine(indent, level) { + return ` +${" ".repeat(indent * level)}`; +} +function isPrintable(c) { + return 32 <= c && c <= 126 || 161 <= c && c <= 55295 && c !== 8232 && c !== 8233 || 57344 <= c && c <= 65533 && c !== BOM || 65536 <= c && c <= 1114111; +} +function isPlainSafe(c) { + return isPrintable(c) && c !== BOM && c !== COMMA && c !== LEFT_SQUARE_BRACKET && c !== RIGHT_SQUARE_BRACKET && c !== LEFT_CURLY_BRACKET && c !== RIGHT_CURLY_BRACKET && c !== COLON && c !== SHARP; +} +function isPlainSafeFirst(c) { + return isPlainSafe(c) && !isWhiteSpace(c) && c !== MINUS && c !== QUESTION && c !== AMPERSAND && c !== ASTERISK && c !== EXCLAMATION && c !== VERTICAL_LINE && c !== GREATER_THAN && c !== SINGLE_QUOTE && c !== DOUBLE_QUOTE && c !== PERCENT && c !== COMMERCIAL_AT && c !== GRAVE_ACCENT; +} +function needIndentIndicator(string) { + return LEADING_SPACE_REGEXP.test(string); +} +function chooseScalarStyle(string, singleLineOnly, indentPerLevel, lineWidth, implicitTypes) { + const shouldTrackWidth = lineWidth !== -1; + let hasLineBreak = false; + let hasFoldableLine = false; + let previousLineBreak = -1; + let plain = isPlainSafeFirst(string.charCodeAt(0)) && !isWhiteSpace(string.charCodeAt(string.length - 1)); + let char; + let i; + if (singleLineOnly) { + for (i = 0; i < string.length; i++) { + char = string.charCodeAt(i); + if (!isPrintable(char)) { + return STYLE_DOUBLE; + } + plain = plain && isPlainSafe(char); + } + } else { + for (i = 0; i < string.length; i++) { + char = string.charCodeAt(i); + if (char === LINE_FEED) { + hasLineBreak = true; + if (shouldTrackWidth) { + hasFoldableLine = hasFoldableLine || // Foldable line = too long, and not more-indented. + i - previousLineBreak - 1 > lineWidth && string[previousLineBreak + 1] !== " "; + previousLineBreak = i; + } + } else if (!isPrintable(char)) { + return STYLE_DOUBLE; + } + plain = plain && isPlainSafe(char); + } + hasFoldableLine = hasFoldableLine || shouldTrackWidth && i - previousLineBreak - 1 > lineWidth && string[previousLineBreak + 1] !== " "; + } + if (!hasLineBreak && !hasFoldableLine) { + return plain && !implicitTypes.some((type) => type.resolve(string)) ? STYLE_PLAIN : STYLE_SINGLE; + } + if (indentPerLevel > 9 && needIndentIndicator(string)) { + return STYLE_DOUBLE; + } + return hasFoldableLine ? STYLE_FOLDED : STYLE_LITERAL; +} +function foldLine(line, width) { + if (line === "" || line[0] === " ") return line; + const breakRegExp = / [^ ]/g; + let start = 0; + let end; + let curr = 0; + let next = 0; + const lines = []; + for (const match of line.matchAll(breakRegExp)) { + next = match.index; + if (next - start > width) { + end = curr > start ? curr : next; + lines.push(line.slice(start, end)); + start = end + 1; + } + curr = next; + } + if (line.length - start > width && curr > start) { + lines.push(line.slice(start, curr)); + lines.push(line.slice(curr + 1)); + } else { + lines.push(line.slice(start)); + } + return lines.join("\n"); +} +function trimTrailingNewline(string) { + return string.at(-1) === "\n" ? string.slice(0, -1) : string; +} +function foldString(string, width) { + const lineRe = /(\n+)([^\n]*)/g; + let result = (() => { + let nextLF = string.indexOf("\n"); + nextLF = nextLF !== -1 ? nextLF : string.length; + lineRe.lastIndex = nextLF; + return foldLine(string.slice(0, nextLF), width); + })(); + let prevMoreIndented = string[0] === "\n" || string[0] === " "; + let moreIndented; + let match; + while (match = lineRe.exec(string)) { + const prefix = match[1]; + const line = match[2] || ""; + moreIndented = line[0] === " "; + result += prefix + (!prevMoreIndented && !moreIndented && line !== "" ? "\n" : "") + foldLine(line, width); + prevMoreIndented = moreIndented; + } + return result; +} +function escapeString(string) { + let result = ""; + let char; + let nextChar; + let escapeSeq; + for (let i = 0; i < string.length; i++) { + char = string.charCodeAt(i); + if (char >= 55296 && char <= 56319) { + nextChar = string.charCodeAt(i + 1); + if (nextChar >= 56320 && nextChar <= 57343) { + result += charCodeToHexString((char - 55296) * 1024 + nextChar - 56320 + 65536); + i++; + continue; + } + } + escapeSeq = ESCAPE_SEQUENCES.get(char); + result += !escapeSeq && isPrintable(char) ? string[i] : escapeSeq || charCodeToHexString(char); + } + return result; +} +function blockHeader(string, indentPerLevel) { + const indentIndicator = needIndentIndicator(string) ? String(indentPerLevel) : ""; + const clip = string[string.length - 1] === "\n"; + const keep = clip && (string[string.length - 2] === "\n" || string === "\n"); + const chomp = keep ? "+" : clip ? "" : "-"; + return `${indentIndicator}${chomp} +`; +} +function inspectNode(object, objects, duplicateObjects) { + if (!isObject(object)) return; + if (objects.has(object)) { + duplicateObjects.add(object); + return; + } + objects.add(object); + const entries = Array.isArray(object) ? object : Object.values(object); + for (const value of entries) { + inspectNode(value, objects, duplicateObjects); + } +} +var DumperState = class { + indent; + arrayIndent; + skipInvalid; + flowLevel; + sortKeys; + lineWidth; + useAnchors; + compatMode; + condenseFlow; + implicitTypes; + explicitTypes; + duplicates = []; + usedDuplicates = /* @__PURE__ */ new Set(); + styleMap = /* @__PURE__ */ new Map(); + constructor({ schema = DEFAULT_SCHEMA, indent = 2, arrayIndent = true, skipInvalid = false, flowLevel = -1, styles = void 0, sortKeys = false, lineWidth = 80, useAnchors = true, compatMode = true, condenseFlow = false }) { + this.indent = Math.max(1, indent); + this.arrayIndent = arrayIndent; + this.skipInvalid = skipInvalid; + this.flowLevel = flowLevel; + if (styles) this.styleMap = createStyleMap(styles); + this.sortKeys = sortKeys; + this.lineWidth = lineWidth; + this.useAnchors = useAnchors; + this.compatMode = compatMode; + this.condenseFlow = condenseFlow; + this.implicitTypes = schema.implicitTypes; + this.explicitTypes = schema.explicitTypes; + } + // Note: line breaking/folding is implemented for only the folded style. + // NB. We drop the last trailing newline (if any) of a returned block scalar + // since the dumper adds its own newline. This always works: + // • No ending newline => unaffected; already using strip "-" chomping. + // • Ending newline => removed then restored. + // Importantly, this keeps the "+" chomp indicator from gaining an extra line. + stringifyScalar(string, { level, isKey }) { + if (string.length === 0) { + return "''"; + } + if (this.compatMode && DEPRECATED_BOOLEANS_SYNTAX.includes(string)) { + return `'${string}'`; + } + const indent = this.indent * Math.max(1, level); + const lineWidth = this.lineWidth === -1 ? -1 : Math.max(Math.min(this.lineWidth, 40), this.lineWidth - indent); + const singleLineOnly = isKey || // No block styles in flow mode. + this.flowLevel > -1 && level >= this.flowLevel; + const scalarStyle = chooseScalarStyle(string, singleLineOnly, this.indent, lineWidth, this.implicitTypes); + switch (scalarStyle) { + case STYLE_PLAIN: + return string; + case STYLE_SINGLE: + return `'${string.replace(/'/g, "''")}'`; + case STYLE_LITERAL: + return `|${blockHeader(string, this.indent)}${trimTrailingNewline(indentString(string, indent))}`; + case STYLE_FOLDED: + return `>${blockHeader(string, this.indent)}${trimTrailingNewline(indentString(foldString(string, lineWidth), indent))}`; + case STYLE_DOUBLE: + return `"${escapeString(string)}"`; + default: + throw new TypeError("Invalid scalar style should be unreachable: please file a bug report against Deno at https://github.com/denoland/std/issues"); + } + } + stringifyFlowSequence(array, { level }) { + const results = []; + for (const value of array) { + const string = this.stringifyNode(value, { + level, + block: false, + compact: false, + isKey: false + }); + if (string === null) continue; + results.push(string); + } + const separator = this.condenseFlow ? "," : ", "; + return `[${results.join(separator)}]`; + } + stringifyBlockSequence(array, { level, compact }) { + const whitespace = generateNextLine(this.indent, level); + const prefix = compact ? "" : whitespace; + const results = []; + for (const value of array) { + const string = this.stringifyNode(value, { + level: level + 1, + block: true, + compact: true, + isKey: false + }); + if (string === null) continue; + const linePrefix = LINE_FEED === string.charCodeAt(0) ? "-" : "- "; + results.push(`${linePrefix}${string}`); + } + return results.length ? prefix + results.join(whitespace) : "[]"; + } + stringifyFlowMapping(object, { level }) { + const quote = this.condenseFlow ? '"' : ""; + const separator = this.condenseFlow ? ":" : ": "; + const results = []; + for (const [key, value] of Object.entries(object)) { + const keyString = this.stringifyNode(key, { + level, + block: false, + compact: false, + isKey: false + }); + if (keyString === null) continue; + const valueString = this.stringifyNode(value, { + level, + block: false, + compact: false, + isKey: false + }); + if (valueString === null) continue; + const keyPrefix = keyString.length > 1024 ? "? " : ""; + results.push(quote + keyPrefix + keyString + quote + separator + valueString); + } + return `{${results.join(", ")}}`; + } + stringifyBlockMapping(object, { tag, level, compact }) { + const keys = Object.keys(object); + if (this.sortKeys === true) { + keys.sort(); + } else if (typeof this.sortKeys === "function") { + keys.sort(this.sortKeys); + } else if (this.sortKeys) { + throw new TypeError(`"sortKeys" must be a boolean or a function: received ${typeof this.sortKeys}`); + } + const separator = generateNextLine(this.indent, level); + const results = []; + for (const key of keys) { + const value = object[key]; + const keyString = this.stringifyNode(key, { + level: level + 1, + block: true, + compact: true, + isKey: true + }); + if (keyString === null) continue; + const explicitPair = tag !== null && tag !== "?" || keyString.length > 1024; + const valueString = this.stringifyNode(value, { + level: level + 1, + block: true, + compact: explicitPair, + isKey: false + }); + if (valueString === null) continue; + let pairBuffer = ""; + if (explicitPair) { + pairBuffer += keyString.charCodeAt(0) === LINE_FEED ? "?" : "? "; + } + pairBuffer += keyString; + if (explicitPair) pairBuffer += separator; + pairBuffer += valueString.charCodeAt(0) === LINE_FEED ? ":" : ": "; + pairBuffer += valueString; + results.push(pairBuffer); + } + const prefix = compact ? "" : separator; + return results.length ? prefix + results.join(separator) : "{}"; + } + getTypeRepresentation(type, value) { + if (!type.represent) return value; + const style = this.styleMap.get(type.tag) ?? type.defaultStyle; + if (typeof type.represent === "function") { + return type.represent(value, style); + } + const represent = type.represent[style]; + if (!represent) { + throw new TypeError(`!<${type.tag}> tag resolver accepts not "${style}" style`); + } + return represent(value, style); + } + detectType(value) { + for (const type of this.implicitTypes) { + if (type.predicate?.(value)) { + value = this.getTypeRepresentation(type, value); + return { + tag: "?", + value + }; + } + } + for (const type of this.explicitTypes) { + if (type.predicate?.(value)) { + value = this.getTypeRepresentation(type, value); + return { + tag: type.tag, + value + }; + } + } + return { + tag: null, + value + }; + } + // Serializes `object` and writes it to global `result`. + // Returns true on success, or false on invalid object. + stringifyNode(value, { level, block, compact, isKey }) { + const result = this.detectType(value); + const tag = result.tag; + value = result.value; + if (block) { + block = this.flowLevel < 0 || this.flowLevel > level; + } + if (typeof value === "string" || value instanceof String) { + value = value instanceof String ? value.valueOf() : value; + if (tag !== "?") { + value = this.stringifyScalar(value, { + level, + isKey + }); + } + } else if (isObject(value)) { + const duplicateIndex = this.duplicates.indexOf(value); + const duplicate = duplicateIndex !== -1; + if (duplicate) { + if (this.usedDuplicates.has(value)) return `*ref_${duplicateIndex}`; + this.usedDuplicates.add(value); + } + if (tag !== null && tag !== "?" || duplicate || this.indent !== 2 && level > 0) { + compact = false; + } + if (Array.isArray(value)) { + const arrayLevel = !this.arrayIndent && level > 0 ? level - 1 : level; + if (block && value.length !== 0) { + value = this.stringifyBlockSequence(value, { + level: arrayLevel, + compact + }); + if (duplicate) { + value = `&ref_${duplicateIndex}${value}`; + } + } else { + value = this.stringifyFlowSequence(value, { + level: arrayLevel + }); + if (duplicate) { + value = `&ref_${duplicateIndex} ${value}`; + } + } + } else { + if (block && Object.keys(value).length !== 0) { + value = this.stringifyBlockMapping(value, { + tag, + level, + compact + }); + if (duplicate) { + value = `&ref_${duplicateIndex}${value}`; + } + } else { + value = this.stringifyFlowMapping(value, { + level + }); + if (duplicate) { + value = `&ref_${duplicateIndex} ${value}`; + } + } + } + } else { + if (this.skipInvalid) return null; + throw new TypeError(`Cannot stringify ${typeof value}`); + } + if (tag !== null && tag !== "?") { + value = `!<${tag}> ${value}`; + } + return value; + } + stringify(value) { + if (this.useAnchors) { + const values = /* @__PURE__ */ new Set(); + const duplicateObjects = /* @__PURE__ */ new Set(); + inspectNode(value, values, duplicateObjects); + this.duplicates = [ + ...duplicateObjects + ]; + this.usedDuplicates = /* @__PURE__ */ new Set(); + } + const string = this.stringifyNode(value, { + level: 0, + block: true, + compact: true, + isKey: false + }); + if (string !== null) { + return `${string} +`; + } + return ""; + } +}; + +// node_modules/@std/yaml/stringify.js +function stringify(data, options = {}) { + const state = new DumperState({ + ...options, + schema: SCHEMA_MAP.get(options.schema) + }); + return state.stringify(data); +} +export { + parse, + parseAll, + stringify +}; diff --git a/jamfile/js_builtins/zod.js b/jamfile/js_builtins/zod.js new file mode 100644 index 0000000000..3754745414 --- /dev/null +++ b/jamfile/js_builtins/zod.js @@ -0,0 +1,5204 @@ +/** + * Original source: https://github.com/colinhacks/zod + * + * Generated from zod@3.23.8 and zod-to-json-schema@3.23.5 using esbuild --bundle. + * + * Re-exports everything from zod, and includes zodToJsonSchema. + */ + +// node_modules/zod/lib/index.mjs +var util; +(function(util2) { + util2.assertEqual = (val) => val; + function assertIs(_arg) { + } + util2.assertIs = assertIs; + function assertNever(_x) { + throw new Error(); + } + util2.assertNever = assertNever; + util2.arrayToEnum = (items) => { + const obj = {}; + for (const item of items) { + obj[item] = item; + } + return obj; + }; + util2.getValidEnumValues = (obj) => { + const validKeys = util2.objectKeys(obj).filter((k) => typeof obj[obj[k]] !== "number"); + const filtered = {}; + for (const k of validKeys) { + filtered[k] = obj[k]; + } + return util2.objectValues(filtered); + }; + util2.objectValues = (obj) => { + return util2.objectKeys(obj).map(function(e) { + return obj[e]; + }); + }; + util2.objectKeys = typeof Object.keys === "function" ? (obj) => Object.keys(obj) : (object) => { + const keys = []; + for (const key in object) { + if (Object.prototype.hasOwnProperty.call(object, key)) { + keys.push(key); + } + } + return keys; + }; + util2.find = (arr, checker) => { + for (const item of arr) { + if (checker(item)) + return item; + } + return void 0; + }; + util2.isInteger = typeof Number.isInteger === "function" ? (val) => Number.isInteger(val) : (val) => typeof val === "number" && isFinite(val) && Math.floor(val) === val; + function joinValues(array, separator = " | ") { + return array.map((val) => typeof val === "string" ? `'${val}'` : val).join(separator); + } + util2.joinValues = joinValues; + util2.jsonStringifyReplacer = (_, value) => { + if (typeof value === "bigint") { + return value.toString(); + } + return value; + }; +})(util || (util = {})); +var objectUtil; +(function(objectUtil2) { + objectUtil2.mergeShapes = (first, second) => { + return { + ...first, + ...second + // second overwrites first + }; + }; +})(objectUtil || (objectUtil = {})); +var ZodParsedType = util.arrayToEnum([ + "string", + "nan", + "number", + "integer", + "float", + "boolean", + "date", + "bigint", + "symbol", + "function", + "undefined", + "null", + "array", + "object", + "unknown", + "promise", + "void", + "never", + "map", + "set" +]); +var getParsedType = (data) => { + const t = typeof data; + switch (t) { + case "undefined": + return ZodParsedType.undefined; + case "string": + return ZodParsedType.string; + case "number": + return isNaN(data) ? ZodParsedType.nan : ZodParsedType.number; + case "boolean": + return ZodParsedType.boolean; + case "function": + return ZodParsedType.function; + case "bigint": + return ZodParsedType.bigint; + case "symbol": + return ZodParsedType.symbol; + case "object": + if (Array.isArray(data)) { + return ZodParsedType.array; + } + if (data === null) { + return ZodParsedType.null; + } + if (data.then && typeof data.then === "function" && data.catch && typeof data.catch === "function") { + return ZodParsedType.promise; + } + if (typeof Map !== "undefined" && data instanceof Map) { + return ZodParsedType.map; + } + if (typeof Set !== "undefined" && data instanceof Set) { + return ZodParsedType.set; + } + if (typeof Date !== "undefined" && data instanceof Date) { + return ZodParsedType.date; + } + return ZodParsedType.object; + default: + return ZodParsedType.unknown; + } +}; +var ZodIssueCode = util.arrayToEnum([ + "invalid_type", + "invalid_literal", + "custom", + "invalid_union", + "invalid_union_discriminator", + "invalid_enum_value", + "unrecognized_keys", + "invalid_arguments", + "invalid_return_type", + "invalid_date", + "invalid_string", + "too_small", + "too_big", + "invalid_intersection_types", + "not_multiple_of", + "not_finite" +]); +var quotelessJson = (obj) => { + const json = JSON.stringify(obj, null, 2); + return json.replace(/"([^"]+)":/g, "$1:"); +}; +var ZodError = class _ZodError extends Error { + constructor(issues) { + super(); + this.issues = []; + this.addIssue = (sub) => { + this.issues = [...this.issues, sub]; + }; + this.addIssues = (subs = []) => { + this.issues = [...this.issues, ...subs]; + }; + const actualProto = new.target.prototype; + if (Object.setPrototypeOf) { + Object.setPrototypeOf(this, actualProto); + } else { + this.__proto__ = actualProto; + } + this.name = "ZodError"; + this.issues = issues; + } + get errors() { + return this.issues; + } + format(_mapper) { + const mapper = _mapper || function(issue) { + return issue.message; + }; + const fieldErrors = { _errors: [] }; + const processError = (error) => { + for (const issue of error.issues) { + if (issue.code === "invalid_union") { + issue.unionErrors.map(processError); + } else if (issue.code === "invalid_return_type") { + processError(issue.returnTypeError); + } else if (issue.code === "invalid_arguments") { + processError(issue.argumentsError); + } else if (issue.path.length === 0) { + fieldErrors._errors.push(mapper(issue)); + } else { + let curr = fieldErrors; + let i = 0; + while (i < issue.path.length) { + const el = issue.path[i]; + const terminal = i === issue.path.length - 1; + if (!terminal) { + curr[el] = curr[el] || { _errors: [] }; + } else { + curr[el] = curr[el] || { _errors: [] }; + curr[el]._errors.push(mapper(issue)); + } + curr = curr[el]; + i++; + } + } + } + }; + processError(this); + return fieldErrors; + } + static assert(value) { + if (!(value instanceof _ZodError)) { + throw new Error(`Not a ZodError: ${value}`); + } + } + toString() { + return this.message; + } + get message() { + return JSON.stringify(this.issues, util.jsonStringifyReplacer, 2); + } + get isEmpty() { + return this.issues.length === 0; + } + flatten(mapper = (issue) => issue.message) { + const fieldErrors = {}; + const formErrors = []; + for (const sub of this.issues) { + if (sub.path.length > 0) { + fieldErrors[sub.path[0]] = fieldErrors[sub.path[0]] || []; + fieldErrors[sub.path[0]].push(mapper(sub)); + } else { + formErrors.push(mapper(sub)); + } + } + return { formErrors, fieldErrors }; + } + get formErrors() { + return this.flatten(); + } +}; +ZodError.create = (issues) => { + const error = new ZodError(issues); + return error; +}; +var errorMap = (issue, _ctx) => { + let message; + switch (issue.code) { + case ZodIssueCode.invalid_type: + if (issue.received === ZodParsedType.undefined) { + message = "Required"; + } else { + message = `Expected ${issue.expected}, received ${issue.received}`; + } + break; + case ZodIssueCode.invalid_literal: + message = `Invalid literal value, expected ${JSON.stringify(issue.expected, util.jsonStringifyReplacer)}`; + break; + case ZodIssueCode.unrecognized_keys: + message = `Unrecognized key(s) in object: ${util.joinValues(issue.keys, ", ")}`; + break; + case ZodIssueCode.invalid_union: + message = `Invalid input`; + break; + case ZodIssueCode.invalid_union_discriminator: + message = `Invalid discriminator value. Expected ${util.joinValues(issue.options)}`; + break; + case ZodIssueCode.invalid_enum_value: + message = `Invalid enum value. Expected ${util.joinValues(issue.options)}, received '${issue.received}'`; + break; + case ZodIssueCode.invalid_arguments: + message = `Invalid function arguments`; + break; + case ZodIssueCode.invalid_return_type: + message = `Invalid function return type`; + break; + case ZodIssueCode.invalid_date: + message = `Invalid date`; + break; + case ZodIssueCode.invalid_string: + if (typeof issue.validation === "object") { + if ("includes" in issue.validation) { + message = `Invalid input: must include "${issue.validation.includes}"`; + if (typeof issue.validation.position === "number") { + message = `${message} at one or more positions greater than or equal to ${issue.validation.position}`; + } + } else if ("startsWith" in issue.validation) { + message = `Invalid input: must start with "${issue.validation.startsWith}"`; + } else if ("endsWith" in issue.validation) { + message = `Invalid input: must end with "${issue.validation.endsWith}"`; + } else { + util.assertNever(issue.validation); + } + } else if (issue.validation !== "regex") { + message = `Invalid ${issue.validation}`; + } else { + message = "Invalid"; + } + break; + case ZodIssueCode.too_small: + if (issue.type === "array") + message = `Array must contain ${issue.exact ? "exactly" : issue.inclusive ? `at least` : `more than`} ${issue.minimum} element(s)`; + else if (issue.type === "string") + message = `String must contain ${issue.exact ? "exactly" : issue.inclusive ? `at least` : `over`} ${issue.minimum} character(s)`; + else if (issue.type === "number") + message = `Number must be ${issue.exact ? `exactly equal to ` : issue.inclusive ? `greater than or equal to ` : `greater than `}${issue.minimum}`; + else if (issue.type === "date") + message = `Date must be ${issue.exact ? `exactly equal to ` : issue.inclusive ? `greater than or equal to ` : `greater than `}${new Date(Number(issue.minimum))}`; + else + message = "Invalid input"; + break; + case ZodIssueCode.too_big: + if (issue.type === "array") + message = `Array must contain ${issue.exact ? `exactly` : issue.inclusive ? `at most` : `less than`} ${issue.maximum} element(s)`; + else if (issue.type === "string") + message = `String must contain ${issue.exact ? `exactly` : issue.inclusive ? `at most` : `under`} ${issue.maximum} character(s)`; + else if (issue.type === "number") + message = `Number must be ${issue.exact ? `exactly` : issue.inclusive ? `less than or equal to` : `less than`} ${issue.maximum}`; + else if (issue.type === "bigint") + message = `BigInt must be ${issue.exact ? `exactly` : issue.inclusive ? `less than or equal to` : `less than`} ${issue.maximum}`; + else if (issue.type === "date") + message = `Date must be ${issue.exact ? `exactly` : issue.inclusive ? `smaller than or equal to` : `smaller than`} ${new Date(Number(issue.maximum))}`; + else + message = "Invalid input"; + break; + case ZodIssueCode.custom: + message = `Invalid input`; + break; + case ZodIssueCode.invalid_intersection_types: + message = `Intersection results could not be merged`; + break; + case ZodIssueCode.not_multiple_of: + message = `Number must be a multiple of ${issue.multipleOf}`; + break; + case ZodIssueCode.not_finite: + message = "Number must be finite"; + break; + default: + message = _ctx.defaultError; + util.assertNever(issue); + } + return { message }; +}; +var overrideErrorMap = errorMap; +function setErrorMap(map) { + overrideErrorMap = map; +} +function getErrorMap() { + return overrideErrorMap; +} +var makeIssue = (params) => { + const { data, path, errorMaps, issueData } = params; + const fullPath = [...path, ...issueData.path || []]; + const fullIssue = { + ...issueData, + path: fullPath + }; + if (issueData.message !== void 0) { + return { + ...issueData, + path: fullPath, + message: issueData.message + }; + } + let errorMessage = ""; + const maps = errorMaps.filter((m) => !!m).slice().reverse(); + for (const map of maps) { + errorMessage = map(fullIssue, { data, defaultError: errorMessage }).message; + } + return { + ...issueData, + path: fullPath, + message: errorMessage + }; +}; +var EMPTY_PATH = []; +function addIssueToContext(ctx, issueData) { + const overrideMap = getErrorMap(); + const issue = makeIssue({ + issueData, + data: ctx.data, + path: ctx.path, + errorMaps: [ + ctx.common.contextualErrorMap, + ctx.schemaErrorMap, + overrideMap, + overrideMap === errorMap ? void 0 : errorMap + // then global default map + ].filter((x) => !!x) + }); + ctx.common.issues.push(issue); +} +var ParseStatus = class _ParseStatus { + constructor() { + this.value = "valid"; + } + dirty() { + if (this.value === "valid") + this.value = "dirty"; + } + abort() { + if (this.value !== "aborted") + this.value = "aborted"; + } + static mergeArray(status, results) { + const arrayValue = []; + for (const s of results) { + if (s.status === "aborted") + return INVALID; + if (s.status === "dirty") + status.dirty(); + arrayValue.push(s.value); + } + return { status: status.value, value: arrayValue }; + } + static async mergeObjectAsync(status, pairs) { + const syncPairs = []; + for (const pair of pairs) { + const key = await pair.key; + const value = await pair.value; + syncPairs.push({ + key, + value + }); + } + return _ParseStatus.mergeObjectSync(status, syncPairs); + } + static mergeObjectSync(status, pairs) { + const finalObject = {}; + for (const pair of pairs) { + const { key, value } = pair; + if (key.status === "aborted") + return INVALID; + if (value.status === "aborted") + return INVALID; + if (key.status === "dirty") + status.dirty(); + if (value.status === "dirty") + status.dirty(); + if (key.value !== "__proto__" && (typeof value.value !== "undefined" || pair.alwaysSet)) { + finalObject[key.value] = value.value; + } + } + return { status: status.value, value: finalObject }; + } +}; +var INVALID = Object.freeze({ + status: "aborted" +}); +var DIRTY = (value) => ({ status: "dirty", value }); +var OK = (value) => ({ status: "valid", value }); +var isAborted = (x) => x.status === "aborted"; +var isDirty = (x) => x.status === "dirty"; +var isValid = (x) => x.status === "valid"; +var isAsync = (x) => typeof Promise !== "undefined" && x instanceof Promise; +function __classPrivateFieldGet(receiver, state, kind, f) { + if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter"); + if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it"); + return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver); +} +function __classPrivateFieldSet(receiver, state, value, kind, f) { + if (kind === "m") throw new TypeError("Private method is not writable"); + if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter"); + if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it"); + return kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value), value; +} +var errorUtil; +(function(errorUtil2) { + errorUtil2.errToObj = (message) => typeof message === "string" ? { message } : message || {}; + errorUtil2.toString = (message) => typeof message === "string" ? message : message === null || message === void 0 ? void 0 : message.message; +})(errorUtil || (errorUtil = {})); +var _ZodEnum_cache; +var _ZodNativeEnum_cache; +var ParseInputLazyPath = class { + constructor(parent, value, path, key) { + this._cachedPath = []; + this.parent = parent; + this.data = value; + this._path = path; + this._key = key; + } + get path() { + if (!this._cachedPath.length) { + if (this._key instanceof Array) { + this._cachedPath.push(...this._path, ...this._key); + } else { + this._cachedPath.push(...this._path, this._key); + } + } + return this._cachedPath; + } +}; +var handleResult = (ctx, result) => { + if (isValid(result)) { + return { success: true, data: result.value }; + } else { + if (!ctx.common.issues.length) { + throw new Error("Validation failed but no issues detected."); + } + return { + success: false, + get error() { + if (this._error) + return this._error; + const error = new ZodError(ctx.common.issues); + this._error = error; + return this._error; + } + }; + } +}; +function processCreateParams(params) { + if (!params) + return {}; + const { errorMap: errorMap2, invalid_type_error, required_error, description } = params; + if (errorMap2 && (invalid_type_error || required_error)) { + throw new Error(`Can't use "invalid_type_error" or "required_error" in conjunction with custom error map.`); + } + if (errorMap2) + return { errorMap: errorMap2, description }; + const customMap = (iss, ctx) => { + var _a, _b; + const { message } = params; + if (iss.code === "invalid_enum_value") { + return { message: message !== null && message !== void 0 ? message : ctx.defaultError }; + } + if (typeof ctx.data === "undefined") { + return { message: (_a = message !== null && message !== void 0 ? message : required_error) !== null && _a !== void 0 ? _a : ctx.defaultError }; + } + if (iss.code !== "invalid_type") + return { message: ctx.defaultError }; + return { message: (_b = message !== null && message !== void 0 ? message : invalid_type_error) !== null && _b !== void 0 ? _b : ctx.defaultError }; + }; + return { errorMap: customMap, description }; +} +var ZodType = class { + constructor(def) { + this.spa = this.safeParseAsync; + this._def = def; + this.parse = this.parse.bind(this); + this.safeParse = this.safeParse.bind(this); + this.parseAsync = this.parseAsync.bind(this); + this.safeParseAsync = this.safeParseAsync.bind(this); + this.spa = this.spa.bind(this); + this.refine = this.refine.bind(this); + this.refinement = this.refinement.bind(this); + this.superRefine = this.superRefine.bind(this); + this.optional = this.optional.bind(this); + this.nullable = this.nullable.bind(this); + this.nullish = this.nullish.bind(this); + this.array = this.array.bind(this); + this.promise = this.promise.bind(this); + this.or = this.or.bind(this); + this.and = this.and.bind(this); + this.transform = this.transform.bind(this); + this.brand = this.brand.bind(this); + this.default = this.default.bind(this); + this.catch = this.catch.bind(this); + this.describe = this.describe.bind(this); + this.pipe = this.pipe.bind(this); + this.readonly = this.readonly.bind(this); + this.isNullable = this.isNullable.bind(this); + this.isOptional = this.isOptional.bind(this); + } + get description() { + return this._def.description; + } + _getType(input) { + return getParsedType(input.data); + } + _getOrReturnCtx(input, ctx) { + return ctx || { + common: input.parent.common, + data: input.data, + parsedType: getParsedType(input.data), + schemaErrorMap: this._def.errorMap, + path: input.path, + parent: input.parent + }; + } + _processInputParams(input) { + return { + status: new ParseStatus(), + ctx: { + common: input.parent.common, + data: input.data, + parsedType: getParsedType(input.data), + schemaErrorMap: this._def.errorMap, + path: input.path, + parent: input.parent + } + }; + } + _parseSync(input) { + const result = this._parse(input); + if (isAsync(result)) { + throw new Error("Synchronous parse encountered promise."); + } + return result; + } + _parseAsync(input) { + const result = this._parse(input); + return Promise.resolve(result); + } + parse(data, params) { + const result = this.safeParse(data, params); + if (result.success) + return result.data; + throw result.error; + } + safeParse(data, params) { + var _a; + const ctx = { + common: { + issues: [], + async: (_a = params === null || params === void 0 ? void 0 : params.async) !== null && _a !== void 0 ? _a : false, + contextualErrorMap: params === null || params === void 0 ? void 0 : params.errorMap + }, + path: (params === null || params === void 0 ? void 0 : params.path) || [], + schemaErrorMap: this._def.errorMap, + parent: null, + data, + parsedType: getParsedType(data) + }; + const result = this._parseSync({ data, path: ctx.path, parent: ctx }); + return handleResult(ctx, result); + } + async parseAsync(data, params) { + const result = await this.safeParseAsync(data, params); + if (result.success) + return result.data; + throw result.error; + } + async safeParseAsync(data, params) { + const ctx = { + common: { + issues: [], + contextualErrorMap: params === null || params === void 0 ? void 0 : params.errorMap, + async: true + }, + path: (params === null || params === void 0 ? void 0 : params.path) || [], + schemaErrorMap: this._def.errorMap, + parent: null, + data, + parsedType: getParsedType(data) + }; + const maybeAsyncResult = this._parse({ data, path: ctx.path, parent: ctx }); + const result = await (isAsync(maybeAsyncResult) ? maybeAsyncResult : Promise.resolve(maybeAsyncResult)); + return handleResult(ctx, result); + } + refine(check, message) { + const getIssueProperties = (val) => { + if (typeof message === "string" || typeof message === "undefined") { + return { message }; + } else if (typeof message === "function") { + return message(val); + } else { + return message; + } + }; + return this._refinement((val, ctx) => { + const result = check(val); + const setError = () => ctx.addIssue({ + code: ZodIssueCode.custom, + ...getIssueProperties(val) + }); + if (typeof Promise !== "undefined" && result instanceof Promise) { + return result.then((data) => { + if (!data) { + setError(); + return false; + } else { + return true; + } + }); + } + if (!result) { + setError(); + return false; + } else { + return true; + } + }); + } + refinement(check, refinementData) { + return this._refinement((val, ctx) => { + if (!check(val)) { + ctx.addIssue(typeof refinementData === "function" ? refinementData(val, ctx) : refinementData); + return false; + } else { + return true; + } + }); + } + _refinement(refinement) { + return new ZodEffects({ + schema: this, + typeName: ZodFirstPartyTypeKind.ZodEffects, + effect: { type: "refinement", refinement } + }); + } + superRefine(refinement) { + return this._refinement(refinement); + } + optional() { + return ZodOptional.create(this, this._def); + } + nullable() { + return ZodNullable.create(this, this._def); + } + nullish() { + return this.nullable().optional(); + } + array() { + return ZodArray.create(this, this._def); + } + promise() { + return ZodPromise.create(this, this._def); + } + or(option) { + return ZodUnion.create([this, option], this._def); + } + and(incoming) { + return ZodIntersection.create(this, incoming, this._def); + } + transform(transform) { + return new ZodEffects({ + ...processCreateParams(this._def), + schema: this, + typeName: ZodFirstPartyTypeKind.ZodEffects, + effect: { type: "transform", transform } + }); + } + default(def) { + const defaultValueFunc = typeof def === "function" ? def : () => def; + return new ZodDefault({ + ...processCreateParams(this._def), + innerType: this, + defaultValue: defaultValueFunc, + typeName: ZodFirstPartyTypeKind.ZodDefault + }); + } + brand() { + return new ZodBranded({ + typeName: ZodFirstPartyTypeKind.ZodBranded, + type: this, + ...processCreateParams(this._def) + }); + } + catch(def) { + const catchValueFunc = typeof def === "function" ? def : () => def; + return new ZodCatch({ + ...processCreateParams(this._def), + innerType: this, + catchValue: catchValueFunc, + typeName: ZodFirstPartyTypeKind.ZodCatch + }); + } + describe(description) { + const This = this.constructor; + return new This({ + ...this._def, + description + }); + } + pipe(target) { + return ZodPipeline.create(this, target); + } + readonly() { + return ZodReadonly.create(this); + } + isOptional() { + return this.safeParse(void 0).success; + } + isNullable() { + return this.safeParse(null).success; + } +}; +var cuidRegex = /^c[^\s-]{8,}$/i; +var cuid2Regex = /^[0-9a-z]+$/; +var ulidRegex = /^[0-9A-HJKMNP-TV-Z]{26}$/; +var uuidRegex = /^[0-9a-fA-F]{8}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{12}$/i; +var nanoidRegex = /^[a-z0-9_-]{21}$/i; +var durationRegex = /^[-+]?P(?!$)(?:(?:[-+]?\d+Y)|(?:[-+]?\d+[.,]\d+Y$))?(?:(?:[-+]?\d+M)|(?:[-+]?\d+[.,]\d+M$))?(?:(?:[-+]?\d+W)|(?:[-+]?\d+[.,]\d+W$))?(?:(?:[-+]?\d+D)|(?:[-+]?\d+[.,]\d+D$))?(?:T(?=[\d+-])(?:(?:[-+]?\d+H)|(?:[-+]?\d+[.,]\d+H$))?(?:(?:[-+]?\d+M)|(?:[-+]?\d+[.,]\d+M$))?(?:[-+]?\d+(?:[.,]\d+)?S)?)??$/; +var emailRegex = /^(?!\.)(?!.*\.\.)([A-Z0-9_'+\-\.]*)[A-Z0-9_+-]@([A-Z0-9][A-Z0-9\-]*\.)+[A-Z]{2,}$/i; +var _emojiRegex = `^(\\p{Extended_Pictographic}|\\p{Emoji_Component})+$`; +var emojiRegex; +var ipv4Regex = /^(?:(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])\.){3}(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])$/; +var ipv6Regex = /^(([a-f0-9]{1,4}:){7}|::([a-f0-9]{1,4}:){0,6}|([a-f0-9]{1,4}:){1}:([a-f0-9]{1,4}:){0,5}|([a-f0-9]{1,4}:){2}:([a-f0-9]{1,4}:){0,4}|([a-f0-9]{1,4}:){3}:([a-f0-9]{1,4}:){0,3}|([a-f0-9]{1,4}:){4}:([a-f0-9]{1,4}:){0,2}|([a-f0-9]{1,4}:){5}:([a-f0-9]{1,4}:){0,1})([a-f0-9]{1,4}|(((25[0-5])|(2[0-4][0-9])|(1[0-9]{2})|([0-9]{1,2}))\.){3}((25[0-5])|(2[0-4][0-9])|(1[0-9]{2})|([0-9]{1,2})))$/; +var base64Regex = /^([0-9a-zA-Z+/]{4})*(([0-9a-zA-Z+/]{2}==)|([0-9a-zA-Z+/]{3}=))?$/; +var dateRegexSource = `((\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-((0[13578]|1[02])-(0[1-9]|[12]\\d|3[01])|(0[469]|11)-(0[1-9]|[12]\\d|30)|(02)-(0[1-9]|1\\d|2[0-8])))`; +var dateRegex = new RegExp(`^${dateRegexSource}$`); +function timeRegexSource(args) { + let regex = `([01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d`; + if (args.precision) { + regex = `${regex}\\.\\d{${args.precision}}`; + } else if (args.precision == null) { + regex = `${regex}(\\.\\d+)?`; + } + return regex; +} +function timeRegex(args) { + return new RegExp(`^${timeRegexSource(args)}$`); +} +function datetimeRegex(args) { + let regex = `${dateRegexSource}T${timeRegexSource(args)}`; + const opts = []; + opts.push(args.local ? `Z?` : `Z`); + if (args.offset) + opts.push(`([+-]\\d{2}:?\\d{2})`); + regex = `${regex}(${opts.join("|")})`; + return new RegExp(`^${regex}$`); +} +function isValidIP(ip, version) { + if ((version === "v4" || !version) && ipv4Regex.test(ip)) { + return true; + } + if ((version === "v6" || !version) && ipv6Regex.test(ip)) { + return true; + } + return false; +} +var ZodString = class _ZodString extends ZodType { + _parse(input) { + if (this._def.coerce) { + input.data = String(input.data); + } + const parsedType = this._getType(input); + if (parsedType !== ZodParsedType.string) { + const ctx2 = this._getOrReturnCtx(input); + addIssueToContext(ctx2, { + code: ZodIssueCode.invalid_type, + expected: ZodParsedType.string, + received: ctx2.parsedType + }); + return INVALID; + } + const status = new ParseStatus(); + let ctx = void 0; + for (const check of this._def.checks) { + if (check.kind === "min") { + if (input.data.length < check.value) { + ctx = this._getOrReturnCtx(input, ctx); + addIssueToContext(ctx, { + code: ZodIssueCode.too_small, + minimum: check.value, + type: "string", + inclusive: true, + exact: false, + message: check.message + }); + status.dirty(); + } + } else if (check.kind === "max") { + if (input.data.length > check.value) { + ctx = this._getOrReturnCtx(input, ctx); + addIssueToContext(ctx, { + code: ZodIssueCode.too_big, + maximum: check.value, + type: "string", + inclusive: true, + exact: false, + message: check.message + }); + status.dirty(); + } + } else if (check.kind === "length") { + const tooBig = input.data.length > check.value; + const tooSmall = input.data.length < check.value; + if (tooBig || tooSmall) { + ctx = this._getOrReturnCtx(input, ctx); + if (tooBig) { + addIssueToContext(ctx, { + code: ZodIssueCode.too_big, + maximum: check.value, + type: "string", + inclusive: true, + exact: true, + message: check.message + }); + } else if (tooSmall) { + addIssueToContext(ctx, { + code: ZodIssueCode.too_small, + minimum: check.value, + type: "string", + inclusive: true, + exact: true, + message: check.message + }); + } + status.dirty(); + } + } else if (check.kind === "email") { + if (!emailRegex.test(input.data)) { + ctx = this._getOrReturnCtx(input, ctx); + addIssueToContext(ctx, { + validation: "email", + code: ZodIssueCode.invalid_string, + message: check.message + }); + status.dirty(); + } + } else if (check.kind === "emoji") { + if (!emojiRegex) { + emojiRegex = new RegExp(_emojiRegex, "u"); + } + if (!emojiRegex.test(input.data)) { + ctx = this._getOrReturnCtx(input, ctx); + addIssueToContext(ctx, { + validation: "emoji", + code: ZodIssueCode.invalid_string, + message: check.message + }); + status.dirty(); + } + } else if (check.kind === "uuid") { + if (!uuidRegex.test(input.data)) { + ctx = this._getOrReturnCtx(input, ctx); + addIssueToContext(ctx, { + validation: "uuid", + code: ZodIssueCode.invalid_string, + message: check.message + }); + status.dirty(); + } + } else if (check.kind === "nanoid") { + if (!nanoidRegex.test(input.data)) { + ctx = this._getOrReturnCtx(input, ctx); + addIssueToContext(ctx, { + validation: "nanoid", + code: ZodIssueCode.invalid_string, + message: check.message + }); + status.dirty(); + } + } else if (check.kind === "cuid") { + if (!cuidRegex.test(input.data)) { + ctx = this._getOrReturnCtx(input, ctx); + addIssueToContext(ctx, { + validation: "cuid", + code: ZodIssueCode.invalid_string, + message: check.message + }); + status.dirty(); + } + } else if (check.kind === "cuid2") { + if (!cuid2Regex.test(input.data)) { + ctx = this._getOrReturnCtx(input, ctx); + addIssueToContext(ctx, { + validation: "cuid2", + code: ZodIssueCode.invalid_string, + message: check.message + }); + status.dirty(); + } + } else if (check.kind === "ulid") { + if (!ulidRegex.test(input.data)) { + ctx = this._getOrReturnCtx(input, ctx); + addIssueToContext(ctx, { + validation: "ulid", + code: ZodIssueCode.invalid_string, + message: check.message + }); + status.dirty(); + } + } else if (check.kind === "url") { + try { + new URL(input.data); + } catch (_a) { + ctx = this._getOrReturnCtx(input, ctx); + addIssueToContext(ctx, { + validation: "url", + code: ZodIssueCode.invalid_string, + message: check.message + }); + status.dirty(); + } + } else if (check.kind === "regex") { + check.regex.lastIndex = 0; + const testResult = check.regex.test(input.data); + if (!testResult) { + ctx = this._getOrReturnCtx(input, ctx); + addIssueToContext(ctx, { + validation: "regex", + code: ZodIssueCode.invalid_string, + message: check.message + }); + status.dirty(); + } + } else if (check.kind === "trim") { + input.data = input.data.trim(); + } else if (check.kind === "includes") { + if (!input.data.includes(check.value, check.position)) { + ctx = this._getOrReturnCtx(input, ctx); + addIssueToContext(ctx, { + code: ZodIssueCode.invalid_string, + validation: { includes: check.value, position: check.position }, + message: check.message + }); + status.dirty(); + } + } else if (check.kind === "toLowerCase") { + input.data = input.data.toLowerCase(); + } else if (check.kind === "toUpperCase") { + input.data = input.data.toUpperCase(); + } else if (check.kind === "startsWith") { + if (!input.data.startsWith(check.value)) { + ctx = this._getOrReturnCtx(input, ctx); + addIssueToContext(ctx, { + code: ZodIssueCode.invalid_string, + validation: { startsWith: check.value }, + message: check.message + }); + status.dirty(); + } + } else if (check.kind === "endsWith") { + if (!input.data.endsWith(check.value)) { + ctx = this._getOrReturnCtx(input, ctx); + addIssueToContext(ctx, { + code: ZodIssueCode.invalid_string, + validation: { endsWith: check.value }, + message: check.message + }); + status.dirty(); + } + } else if (check.kind === "datetime") { + const regex = datetimeRegex(check); + if (!regex.test(input.data)) { + ctx = this._getOrReturnCtx(input, ctx); + addIssueToContext(ctx, { + code: ZodIssueCode.invalid_string, + validation: "datetime", + message: check.message + }); + status.dirty(); + } + } else if (check.kind === "date") { + const regex = dateRegex; + if (!regex.test(input.data)) { + ctx = this._getOrReturnCtx(input, ctx); + addIssueToContext(ctx, { + code: ZodIssueCode.invalid_string, + validation: "date", + message: check.message + }); + status.dirty(); + } + } else if (check.kind === "time") { + const regex = timeRegex(check); + if (!regex.test(input.data)) { + ctx = this._getOrReturnCtx(input, ctx); + addIssueToContext(ctx, { + code: ZodIssueCode.invalid_string, + validation: "time", + message: check.message + }); + status.dirty(); + } + } else if (check.kind === "duration") { + if (!durationRegex.test(input.data)) { + ctx = this._getOrReturnCtx(input, ctx); + addIssueToContext(ctx, { + validation: "duration", + code: ZodIssueCode.invalid_string, + message: check.message + }); + status.dirty(); + } + } else if (check.kind === "ip") { + if (!isValidIP(input.data, check.version)) { + ctx = this._getOrReturnCtx(input, ctx); + addIssueToContext(ctx, { + validation: "ip", + code: ZodIssueCode.invalid_string, + message: check.message + }); + status.dirty(); + } + } else if (check.kind === "base64") { + if (!base64Regex.test(input.data)) { + ctx = this._getOrReturnCtx(input, ctx); + addIssueToContext(ctx, { + validation: "base64", + code: ZodIssueCode.invalid_string, + message: check.message + }); + status.dirty(); + } + } else { + util.assertNever(check); + } + } + return { status: status.value, value: input.data }; + } + _regex(regex, validation, message) { + return this.refinement((data) => regex.test(data), { + validation, + code: ZodIssueCode.invalid_string, + ...errorUtil.errToObj(message) + }); + } + _addCheck(check) { + return new _ZodString({ + ...this._def, + checks: [...this._def.checks, check] + }); + } + email(message) { + return this._addCheck({ kind: "email", ...errorUtil.errToObj(message) }); + } + url(message) { + return this._addCheck({ kind: "url", ...errorUtil.errToObj(message) }); + } + emoji(message) { + return this._addCheck({ kind: "emoji", ...errorUtil.errToObj(message) }); + } + uuid(message) { + return this._addCheck({ kind: "uuid", ...errorUtil.errToObj(message) }); + } + nanoid(message) { + return this._addCheck({ kind: "nanoid", ...errorUtil.errToObj(message) }); + } + cuid(message) { + return this._addCheck({ kind: "cuid", ...errorUtil.errToObj(message) }); + } + cuid2(message) { + return this._addCheck({ kind: "cuid2", ...errorUtil.errToObj(message) }); + } + ulid(message) { + return this._addCheck({ kind: "ulid", ...errorUtil.errToObj(message) }); + } + base64(message) { + return this._addCheck({ kind: "base64", ...errorUtil.errToObj(message) }); + } + ip(options) { + return this._addCheck({ kind: "ip", ...errorUtil.errToObj(options) }); + } + datetime(options) { + var _a, _b; + if (typeof options === "string") { + return this._addCheck({ + kind: "datetime", + precision: null, + offset: false, + local: false, + message: options + }); + } + return this._addCheck({ + kind: "datetime", + precision: typeof (options === null || options === void 0 ? void 0 : options.precision) === "undefined" ? null : options === null || options === void 0 ? void 0 : options.precision, + offset: (_a = options === null || options === void 0 ? void 0 : options.offset) !== null && _a !== void 0 ? _a : false, + local: (_b = options === null || options === void 0 ? void 0 : options.local) !== null && _b !== void 0 ? _b : false, + ...errorUtil.errToObj(options === null || options === void 0 ? void 0 : options.message) + }); + } + date(message) { + return this._addCheck({ kind: "date", message }); + } + time(options) { + if (typeof options === "string") { + return this._addCheck({ + kind: "time", + precision: null, + message: options + }); + } + return this._addCheck({ + kind: "time", + precision: typeof (options === null || options === void 0 ? void 0 : options.precision) === "undefined" ? null : options === null || options === void 0 ? void 0 : options.precision, + ...errorUtil.errToObj(options === null || options === void 0 ? void 0 : options.message) + }); + } + duration(message) { + return this._addCheck({ kind: "duration", ...errorUtil.errToObj(message) }); + } + regex(regex, message) { + return this._addCheck({ + kind: "regex", + regex, + ...errorUtil.errToObj(message) + }); + } + includes(value, options) { + return this._addCheck({ + kind: "includes", + value, + position: options === null || options === void 0 ? void 0 : options.position, + ...errorUtil.errToObj(options === null || options === void 0 ? void 0 : options.message) + }); + } + startsWith(value, message) { + return this._addCheck({ + kind: "startsWith", + value, + ...errorUtil.errToObj(message) + }); + } + endsWith(value, message) { + return this._addCheck({ + kind: "endsWith", + value, + ...errorUtil.errToObj(message) + }); + } + min(minLength, message) { + return this._addCheck({ + kind: "min", + value: minLength, + ...errorUtil.errToObj(message) + }); + } + max(maxLength, message) { + return this._addCheck({ + kind: "max", + value: maxLength, + ...errorUtil.errToObj(message) + }); + } + length(len, message) { + return this._addCheck({ + kind: "length", + value: len, + ...errorUtil.errToObj(message) + }); + } + /** + * @deprecated Use z.string().min(1) instead. + * @see {@link ZodString.min} + */ + nonempty(message) { + return this.min(1, errorUtil.errToObj(message)); + } + trim() { + return new _ZodString({ + ...this._def, + checks: [...this._def.checks, { kind: "trim" }] + }); + } + toLowerCase() { + return new _ZodString({ + ...this._def, + checks: [...this._def.checks, { kind: "toLowerCase" }] + }); + } + toUpperCase() { + return new _ZodString({ + ...this._def, + checks: [...this._def.checks, { kind: "toUpperCase" }] + }); + } + get isDatetime() { + return !!this._def.checks.find((ch) => ch.kind === "datetime"); + } + get isDate() { + return !!this._def.checks.find((ch) => ch.kind === "date"); + } + get isTime() { + return !!this._def.checks.find((ch) => ch.kind === "time"); + } + get isDuration() { + return !!this._def.checks.find((ch) => ch.kind === "duration"); + } + get isEmail() { + return !!this._def.checks.find((ch) => ch.kind === "email"); + } + get isURL() { + return !!this._def.checks.find((ch) => ch.kind === "url"); + } + get isEmoji() { + return !!this._def.checks.find((ch) => ch.kind === "emoji"); + } + get isUUID() { + return !!this._def.checks.find((ch) => ch.kind === "uuid"); + } + get isNANOID() { + return !!this._def.checks.find((ch) => ch.kind === "nanoid"); + } + get isCUID() { + return !!this._def.checks.find((ch) => ch.kind === "cuid"); + } + get isCUID2() { + return !!this._def.checks.find((ch) => ch.kind === "cuid2"); + } + get isULID() { + return !!this._def.checks.find((ch) => ch.kind === "ulid"); + } + get isIP() { + return !!this._def.checks.find((ch) => ch.kind === "ip"); + } + get isBase64() { + return !!this._def.checks.find((ch) => ch.kind === "base64"); + } + get minLength() { + let min = null; + for (const ch of this._def.checks) { + if (ch.kind === "min") { + if (min === null || ch.value > min) + min = ch.value; + } + } + return min; + } + get maxLength() { + let max = null; + for (const ch of this._def.checks) { + if (ch.kind === "max") { + if (max === null || ch.value < max) + max = ch.value; + } + } + return max; + } +}; +ZodString.create = (params) => { + var _a; + return new ZodString({ + checks: [], + typeName: ZodFirstPartyTypeKind.ZodString, + coerce: (_a = params === null || params === void 0 ? void 0 : params.coerce) !== null && _a !== void 0 ? _a : false, + ...processCreateParams(params) + }); +}; +function floatSafeRemainder(val, step) { + const valDecCount = (val.toString().split(".")[1] || "").length; + const stepDecCount = (step.toString().split(".")[1] || "").length; + const decCount = valDecCount > stepDecCount ? valDecCount : stepDecCount; + const valInt = parseInt(val.toFixed(decCount).replace(".", "")); + const stepInt = parseInt(step.toFixed(decCount).replace(".", "")); + return valInt % stepInt / Math.pow(10, decCount); +} +var ZodNumber = class _ZodNumber extends ZodType { + constructor() { + super(...arguments); + this.min = this.gte; + this.max = this.lte; + this.step = this.multipleOf; + } + _parse(input) { + if (this._def.coerce) { + input.data = Number(input.data); + } + const parsedType = this._getType(input); + if (parsedType !== ZodParsedType.number) { + const ctx2 = this._getOrReturnCtx(input); + addIssueToContext(ctx2, { + code: ZodIssueCode.invalid_type, + expected: ZodParsedType.number, + received: ctx2.parsedType + }); + return INVALID; + } + let ctx = void 0; + const status = new ParseStatus(); + for (const check of this._def.checks) { + if (check.kind === "int") { + if (!util.isInteger(input.data)) { + ctx = this._getOrReturnCtx(input, ctx); + addIssueToContext(ctx, { + code: ZodIssueCode.invalid_type, + expected: "integer", + received: "float", + message: check.message + }); + status.dirty(); + } + } else if (check.kind === "min") { + const tooSmall = check.inclusive ? input.data < check.value : input.data <= check.value; + if (tooSmall) { + ctx = this._getOrReturnCtx(input, ctx); + addIssueToContext(ctx, { + code: ZodIssueCode.too_small, + minimum: check.value, + type: "number", + inclusive: check.inclusive, + exact: false, + message: check.message + }); + status.dirty(); + } + } else if (check.kind === "max") { + const tooBig = check.inclusive ? input.data > check.value : input.data >= check.value; + if (tooBig) { + ctx = this._getOrReturnCtx(input, ctx); + addIssueToContext(ctx, { + code: ZodIssueCode.too_big, + maximum: check.value, + type: "number", + inclusive: check.inclusive, + exact: false, + message: check.message + }); + status.dirty(); + } + } else if (check.kind === "multipleOf") { + if (floatSafeRemainder(input.data, check.value) !== 0) { + ctx = this._getOrReturnCtx(input, ctx); + addIssueToContext(ctx, { + code: ZodIssueCode.not_multiple_of, + multipleOf: check.value, + message: check.message + }); + status.dirty(); + } + } else if (check.kind === "finite") { + if (!Number.isFinite(input.data)) { + ctx = this._getOrReturnCtx(input, ctx); + addIssueToContext(ctx, { + code: ZodIssueCode.not_finite, + message: check.message + }); + status.dirty(); + } + } else { + util.assertNever(check); + } + } + return { status: status.value, value: input.data }; + } + gte(value, message) { + return this.setLimit("min", value, true, errorUtil.toString(message)); + } + gt(value, message) { + return this.setLimit("min", value, false, errorUtil.toString(message)); + } + lte(value, message) { + return this.setLimit("max", value, true, errorUtil.toString(message)); + } + lt(value, message) { + return this.setLimit("max", value, false, errorUtil.toString(message)); + } + setLimit(kind, value, inclusive, message) { + return new _ZodNumber({ + ...this._def, + checks: [ + ...this._def.checks, + { + kind, + value, + inclusive, + message: errorUtil.toString(message) + } + ] + }); + } + _addCheck(check) { + return new _ZodNumber({ + ...this._def, + checks: [...this._def.checks, check] + }); + } + int(message) { + return this._addCheck({ + kind: "int", + message: errorUtil.toString(message) + }); + } + positive(message) { + return this._addCheck({ + kind: "min", + value: 0, + inclusive: false, + message: errorUtil.toString(message) + }); + } + negative(message) { + return this._addCheck({ + kind: "max", + value: 0, + inclusive: false, + message: errorUtil.toString(message) + }); + } + nonpositive(message) { + return this._addCheck({ + kind: "max", + value: 0, + inclusive: true, + message: errorUtil.toString(message) + }); + } + nonnegative(message) { + return this._addCheck({ + kind: "min", + value: 0, + inclusive: true, + message: errorUtil.toString(message) + }); + } + multipleOf(value, message) { + return this._addCheck({ + kind: "multipleOf", + value, + message: errorUtil.toString(message) + }); + } + finite(message) { + return this._addCheck({ + kind: "finite", + message: errorUtil.toString(message) + }); + } + safe(message) { + return this._addCheck({ + kind: "min", + inclusive: true, + value: Number.MIN_SAFE_INTEGER, + message: errorUtil.toString(message) + })._addCheck({ + kind: "max", + inclusive: true, + value: Number.MAX_SAFE_INTEGER, + message: errorUtil.toString(message) + }); + } + get minValue() { + let min = null; + for (const ch of this._def.checks) { + if (ch.kind === "min") { + if (min === null || ch.value > min) + min = ch.value; + } + } + return min; + } + get maxValue() { + let max = null; + for (const ch of this._def.checks) { + if (ch.kind === "max") { + if (max === null || ch.value < max) + max = ch.value; + } + } + return max; + } + get isInt() { + return !!this._def.checks.find((ch) => ch.kind === "int" || ch.kind === "multipleOf" && util.isInteger(ch.value)); + } + get isFinite() { + let max = null, min = null; + for (const ch of this._def.checks) { + if (ch.kind === "finite" || ch.kind === "int" || ch.kind === "multipleOf") { + return true; + } else if (ch.kind === "min") { + if (min === null || ch.value > min) + min = ch.value; + } else if (ch.kind === "max") { + if (max === null || ch.value < max) + max = ch.value; + } + } + return Number.isFinite(min) && Number.isFinite(max); + } +}; +ZodNumber.create = (params) => { + return new ZodNumber({ + checks: [], + typeName: ZodFirstPartyTypeKind.ZodNumber, + coerce: (params === null || params === void 0 ? void 0 : params.coerce) || false, + ...processCreateParams(params) + }); +}; +var ZodBigInt = class _ZodBigInt extends ZodType { + constructor() { + super(...arguments); + this.min = this.gte; + this.max = this.lte; + } + _parse(input) { + if (this._def.coerce) { + input.data = BigInt(input.data); + } + const parsedType = this._getType(input); + if (parsedType !== ZodParsedType.bigint) { + const ctx2 = this._getOrReturnCtx(input); + addIssueToContext(ctx2, { + code: ZodIssueCode.invalid_type, + expected: ZodParsedType.bigint, + received: ctx2.parsedType + }); + return INVALID; + } + let ctx = void 0; + const status = new ParseStatus(); + for (const check of this._def.checks) { + if (check.kind === "min") { + const tooSmall = check.inclusive ? input.data < check.value : input.data <= check.value; + if (tooSmall) { + ctx = this._getOrReturnCtx(input, ctx); + addIssueToContext(ctx, { + code: ZodIssueCode.too_small, + type: "bigint", + minimum: check.value, + inclusive: check.inclusive, + message: check.message + }); + status.dirty(); + } + } else if (check.kind === "max") { + const tooBig = check.inclusive ? input.data > check.value : input.data >= check.value; + if (tooBig) { + ctx = this._getOrReturnCtx(input, ctx); + addIssueToContext(ctx, { + code: ZodIssueCode.too_big, + type: "bigint", + maximum: check.value, + inclusive: check.inclusive, + message: check.message + }); + status.dirty(); + } + } else if (check.kind === "multipleOf") { + if (input.data % check.value !== BigInt(0)) { + ctx = this._getOrReturnCtx(input, ctx); + addIssueToContext(ctx, { + code: ZodIssueCode.not_multiple_of, + multipleOf: check.value, + message: check.message + }); + status.dirty(); + } + } else { + util.assertNever(check); + } + } + return { status: status.value, value: input.data }; + } + gte(value, message) { + return this.setLimit("min", value, true, errorUtil.toString(message)); + } + gt(value, message) { + return this.setLimit("min", value, false, errorUtil.toString(message)); + } + lte(value, message) { + return this.setLimit("max", value, true, errorUtil.toString(message)); + } + lt(value, message) { + return this.setLimit("max", value, false, errorUtil.toString(message)); + } + setLimit(kind, value, inclusive, message) { + return new _ZodBigInt({ + ...this._def, + checks: [ + ...this._def.checks, + { + kind, + value, + inclusive, + message: errorUtil.toString(message) + } + ] + }); + } + _addCheck(check) { + return new _ZodBigInt({ + ...this._def, + checks: [...this._def.checks, check] + }); + } + positive(message) { + return this._addCheck({ + kind: "min", + value: BigInt(0), + inclusive: false, + message: errorUtil.toString(message) + }); + } + negative(message) { + return this._addCheck({ + kind: "max", + value: BigInt(0), + inclusive: false, + message: errorUtil.toString(message) + }); + } + nonpositive(message) { + return this._addCheck({ + kind: "max", + value: BigInt(0), + inclusive: true, + message: errorUtil.toString(message) + }); + } + nonnegative(message) { + return this._addCheck({ + kind: "min", + value: BigInt(0), + inclusive: true, + message: errorUtil.toString(message) + }); + } + multipleOf(value, message) { + return this._addCheck({ + kind: "multipleOf", + value, + message: errorUtil.toString(message) + }); + } + get minValue() { + let min = null; + for (const ch of this._def.checks) { + if (ch.kind === "min") { + if (min === null || ch.value > min) + min = ch.value; + } + } + return min; + } + get maxValue() { + let max = null; + for (const ch of this._def.checks) { + if (ch.kind === "max") { + if (max === null || ch.value < max) + max = ch.value; + } + } + return max; + } +}; +ZodBigInt.create = (params) => { + var _a; + return new ZodBigInt({ + checks: [], + typeName: ZodFirstPartyTypeKind.ZodBigInt, + coerce: (_a = params === null || params === void 0 ? void 0 : params.coerce) !== null && _a !== void 0 ? _a : false, + ...processCreateParams(params) + }); +}; +var ZodBoolean = class extends ZodType { + _parse(input) { + if (this._def.coerce) { + input.data = Boolean(input.data); + } + const parsedType = this._getType(input); + if (parsedType !== ZodParsedType.boolean) { + const ctx = this._getOrReturnCtx(input); + addIssueToContext(ctx, { + code: ZodIssueCode.invalid_type, + expected: ZodParsedType.boolean, + received: ctx.parsedType + }); + return INVALID; + } + return OK(input.data); + } +}; +ZodBoolean.create = (params) => { + return new ZodBoolean({ + typeName: ZodFirstPartyTypeKind.ZodBoolean, + coerce: (params === null || params === void 0 ? void 0 : params.coerce) || false, + ...processCreateParams(params) + }); +}; +var ZodDate = class _ZodDate extends ZodType { + _parse(input) { + if (this._def.coerce) { + input.data = new Date(input.data); + } + const parsedType = this._getType(input); + if (parsedType !== ZodParsedType.date) { + const ctx2 = this._getOrReturnCtx(input); + addIssueToContext(ctx2, { + code: ZodIssueCode.invalid_type, + expected: ZodParsedType.date, + received: ctx2.parsedType + }); + return INVALID; + } + if (isNaN(input.data.getTime())) { + const ctx2 = this._getOrReturnCtx(input); + addIssueToContext(ctx2, { + code: ZodIssueCode.invalid_date + }); + return INVALID; + } + const status = new ParseStatus(); + let ctx = void 0; + for (const check of this._def.checks) { + if (check.kind === "min") { + if (input.data.getTime() < check.value) { + ctx = this._getOrReturnCtx(input, ctx); + addIssueToContext(ctx, { + code: ZodIssueCode.too_small, + message: check.message, + inclusive: true, + exact: false, + minimum: check.value, + type: "date" + }); + status.dirty(); + } + } else if (check.kind === "max") { + if (input.data.getTime() > check.value) { + ctx = this._getOrReturnCtx(input, ctx); + addIssueToContext(ctx, { + code: ZodIssueCode.too_big, + message: check.message, + inclusive: true, + exact: false, + maximum: check.value, + type: "date" + }); + status.dirty(); + } + } else { + util.assertNever(check); + } + } + return { + status: status.value, + value: new Date(input.data.getTime()) + }; + } + _addCheck(check) { + return new _ZodDate({ + ...this._def, + checks: [...this._def.checks, check] + }); + } + min(minDate, message) { + return this._addCheck({ + kind: "min", + value: minDate.getTime(), + message: errorUtil.toString(message) + }); + } + max(maxDate, message) { + return this._addCheck({ + kind: "max", + value: maxDate.getTime(), + message: errorUtil.toString(message) + }); + } + get minDate() { + let min = null; + for (const ch of this._def.checks) { + if (ch.kind === "min") { + if (min === null || ch.value > min) + min = ch.value; + } + } + return min != null ? new Date(min) : null; + } + get maxDate() { + let max = null; + for (const ch of this._def.checks) { + if (ch.kind === "max") { + if (max === null || ch.value < max) + max = ch.value; + } + } + return max != null ? new Date(max) : null; + } +}; +ZodDate.create = (params) => { + return new ZodDate({ + checks: [], + coerce: (params === null || params === void 0 ? void 0 : params.coerce) || false, + typeName: ZodFirstPartyTypeKind.ZodDate, + ...processCreateParams(params) + }); +}; +var ZodSymbol = class extends ZodType { + _parse(input) { + const parsedType = this._getType(input); + if (parsedType !== ZodParsedType.symbol) { + const ctx = this._getOrReturnCtx(input); + addIssueToContext(ctx, { + code: ZodIssueCode.invalid_type, + expected: ZodParsedType.symbol, + received: ctx.parsedType + }); + return INVALID; + } + return OK(input.data); + } +}; +ZodSymbol.create = (params) => { + return new ZodSymbol({ + typeName: ZodFirstPartyTypeKind.ZodSymbol, + ...processCreateParams(params) + }); +}; +var ZodUndefined = class extends ZodType { + _parse(input) { + const parsedType = this._getType(input); + if (parsedType !== ZodParsedType.undefined) { + const ctx = this._getOrReturnCtx(input); + addIssueToContext(ctx, { + code: ZodIssueCode.invalid_type, + expected: ZodParsedType.undefined, + received: ctx.parsedType + }); + return INVALID; + } + return OK(input.data); + } +}; +ZodUndefined.create = (params) => { + return new ZodUndefined({ + typeName: ZodFirstPartyTypeKind.ZodUndefined, + ...processCreateParams(params) + }); +}; +var ZodNull = class extends ZodType { + _parse(input) { + const parsedType = this._getType(input); + if (parsedType !== ZodParsedType.null) { + const ctx = this._getOrReturnCtx(input); + addIssueToContext(ctx, { + code: ZodIssueCode.invalid_type, + expected: ZodParsedType.null, + received: ctx.parsedType + }); + return INVALID; + } + return OK(input.data); + } +}; +ZodNull.create = (params) => { + return new ZodNull({ + typeName: ZodFirstPartyTypeKind.ZodNull, + ...processCreateParams(params) + }); +}; +var ZodAny = class extends ZodType { + constructor() { + super(...arguments); + this._any = true; + } + _parse(input) { + return OK(input.data); + } +}; +ZodAny.create = (params) => { + return new ZodAny({ + typeName: ZodFirstPartyTypeKind.ZodAny, + ...processCreateParams(params) + }); +}; +var ZodUnknown = class extends ZodType { + constructor() { + super(...arguments); + this._unknown = true; + } + _parse(input) { + return OK(input.data); + } +}; +ZodUnknown.create = (params) => { + return new ZodUnknown({ + typeName: ZodFirstPartyTypeKind.ZodUnknown, + ...processCreateParams(params) + }); +}; +var ZodNever = class extends ZodType { + _parse(input) { + const ctx = this._getOrReturnCtx(input); + addIssueToContext(ctx, { + code: ZodIssueCode.invalid_type, + expected: ZodParsedType.never, + received: ctx.parsedType + }); + return INVALID; + } +}; +ZodNever.create = (params) => { + return new ZodNever({ + typeName: ZodFirstPartyTypeKind.ZodNever, + ...processCreateParams(params) + }); +}; +var ZodVoid = class extends ZodType { + _parse(input) { + const parsedType = this._getType(input); + if (parsedType !== ZodParsedType.undefined) { + const ctx = this._getOrReturnCtx(input); + addIssueToContext(ctx, { + code: ZodIssueCode.invalid_type, + expected: ZodParsedType.void, + received: ctx.parsedType + }); + return INVALID; + } + return OK(input.data); + } +}; +ZodVoid.create = (params) => { + return new ZodVoid({ + typeName: ZodFirstPartyTypeKind.ZodVoid, + ...processCreateParams(params) + }); +}; +var ZodArray = class _ZodArray extends ZodType { + _parse(input) { + const { ctx, status } = this._processInputParams(input); + const def = this._def; + if (ctx.parsedType !== ZodParsedType.array) { + addIssueToContext(ctx, { + code: ZodIssueCode.invalid_type, + expected: ZodParsedType.array, + received: ctx.parsedType + }); + return INVALID; + } + if (def.exactLength !== null) { + const tooBig = ctx.data.length > def.exactLength.value; + const tooSmall = ctx.data.length < def.exactLength.value; + if (tooBig || tooSmall) { + addIssueToContext(ctx, { + code: tooBig ? ZodIssueCode.too_big : ZodIssueCode.too_small, + minimum: tooSmall ? def.exactLength.value : void 0, + maximum: tooBig ? def.exactLength.value : void 0, + type: "array", + inclusive: true, + exact: true, + message: def.exactLength.message + }); + status.dirty(); + } + } + if (def.minLength !== null) { + if (ctx.data.length < def.minLength.value) { + addIssueToContext(ctx, { + code: ZodIssueCode.too_small, + minimum: def.minLength.value, + type: "array", + inclusive: true, + exact: false, + message: def.minLength.message + }); + status.dirty(); + } + } + if (def.maxLength !== null) { + if (ctx.data.length > def.maxLength.value) { + addIssueToContext(ctx, { + code: ZodIssueCode.too_big, + maximum: def.maxLength.value, + type: "array", + inclusive: true, + exact: false, + message: def.maxLength.message + }); + status.dirty(); + } + } + if (ctx.common.async) { + return Promise.all([...ctx.data].map((item, i) => { + return def.type._parseAsync(new ParseInputLazyPath(ctx, item, ctx.path, i)); + })).then((result2) => { + return ParseStatus.mergeArray(status, result2); + }); + } + const result = [...ctx.data].map((item, i) => { + return def.type._parseSync(new ParseInputLazyPath(ctx, item, ctx.path, i)); + }); + return ParseStatus.mergeArray(status, result); + } + get element() { + return this._def.type; + } + min(minLength, message) { + return new _ZodArray({ + ...this._def, + minLength: { value: minLength, message: errorUtil.toString(message) } + }); + } + max(maxLength, message) { + return new _ZodArray({ + ...this._def, + maxLength: { value: maxLength, message: errorUtil.toString(message) } + }); + } + length(len, message) { + return new _ZodArray({ + ...this._def, + exactLength: { value: len, message: errorUtil.toString(message) } + }); + } + nonempty(message) { + return this.min(1, message); + } +}; +ZodArray.create = (schema, params) => { + return new ZodArray({ + type: schema, + minLength: null, + maxLength: null, + exactLength: null, + typeName: ZodFirstPartyTypeKind.ZodArray, + ...processCreateParams(params) + }); +}; +function deepPartialify(schema) { + if (schema instanceof ZodObject) { + const newShape = {}; + for (const key in schema.shape) { + const fieldSchema = schema.shape[key]; + newShape[key] = ZodOptional.create(deepPartialify(fieldSchema)); + } + return new ZodObject({ + ...schema._def, + shape: () => newShape + }); + } else if (schema instanceof ZodArray) { + return new ZodArray({ + ...schema._def, + type: deepPartialify(schema.element) + }); + } else if (schema instanceof ZodOptional) { + return ZodOptional.create(deepPartialify(schema.unwrap())); + } else if (schema instanceof ZodNullable) { + return ZodNullable.create(deepPartialify(schema.unwrap())); + } else if (schema instanceof ZodTuple) { + return ZodTuple.create(schema.items.map((item) => deepPartialify(item))); + } else { + return schema; + } +} +var ZodObject = class _ZodObject extends ZodType { + constructor() { + super(...arguments); + this._cached = null; + this.nonstrict = this.passthrough; + this.augment = this.extend; + } + _getCached() { + if (this._cached !== null) + return this._cached; + const shape = this._def.shape(); + const keys = util.objectKeys(shape); + return this._cached = { shape, keys }; + } + _parse(input) { + const parsedType = this._getType(input); + if (parsedType !== ZodParsedType.object) { + const ctx2 = this._getOrReturnCtx(input); + addIssueToContext(ctx2, { + code: ZodIssueCode.invalid_type, + expected: ZodParsedType.object, + received: ctx2.parsedType + }); + return INVALID; + } + const { status, ctx } = this._processInputParams(input); + const { shape, keys: shapeKeys } = this._getCached(); + const extraKeys = []; + if (!(this._def.catchall instanceof ZodNever && this._def.unknownKeys === "strip")) { + for (const key in ctx.data) { + if (!shapeKeys.includes(key)) { + extraKeys.push(key); + } + } + } + const pairs = []; + for (const key of shapeKeys) { + const keyValidator = shape[key]; + const value = ctx.data[key]; + pairs.push({ + key: { status: "valid", value: key }, + value: keyValidator._parse(new ParseInputLazyPath(ctx, value, ctx.path, key)), + alwaysSet: key in ctx.data + }); + } + if (this._def.catchall instanceof ZodNever) { + const unknownKeys = this._def.unknownKeys; + if (unknownKeys === "passthrough") { + for (const key of extraKeys) { + pairs.push({ + key: { status: "valid", value: key }, + value: { status: "valid", value: ctx.data[key] } + }); + } + } else if (unknownKeys === "strict") { + if (extraKeys.length > 0) { + addIssueToContext(ctx, { + code: ZodIssueCode.unrecognized_keys, + keys: extraKeys + }); + status.dirty(); + } + } else if (unknownKeys === "strip") ; + else { + throw new Error(`Internal ZodObject error: invalid unknownKeys value.`); + } + } else { + const catchall = this._def.catchall; + for (const key of extraKeys) { + const value = ctx.data[key]; + pairs.push({ + key: { status: "valid", value: key }, + value: catchall._parse( + new ParseInputLazyPath(ctx, value, ctx.path, key) + //, ctx.child(key), value, getParsedType(value) + ), + alwaysSet: key in ctx.data + }); + } + } + if (ctx.common.async) { + return Promise.resolve().then(async () => { + const syncPairs = []; + for (const pair of pairs) { + const key = await pair.key; + const value = await pair.value; + syncPairs.push({ + key, + value, + alwaysSet: pair.alwaysSet + }); + } + return syncPairs; + }).then((syncPairs) => { + return ParseStatus.mergeObjectSync(status, syncPairs); + }); + } else { + return ParseStatus.mergeObjectSync(status, pairs); + } + } + get shape() { + return this._def.shape(); + } + strict(message) { + errorUtil.errToObj; + return new _ZodObject({ + ...this._def, + unknownKeys: "strict", + ...message !== void 0 ? { + errorMap: (issue, ctx) => { + var _a, _b, _c, _d; + const defaultError = (_c = (_b = (_a = this._def).errorMap) === null || _b === void 0 ? void 0 : _b.call(_a, issue, ctx).message) !== null && _c !== void 0 ? _c : ctx.defaultError; + if (issue.code === "unrecognized_keys") + return { + message: (_d = errorUtil.errToObj(message).message) !== null && _d !== void 0 ? _d : defaultError + }; + return { + message: defaultError + }; + } + } : {} + }); + } + strip() { + return new _ZodObject({ + ...this._def, + unknownKeys: "strip" + }); + } + passthrough() { + return new _ZodObject({ + ...this._def, + unknownKeys: "passthrough" + }); + } + // const AugmentFactory = + // <Def extends ZodObjectDef>(def: Def) => + // <Augmentation extends ZodRawShape>( + // augmentation: Augmentation + // ): ZodObject< + // extendShape<ReturnType<Def["shape"]>, Augmentation>, + // Def["unknownKeys"], + // Def["catchall"] + // > => { + // return new ZodObject({ + // ...def, + // shape: () => ({ + // ...def.shape(), + // ...augmentation, + // }), + // }) as any; + // }; + extend(augmentation) { + return new _ZodObject({ + ...this._def, + shape: () => ({ + ...this._def.shape(), + ...augmentation + }) + }); + } + /** + * Prior to zod@1.0.12 there was a bug in the + * inferred type of merged objects. Please + * upgrade if you are experiencing issues. + */ + merge(merging) { + const merged = new _ZodObject({ + unknownKeys: merging._def.unknownKeys, + catchall: merging._def.catchall, + shape: () => ({ + ...this._def.shape(), + ...merging._def.shape() + }), + typeName: ZodFirstPartyTypeKind.ZodObject + }); + return merged; + } + // merge< + // Incoming extends AnyZodObject, + // Augmentation extends Incoming["shape"], + // NewOutput extends { + // [k in keyof Augmentation | keyof Output]: k extends keyof Augmentation + // ? Augmentation[k]["_output"] + // : k extends keyof Output + // ? Output[k] + // : never; + // }, + // NewInput extends { + // [k in keyof Augmentation | keyof Input]: k extends keyof Augmentation + // ? Augmentation[k]["_input"] + // : k extends keyof Input + // ? Input[k] + // : never; + // } + // >( + // merging: Incoming + // ): ZodObject< + // extendShape<T, ReturnType<Incoming["_def"]["shape"]>>, + // Incoming["_def"]["unknownKeys"], + // Incoming["_def"]["catchall"], + // NewOutput, + // NewInput + // > { + // const merged: any = new ZodObject({ + // unknownKeys: merging._def.unknownKeys, + // catchall: merging._def.catchall, + // shape: () => + // objectUtil.mergeShapes(this._def.shape(), merging._def.shape()), + // typeName: ZodFirstPartyTypeKind.ZodObject, + // }) as any; + // return merged; + // } + setKey(key, schema) { + return this.augment({ [key]: schema }); + } + // merge<Incoming extends AnyZodObject>( + // merging: Incoming + // ): //ZodObject<T & Incoming["_shape"], UnknownKeys, Catchall> = (merging) => { + // ZodObject< + // extendShape<T, ReturnType<Incoming["_def"]["shape"]>>, + // Incoming["_def"]["unknownKeys"], + // Incoming["_def"]["catchall"] + // > { + // // const mergedShape = objectUtil.mergeShapes( + // // this._def.shape(), + // // merging._def.shape() + // // ); + // const merged: any = new ZodObject({ + // unknownKeys: merging._def.unknownKeys, + // catchall: merging._def.catchall, + // shape: () => + // objectUtil.mergeShapes(this._def.shape(), merging._def.shape()), + // typeName: ZodFirstPartyTypeKind.ZodObject, + // }) as any; + // return merged; + // } + catchall(index) { + return new _ZodObject({ + ...this._def, + catchall: index + }); + } + pick(mask) { + const shape = {}; + util.objectKeys(mask).forEach((key) => { + if (mask[key] && this.shape[key]) { + shape[key] = this.shape[key]; + } + }); + return new _ZodObject({ + ...this._def, + shape: () => shape + }); + } + omit(mask) { + const shape = {}; + util.objectKeys(this.shape).forEach((key) => { + if (!mask[key]) { + shape[key] = this.shape[key]; + } + }); + return new _ZodObject({ + ...this._def, + shape: () => shape + }); + } + /** + * @deprecated + */ + deepPartial() { + return deepPartialify(this); + } + partial(mask) { + const newShape = {}; + util.objectKeys(this.shape).forEach((key) => { + const fieldSchema = this.shape[key]; + if (mask && !mask[key]) { + newShape[key] = fieldSchema; + } else { + newShape[key] = fieldSchema.optional(); + } + }); + return new _ZodObject({ + ...this._def, + shape: () => newShape + }); + } + required(mask) { + const newShape = {}; + util.objectKeys(this.shape).forEach((key) => { + if (mask && !mask[key]) { + newShape[key] = this.shape[key]; + } else { + const fieldSchema = this.shape[key]; + let newField = fieldSchema; + while (newField instanceof ZodOptional) { + newField = newField._def.innerType; + } + newShape[key] = newField; + } + }); + return new _ZodObject({ + ...this._def, + shape: () => newShape + }); + } + keyof() { + return createZodEnum(util.objectKeys(this.shape)); + } +}; +ZodObject.create = (shape, params) => { + return new ZodObject({ + shape: () => shape, + unknownKeys: "strip", + catchall: ZodNever.create(), + typeName: ZodFirstPartyTypeKind.ZodObject, + ...processCreateParams(params) + }); +}; +ZodObject.strictCreate = (shape, params) => { + return new ZodObject({ + shape: () => shape, + unknownKeys: "strict", + catchall: ZodNever.create(), + typeName: ZodFirstPartyTypeKind.ZodObject, + ...processCreateParams(params) + }); +}; +ZodObject.lazycreate = (shape, params) => { + return new ZodObject({ + shape, + unknownKeys: "strip", + catchall: ZodNever.create(), + typeName: ZodFirstPartyTypeKind.ZodObject, + ...processCreateParams(params) + }); +}; +var ZodUnion = class extends ZodType { + _parse(input) { + const { ctx } = this._processInputParams(input); + const options = this._def.options; + function handleResults(results) { + for (const result of results) { + if (result.result.status === "valid") { + return result.result; + } + } + for (const result of results) { + if (result.result.status === "dirty") { + ctx.common.issues.push(...result.ctx.common.issues); + return result.result; + } + } + const unionErrors = results.map((result) => new ZodError(result.ctx.common.issues)); + addIssueToContext(ctx, { + code: ZodIssueCode.invalid_union, + unionErrors + }); + return INVALID; + } + if (ctx.common.async) { + return Promise.all(options.map(async (option) => { + const childCtx = { + ...ctx, + common: { + ...ctx.common, + issues: [] + }, + parent: null + }; + return { + result: await option._parseAsync({ + data: ctx.data, + path: ctx.path, + parent: childCtx + }), + ctx: childCtx + }; + })).then(handleResults); + } else { + let dirty = void 0; + const issues = []; + for (const option of options) { + const childCtx = { + ...ctx, + common: { + ...ctx.common, + issues: [] + }, + parent: null + }; + const result = option._parseSync({ + data: ctx.data, + path: ctx.path, + parent: childCtx + }); + if (result.status === "valid") { + return result; + } else if (result.status === "dirty" && !dirty) { + dirty = { result, ctx: childCtx }; + } + if (childCtx.common.issues.length) { + issues.push(childCtx.common.issues); + } + } + if (dirty) { + ctx.common.issues.push(...dirty.ctx.common.issues); + return dirty.result; + } + const unionErrors = issues.map((issues2) => new ZodError(issues2)); + addIssueToContext(ctx, { + code: ZodIssueCode.invalid_union, + unionErrors + }); + return INVALID; + } + } + get options() { + return this._def.options; + } +}; +ZodUnion.create = (types, params) => { + return new ZodUnion({ + options: types, + typeName: ZodFirstPartyTypeKind.ZodUnion, + ...processCreateParams(params) + }); +}; +var getDiscriminator = (type) => { + if (type instanceof ZodLazy) { + return getDiscriminator(type.schema); + } else if (type instanceof ZodEffects) { + return getDiscriminator(type.innerType()); + } else if (type instanceof ZodLiteral) { + return [type.value]; + } else if (type instanceof ZodEnum) { + return type.options; + } else if (type instanceof ZodNativeEnum) { + return util.objectValues(type.enum); + } else if (type instanceof ZodDefault) { + return getDiscriminator(type._def.innerType); + } else if (type instanceof ZodUndefined) { + return [void 0]; + } else if (type instanceof ZodNull) { + return [null]; + } else if (type instanceof ZodOptional) { + return [void 0, ...getDiscriminator(type.unwrap())]; + } else if (type instanceof ZodNullable) { + return [null, ...getDiscriminator(type.unwrap())]; + } else if (type instanceof ZodBranded) { + return getDiscriminator(type.unwrap()); + } else if (type instanceof ZodReadonly) { + return getDiscriminator(type.unwrap()); + } else if (type instanceof ZodCatch) { + return getDiscriminator(type._def.innerType); + } else { + return []; + } +}; +var ZodDiscriminatedUnion = class _ZodDiscriminatedUnion extends ZodType { + _parse(input) { + const { ctx } = this._processInputParams(input); + if (ctx.parsedType !== ZodParsedType.object) { + addIssueToContext(ctx, { + code: ZodIssueCode.invalid_type, + expected: ZodParsedType.object, + received: ctx.parsedType + }); + return INVALID; + } + const discriminator = this.discriminator; + const discriminatorValue = ctx.data[discriminator]; + const option = this.optionsMap.get(discriminatorValue); + if (!option) { + addIssueToContext(ctx, { + code: ZodIssueCode.invalid_union_discriminator, + options: Array.from(this.optionsMap.keys()), + path: [discriminator] + }); + return INVALID; + } + if (ctx.common.async) { + return option._parseAsync({ + data: ctx.data, + path: ctx.path, + parent: ctx + }); + } else { + return option._parseSync({ + data: ctx.data, + path: ctx.path, + parent: ctx + }); + } + } + get discriminator() { + return this._def.discriminator; + } + get options() { + return this._def.options; + } + get optionsMap() { + return this._def.optionsMap; + } + /** + * The constructor of the discriminated union schema. Its behaviour is very similar to that of the normal z.union() constructor. + * However, it only allows a union of objects, all of which need to share a discriminator property. This property must + * have a different value for each object in the union. + * @param discriminator the name of the discriminator property + * @param types an array of object schemas + * @param params + */ + static create(discriminator, options, params) { + const optionsMap = /* @__PURE__ */ new Map(); + for (const type of options) { + const discriminatorValues = getDiscriminator(type.shape[discriminator]); + if (!discriminatorValues.length) { + throw new Error(`A discriminator value for key \`${discriminator}\` could not be extracted from all schema options`); + } + for (const value of discriminatorValues) { + if (optionsMap.has(value)) { + throw new Error(`Discriminator property ${String(discriminator)} has duplicate value ${String(value)}`); + } + optionsMap.set(value, type); + } + } + return new _ZodDiscriminatedUnion({ + typeName: ZodFirstPartyTypeKind.ZodDiscriminatedUnion, + discriminator, + options, + optionsMap, + ...processCreateParams(params) + }); + } +}; +function mergeValues(a, b) { + const aType = getParsedType(a); + const bType = getParsedType(b); + if (a === b) { + return { valid: true, data: a }; + } else if (aType === ZodParsedType.object && bType === ZodParsedType.object) { + const bKeys = util.objectKeys(b); + const sharedKeys = util.objectKeys(a).filter((key) => bKeys.indexOf(key) !== -1); + const newObj = { ...a, ...b }; + for (const key of sharedKeys) { + const sharedValue = mergeValues(a[key], b[key]); + if (!sharedValue.valid) { + return { valid: false }; + } + newObj[key] = sharedValue.data; + } + return { valid: true, data: newObj }; + } else if (aType === ZodParsedType.array && bType === ZodParsedType.array) { + if (a.length !== b.length) { + return { valid: false }; + } + const newArray = []; + for (let index = 0; index < a.length; index++) { + const itemA = a[index]; + const itemB = b[index]; + const sharedValue = mergeValues(itemA, itemB); + if (!sharedValue.valid) { + return { valid: false }; + } + newArray.push(sharedValue.data); + } + return { valid: true, data: newArray }; + } else if (aType === ZodParsedType.date && bType === ZodParsedType.date && +a === +b) { + return { valid: true, data: a }; + } else { + return { valid: false }; + } +} +var ZodIntersection = class extends ZodType { + _parse(input) { + const { status, ctx } = this._processInputParams(input); + const handleParsed = (parsedLeft, parsedRight) => { + if (isAborted(parsedLeft) || isAborted(parsedRight)) { + return INVALID; + } + const merged = mergeValues(parsedLeft.value, parsedRight.value); + if (!merged.valid) { + addIssueToContext(ctx, { + code: ZodIssueCode.invalid_intersection_types + }); + return INVALID; + } + if (isDirty(parsedLeft) || isDirty(parsedRight)) { + status.dirty(); + } + return { status: status.value, value: merged.data }; + }; + if (ctx.common.async) { + return Promise.all([ + this._def.left._parseAsync({ + data: ctx.data, + path: ctx.path, + parent: ctx + }), + this._def.right._parseAsync({ + data: ctx.data, + path: ctx.path, + parent: ctx + }) + ]).then(([left, right]) => handleParsed(left, right)); + } else { + return handleParsed(this._def.left._parseSync({ + data: ctx.data, + path: ctx.path, + parent: ctx + }), this._def.right._parseSync({ + data: ctx.data, + path: ctx.path, + parent: ctx + })); + } + } +}; +ZodIntersection.create = (left, right, params) => { + return new ZodIntersection({ + left, + right, + typeName: ZodFirstPartyTypeKind.ZodIntersection, + ...processCreateParams(params) + }); +}; +var ZodTuple = class _ZodTuple extends ZodType { + _parse(input) { + const { status, ctx } = this._processInputParams(input); + if (ctx.parsedType !== ZodParsedType.array) { + addIssueToContext(ctx, { + code: ZodIssueCode.invalid_type, + expected: ZodParsedType.array, + received: ctx.parsedType + }); + return INVALID; + } + if (ctx.data.length < this._def.items.length) { + addIssueToContext(ctx, { + code: ZodIssueCode.too_small, + minimum: this._def.items.length, + inclusive: true, + exact: false, + type: "array" + }); + return INVALID; + } + const rest = this._def.rest; + if (!rest && ctx.data.length > this._def.items.length) { + addIssueToContext(ctx, { + code: ZodIssueCode.too_big, + maximum: this._def.items.length, + inclusive: true, + exact: false, + type: "array" + }); + status.dirty(); + } + const items = [...ctx.data].map((item, itemIndex) => { + const schema = this._def.items[itemIndex] || this._def.rest; + if (!schema) + return null; + return schema._parse(new ParseInputLazyPath(ctx, item, ctx.path, itemIndex)); + }).filter((x) => !!x); + if (ctx.common.async) { + return Promise.all(items).then((results) => { + return ParseStatus.mergeArray(status, results); + }); + } else { + return ParseStatus.mergeArray(status, items); + } + } + get items() { + return this._def.items; + } + rest(rest) { + return new _ZodTuple({ + ...this._def, + rest + }); + } +}; +ZodTuple.create = (schemas, params) => { + if (!Array.isArray(schemas)) { + throw new Error("You must pass an array of schemas to z.tuple([ ... ])"); + } + return new ZodTuple({ + items: schemas, + typeName: ZodFirstPartyTypeKind.ZodTuple, + rest: null, + ...processCreateParams(params) + }); +}; +var ZodRecord = class _ZodRecord extends ZodType { + get keySchema() { + return this._def.keyType; + } + get valueSchema() { + return this._def.valueType; + } + _parse(input) { + const { status, ctx } = this._processInputParams(input); + if (ctx.parsedType !== ZodParsedType.object) { + addIssueToContext(ctx, { + code: ZodIssueCode.invalid_type, + expected: ZodParsedType.object, + received: ctx.parsedType + }); + return INVALID; + } + const pairs = []; + const keyType = this._def.keyType; + const valueType = this._def.valueType; + for (const key in ctx.data) { + pairs.push({ + key: keyType._parse(new ParseInputLazyPath(ctx, key, ctx.path, key)), + value: valueType._parse(new ParseInputLazyPath(ctx, ctx.data[key], ctx.path, key)), + alwaysSet: key in ctx.data + }); + } + if (ctx.common.async) { + return ParseStatus.mergeObjectAsync(status, pairs); + } else { + return ParseStatus.mergeObjectSync(status, pairs); + } + } + get element() { + return this._def.valueType; + } + static create(first, second, third) { + if (second instanceof ZodType) { + return new _ZodRecord({ + keyType: first, + valueType: second, + typeName: ZodFirstPartyTypeKind.ZodRecord, + ...processCreateParams(third) + }); + } + return new _ZodRecord({ + keyType: ZodString.create(), + valueType: first, + typeName: ZodFirstPartyTypeKind.ZodRecord, + ...processCreateParams(second) + }); + } +}; +var ZodMap = class extends ZodType { + get keySchema() { + return this._def.keyType; + } + get valueSchema() { + return this._def.valueType; + } + _parse(input) { + const { status, ctx } = this._processInputParams(input); + if (ctx.parsedType !== ZodParsedType.map) { + addIssueToContext(ctx, { + code: ZodIssueCode.invalid_type, + expected: ZodParsedType.map, + received: ctx.parsedType + }); + return INVALID; + } + const keyType = this._def.keyType; + const valueType = this._def.valueType; + const pairs = [...ctx.data.entries()].map(([key, value], index) => { + return { + key: keyType._parse(new ParseInputLazyPath(ctx, key, ctx.path, [index, "key"])), + value: valueType._parse(new ParseInputLazyPath(ctx, value, ctx.path, [index, "value"])) + }; + }); + if (ctx.common.async) { + const finalMap = /* @__PURE__ */ new Map(); + return Promise.resolve().then(async () => { + for (const pair of pairs) { + const key = await pair.key; + const value = await pair.value; + if (key.status === "aborted" || value.status === "aborted") { + return INVALID; + } + if (key.status === "dirty" || value.status === "dirty") { + status.dirty(); + } + finalMap.set(key.value, value.value); + } + return { status: status.value, value: finalMap }; + }); + } else { + const finalMap = /* @__PURE__ */ new Map(); + for (const pair of pairs) { + const key = pair.key; + const value = pair.value; + if (key.status === "aborted" || value.status === "aborted") { + return INVALID; + } + if (key.status === "dirty" || value.status === "dirty") { + status.dirty(); + } + finalMap.set(key.value, value.value); + } + return { status: status.value, value: finalMap }; + } + } +}; +ZodMap.create = (keyType, valueType, params) => { + return new ZodMap({ + valueType, + keyType, + typeName: ZodFirstPartyTypeKind.ZodMap, + ...processCreateParams(params) + }); +}; +var ZodSet = class _ZodSet extends ZodType { + _parse(input) { + const { status, ctx } = this._processInputParams(input); + if (ctx.parsedType !== ZodParsedType.set) { + addIssueToContext(ctx, { + code: ZodIssueCode.invalid_type, + expected: ZodParsedType.set, + received: ctx.parsedType + }); + return INVALID; + } + const def = this._def; + if (def.minSize !== null) { + if (ctx.data.size < def.minSize.value) { + addIssueToContext(ctx, { + code: ZodIssueCode.too_small, + minimum: def.minSize.value, + type: "set", + inclusive: true, + exact: false, + message: def.minSize.message + }); + status.dirty(); + } + } + if (def.maxSize !== null) { + if (ctx.data.size > def.maxSize.value) { + addIssueToContext(ctx, { + code: ZodIssueCode.too_big, + maximum: def.maxSize.value, + type: "set", + inclusive: true, + exact: false, + message: def.maxSize.message + }); + status.dirty(); + } + } + const valueType = this._def.valueType; + function finalizeSet(elements2) { + const parsedSet = /* @__PURE__ */ new Set(); + for (const element of elements2) { + if (element.status === "aborted") + return INVALID; + if (element.status === "dirty") + status.dirty(); + parsedSet.add(element.value); + } + return { status: status.value, value: parsedSet }; + } + const elements = [...ctx.data.values()].map((item, i) => valueType._parse(new ParseInputLazyPath(ctx, item, ctx.path, i))); + if (ctx.common.async) { + return Promise.all(elements).then((elements2) => finalizeSet(elements2)); + } else { + return finalizeSet(elements); + } + } + min(minSize, message) { + return new _ZodSet({ + ...this._def, + minSize: { value: minSize, message: errorUtil.toString(message) } + }); + } + max(maxSize, message) { + return new _ZodSet({ + ...this._def, + maxSize: { value: maxSize, message: errorUtil.toString(message) } + }); + } + size(size, message) { + return this.min(size, message).max(size, message); + } + nonempty(message) { + return this.min(1, message); + } +}; +ZodSet.create = (valueType, params) => { + return new ZodSet({ + valueType, + minSize: null, + maxSize: null, + typeName: ZodFirstPartyTypeKind.ZodSet, + ...processCreateParams(params) + }); +}; +var ZodFunction = class _ZodFunction extends ZodType { + constructor() { + super(...arguments); + this.validate = this.implement; + } + _parse(input) { + const { ctx } = this._processInputParams(input); + if (ctx.parsedType !== ZodParsedType.function) { + addIssueToContext(ctx, { + code: ZodIssueCode.invalid_type, + expected: ZodParsedType.function, + received: ctx.parsedType + }); + return INVALID; + } + function makeArgsIssue(args, error) { + return makeIssue({ + data: args, + path: ctx.path, + errorMaps: [ + ctx.common.contextualErrorMap, + ctx.schemaErrorMap, + getErrorMap(), + errorMap + ].filter((x) => !!x), + issueData: { + code: ZodIssueCode.invalid_arguments, + argumentsError: error + } + }); + } + function makeReturnsIssue(returns, error) { + return makeIssue({ + data: returns, + path: ctx.path, + errorMaps: [ + ctx.common.contextualErrorMap, + ctx.schemaErrorMap, + getErrorMap(), + errorMap + ].filter((x) => !!x), + issueData: { + code: ZodIssueCode.invalid_return_type, + returnTypeError: error + } + }); + } + const params = { errorMap: ctx.common.contextualErrorMap }; + const fn = ctx.data; + if (this._def.returns instanceof ZodPromise) { + const me = this; + return OK(async function(...args) { + const error = new ZodError([]); + const parsedArgs = await me._def.args.parseAsync(args, params).catch((e) => { + error.addIssue(makeArgsIssue(args, e)); + throw error; + }); + const result = await Reflect.apply(fn, this, parsedArgs); + const parsedReturns = await me._def.returns._def.type.parseAsync(result, params).catch((e) => { + error.addIssue(makeReturnsIssue(result, e)); + throw error; + }); + return parsedReturns; + }); + } else { + const me = this; + return OK(function(...args) { + const parsedArgs = me._def.args.safeParse(args, params); + if (!parsedArgs.success) { + throw new ZodError([makeArgsIssue(args, parsedArgs.error)]); + } + const result = Reflect.apply(fn, this, parsedArgs.data); + const parsedReturns = me._def.returns.safeParse(result, params); + if (!parsedReturns.success) { + throw new ZodError([makeReturnsIssue(result, parsedReturns.error)]); + } + return parsedReturns.data; + }); + } + } + parameters() { + return this._def.args; + } + returnType() { + return this._def.returns; + } + args(...items) { + return new _ZodFunction({ + ...this._def, + args: ZodTuple.create(items).rest(ZodUnknown.create()) + }); + } + returns(returnType) { + return new _ZodFunction({ + ...this._def, + returns: returnType + }); + } + implement(func) { + const validatedFunc = this.parse(func); + return validatedFunc; + } + strictImplement(func) { + const validatedFunc = this.parse(func); + return validatedFunc; + } + static create(args, returns, params) { + return new _ZodFunction({ + args: args ? args : ZodTuple.create([]).rest(ZodUnknown.create()), + returns: returns || ZodUnknown.create(), + typeName: ZodFirstPartyTypeKind.ZodFunction, + ...processCreateParams(params) + }); + } +}; +var ZodLazy = class extends ZodType { + get schema() { + return this._def.getter(); + } + _parse(input) { + const { ctx } = this._processInputParams(input); + const lazySchema = this._def.getter(); + return lazySchema._parse({ data: ctx.data, path: ctx.path, parent: ctx }); + } +}; +ZodLazy.create = (getter, params) => { + return new ZodLazy({ + getter, + typeName: ZodFirstPartyTypeKind.ZodLazy, + ...processCreateParams(params) + }); +}; +var ZodLiteral = class extends ZodType { + _parse(input) { + if (input.data !== this._def.value) { + const ctx = this._getOrReturnCtx(input); + addIssueToContext(ctx, { + received: ctx.data, + code: ZodIssueCode.invalid_literal, + expected: this._def.value + }); + return INVALID; + } + return { status: "valid", value: input.data }; + } + get value() { + return this._def.value; + } +}; +ZodLiteral.create = (value, params) => { + return new ZodLiteral({ + value, + typeName: ZodFirstPartyTypeKind.ZodLiteral, + ...processCreateParams(params) + }); +}; +function createZodEnum(values, params) { + return new ZodEnum({ + values, + typeName: ZodFirstPartyTypeKind.ZodEnum, + ...processCreateParams(params) + }); +} +var ZodEnum = class _ZodEnum extends ZodType { + constructor() { + super(...arguments); + _ZodEnum_cache.set(this, void 0); + } + _parse(input) { + if (typeof input.data !== "string") { + const ctx = this._getOrReturnCtx(input); + const expectedValues = this._def.values; + addIssueToContext(ctx, { + expected: util.joinValues(expectedValues), + received: ctx.parsedType, + code: ZodIssueCode.invalid_type + }); + return INVALID; + } + if (!__classPrivateFieldGet(this, _ZodEnum_cache, "f")) { + __classPrivateFieldSet(this, _ZodEnum_cache, new Set(this._def.values), "f"); + } + if (!__classPrivateFieldGet(this, _ZodEnum_cache, "f").has(input.data)) { + const ctx = this._getOrReturnCtx(input); + const expectedValues = this._def.values; + addIssueToContext(ctx, { + received: ctx.data, + code: ZodIssueCode.invalid_enum_value, + options: expectedValues + }); + return INVALID; + } + return OK(input.data); + } + get options() { + return this._def.values; + } + get enum() { + const enumValues = {}; + for (const val of this._def.values) { + enumValues[val] = val; + } + return enumValues; + } + get Values() { + const enumValues = {}; + for (const val of this._def.values) { + enumValues[val] = val; + } + return enumValues; + } + get Enum() { + const enumValues = {}; + for (const val of this._def.values) { + enumValues[val] = val; + } + return enumValues; + } + extract(values, newDef = this._def) { + return _ZodEnum.create(values, { + ...this._def, + ...newDef + }); + } + exclude(values, newDef = this._def) { + return _ZodEnum.create(this.options.filter((opt) => !values.includes(opt)), { + ...this._def, + ...newDef + }); + } +}; +_ZodEnum_cache = /* @__PURE__ */ new WeakMap(); +ZodEnum.create = createZodEnum; +var ZodNativeEnum = class extends ZodType { + constructor() { + super(...arguments); + _ZodNativeEnum_cache.set(this, void 0); + } + _parse(input) { + const nativeEnumValues = util.getValidEnumValues(this._def.values); + const ctx = this._getOrReturnCtx(input); + if (ctx.parsedType !== ZodParsedType.string && ctx.parsedType !== ZodParsedType.number) { + const expectedValues = util.objectValues(nativeEnumValues); + addIssueToContext(ctx, { + expected: util.joinValues(expectedValues), + received: ctx.parsedType, + code: ZodIssueCode.invalid_type + }); + return INVALID; + } + if (!__classPrivateFieldGet(this, _ZodNativeEnum_cache, "f")) { + __classPrivateFieldSet(this, _ZodNativeEnum_cache, new Set(util.getValidEnumValues(this._def.values)), "f"); + } + if (!__classPrivateFieldGet(this, _ZodNativeEnum_cache, "f").has(input.data)) { + const expectedValues = util.objectValues(nativeEnumValues); + addIssueToContext(ctx, { + received: ctx.data, + code: ZodIssueCode.invalid_enum_value, + options: expectedValues + }); + return INVALID; + } + return OK(input.data); + } + get enum() { + return this._def.values; + } +}; +_ZodNativeEnum_cache = /* @__PURE__ */ new WeakMap(); +ZodNativeEnum.create = (values, params) => { + return new ZodNativeEnum({ + values, + typeName: ZodFirstPartyTypeKind.ZodNativeEnum, + ...processCreateParams(params) + }); +}; +var ZodPromise = class extends ZodType { + unwrap() { + return this._def.type; + } + _parse(input) { + const { ctx } = this._processInputParams(input); + if (ctx.parsedType !== ZodParsedType.promise && ctx.common.async === false) { + addIssueToContext(ctx, { + code: ZodIssueCode.invalid_type, + expected: ZodParsedType.promise, + received: ctx.parsedType + }); + return INVALID; + } + const promisified = ctx.parsedType === ZodParsedType.promise ? ctx.data : Promise.resolve(ctx.data); + return OK(promisified.then((data) => { + return this._def.type.parseAsync(data, { + path: ctx.path, + errorMap: ctx.common.contextualErrorMap + }); + })); + } +}; +ZodPromise.create = (schema, params) => { + return new ZodPromise({ + type: schema, + typeName: ZodFirstPartyTypeKind.ZodPromise, + ...processCreateParams(params) + }); +}; +var ZodEffects = class extends ZodType { + innerType() { + return this._def.schema; + } + sourceType() { + return this._def.schema._def.typeName === ZodFirstPartyTypeKind.ZodEffects ? this._def.schema.sourceType() : this._def.schema; + } + _parse(input) { + const { status, ctx } = this._processInputParams(input); + const effect = this._def.effect || null; + const checkCtx = { + addIssue: (arg) => { + addIssueToContext(ctx, arg); + if (arg.fatal) { + status.abort(); + } else { + status.dirty(); + } + }, + get path() { + return ctx.path; + } + }; + checkCtx.addIssue = checkCtx.addIssue.bind(checkCtx); + if (effect.type === "preprocess") { + const processed = effect.transform(ctx.data, checkCtx); + if (ctx.common.async) { + return Promise.resolve(processed).then(async (processed2) => { + if (status.value === "aborted") + return INVALID; + const result = await this._def.schema._parseAsync({ + data: processed2, + path: ctx.path, + parent: ctx + }); + if (result.status === "aborted") + return INVALID; + if (result.status === "dirty") + return DIRTY(result.value); + if (status.value === "dirty") + return DIRTY(result.value); + return result; + }); + } else { + if (status.value === "aborted") + return INVALID; + const result = this._def.schema._parseSync({ + data: processed, + path: ctx.path, + parent: ctx + }); + if (result.status === "aborted") + return INVALID; + if (result.status === "dirty") + return DIRTY(result.value); + if (status.value === "dirty") + return DIRTY(result.value); + return result; + } + } + if (effect.type === "refinement") { + const executeRefinement = (acc) => { + const result = effect.refinement(acc, checkCtx); + if (ctx.common.async) { + return Promise.resolve(result); + } + if (result instanceof Promise) { + throw new Error("Async refinement encountered during synchronous parse operation. Use .parseAsync instead."); + } + return acc; + }; + if (ctx.common.async === false) { + const inner = this._def.schema._parseSync({ + data: ctx.data, + path: ctx.path, + parent: ctx + }); + if (inner.status === "aborted") + return INVALID; + if (inner.status === "dirty") + status.dirty(); + executeRefinement(inner.value); + return { status: status.value, value: inner.value }; + } else { + return this._def.schema._parseAsync({ data: ctx.data, path: ctx.path, parent: ctx }).then((inner) => { + if (inner.status === "aborted") + return INVALID; + if (inner.status === "dirty") + status.dirty(); + return executeRefinement(inner.value).then(() => { + return { status: status.value, value: inner.value }; + }); + }); + } + } + if (effect.type === "transform") { + if (ctx.common.async === false) { + const base = this._def.schema._parseSync({ + data: ctx.data, + path: ctx.path, + parent: ctx + }); + if (!isValid(base)) + return base; + const result = effect.transform(base.value, checkCtx); + if (result instanceof Promise) { + throw new Error(`Asynchronous transform encountered during synchronous parse operation. Use .parseAsync instead.`); + } + return { status: status.value, value: result }; + } else { + return this._def.schema._parseAsync({ data: ctx.data, path: ctx.path, parent: ctx }).then((base) => { + if (!isValid(base)) + return base; + return Promise.resolve(effect.transform(base.value, checkCtx)).then((result) => ({ status: status.value, value: result })); + }); + } + } + util.assertNever(effect); + } +}; +ZodEffects.create = (schema, effect, params) => { + return new ZodEffects({ + schema, + typeName: ZodFirstPartyTypeKind.ZodEffects, + effect, + ...processCreateParams(params) + }); +}; +ZodEffects.createWithPreprocess = (preprocess, schema, params) => { + return new ZodEffects({ + schema, + effect: { type: "preprocess", transform: preprocess }, + typeName: ZodFirstPartyTypeKind.ZodEffects, + ...processCreateParams(params) + }); +}; +var ZodOptional = class extends ZodType { + _parse(input) { + const parsedType = this._getType(input); + if (parsedType === ZodParsedType.undefined) { + return OK(void 0); + } + return this._def.innerType._parse(input); + } + unwrap() { + return this._def.innerType; + } +}; +ZodOptional.create = (type, params) => { + return new ZodOptional({ + innerType: type, + typeName: ZodFirstPartyTypeKind.ZodOptional, + ...processCreateParams(params) + }); +}; +var ZodNullable = class extends ZodType { + _parse(input) { + const parsedType = this._getType(input); + if (parsedType === ZodParsedType.null) { + return OK(null); + } + return this._def.innerType._parse(input); + } + unwrap() { + return this._def.innerType; + } +}; +ZodNullable.create = (type, params) => { + return new ZodNullable({ + innerType: type, + typeName: ZodFirstPartyTypeKind.ZodNullable, + ...processCreateParams(params) + }); +}; +var ZodDefault = class extends ZodType { + _parse(input) { + const { ctx } = this._processInputParams(input); + let data = ctx.data; + if (ctx.parsedType === ZodParsedType.undefined) { + data = this._def.defaultValue(); + } + return this._def.innerType._parse({ + data, + path: ctx.path, + parent: ctx + }); + } + removeDefault() { + return this._def.innerType; + } +}; +ZodDefault.create = (type, params) => { + return new ZodDefault({ + innerType: type, + typeName: ZodFirstPartyTypeKind.ZodDefault, + defaultValue: typeof params.default === "function" ? params.default : () => params.default, + ...processCreateParams(params) + }); +}; +var ZodCatch = class extends ZodType { + _parse(input) { + const { ctx } = this._processInputParams(input); + const newCtx = { + ...ctx, + common: { + ...ctx.common, + issues: [] + } + }; + const result = this._def.innerType._parse({ + data: newCtx.data, + path: newCtx.path, + parent: { + ...newCtx + } + }); + if (isAsync(result)) { + return result.then((result2) => { + return { + status: "valid", + value: result2.status === "valid" ? result2.value : this._def.catchValue({ + get error() { + return new ZodError(newCtx.common.issues); + }, + input: newCtx.data + }) + }; + }); + } else { + return { + status: "valid", + value: result.status === "valid" ? result.value : this._def.catchValue({ + get error() { + return new ZodError(newCtx.common.issues); + }, + input: newCtx.data + }) + }; + } + } + removeCatch() { + return this._def.innerType; + } +}; +ZodCatch.create = (type, params) => { + return new ZodCatch({ + innerType: type, + typeName: ZodFirstPartyTypeKind.ZodCatch, + catchValue: typeof params.catch === "function" ? params.catch : () => params.catch, + ...processCreateParams(params) + }); +}; +var ZodNaN = class extends ZodType { + _parse(input) { + const parsedType = this._getType(input); + if (parsedType !== ZodParsedType.nan) { + const ctx = this._getOrReturnCtx(input); + addIssueToContext(ctx, { + code: ZodIssueCode.invalid_type, + expected: ZodParsedType.nan, + received: ctx.parsedType + }); + return INVALID; + } + return { status: "valid", value: input.data }; + } +}; +ZodNaN.create = (params) => { + return new ZodNaN({ + typeName: ZodFirstPartyTypeKind.ZodNaN, + ...processCreateParams(params) + }); +}; +var BRAND = Symbol("zod_brand"); +var ZodBranded = class extends ZodType { + _parse(input) { + const { ctx } = this._processInputParams(input); + const data = ctx.data; + return this._def.type._parse({ + data, + path: ctx.path, + parent: ctx + }); + } + unwrap() { + return this._def.type; + } +}; +var ZodPipeline = class _ZodPipeline extends ZodType { + _parse(input) { + const { status, ctx } = this._processInputParams(input); + if (ctx.common.async) { + const handleAsync = async () => { + const inResult = await this._def.in._parseAsync({ + data: ctx.data, + path: ctx.path, + parent: ctx + }); + if (inResult.status === "aborted") + return INVALID; + if (inResult.status === "dirty") { + status.dirty(); + return DIRTY(inResult.value); + } else { + return this._def.out._parseAsync({ + data: inResult.value, + path: ctx.path, + parent: ctx + }); + } + }; + return handleAsync(); + } else { + const inResult = this._def.in._parseSync({ + data: ctx.data, + path: ctx.path, + parent: ctx + }); + if (inResult.status === "aborted") + return INVALID; + if (inResult.status === "dirty") { + status.dirty(); + return { + status: "dirty", + value: inResult.value + }; + } else { + return this._def.out._parseSync({ + data: inResult.value, + path: ctx.path, + parent: ctx + }); + } + } + } + static create(a, b) { + return new _ZodPipeline({ + in: a, + out: b, + typeName: ZodFirstPartyTypeKind.ZodPipeline + }); + } +}; +var ZodReadonly = class extends ZodType { + _parse(input) { + const result = this._def.innerType._parse(input); + const freeze = (data) => { + if (isValid(data)) { + data.value = Object.freeze(data.value); + } + return data; + }; + return isAsync(result) ? result.then((data) => freeze(data)) : freeze(result); + } + unwrap() { + return this._def.innerType; + } +}; +ZodReadonly.create = (type, params) => { + return new ZodReadonly({ + innerType: type, + typeName: ZodFirstPartyTypeKind.ZodReadonly, + ...processCreateParams(params) + }); +}; +function custom(check, params = {}, fatal) { + if (check) + return ZodAny.create().superRefine((data, ctx) => { + var _a, _b; + if (!check(data)) { + const p = typeof params === "function" ? params(data) : typeof params === "string" ? { message: params } : params; + const _fatal = (_b = (_a = p.fatal) !== null && _a !== void 0 ? _a : fatal) !== null && _b !== void 0 ? _b : true; + const p2 = typeof p === "string" ? { message: p } : p; + ctx.addIssue({ code: "custom", ...p2, fatal: _fatal }); + } + }); + return ZodAny.create(); +} +var late = { + object: ZodObject.lazycreate +}; +var ZodFirstPartyTypeKind; +(function(ZodFirstPartyTypeKind2) { + ZodFirstPartyTypeKind2["ZodString"] = "ZodString"; + ZodFirstPartyTypeKind2["ZodNumber"] = "ZodNumber"; + ZodFirstPartyTypeKind2["ZodNaN"] = "ZodNaN"; + ZodFirstPartyTypeKind2["ZodBigInt"] = "ZodBigInt"; + ZodFirstPartyTypeKind2["ZodBoolean"] = "ZodBoolean"; + ZodFirstPartyTypeKind2["ZodDate"] = "ZodDate"; + ZodFirstPartyTypeKind2["ZodSymbol"] = "ZodSymbol"; + ZodFirstPartyTypeKind2["ZodUndefined"] = "ZodUndefined"; + ZodFirstPartyTypeKind2["ZodNull"] = "ZodNull"; + ZodFirstPartyTypeKind2["ZodAny"] = "ZodAny"; + ZodFirstPartyTypeKind2["ZodUnknown"] = "ZodUnknown"; + ZodFirstPartyTypeKind2["ZodNever"] = "ZodNever"; + ZodFirstPartyTypeKind2["ZodVoid"] = "ZodVoid"; + ZodFirstPartyTypeKind2["ZodArray"] = "ZodArray"; + ZodFirstPartyTypeKind2["ZodObject"] = "ZodObject"; + ZodFirstPartyTypeKind2["ZodUnion"] = "ZodUnion"; + ZodFirstPartyTypeKind2["ZodDiscriminatedUnion"] = "ZodDiscriminatedUnion"; + ZodFirstPartyTypeKind2["ZodIntersection"] = "ZodIntersection"; + ZodFirstPartyTypeKind2["ZodTuple"] = "ZodTuple"; + ZodFirstPartyTypeKind2["ZodRecord"] = "ZodRecord"; + ZodFirstPartyTypeKind2["ZodMap"] = "ZodMap"; + ZodFirstPartyTypeKind2["ZodSet"] = "ZodSet"; + ZodFirstPartyTypeKind2["ZodFunction"] = "ZodFunction"; + ZodFirstPartyTypeKind2["ZodLazy"] = "ZodLazy"; + ZodFirstPartyTypeKind2["ZodLiteral"] = "ZodLiteral"; + ZodFirstPartyTypeKind2["ZodEnum"] = "ZodEnum"; + ZodFirstPartyTypeKind2["ZodEffects"] = "ZodEffects"; + ZodFirstPartyTypeKind2["ZodNativeEnum"] = "ZodNativeEnum"; + ZodFirstPartyTypeKind2["ZodOptional"] = "ZodOptional"; + ZodFirstPartyTypeKind2["ZodNullable"] = "ZodNullable"; + ZodFirstPartyTypeKind2["ZodDefault"] = "ZodDefault"; + ZodFirstPartyTypeKind2["ZodCatch"] = "ZodCatch"; + ZodFirstPartyTypeKind2["ZodPromise"] = "ZodPromise"; + ZodFirstPartyTypeKind2["ZodBranded"] = "ZodBranded"; + ZodFirstPartyTypeKind2["ZodPipeline"] = "ZodPipeline"; + ZodFirstPartyTypeKind2["ZodReadonly"] = "ZodReadonly"; +})(ZodFirstPartyTypeKind || (ZodFirstPartyTypeKind = {})); +var instanceOfType = (cls, params = { + message: `Input not instance of ${cls.name}` +}) => custom((data) => data instanceof cls, params); +var stringType = ZodString.create; +var numberType = ZodNumber.create; +var nanType = ZodNaN.create; +var bigIntType = ZodBigInt.create; +var booleanType = ZodBoolean.create; +var dateType = ZodDate.create; +var symbolType = ZodSymbol.create; +var undefinedType = ZodUndefined.create; +var nullType = ZodNull.create; +var anyType = ZodAny.create; +var unknownType = ZodUnknown.create; +var neverType = ZodNever.create; +var voidType = ZodVoid.create; +var arrayType = ZodArray.create; +var objectType = ZodObject.create; +var strictObjectType = ZodObject.strictCreate; +var unionType = ZodUnion.create; +var discriminatedUnionType = ZodDiscriminatedUnion.create; +var intersectionType = ZodIntersection.create; +var tupleType = ZodTuple.create; +var recordType = ZodRecord.create; +var mapType = ZodMap.create; +var setType = ZodSet.create; +var functionType = ZodFunction.create; +var lazyType = ZodLazy.create; +var literalType = ZodLiteral.create; +var enumType = ZodEnum.create; +var nativeEnumType = ZodNativeEnum.create; +var promiseType = ZodPromise.create; +var effectsType = ZodEffects.create; +var optionalType = ZodOptional.create; +var nullableType = ZodNullable.create; +var preprocessType = ZodEffects.createWithPreprocess; +var pipelineType = ZodPipeline.create; +var ostring = () => stringType().optional(); +var onumber = () => numberType().optional(); +var oboolean = () => booleanType().optional(); +var coerce = { + string: (arg) => ZodString.create({ ...arg, coerce: true }), + number: (arg) => ZodNumber.create({ ...arg, coerce: true }), + boolean: (arg) => ZodBoolean.create({ + ...arg, + coerce: true + }), + bigint: (arg) => ZodBigInt.create({ ...arg, coerce: true }), + date: (arg) => ZodDate.create({ ...arg, coerce: true }) +}; +var NEVER = INVALID; +var z = /* @__PURE__ */ Object.freeze({ + __proto__: null, + defaultErrorMap: errorMap, + setErrorMap, + getErrorMap, + makeIssue, + EMPTY_PATH, + addIssueToContext, + ParseStatus, + INVALID, + DIRTY, + OK, + isAborted, + isDirty, + isValid, + isAsync, + get util() { + return util; + }, + get objectUtil() { + return objectUtil; + }, + ZodParsedType, + getParsedType, + ZodType, + datetimeRegex, + ZodString, + ZodNumber, + ZodBigInt, + ZodBoolean, + ZodDate, + ZodSymbol, + ZodUndefined, + ZodNull, + ZodAny, + ZodUnknown, + ZodNever, + ZodVoid, + ZodArray, + ZodObject, + ZodUnion, + ZodDiscriminatedUnion, + ZodIntersection, + ZodTuple, + ZodRecord, + ZodMap, + ZodSet, + ZodFunction, + ZodLazy, + ZodLiteral, + ZodEnum, + ZodNativeEnum, + ZodPromise, + ZodEffects, + ZodTransformer: ZodEffects, + ZodOptional, + ZodNullable, + ZodDefault, + ZodCatch, + ZodNaN, + BRAND, + ZodBranded, + ZodPipeline, + ZodReadonly, + custom, + Schema: ZodType, + ZodSchema: ZodType, + late, + get ZodFirstPartyTypeKind() { + return ZodFirstPartyTypeKind; + }, + coerce, + any: anyType, + array: arrayType, + bigint: bigIntType, + boolean: booleanType, + date: dateType, + discriminatedUnion: discriminatedUnionType, + effect: effectsType, + "enum": enumType, + "function": functionType, + "instanceof": instanceOfType, + intersection: intersectionType, + lazy: lazyType, + literal: literalType, + map: mapType, + nan: nanType, + nativeEnum: nativeEnumType, + never: neverType, + "null": nullType, + nullable: nullableType, + number: numberType, + object: objectType, + oboolean, + onumber, + optional: optionalType, + ostring, + pipeline: pipelineType, + preprocess: preprocessType, + promise: promiseType, + record: recordType, + set: setType, + strictObject: strictObjectType, + string: stringType, + symbol: symbolType, + transformer: effectsType, + tuple: tupleType, + "undefined": undefinedType, + union: unionType, + unknown: unknownType, + "void": voidType, + NEVER, + ZodIssueCode, + quotelessJson, + ZodError +}); + +// node_modules/zod-to-json-schema/dist/esm/Options.js +var ignoreOverride = Symbol("Let zodToJsonSchema decide on which parser to use"); +var defaultOptions = { + name: void 0, + $refStrategy: "root", + basePath: ["#"], + effectStrategy: "input", + pipeStrategy: "all", + dateStrategy: "format:date-time", + mapStrategy: "entries", + removeAdditionalStrategy: "passthrough", + definitionPath: "definitions", + target: "jsonSchema7", + strictUnions: false, + definitions: {}, + errorMessages: false, + markdownDescription: false, + patternStrategy: "escape", + applyRegexFlags: false, + emailStrategy: "format:email", + base64Strategy: "contentEncoding:base64", + nameStrategy: "ref" +}; +var getDefaultOptions = (options) => typeof options === "string" ? { + ...defaultOptions, + name: options +} : { + ...defaultOptions, + ...options +}; + +// node_modules/zod-to-json-schema/dist/esm/Refs.js +var getRefs = (options) => { + const _options = getDefaultOptions(options); + const currentPath = _options.name !== void 0 ? [..._options.basePath, _options.definitionPath, _options.name] : _options.basePath; + return { + ..._options, + currentPath, + propertyPath: void 0, + seen: new Map(Object.entries(_options.definitions).map(([name, def]) => [ + def._def, + { + def: def._def, + path: [..._options.basePath, _options.definitionPath, name], + // Resolution of references will be forced even though seen, so it's ok that the schema is undefined here for now. + jsonSchema: void 0 + } + ])) + }; +}; + +// node_modules/zod-to-json-schema/dist/esm/errorMessages.js +function addErrorMessage(res, key, errorMessage, refs) { + if (!refs?.errorMessages) + return; + if (errorMessage) { + res.errorMessage = { + ...res.errorMessage, + [key]: errorMessage + }; + } +} +function setResponseValueAndErrors(res, key, value, errorMessage, refs) { + res[key] = value; + addErrorMessage(res, key, errorMessage, refs); +} + +// node_modules/zod-to-json-schema/dist/esm/parsers/any.js +function parseAnyDef() { + return {}; +} + +// node_modules/zod-to-json-schema/dist/esm/parsers/array.js +function parseArrayDef(def, refs) { + const res = { + type: "array" + }; + if (def.type?._def && def.type?._def?.typeName !== ZodFirstPartyTypeKind.ZodAny) { + res.items = parseDef(def.type._def, { + ...refs, + currentPath: [...refs.currentPath, "items"] + }); + } + if (def.minLength) { + setResponseValueAndErrors(res, "minItems", def.minLength.value, def.minLength.message, refs); + } + if (def.maxLength) { + setResponseValueAndErrors(res, "maxItems", def.maxLength.value, def.maxLength.message, refs); + } + if (def.exactLength) { + setResponseValueAndErrors(res, "minItems", def.exactLength.value, def.exactLength.message, refs); + setResponseValueAndErrors(res, "maxItems", def.exactLength.value, def.exactLength.message, refs); + } + return res; +} + +// node_modules/zod-to-json-schema/dist/esm/parsers/bigint.js +function parseBigintDef(def, refs) { + const res = { + type: "integer", + format: "int64" + }; + if (!def.checks) + return res; + for (const check of def.checks) { + switch (check.kind) { + case "min": + if (refs.target === "jsonSchema7") { + if (check.inclusive) { + setResponseValueAndErrors(res, "minimum", check.value, check.message, refs); + } else { + setResponseValueAndErrors(res, "exclusiveMinimum", check.value, check.message, refs); + } + } else { + if (!check.inclusive) { + res.exclusiveMinimum = true; + } + setResponseValueAndErrors(res, "minimum", check.value, check.message, refs); + } + break; + case "max": + if (refs.target === "jsonSchema7") { + if (check.inclusive) { + setResponseValueAndErrors(res, "maximum", check.value, check.message, refs); + } else { + setResponseValueAndErrors(res, "exclusiveMaximum", check.value, check.message, refs); + } + } else { + if (!check.inclusive) { + res.exclusiveMaximum = true; + } + setResponseValueAndErrors(res, "maximum", check.value, check.message, refs); + } + break; + case "multipleOf": + setResponseValueAndErrors(res, "multipleOf", check.value, check.message, refs); + break; + } + } + return res; +} + +// node_modules/zod-to-json-schema/dist/esm/parsers/boolean.js +function parseBooleanDef() { + return { + type: "boolean" + }; +} + +// node_modules/zod-to-json-schema/dist/esm/parsers/branded.js +function parseBrandedDef(_def, refs) { + return parseDef(_def.type._def, refs); +} + +// node_modules/zod-to-json-schema/dist/esm/parsers/catch.js +var parseCatchDef = (def, refs) => { + return parseDef(def.innerType._def, refs); +}; + +// node_modules/zod-to-json-schema/dist/esm/parsers/date.js +function parseDateDef(def, refs, overrideDateStrategy) { + const strategy = overrideDateStrategy ?? refs.dateStrategy; + if (Array.isArray(strategy)) { + return { + anyOf: strategy.map((item, i) => parseDateDef(def, refs, item)) + }; + } + switch (strategy) { + case "string": + case "format:date-time": + return { + type: "string", + format: "date-time" + }; + case "format:date": + return { + type: "string", + format: "date" + }; + case "integer": + return integerDateParser(def, refs); + } +} +var integerDateParser = (def, refs) => { + const res = { + type: "integer", + format: "unix-time" + }; + if (refs.target === "openApi3") { + return res; + } + for (const check of def.checks) { + switch (check.kind) { + case "min": + setResponseValueAndErrors( + res, + "minimum", + check.value, + // This is in milliseconds + check.message, + refs + ); + break; + case "max": + setResponseValueAndErrors( + res, + "maximum", + check.value, + // This is in milliseconds + check.message, + refs + ); + break; + } + } + return res; +}; + +// node_modules/zod-to-json-schema/dist/esm/parsers/default.js +function parseDefaultDef(_def, refs) { + return { + ...parseDef(_def.innerType._def, refs), + default: _def.defaultValue() + }; +} + +// node_modules/zod-to-json-schema/dist/esm/parsers/effects.js +function parseEffectsDef(_def, refs) { + return refs.effectStrategy === "input" ? parseDef(_def.schema._def, refs) : {}; +} + +// node_modules/zod-to-json-schema/dist/esm/parsers/enum.js +function parseEnumDef(def) { + return { + type: "string", + enum: def.values + }; +} + +// node_modules/zod-to-json-schema/dist/esm/parsers/intersection.js +var isJsonSchema7AllOfType = (type) => { + if ("type" in type && type.type === "string") + return false; + return "allOf" in type; +}; +function parseIntersectionDef(def, refs) { + const allOf = [ + parseDef(def.left._def, { + ...refs, + currentPath: [...refs.currentPath, "allOf", "0"] + }), + parseDef(def.right._def, { + ...refs, + currentPath: [...refs.currentPath, "allOf", "1"] + }) + ].filter((x) => !!x); + let unevaluatedProperties = refs.target === "jsonSchema2019-09" ? { unevaluatedProperties: false } : void 0; + const mergedAllOf = []; + allOf.forEach((schema) => { + if (isJsonSchema7AllOfType(schema)) { + mergedAllOf.push(...schema.allOf); + if (schema.unevaluatedProperties === void 0) { + unevaluatedProperties = void 0; + } + } else { + let nestedSchema = schema; + if ("additionalProperties" in schema && schema.additionalProperties === false) { + const { additionalProperties, ...rest } = schema; + nestedSchema = rest; + } else { + unevaluatedProperties = void 0; + } + mergedAllOf.push(nestedSchema); + } + }); + return mergedAllOf.length ? { + allOf: mergedAllOf, + ...unevaluatedProperties + } : void 0; +} + +// node_modules/zod-to-json-schema/dist/esm/parsers/literal.js +function parseLiteralDef(def, refs) { + const parsedType = typeof def.value; + if (parsedType !== "bigint" && parsedType !== "number" && parsedType !== "boolean" && parsedType !== "string") { + return { + type: Array.isArray(def.value) ? "array" : "object" + }; + } + if (refs.target === "openApi3") { + return { + type: parsedType === "bigint" ? "integer" : parsedType, + enum: [def.value] + }; + } + return { + type: parsedType === "bigint" ? "integer" : parsedType, + const: def.value + }; +} + +// node_modules/zod-to-json-schema/dist/esm/parsers/string.js +var emojiRegex2; +var zodPatterns = { + /** + * `c` was changed to `[cC]` to replicate /i flag + */ + cuid: /^[cC][^\s-]{8,}$/, + cuid2: /^[0-9a-z]+$/, + ulid: /^[0-9A-HJKMNP-TV-Z]{26}$/, + /** + * `a-z` was added to replicate /i flag + */ + email: /^(?!\.)(?!.*\.\.)([a-zA-Z0-9_'+\-\.]*)[a-zA-Z0-9_+-]@([a-zA-Z0-9][a-zA-Z0-9\-]*\.)+[a-zA-Z]{2,}$/, + /** + * Constructed a valid Unicode RegExp + * + * Lazily instantiate since this type of regex isn't supported + * in all envs (e.g. React Native). + * + * See: + * https://github.com/colinhacks/zod/issues/2433 + * Fix in Zod: + * https://github.com/colinhacks/zod/commit/9340fd51e48576a75adc919bff65dbc4a5d4c99b + */ + emoji: () => { + if (emojiRegex2 === void 0) { + emojiRegex2 = RegExp("^(\\p{Extended_Pictographic}|\\p{Emoji_Component})+$", "u"); + } + return emojiRegex2; + }, + /** + * Unused + */ + uuid: /^[0-9a-fA-F]{8}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{12}$/, + /** + * Unused + */ + ipv4: /^(?:(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])\.){3}(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])$/, + /** + * Unused + */ + ipv6: /^(([a-f0-9]{1,4}:){7}|::([a-f0-9]{1,4}:){0,6}|([a-f0-9]{1,4}:){1}:([a-f0-9]{1,4}:){0,5}|([a-f0-9]{1,4}:){2}:([a-f0-9]{1,4}:){0,4}|([a-f0-9]{1,4}:){3}:([a-f0-9]{1,4}:){0,3}|([a-f0-9]{1,4}:){4}:([a-f0-9]{1,4}:){0,2}|([a-f0-9]{1,4}:){5}:([a-f0-9]{1,4}:){0,1})([a-f0-9]{1,4}|(((25[0-5])|(2[0-4][0-9])|(1[0-9]{2})|([0-9]{1,2}))\.){3}((25[0-5])|(2[0-4][0-9])|(1[0-9]{2})|([0-9]{1,2})))$/, + base64: /^([0-9a-zA-Z+/]{4})*(([0-9a-zA-Z+/]{2}==)|([0-9a-zA-Z+/]{3}=))?$/, + nanoid: /^[a-zA-Z0-9_-]{21}$/ +}; +function parseStringDef(def, refs) { + const res = { + type: "string" + }; + function processPattern(value) { + return refs.patternStrategy === "escape" ? escapeNonAlphaNumeric(value) : value; + } + if (def.checks) { + for (const check of def.checks) { + switch (check.kind) { + case "min": + setResponseValueAndErrors(res, "minLength", typeof res.minLength === "number" ? Math.max(res.minLength, check.value) : check.value, check.message, refs); + break; + case "max": + setResponseValueAndErrors(res, "maxLength", typeof res.maxLength === "number" ? Math.min(res.maxLength, check.value) : check.value, check.message, refs); + break; + case "email": + switch (refs.emailStrategy) { + case "format:email": + addFormat(res, "email", check.message, refs); + break; + case "format:idn-email": + addFormat(res, "idn-email", check.message, refs); + break; + case "pattern:zod": + addPattern(res, zodPatterns.email, check.message, refs); + break; + } + break; + case "url": + addFormat(res, "uri", check.message, refs); + break; + case "uuid": + addFormat(res, "uuid", check.message, refs); + break; + case "regex": + addPattern(res, check.regex, check.message, refs); + break; + case "cuid": + addPattern(res, zodPatterns.cuid, check.message, refs); + break; + case "cuid2": + addPattern(res, zodPatterns.cuid2, check.message, refs); + break; + case "startsWith": + addPattern(res, RegExp(`^${processPattern(check.value)}`), check.message, refs); + break; + case "endsWith": + addPattern(res, RegExp(`${processPattern(check.value)}$`), check.message, refs); + break; + case "datetime": + addFormat(res, "date-time", check.message, refs); + break; + case "date": + addFormat(res, "date", check.message, refs); + break; + case "time": + addFormat(res, "time", check.message, refs); + break; + case "duration": + addFormat(res, "duration", check.message, refs); + break; + case "length": + setResponseValueAndErrors(res, "minLength", typeof res.minLength === "number" ? Math.max(res.minLength, check.value) : check.value, check.message, refs); + setResponseValueAndErrors(res, "maxLength", typeof res.maxLength === "number" ? Math.min(res.maxLength, check.value) : check.value, check.message, refs); + break; + case "includes": { + addPattern(res, RegExp(processPattern(check.value)), check.message, refs); + break; + } + case "ip": { + if (check.version !== "v6") { + addFormat(res, "ipv4", check.message, refs); + } + if (check.version !== "v4") { + addFormat(res, "ipv6", check.message, refs); + } + break; + } + case "emoji": + addPattern(res, zodPatterns.emoji, check.message, refs); + break; + case "ulid": { + addPattern(res, zodPatterns.ulid, check.message, refs); + break; + } + case "base64": { + switch (refs.base64Strategy) { + case "format:binary": { + addFormat(res, "binary", check.message, refs); + break; + } + case "contentEncoding:base64": { + setResponseValueAndErrors(res, "contentEncoding", "base64", check.message, refs); + break; + } + case "pattern:zod": { + addPattern(res, zodPatterns.base64, check.message, refs); + break; + } + } + break; + } + case "nanoid": { + addPattern(res, zodPatterns.nanoid, check.message, refs); + } + case "toLowerCase": + case "toUpperCase": + case "trim": + break; + default: + /* @__PURE__ */ ((_) => { + })(check); + } + } + } + return res; +} +var escapeNonAlphaNumeric = (value) => Array.from(value).map((c) => /[a-zA-Z0-9]/.test(c) ? c : `\\${c}`).join(""); +var addFormat = (schema, value, message, refs) => { + if (schema.format || schema.anyOf?.some((x) => x.format)) { + if (!schema.anyOf) { + schema.anyOf = []; + } + if (schema.format) { + schema.anyOf.push({ + format: schema.format, + ...schema.errorMessage && refs.errorMessages && { + errorMessage: { format: schema.errorMessage.format } + } + }); + delete schema.format; + if (schema.errorMessage) { + delete schema.errorMessage.format; + if (Object.keys(schema.errorMessage).length === 0) { + delete schema.errorMessage; + } + } + } + schema.anyOf.push({ + format: value, + ...message && refs.errorMessages && { errorMessage: { format: message } } + }); + } else { + setResponseValueAndErrors(schema, "format", value, message, refs); + } +}; +var addPattern = (schema, regex, message, refs) => { + if (schema.pattern || schema.allOf?.some((x) => x.pattern)) { + if (!schema.allOf) { + schema.allOf = []; + } + if (schema.pattern) { + schema.allOf.push({ + pattern: schema.pattern, + ...schema.errorMessage && refs.errorMessages && { + errorMessage: { pattern: schema.errorMessage.pattern } + } + }); + delete schema.pattern; + if (schema.errorMessage) { + delete schema.errorMessage.pattern; + if (Object.keys(schema.errorMessage).length === 0) { + delete schema.errorMessage; + } + } + } + schema.allOf.push({ + pattern: processRegExp(regex, refs), + ...message && refs.errorMessages && { errorMessage: { pattern: message } } + }); + } else { + setResponseValueAndErrors(schema, "pattern", processRegExp(regex, refs), message, refs); + } +}; +var processRegExp = (regexOrFunction, refs) => { + const regex = typeof regexOrFunction === "function" ? regexOrFunction() : regexOrFunction; + if (!refs.applyRegexFlags || !regex.flags) + return regex.source; + const flags = { + i: regex.flags.includes("i"), + m: regex.flags.includes("m"), + s: regex.flags.includes("s") + // `.` matches newlines + }; + const source = flags.i ? regex.source.toLowerCase() : regex.source; + let pattern = ""; + let isEscaped = false; + let inCharGroup = false; + let inCharRange = false; + for (let i = 0; i < source.length; i++) { + if (isEscaped) { + pattern += source[i]; + isEscaped = false; + continue; + } + if (flags.i) { + if (inCharGroup) { + if (source[i].match(/[a-z]/)) { + if (inCharRange) { + pattern += source[i]; + pattern += `${source[i - 2]}-${source[i]}`.toUpperCase(); + inCharRange = false; + } else if (source[i + 1] === "-" && source[i + 2]?.match(/[a-z]/)) { + pattern += source[i]; + inCharRange = true; + } else { + pattern += `${source[i]}${source[i].toUpperCase()}`; + } + continue; + } + } else if (source[i].match(/[a-z]/)) { + pattern += `[${source[i]}${source[i].toUpperCase()}]`; + continue; + } + } + if (flags.m) { + if (source[i] === "^") { + pattern += `(^|(?<=[\r +]))`; + continue; + } else if (source[i] === "$") { + pattern += `($|(?=[\r +]))`; + continue; + } + } + if (flags.s && source[i] === ".") { + pattern += inCharGroup ? `${source[i]}\r +` : `[${source[i]}\r +]`; + continue; + } + pattern += source[i]; + if (source[i] === "\\") { + isEscaped = true; + } else if (inCharGroup && source[i] === "]") { + inCharGroup = false; + } else if (!inCharGroup && source[i] === "[") { + inCharGroup = true; + } + } + try { + const regexTest = new RegExp(pattern); + } catch { + console.warn(`Could not convert regex pattern at ${refs.currentPath.join("/")} to a flag-independent form! Falling back to the flag-ignorant source`); + return regex.source; + } + return pattern; +}; + +// node_modules/zod-to-json-schema/dist/esm/parsers/record.js +function parseRecordDef(def, refs) { + if (refs.target === "openApi3" && def.keyType?._def.typeName === ZodFirstPartyTypeKind.ZodEnum) { + return { + type: "object", + required: def.keyType._def.values, + properties: def.keyType._def.values.reduce((acc, key) => ({ + ...acc, + [key]: parseDef(def.valueType._def, { + ...refs, + currentPath: [...refs.currentPath, "properties", key] + }) ?? {} + }), {}), + additionalProperties: false + }; + } + const schema = { + type: "object", + additionalProperties: parseDef(def.valueType._def, { + ...refs, + currentPath: [...refs.currentPath, "additionalProperties"] + }) ?? {} + }; + if (refs.target === "openApi3") { + return schema; + } + if (def.keyType?._def.typeName === ZodFirstPartyTypeKind.ZodString && def.keyType._def.checks?.length) { + const { type, ...keyType } = parseStringDef(def.keyType._def, refs); + return { + ...schema, + propertyNames: keyType + }; + } else if (def.keyType?._def.typeName === ZodFirstPartyTypeKind.ZodEnum) { + return { + ...schema, + propertyNames: { + enum: def.keyType._def.values + } + }; + } else if (def.keyType?._def.typeName === ZodFirstPartyTypeKind.ZodBranded && def.keyType._def.type._def.typeName === ZodFirstPartyTypeKind.ZodString && def.keyType._def.type._def.checks?.length) { + const { type, ...keyType } = parseBrandedDef(def.keyType._def, refs); + return { + ...schema, + propertyNames: keyType + }; + } + return schema; +} + +// node_modules/zod-to-json-schema/dist/esm/parsers/map.js +function parseMapDef(def, refs) { + if (refs.mapStrategy === "record") { + return parseRecordDef(def, refs); + } + const keys = parseDef(def.keyType._def, { + ...refs, + currentPath: [...refs.currentPath, "items", "items", "0"] + }) || {}; + const values = parseDef(def.valueType._def, { + ...refs, + currentPath: [...refs.currentPath, "items", "items", "1"] + }) || {}; + return { + type: "array", + maxItems: 125, + items: { + type: "array", + items: [keys, values], + minItems: 2, + maxItems: 2 + } + }; +} + +// node_modules/zod-to-json-schema/dist/esm/parsers/nativeEnum.js +function parseNativeEnumDef(def) { + const object = def.values; + const actualKeys = Object.keys(def.values).filter((key) => { + return typeof object[object[key]] !== "number"; + }); + const actualValues = actualKeys.map((key) => object[key]); + const parsedTypes = Array.from(new Set(actualValues.map((values) => typeof values))); + return { + type: parsedTypes.length === 1 ? parsedTypes[0] === "string" ? "string" : "number" : ["string", "number"], + enum: actualValues + }; +} + +// node_modules/zod-to-json-schema/dist/esm/parsers/never.js +function parseNeverDef() { + return { + not: {} + }; +} + +// node_modules/zod-to-json-schema/dist/esm/parsers/null.js +function parseNullDef(refs) { + return refs.target === "openApi3" ? { + enum: ["null"], + nullable: true + } : { + type: "null" + }; +} + +// node_modules/zod-to-json-schema/dist/esm/parsers/union.js +var primitiveMappings = { + ZodString: "string", + ZodNumber: "number", + ZodBigInt: "integer", + ZodBoolean: "boolean", + ZodNull: "null" +}; +function parseUnionDef(def, refs) { + if (refs.target === "openApi3") + return asAnyOf(def, refs); + const options = def.options instanceof Map ? Array.from(def.options.values()) : def.options; + if (options.every((x) => x._def.typeName in primitiveMappings && (!x._def.checks || !x._def.checks.length))) { + const types = options.reduce((types2, x) => { + const type = primitiveMappings[x._def.typeName]; + return type && !types2.includes(type) ? [...types2, type] : types2; + }, []); + return { + type: types.length > 1 ? types : types[0] + }; + } else if (options.every((x) => x._def.typeName === "ZodLiteral" && !x.description)) { + const types = options.reduce((acc, x) => { + const type = typeof x._def.value; + switch (type) { + case "string": + case "number": + case "boolean": + return [...acc, type]; + case "bigint": + return [...acc, "integer"]; + case "object": + if (x._def.value === null) + return [...acc, "null"]; + case "symbol": + case "undefined": + case "function": + default: + return acc; + } + }, []); + if (types.length === options.length) { + const uniqueTypes = types.filter((x, i, a) => a.indexOf(x) === i); + return { + type: uniqueTypes.length > 1 ? uniqueTypes : uniqueTypes[0], + enum: options.reduce((acc, x) => { + return acc.includes(x._def.value) ? acc : [...acc, x._def.value]; + }, []) + }; + } + } else if (options.every((x) => x._def.typeName === "ZodEnum")) { + return { + type: "string", + enum: options.reduce((acc, x) => [ + ...acc, + ...x._def.values.filter((x2) => !acc.includes(x2)) + ], []) + }; + } + return asAnyOf(def, refs); +} +var asAnyOf = (def, refs) => { + const anyOf = (def.options instanceof Map ? Array.from(def.options.values()) : def.options).map((x, i) => parseDef(x._def, { + ...refs, + currentPath: [...refs.currentPath, "anyOf", `${i}`] + })).filter((x) => !!x && (!refs.strictUnions || typeof x === "object" && Object.keys(x).length > 0)); + return anyOf.length ? { anyOf } : void 0; +}; + +// node_modules/zod-to-json-schema/dist/esm/parsers/nullable.js +function parseNullableDef(def, refs) { + if (["ZodString", "ZodNumber", "ZodBigInt", "ZodBoolean", "ZodNull"].includes(def.innerType._def.typeName) && (!def.innerType._def.checks || !def.innerType._def.checks.length)) { + if (refs.target === "openApi3") { + return { + type: primitiveMappings[def.innerType._def.typeName], + nullable: true + }; + } + return { + type: [ + primitiveMappings[def.innerType._def.typeName], + "null" + ] + }; + } + if (refs.target === "openApi3") { + const base2 = parseDef(def.innerType._def, { + ...refs, + currentPath: [...refs.currentPath] + }); + if (base2 && "$ref" in base2) + return { allOf: [base2], nullable: true }; + return base2 && { ...base2, nullable: true }; + } + const base = parseDef(def.innerType._def, { + ...refs, + currentPath: [...refs.currentPath, "anyOf", "0"] + }); + return base && { anyOf: [base, { type: "null" }] }; +} + +// node_modules/zod-to-json-schema/dist/esm/parsers/number.js +function parseNumberDef(def, refs) { + const res = { + type: "number" + }; + if (!def.checks) + return res; + for (const check of def.checks) { + switch (check.kind) { + case "int": + res.type = "integer"; + addErrorMessage(res, "type", check.message, refs); + break; + case "min": + if (refs.target === "jsonSchema7") { + if (check.inclusive) { + setResponseValueAndErrors(res, "minimum", check.value, check.message, refs); + } else { + setResponseValueAndErrors(res, "exclusiveMinimum", check.value, check.message, refs); + } + } else { + if (!check.inclusive) { + res.exclusiveMinimum = true; + } + setResponseValueAndErrors(res, "minimum", check.value, check.message, refs); + } + break; + case "max": + if (refs.target === "jsonSchema7") { + if (check.inclusive) { + setResponseValueAndErrors(res, "maximum", check.value, check.message, refs); + } else { + setResponseValueAndErrors(res, "exclusiveMaximum", check.value, check.message, refs); + } + } else { + if (!check.inclusive) { + res.exclusiveMaximum = true; + } + setResponseValueAndErrors(res, "maximum", check.value, check.message, refs); + } + break; + case "multipleOf": + setResponseValueAndErrors(res, "multipleOf", check.value, check.message, refs); + break; + } + } + return res; +} + +// node_modules/zod-to-json-schema/dist/esm/parsers/object.js +function decideAdditionalProperties(def, refs) { + if (refs.removeAdditionalStrategy === "strict") { + return def.catchall._def.typeName === "ZodNever" ? def.unknownKeys !== "strict" : parseDef(def.catchall._def, { + ...refs, + currentPath: [...refs.currentPath, "additionalProperties"] + }) ?? true; + } else { + return def.catchall._def.typeName === "ZodNever" ? def.unknownKeys === "passthrough" : parseDef(def.catchall._def, { + ...refs, + currentPath: [...refs.currentPath, "additionalProperties"] + }) ?? true; + } +} +function parseObjectDef(def, refs) { + const result = { + type: "object", + ...Object.entries(def.shape()).reduce((acc, [propName, propDef]) => { + if (propDef === void 0 || propDef._def === void 0) + return acc; + const parsedDef = parseDef(propDef._def, { + ...refs, + currentPath: [...refs.currentPath, "properties", propName], + propertyPath: [...refs.currentPath, "properties", propName] + }); + if (parsedDef === void 0) + return acc; + return { + properties: { ...acc.properties, [propName]: parsedDef }, + required: propDef.isOptional() ? acc.required : [...acc.required, propName] + }; + }, { properties: {}, required: [] }), + additionalProperties: decideAdditionalProperties(def, refs) + }; + if (!result.required.length) + delete result.required; + return result; +} + +// node_modules/zod-to-json-schema/dist/esm/parsers/optional.js +var parseOptionalDef = (def, refs) => { + if (refs.currentPath.toString() === refs.propertyPath?.toString()) { + return parseDef(def.innerType._def, refs); + } + const innerSchema = parseDef(def.innerType._def, { + ...refs, + currentPath: [...refs.currentPath, "anyOf", "1"] + }); + return innerSchema ? { + anyOf: [ + { + not: {} + }, + innerSchema + ] + } : {}; +}; + +// node_modules/zod-to-json-schema/dist/esm/parsers/pipeline.js +var parsePipelineDef = (def, refs) => { + if (refs.pipeStrategy === "input") { + return parseDef(def.in._def, refs); + } else if (refs.pipeStrategy === "output") { + return parseDef(def.out._def, refs); + } + const a = parseDef(def.in._def, { + ...refs, + currentPath: [...refs.currentPath, "allOf", "0"] + }); + const b = parseDef(def.out._def, { + ...refs, + currentPath: [...refs.currentPath, "allOf", a ? "1" : "0"] + }); + return { + allOf: [a, b].filter((x) => x !== void 0) + }; +}; + +// node_modules/zod-to-json-schema/dist/esm/parsers/promise.js +function parsePromiseDef(def, refs) { + return parseDef(def.type._def, refs); +} + +// node_modules/zod-to-json-schema/dist/esm/parsers/set.js +function parseSetDef(def, refs) { + const items = parseDef(def.valueType._def, { + ...refs, + currentPath: [...refs.currentPath, "items"] + }); + const schema = { + type: "array", + uniqueItems: true, + items + }; + if (def.minSize) { + setResponseValueAndErrors(schema, "minItems", def.minSize.value, def.minSize.message, refs); + } + if (def.maxSize) { + setResponseValueAndErrors(schema, "maxItems", def.maxSize.value, def.maxSize.message, refs); + } + return schema; +} + +// node_modules/zod-to-json-schema/dist/esm/parsers/tuple.js +function parseTupleDef(def, refs) { + if (def.rest) { + return { + type: "array", + minItems: def.items.length, + items: def.items.map((x, i) => parseDef(x._def, { + ...refs, + currentPath: [...refs.currentPath, "items", `${i}`] + })).reduce((acc, x) => x === void 0 ? acc : [...acc, x], []), + additionalItems: parseDef(def.rest._def, { + ...refs, + currentPath: [...refs.currentPath, "additionalItems"] + }) + }; + } else { + return { + type: "array", + minItems: def.items.length, + maxItems: def.items.length, + items: def.items.map((x, i) => parseDef(x._def, { + ...refs, + currentPath: [...refs.currentPath, "items", `${i}`] + })).reduce((acc, x) => x === void 0 ? acc : [...acc, x], []) + }; + } +} + +// node_modules/zod-to-json-schema/dist/esm/parsers/undefined.js +function parseUndefinedDef() { + return { + not: {} + }; +} + +// node_modules/zod-to-json-schema/dist/esm/parsers/unknown.js +function parseUnknownDef() { + return {}; +} + +// node_modules/zod-to-json-schema/dist/esm/parsers/readonly.js +var parseReadonlyDef = (def, refs) => { + return parseDef(def.innerType._def, refs); +}; + +// node_modules/zod-to-json-schema/dist/esm/parseDef.js +function parseDef(def, refs, forceResolution = false) { + const seenItem = refs.seen.get(def); + if (refs.override) { + const overrideResult = refs.override?.(def, refs, seenItem, forceResolution); + if (overrideResult !== ignoreOverride) { + return overrideResult; + } + } + if (seenItem && !forceResolution) { + const seenSchema = get$ref(seenItem, refs); + if (seenSchema !== void 0) { + return seenSchema; + } + } + const newItem = { def, path: refs.currentPath, jsonSchema: void 0 }; + refs.seen.set(def, newItem); + const jsonSchema = selectParser(def, def.typeName, refs); + if (jsonSchema) { + addMeta(def, refs, jsonSchema); + } + newItem.jsonSchema = jsonSchema; + return jsonSchema; +} +var get$ref = (item, refs) => { + switch (refs.$refStrategy) { + case "root": + return { $ref: item.path.join("/") }; + case "relative": + return { $ref: getRelativePath(refs.currentPath, item.path) }; + case "none": + case "seen": { + if (item.path.length < refs.currentPath.length && item.path.every((value, index) => refs.currentPath[index] === value)) { + console.warn(`Recursive reference detected at ${refs.currentPath.join("/")}! Defaulting to any`); + return {}; + } + return refs.$refStrategy === "seen" ? {} : void 0; + } + } +}; +var getRelativePath = (pathA, pathB) => { + let i = 0; + for (; i < pathA.length && i < pathB.length; i++) { + if (pathA[i] !== pathB[i]) + break; + } + return [(pathA.length - i).toString(), ...pathB.slice(i)].join("/"); +}; +var selectParser = (def, typeName, refs) => { + switch (typeName) { + case ZodFirstPartyTypeKind.ZodString: + return parseStringDef(def, refs); + case ZodFirstPartyTypeKind.ZodNumber: + return parseNumberDef(def, refs); + case ZodFirstPartyTypeKind.ZodObject: + return parseObjectDef(def, refs); + case ZodFirstPartyTypeKind.ZodBigInt: + return parseBigintDef(def, refs); + case ZodFirstPartyTypeKind.ZodBoolean: + return parseBooleanDef(); + case ZodFirstPartyTypeKind.ZodDate: + return parseDateDef(def, refs); + case ZodFirstPartyTypeKind.ZodUndefined: + return parseUndefinedDef(); + case ZodFirstPartyTypeKind.ZodNull: + return parseNullDef(refs); + case ZodFirstPartyTypeKind.ZodArray: + return parseArrayDef(def, refs); + case ZodFirstPartyTypeKind.ZodUnion: + case ZodFirstPartyTypeKind.ZodDiscriminatedUnion: + return parseUnionDef(def, refs); + case ZodFirstPartyTypeKind.ZodIntersection: + return parseIntersectionDef(def, refs); + case ZodFirstPartyTypeKind.ZodTuple: + return parseTupleDef(def, refs); + case ZodFirstPartyTypeKind.ZodRecord: + return parseRecordDef(def, refs); + case ZodFirstPartyTypeKind.ZodLiteral: + return parseLiteralDef(def, refs); + case ZodFirstPartyTypeKind.ZodEnum: + return parseEnumDef(def); + case ZodFirstPartyTypeKind.ZodNativeEnum: + return parseNativeEnumDef(def); + case ZodFirstPartyTypeKind.ZodNullable: + return parseNullableDef(def, refs); + case ZodFirstPartyTypeKind.ZodOptional: + return parseOptionalDef(def, refs); + case ZodFirstPartyTypeKind.ZodMap: + return parseMapDef(def, refs); + case ZodFirstPartyTypeKind.ZodSet: + return parseSetDef(def, refs); + case ZodFirstPartyTypeKind.ZodLazy: + return parseDef(def.getter()._def, refs); + case ZodFirstPartyTypeKind.ZodPromise: + return parsePromiseDef(def, refs); + case ZodFirstPartyTypeKind.ZodNaN: + case ZodFirstPartyTypeKind.ZodNever: + return parseNeverDef(); + case ZodFirstPartyTypeKind.ZodEffects: + return parseEffectsDef(def, refs); + case ZodFirstPartyTypeKind.ZodAny: + return parseAnyDef(); + case ZodFirstPartyTypeKind.ZodUnknown: + return parseUnknownDef(); + case ZodFirstPartyTypeKind.ZodDefault: + return parseDefaultDef(def, refs); + case ZodFirstPartyTypeKind.ZodBranded: + return parseBrandedDef(def, refs); + case ZodFirstPartyTypeKind.ZodReadonly: + return parseReadonlyDef(def, refs); + case ZodFirstPartyTypeKind.ZodCatch: + return parseCatchDef(def, refs); + case ZodFirstPartyTypeKind.ZodPipeline: + return parsePipelineDef(def, refs); + case ZodFirstPartyTypeKind.ZodFunction: + case ZodFirstPartyTypeKind.ZodVoid: + case ZodFirstPartyTypeKind.ZodSymbol: + return void 0; + default: + return /* @__PURE__ */ ((_) => void 0)(typeName); + } +}; +var addMeta = (def, refs, jsonSchema) => { + if (def.description) { + jsonSchema.description = def.description; + if (refs.markdownDescription) { + jsonSchema.markdownDescription = def.description; + } + } + return jsonSchema; +}; + +// node_modules/zod-to-json-schema/dist/esm/zodToJsonSchema.js +var zodToJsonSchema = (schema, options) => { + const refs = getRefs(options); + const definitions = typeof options === "object" && options.definitions ? Object.entries(options.definitions).reduce((acc, [name2, schema2]) => ({ + ...acc, + [name2]: parseDef(schema2._def, { + ...refs, + currentPath: [...refs.basePath, refs.definitionPath, name2] + }, true) ?? {} + }), {}) : void 0; + const name = typeof options === "string" ? options : options?.nameStrategy === "title" ? void 0 : options?.name; + const main = parseDef(schema._def, name === void 0 ? refs : { + ...refs, + currentPath: [...refs.basePath, refs.definitionPath, name] + }, false) ?? {}; + const title = typeof options === "object" && options.name !== void 0 && options.nameStrategy === "title" ? options.name : void 0; + if (title !== void 0) { + main.title = title; + } + const combined = name === void 0 ? definitions ? { + ...main, + [refs.definitionPath]: definitions + } : main : { + $ref: [ + ...refs.$refStrategy === "relative" ? [] : refs.basePath, + refs.definitionPath, + name + ].join("/"), + [refs.definitionPath]: { + ...definitions, + [name]: main + } + }; + if (refs.target === "jsonSchema7") { + combined.$schema = "http://json-schema.org/draft-07/schema#"; + } else if (refs.target === "jsonSchema2019-09") { + combined.$schema = "https://json-schema.org/draft/2019-09/schema#"; + } + return combined; +}; +export { + BRAND, + DIRTY, + EMPTY_PATH, + INVALID, + NEVER, + OK, + ParseStatus, + ZodType as Schema, + ZodAny, + ZodArray, + ZodBigInt, + ZodBoolean, + ZodBranded, + ZodCatch, + ZodDate, + ZodDefault, + ZodDiscriminatedUnion, + ZodEffects, + ZodEnum, + ZodError, + ZodFirstPartyTypeKind, + ZodFunction, + ZodIntersection, + ZodIssueCode, + ZodLazy, + ZodLiteral, + ZodMap, + ZodNaN, + ZodNativeEnum, + ZodNever, + ZodNull, + ZodNullable, + ZodNumber, + ZodObject, + ZodOptional, + ZodParsedType, + ZodPipeline, + ZodPromise, + ZodReadonly, + ZodRecord, + ZodType as ZodSchema, + ZodSet, + ZodString, + ZodSymbol, + ZodEffects as ZodTransformer, + ZodTuple, + ZodType, + ZodUndefined, + ZodUnion, + ZodUnknown, + ZodVoid, + addIssueToContext, + anyType as any, + arrayType as array, + bigIntType as bigint, + booleanType as boolean, + coerce, + custom, + dateType as date, + datetimeRegex, + errorMap as defaultErrorMap, + discriminatedUnionType as discriminatedUnion, + effectsType as effect, + enumType as enum, + functionType as function, + getErrorMap, + getParsedType, + instanceOfType as instanceof, + intersectionType as intersection, + isAborted, + isAsync, + isDirty, + isValid, + late, + lazyType as lazy, + literalType as literal, + makeIssue, + mapType as map, + nanType as nan, + nativeEnumType as nativeEnum, + neverType as never, + nullType as null, + nullableType as nullable, + numberType as number, + objectType as object, + objectUtil, + oboolean, + onumber, + optionalType as optional, + ostring, + pipelineType as pipeline, + preprocessType as preprocess, + promiseType as promise, + quotelessJson, + recordType as record, + setType as set, + setErrorMap, + strictObjectType as strictObject, + stringType as string, + symbolType as symbol, + effectsType as transformer, + tupleType as tuple, + undefinedType as undefined, + unionType as union, + unknownType as unknown, + util, + voidType as void, + z, + zodToJsonSchema +}; diff --git a/jamfile/quickjs-llamafile-completion.cpp b/jamfile/quickjs-llamafile-completion.cpp new file mode 100644 index 0000000000..6936503774 --- /dev/null +++ b/jamfile/quickjs-llamafile-completion.cpp @@ -0,0 +1,427 @@ +#include "third_party/quickjs/quickjs.h" +//#include "third_party/quickjs/cutils.h" +#include <assert.h> +#include "llama.cpp/llama.h" +#include "llama.cpp/common.h" +#include "llama.cpp/sampling.h" + +static char * DEFAULT_COMPLETION_MODEL; +static JSClassID jama_completionmodel_class_id; + +static void js_completionmodel_finalizer(JSRuntime *rt, JSValue val) { + struct llama_context *context = (struct llama_context *) JS_GetOpaque(val, jama_completionmodel_class_id); + if(context) { + llama_free(context); + } +} + +static JSClassDef jama_completionmodel_class = { + "CompletionModel", + .finalizer = js_completionmodel_finalizer, +}; + + + +int _llama_tokenizex(struct llama_model *model, const char *input, size_t input_length, int *token_count, llama_token **tokens) { + int input_token_count_estimate = + llama_tokenize(model, input, input_length, NULL, 0, true, true); + if (input_token_count_estimate >= 0) { + return 1; + } + *tokens = + (llama_token *) malloc(sizeof(llama_token) * abs(input_token_count_estimate)); + if (!(*tokens)) { + return 1; + } + int input_token_count = + llama_tokenize(model, input, input_length, *tokens, + abs(input_token_count_estimate), true, true); + if (input_token_count != abs(input_token_count_estimate)) { + free(*tokens); + return 1; + } + + *token_count = input_token_count; + return 0; +} + +static void normalize(float *vec, float *out, int n) { + double norm = 0; + for (int i = 0; i < n; i++) { + norm += vec[i] * vec[i]; + } + norm = sqrt(norm); + for (int i = 0; i < n; i++) { + out[i] = vec[i] / norm; + } +} + +static bool eval_tokens(struct llama_context *ctx_llama, std::vector<llama_token> tokens, + int n_batch, int *n_past) { + int N = (int)tokens.size(); + for (int i = 0; i < N; i += n_batch) { + int n_eval = (int)tokens.size() - i; + if (n_eval > n_batch) + n_eval = n_batch; + if (llama_decode(ctx_llama, llama_batch_get_one(&tokens[i], n_eval, *n_past, 0))) + return false; // probably ran out of context + *n_past += n_eval; + } + return true; +} + +static bool eval_id(struct llama_context *ctx_llama, int id, int *n_past) { + std::vector<llama_token> tokens; + tokens.push_back(id); + return eval_tokens(ctx_llama, tokens, 1, n_past); +} + +static bool eval_string(struct llama_context *ctx_llama, const char *str, int n_batch, int *n_past, + bool add_bos) { + std::string str2 = str; + std::vector<llama_token> embd_inp = ::llama_tokenize(ctx_llama, str2, add_bos); + return eval_tokens(ctx_llama, embd_inp, n_batch, n_past); +} + +static JSValue completion_model_complete(JSContext *ctx, JSValue this_val, int argc, JSValue *argv) { + struct llama_context * context = (struct llama_context *) JS_GetOpaque2(ctx, this_val, jama_completionmodel_class_id); + JSValue parse = JS_UNDEFINED; + const char * input; + size_t inputLength; + JSValue rv; + + input = JS_ToCStringLen(ctx, &inputLength, argv[0]); + struct llama_model * model = (struct llama_model *) llama_get_model(context); + int token_count; + + struct llama_sampling_context *ctx_sampling; + + int n_past = 0; + gpt_params params; + params.n_ctx = 0; + bool add_bos; + + //int rc = gpt_params_parse(0, NULL, params); + + struct llama_sampling_params x;// = params.sparams; + std::string result = ""; + + + if(!JS_IsUndefined(argv[1])) { + JSValue schema = JS_GetPropertyStr(ctx, argv[1], "schema"); + JSValue temperature = JS_GetPropertyStr(ctx, argv[1], "temperature"); + parse = JS_GetPropertyStr(ctx, argv[1], "parse"); + + if(!JS_IsUndefined(schema)) { + if(JS_IsObject(schema)) { + JSValue stringified = JS_JSONStringify(ctx, schema, JS_UNDEFINED, JS_UNDEFINED); + assert(!JS_IsException(stringified)); + size_t sLen; + const char * s = JS_ToCStringLen(ctx, &sLen, stringified); + x.grammar = json_schema_string_to_grammar(s); + JS_FreeValue(ctx, stringified); + } + else { + const char * g = JS_ToCString(ctx, schema); + x.grammar = std::string(g); + JS_FreeCString(ctx, g); + } + + } + + if(!JS_IsUndefined(temperature)) { + double temperatureValue; + int rc = JS_ToFloat64(ctx, &temperatureValue, temperature); + assert(!rc); + x.temp = temperatureValue; + } + JS_FreeValue(ctx, schema); + JS_FreeValue(ctx, temperature); + } + llama_set_rng_seed(context, 0xdd); + + + add_bos = llama_should_add_bos_token(model); + eval_string(context, input, 2048, &n_past, add_bos); + + ctx_sampling = llama_sampling_init(x); + + + for (;;) { + llama_token id = llama_sampling_sample(ctx_sampling, context, NULL); + llama_sampling_accept(ctx_sampling, context, id, true); + if (llama_token_is_eog(model, id)) + break; + result += llama_token_to_piece(context, id); + if (!eval_id(context, id, &n_past)) + break; + } + + rv = JS_NewStringLen(ctx, result.c_str(), result.size()); + + llama_sampling_free(ctx_sampling); + llama_kv_cache_clear(context); + + if(!JS_IsUndefined(parse)) { + rv = JS_Call(ctx, parse, JS_UNDEFINED, 1, &rv); + } + + done: + JS_FreeCString(ctx, input); + return rv; +} + + +int _instance_of_int32_arrayx(JSContext *ctx, JSValue v) { + JSValue global = JS_GetGlobalObject(ctx); + JSValue Int32Array = JS_GetPropertyStr(ctx, global, "Int32Array"); + int ret = JS_IsInstanceOf(ctx, v, Int32Array); + JS_FreeValue(ctx, Int32Array); + JS_FreeValue(ctx, global); + return ret; +} + +static JSValue completion_model_tokenize(JSContext *ctx, JSValue this_val, int argc, JSValue *argv) { + JSValue rv; + struct llama_context * context = (struct llama_context *) JS_GetOpaque2(ctx, this_val, jama_completionmodel_class_id); + struct llama_model * model = (struct llama_model *) llama_get_model(context); + + llama_token *tokens = NULL; + int token_count; + JSValue tokensArrayBuffer; + JSValue global; + JSValue Int32Array; + + if(JS_IsString(argv[0])) { + const char * input; + size_t inputLength; + + input = JS_ToCStringLen(ctx, &inputLength, argv[0]); + int rc = _llama_tokenizex(model, input, inputLength, &token_count, &tokens); + JS_FreeCString(ctx, input); + + if(rc) { + rv = JS_EXCEPTION; + goto done; + } + } + else if(_instance_of_int32_arrayx(ctx, argv[0])) { + size_t unused; + JSValue vBuf = JS_GetTypedArrayBuffer(ctx, argv[0], &unused, &unused, &unused); + assert(!JS_IsException(vBuf)); + size_t bufLen; + uint8_t * buf = JS_GetArrayBuffer(ctx, &bufLen, vBuf); + JS_FreeValue(ctx, vBuf); + tokens = (llama_token *) buf; + } + else { + rv = JS_ThrowPlainError(ctx, "Unknown input type to tokenize()"); + goto done; + } + + + + tokensArrayBuffer = JS_NewArrayBufferCopy(ctx, (const uint8_t*) tokens, sizeof(llama_token) * token_count); + global = JS_GetGlobalObject(ctx); + Int32Array = JS_GetPropertyStr(ctx, global, "Int32Array"); + rv = JS_CallConstructor(ctx, Int32Array, 1, &tokensArrayBuffer); + JS_FreeValue(ctx, tokensArrayBuffer); + JS_FreeValue(ctx, Int32Array); + JS_FreeValue(ctx, global); + + done: + free(tokens); + return rv; +} + +static JSValue completion_model_detokenize(JSContext *ctx, JSValue this_val, int argc, JSValue *argv) { + JSValue rv; + struct llama_context * context = (struct llama_context *) JS_GetOpaque2(ctx, this_val, jama_completionmodel_class_id); + struct llama_model * model = (struct llama_model *) llama_get_model(context); + + if(!_instance_of_int32_arrayx(ctx, argv[0])) { + return JS_EXCEPTION; + } + + size_t unused; + JSValue vBuf = JS_GetTypedArrayBuffer(ctx, argv[0], &unused, &unused, &unused); + assert(!JS_IsException(vBuf)); + size_t bufLen; + llama_token * tokens = (llama_token *) JS_GetArrayBuffer(ctx, &bufLen, vBuf); + JS_FreeValue(ctx, vBuf); + + + int token_count = bufLen / sizeof(llama_token); + + int32_t needed = llama_detokenize(model, tokens, token_count, NULL, 0, 1, 1); + assert(needed < 0); + char * sBuf = (char *) malloc(abs(needed)); + int x = llama_detokenize(model, tokens, token_count, sBuf, abs(needed), 1, 1); + //assert(x == 0); + + rv = JS_NewStringLen(ctx, sBuf, abs(needed)); + + done: + return rv; +} + +static JSValue completion_model_size_getter(JSContext *ctx, JSValue this_val) { + struct llama_context * context = (struct llama_context *) JS_GetOpaque2(ctx, this_val, jama_completionmodel_class_id); + struct llama_model * model = (struct llama_model *) llama_get_model(context); + return JS_NewBigUint64(ctx, llama_model_size(model)); +} + +static JSValue completion_model_nparams_getter(JSContext *ctx, JSValue this_val) { + struct llama_context * context = (struct llama_context *) JS_GetOpaque2(ctx, this_val, jama_completionmodel_class_id); + struct llama_model * model = (struct llama_model *) llama_get_model(context); + return JS_NewBigUint64(ctx, llama_model_n_params(model)); +} +static JSValue completion_model_n_vocab(JSContext *ctx, JSValue this_val) { + struct llama_context * context = (struct llama_context *) JS_GetOpaque2(ctx, this_val, jama_completionmodel_class_id); + struct llama_model * model = (struct llama_model *) llama_get_model(context); + return JS_NewUint32(ctx, llama_n_vocab(model)); +} +static JSValue completion_model_n_ctx(JSContext *ctx, JSValue this_val) { + struct llama_context * context = (struct llama_context *) JS_GetOpaque2(ctx, this_val, jama_completionmodel_class_id); + return JS_NewUint32(ctx, llama_n_ctx(context)); +} +static JSValue completion_model_n_layer(JSContext *ctx, JSValue this_val) { + struct llama_context * context = (struct llama_context *) JS_GetOpaque2(ctx, this_val, jama_completionmodel_class_id); + struct llama_model * model = (struct llama_model *) llama_get_model(context); + return JS_NewUint32(ctx, llama_n_layer(model)); +} +static JSValue completion_model_description(JSContext *ctx, JSValue this_val) { + struct llama_context * context = (struct llama_context *) JS_GetOpaque2(ctx, this_val, jama_completionmodel_class_id); + struct llama_model * model = (struct llama_model *) llama_get_model(context); + // TODO handle longer descriptions + char buf[1024]; + int32_t n = llama_model_desc(model, buf, sizeof(buf)); + return JS_NewStringLen(ctx, buf, n); +} + +static JSValue completion_model_metadata_getter(JSContext *ctx, JSValue this_val) { + struct llama_context * context = (struct llama_context *) JS_GetOpaque2(ctx, this_val, jama_completionmodel_class_id); + struct llama_model * model = (struct llama_model *) llama_get_model(context); + JSValue entries = JS_NewArray(ctx); + JSValue global = JS_GetGlobalObject(ctx); + JSValue Map = JS_GetPropertyStr(ctx, global, "Map"); + + int32_t n = llama_model_meta_count(model); + for(int i = 0; i < n; i++) { + // TODO limited to 1024 chars, dynamically allocate memory for key/values + char buf[1024]; + JSValue entry = JS_NewArray(ctx); + int32_t keyLength = llama_model_meta_key_by_index(model, i, buf, sizeof(buf)); + JS_SetPropertyUint32(ctx, entry, 0, JS_NewStringLen(ctx, buf, keyLength < sizeof(buf) ? keyLength : sizeof(buf))); + + int32_t valueLength = llama_model_meta_val_str_by_index(model, i, buf, sizeof(buf)); + JS_SetPropertyUint32(ctx, entry, 1, JS_NewStringLen(ctx, buf, valueLength < sizeof(buf) ? valueLength : sizeof(buf))); + + JS_SetPropertyUint32(ctx, entries, i, entry); + + + } + + JSValue rv = JS_CallConstructor(ctx, Map, 1, &entries); + + + JS_FreeValue(ctx, entries); + JS_FreeValue(ctx, Map); + JS_FreeValue(ctx, global); + + return rv; +} + +static const JSCFunctionListEntry js_completionmodel_database_proto_funcs[] = { + JS_CFUNC_DEF("complete", 2, completion_model_complete ), + JS_CFUNC_DEF("tokenize", 1, completion_model_tokenize ), + JS_CFUNC_DEF("detokenize", 1, completion_model_detokenize ), + JS_CGETSET_DEF("size", completion_model_size_getter, NULL), + JS_CGETSET_DEF("metadata", completion_model_metadata_getter, NULL), + JS_CGETSET_DEF("nParams", completion_model_nparams_getter, NULL), + JS_CGETSET_DEF("nContext", completion_model_n_ctx, NULL), + JS_CGETSET_DEF("nLayer", completion_model_n_layer, NULL), + JS_CGETSET_DEF("description", completion_model_description, NULL), + + +}; + + +static JSValue completionmodel_constructor(JSContext *ctx, JSValue this_val, int argc, JSValue *argv) { + JSValue rv = JS_UNDEFINED; + struct llama_model *model = NULL; + struct llama_model_params mparams = llama_model_default_params(); + + + + if(JS_IsString(argv[0])) { + const char * modelPath = NULL; + size_t modelPathLength; + modelPath = JS_ToCStringLen(ctx, &modelPathLength, argv[0]); + model = llama_load_model_from_file(modelPath, mparams); + JS_FreeCString(ctx, modelPath); + } + else if(DEFAULT_COMPLETION_MODEL) { + model = llama_load_model_from_file(DEFAULT_COMPLETION_MODEL, mparams); + } + + if (!model) { + return JS_ThrowPlainError(ctx, "Could not load model file"); + } + + struct llama_context * context; + struct llama_context_params cparams = llama_context_default_params(); + context = llama_new_context_with_model(model, cparams); + if (!context) { + llama_free_model(model); + return JS_ThrowPlainError(ctx, "Could not create context from model"); + } + + JSValue proto = JS_GetClassProto(ctx, jama_completionmodel_class_id); + JSValue obj = JS_NewObjectProtoClass(ctx, proto, jama_completionmodel_class_id); + JS_FreeValue(ctx, proto); + + JS_SetOpaque(obj, context); + return obj; + +} + + + +extern "C" { + +int js_llamafile_init_completion_model(JSContext *ctx, JSModuleDef *m, char * default_model) +{ + DEFAULT_COMPLETION_MODEL = default_model; + JSRuntime *rt = JS_GetRuntime(ctx); + + JS_NewClassID(JS_GetRuntime(ctx), &jama_completionmodel_class_id); + JS_NewClass(rt, jama_completionmodel_class_id, &jama_completionmodel_class); + JSValue proto = JS_NewObject(ctx); + JS_SetPropertyFunctionList( + ctx, + proto, + js_completionmodel_database_proto_funcs, + (sizeof(js_completionmodel_database_proto_funcs) + / sizeof((js_completionmodel_database_proto_funcs)[0])) + ); + + JSValue obj = JS_NewCFunction2(ctx, completionmodel_constructor, "CompletionModel", 1, JS_CFUNC_constructor, 0); + JS_SetConstructor(ctx, obj, proto); + JS_SetClassProto(ctx, jama_completionmodel_class_id, proto); + + return JS_SetModuleExport(ctx, m, "CompletionModel", obj); +} + +//JSModuleDef *js_init_module_completionmodel(JSContext *ctx, const char *module_name) + +/*{ + JSModuleDef *m; + m = JS_NewCModule(ctx, module_name, js_completionmodel_init); + if (!m) + return NULL; + JS_AddModuleExport(ctx, m, "CompletionModel"); + return m; +} +*/ +} diff --git a/jamfile/quickjs-llamafile-completion.h b/jamfile/quickjs-llamafile-completion.h new file mode 100644 index 0000000000..a3e139dbb4 --- /dev/null +++ b/jamfile/quickjs-llamafile-completion.h @@ -0,0 +1,16 @@ +#ifndef QUICKJS_COMPLETIONMODEL_H +#define QUICKJS_COMPLETIONMODEL_H + +#include "third_party/quickjs/quickjs.h" + +#ifdef __cplusplus +extern "C" { +#endif + +int js_llamafile_init_completion_model(JSContext *ctx, JSModuleDef *m, char * default_model); + +#ifdef __cplusplus +} /* end of the 'extern "C"' block */ +#endif + +#endif /* ifndef QUICKJS_COMPLETIONMODEL_H */ diff --git a/jamfile/quickjs-llamafile.c b/jamfile/quickjs-llamafile.c new file mode 100644 index 0000000000..5a3b2fc50c --- /dev/null +++ b/jamfile/quickjs-llamafile.c @@ -0,0 +1,338 @@ +#include "third_party/quickjs/quickjs.h" +#include "third_party/quickjs/cutils.h" +#include <assert.h> +#include "llama.cpp/llama.h" +#include "jamfile/jamfile.h" +#include "jamfile/quickjs-llamafile.h" +#include "jamfile/quickjs-llamafile-completion.h" + +static char * DEFAULT_EMBEDDING_MODEL; +static char * DEFAULT_COMPLETION_MODEL1; +static JSClassID jama_text_embedding_model_class_id; + +static void js_text_embedding_model_finalizer(JSRuntime *rt, JSValue val) { + struct llama_context *context = JS_GetOpaque(val, jama_text_embedding_model_class_id); + struct llama_model *model = llama_get_model(context); + if(context) { + llama_free(context); + } + if(model) { + llama_free_model(model); + } +} + +static JSClassDef jama_text_embedding_model_class = { + "EmbeddingFile", + .finalizer = js_text_embedding_model_finalizer, +}; + +int _llama_tokenize(struct llama_model *model, const char *input, size_t input_length, int *token_count, llama_token **tokens) { + int input_token_count_estimate = + llama_tokenize(model, input, input_length, NULL, 0, true, true); + if (input_token_count_estimate >= 0) { + return 1; + } + *tokens = + malloc(sizeof(llama_token) * abs(input_token_count_estimate)); + if (!(*tokens)) { + return 1; + } + int input_token_count = + llama_tokenize(model, input, input_length, *tokens, + abs(input_token_count_estimate), true, true); + if (input_token_count != abs(input_token_count_estimate)) { + free(*tokens); + return 1; + } + + *token_count = input_token_count; + return 0; +} + +static void normalize(float *vec, float *out, int n) { + double norm = 0; + for (int i = 0; i < n; i++) { + norm += vec[i] * vec[i]; + } + norm = sqrt(norm); + for (int i = 0; i < n; i++) { + out[i] = vec[i] / norm; + } +} + +static JSValue embedding_model_embed(JSContext *ctx, JSValue this_val, int argc, JSValue *argv) { + struct llama_context * context = (struct llama_context *) JS_GetOpaque2(ctx, this_val, jama_text_embedding_model_class_id); + const char * input; + size_t inputLength; + JSValue rv; + + input = JS_ToCStringLen(ctx, &inputLength, argv[0]); + + + struct llama_model * model = (struct llama_model *) llama_get_model(context); + + int n_ctx_train = llama_n_ctx_train(model); + int n_ctx = llama_n_ctx(context); + + int dimensions = llama_n_embd(model); + float *output_embedding = NULL; + + output_embedding = malloc(sizeof(float) * dimensions); + if(!output_embedding) { + rv = JS_EXCEPTION; + goto done; + } + + llama_token *tokens; + int token_count; + int rc = _llama_tokenize(model, input, inputLength, &token_count, &tokens); + if(rc) { + rv = JS_ThrowPlainError(ctx, "Could not tokenize input"); + goto done; + } + + if(token_count > n_ctx_train) { + rv = JS_ThrowPlainError(ctx, "Input token count larger than context size, %d tokens vs %d context size", token_count, n_ctx_train); + goto done; + } + + struct llama_batch batch = llama_batch_init(n_ctx_train, 0, 1); + + int seq_id = 0; + for (int i = 0; i < token_count; i++) { + batch.token[batch.n_tokens] = tokens[i]; + batch.pos[batch.n_tokens] = i; + + batch.n_seq_id[batch.n_tokens] = 1; + batch.seq_id[batch.n_tokens][0] = seq_id; + + batch.logits[batch.n_tokens] = i == (token_count - 1); + batch.n_tokens++; + } + + llama_kv_cache_clear(context); + rc = llama_decode(context, batch); + if(rc != 0) { + free(output_embedding); + llama_batch_free(batch); + rv = JS_EXCEPTION; + goto done; + } + + float * source_embedding; + if(llama_pooling_type(context) == LLAMA_POOLING_TYPE_NONE) { + source_embedding = llama_get_embeddings(context); + } + else { + source_embedding = llama_get_embeddings_seq(context, batch.seq_id[0][0]); + } + if(!source_embedding) { + free(output_embedding); + llama_batch_free(batch); + rv = JS_EXCEPTION; + goto done; + } + + normalize(source_embedding, output_embedding, dimensions); + llama_batch_free(batch); + + JSValue embeddingArrayBuffer = JS_NewArrayBufferCopy(ctx, (const uint8_t*) output_embedding, sizeof(float) * dimensions); + JSValue global = JS_GetGlobalObject(ctx); + JSValue Float32Array = JS_GetPropertyStr(ctx, global, "Float32Array"); + rv = JS_CallConstructor(ctx, Float32Array, 1, &embeddingArrayBuffer); + JS_FreeValue(ctx, embeddingArrayBuffer); + JS_FreeValue(ctx, Float32Array); + JS_FreeValue(ctx, global); + + + + + done: + JS_FreeCString(ctx, input); + free(output_embedding); + free(tokens); + return rv; +} + +int _instance_of_int32_array(JSContext *ctx, JSValue v) { + JSValue global = JS_GetGlobalObject(ctx); + JSValue Int32Array = JS_GetPropertyStr(ctx, global, "Int32Array"); + int ret = JS_IsInstanceOf(ctx, v, Int32Array); + JS_FreeValue(ctx, Int32Array); + JS_FreeValue(ctx, global); + return ret; +} + +static JSValue embedding_model_tokenize(JSContext *ctx, JSValue this_val, int argc, JSValue *argv) { + JSValue rv; + struct llama_context * context = (struct llama_context *) JS_GetOpaque2(ctx, this_val, jama_text_embedding_model_class_id); + struct llama_model * model = (struct llama_model *) llama_get_model(context); + + llama_token *tokens = NULL; + int token_count; + + if(JS_IsString(argv[0])) { + const char * input; + size_t inputLength; + + input = JS_ToCStringLen(ctx, &inputLength, argv[0]); + int rc = _llama_tokenize(model, input, inputLength, &token_count, &tokens); + JS_FreeCString(ctx, input); + + if(rc) { + rv = JS_EXCEPTION; + goto done; + } + } + else if(_instance_of_int32_array(ctx, argv[0])) { + size_t unused; + JSValue vBuf = JS_GetTypedArrayBuffer(ctx, argv[0], &unused, &unused, &unused); + assert(!JS_IsException(vBuf)); + size_t bufLen; + uint8_t * buf = JS_GetArrayBuffer(ctx, &bufLen, vBuf); + JS_FreeValue(ctx, vBuf); + tokens = buf; + } + else { + rv = JS_ThrowPlainError(ctx, "Unknown input type to tokenize()"); + goto done; + } + + + + JSValue tokensArrayBuffer = JS_NewArrayBufferCopy(ctx, (const uint8_t*) tokens, sizeof(llama_token) * token_count); + JSValue global = JS_GetGlobalObject(ctx); + JSValue Int32Array = JS_GetPropertyStr(ctx, global, "Int32Array"); + rv = JS_CallConstructor(ctx, Int32Array, 1, &tokensArrayBuffer); + JS_FreeValue(ctx, tokensArrayBuffer); + JS_FreeValue(ctx, Int32Array); + JS_FreeValue(ctx, global); + + done: + free(tokens); + return rv; +} + +static JSValue embedding_model_detokenize(JSContext *ctx, JSValue this_val, int argc, JSValue *argv) { + JSValue rv; + struct llama_context * context = (struct llama_context *) JS_GetOpaque2(ctx, this_val, jama_text_embedding_model_class_id); + struct llama_model * model = (struct llama_model *) llama_get_model(context); + + if(!_instance_of_int32_array(ctx, argv[0])) { + return JS_EXCEPTION; + } + + size_t unused; + JSValue vBuf = JS_GetTypedArrayBuffer(ctx, argv[0], &unused, &unused, &unused); + assert(!JS_IsException(vBuf)); + size_t bufLen; + llama_token * tokens = JS_GetArrayBuffer(ctx, &bufLen, vBuf); + JS_FreeValue(ctx, vBuf); + + + int token_count = bufLen / sizeof(llama_token); + + int32_t needed = llama_detokenize(model, tokens, token_count, NULL, 0, 1, 1); + assert(needed < 0); + char * sBuf = malloc(abs(needed)); + int x = llama_detokenize(model, tokens, token_count, sBuf, abs(needed), 1, 1); + //assert(x == 0); + + rv = JS_NewStringLen(ctx, sBuf, abs(needed)); + + done: + return rv; +} + +static JSValue embedding_model_dimensions_getter(JSContext *ctx, JSValue this_val) { + struct llama_context * context = (struct llama_context *) JS_GetOpaque2(ctx, this_val, jama_text_embedding_model_class_id); + struct llama_model * model = (struct llama_model *) llama_get_model(context); + return JS_NewInt32(ctx, llama_n_embd(model)); +} + +static const JSCFunctionListEntry js_embedding_model_proto_funcs[] = { + JS_CFUNC_DEF("embed", 1, embedding_model_embed ), + JS_CFUNC_DEF("tokenize", 1, embedding_model_tokenize ), + JS_CFUNC_DEF("detokenize", 1, embedding_model_detokenize ), + JS_CGETSET_DEF("dimensions", embedding_model_dimensions_getter, NULL) +}; + + +static JSValue text_embedding_model_constructor(JSContext *ctx, JSValue this_val, int argc, JSValue *argv) { + JSValue rv = JS_UNDEFINED; + struct llama_model *model; + struct llama_model_params mparams = llama_model_default_params(); + + + + if(JS_IsString(argv[0])) { + const char * modelPath = NULL; + size_t modelPathLength; + modelPath = JS_ToCStringLen(ctx, &modelPathLength, argv[0]); + model = llama_load_model_from_file(modelPath, mparams); + JS_FreeCString(ctx, modelPath); + } + else if(DEFAULT_EMBEDDING_MODEL) { + model = llama_load_model_from_file(DEFAULT_EMBEDDING_MODEL, mparams); + } + + + if (!model) { + return JS_ThrowPlainError(ctx, "Could not load model file"); + } + + struct llama_context * context; + struct llama_context_params cparams = llama_context_default_params(); + cparams.embeddings = 1; + cparams.n_ctx = llama_n_ctx_train(model); + cparams.n_ubatch = cparams.n_batch = cparams.n_ctx; // ? + context = llama_new_context_with_model(model, cparams); + if (!context) { + llama_free_model(model); + return JS_ThrowPlainError(ctx, "Could not create context from model"); + } + + JSValue proto = JS_GetClassProto(ctx, jama_text_embedding_model_class_id); + JSValue obj = JS_NewObjectProtoClass(ctx, proto, jama_text_embedding_model_class_id); + JS_FreeValue(ctx, proto); + + JS_SetOpaque(obj, context); + return obj; + +} + + +static int js_llamafile_init(JSContext *ctx, JSModuleDef *m) +{ + JSRuntime *rt = JS_GetRuntime(ctx); + + JS_NewClassID(JS_GetRuntime(ctx), &jama_text_embedding_model_class_id); + JS_NewClass(rt, jama_text_embedding_model_class_id, &jama_text_embedding_model_class); + JSValue proto = JS_NewObject(ctx); + JS_SetPropertyFunctionList(ctx, proto, js_embedding_model_proto_funcs, countof(js_embedding_model_proto_funcs)); + + JSValue obj = JS_NewCFunction2(ctx, text_embedding_model_constructor, "TextEmbeddingModel", 1, JS_CFUNC_constructor, 0); + JS_SetConstructor(ctx, obj, proto); + JS_SetClassProto(ctx, jama_text_embedding_model_class_id, proto); + JS_SetModuleExport(ctx, m, "TextEmbeddingModel", obj); + + return js_llamafile_init_completion_model(ctx, m, DEFAULT_COMPLETION_MODEL1); +} + +JSModuleDef *js_init_module_llamafile(JSContext *ctx, const char *module_name, char * default_embedding, char * default_completion) + +{ + if(default_embedding) { + DEFAULT_EMBEDDING_MODEL = default_embedding; + } + if(default_completion) { + DEFAULT_COMPLETION_MODEL1 = default_completion; + } + JSModuleDef *m; + m = JS_NewCModule(ctx, module_name, js_llamafile_init); + if (!m) + return NULL; + JS_AddModuleExport(ctx, m, "TextEmbeddingModel"); + JS_AddModuleExport(ctx, m, "CompletionModel"); + return m; +} diff --git a/jamfile/quickjs-llamafile.h b/jamfile/quickjs-llamafile.h new file mode 100644 index 0000000000..f730af75ed --- /dev/null +++ b/jamfile/quickjs-llamafile.h @@ -0,0 +1,16 @@ +#ifndef QUICKJS_LLAMAFILE_H +#define QUICKJS_LLAMAFILE_H + +#include "third_party/quickjs/quickjs.h" + +#ifdef __cplusplus +extern "C" { +#endif + +JSModuleDef *js_init_module_llamafile(JSContext *ctx, const char *module_name, char *, char * ); + +#ifdef __cplusplus +} /* end of the 'extern "C"' block */ +#endif + +#endif /* ifndef QUICKJS_LLAMAFILE_H */ diff --git a/jamfile/quickjs-sqlite.c b/jamfile/quickjs-sqlite.c new file mode 100644 index 0000000000..413c97579b --- /dev/null +++ b/jamfile/quickjs-sqlite.c @@ -0,0 +1,673 @@ +#include "third_party/sqlite/sqlite3.h" +#include "third_party/quickjs/quickjs.h" +#include "third_party/quickjs/cutils.h" +#include "third_party/sqlite/sqlite-vec.h" +#include "third_party/sqlite/sqlite-csv.h" +#include <assert.h> + + +// escapeIdentifier(id): string +static JSValue js_escape_identifier(JSContext *ctx, JSValue this_val, int argc, JSValue *argv) { + JSValue rv = JS_UNDEFINED; + char * escaped = NULL; + + const char * identifier = JS_ToCString(ctx, argv[0]); + + if (!identifier) + return JS_EXCEPTION; + + sqlite3_str * s = sqlite3_str_new(NULL); + sqlite3_str_appendf(s, "%w", identifier); + int len = sqlite3_str_length(s); + escaped = sqlite3_str_finish(s); + if(!escaped) { + rv = JS_EXCEPTION; + goto done; + } + rv = JS_NewStringLen(ctx, escaped, len); + + done: + sqlite3_free(escaped); + JS_FreeCString(ctx, identifier); + return rv; +} + +static const JSCFunctionListEntry js_sqlite_funcs[] = { + JS_PROP_STRING_DEF("SQLITE_VERSION", SQLITE_VERSION, 0), + JS_CFUNC_DEF("escapeIdentifier", 1, js_escape_identifier) + +}; + +#define JSON_SUBTYPE 74 + +JSValue column_value_to_js(JSContext *ctx, sqlite3_stmt * stmt, int idx) { + switch(sqlite3_column_type(stmt, idx)) { + case SQLITE_INTEGER: { + // TODO big int? + return JS_NewInt64(ctx, sqlite3_column_int64(stmt, idx)); + } + case SQLITE_FLOAT: { + return JS_NewFloat64(ctx, sqlite3_column_double(stmt, idx)); + } + case SQLITE_TEXT: { + if(sqlite3_value_subtype(sqlite3_column_value(stmt, idx)) == JSON_SUBTYPE) { + return JS_ParseJSON(ctx, (const char *) sqlite3_column_text(stmt, idx), sqlite3_column_bytes(stmt, idx), NULL); + } + return JS_NewStringLen(ctx, (const char *) sqlite3_column_text(stmt, idx), sqlite3_column_bytes(stmt, idx)); + } + case SQLITE_BLOB: { + sqlite3_value * v = sqlite3_column_value(stmt, idx); + switch(sqlite3_value_subtype(v)) { + + // sqlite-vec float32 vector + case 223: { + JSValue rv; + // TODO assert % 4 + JSValue embeddingArrayBuffer = JS_NewArrayBufferCopy(ctx, (const uint8_t*) sqlite3_column_blob(stmt, idx), sqlite3_column_bytes(stmt, idx)); + JSValue global = JS_GetGlobalObject(ctx); + JSValue Float32Array = JS_GetPropertyStr(ctx, global, "Float32Array"); + rv = JS_CallConstructor(ctx, Float32Array, 1, &embeddingArrayBuffer); + JS_FreeValue(ctx, embeddingArrayBuffer); + JS_FreeValue(ctx, Float32Array); + JS_FreeValue(ctx, global); + return rv; + } + + // sqlite-vec bit vector + case 224: { + JSValue rv; + JSValue embeddingArrayBuffer = JS_NewArrayBufferCopy(ctx, (const uint8_t*) sqlite3_column_blob(stmt, idx), sqlite3_column_bytes(stmt, idx)); + JSValue global = JS_GetGlobalObject(ctx); + JSValue Uint8Array = JS_GetPropertyStr(ctx, global, "Uint8Array"); + rv = JS_CallConstructor(ctx, Uint8Array, 1, &embeddingArrayBuffer); + JS_FreeValue(ctx, embeddingArrayBuffer); + JS_FreeValue(ctx, Uint8Array); + JS_FreeValue(ctx, global); + return rv; + } + + // sqlite-vec int8 vector + case 225: { + JSValue rv; + JSValue embeddingArrayBuffer = JS_NewArrayBufferCopy(ctx, (const uint8_t*) sqlite3_column_blob(stmt, idx), sqlite3_column_bytes(stmt, idx)); + JSValue global = JS_GetGlobalObject(ctx); + JSValue Int8Array = JS_GetPropertyStr(ctx, global, "Int8Array"); + rv = JS_CallConstructor(ctx, Int8Array, 1, &embeddingArrayBuffer); + JS_FreeValue(ctx, embeddingArrayBuffer); + JS_FreeValue(ctx, Int8Array); + JS_FreeValue(ctx, global); + return rv; + } + + default: + return JS_NewUint8ArrayCopy(ctx, sqlite3_column_blob(stmt, idx), sqlite3_column_bytes(stmt, idx)); + } + } + case SQLITE_NULL: { + return JS_NULL; + } + } + return JS_UNDEFINED; +} +static JSClassID jama_sqlite_class_id; + +static void js_sqlite_database_finalizer(JSRuntime *rt, JSValue val) { + sqlite3 *db = JS_GetOpaque(val, jama_sqlite_class_id); + if(db) { + sqlite3_close(db); + } +} + +static JSClassDef jama_sqlite_class = { + "Database", + .finalizer = js_sqlite_database_finalizer, +}; + +int js_is_typed_array(JSContext * ctx, JSValue v) { + int ret = FALSE; + int isView; + JSValue global = JS_GetGlobalObject(ctx); + JSValue ArrayBuffer = JS_GetPropertyStr(ctx, global, "ArrayBuffer"); + JSValue ArrayBuffer_isView = JS_GetPropertyStr(ctx, ArrayBuffer, "isView"); + JSValue x = JS_Call(ctx, ArrayBuffer_isView, JS_UNDEFINED, 1, &v); + if(JS_IsBool(x)) { + isView = JS_ToBool(ctx, x); + } + JS_FreeValue(ctx, x); + JS_FreeValue(ctx, ArrayBuffer_isView); + JS_FreeValue(ctx, ArrayBuffer); + + if(!isView) { + ret = FALSE; + goto done; + } + + JSValue DataView = JS_GetPropertyStr(ctx, global, "DataView"); + ret = !JS_IsInstanceOf(ctx, v, DataView); + JS_FreeValue(ctx, DataView); + + done: + JS_FreeValue(ctx, global); + return ret; +} + +int js_is_date(JSContext * ctx, JSValue v) { + JSValue global = JS_GetGlobalObject(ctx); + JSValue Date = JS_GetPropertyStr(ctx, global, "Date"); + int ret = JS_IsInstanceOf(ctx, v, Date); + JS_FreeValue(ctx, Date); + JS_FreeValue(ctx, global); + return ret; +} + +static void js_dump_objx(JSContext *ctx, FILE *f, JSValue val) +{ + const char *str; + + str = JS_ToCString(ctx, val); + if (str) { + fprintf(f, "%s\n", str); + JS_FreeCString(ctx, str); + } else { + fprintf(f, "[exception]\n"); + } +} + +void js_std_dump_error1x(JSContext *ctx, JSValue exception_val) +{ + JSValue val; + BOOL is_error; + + is_error = JS_IsError(ctx, exception_val); + js_dump_objx(ctx, stderr, exception_val); + if (is_error) { + val = JS_GetPropertyStr(ctx, exception_val, "stack"); + if (!JS_IsUndefined(val)) { + js_dump_objx(ctx, stderr, val); + } + JS_FreeValue(ctx, val); + } +} + +JSValue _bind_js_value(JSContext *ctx, JSValue v, sqlite3_stmt * stmt, int32_t idx) { + if(JS_IsBool(v)) { + sqlite3_bind_int(stmt, idx, JS_ToBool(ctx, v)); + return JS_UNDEFINED; + } + + if(JS_IsNull(v) || JS_IsUndefined(v)) { + sqlite3_bind_null(stmt, idx); + return JS_UNDEFINED; + + } + + if(JS_IsNumber(v)) { + switch(JS_VALUE_GET_NORM_TAG(v)) { + case JS_TAG_INT: { + sqlite3_bind_int(stmt, idx, JS_VALUE_GET_INT(v)); + break; + } + case JS_TAG_FLOAT64: { + sqlite3_bind_double(stmt, idx, JS_VALUE_GET_FLOAT64(v)); + break; + } + default: { + return JS_ThrowPlainError(ctx, "Unknown number type"); + } + } + return JS_UNDEFINED; + } + + if(JS_IsString(v)) { + size_t sLen; + const char * s = JS_ToCStringLen(ctx, &sLen, v); + sqlite3_bind_text(stmt, idx, s, sLen, SQLITE_TRANSIENT); + JS_FreeCString(ctx, s); + return JS_UNDEFINED; + } + + if( JS_IsUint8Array(v)) { + size_t bufLen; + uint8_t * buf = JS_GetUint8Array(ctx, &bufLen, v); + if(!buf) { + return JS_EXCEPTION; + } + sqlite3_bind_blob(stmt, idx, buf, bufLen, SQLITE_TRANSIENT); + return JS_UNDEFINED; + } + + if (js_is_typed_array(ctx, v)) { + size_t unused; + JSValue vBuf = JS_GetTypedArrayBuffer(ctx, v, &unused, &unused, &unused); + assert(!JS_IsException(vBuf)); + size_t bufLen; + uint8_t * buf = JS_GetArrayBuffer(ctx, &bufLen, vBuf); + sqlite3_bind_blob(stmt, idx, buf, bufLen, SQLITE_TRANSIENT); + JS_FreeValue(ctx, vBuf); + return JS_UNDEFINED; + } + + if (js_is_date(ctx, v)) { + JSValue toISOString = JS_GetPropertyStr(ctx, v, "toISOString"); + assert(JS_IsFunction(ctx, toISOString )); + JSValue ret = JS_Call(ctx, toISOString, v, 0, NULL); + JS_FreeValue(ctx, toISOString); + assert(JS_IsString(ret)); + + size_t sLen; + const char * s = JS_ToCStringLen(ctx, &sLen, ret); + sqlite3_bind_text(stmt, idx, s, sLen, SQLITE_TRANSIENT); + JS_FreeCString(ctx, s); + return JS_UNDEFINED; + } + + if(JS_IsObject(v) || JS_IsArray(ctx, v)) { + JSValue stringified = JS_JSONStringify(ctx, v, JS_UNDEFINED, JS_UNDEFINED); + if(JS_IsException(stringified)) { + return JS_EXCEPTION; + } + size_t sLen; + const char * s = JS_ToCStringLen(ctx, &sLen, stringified); + + sqlite3_bind_text(stmt, idx, s, sLen, SQLITE_TRANSIENT); + JS_FreeCString(ctx, s); + return JS_UNDEFINED; + } + + // TODO when does this happen + return JS_EXCEPTION; +} + +JSValue apply_parameters(JSContext *ctx, JSValue parameters, sqlite3_stmt * stmt) { + JSValue rv = JS_UNDEFINED; + + if(JS_IsArray(ctx, parameters)) { + int64_t n; + if (JS_GetLength(ctx, parameters, &n)) { + rv = JS_EXCEPTION; + goto done; + } + for(int i = 0; i < n; i++) { + JSValue v = JS_GetPropertyInt64(ctx, parameters, i); + if (JS_IsException(v)) { + rv = JS_EXCEPTION; + goto done; + } + + JSValue rv = _bind_js_value(ctx, v, stmt, i + 1); + JS_FreeValue(ctx, v); + + if(JS_IsException(rv)) { + goto done; + } + + } + } + else { + rv = JS_ThrowPlainError(ctx, "Unknown parameter type"); + } + + done: + return rv; +} +static JSValue db_query_value(JSContext *ctx, JSValue this_val, int argc, JSValue *argv) { + sqlite3 * db = (sqlite3 * ) JS_GetOpaque2(ctx, this_val, jama_sqlite_class_id); + sqlite3_stmt * stmt = NULL; + size_t sqlLength; + const char * sql; + JSValue rv; + + sql = JS_ToCStringLen(ctx, &sqlLength, argv[0]); + + int rc = sqlite3_prepare_v2(db, sql, sqlLength, &stmt, NULL); + if(rc != SQLITE_OK) { + rv = JS_EXCEPTION; + goto done; + } + if(!(sqlite3_stmt_readonly(stmt))) { + rv = JS_ThrowPlainError(ctx, "SQL in queryValue() must be a read-only script, ie SELECT only. "); + goto done; + } + + if(!JS_IsUndefined(argv[1])) { + rv = apply_parameters(ctx, argv[1], stmt); + if(JS_IsException(rv)) { + goto done; + } + } + + rc = sqlite3_step(stmt); + if(rc != SQLITE_ROW) { + rv = JS_EXCEPTION; + goto done; + } + + rv = column_value_to_js(ctx, stmt, 0); + + done: + JS_FreeCString(ctx, sql); + sqlite3_finalize(stmt); + return rv; +} + +static JSValue db_query_row(JSContext *ctx, JSValue this_val, int argc, JSValue *argv) { + sqlite3 * db = (sqlite3 * ) JS_GetOpaque2(ctx, this_val, jama_sqlite_class_id); + sqlite3_stmt * stmt = NULL; + size_t sqlLength; + const char * sql; + JSValue rv; + + sql = JS_ToCStringLen(ctx, &sqlLength, argv[0]); + + int rc = sqlite3_prepare_v2(db, sql, sqlLength, &stmt, NULL); + if(rc != SQLITE_OK) { + rv = JS_EXCEPTION; + goto done; + } + if(!(sqlite3_stmt_readonly(stmt))) { + rv = JS_ThrowPlainError(ctx, "SQL in queryRow() must be a read-only script, ie SELECT only. "); + goto done; + } + + rc = sqlite3_step(stmt); + if(rc != SQLITE_ROW) { + rv = JS_EXCEPTION; + goto done; + } + + JSValue row = JS_NewObject(ctx); + + for(int i = 0; i < sqlite3_column_count(stmt); i++) { + JSValue v = column_value_to_js(ctx, stmt, i); + JS_DefinePropertyValueStr(ctx, row, sqlite3_column_name(stmt, i), v, JS_PROP_C_W_E); + } + rv = row; + + done: + JS_FreeCString(ctx, sql); + sqlite3_finalize(stmt); + return rv; +} + +static JSValue db_execute(JSContext *ctx, JSValue this_val, int argc, JSValue *argv) { + sqlite3 * db = (sqlite3 * ) JS_GetOpaque2(ctx, this_val, jama_sqlite_class_id); + sqlite3_stmt * stmt = NULL; + size_t sqlLength; + const char * sql; + JSValue rv; + + sql = JS_ToCStringLen(ctx, &sqlLength, argv[0]); + + // TODO Can it handle comments? + int rc = sqlite3_prepare_v2(db, sql, sqlLength, &stmt, NULL); + if(rc != SQLITE_OK) { + rv = JS_ThrowPlainError(ctx, "error preparing statement: %s", sqlite3_errmsg(db)); + goto done; + } + + if(!JS_IsUndefined(argv[1])) { + rv = apply_parameters(ctx, argv[1], stmt); + if(JS_IsException(rv)) { + goto done; + } + } + + while(1) { + rc = sqlite3_step(stmt); + if(rc == SQLITE_DONE) { + break; + } + if(rc != SQLITE_ROW) { + rv = JS_ThrowPlainError(ctx, "Error executing statement: %s", sqlite3_errmsg(db)); + goto done; + } + } + + rv = JS_TRUE; + + done: + JS_FreeCString(ctx, sql); + sqlite3_finalize(stmt); + return rv; +} + +static JSValue db_execute_script(JSContext *ctx, JSValue this_val, int argc, JSValue *argv) { + sqlite3 * db = (sqlite3 * ) JS_GetOpaque2(ctx, this_val, jama_sqlite_class_id); + sqlite3_stmt * stmt = NULL; + const char * extra; + + size_t sqlLength; + const char * sql; + JSValue rv; + + sql = JS_ToCStringLen(ctx, &sqlLength, argv[0]); + const char * curr = sql; + + // TODO is the extra - curr math wrong? Could this overflow? Can it handle comments? + while(1) { + stmt = NULL; + int rc = sqlite3_prepare_v2(db, curr, sqlLength, &stmt, &extra); + if(rc != SQLITE_OK) { + rv = JS_ThrowPlainError(ctx, "Error preparing SQL statement"); + goto done; + } + if(!stmt) { + break; + } + + while(1) { + rc = sqlite3_step(stmt); + if(rc == SQLITE_DONE) { + break; + } + if(rc != SQLITE_ROW) { + rv = JS_EXCEPTION; + goto done; + } + } + sqlite3_finalize(stmt); + stmt = NULL; + curr = extra; + sqlLength = sqlLength - (extra-curr); + } + + + rv = JS_TRUE; + + done: + JS_FreeCString(ctx, sql); + sqlite3_finalize(stmt); + return rv; +} + +static JSValue db_query_all(JSContext *ctx, JSValue this_val, int argc, JSValue *argv) { + sqlite3 * db = (sqlite3 * ) JS_GetOpaque2(ctx, this_val, jama_sqlite_class_id); + sqlite3_stmt * stmt = NULL; + size_t sqlLength; + const char * sql; + JSValue rv; + + sql = JS_ToCStringLen(ctx, &sqlLength, argv[0]); + + int rc = sqlite3_prepare_v2(db, sql, sqlLength, &stmt, NULL); + if(rc != SQLITE_OK) { + rv = JS_EXCEPTION; + goto done; + } + if(!(sqlite3_stmt_readonly(stmt))) { + rv = JS_ThrowPlainError(ctx, "SQL in queryAll() must be a read-only script, ie SELECT only. "); + goto done; + } + + if(!JS_IsUndefined(argv[1])) { + rv = apply_parameters(ctx, argv[1], stmt); + if(JS_IsException(rv)) { + goto done; + } + } + + JSValue array = JS_NewArray(ctx); + int i = 0; + while(1) { + rc = sqlite3_step(stmt); + if(rc == SQLITE_DONE) { + break; + } + if(rc != SQLITE_ROW) { + rv = JS_EXCEPTION; + goto done; + } + JSValue row = JS_NewObject(ctx); + + for(int i = 0; i < sqlite3_column_count(stmt); i++) { + JSValue v = column_value_to_js(ctx, stmt, i); + JS_DefinePropertyValueStr(ctx, row, sqlite3_column_name(stmt, i), v, JS_PROP_C_W_E); + ; + } + + JS_DefinePropertyValueUint32(ctx, array, i, row, JS_PROP_C_W_E); + i++; + } + + rv = array; + + + done: + JS_FreeCString(ctx, sql); + sqlite3_finalize(stmt); + return rv; +} + + +struct u { + JSContext * ctx; + JSValue callback; +}; + +static void _update_hook(void * p, int operation,char const * db, char const * table,sqlite3_int64 rowid) { + struct u * update = (struct u *) p; + JSContext *ctx = update->ctx; + JSValue callback = JS_DupValue(ctx, update->callback); + + char * opName = ""; + switch(operation) { + case SQLITE_INSERT: + opName = "insert"; + break; + case SQLITE_DELETE: + opName = "delete"; + break; + case SQLITE_UPDATE: + opName = "update"; + break; + } + + JSValue argv[] = { + JS_NewString(ctx, opName), + JS_NewString(ctx, db), + JS_NewString(ctx, table), + JS_NewInt64(ctx, rowid) + }; + + // ignore return value for now? + JSValue rv = JS_Call(update->ctx, callback, JS_UNDEFINED, countof(argv), argv); + + for(int i = 0; i < countof(argv); i++) { + JS_FreeValue(ctx, argv[i]); + } + JS_FreeValue(ctx, callback); + JS_FreeValue(ctx, rv); +} + +static JSValue db_update_hook(JSContext *ctx, JSValue this_val, int argc, JSValue *argv) { + sqlite3 * db = (sqlite3 * ) JS_GetOpaque2(ctx, this_val, jama_sqlite_class_id); + struct u * ux; + + JSValue func = argv[0]; + + if(JS_IsUndefined(func)) { + struct u *prev = (struct u *) sqlite3_update_hook(db, NULL, NULL); + if(prev) { + JS_FreeValue(ctx, prev->callback); + } + return JS_UNDEFINED; + } + if (!JS_IsFunction(ctx, func)) + return JS_ThrowTypeError(ctx, "not a function"); + + ux = js_mallocz(ctx, sizeof(ux)); + assert(ux); + + ux->ctx = ctx; + ux->callback = JS_DupValue(ctx, func); + void * prev = sqlite3_update_hook(db, _update_hook, ux); + return JS_UNDEFINED; +} + +static const JSCFunctionListEntry js_sqlite_database_proto_funcs[] = { + + JS_CFUNC_DEF("queryAll", 2, db_query_all ), + JS_CFUNC_DEF("queryRow", 1, db_query_row ), + JS_CFUNC_DEF("queryValue", 2, db_query_value ), + + JS_CFUNC_DEF("execute", 2, db_execute ), + JS_CFUNC_DEF("executeScript", 1, db_execute_script ), + + JS_CFUNC_DEF("updateHook", 1, db_update_hook ), + +}; + + +static JSValue database_create(JSContext *ctx, JSValue this_val, int argc, JSValue *argv) { + int rc; + JSValue proto = JS_GetClassProto(ctx, jama_sqlite_class_id); + JSValue obj = JS_NewObjectProtoClass(ctx, proto, jama_sqlite_class_id); + JS_FreeValue(ctx, proto); + sqlite3* db; + + if(JS_IsString(argv[0])) { + const char * dbPath = JS_ToCString(ctx, argv[0]); + rc = sqlite3_open(dbPath, &db); + JS_FreeCString(ctx, dbPath); + } + else { + rc = sqlite3_open(":memory:", &db); + } + + assert(rc == SQLITE_OK); + sqlite3_vec_init(db, NULL, NULL); + sqlite3_csv_init(db, NULL, NULL); + + JS_SetOpaque(obj, db); + return obj; +} + + +static int js_sqlite_init(JSContext *ctx, JSModuleDef *m) +{ + JSRuntime *rt = JS_GetRuntime(ctx); + + JS_NewClassID(JS_GetRuntime(ctx), &jama_sqlite_class_id); + JS_NewClass(rt, jama_sqlite_class_id, &jama_sqlite_class); + JSValue proto = JS_NewObject(ctx); + JS_SetPropertyFunctionList(ctx, proto, js_sqlite_database_proto_funcs, countof(js_sqlite_database_proto_funcs)); + + JSValue obj = JS_NewCFunction2(ctx, database_create, "Database", 1, JS_CFUNC_constructor, 0); + JS_SetConstructor(ctx, obj, proto); + + JS_SetClassProto(ctx, jama_sqlite_class_id, proto); + + JS_SetModuleExport(ctx, m, "Database", obj); + return JS_SetModuleExportList(ctx, m, js_sqlite_funcs, countof(js_sqlite_funcs)); +} + +JSModuleDef *js_init_module_sqlite(JSContext *ctx, const char *module_name) +{ + JSModuleDef *m; + m = JS_NewCModule(ctx, module_name, js_sqlite_init); + if (!m) + return NULL; + JS_AddModuleExportList(ctx, m, js_sqlite_funcs, countof(js_sqlite_funcs)); + JS_AddModuleExport(ctx, m, "Database"); + return m; +} diff --git a/jamfile/quickjs-sqlite.h b/jamfile/quickjs-sqlite.h new file mode 100644 index 0000000000..ff761960df --- /dev/null +++ b/jamfile/quickjs-sqlite.h @@ -0,0 +1,16 @@ +#ifndef QUICKJS_SQLITE_H +#define QUICKJS_SQLITE_H + +#include "third_party/quickjs/quickjs.h" + +#ifdef __cplusplus +extern "C" { +#endif + +JSModuleDef *js_init_module_sqlite(JSContext *ctx, const char *module_name); + +#ifdef __cplusplus +} /* end of the 'extern "C"' block */ +#endif + +#endif /* ifndef QUICKJS_SQLITE_H */ diff --git a/jamfile/tests/js/example.gbnf b/jamfile/tests/js/example.gbnf new file mode 100644 index 0000000000..91aed04722 --- /dev/null +++ b/jamfile/tests/js/example.gbnf @@ -0,0 +1,6 @@ +root ::= (expr "=" ws term "\n")+ +expr ::= term ([-+*/] term)* +term ::= ident | num | "(" ws expr ")" ws +ident ::= [a-z] [a-z0-9_]* ws +num ::= [0-9]+ ws +ws ::= [ \t\n]* \ No newline at end of file diff --git a/jamfile/tests/js/example.sql b/jamfile/tests/js/example.sql new file mode 100644 index 0000000000..6f95136230 --- /dev/null +++ b/jamfile/tests/js/example.sql @@ -0,0 +1 @@ +select 1 + 1; \ No newline at end of file diff --git a/jamfile/tests/js/example.txt b/jamfile/tests/js/example.txt new file mode 100644 index 0000000000..3462721fd4 --- /dev/null +++ b/jamfile/tests/js/example.txt @@ -0,0 +1 @@ +hello! \ No newline at end of file diff --git a/jamfile/tests/js/test-cli.js b/jamfile/tests/js/test-cli.js new file mode 100644 index 0000000000..4c023dc12f --- /dev/null +++ b/jamfile/tests/js/test-cli.js @@ -0,0 +1,13 @@ +import { test } from "./test.js"; +import { assertEquals } from "jamfile:assert"; + +import { parseArgs } from "jamfile:cli"; + +function testCli() { + const args = parseArgs(["--foo", "--bar=baz", "./quux.txt"]); + assertEquals(args, { "_": ["./quux.txt"], "foo": true, "bar": "baz" }); +} + +export default function () { + test("jamfile:cli", testCli); +} diff --git a/jamfile/tests/js/test-fmt.js b/jamfile/tests/js/test-fmt.js new file mode 100644 index 0000000000..b5fb2f2b73 --- /dev/null +++ b/jamfile/tests/js/test-fmt.js @@ -0,0 +1,18 @@ +import { test } from "./test.js"; + +import { formatBytes, formatDuration } from "jamfile:fmt"; +import { assertEquals } from "jamfile:assert"; + +function testFmt() { + assertEquals(formatBytes(3012), "3.01 kB"); + assertEquals( + formatDuration( + (60 * 1000) + (1000 * 32) + 4, + { ignoreZero: true }, + ), + "1m 32s 4ms", + ); +} +export default function () { + test("jamfile:fmt", testFmt); +} diff --git a/jamfile/tests/js/test-frontmatter.js b/jamfile/tests/js/test-frontmatter.js new file mode 100644 index 0000000000..247eff81bd --- /dev/null +++ b/jamfile/tests/js/test-frontmatter.js @@ -0,0 +1,21 @@ +/// <reference types="../../jamfile.d.ts"/> + +import { test } from "./test.js"; +import { assertEquals } from "jamfile:assert"; +import { extractToml } from "jamfile:frontmatter"; + + +function testFrontmatter() { + assertEquals( + extractToml('---toml\nname = "alex"\n---\n# markdown'), + { + frontMatter: 'name = "alex"', + body: "# markdown", + attrs: {name: "alex"} + } + ); +} + +export default function () { + test("jamfile:frontmatter", testFrontmatter); +} diff --git a/jamfile/tests/js/test-globals.js b/jamfile/tests/js/test-globals.js new file mode 100644 index 0000000000..b5c502f443 --- /dev/null +++ b/jamfile/tests/js/test-globals.js @@ -0,0 +1,25 @@ +import { test } from "./test.js"; +import { assert, assertEquals } from "jamfile:assert"; + +function testGlobals() { + assertEquals( + Array.from(Object.keys(globalThis)).sort(), + [ + "Jamfile", + "argv0", + "console", + "performance", + ], + ); + assertEquals( + Object.keys(Jamfile).sort(), + ["args", "version"], + ); + + assert(typeof Jamfile.version === "string"); + assert(Array.isArray(Jamfile.args)); + assertEquals(Jamfile.args, []); +} +export default function () { + test("runtime: globals", testGlobals); +} diff --git a/jamfile/tests/js/test-imports.js b/jamfile/tests/js/test-imports.js new file mode 100644 index 0000000000..a7c58630ad --- /dev/null +++ b/jamfile/tests/js/test-imports.js @@ -0,0 +1,21 @@ + +import { test } from "./test.js"; +import { assertEquals } from "jamfile:assert"; + +import sql from "./example.sql"; +import txt from "./example.txt"; +import gbnf from "./example.gbnf"; + +function testImports() { + assertEquals(sql, "select 1 + 1;"); + assertEquals(txt, "hello!"); + assertEquals(gbnf, `root ::= (expr "=" ws term "\n")+ +expr ::= term ([-+*/] term)* +term ::= ident | num | "(" ws expr ")" ws +ident ::= [a-z] [a-z0-9_]* ws +num ::= [0-9]+ ws +ws ::= [ \t\n]*`); +} +export default function () { + test("runtime: imports", testImports); +} diff --git a/jamfile/tests/js/test-linkedom.js b/jamfile/tests/js/test-linkedom.js new file mode 100644 index 0000000000..89912f7acf --- /dev/null +++ b/jamfile/tests/js/test-linkedom.js @@ -0,0 +1,19 @@ +/// <reference types="../../jamfile.d.ts"/> + +import { test } from "./test.js"; +import { assertEquals } from "jamfile:assert"; +import { parseHTML } from "jamfile:linkedom"; + + +function testLinkedom() { + const {document} = parseHTML('<div id="lol" class="Alex">yo</div>') + + assertEquals( + Array.from(document.querySelector('div').classList), + ['Alex'] + ); +} + +export default function () { + test("jamfile:linkedom", testLinkedom); +} diff --git a/jamfile/tests/js/test-llamafile.js b/jamfile/tests/js/test-llamafile.js new file mode 100644 index 0000000000..e743a77332 --- /dev/null +++ b/jamfile/tests/js/test-llamafile.js @@ -0,0 +1,117 @@ +/// <reference types="../../jamfile.d.ts"/> + +import { test } from "./test.js"; +import { assert, assertEquals, assertThrows } from "jamfile:assert"; + +import { CompletionModel, TextEmbeddingModel } from "jamfile:llamafile"; +import * as llamafile from "jamfile:llamafile"; + +function testLlamafile() { + assertEquals(Object.getOwnPropertyNames(llamafile), [ + "CompletionModel", + "TextEmbeddingModel", + ]); +} + +function testLlamafileTextEmbeddingModel() { + assertEquals( + Object.getOwnPropertyNames(TextEmbeddingModel.prototype).sort(), + ["constructor", "detokenize", "dimensions", "embed", "tokenize"], + ); + + const model = new TextEmbeddingModel("models/mxbai-embed-xsmall-v1-f16.gguf"); + + assertEquals(model.dimensions, 384); + + assertThrows( + () => { + model.dimensions = 4; + }, + Error, + "no setter for property", + ); + + const embedding = model.embed("hello!"); + + assert(embedding instanceof Float32Array); + assertEquals(embedding.length, 384); + assertEquals( + Array.from(embedding.slice(0, 4)), + [ + -0.05826485529541969, + 0.04374322667717934, + 0.03084852732717991, + 0.047234565019607544, + ], + ); + + const tokens = model.tokenize("hello!"); + + assert(tokens instanceof Int32Array); + assertEquals(tokens.length, 4); + assertEquals( + Array.from(tokens), + [101, 7592, 999, 102], + ); + + const detokenized = model.detokenize(tokens); + // TODO is this what we expect? + assertEquals(detokenized, "[CLS] hello![SEP]]"); +} + +function testLlamafileCompletionModel() { + assertEquals( + Object.getOwnPropertyNames(CompletionModel.prototype).sort(), + ["complete", "constructor", "description", "detokenize", "metadata", "nContext", "nLayer", "nParams", "size", "tokenize"], + ); + + const model = new CompletionModel("models/TinyLLama-v0.1-5M-F16.gguf"); + assertEquals(model.description, 'llama ?B F16'); + assertEquals(model.size, BigInt(9244928)); + assertEquals(model.nParams, BigInt(4621376)); + assertEquals(model.nContext, 512); + assertEquals(model.nLayer, 8); + assertEquals( + model.metadata, + new Map([ + ["tokenizer.ggml.padding_token_id", "0"], + ["tokenizer.ggml.unknown_token_id", "0"], + ["tokenizer.ggml.eos_token_id", "2"], + ["tokenizer.ggml.bos_token_id", "1"], + ["tokenizer.ggml.model", "llama"], + ["llama.attention.head_count_kv", "16"], + ["general.file_type", "1"], + ["llama.feed_forward_length", "256"], + ["llama.vocab_size", "32000"], + ["llama.rope.dimension_count", "4"], + [ + "general.description", + "This gguf is ported from a first version of Maykeye attempt at recreating roneneldan/TinyStories-1M but using Llama architecture", + ], + ["llama.attention.layer_norm_rms_epsilon", "0.000001"], + ["general.architecture", "llama"], + ["general.author", "mofosyne"], + ["general.version", "v0.1"], + ["llama.embedding_length", "64"], + ["llama.block_count", "8"], + ["general.url", "https://huggingface.co/mofosyne/TinyLLama-v0-llamafile"], + [ + "general.source.huggingface.repository", + "https://huggingface.co/Maykeye/TinyLLama-v0", + ], + ["general.source.url", "https://huggingface.co/Maykeye/TinyLLama-v0"], + ["llama.attention.head_count", "16"], + ["llama.context_length", "2048"], + ["general.name", "TinyLLama"], + ]), + ); + assertEquals(typeof model.complete("lol"), "string"); + assertEquals(model.tokenize("lol"), new Int32Array([1, 26953])); + assertEquals(model.detokenize(new Int32Array([1, 26953])), " lol"); +} + +export default function () { + test("jamfile:llamafile", testLlamafile); + test("jamfile:llamafile TextEmbeddingModel", testLlamafileTextEmbeddingModel); + test("jamfile:llamafile CompletionModel", testLlamafileCompletionModel); +} diff --git a/jamfile/tests/js/test-marked.js b/jamfile/tests/js/test-marked.js new file mode 100644 index 0000000000..97c4d9dc5c --- /dev/null +++ b/jamfile/tests/js/test-marked.js @@ -0,0 +1,14 @@ +/// <reference types="../../jamfile.d.ts"/> + +import { test } from "./test.js"; +import { assertEquals } from "jamfile:assert"; +import {marked} from "jamfile:marked"; + + +function testMarked() { + assertEquals(marked.parse('# hello'), '<h1>hello</h1>\n'); +} + +export default function () { + test("jamfile:marked", testMarked); +} diff --git a/jamfile/tests/js/test-sqlite-vec.js b/jamfile/tests/js/test-sqlite-vec.js new file mode 100644 index 0000000000..74222d6040 --- /dev/null +++ b/jamfile/tests/js/test-sqlite-vec.js @@ -0,0 +1,26 @@ +import { test } from "./test.js"; +import { assertEquals } from "jamfile:assert"; +import { Database } from "jamfile:sqlite"; + +function testSqliteVec() { + const db = new Database(":memory:"); + assertEquals( + db.queryValue("select vec_f32('[1, 2, 3, 4]')"), + new Float32Array([1, 2, 3, 4]) + ); + assertEquals( + db.queryValue("select vec_bit(X'F0')"), + new Uint8Array([0xf0]) + ); + + // TODO: wut + /* + assertEquals( + db.queryValue("select vec_int8('[1, -1, -128, 127, 0]')"), + new Int8Array([1, -1, -128, 127, 0]) + );*/ +} + +export default function () { + test("sqlite-vec", testSqliteVec); +} diff --git a/jamfile/tests/js/test-sqlite.js b/jamfile/tests/js/test-sqlite.js new file mode 100644 index 0000000000..0d27bab4cd --- /dev/null +++ b/jamfile/tests/js/test-sqlite.js @@ -0,0 +1,177 @@ +import { test } from "./test.js"; +import { assertEquals, assertThrows } from "jamfile:assert"; + +import { Database, escapeIdentifier, SQLITE_VERSION } from "jamfile:sqlite"; + +function testSqlite() { + assertEquals(SQLITE_VERSION, "3.47.1"); + assertEquals(escapeIdentifier("alex"), "alex"); + assertEquals(escapeIdentifier('al"ex'), 'al""ex'); +} + +function testDb() { + assertEquals( + Object.getOwnPropertyNames(Database.prototype).sort(), + [ + "constructor", + "execute", + "executeScript", + "queryAll", + "queryRow", + "queryValue", + "updateHook", + ], + ); +} + +function testDbQueryAll() { + const db = new Database(":memory:"); + assertEquals( + db.queryAll( + "select fullkey, key, value, X'00' as b, null from json_each('[1,2,3]')", + ), + [ + { + "fullkey": "$[0]", + "key": 0, + "value": 1, + "b": new Uint8Array([0]), + "null": null, + }, + { + "fullkey": "$[1]", + "key": 1, + "value": 2, + "b": new Uint8Array([0]), + "null": null, + }, + { + "fullkey": "$[2]", + "key": 2, + "value": 3, + "b": new Uint8Array([0]), + "null": null, + }, + ], + ); +} + +function testDbQueryRow() { + const db = new Database(":memory:"); + + const tests = [ + ["select 1 + 1", { "1 + 1": 2 }], + ["select 1000 as a, 1.111 as b, 'ccc' as c, X'AABBCCDD' as d, null as e", { + a: 1000, + b: 1.111, + c: "ccc", + d: new Uint8Array([0xaa, 0xbb, 0xcc, 0xdd]), + e: null, + }], + ]; + + for (const [sql, expected] of tests) { + assertEquals(db.queryRow(sql), expected); + } + + const throwTests = [ + [ + "create table t(a)", + Error, + "SQL in queryRow() must be a read-only script", + ], + ]; + + for (const [sql, errorClass, message] of throwTests) { + assertThrows(() => db.queryRow(sql), errorClass, message); + } +} + +function testDbQueryValue() { + const db = new Database(":memory:"); + + const tests = [ + {sql:"select 1 + 1", expected: 2}, + {sql:"select 100 + 100", expected: 200}, + {sql:"select upper('mario')", expected: "MARIO"}, + {sql:"select X'00112233'", expected: new Uint8Array([0, 17, 34, 51])}, + {sql:"select sqlite_version()", expected: "3.47.1"}, + {sql:"select vec_version()", expected: "v0.1.6"}, + {sql:"select null", expected: null}, + + {sql: "select ?", params: [null], expected: null}, + {sql: "select ?", params: [undefined], expected: null}, + {sql: "select ?", params: [true], expected: 1}, + {sql: "select ?", params: [false], expected: 0}, + {sql: "select ?", params: [1000], expected: 1000}, + {sql: "select ?", params: [123.456], expected: 123.456}, + {sql: "select ?", params: ['alex'], expected: 'alex'}, + {sql: "select ?", params: [new Uint8Array([0xaa, 0xbb])], expected: new Uint8Array([0xaa, 0xbb])}, + {sql: "select ?", params: [[1,2,3,4]], expected: '[1,2,3,4]'}, + {sql: "select ?", params: [{name: 'alex'}], expected: '{"name":"alex"}'}, + + // TypedArray + {sql: "select ?", params: [new Int8Array([2])], expected: new Uint8Array([2])}, + {sql: "select ?", params: [new Float32Array([1.1])], expected: new Uint8Array([205,204,140,63])}, + + // Dates? + {sql: "select ?", params: [new Date('2002')], expected: new Date('2002').toISOString()}, + + {sql: "select json(?)", params: [[1,2,3,4]], expected: [1,2,3,4]}, + + {sql: "select ? + ?", params: [1, 1], expected: 2}, + ]; + + for (const {sql, expected, params} of tests) { + assertEquals(db.queryValue(sql, params), expected); + } + + const throwTests = [ + [ + "create table t(a)", + Error, + "SQL in queryValue() must be a read-only script", + ], + ]; + + for (const [sql, errorClass, message] of throwTests) { + assertThrows(() => db.queryValue(sql), errorClass, message); + } +} + +function testDbUpdateHook() { + const db = new Database(":memory:"); + const calls = []; + + function onUpdate(op, db, table, rowid) { + calls.push([op, db, table, rowid]); + } + db.updateHook(onUpdate); + + db.executeScript(` + create table t(a); + insert into t values (1), (2), (3); + delete from t where a > 2; + update t set rowid = 100 where a = 1; + `); + + assertEquals(calls, [ + ["insert", "main", "t", 1], + ["insert", "main", "t", 2], + ["insert", "main", "t", 3], + ["delete", "main", "t", 3], + ["update", "main", "t", 100], + ]); + + // TODO "cleanup" needed to free the duped callback value + db.updateHook(undefined); +} + +export default function () { + test("jamfile:sqlite", testSqlite); + test("jamfile:sqlite db", testDb); + test("jamfile:sqlite db.queryValue", testDbQueryValue); + test("jamfile:sqlite db.queryRow", testDbQueryRow); + test("jamfile:sqlite db.queryAll", testDbQueryAll); + test("jamfile:sqlite db.updateHook", testDbUpdateHook); +} diff --git a/jamfile/tests/js/test-toml.js b/jamfile/tests/js/test-toml.js new file mode 100644 index 0000000000..f23f138fc6 --- /dev/null +++ b/jamfile/tests/js/test-toml.js @@ -0,0 +1,14 @@ +/// <reference types="../../jamfile.d.ts"/> + +import { test } from "./test.js"; +import { assertEquals } from "jamfile:assert"; +import {stringify} from "jamfile:toml"; + + +function testToml() { + assertEquals(stringify({name: "alex"}), 'name = "alex"\n'); +} + +export default function () { + test("jamfile:toml", testToml); +} diff --git a/jamfile/tests/js/test-yaml.js b/jamfile/tests/js/test-yaml.js new file mode 100644 index 0000000000..df2ea22773 --- /dev/null +++ b/jamfile/tests/js/test-yaml.js @@ -0,0 +1,14 @@ +/// <reference types="../../jamfile.d.ts"/> + +import { test } from "./test.js"; +import { assertEquals } from "jamfile:assert"; +import { stringify } from "jamfile:yaml"; + + +function testYaml() { + assertEquals(stringify({name: 'alex'}), 'name: alex\n'); +} + +export default function () { + test("jamfile:yaml", testYaml); +} diff --git a/jamfile/tests/js/test-zod.js b/jamfile/tests/js/test-zod.js new file mode 100644 index 0000000000..7bbe95c529 --- /dev/null +++ b/jamfile/tests/js/test-zod.js @@ -0,0 +1,32 @@ +/// <reference types="../../jamfile.d.ts"/> + +import { test } from "./test.js"; +import { assertEquals } from "jamfile:assert"; +import { z, zodToJsonSchema } from "jamfile:zod"; + + +function testZod() { + const Country = z.object({ + name: z.string(), + capital: z.string(), + languages: z.array(z.string()), + }); + + assertEquals( + zodToJsonSchema(Country), + { + "type": "object", + "properties": { + "name": { "type": "string" }, + "capital": { "type": "string" }, + "languages": { "type": "array", "items": { "type": "string" } }, + }, + "required": ["name", "capital", "languages"], + "additionalProperties": false, + "$schema": "http://json-schema.org/draft-07/schema#", + }, + ); +} +export default function () { + test("jamfile:zod", testZod); +} diff --git a/jamfile/tests/js/test.js b/jamfile/tests/js/test.js new file mode 100644 index 0000000000..93b0fbb3b8 --- /dev/null +++ b/jamfile/tests/js/test.js @@ -0,0 +1,44 @@ +/// <reference types="../../jamfile.d.ts"/> + +import { bold, green, red } from "jamfile:colors"; +import { printf } from "qjs:std"; + +import testGlobals from './test-globals.js'; +import testImports from './test-imports.js'; +import testFmt from "./test-fmt.js"; +import testZod from "./test-zod.js"; +import testSqlite from "./test-sqlite.js"; +import testSqliteVec from "./test-sqlite-vec.js"; +import testLlamafile from "./test-llamafile.js"; +import testYaml from "./test-yaml.js"; +import testToml from "./test-toml.js"; +import testFrontmatter from "./test-frontmatter.js"; +import testMarked from "./test-marked.js"; +import testLinkedom from "./test-linkedom.js"; + +export function test(name, fn) { + try { + printf(`Testing ${name}… `); + fn(); + printf("✔︎\n"); + } catch (error) { + printf(red("✗\n")); + throw error; + } +} + +testGlobals(); +testImports(); + +testFmt(); +testZod(); +testSqlite(); +testLlamafile(); +testSqliteVec(); +testYaml(); +testToml(); +testFrontmatter(); +testMarked(); +testLinkedom(); + +console.log(green("✔︎"), bold("All tests passed!")); diff --git a/llamafile/llamafile.c b/llamafile/llamafile.c index 161d31155d..01dcf63ebc 100644 --- a/llamafile/llamafile.c +++ b/llamafile/llamafile.c @@ -304,6 +304,30 @@ struct llamafile *llamafile_open_gguf(const char *fname, const char *mode) { return llamafile_open_zip(fname, 0, mode); } +struct llamafile *llamafile_open_xxx(const char *fname, const char *mode) { + // support filenames like `foo.zip@xxx` + const char *p; + if ((p = strchr(fname, '@'))) { + return llamafile_open_zip(gc(strndup(fname, p - fname)), p + 1, mode); + } + + // open from file or from our own executable if it doesn't exist + struct llamafile *file; + if (!(file = llamafile_open_file(fname, mode))) { + if (errno == ENOENT) { + if (!(file = llamafile_open_zip(GetProgramExecutableName(), fname, mode))) { + errno = ENOENT; + return 0; + } + return file; + } else { + return 0; + } + } + // TODO otherwise assume user opened a .zip or .llamafile? + return file; +} + FILE *llamafile_fp(struct llamafile *file) { return file->fp; } diff --git a/llamafile/llamafile.h b/llamafile/llamafile.h index fcc770bc08..0ed0cba0b1 100644 --- a/llamafile/llamafile.h +++ b/llamafile/llamafile.h @@ -65,6 +65,7 @@ extern int FLAG_workers; extern unsigned FLAG_seed; struct llamafile; +struct llamafile *llamafile_open_xxx(const char *, const char *); struct llamafile *llamafile_open_gguf(const char *, const char *); void llamafile_close(struct llamafile *); long llamafile_read(struct llamafile *, void *, size_t); diff --git a/third_party/BUILD.mk b/third_party/BUILD.mk index f3caa75d74..50002a6e07 100644 --- a/third_party/BUILD.mk +++ b/third_party/BUILD.mk @@ -1,5 +1,6 @@ include third_party/double-conversion/BUILD.mk include third_party/mbedtls/BUILD.mk +include third_party/quickjs/BUILD.mk include third_party/sqlite/BUILD.mk include third_party/stb/BUILD.mk diff --git a/third_party/quickjs/BUILD.mk b/third_party/quickjs/BUILD.mk new file mode 100644 index 0000000000..be2176c384 --- /dev/null +++ b/third_party/quickjs/BUILD.mk @@ -0,0 +1,84 @@ +#-*-mode:makefile-gmake;indent-tabs-mode:t;tab-width:8;coding:utf-8-*-┐ +#── vi: set noet ft=make ts=8 sw=8 fenc=utf-8 :vi ────────────────────┘ + +PKGS += THIRD_PARTY_QUICKJS + +THIRD_PARTY_QUICKJS_SRCS = \ + third_party/quickjs/qjs.c \ + third_party/quickjs/repl.c \ + third_party/quickjs/libbf.c \ + third_party/quickjs/cutils.c \ + third_party/quickjs/quickjs.c \ + third_party/quickjs/libregexp.c \ + third_party/quickjs/libunicode.c \ + third_party/quickjs/quickjs-libc.c \ + +THIRD_PARTY_QUICKJS_HDRS = \ + third_party/quickjs/qjs.h \ + third_party/quickjs/list.h \ + third_party/quickjs/libbf.h \ + third_party/quickjs/cutils.h \ + third_party/quickjs/quickjs.h \ + third_party/quickjs/libregexp.h \ + third_party/quickjs/libunicode.h \ + third_party/quickjs/dirent_compat.h \ + third_party/quickjs/quickjs-libc.h \ + third_party/quickjs/quickjs-atom.h \ + third_party/quickjs/quickjs-opcode.h \ + third_party/quickjs/libregexp-opcode.h \ + third_party/quickjs/quickjs-c-atomics.h \ + third_party/quickjs/libunicode-table.h \ + + +o//third_party/quickjs/qjs.a \ +o/$(MODE)/third_party/quickjs/qjs: \ + o/$(MODE)/third_party/quickjs/qjs.o \ + o/$(MODE)/third_party/quickjs/repl.o \ + o/$(MODE)/third_party/quickjs/cutils.o \ + o/$(MODE)/third_party/quickjs/libbf.o \ + o/$(MODE)/third_party/quickjs/quickjs.o \ + o/$(MODE)/third_party/quickjs/libregexp.o \ + o/$(MODE)/third_party/quickjs/libunicode.o \ + o/$(MODE)/third_party/quickjs/quickjs-libc.o \ + +o//third_party/quickjs/qjsc.a \ +o/$(MODE)/third_party/quickjs/qjsc: \ + o/$(MODE)/third_party/quickjs/qjsc.o \ + o/$(MODE)/third_party/quickjs/repl.o \ + o/$(MODE)/third_party/quickjs/cutils.o \ + o/$(MODE)/third_party/quickjs/libbf.o \ + o/$(MODE)/third_party/quickjs/quickjs.o \ + o/$(MODE)/third_party/quickjs/libregexp.o \ + o/$(MODE)/third_party/quickjs/libunicode.o \ + o/$(MODE)/third_party/quickjs/quickjs-libc.o \ + +o//third_party/quickjs/quickjs.a: o//third_party/quickjs/quickjs.o + +o/$(MODE)/third_party/quickjs/qjs.o \ +o/$(MODE)/third_party/quickjs/list.o \ +o/$(MODE)/third_party/quickjs/libbf.o \ +o/$(MODE)/third_party/quickjs/cutils.o \ +o/$(MODE)/third_party/quickjs/quickjs.o \ +o/$(MODE)/third_party/quickjs/libregexp.o \ +o/$(MODE)/third_party/quickjs/libunicode.o \ +o/$(MODE)/third_party/quickjs/quickjs-libc.o: \ + private CFLAGS += \ + -mgcc \ + +o/$(MODE)/third_party/quickjs/qjs.o \ +o/$(MODE)/third_party/quickjs/list.o \ +o/$(MODE)/third_party/quickjs/libbf.o \ +o/$(MODE)/third_party/quickjs/cutils.o \ +o/$(MODE)/third_party/quickjs/quickjs.o \ +o/$(MODE)/third_party/quickjs/libregexp.o \ +o/$(MODE)/third_party/quickjs/libunicode.o \ +o/$(MODE)/third_party/quickjs/quickjs-libc.o: \ + third_party/quickjs/BUILD.mk + + + +.PHONY: o/$(MODE)/third_party/quickjs +o/$(MODE)/third_party/quickjs: \ + o/$(MODE)/third_party/sqlite/qjs \ + o/$(MODE)/third_party/sqlite/qjsc \ + o/$(MODE)/third_party/sqlite/quickjs.a \ diff --git a/third_party/quickjs/LICENSE b/third_party/quickjs/LICENSE new file mode 100644 index 0000000000..7d87e3ca56 --- /dev/null +++ b/third_party/quickjs/LICENSE @@ -0,0 +1,24 @@ +The MIT License (MIT) + +Copyright (c) 2017-2021 Fabrice Bellard +Copyright (c) 2017-2021 Charlie Gordon +Copyright (c) 2023 Ben Noordhuis +Copyright (c) 2023 Saúl Ibarra Corretgé + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/third_party/quickjs/README.llamafile b/third_party/quickjs/README.llamafile new file mode 100644 index 0000000000..397d1aea21 --- /dev/null +++ b/third_party/quickjs/README.llamafile @@ -0,0 +1,15 @@ +DESCRIPTION + + A small and embeddable Javascript engine + +ORIGIN + + https://github.com/quickjs-ng/quickjs/releases/tag/v0.7.0 + +LICENSE + + MIT + +LOCAL CHANGES + + - Expose js_print() function in quickjs-libc.h diff --git a/third_party/quickjs/cutils.c b/third_party/quickjs/cutils.c new file mode 100644 index 0000000000..5515b4607a --- /dev/null +++ b/third_party/quickjs/cutils.c @@ -0,0 +1,1411 @@ +/* + * C utilities + * + * Copyright (c) 2017 Fabrice Bellard + * Copyright (c) 2018 Charlie Gordon + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include <assert.h> +#include <stdlib.h> +#include <stdio.h> +#include <stdarg.h> +#include <string.h> +#include <time.h> +#if !defined(_MSC_VER) +#include <sys/time.h> +#endif + +#include "cutils.h" + +#undef NANOSEC +#define NANOSEC ((uint64_t) 1e9) + +#ifdef __GNUC__ +#pragma GCC visibility push(default) +#endif + +void pstrcpy(char *buf, int buf_size, const char *str) +{ + int c; + char *q = buf; + + if (buf_size <= 0) + return; + + for(;;) { + c = *str++; + if (c == 0 || q >= buf + buf_size - 1) + break; + *q++ = c; + } + *q = '\0'; +} + +/* strcat and truncate. */ +char *pstrcat(char *buf, int buf_size, const char *s) +{ + int len; + len = strlen(buf); + if (len < buf_size) + pstrcpy(buf + len, buf_size - len, s); + return buf; +} + +int strstart(const char *str, const char *val, const char **ptr) +{ + const char *p, *q; + p = str; + q = val; + while (*q != '\0') { + if (*p != *q) + return 0; + p++; + q++; + } + if (ptr) + *ptr = p; + return 1; +} + +int has_suffix(const char *str, const char *suffix) +{ + size_t len = strlen(str); + size_t slen = strlen(suffix); + return (len >= slen && !memcmp(str + len - slen, suffix, slen)); +} + +/* Dynamic buffer package */ + +static void *dbuf_default_realloc(void *opaque, void *ptr, size_t size) +{ + return realloc(ptr, size); +} + +void dbuf_init2(DynBuf *s, void *opaque, DynBufReallocFunc *realloc_func) +{ + memset(s, 0, sizeof(*s)); + if (!realloc_func) + realloc_func = dbuf_default_realloc; + s->opaque = opaque; + s->realloc_func = realloc_func; +} + +void dbuf_init(DynBuf *s) +{ + dbuf_init2(s, NULL, NULL); +} + +/* return < 0 if error */ +int dbuf_realloc(DynBuf *s, size_t new_size) +{ + size_t size; + uint8_t *new_buf; + if (new_size > s->allocated_size) { + if (s->error) + return -1; + size = s->allocated_size * 3 / 2; + if (size > new_size) + new_size = size; + new_buf = s->realloc_func(s->opaque, s->buf, new_size); + if (!new_buf) { + s->error = TRUE; + return -1; + } + s->buf = new_buf; + s->allocated_size = new_size; + } + return 0; +} + +int dbuf_write(DynBuf *s, size_t offset, const void *data, size_t len) +{ + size_t end; + end = offset + len; + if (dbuf_realloc(s, end)) + return -1; + memcpy(s->buf + offset, data, len); + if (end > s->size) + s->size = end; + return 0; +} + +int dbuf_put(DynBuf *s, const void *data, size_t len) +{ + if (unlikely((s->size + len) > s->allocated_size)) { + if (dbuf_realloc(s, s->size + len)) + return -1; + } + if (len > 0) { + memcpy(s->buf + s->size, data, len); + s->size += len; + } + return 0; +} + +int dbuf_put_self(DynBuf *s, size_t offset, size_t len) +{ + if (unlikely((s->size + len) > s->allocated_size)) { + if (dbuf_realloc(s, s->size + len)) + return -1; + } + memcpy(s->buf + s->size, s->buf + offset, len); + s->size += len; + return 0; +} + +int dbuf_putc(DynBuf *s, uint8_t c) +{ + return dbuf_put(s, &c, 1); +} + +int dbuf_putstr(DynBuf *s, const char *str) +{ + return dbuf_put(s, (const uint8_t *)str, strlen(str)); +} + +int __attribute__((format(printf, 2, 3))) dbuf_printf(DynBuf *s, + const char *fmt, ...) +{ + va_list ap; + char buf[128]; + int len; + + va_start(ap, fmt); + len = vsnprintf(buf, sizeof(buf), fmt, ap); + va_end(ap); + if (len < sizeof(buf)) { + /* fast case */ + return dbuf_put(s, (uint8_t *)buf, len); + } else { + if (dbuf_realloc(s, s->size + len + 1)) + return -1; + va_start(ap, fmt); + vsnprintf((char *)(s->buf + s->size), s->allocated_size - s->size, + fmt, ap); + va_end(ap); + s->size += len; + } + return 0; +} + +void dbuf_free(DynBuf *s) +{ + /* we test s->buf as a fail safe to avoid crashing if dbuf_free() + is called twice */ + if (s->buf) { + s->realloc_func(s->opaque, s->buf, 0); + } + memset(s, 0, sizeof(*s)); +} + +/*--- UTF-8 utility functions --*/ + +/* Note: only encode valid codepoints (0x0000..0x10FFFF). + At most UTF8_CHAR_LEN_MAX bytes are output. */ + +/* Compute the number of bytes of the UTF-8 encoding for a codepoint + `c` is a code-point. + Returns the number of bytes. If a codepoint is beyond 0x10FFFF the + return value is 3 as the codepoint would be encoded as 0xFFFD. + */ +size_t utf8_encode_len(uint32_t c) +{ + if (c < 0x80) + return 1; + if (c < 0x800) + return 2; + if (c < 0x10000) + return 3; + if (c < 0x110000) + return 4; + return 3; +} + +/* Encode a codepoint in UTF-8 + `buf` points to an array of at least `UTF8_CHAR_LEN_MAX` bytes + `c` is a code-point. + Returns the number of bytes. If a codepoint is beyond 0x10FFFF the + return value is 3 and the codepoint is encoded as 0xFFFD. + No null byte is stored after the encoded bytes. + Return value is in range 1..4 + */ +size_t utf8_encode(uint8_t buf[minimum_length(UTF8_CHAR_LEN_MAX)], uint32_t c) +{ + if (c < 0x80) { + buf[0] = c; + return 1; + } + if (c < 0x800) { + buf[0] = (c >> 6) | 0xC0; + buf[1] = (c & 0x3F) | 0x80; + return 2; + } + if (c < 0x10000) { + buf[0] = (c >> 12) | 0xE0; + buf[1] = ((c >> 6) & 0x3F) | 0x80; + buf[2] = (c & 0x3F) | 0x80; + return 3; + } + if (c < 0x110000) { + buf[0] = (c >> 18) | 0xF0; + buf[1] = ((c >> 12) & 0x3F) | 0x80; + buf[2] = ((c >> 6) & 0x3F) | 0x80; + buf[3] = (c & 0x3F) | 0x80; + return 4; + } + buf[0] = (0xFFFD >> 12) | 0xE0; + buf[1] = ((0xFFFD >> 6) & 0x3F) | 0x80; + buf[2] = (0xFFFD & 0x3F) | 0x80; + return 3; +} + +/* Decode a single code point from a UTF-8 encoded array of bytes + `p` is a valid pointer to an array of bytes + `pp` is a valid pointer to a `const uint8_t *` to store a pointer + to the byte following the current sequence. + Return the code point at `p`, in the range `0..0x10FFFF` + Return 0xFFFD on error. Only a single byte is consumed in this case + The maximum length for a UTF-8 byte sequence is 4 bytes. + This implements the algorithm specified in whatwg.org, except it accepts + UTF-8 encoded surrogates as JavaScript allows them in strings. + The source string is assumed to have at least UTF8_CHAR_LEN_MAX bytes + or be null terminated. + If `p[0]` is '\0', the return value is `0` and the byte is consumed. + cf: https://encoding.spec.whatwg.org/#utf-8-encoder + */ +uint32_t utf8_decode(const uint8_t *p, const uint8_t **pp) +{ + uint32_t c; + uint8_t lower, upper; + + c = *p++; + if (c < 0x80) { + *pp = p; + return c; + } + switch(c) { + case 0xC2: case 0xC3: + case 0xC4: case 0xC5: case 0xC6: case 0xC7: + case 0xC8: case 0xC9: case 0xCA: case 0xCB: + case 0xCC: case 0xCD: case 0xCE: case 0xCF: + case 0xD0: case 0xD1: case 0xD2: case 0xD3: + case 0xD4: case 0xD5: case 0xD6: case 0xD7: + case 0xD8: case 0xD9: case 0xDA: case 0xDB: + case 0xDC: case 0xDD: case 0xDE: case 0xDF: + if (*p >= 0x80 && *p <= 0xBF) { + *pp = p + 1; + return ((c - 0xC0) << 6) + (*p - 0x80); + } + // otherwise encoding error + break; + case 0xE0: + lower = 0xA0; /* reject invalid encoding */ + goto need2; + case 0xE1: case 0xE2: case 0xE3: + case 0xE4: case 0xE5: case 0xE6: case 0xE7: + case 0xE8: case 0xE9: case 0xEA: case 0xEB: + case 0xEC: case 0xED: case 0xEE: case 0xEF: + lower = 0x80; + need2: + if (*p >= lower && *p <= 0xBF && p[1] >= 0x80 && p[1] <= 0xBF) { + *pp = p + 2; + return ((c - 0xE0) << 12) + ((*p - 0x80) << 6) + (p[1] - 0x80); + } + // otherwise encoding error + break; + case 0xF0: + lower = 0x90; /* reject invalid encoding */ + upper = 0xBF; + goto need3; + case 0xF4: + lower = 0x80; + upper = 0x8F; /* reject values above 0x10FFFF */ + goto need3; + case 0xF1: case 0xF2: case 0xF3: + lower = 0x80; + upper = 0xBF; + need3: + if (*p >= lower && *p <= upper && p[1] >= 0x80 && p[1] <= 0xBF + && p[2] >= 0x80 && p[2] <= 0xBF) { + *pp = p + 3; + return ((c - 0xF0) << 18) + ((*p - 0x80) << 12) + + ((p[1] - 0x80) << 6) + (p[2] - 0x80); + } + // otherwise encoding error + break; + default: + // invalid lead byte + break; + } + *pp = p; + return 0xFFFD; +} + +uint32_t utf8_decode_len(const uint8_t *p, size_t max_len, const uint8_t **pp) { + switch (max_len) { + case 0: + *pp = p; + return 0xFFFD; + case 1: + if (*p < 0x80) + goto good; + break; + case 2: + if (*p < 0xE0) + goto good; + break; + case 3: + if (*p < 0xF0) + goto good; + break; + default: + good: + return utf8_decode(p, pp); + } + *pp = p + 1; + return 0xFFFD; +} + +/* Scan a UTF-8 encoded buffer for content type + `buf` is a valid pointer to a UTF-8 encoded string + `len` is the number of bytes to scan + `plen` points to a `size_t` variable to receive the number of units + Return value is a mask of bits. + - `UTF8_PLAIN_ASCII`: return value for 7-bit ASCII plain text + - `UTF8_NON_ASCII`: bit for non ASCII code points (8-bit or more) + - `UTF8_HAS_16BIT`: bit for 16-bit code points + - `UTF8_HAS_NON_BMP1`: bit for non-BMP1 code points, needs UTF-16 surrogate pairs + - `UTF8_HAS_ERRORS`: bit for encoding errors + */ +int utf8_scan(const char *buf, size_t buf_len, size_t *plen) +{ + const uint8_t *p, *p_end, *p_next; + size_t i, len; + int kind; + uint8_t cbits; + + kind = UTF8_PLAIN_ASCII; + cbits = 0; + len = buf_len; + // TODO: handle more than 1 byte at a time + for (i = 0; i < buf_len; i++) + cbits |= buf[i]; + if (cbits >= 0x80) { + p = (const uint8_t *)buf; + p_end = p + buf_len; + kind = UTF8_NON_ASCII; + len = 0; + while (p < p_end) { + len++; + if (*p++ >= 0x80) { + /* parse UTF-8 sequence, check for encoding error */ + uint32_t c = utf8_decode_len(p - 1, p_end - (p - 1), &p_next); + if (p_next == p) + kind |= UTF8_HAS_ERRORS; + p = p_next; + if (c > 0xFF) { + kind |= UTF8_HAS_16BIT; + if (c > 0xFFFF) { + len++; + kind |= UTF8_HAS_NON_BMP1; + } + } + } + } + } + *plen = len; + return kind; +} + +/* Decode a string encoded in UTF-8 into an array of bytes + `src` points to the source string. It is assumed to be correctly encoded + and only contains code points below 0x800 + `src_len` is the length of the source string + `dest` points to the destination array, it can be null if `dest_len` is `0` + `dest_len` is the length of the destination array. A null + terminator is stored at the end of the array unless `dest_len` is `0`. + */ +size_t utf8_decode_buf8(uint8_t *dest, size_t dest_len, const char *src, size_t src_len) +{ + const uint8_t *p, *p_end; + size_t i; + + p = (const uint8_t *)src; + p_end = p + src_len; + for (i = 0; p < p_end; i++) { + uint32_t c = *p++; + if (c >= 0xC0) + c = (c << 6) + *p++ - ((0xC0 << 6) + 0x80); + if (i < dest_len) + dest[i] = c; + } + if (i < dest_len) + dest[i] = '\0'; + else if (dest_len > 0) + dest[dest_len - 1] = '\0'; + return i; +} + +/* Decode a string encoded in UTF-8 into an array of 16-bit words + `src` points to the source string. It is assumed to be correctly encoded. + `src_len` is the length of the source string + `dest` points to the destination array, it can be null if `dest_len` is `0` + `dest_len` is the length of the destination array. No null terminator is + stored at the end of the array. + */ +size_t utf8_decode_buf16(uint16_t *dest, size_t dest_len, const char *src, size_t src_len) +{ + const uint8_t *p, *p_end; + size_t i; + + p = (const uint8_t *)src; + p_end = p + src_len; + for (i = 0; p < p_end; i++) { + uint32_t c = *p++; + if (c >= 0x80) { + /* parse utf-8 sequence */ + c = utf8_decode_len(p - 1, p_end - (p - 1), &p); + /* encoding errors are converted as 0xFFFD and use a single byte */ + if (c > 0xFFFF) { + if (i < dest_len) + dest[i] = get_hi_surrogate(c); + i++; + c = get_lo_surrogate(c); + } + } + if (i < dest_len) + dest[i] = c; + } + return i; +} + +/* Encode a buffer of 8-bit bytes as a UTF-8 encoded string + `src` points to the source buffer. + `src_len` is the length of the source buffer + `dest` points to the destination array, it can be null if `dest_len` is `0` + `dest_len` is the length in bytes of the destination array. A null + terminator is stored at the end of the array unless `dest_len` is `0`. + */ +size_t utf8_encode_buf8(char *dest, size_t dest_len, const uint8_t *src, size_t src_len) +{ + size_t i, j; + uint32_t c; + + for (i = j = 0; i < src_len; i++) { + c = src[i]; + if (c < 0x80) { + if (j + 1 >= dest_len) + goto overflow; + dest[j++] = c; + } else { + if (j + 2 >= dest_len) + goto overflow; + dest[j++] = (c >> 6) | 0xC0; + dest[j++] = (c & 0x3F) | 0x80; + } + } + if (j < dest_len) + dest[j] = '\0'; + return j; + +overflow: + if (j < dest_len) + dest[j] = '\0'; + while (i < src_len) + j += 1 + (src[i++] >= 0x80); + return j; +} + +/* Encode a buffer of 16-bit code points as a UTF-8 encoded string + `src` points to the source buffer. + `src_len` is the length of the source buffer + `dest` points to the destination array, it can be null if `dest_len` is `0` + `dest_len` is the length in bytes of the destination array. A null + terminator is stored at the end of the array unless `dest_len` is `0`. + */ +size_t utf8_encode_buf16(char *dest, size_t dest_len, const uint16_t *src, size_t src_len) +{ + size_t i, j; + uint32_t c; + + for (i = j = 0; i < src_len;) { + c = src[i++]; + if (c < 0x80) { + if (j + 1 >= dest_len) + goto overflow; + dest[j++] = c; + } else { + if (is_hi_surrogate(c) && i < src_len && is_lo_surrogate(src[i])) + c = from_surrogate(c, src[i++]); + if (j + utf8_encode_len(c) >= dest_len) + goto overflow; + j += utf8_encode((uint8_t *)dest + j, c); + } + } + if (j < dest_len) + dest[j] = '\0'; + return j; + +overflow: + i -= 1 + (c > 0xFFFF); + if (j < dest_len) + dest[j] = '\0'; + while (i < src_len) { + c = src[i++]; + if (c < 0x80) { + j++; + } else { + if (is_hi_surrogate(c) && i < src_len && is_lo_surrogate(src[i])) + c = from_surrogate(c, src[i++]); + j += utf8_encode_len(c); + } + } + return j; +} + +/*--- integer to string conversions --*/ + +/* All conversion functions: + - require a destination array `buf` of sufficient length + - write the string representation at the beginning of `buf` + - null terminate the string + - return the string length + */ + +/* 2 <= base <= 36 */ +char const digits36[36] = "0123456789abcdefghijklmnopqrstuvwxyz"; + +#define USE_SPECIAL_RADIX_10 1 // special case base 10 radix conversions +#define USE_SINGLE_CASE_FAST 1 // special case single digit numbers + +/* using u32toa_shift variant */ + +#define gen_digit(buf, c) if (is_be()) \ + buf = (buf >> 8) | ((uint64_t)(c) << ((sizeof(buf) - 1) * 8)); \ + else \ + buf = (buf << 8) | (c) + +static size_t u7toa_shift(char dest[minimum_length(8)], uint32_t n) +{ + size_t len = 1; + uint64_t buf = 0; + while (n >= 10) { + uint32_t quo = n % 10; + n /= 10; + gen_digit(buf, '0' + quo); + len++; + } + gen_digit(buf, '0' + n); + memcpy(dest, &buf, sizeof buf); + return len; +} + +static size_t u07toa_shift(char dest[minimum_length(8)], uint32_t n, size_t len) +{ + size_t i; + dest += len; + dest[7] = '\0'; + for (i = 7; i-- > 1;) { + uint32_t quo = n % 10; + n /= 10; + dest[i] = (char)('0' + quo); + } + dest[i] = (char)('0' + n); + return len + 7; +} + +size_t u32toa(char buf[minimum_length(11)], uint32_t n) +{ +#ifdef USE_SINGLE_CASE_FAST /* 10% */ + if (n < 10) { + buf[0] = (char)('0' + n); + buf[1] = '\0'; + return 1; + } +#endif +#define TEN_POW_7 10000000 + if (n >= TEN_POW_7) { + uint32_t quo = n / TEN_POW_7; + n %= TEN_POW_7; + size_t len = u7toa_shift(buf, quo); + return u07toa_shift(buf, n, len); + } + return u7toa_shift(buf, n); +} + +size_t u64toa(char buf[minimum_length(21)], uint64_t n) +{ + if (likely(n < 0x100000000)) + return u32toa(buf, n); + + size_t len; + if (n >= TEN_POW_7) { + uint64_t n1 = n / TEN_POW_7; + n %= TEN_POW_7; + if (n1 >= TEN_POW_7) { + uint32_t quo = n1 / TEN_POW_7; + n1 %= TEN_POW_7; + len = u7toa_shift(buf, quo); + len = u07toa_shift(buf, n1, len); + } else { + len = u7toa_shift(buf, n1); + } + return u07toa_shift(buf, n, len); + } + return u7toa_shift(buf, n); +} + +size_t i32toa(char buf[minimum_length(12)], int32_t n) +{ + if (likely(n >= 0)) + return u32toa(buf, n); + + buf[0] = '-'; + return 1 + u32toa(buf + 1, -(uint32_t)n); +} + +size_t i64toa(char buf[minimum_length(22)], int64_t n) +{ + if (likely(n >= 0)) + return u64toa(buf, n); + + buf[0] = '-'; + return 1 + u64toa(buf + 1, -(uint64_t)n); +} + +/* using u32toa_radix_length variant */ + +static uint8_t const radix_shift[64] = { + 0, 0, 1, 0, 2, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, + 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +}; + +size_t u32toa_radix(char buf[minimum_length(33)], uint32_t n, unsigned base) +{ + int shift; + +#ifdef USE_SPECIAL_RADIX_10 + if (likely(base == 10)) + return u32toa(buf, n); +#endif + if (n < base) { + buf[0] = digits36[n]; + buf[1] = '\0'; + return 1; + } + shift = radix_shift[base & 63]; + if (shift) { + uint32_t mask = (1 << shift) - 1; + size_t len = (32 - clz32(n) + shift - 1) / shift; + size_t last = n & mask; + char *end = buf + len; + n >>= shift; + *end-- = '\0'; + *end-- = digits36[last]; + while (n >= base) { + size_t quo = n & mask; + n >>= shift; + *end-- = digits36[quo]; + } + *end = digits36[n]; + return len; + } else { + size_t len = 2; + size_t last = n % base; + n /= base; + uint32_t nbase = base; + while (n >= nbase) { + nbase *= base; + len++; + } + char *end = buf + len; + *end-- = '\0'; + *end-- = digits36[last]; + while (n >= base) { + size_t quo = n % base; + n /= base; + *end-- = digits36[quo]; + } + *end = digits36[n]; + return len; + } +} + +size_t u64toa_radix(char buf[minimum_length(65)], uint64_t n, unsigned base) +{ + int shift; + +#ifdef USE_SPECIAL_RADIX_10 + if (likely(base == 10)) + return u64toa(buf, n); +#endif + shift = radix_shift[base & 63]; + if (shift) { + if (n < base) { + buf[0] = digits36[n]; + buf[1] = '\0'; + return 1; + } + uint64_t mask = (1 << shift) - 1; + size_t len = (64 - clz64(n) + shift - 1) / shift; + size_t last = n & mask; + char *end = buf + len; + n >>= shift; + *end-- = '\0'; + *end-- = digits36[last]; + while (n >= base) { + size_t quo = n & mask; + n >>= shift; + *end-- = digits36[quo]; + } + *end = digits36[n]; + return len; + } else { + if (likely(n < 0x100000000)) + return u32toa_radix(buf, n, base); + size_t last = n % base; + n /= base; + uint64_t nbase = base; + size_t len = 2; + while (n >= nbase) { + nbase *= base; + len++; + } + char *end = buf + len; + *end-- = '\0'; + *end-- = digits36[last]; + while (n >= base) { + size_t quo = n % base; + n /= base; + *end-- = digits36[quo]; + } + *end = digits36[n]; + return len; + } +} + +size_t i32toa_radix(char buf[minimum_length(34)], int32_t n, unsigned int base) +{ + if (likely(n >= 0)) + return u32toa_radix(buf, n, base); + + buf[0] = '-'; + return 1 + u32toa_radix(buf + 1, -(uint32_t)n, base); +} + +size_t i64toa_radix(char buf[minimum_length(66)], int64_t n, unsigned int base) +{ + if (likely(n >= 0)) + return u64toa_radix(buf, n, base); + + buf[0] = '-'; + return 1 + u64toa_radix(buf + 1, -(uint64_t)n, base); +} + +#undef gen_digit +#undef TEN_POW_7 +#undef USE_SPECIAL_RADIX_10 +#undef USE_SINGLE_CASE_FAST + +/*---- sorting with opaque argument ----*/ + +typedef void (*exchange_f)(void *a, void *b, size_t size); +typedef int (*cmp_f)(const void *, const void *, void *opaque); + +static void exchange_bytes(void *a, void *b, size_t size) { + uint8_t *ap = (uint8_t *)a; + uint8_t *bp = (uint8_t *)b; + + while (size-- != 0) { + uint8_t t = *ap; + *ap++ = *bp; + *bp++ = t; + } +} + +static void exchange_one_byte(void *a, void *b, size_t size) { + uint8_t *ap = (uint8_t *)a; + uint8_t *bp = (uint8_t *)b; + uint8_t t = *ap; + *ap = *bp; + *bp = t; +} + +static void exchange_int16s(void *a, void *b, size_t size) { + uint16_t *ap = (uint16_t *)a; + uint16_t *bp = (uint16_t *)b; + + for (size /= sizeof(uint16_t); size-- != 0;) { + uint16_t t = *ap; + *ap++ = *bp; + *bp++ = t; + } +} + +static void exchange_one_int16(void *a, void *b, size_t size) { + uint16_t *ap = (uint16_t *)a; + uint16_t *bp = (uint16_t *)b; + uint16_t t = *ap; + *ap = *bp; + *bp = t; +} + +static void exchange_int32s(void *a, void *b, size_t size) { + uint32_t *ap = (uint32_t *)a; + uint32_t *bp = (uint32_t *)b; + + for (size /= sizeof(uint32_t); size-- != 0;) { + uint32_t t = *ap; + *ap++ = *bp; + *bp++ = t; + } +} + +static void exchange_one_int32(void *a, void *b, size_t size) { + uint32_t *ap = (uint32_t *)a; + uint32_t *bp = (uint32_t *)b; + uint32_t t = *ap; + *ap = *bp; + *bp = t; +} + +static void exchange_int64s(void *a, void *b, size_t size) { + uint64_t *ap = (uint64_t *)a; + uint64_t *bp = (uint64_t *)b; + + for (size /= sizeof(uint64_t); size-- != 0;) { + uint64_t t = *ap; + *ap++ = *bp; + *bp++ = t; + } +} + +static void exchange_one_int64(void *a, void *b, size_t size) { + uint64_t *ap = (uint64_t *)a; + uint64_t *bp = (uint64_t *)b; + uint64_t t = *ap; + *ap = *bp; + *bp = t; +} + +static void exchange_int128s(void *a, void *b, size_t size) { + uint64_t *ap = (uint64_t *)a; + uint64_t *bp = (uint64_t *)b; + + for (size /= sizeof(uint64_t) * 2; size-- != 0; ap += 2, bp += 2) { + uint64_t t = ap[0]; + uint64_t u = ap[1]; + ap[0] = bp[0]; + ap[1] = bp[1]; + bp[0] = t; + bp[1] = u; + } +} + +static void exchange_one_int128(void *a, void *b, size_t size) { + uint64_t *ap = (uint64_t *)a; + uint64_t *bp = (uint64_t *)b; + uint64_t t = ap[0]; + uint64_t u = ap[1]; + ap[0] = bp[0]; + ap[1] = bp[1]; + bp[0] = t; + bp[1] = u; +} + +static inline exchange_f exchange_func(const void *base, size_t size) { + switch (((uintptr_t)base | (uintptr_t)size) & 15) { + case 0: + if (size == sizeof(uint64_t) * 2) + return exchange_one_int128; + else + return exchange_int128s; + case 8: + if (size == sizeof(uint64_t)) + return exchange_one_int64; + else + return exchange_int64s; + case 4: + case 12: + if (size == sizeof(uint32_t)) + return exchange_one_int32; + else + return exchange_int32s; + case 2: + case 6: + case 10: + case 14: + if (size == sizeof(uint16_t)) + return exchange_one_int16; + else + return exchange_int16s; + default: + if (size == 1) + return exchange_one_byte; + else + return exchange_bytes; + } +} + +static void heapsortx(void *base, size_t nmemb, size_t size, cmp_f cmp, void *opaque) +{ + uint8_t *basep = (uint8_t *)base; + size_t i, n, c, r; + exchange_f swap = exchange_func(base, size); + + if (nmemb > 1) { + i = (nmemb / 2) * size; + n = nmemb * size; + + while (i > 0) { + i -= size; + for (r = i; (c = r * 2 + size) < n; r = c) { + if (c < n - size && cmp(basep + c, basep + c + size, opaque) <= 0) + c += size; + if (cmp(basep + r, basep + c, opaque) > 0) + break; + swap(basep + r, basep + c, size); + } + } + for (i = n - size; i > 0; i -= size) { + swap(basep, basep + i, size); + + for (r = 0; (c = r * 2 + size) < i; r = c) { + if (c < i - size && cmp(basep + c, basep + c + size, opaque) <= 0) + c += size; + if (cmp(basep + r, basep + c, opaque) > 0) + break; + swap(basep + r, basep + c, size); + } + } + } +} + +static inline void *med3(void *a, void *b, void *c, cmp_f cmp, void *opaque) +{ + return cmp(a, b, opaque) < 0 ? + (cmp(b, c, opaque) < 0 ? b : (cmp(a, c, opaque) < 0 ? c : a )) : + (cmp(b, c, opaque) > 0 ? b : (cmp(a, c, opaque) < 0 ? a : c )); +} + +/* pointer based version with local stack and insertion sort threshhold */ +void rqsort(void *base, size_t nmemb, size_t size, cmp_f cmp, void *opaque) +{ + struct { uint8_t *base; size_t count; int depth; } stack[50], *sp = stack; + uint8_t *ptr, *pi, *pj, *plt, *pgt, *top, *m; + size_t m4, i, lt, gt, span, span2; + int c, depth; + exchange_f swap = exchange_func(base, size); + exchange_f swap_block = exchange_func(base, size | 128); + + if (nmemb < 2 || size <= 0) + return; + + sp->base = (uint8_t *)base; + sp->count = nmemb; + sp->depth = 0; + sp++; + + while (sp > stack) { + sp--; + ptr = sp->base; + nmemb = sp->count; + depth = sp->depth; + + while (nmemb > 6) { + if (++depth > 50) { + /* depth check to ensure worst case logarithmic time */ + heapsortx(ptr, nmemb, size, cmp, opaque); + nmemb = 0; + break; + } + /* select median of 3 from 1/4, 1/2, 3/4 positions */ + /* should use median of 5 or 9? */ + m4 = (nmemb >> 2) * size; + m = med3(ptr + m4, ptr + 2 * m4, ptr + 3 * m4, cmp, opaque); + swap(ptr, m, size); /* move the pivot to the start or the array */ + i = lt = 1; + pi = plt = ptr + size; + gt = nmemb; + pj = pgt = top = ptr + nmemb * size; + for (;;) { + while (pi < pj && (c = cmp(ptr, pi, opaque)) >= 0) { + if (c == 0) { + swap(plt, pi, size); + lt++; + plt += size; + } + i++; + pi += size; + } + while (pi < (pj -= size) && (c = cmp(ptr, pj, opaque)) <= 0) { + if (c == 0) { + gt--; + pgt -= size; + swap(pgt, pj, size); + } + } + if (pi >= pj) + break; + swap(pi, pj, size); + i++; + pi += size; + } + /* array has 4 parts: + * from 0 to lt excluded: elements identical to pivot + * from lt to pi excluded: elements smaller than pivot + * from pi to gt excluded: elements greater than pivot + * from gt to n excluded: elements identical to pivot + */ + /* move elements identical to pivot in the middle of the array: */ + /* swap values in ranges [0..lt[ and [i-lt..i[ + swapping the smallest span between lt and i-lt is sufficient + */ + span = plt - ptr; + span2 = pi - plt; + lt = i - lt; + if (span > span2) + span = span2; + swap_block(ptr, pi - span, span); + /* swap values in ranges [gt..top[ and [i..top-(top-gt)[ + swapping the smallest span between top-gt and gt-i is sufficient + */ + span = top - pgt; + span2 = pgt - pi; + pgt = top - span2; + gt = nmemb - (gt - i); + if (span > span2) + span = span2; + swap_block(pi, top - span, span); + + /* now array has 3 parts: + * from 0 to lt excluded: elements smaller than pivot + * from lt to gt excluded: elements identical to pivot + * from gt to n excluded: elements greater than pivot + */ + /* stack the larger segment and keep processing the smaller one + to minimize stack use for pathological distributions */ + if (lt > nmemb - gt) { + sp->base = ptr; + sp->count = lt; + sp->depth = depth; + sp++; + ptr = pgt; + nmemb -= gt; + } else { + sp->base = pgt; + sp->count = nmemb - gt; + sp->depth = depth; + sp++; + nmemb = lt; + } + } + /* Use insertion sort for small fragments */ + for (pi = ptr + size, top = ptr + nmemb * size; pi < top; pi += size) { + for (pj = pi; pj > ptr && cmp(pj - size, pj, opaque) > 0; pj -= size) + swap(pj, pj - size, size); + } + } +} + +/*---- Portable time functions ----*/ + +#ifdef _WIN32 + // From: https://stackoverflow.com/a/26085827 +static int gettimeofday_msvc(struct timeval *tp, struct timezone *tzp) +{ + static const uint64_t EPOCH = ((uint64_t)116444736000000000ULL); + + SYSTEMTIME system_time; + FILETIME file_time; + uint64_t time; + + GetSystemTime(&system_time); + SystemTimeToFileTime(&system_time, &file_time); + time = ((uint64_t)file_time.dwLowDateTime); + time += ((uint64_t)file_time.dwHighDateTime) << 32; + + tp->tv_sec = (long)((time - EPOCH) / 10000000L); + tp->tv_usec = (long)(system_time.wMilliseconds * 1000); + + return 0; +} + +uint64_t js__hrtime_ns(void) { + LARGE_INTEGER counter, frequency; + double scaled_freq; + double result; + + if (!QueryPerformanceFrequency(&frequency)) + abort(); + assert(frequency.QuadPart != 0); + + if (!QueryPerformanceCounter(&counter)) + abort(); + assert(counter.QuadPart != 0); + + /* Because we have no guarantee about the order of magnitude of the + * performance counter interval, integer math could cause this computation + * to overflow. Therefore we resort to floating point math. + */ + scaled_freq = (double) frequency.QuadPart / NANOSEC; + result = (double) counter.QuadPart / scaled_freq; + return (uint64_t) result; +} +#else +uint64_t js__hrtime_ns(void) { + struct timespec t; + + if (clock_gettime(CLOCK_MONOTONIC, &t)) + abort(); + + return t.tv_sec * NANOSEC + t.tv_nsec; +} +#endif + +int64_t js__gettimeofday_us(void) { + struct timeval tv; +#ifdef _WIN32 + gettimeofday_msvc(&tv, NULL); +#else + gettimeofday(&tv, NULL); +#endif + return ((int64_t)tv.tv_sec * 1000000) + tv.tv_usec; +} + +/*--- Cross-platform threading APIs. ----*/ + +#if !defined(EMSCRIPTEN) && !defined(__wasi__) + +#if defined(_WIN32) +typedef void (*js__once_cb)(void); + +typedef struct { + js__once_cb callback; +} js__once_data_t; + +static BOOL WINAPI js__once_inner(INIT_ONCE *once, void *param, void **context) { + js__once_data_t *data = param; + + data->callback(); + + return TRUE; +} + +void js_once(js_once_t *guard, js__once_cb callback) { + js__once_data_t data = { .callback = callback }; + InitOnceExecuteOnce(guard, js__once_inner, (void*) &data, NULL); +} + +void js_mutex_init(js_mutex_t *mutex) { + InitializeCriticalSection(mutex); +} + +void js_mutex_destroy(js_mutex_t *mutex) { + DeleteCriticalSection(mutex); +} + +void js_mutex_lock(js_mutex_t *mutex) { + EnterCriticalSection(mutex); +} + +void js_mutex_unlock(js_mutex_t *mutex) { + LeaveCriticalSection(mutex); +} + +void js_cond_init(js_cond_t *cond) { + InitializeConditionVariable(cond); +} + +void js_cond_destroy(js_cond_t *cond) { + /* nothing to do */ + (void) cond; +} + +void js_cond_signal(js_cond_t *cond) { + WakeConditionVariable(cond); +} + +void js_cond_broadcast(js_cond_t *cond) { + WakeAllConditionVariable(cond); +} + +void js_cond_wait(js_cond_t *cond, js_mutex_t *mutex) { + if (!SleepConditionVariableCS(cond, mutex, INFINITE)) + abort(); +} + +int js_cond_timedwait(js_cond_t *cond, js_mutex_t *mutex, uint64_t timeout) { + if (SleepConditionVariableCS(cond, mutex, (DWORD)(timeout / 1e6))) + return 0; + if (GetLastError() != ERROR_TIMEOUT) + abort(); + return -1; +} + +#else /* !defined(_WIN32) */ + +void js_once(js_once_t *guard, void (*callback)(void)) { + if (pthread_once(guard, callback)) + abort(); +} + +void js_mutex_init(js_mutex_t *mutex) { + if (pthread_mutex_init(mutex, NULL)) + abort(); +} + +void js_mutex_destroy(js_mutex_t *mutex) { + if (pthread_mutex_destroy(mutex)) + abort(); +} + +void js_mutex_lock(js_mutex_t *mutex) { + if (pthread_mutex_lock(mutex)) + abort(); +} + +void js_mutex_unlock(js_mutex_t *mutex) { + if (pthread_mutex_unlock(mutex)) + abort(); +} + +void js_cond_init(js_cond_t *cond) { +#if defined(__APPLE__) && defined(__MACH__) + if (pthread_cond_init(cond, NULL)) + abort(); +#else + pthread_condattr_t attr; + + if (pthread_condattr_init(&attr)) + abort(); + + if (pthread_condattr_setclock(&attr, CLOCK_MONOTONIC)) + abort(); + + if (pthread_cond_init(cond, &attr)) + abort(); + + if (pthread_condattr_destroy(&attr)) + abort(); +#endif +} + +void js_cond_destroy(js_cond_t *cond) { +#if defined(__APPLE__) && defined(__MACH__) + /* It has been reported that destroying condition variables that have been + * signalled but not waited on can sometimes result in application crashes. + * See https://codereview.chromium.org/1323293005. + */ + pthread_mutex_t mutex; + struct timespec ts; + int err; + + if (pthread_mutex_init(&mutex, NULL)) + abort(); + + if (pthread_mutex_lock(&mutex)) + abort(); + + ts.tv_sec = 0; + ts.tv_nsec = 1; + + err = pthread_cond_timedwait_relative_np(cond, &mutex, &ts); + if (err != 0 && err != ETIMEDOUT) + abort(); + + if (pthread_mutex_unlock(&mutex)) + abort(); + + if (pthread_mutex_destroy(&mutex)) + abort(); +#endif /* defined(__APPLE__) && defined(__MACH__) */ + + if (pthread_cond_destroy(cond)) + abort(); +} + +void js_cond_signal(js_cond_t *cond) { + if (pthread_cond_signal(cond)) + abort(); +} + +void js_cond_broadcast(js_cond_t *cond) { + if (pthread_cond_broadcast(cond)) + abort(); +} + +void js_cond_wait(js_cond_t *cond, js_mutex_t *mutex) { +#if defined(__APPLE__) && defined(__MACH__) + int r; + + errno = 0; + r = pthread_cond_wait(cond, mutex); + + /* Workaround for a bug in OS X at least up to 13.6 + * See https://github.com/libuv/libuv/issues/4165 + */ + if (r == EINVAL && errno == EBUSY) + return; + if (r) + abort(); +#else + if (pthread_cond_wait(cond, mutex)) + abort(); +#endif +} + +int js_cond_timedwait(js_cond_t *cond, js_mutex_t *mutex, uint64_t timeout) { + int r; + struct timespec ts; + +#if !defined(__APPLE__) + timeout += js__hrtime_ns(); +#endif + + ts.tv_sec = timeout / NANOSEC; + ts.tv_nsec = timeout % NANOSEC; +#if defined(__APPLE__) && defined(__MACH__) + r = pthread_cond_timedwait_relative_np(cond, mutex, &ts); +#else + r = pthread_cond_timedwait(cond, mutex, &ts); +#endif + + if (r == 0) + return 0; + + if (r == ETIMEDOUT) + return -1; + + abort(); + + /* Pacify some compilers. */ + return -1; +} + +#endif + +#endif /* !defined(EMSCRIPTEN) && !defined(__wasi__) */ + +#ifdef __GNUC__ +#pragma GCC visibility pop +#endif diff --git a/third_party/quickjs/cutils.h b/third_party/quickjs/cutils.h new file mode 100644 index 0000000000..1e96ed680f --- /dev/null +++ b/third_party/quickjs/cutils.h @@ -0,0 +1,616 @@ +/* + * C utilities + * + * Copyright (c) 2017 Fabrice Bellard + * Copyright (c) 2018 Charlie Gordon + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#ifndef CUTILS_H +#define CUTILS_H + +#include <stdlib.h> +#include <string.h> +#include <inttypes.h> +#include <math.h> + +#ifdef __cplusplus +extern "C" { +#endif + +#if defined(_MSC_VER) +#include <winsock2.h> +#include <malloc.h> +#define alloca _alloca +#define ssize_t ptrdiff_t +#endif +#if defined(__APPLE__) +#include <malloc/malloc.h> +#elif defined(__linux__) || defined(__ANDROID__) || defined(__CYGWIN__) +#include <malloc.h> +#elif defined(__FreeBSD__) +#include <malloc_np.h> +#elif defined(_WIN32) +#include <windows.h> +#endif +#if !defined(_WIN32) && !defined(EMSCRIPTEN) && !defined(__wasi__) +#include <errno.h> +#include <pthread.h> +#endif + +#if defined(__SANITIZE_ADDRESS__) +# define __ASAN__ 1 +#elif defined(__has_feature) +# if __has_feature(address_sanitizer) +# define __ASAN__ 1 +# endif +#endif + +#if defined(_MSC_VER) && !defined(__clang__) +# define likely(x) (x) +# define unlikely(x) (x) +# define force_inline __forceinline +# define no_inline __declspec(noinline) +# define __maybe_unused +# define __attribute__(x) +# define __attribute(x) +# include <intrin.h> +static void *__builtin_frame_address(unsigned int level) { + return (void *)((char*)_AddressOfReturnAddress() - sizeof(int *) - level * sizeof(int *)); +} +#else +# define likely(x) __builtin_expect(!!(x), 1) +# define unlikely(x) __builtin_expect(!!(x), 0) +# define force_inline inline __attribute__((always_inline)) +# define no_inline __attribute__((noinline)) +# define __maybe_unused __attribute__((unused)) +#endif + +// https://stackoverflow.com/a/6849629 +#undef FORMAT_STRING +#if _MSC_VER >= 1400 +# include <sal.h> +# if _MSC_VER > 1400 +# define FORMAT_STRING(p) _Printf_format_string_ p +# else +# define FORMAT_STRING(p) __format_string p +# endif /* FORMAT_STRING */ +#else +# define FORMAT_STRING(p) p +#endif /* _MSC_VER */ + +#if defined(_MSC_VER) && !defined(__clang__) +#include <math.h> +#define INF INFINITY +#define NEG_INF -INFINITY +#else +#define INF (1.0/0.0) +#define NEG_INF (-1.0/0.0) +#endif + +#ifndef offsetof +#define offsetof(type, field) ((size_t) &((type *)0)->field) +#endif +#ifndef countof +#define countof(x) (sizeof(x) / sizeof((x)[0])) +#ifndef endof +#define endof(x) ((x) + countof(x)) +#endif +#endif +#ifndef container_of +/* return the pointer of type 'type *' containing 'ptr' as field 'member' */ +#define container_of(ptr, type, member) ((type *)((uint8_t *)(ptr) - offsetof(type, member))) +#endif + +#if defined(_MSC_VER) +#define minimum_length(n) n +#else +#define minimum_length(n) static n +#endif + +typedef int BOOL; + +#ifndef FALSE +enum { + FALSE = 0, + TRUE = 1, +}; +#endif + +void pstrcpy(char *buf, int buf_size, const char *str); +char *pstrcat(char *buf, int buf_size, const char *s); +int strstart(const char *str, const char *val, const char **ptr); +int has_suffix(const char *str, const char *suffix); + +static inline uint8_t is_be(void) { + union { + uint16_t a; + uint8_t b; + } u = { 0x100 }; + return u.b; +} + +static inline int max_int(int a, int b) +{ + if (a > b) + return a; + else + return b; +} + +static inline int min_int(int a, int b) +{ + if (a < b) + return a; + else + return b; +} + +static inline uint32_t max_uint32(uint32_t a, uint32_t b) +{ + if (a > b) + return a; + else + return b; +} + +static inline uint32_t min_uint32(uint32_t a, uint32_t b) +{ + if (a < b) + return a; + else + return b; +} + +static inline int64_t max_int64(int64_t a, int64_t b) +{ + if (a > b) + return a; + else + return b; +} + +static inline int64_t min_int64(int64_t a, int64_t b) +{ + if (a < b) + return a; + else + return b; +} + +/* WARNING: undefined if a = 0 */ +static inline int clz32(unsigned int a) +{ +#if defined(_MSC_VER) && !defined(__clang__) + unsigned long index; + _BitScanReverse(&index, a); + return 31 - index; +#else + return __builtin_clz(a); +#endif +} + +/* WARNING: undefined if a = 0 */ +static inline int clz64(uint64_t a) +{ +#if defined(_MSC_VER) && !defined(__clang__) +#if INTPTR_MAX == INT64_MAX + unsigned long index; + _BitScanReverse64(&index, a); + return 63 - index; +#else + if (a >> 32) + return clz32((unsigned)(a >> 32)); + else + return clz32((unsigned)a) + 32; +#endif +#else + return __builtin_clzll(a); +#endif +} + +/* WARNING: undefined if a = 0 */ +static inline int ctz32(unsigned int a) +{ +#if defined(_MSC_VER) && !defined(__clang__) + unsigned long index; + _BitScanForward(&index, a); + return index; +#else + return __builtin_ctz(a); +#endif +} + +/* WARNING: undefined if a = 0 */ +static inline int ctz64(uint64_t a) +{ +#if defined(_MSC_VER) && !defined(__clang__) + unsigned long index; + _BitScanForward64(&index, a); + return index; +#else + return __builtin_ctzll(a); +#endif +} + +static inline uint64_t get_u64(const uint8_t *tab) +{ + uint64_t v; + memcpy(&v, tab, sizeof(v)); + return v; +} + +static inline int64_t get_i64(const uint8_t *tab) +{ + int64_t v; + memcpy(&v, tab, sizeof(v)); + return v; +} + +static inline void put_u64(uint8_t *tab, uint64_t val) +{ + memcpy(tab, &val, sizeof(val)); +} + +static inline uint32_t get_u32(const uint8_t *tab) +{ + uint32_t v; + memcpy(&v, tab, sizeof(v)); + return v; +} + +static inline int32_t get_i32(const uint8_t *tab) +{ + int32_t v; + memcpy(&v, tab, sizeof(v)); + return v; +} + +static inline void put_u32(uint8_t *tab, uint32_t val) +{ + memcpy(tab, &val, sizeof(val)); +} + +static inline uint32_t get_u16(const uint8_t *tab) +{ + uint16_t v; + memcpy(&v, tab, sizeof(v)); + return v; +} + +static inline int32_t get_i16(const uint8_t *tab) +{ + int16_t v; + memcpy(&v, tab, sizeof(v)); + return v; +} + +static inline void put_u16(uint8_t *tab, uint16_t val) +{ + memcpy(tab, &val, sizeof(val)); +} + +static inline uint32_t get_u8(const uint8_t *tab) +{ + return *tab; +} + +static inline int32_t get_i8(const uint8_t *tab) +{ + return (int8_t)*tab; +} + +static inline void put_u8(uint8_t *tab, uint8_t val) +{ + *tab = val; +} + +#ifndef bswap16 +static inline uint16_t bswap16(uint16_t x) +{ + return (x >> 8) | (x << 8); +} +#endif + +#ifndef bswap32 +static inline uint32_t bswap32(uint32_t v) +{ + return ((v & 0xff000000) >> 24) | ((v & 0x00ff0000) >> 8) | + ((v & 0x0000ff00) << 8) | ((v & 0x000000ff) << 24); +} +#endif + +#ifndef bswap64 +static inline uint64_t bswap64(uint64_t v) +{ + return ((v & ((uint64_t)0xff << (7 * 8))) >> (7 * 8)) | + ((v & ((uint64_t)0xff << (6 * 8))) >> (5 * 8)) | + ((v & ((uint64_t)0xff << (5 * 8))) >> (3 * 8)) | + ((v & ((uint64_t)0xff << (4 * 8))) >> (1 * 8)) | + ((v & ((uint64_t)0xff << (3 * 8))) << (1 * 8)) | + ((v & ((uint64_t)0xff << (2 * 8))) << (3 * 8)) | + ((v & ((uint64_t)0xff << (1 * 8))) << (5 * 8)) | + ((v & ((uint64_t)0xff << (0 * 8))) << (7 * 8)); +} +#endif + +static inline void inplace_bswap16(uint8_t *tab) { + put_u16(tab, bswap16(get_u16(tab))); +} + +static inline void inplace_bswap32(uint8_t *tab) { + put_u32(tab, bswap32(get_u32(tab))); +} + +static inline double fromfp16(uint16_t v) { + double d, s; + int e; + if ((v & 0x7C00) == 0x7C00) { + d = (v & 0x3FF) ? NAN : INFINITY; + } else { + d = (v & 0x3FF) / 1024.; + e = (v & 0x7C00) >> 10; + if (e == 0) { + e = -14; + } else { + d += 1; + e -= 15; + } + d = scalbn(d, e); + } + s = (v & 0x8000) ? -1.0 : 1.0; + return d * s; +} + +static inline uint16_t tofp16(double d) { + uint16_t f, s; + double t; + int e; + s = 0; + if (copysign(1, d) < 0) { // preserve sign when |d| is negative zero + d = -d; + s = 0x8000; + } + if (isinf(d)) + return s | 0x7C00; + if (isnan(d)) + return s | 0x7C01; + if (d == 0) + return s | 0; + d = 2 * frexp(d, &e); + e--; + if (e > 15) + return s | 0x7C00; // out of range, return +/-infinity + if (e < -25) { + d = 0; + e = 0; + } else if (e < -14) { + d = scalbn(d, e + 14); + e = 0; + } else { + d -= 1; + e += 15; + } + d *= 1024.; + f = (uint16_t)d; + t = d - f; + if (t < 0.5) + goto done; + if (t == 0.5) + if ((f & 1) == 0) + goto done; + // adjust for rounding + if (++f == 1024) { + f = 0; + if (++e == 31) + return s | 0x7C00; // out of range, return +/-infinity + } +done: + return s | (e << 10) | f; +} + +static inline int isfp16nan(uint16_t v) { + return (v & 0x7FFF) > 0x7C00; +} + +static inline int isfp16zero(uint16_t v) { + return (v & 0x7FFF) == 0; +} + +/* XXX: should take an extra argument to pass slack information to the caller */ +typedef void *DynBufReallocFunc(void *opaque, void *ptr, size_t size); + +typedef struct DynBuf { + uint8_t *buf; + size_t size; + size_t allocated_size; + BOOL error; /* true if a memory allocation error occurred */ + DynBufReallocFunc *realloc_func; + void *opaque; /* for realloc_func */ +} DynBuf; + +void dbuf_init(DynBuf *s); +void dbuf_init2(DynBuf *s, void *opaque, DynBufReallocFunc *realloc_func); +int dbuf_realloc(DynBuf *s, size_t new_size); +int dbuf_write(DynBuf *s, size_t offset, const void *data, size_t len); +int dbuf_put(DynBuf *s, const void *data, size_t len); +int dbuf_put_self(DynBuf *s, size_t offset, size_t len); +int dbuf_putc(DynBuf *s, uint8_t c); +int dbuf_putstr(DynBuf *s, const char *str); +static inline int dbuf_put_u16(DynBuf *s, uint16_t val) +{ + return dbuf_put(s, (uint8_t *)&val, 2); +} +static inline int dbuf_put_u32(DynBuf *s, uint32_t val) +{ + return dbuf_put(s, (uint8_t *)&val, 4); +} +static inline int dbuf_put_u64(DynBuf *s, uint64_t val) +{ + return dbuf_put(s, (uint8_t *)&val, 8); +} +int __attribute__((format(printf, 2, 3))) dbuf_printf(DynBuf *s, + FORMAT_STRING(const char *fmt), ...); +void dbuf_free(DynBuf *s); +static inline BOOL dbuf_error(DynBuf *s) { + return s->error; +} +static inline void dbuf_set_error(DynBuf *s) +{ + s->error = TRUE; +} + +/*---- UTF-8 and UTF-16 handling ----*/ + +#define UTF8_CHAR_LEN_MAX 4 + +enum { + UTF8_PLAIN_ASCII = 0, // 7-bit ASCII plain text + UTF8_NON_ASCII = 1, // has non ASCII code points (8-bit or more) + UTF8_HAS_16BIT = 2, // has 16-bit code points + UTF8_HAS_NON_BMP1 = 4, // has non-BMP1 code points, needs UTF-16 surrogate pairs + UTF8_HAS_ERRORS = 8, // has encoding errors +}; +int utf8_scan(const char *buf, size_t len, size_t *plen); +size_t utf8_encode_len(uint32_t c); +size_t utf8_encode(uint8_t buf[minimum_length(UTF8_CHAR_LEN_MAX)], uint32_t c); +uint32_t utf8_decode_len(const uint8_t *p, size_t max_len, const uint8_t **pp); +uint32_t utf8_decode(const uint8_t *p, const uint8_t **pp); +size_t utf8_decode_buf8(uint8_t *dest, size_t dest_len, const char *src, size_t src_len); +size_t utf8_decode_buf16(uint16_t *dest, size_t dest_len, const char *src, size_t src_len); +size_t utf8_encode_buf8(char *dest, size_t dest_len, const uint8_t *src, size_t src_len); +size_t utf8_encode_buf16(char *dest, size_t dest_len, const uint16_t *src, size_t src_len); + +static inline BOOL is_surrogate(uint32_t c) +{ + return (c >> 11) == (0xD800 >> 11); // 0xD800-0xDFFF +} + +static inline BOOL is_hi_surrogate(uint32_t c) +{ + return (c >> 10) == (0xD800 >> 10); // 0xD800-0xDBFF +} + +static inline BOOL is_lo_surrogate(uint32_t c) +{ + return (c >> 10) == (0xDC00 >> 10); // 0xDC00-0xDFFF +} + +static inline uint32_t get_hi_surrogate(uint32_t c) +{ + return (c >> 10) - (0x10000 >> 10) + 0xD800; +} + +static inline uint32_t get_lo_surrogate(uint32_t c) +{ + return (c & 0x3FF) | 0xDC00; +} + +static inline uint32_t from_surrogate(uint32_t hi, uint32_t lo) +{ + return 65536 + 1024 * (hi & 1023) + (lo & 1023); +} + +static inline int from_hex(int c) +{ + if (c >= '0' && c <= '9') + return c - '0'; + else if (c >= 'A' && c <= 'F') + return c - 'A' + 10; + else if (c >= 'a' && c <= 'f') + return c - 'a' + 10; + else + return -1; +} + +static inline uint8_t is_upper_ascii(uint8_t c) { + return c >= 'A' && c <= 'Z'; +} + +static inline uint8_t to_upper_ascii(uint8_t c) { + return c >= 'a' && c <= 'z' ? c - 'a' + 'A' : c; +} + +extern char const digits36[36]; +size_t u32toa(char buf[minimum_length(11)], uint32_t n); +size_t i32toa(char buf[minimum_length(12)], int32_t n); +size_t u64toa(char buf[minimum_length(21)], uint64_t n); +size_t i64toa(char buf[minimum_length(22)], int64_t n); +size_t u32toa_radix(char buf[minimum_length(33)], uint32_t n, unsigned int base); +size_t i32toa_radix(char buf[minimum_length(34)], int32_t n, unsigned base); +size_t u64toa_radix(char buf[minimum_length(65)], uint64_t n, unsigned int base); +size_t i64toa_radix(char buf[minimum_length(66)], int64_t n, unsigned int base); + +void rqsort(void *base, size_t nmemb, size_t size, + int (*cmp)(const void *, const void *, void *), + void *arg); + +int64_t js__gettimeofday_us(void); +uint64_t js__hrtime_ns(void); + +static inline size_t js__malloc_usable_size(const void *ptr) +{ +#if defined(__APPLE__) + return malloc_size(ptr); +#elif defined(_WIN32) + return _msize((void *)ptr); +#elif defined(__linux__) || defined(__ANDROID__) || defined(__CYGWIN__) || defined(__FreeBSD__) + return malloc_usable_size((void *)ptr); +#else + return 0; +#endif +} + +/* Cross-platform threading APIs. */ + +#if !defined(EMSCRIPTEN) && !defined(__wasi__) + +#if defined(_WIN32) +#define JS_ONCE_INIT INIT_ONCE_STATIC_INIT +typedef INIT_ONCE js_once_t; +typedef CRITICAL_SECTION js_mutex_t; +typedef CONDITION_VARIABLE js_cond_t; +#else +#define JS_ONCE_INIT PTHREAD_ONCE_INIT +typedef pthread_once_t js_once_t; +typedef pthread_mutex_t js_mutex_t; +typedef pthread_cond_t js_cond_t; +#endif + +void js_once(js_once_t *guard, void (*callback)(void)); + +void js_mutex_init(js_mutex_t *mutex); +void js_mutex_destroy(js_mutex_t *mutex); +void js_mutex_lock(js_mutex_t *mutex); +void js_mutex_unlock(js_mutex_t *mutex); + +void js_cond_init(js_cond_t *cond); +void js_cond_destroy(js_cond_t *cond); +void js_cond_signal(js_cond_t *cond); +void js_cond_broadcast(js_cond_t *cond); +void js_cond_wait(js_cond_t *cond, js_mutex_t *mutex); +int js_cond_timedwait(js_cond_t *cond, js_mutex_t *mutex, uint64_t timeout); + +#endif /* !defined(EMSCRIPTEN) && !defined(__wasi__) */ + +#ifdef __cplusplus +} /* extern "C" { */ +#endif + +#endif /* CUTILS_H */ diff --git a/third_party/quickjs/dirent_compat.h b/third_party/quickjs/dirent_compat.h new file mode 100644 index 0000000000..077674e45c --- /dev/null +++ b/third_party/quickjs/dirent_compat.h @@ -0,0 +1,1166 @@ +/* + * Dirent interface for Microsoft Visual Studio + * + * Copyright (C) 1998-2019 Toni Ronkko + * This file is part of dirent. Dirent may be freely distributed + * under the MIT license. For all details and documentation, see + * https://github.com/tronkko/dirent + */ +#ifndef DIRENT_H +#define DIRENT_H + +/* Hide warnings about unreferenced local functions */ +#if defined(__clang__) +# pragma clang diagnostic ignored "-Wunused-function" +#elif defined(_MSC_VER) +# pragma warning(disable:4505) +#elif defined(__GNUC__) +# pragma GCC diagnostic ignored "-Wunused-function" +#endif + +/* + * Include windows.h without Windows Sockets 1.1 to prevent conflicts with + * Windows Sockets 2.0. + */ +#ifndef WIN32_LEAN_AND_MEAN +# define WIN32_LEAN_AND_MEAN +#endif +#include <windows.h> + +#include <stdio.h> +#include <stdarg.h> +#include <wchar.h> +#include <string.h> +#include <stdlib.h> +#include <malloc.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <errno.h> + +/* Indicates that d_type field is available in dirent structure */ +#define _DIRENT_HAVE_D_TYPE + +/* Indicates that d_namlen field is available in dirent structure */ +#define _DIRENT_HAVE_D_NAMLEN + +/* Entries missing from MSVC 6.0 */ +#if !defined(FILE_ATTRIBUTE_DEVICE) +# define FILE_ATTRIBUTE_DEVICE 0x40 +#endif + +/* File type and permission flags for stat(), general mask */ +#if !defined(S_IFMT) +# define S_IFMT _S_IFMT +#endif + +/* Directory bit */ +#if !defined(S_IFDIR) +# define S_IFDIR _S_IFDIR +#endif + +/* Character device bit */ +#if !defined(S_IFCHR) +# define S_IFCHR _S_IFCHR +#endif + +/* Pipe bit */ +#if !defined(S_IFFIFO) +# define S_IFFIFO _S_IFFIFO +#endif + +/* Regular file bit */ +#if !defined(S_IFREG) +# define S_IFREG _S_IFREG +#endif + +/* Read permission */ +#if !defined(S_IREAD) +# define S_IREAD _S_IREAD +#endif + +/* Write permission */ +#if !defined(S_IWRITE) +# define S_IWRITE _S_IWRITE +#endif + +/* Execute permission */ +#if !defined(S_IEXEC) +# define S_IEXEC _S_IEXEC +#endif + +/* Pipe */ +#if !defined(S_IFIFO) +# define S_IFIFO _S_IFIFO +#endif + +/* Block device */ +#if !defined(S_IFBLK) +# define S_IFBLK 0 +#endif + +/* Link */ +#if !defined(S_IFLNK) +# define S_IFLNK 0 +#endif + +/* Socket */ +#if !defined(S_IFSOCK) +# define S_IFSOCK 0 +#endif + +/* Read user permission */ +#if !defined(S_IRUSR) +# define S_IRUSR S_IREAD +#endif + +/* Write user permission */ +#if !defined(S_IWUSR) +# define S_IWUSR S_IWRITE +#endif + +/* Execute user permission */ +#if !defined(S_IXUSR) +# define S_IXUSR 0 +#endif + +/* Read group permission */ +#if !defined(S_IRGRP) +# define S_IRGRP 0 +#endif + +/* Write group permission */ +#if !defined(S_IWGRP) +# define S_IWGRP 0 +#endif + +/* Execute group permission */ +#if !defined(S_IXGRP) +# define S_IXGRP 0 +#endif + +/* Read others permission */ +#if !defined(S_IROTH) +# define S_IROTH 0 +#endif + +/* Write others permission */ +#if !defined(S_IWOTH) +# define S_IWOTH 0 +#endif + +/* Execute others permission */ +#if !defined(S_IXOTH) +# define S_IXOTH 0 +#endif + +/* Maximum length of file name */ +#if !defined(PATH_MAX) +# define PATH_MAX MAX_PATH +#endif +#if !defined(FILENAME_MAX) +# define FILENAME_MAX MAX_PATH +#endif +#if !defined(NAME_MAX) +# define NAME_MAX FILENAME_MAX +#endif + +/* File type flags for d_type */ +#define DT_UNKNOWN 0 +#define DT_REG S_IFREG +#define DT_DIR S_IFDIR +#define DT_FIFO S_IFIFO +#define DT_SOCK S_IFSOCK +#define DT_CHR S_IFCHR +#define DT_BLK S_IFBLK +#define DT_LNK S_IFLNK + +/* Macros for converting between st_mode and d_type */ +#define IFTODT(mode) ((mode) & S_IFMT) +#define DTTOIF(type) (type) + +/* + * File type macros. Note that block devices, sockets and links cannot be + * distinguished on Windows and the macros S_ISBLK, S_ISSOCK and S_ISLNK are + * only defined for compatibility. These macros should always return false + * on Windows. + */ +#if !defined(S_ISFIFO) +# define S_ISFIFO(mode) (((mode) & S_IFMT) == S_IFIFO) +#endif +#if !defined(S_ISDIR) +# define S_ISDIR(mode) (((mode) & S_IFMT) == S_IFDIR) +#endif +#if !defined(S_ISREG) +# define S_ISREG(mode) (((mode) & S_IFMT) == S_IFREG) +#endif +#if !defined(S_ISLNK) +# define S_ISLNK(mode) (((mode) & S_IFMT) == S_IFLNK) +#endif +#if !defined(S_ISSOCK) +# define S_ISSOCK(mode) (((mode) & S_IFMT) == S_IFSOCK) +#endif +#if !defined(S_ISCHR) +# define S_ISCHR(mode) (((mode) & S_IFMT) == S_IFCHR) +#endif +#if !defined(S_ISBLK) +# define S_ISBLK(mode) (((mode) & S_IFMT) == S_IFBLK) +#endif + +/* Return the exact length of the file name without zero terminator */ +#define _D_EXACT_NAMLEN(p) ((p)->d_namlen) + +/* Return the maximum size of a file name */ +#define _D_ALLOC_NAMLEN(p) ((PATH_MAX)+1) + + +#ifdef __cplusplus +extern "C" { +#endif + + +/* Wide-character version */ +struct _wdirent { + /* Always zero */ + long d_ino; + + /* File position within stream */ + long d_off; + + /* Structure size */ + unsigned short d_reclen; + + /* Length of name without \0 */ + size_t d_namlen; + + /* File type */ + int d_type; + + /* File name */ + wchar_t d_name[PATH_MAX+1]; +}; +typedef struct _wdirent _wdirent; + +struct _WDIR { + /* Current directory entry */ + struct _wdirent ent; + + /* Private file data */ + WIN32_FIND_DATAW data; + + /* True if data is valid */ + int cached; + + /* Win32 search handle */ + HANDLE handle; + + /* Initial directory name */ + wchar_t *patt; +}; +typedef struct _WDIR _WDIR; + +/* Multi-byte character version */ +struct dirent { + /* Always zero */ + long d_ino; + + /* File position within stream */ + long d_off; + + /* Structure size */ + unsigned short d_reclen; + + /* Length of name without \0 */ + size_t d_namlen; + + /* File type */ + int d_type; + + /* File name */ + char d_name[PATH_MAX+1]; +}; +typedef struct dirent dirent; + +struct DIR { + struct dirent ent; + struct _WDIR *wdirp; +}; +typedef struct DIR DIR; + + +/* Dirent functions */ +static DIR *opendir (const char *dirname); +static _WDIR *_wopendir (const wchar_t *dirname); + +static struct dirent *readdir (DIR *dirp); +static struct _wdirent *_wreaddir (_WDIR *dirp); + +static int readdir_r( + DIR *dirp, struct dirent *entry, struct dirent **result); +static int _wreaddir_r( + _WDIR *dirp, struct _wdirent *entry, struct _wdirent **result); + +static int closedir (DIR *dirp); +static int _wclosedir (_WDIR *dirp); + +static void rewinddir (DIR* dirp); +static void _wrewinddir (_WDIR* dirp); + +static int scandir (const char *dirname, struct dirent ***namelist, + int (*filter)(const struct dirent*), + int (*compare)(const struct dirent**, const struct dirent**)); + +static int alphasort (const struct dirent **a, const struct dirent **b); + +static int versionsort (const struct dirent **a, const struct dirent **b); + + +/* For compatibility with Symbian */ +#define wdirent _wdirent +#define WDIR _WDIR +#define wopendir _wopendir +#define wreaddir _wreaddir +#define wclosedir _wclosedir +#define wrewinddir _wrewinddir + + +/* Internal utility functions */ +static WIN32_FIND_DATAW *dirent_first (_WDIR *dirp); +static WIN32_FIND_DATAW *dirent_next (_WDIR *dirp); + +static int dirent_mbstowcs_s( + size_t *pReturnValue, + wchar_t *wcstr, + size_t sizeInWords, + const char *mbstr, + size_t count); + +static int dirent_wcstombs_s( + size_t *pReturnValue, + char *mbstr, + size_t sizeInBytes, + const wchar_t *wcstr, + size_t count); + +static void dirent_set_errno (int error); + + +/* + * Open directory stream DIRNAME for read and return a pointer to the + * internal working area that is used to retrieve individual directory + * entries. + */ +static _WDIR* +_wopendir( + const wchar_t *dirname) +{ + _WDIR *dirp; +#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) + /* Desktop */ + DWORD n; +#else + /* WinRT */ + size_t n; +#endif + wchar_t *p; + + /* Must have directory name */ + if (dirname == NULL || dirname[0] == '\0') { + dirent_set_errno (ENOENT); + return NULL; + } + + /* Allocate new _WDIR structure */ + dirp = (_WDIR*) malloc (sizeof (struct _WDIR)); + if (!dirp) { + return NULL; + } + + /* Reset _WDIR structure */ + dirp->handle = INVALID_HANDLE_VALUE; + dirp->patt = NULL; + dirp->cached = 0; + + /* + * Compute the length of full path plus zero terminator + * + * Note that on WinRT there's no way to convert relative paths + * into absolute paths, so just assume it is an absolute path. + */ +#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) + /* Desktop */ + n = GetFullPathNameW (dirname, 0, NULL, NULL); +#else + /* WinRT */ + n = wcslen (dirname); +#endif + + /* Allocate room for absolute directory name and search pattern */ + dirp->patt = (wchar_t*) malloc (sizeof (wchar_t) * n + 16); + if (dirp->patt == NULL) { + goto exit_closedir; + } + + /* + * Convert relative directory name to an absolute one. This + * allows rewinddir() to function correctly even when current + * working directory is changed between opendir() and rewinddir(). + * + * Note that on WinRT there's no way to convert relative paths + * into absolute paths, so just assume it is an absolute path. + */ +#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) + /* Desktop */ + n = GetFullPathNameW (dirname, n, dirp->patt, NULL); + if (n <= 0) { + goto exit_closedir; + } +#else + /* WinRT */ + wcsncpy_s (dirp->patt, n+1, dirname, n); +#endif + + /* Append search pattern \* to the directory name */ + p = dirp->patt + n; + switch (p[-1]) { + case '\\': + case '/': + case ':': + /* Directory ends in path separator, e.g. c:\temp\ */ + /*NOP*/; + break; + + default: + /* Directory name doesn't end in path separator */ + *p++ = '\\'; + } + *p++ = '*'; + *p = '\0'; + + /* Open directory stream and retrieve the first entry */ + if (!dirent_first (dirp)) { + goto exit_closedir; + } + + /* Success */ + return dirp; + + /* Failure */ +exit_closedir: + _wclosedir (dirp); + return NULL; +} + +/* + * Read next directory entry. + * + * Returns pointer to static directory entry which may be overwritten by + * subsequent calls to _wreaddir(). + */ +static struct _wdirent* +_wreaddir( + _WDIR *dirp) +{ + struct _wdirent *entry; + + /* + * Read directory entry to buffer. We can safely ignore the return value + * as entry will be set to NULL in case of error. + */ + (void) _wreaddir_r (dirp, &dirp->ent, &entry); + + /* Return pointer to statically allocated directory entry */ + return entry; +} + +/* + * Read next directory entry. + * + * Returns zero on success. If end of directory stream is reached, then sets + * result to NULL and returns zero. + */ +static int +_wreaddir_r( + _WDIR *dirp, + struct _wdirent *entry, + struct _wdirent **result) +{ + WIN32_FIND_DATAW *datap; + + /* Read next directory entry */ + datap = dirent_next (dirp); + if (datap) { + size_t n; + DWORD attr; + + /* + * Copy file name as wide-character string. If the file name is too + * long to fit in to the destination buffer, then truncate file name + * to PATH_MAX characters and zero-terminate the buffer. + */ + n = 0; + while (n < PATH_MAX && datap->cFileName[n] != 0) { + entry->d_name[n] = datap->cFileName[n]; + n++; + } + entry->d_name[n] = 0; + + /* Length of file name excluding zero terminator */ + entry->d_namlen = n; + + /* File type */ + attr = datap->dwFileAttributes; + if ((attr & FILE_ATTRIBUTE_DEVICE) != 0) { + entry->d_type = DT_CHR; + } else if ((attr & FILE_ATTRIBUTE_DIRECTORY) != 0) { + entry->d_type = DT_DIR; + } else { + entry->d_type = DT_REG; + } + + /* Reset dummy fields */ + entry->d_ino = 0; + entry->d_off = 0; + entry->d_reclen = sizeof (struct _wdirent); + + /* Set result address */ + *result = entry; + + } else { + + /* Return NULL to indicate end of directory */ + *result = NULL; + + } + + return /*OK*/0; +} + +/* + * Close directory stream opened by opendir() function. This invalidates the + * DIR structure as well as any directory entry read previously by + * _wreaddir(). + */ +static int +_wclosedir( + _WDIR *dirp) +{ + int ok; + if (dirp) { + + /* Release search handle */ + if (dirp->handle != INVALID_HANDLE_VALUE) { + FindClose (dirp->handle); + } + + /* Release search pattern */ + free (dirp->patt); + + /* Release directory structure */ + free (dirp); + ok = /*success*/0; + + } else { + + /* Invalid directory stream */ + dirent_set_errno (EBADF); + ok = /*failure*/-1; + + } + return ok; +} + +/* + * Rewind directory stream such that _wreaddir() returns the very first + * file name again. + */ +static void +_wrewinddir( + _WDIR* dirp) +{ + if (dirp) { + /* Release existing search handle */ + if (dirp->handle != INVALID_HANDLE_VALUE) { + FindClose (dirp->handle); + } + + /* Open new search handle */ + dirent_first (dirp); + } +} + +/* Get first directory entry (internal) */ +static WIN32_FIND_DATAW* +dirent_first( + _WDIR *dirp) +{ + WIN32_FIND_DATAW *datap; + DWORD error; + + /* Open directory and retrieve the first entry */ + dirp->handle = FindFirstFileExW( + dirp->patt, FindExInfoStandard, &dirp->data, + FindExSearchNameMatch, NULL, 0); + if (dirp->handle != INVALID_HANDLE_VALUE) { + + /* a directory entry is now waiting in memory */ + datap = &dirp->data; + dirp->cached = 1; + + } else { + + /* Failed to open directory: no directory entry in memory */ + dirp->cached = 0; + datap = NULL; + + /* Set error code */ + error = GetLastError (); + switch (error) { + case ERROR_ACCESS_DENIED: + /* No read access to directory */ + dirent_set_errno (EACCES); + break; + + case ERROR_DIRECTORY: + /* Directory name is invalid */ + dirent_set_errno (ENOTDIR); + break; + + case ERROR_PATH_NOT_FOUND: + default: + /* Cannot find the file */ + dirent_set_errno (ENOENT); + } + + } + return datap; +} + +/* + * Get next directory entry (internal). + * + * Returns + */ +static WIN32_FIND_DATAW* +dirent_next( + _WDIR *dirp) +{ + WIN32_FIND_DATAW *p; + + /* Get next directory entry */ + if (dirp->cached != 0) { + + /* A valid directory entry already in memory */ + p = &dirp->data; + dirp->cached = 0; + + } else if (dirp->handle != INVALID_HANDLE_VALUE) { + + /* Get the next directory entry from stream */ + if (FindNextFileW (dirp->handle, &dirp->data) != FALSE) { + /* Got a file */ + p = &dirp->data; + } else { + /* The very last entry has been processed or an error occurred */ + FindClose (dirp->handle); + dirp->handle = INVALID_HANDLE_VALUE; + p = NULL; + } + + } else { + + /* End of directory stream reached */ + p = NULL; + + } + + return p; +} + +/* + * Open directory stream using plain old C-string. + */ +static DIR* +opendir( + const char *dirname) +{ + struct DIR *dirp; + + /* Must have directory name */ + if (dirname == NULL || dirname[0] == '\0') { + dirent_set_errno (ENOENT); + return NULL; + } + + /* Allocate memory for DIR structure */ + dirp = (DIR*) malloc (sizeof (struct DIR)); + if (!dirp) { + return NULL; + } + { + int error; + wchar_t wname[PATH_MAX + 1]; + size_t n; + + /* Convert directory name to wide-character string */ + error = dirent_mbstowcs_s( + &n, wname, PATH_MAX + 1, dirname, PATH_MAX + 1); + if (error) { + /* + * Cannot convert file name to wide-character string. This + * occurs if the string contains invalid multi-byte sequences or + * the output buffer is too small to contain the resulting + * string. + */ + goto exit_free; + } + + + /* Open directory stream using wide-character name */ + dirp->wdirp = _wopendir (wname); + if (!dirp->wdirp) { + goto exit_free; + } + + } + + /* Success */ + return dirp; + + /* Failure */ +exit_free: + free (dirp); + return NULL; +} + +/* + * Read next directory entry. + */ +static struct dirent* +readdir( + DIR *dirp) +{ + struct dirent *entry; + + /* + * Read directory entry to buffer. We can safely ignore the return value + * as entry will be set to NULL in case of error. + */ + (void) readdir_r (dirp, &dirp->ent, &entry); + + /* Return pointer to statically allocated directory entry */ + return entry; +} + +/* + * Read next directory entry into called-allocated buffer. + * + * Returns zero on success. If the end of directory stream is reached, then + * sets result to NULL and returns zero. + */ +static int +readdir_r( + DIR *dirp, + struct dirent *entry, + struct dirent **result) +{ + WIN32_FIND_DATAW *datap; + + /* Read next directory entry */ + datap = dirent_next (dirp->wdirp); + if (datap) { + size_t n; + int error; + + /* Attempt to convert file name to multi-byte string */ + error = dirent_wcstombs_s( + &n, entry->d_name, PATH_MAX + 1, datap->cFileName, PATH_MAX + 1); + + /* + * If the file name cannot be represented by a multi-byte string, + * then attempt to use old 8+3 file name. This allows traditional + * Unix-code to access some file names despite of unicode + * characters, although file names may seem unfamiliar to the user. + * + * Be ware that the code below cannot come up with a short file + * name unless the file system provides one. At least + * VirtualBox shared folders fail to do this. + */ + if (error && datap->cAlternateFileName[0] != '\0') { + error = dirent_wcstombs_s( + &n, entry->d_name, PATH_MAX + 1, + datap->cAlternateFileName, PATH_MAX + 1); + } + + if (!error) { + DWORD attr; + + /* Length of file name excluding zero terminator */ + entry->d_namlen = n - 1; + + /* File attributes */ + attr = datap->dwFileAttributes; + if ((attr & FILE_ATTRIBUTE_DEVICE) != 0) { + entry->d_type = DT_CHR; + } else if ((attr & FILE_ATTRIBUTE_DIRECTORY) != 0) { + entry->d_type = DT_DIR; + } else { + entry->d_type = DT_REG; + } + + /* Reset dummy fields */ + entry->d_ino = 0; + entry->d_off = 0; + entry->d_reclen = sizeof (struct dirent); + + } else { + + /* + * Cannot convert file name to multi-byte string so construct + * an erroneous directory entry and return that. Note that + * we cannot return NULL as that would stop the processing + * of directory entries completely. + */ + entry->d_name[0] = '?'; + entry->d_name[1] = '\0'; + entry->d_namlen = 1; + entry->d_type = DT_UNKNOWN; + entry->d_ino = 0; + entry->d_off = -1; + entry->d_reclen = 0; + + } + + /* Return pointer to directory entry */ + *result = entry; + + } else { + + /* No more directory entries */ + *result = NULL; + + } + + return /*OK*/0; +} + +/* + * Close directory stream. + */ +static int +closedir( + DIR *dirp) +{ + int ok; + if (dirp) { + + /* Close wide-character directory stream */ + ok = _wclosedir (dirp->wdirp); + dirp->wdirp = NULL; + + /* Release multi-byte character version */ + free (dirp); + + } else { + + /* Invalid directory stream */ + dirent_set_errno (EBADF); + ok = /*failure*/-1; + + } + return ok; +} + +/* + * Rewind directory stream to beginning. + */ +static void +rewinddir( + DIR* dirp) +{ + /* Rewind wide-character string directory stream */ + _wrewinddir (dirp->wdirp); +} + +/* + * Scan directory for entries. + */ +static int +scandir( + const char *dirname, + struct dirent ***namelist, + int (*filter)(const struct dirent*), + int (*compare)(const struct dirent**, const struct dirent**)) +{ + struct dirent **files = NULL; + size_t size = 0; + size_t allocated = 0; + const size_t init_size = 1; + DIR *dir = NULL; + struct dirent *entry; + struct dirent *tmp = NULL; + size_t i; + int result = 0; + + /* Open directory stream */ + dir = opendir (dirname); + if (dir) { + + /* Read directory entries to memory */ + while (1) { + + /* Enlarge pointer table to make room for another pointer */ + if (size >= allocated) { + void *p; + size_t num_entries; + + /* Compute number of entries in the enlarged pointer table */ + if (size < init_size) { + /* Allocate initial pointer table */ + num_entries = init_size; + } else { + /* Double the size */ + num_entries = size * 2; + } + + /* Allocate first pointer table or enlarge existing table */ + p = realloc (files, sizeof (void*) * num_entries); + if (p != NULL) { + /* Got the memory */ + files = (dirent**) p; + allocated = num_entries; + } else { + /* Out of memory */ + result = -1; + break; + } + + } + + /* Allocate room for temporary directory entry */ + if (tmp == NULL) { + tmp = (struct dirent*) malloc (sizeof (struct dirent)); + if (tmp == NULL) { + /* Cannot allocate temporary directory entry */ + result = -1; + break; + } + } + + /* Read directory entry to temporary area */ + if (readdir_r (dir, tmp, &entry) == /*OK*/0) { + + /* Did we get an entry? */ + if (entry != NULL) { + int pass; + + /* Determine whether to include the entry in result */ + if (filter) { + /* Let the filter function decide */ + pass = filter (tmp); + } else { + /* No filter function, include everything */ + pass = 1; + } + + if (pass) { + /* Store the temporary entry to pointer table */ + files[size++] = tmp; + tmp = NULL; + + /* Keep up with the number of files */ + result++; + } + + } else { + + /* + * End of directory stream reached => sort entries and + * exit. + */ + qsort (files, size, sizeof (void*), + (int (*) (const void*, const void*)) compare); + break; + + } + + } else { + /* Error reading directory entry */ + result = /*Error*/ -1; + break; + } + + } + + } else { + /* Cannot open directory */ + result = /*Error*/ -1; + } + + /* Release temporary directory entry */ + free (tmp); + + /* Release allocated memory on error */ + if (result < 0) { + for (i = 0; i < size; i++) { + free (files[i]); + } + free (files); + files = NULL; + } + + /* Close directory stream */ + if (dir) { + closedir (dir); + } + + /* Pass pointer table to caller */ + if (namelist) { + *namelist = files; + } + return result; +} + +/* Alphabetical sorting */ +static int +alphasort( + const struct dirent **a, const struct dirent **b) +{ + return strcoll ((*a)->d_name, (*b)->d_name); +} + +/* Sort versions */ +static int +versionsort( + const struct dirent **a, const struct dirent **b) +{ + /* FIXME: implement strverscmp and use that */ + return alphasort (a, b); +} + +/* Convert multi-byte string to wide character string */ +static int +dirent_mbstowcs_s( + size_t *pReturnValue, + wchar_t *wcstr, + size_t sizeInWords, + const char *mbstr, + size_t count) +{ + int error; + +#if defined(_MSC_VER) && _MSC_VER >= 1400 + + /* Microsoft Visual Studio 2005 or later */ + error = mbstowcs_s (pReturnValue, wcstr, sizeInWords, mbstr, count); + +#else + + /* Older Visual Studio or non-Microsoft compiler */ + size_t n; + + /* Convert to wide-character string (or count characters) */ + n = mbstowcs (wcstr, mbstr, sizeInWords); + if (!wcstr || n < count) { + + /* Zero-terminate output buffer */ + if (wcstr && sizeInWords) { + if (n >= sizeInWords) { + n = sizeInWords - 1; + } + wcstr[n] = 0; + } + + /* Length of resulting multi-byte string WITH zero terminator */ + if (pReturnValue) { + *pReturnValue = n + 1; + } + + /* Success */ + error = 0; + + } else { + + /* Could not convert string */ + error = 1; + + } + +#endif + return error; +} + +/* Convert wide-character string to multi-byte string */ +static int +dirent_wcstombs_s( + size_t *pReturnValue, + char *mbstr, + size_t sizeInBytes, /* max size of mbstr */ + const wchar_t *wcstr, + size_t count) +{ + int error; + +#if defined(_MSC_VER) && _MSC_VER >= 1400 + + /* Microsoft Visual Studio 2005 or later */ + error = wcstombs_s (pReturnValue, mbstr, sizeInBytes, wcstr, count); + +#else + + /* Older Visual Studio or non-Microsoft compiler */ + size_t n; + + /* Convert to multi-byte string (or count the number of bytes needed) */ + n = wcstombs (mbstr, wcstr, sizeInBytes); + if (!mbstr || n < count) { + + /* Zero-terminate output buffer */ + if (mbstr && sizeInBytes) { + if (n >= sizeInBytes) { + n = sizeInBytes - 1; + } + mbstr[n] = '\0'; + } + + /* Length of resulting multi-bytes string WITH zero-terminator */ + if (pReturnValue) { + *pReturnValue = n + 1; + } + + /* Success */ + error = 0; + + } else { + + /* Cannot convert string */ + error = 1; + + } + +#endif + return error; +} + +/* Set errno variable */ +static void +dirent_set_errno( + int error) +{ +#if defined(_MSC_VER) && _MSC_VER >= 1400 + + /* Microsoft Visual Studio 2005 and later */ + _set_errno (error); + +#else + + /* Non-Microsoft compiler or older Microsoft compiler */ + errno = error; + +#endif +} + + +#ifdef __cplusplus +} +#endif +#endif /*DIRENT_H*/ \ No newline at end of file diff --git a/third_party/quickjs/getopt_compat.h b/third_party/quickjs/getopt_compat.h new file mode 100644 index 0000000000..f98ba27394 --- /dev/null +++ b/third_party/quickjs/getopt_compat.h @@ -0,0 +1,653 @@ +#ifndef __GETOPT_H__ +/** + * DISCLAIMER + * This file is part of the mingw-w64 runtime package. + * + * The mingw-w64 runtime package and its code is distributed in the hope that it + * will be useful but WITHOUT ANY WARRANTY. ALL WARRANTIES, EXPRESSED OR + * IMPLIED ARE HEREBY DISCLAIMED. This includes but is not limited to + * warranties of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + */ + /* + * Copyright (c) 2002 Todd C. Miller <Todd.Miller@courtesan.com> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * Sponsored in part by the Defense Advanced Research Projects + * Agency (DARPA) and Air Force Research Laboratory, Air Force + * Materiel Command, USAF, under agreement number F39502-99-1-0512. + */ +/*- + * Copyright (c) 2000 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Dieter Baron and Thomas Klausner. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma warning(disable:4996) + +#define __GETOPT_H__ + +/* All the headers include this file. */ +#include <crtdefs.h> +#include <errno.h> +#include <stdlib.h> +#include <string.h> +#include <stdarg.h> +#include <stdio.h> +#include <windows.h> + +#ifdef __cplusplus +extern "C" { +#endif + +#define REPLACE_GETOPT /* use this getopt as the system getopt(3) */ + +#ifdef REPLACE_GETOPT +int opterr = 1; /* if error message should be printed */ +int optind = 1; /* index into parent argv vector */ +int optopt = '?'; /* character checked for validity */ +#undef optreset /* see getopt.h */ +#define optreset __mingw_optreset +int optreset; /* reset getopt */ +char *optarg; /* argument associated with option */ +#endif + +//extern int optind; /* index of first non-option in argv */ +//extern int optopt; /* single option character, as parsed */ +//extern int opterr; /* flag to enable built-in diagnostics... */ +// /* (user may set to zero, to suppress) */ +// +//extern char *optarg; /* pointer to argument of current option */ + +#define PRINT_ERROR ((opterr) && (*options != ':')) + +#define FLAG_PERMUTE 0x01 /* permute non-options to the end of argv */ +#define FLAG_ALLARGS 0x02 /* treat non-options as args to option "-1" */ +#define FLAG_LONGONLY 0x04 /* operate as getopt_long_only */ + +/* return values */ +#define BADCH (int)'?' +#define BADARG ((*options == ':') ? (int)':' : (int)'?') +#define INORDER (int)1 + +#ifndef __CYGWIN__ +#define __progname __argv[0] +#else +extern char __declspec(dllimport) *__progname; +#endif + +#ifdef __CYGWIN__ +static char EMSG[] = ""; +#else +#define EMSG "" +#endif + +struct option /* specification for a long form option... */ +{ + const char *name; /* option name, without leading hyphens */ + int has_arg; /* does it take an argument? */ + int *flag; /* where to save its status, or NULL */ + int val; /* its associated status value */ +}; + +static int getopt_internal(int, char * const *, const char *, + const struct option *, int *, int); +static int parse_long_options(char * const *, const char *, + const struct option *, int *, int); +static int gcd(int, int); +static void permute_args(int, int, int, char * const *); + +static char *place = EMSG; /* option letter processing */ + +/* XXX: set optreset to 1 rather than these two */ +static int nonopt_start = -1; /* first non option argument (for permute) */ +static int nonopt_end = -1; /* first option after non options (for permute) */ + +/* Error messages */ +static const char recargchar[] = "option requires an argument -- %c"; +static const char recargstring[] = "option requires an argument -- %s"; +static const char ambig[] = "ambiguous option -- %.*s"; +static const char noarg[] = "option doesn't take an argument -- %.*s"; +static const char illoptchar[] = "unknown option -- %c"; +static const char illoptstring[] = "unknown option -- %s"; + +static void +_vwarnx(const char *fmt,va_list ap) +{ + (void)fprintf(stderr,"%s: ",__progname); + if (fmt != NULL) + (void)vfprintf(stderr,fmt,ap); + (void)fprintf(stderr,"\n"); +} + +static void +warnx(const char *fmt,...) +{ + va_list ap; + va_start(ap,fmt); + _vwarnx(fmt,ap); + va_end(ap); +} + +/* + * Compute the greatest common divisor of a and b. + */ +static int +gcd(int a, int b) +{ + int c; + + c = a % b; + while (c != 0) { + a = b; + b = c; + c = a % b; + } + + return (b); +} + +/* + * Exchange the block from nonopt_start to nonopt_end with the block + * from nonopt_end to opt_end (keeping the same order of arguments + * in each block). + */ +static void +permute_args(int panonopt_start, int panonopt_end, int opt_end, + char * const *nargv) +{ + int cstart, cyclelen, i, j, ncycle, nnonopts, nopts, pos; + char *swap; + + /* + * compute lengths of blocks and number and size of cycles + */ + nnonopts = panonopt_end - panonopt_start; + nopts = opt_end - panonopt_end; + ncycle = gcd(nnonopts, nopts); + cyclelen = (opt_end - panonopt_start) / ncycle; + + for (i = 0; i < ncycle; i++) { + cstart = panonopt_end+i; + pos = cstart; + for (j = 0; j < cyclelen; j++) { + if (pos >= panonopt_end) + pos -= nnonopts; + else + pos += nopts; + swap = nargv[pos]; + /* LINTED const cast */ + ((char **) nargv)[pos] = nargv[cstart]; + /* LINTED const cast */ + ((char **)nargv)[cstart] = swap; + } + } +} + +#ifdef REPLACE_GETOPT +/* + * getopt -- + * Parse argc/argv argument vector. + * + * [eventually this will replace the BSD getopt] + */ +int +getopt(int nargc, char * const *nargv, const char *options) +{ + + /* + * We don't pass FLAG_PERMUTE to getopt_internal() since + * the BSD getopt(3) (unlike GNU) has never done this. + * + * Furthermore, since many privileged programs call getopt() + * before dropping privileges it makes sense to keep things + * as simple (and bug-free) as possible. + */ + return (getopt_internal(nargc, nargv, options, NULL, NULL, 0)); +} +#endif /* REPLACE_GETOPT */ + +//extern int getopt(int nargc, char * const *nargv, const char *options); + +#ifdef _BSD_SOURCE +/* + * BSD adds the non-standard `optreset' feature, for reinitialisation + * of `getopt' parsing. We support this feature, for applications which + * proclaim their BSD heritage, before including this header; however, + * to maintain portability, developers are advised to avoid it. + */ +# define optreset __mingw_optreset +extern int optreset; +#endif +#ifdef __cplusplus +} +#endif +/* + * POSIX requires the `getopt' API to be specified in `unistd.h'; + * thus, `unistd.h' includes this header. However, we do not want + * to expose the `getopt_long' or `getopt_long_only' APIs, when + * included in this manner. Thus, close the standard __GETOPT_H__ + * declarations block, and open an additional __GETOPT_LONG_H__ + * specific block, only when *not* __UNISTD_H_SOURCED__, in which + * to declare the extended API. + */ +#endif /* !defined(__GETOPT_H__) */ + +#if !defined(__UNISTD_H_SOURCED__) && !defined(__GETOPT_LONG_H__) +#define __GETOPT_LONG_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +enum /* permitted values for its `has_arg' field... */ +{ + no_argument = 0, /* option never takes an argument */ + required_argument, /* option always requires an argument */ + optional_argument /* option may take an argument */ +}; + +/* + * parse_long_options -- + * Parse long options in argc/argv argument vector. + * Returns -1 if short_too is set and the option does not match long_options. + */ +static int +parse_long_options(char * const *nargv, const char *options, + const struct option *long_options, int *idx, int short_too) +{ + char *current_argv, *has_equal; + size_t current_argv_len; + int i, ambiguous, match; + +#define IDENTICAL_INTERPRETATION(_x, _y) \ + (long_options[(_x)].has_arg == long_options[(_y)].has_arg && \ + long_options[(_x)].flag == long_options[(_y)].flag && \ + long_options[(_x)].val == long_options[(_y)].val) + + current_argv = place; + match = -1; + ambiguous = 0; + + optind++; + + if ((has_equal = strchr(current_argv, '=')) != NULL) { + /* argument found (--option=arg) */ + current_argv_len = has_equal - current_argv; + has_equal++; + } else + current_argv_len = strlen(current_argv); + + for (i = 0; long_options[i].name; i++) { + /* find matching long option */ + if (strncmp(current_argv, long_options[i].name, + current_argv_len)) + continue; + + if (strlen(long_options[i].name) == current_argv_len) { + /* exact match */ + match = i; + ambiguous = 0; + break; + } + /* + * If this is a known short option, don't allow + * a partial match of a single character. + */ + if (short_too && current_argv_len == 1) + continue; + + if (match == -1) /* partial match */ + match = i; + else if (!IDENTICAL_INTERPRETATION(i, match)) + ambiguous = 1; + } + if (ambiguous) { + /* ambiguous abbreviation */ + if (PRINT_ERROR) + warnx(ambig, (int)current_argv_len, + current_argv); + optopt = 0; + return (BADCH); + } + if (match != -1) { /* option found */ + if (long_options[match].has_arg == no_argument + && has_equal) { + if (PRINT_ERROR) + warnx(noarg, (int)current_argv_len, + current_argv); + /* + * XXX: GNU sets optopt to val regardless of flag + */ + if (long_options[match].flag == NULL) + optopt = long_options[match].val; + else + optopt = 0; + return (BADARG); + } + if (long_options[match].has_arg == required_argument || + long_options[match].has_arg == optional_argument) { + if (has_equal) + optarg = has_equal; + else if (long_options[match].has_arg == + required_argument) { + /* + * optional argument doesn't use next nargv + */ + optarg = nargv[optind++]; + } + } + if ((long_options[match].has_arg == required_argument) + && (optarg == NULL)) { + /* + * Missing argument; leading ':' indicates no error + * should be generated. + */ + if (PRINT_ERROR) + warnx(recargstring, + current_argv); + /* + * XXX: GNU sets optopt to val regardless of flag + */ + if (long_options[match].flag == NULL) + optopt = long_options[match].val; + else + optopt = 0; + --optind; + return (BADARG); + } + } else { /* unknown option */ + if (short_too) { + --optind; + return (-1); + } + if (PRINT_ERROR) + warnx(illoptstring, current_argv); + optopt = 0; + return (BADCH); + } + if (idx) + *idx = match; + if (long_options[match].flag) { + *long_options[match].flag = long_options[match].val; + return (0); + } else + return (long_options[match].val); +#undef IDENTICAL_INTERPRETATION +} + +/* + * getopt_internal -- + * Parse argc/argv argument vector. Called by user level routines. + */ +static int +getopt_internal(int nargc, char * const *nargv, const char *options, + const struct option *long_options, int *idx, int flags) +{ + char *oli; /* option letter list index */ + int optchar, short_too; + static int posixly_correct = -1; + + if (options == NULL) + return (-1); + + /* + * XXX Some GNU programs (like cvs) set optind to 0 instead of + * XXX using optreset. Work around this braindamage. + */ + if (optind == 0) + optind = optreset = 1; + + /* + * Disable GNU extensions if POSIXLY_CORRECT is set or options + * string begins with a '+'. + * + * CV, 2009-12-14: Check POSIXLY_CORRECT anew if optind == 0 or + * optreset != 0 for GNU compatibility. + */ + if (posixly_correct == -1 || optreset != 0) + posixly_correct = (getenv("POSIXLY_CORRECT") != NULL); + if (*options == '-') + flags |= FLAG_ALLARGS; + else if (posixly_correct || *options == '+') + flags &= ~FLAG_PERMUTE; + if (*options == '+' || *options == '-') + options++; + + optarg = NULL; + if (optreset) + nonopt_start = nonopt_end = -1; +start: + if (optreset || !*place) { /* update scanning pointer */ + optreset = 0; + if (optind >= nargc) { /* end of argument vector */ + place = EMSG; + if (nonopt_end != -1) { + /* do permutation, if we have to */ + permute_args(nonopt_start, nonopt_end, + optind, nargv); + optind -= nonopt_end - nonopt_start; + } + else if (nonopt_start != -1) { + /* + * If we skipped non-options, set optind + * to the first of them. + */ + optind = nonopt_start; + } + nonopt_start = nonopt_end = -1; + return (-1); + } + if (*(place = nargv[optind]) != '-' || + (place[1] == '\0' && strchr(options, '-') == NULL)) { + place = EMSG; /* found non-option */ + if (flags & FLAG_ALLARGS) { + /* + * GNU extension: + * return non-option as argument to option 1 + */ + optarg = nargv[optind++]; + return (INORDER); + } + if (!(flags & FLAG_PERMUTE)) { + /* + * If no permutation wanted, stop parsing + * at first non-option. + */ + return (-1); + } + /* do permutation */ + if (nonopt_start == -1) + nonopt_start = optind; + else if (nonopt_end != -1) { + permute_args(nonopt_start, nonopt_end, + optind, nargv); + nonopt_start = optind - + (nonopt_end - nonopt_start); + nonopt_end = -1; + } + optind++; + /* process next argument */ + goto start; + } + if (nonopt_start != -1 && nonopt_end == -1) + nonopt_end = optind; + + /* + * If we have "-" do nothing, if "--" we are done. + */ + if (place[1] != '\0' && *++place == '-' && place[1] == '\0') { + optind++; + place = EMSG; + /* + * We found an option (--), so if we skipped + * non-options, we have to permute. + */ + if (nonopt_end != -1) { + permute_args(nonopt_start, nonopt_end, + optind, nargv); + optind -= nonopt_end - nonopt_start; + } + nonopt_start = nonopt_end = -1; + return (-1); + } + } + + /* + * Check long options if: + * 1) we were passed some + * 2) the arg is not just "-" + * 3) either the arg starts with -- we are getopt_long_only() + */ + if (long_options != NULL && place != nargv[optind] && + (*place == '-' || (flags & FLAG_LONGONLY))) { + short_too = 0; + if (*place == '-') + place++; /* --foo long option */ + else if (*place != ':' && strchr(options, *place) != NULL) + short_too = 1; /* could be short option too */ + + optchar = parse_long_options(nargv, options, long_options, + idx, short_too); + if (optchar != -1) { + place = EMSG; + return (optchar); + } + } + + if ((optchar = (int)*place++) == (int)':' || + (optchar == (int)'-' && *place != '\0') || + (oli = (char*)strchr(options, optchar)) == NULL) { + /* + * If the user specified "-" and '-' isn't listed in + * options, return -1 (non-option) as per POSIX. + * Otherwise, it is an unknown option character (or ':'). + */ + if (optchar == (int)'-' && *place == '\0') + return (-1); + if (!*place) + ++optind; + if (PRINT_ERROR) + warnx(illoptchar, optchar); + optopt = optchar; + return (BADCH); + } + if (long_options != NULL && optchar == 'W' && oli[1] == ';') { + /* -W long-option */ + if (*place) /* no space */ + /* NOTHING */; + else if (++optind >= nargc) { /* no arg */ + place = EMSG; + if (PRINT_ERROR) + warnx(recargchar, optchar); + optopt = optchar; + return (BADARG); + } else /* white space */ + place = nargv[optind]; + optchar = parse_long_options(nargv, options, long_options, + idx, 0); + place = EMSG; + return (optchar); + } + if (*++oli != ':') { /* doesn't take argument */ + if (!*place) + ++optind; + } else { /* takes (optional) argument */ + optarg = NULL; + if (*place) /* no white space */ + optarg = place; + else if (oli[1] != ':') { /* arg not optional */ + if (++optind >= nargc) { /* no arg */ + place = EMSG; + if (PRINT_ERROR) + warnx(recargchar, optchar); + optopt = optchar; + return (BADARG); + } else + optarg = nargv[optind]; + } + place = EMSG; + ++optind; + } + /* dump back option letter */ + return (optchar); +} + +/* + * getopt_long -- + * Parse argc/argv argument vector. + */ +int +getopt_long(int nargc, char * const *nargv, const char *options, + const struct option *long_options, int *idx) +{ + + return (getopt_internal(nargc, nargv, options, long_options, idx, + FLAG_PERMUTE)); +} + +/* + * getopt_long_only -- + * Parse argc/argv argument vector. + */ +int +getopt_long_only(int nargc, char * const *nargv, const char *options, + const struct option *long_options, int *idx) +{ + + return (getopt_internal(nargc, nargv, options, long_options, idx, + FLAG_PERMUTE|FLAG_LONGONLY)); +} + +//extern int getopt_long(int nargc, char * const *nargv, const char *options, +// const struct option *long_options, int *idx); +//extern int getopt_long_only(int nargc, char * const *nargv, const char *options, +// const struct option *long_options, int *idx); +/* + * Previous MinGW implementation had... + */ +#ifndef HAVE_DECL_GETOPT +/* + * ...for the long form API only; keep this for compatibility. + */ +# define HAVE_DECL_GETOPT 1 +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* !defined(__UNISTD_H_SOURCED__) && !defined(__GETOPT_LONG_H__) */ \ No newline at end of file diff --git a/third_party/quickjs/libbf.c b/third_party/quickjs/libbf.c new file mode 100644 index 0000000000..6293f4ea14 --- /dev/null +++ b/third_party/quickjs/libbf.c @@ -0,0 +1,8424 @@ +/* + * Tiny arbitrary precision floating point library + * + * Copyright (c) 2017-2021 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include <stdlib.h> +#include <stdio.h> +#include <inttypes.h> +#include <math.h> +#include <string.h> +#include <assert.h> + +#ifdef __AVX2__ +#include <immintrin.h> +#endif + +#include "cutils.h" +#include "libbf.h" + +/* enable it to check the multiplication result */ +//#define USE_MUL_CHECK +/* enable it to use FFT/NTT multiplication */ +#define USE_FFT_MUL +/* enable decimal floating point support */ +#define USE_BF_DEC + +//#define inline __attribute__((always_inline)) + +#ifdef __AVX2__ +#define FFT_MUL_THRESHOLD 100 /* in limbs of the smallest factor */ +#else +#define FFT_MUL_THRESHOLD 100 /* in limbs of the smallest factor */ +#endif + +/* XXX: adjust */ +#define DIVNORM_LARGE_THRESHOLD 50 +#define UDIV1NORM_THRESHOLD 3 + +#if LIMB_BITS == 64 +#define FMT_LIMB1 "%" PRIx64 +#define FMT_LIMB "%016" PRIx64 +#define PRId_LIMB PRId64 +#define PRIu_LIMB PRIu64 + +#else + +#define FMT_LIMB1 "%x" +#define FMT_LIMB "%08x" +#define PRId_LIMB "d" +#define PRIu_LIMB "u" + +#endif + +typedef intptr_t mp_size_t; + +typedef int bf_op2_func_t(bf_t *r, const bf_t *a, const bf_t *b, limb_t prec, + bf_flags_t flags); + +#ifdef USE_FFT_MUL + +#define FFT_MUL_R_OVERLAP_A (1 << 0) +#define FFT_MUL_R_OVERLAP_B (1 << 1) +#define FFT_MUL_R_NORESIZE (1 << 2) + +static no_inline int fft_mul(bf_context_t *s, + bf_t *res, limb_t *a_tab, limb_t a_len, + limb_t *b_tab, limb_t b_len, int mul_flags); +static void fft_clear_cache(bf_context_t *s); +#endif +#ifdef USE_BF_DEC +static limb_t get_digit(const limb_t *tab, limb_t len, slimb_t pos); +#endif + + +/* could leading zeros */ +static inline int clz(limb_t a) +{ + if (a == 0) { + return LIMB_BITS; + } else { +#if LIMB_BITS == 64 + return clz64(a); +#else + return clz32(a); +#endif + } +} + +static inline int ctz(limb_t a) +{ + if (a == 0) { + return LIMB_BITS; + } else { +#if LIMB_BITS == 64 + return ctz64(a); +#else + return ctz32(a); +#endif + } +} + +static inline int ceil_log2(limb_t a) +{ + if (a <= 1) + return 0; + else + return LIMB_BITS - clz(a - 1); +} + +/* b must be >= 1 */ +static inline slimb_t ceil_div(slimb_t a, slimb_t b) +{ + if (a >= 0) + return (a + b - 1) / b; + else + return a / b; +} + +/* b must be >= 1 */ +static inline slimb_t floor_div(slimb_t a, slimb_t b) +{ + if (a >= 0) { + return a / b; + } else { + return (a - b + 1) / b; + } +} + +/* return r = a modulo b (0 <= r <= b - 1. b must be >= 1 */ +static inline limb_t smod(slimb_t a, slimb_t b) +{ + a = a % (slimb_t)b; + if (a < 0) + a += b; + return a; +} + +/* signed addition with saturation */ +static inline slimb_t sat_add(slimb_t a, slimb_t b) +{ + slimb_t r; + r = a + b; + /* overflow ? */ + if (((a ^ r) & (b ^ r)) < 0) + r = (a >> (LIMB_BITS - 1)) ^ (((limb_t)1 << (LIMB_BITS - 1)) - 1); + return r; +} + +static inline __maybe_unused limb_t shrd(limb_t low, limb_t high, long shift) +{ + if (shift != 0) + low = (low >> shift) | (high << (LIMB_BITS - shift)); + return low; +} + +static inline __maybe_unused limb_t shld(limb_t a1, limb_t a0, long shift) +{ + if (shift != 0) + return (a1 << shift) | (a0 >> (LIMB_BITS - shift)); + else + return a1; +} + +#define malloc(s) malloc_is_forbidden(s) +#define free(p) free_is_forbidden(p) +#define realloc(p, s) realloc_is_forbidden(p, s) + +void bf_context_init(bf_context_t *s, bf_realloc_func_t *realloc_func, + void *realloc_opaque) +{ + memset(s, 0, sizeof(*s)); + s->realloc_func = realloc_func; + s->realloc_opaque = realloc_opaque; +} + +void bf_context_end(bf_context_t *s) +{ + bf_clear_cache(s); +} + +void bf_init(bf_context_t *s, bf_t *r) +{ + r->ctx = s; + r->sign = 0; + r->expn = BF_EXP_ZERO; + r->len = 0; + r->tab = NULL; +} + +/* return 0 if OK, -1 if alloc error */ +int bf_resize(bf_t *r, limb_t len) +{ + limb_t *tab; + + if (len != r->len) { + tab = bf_realloc(r->ctx, r->tab, len * sizeof(limb_t)); + if (!tab && len != 0) + return -1; + r->tab = tab; + r->len = len; + } + return 0; +} + +/* return 0 or BF_ST_MEM_ERROR */ +int bf_set_ui(bf_t *r, uint64_t a) +{ + r->sign = 0; + if (a == 0) { + r->expn = BF_EXP_ZERO; + bf_resize(r, 0); /* cannot fail */ + } +#if LIMB_BITS == 32 + else if (a <= 0xffffffff) +#else + else +#endif + { + int shift; + if (bf_resize(r, 1)) + goto fail; + shift = clz(a); + r->tab[0] = a << shift; + r->expn = LIMB_BITS - shift; + } +#if LIMB_BITS == 32 + else { + uint32_t a1, a0; + int shift; + if (bf_resize(r, 2)) + goto fail; + a0 = a; + a1 = a >> 32; + shift = clz(a1); + r->tab[0] = a0 << shift; + r->tab[1] = shld(a1, a0, shift); + r->expn = 2 * LIMB_BITS - shift; + } +#endif + return 0; + fail: + bf_set_nan(r); + return BF_ST_MEM_ERROR; +} + +/* return 0 or BF_ST_MEM_ERROR */ +int bf_set_si(bf_t *r, int64_t a) +{ + int ret; + + if (a < 0) { + ret = bf_set_ui(r, -a); + r->sign = 1; + } else { + ret = bf_set_ui(r, a); + } + return ret; +} + +void bf_set_nan(bf_t *r) +{ + bf_resize(r, 0); /* cannot fail */ + r->expn = BF_EXP_NAN; + r->sign = 0; +} + +void bf_set_zero(bf_t *r, int is_neg) +{ + bf_resize(r, 0); /* cannot fail */ + r->expn = BF_EXP_ZERO; + r->sign = is_neg; +} + +void bf_set_inf(bf_t *r, int is_neg) +{ + bf_resize(r, 0); /* cannot fail */ + r->expn = BF_EXP_INF; + r->sign = is_neg; +} + +/* return 0 or BF_ST_MEM_ERROR */ +int bf_set(bf_t *r, const bf_t *a) +{ + if (r == a) + return 0; + if (bf_resize(r, a->len)) { + bf_set_nan(r); + return BF_ST_MEM_ERROR; + } + r->sign = a->sign; + r->expn = a->expn; + if (a->len > 0) + memcpy(r->tab, a->tab, a->len * sizeof(limb_t)); + return 0; +} + +/* equivalent to bf_set(r, a); bf_delete(a) */ +void bf_move(bf_t *r, bf_t *a) +{ + bf_context_t *s = r->ctx; + if (r == a) + return; + bf_free(s, r->tab); + *r = *a; +} + +static limb_t get_limbz(const bf_t *a, limb_t idx) +{ + if (idx >= a->len) + return 0; + else + return a->tab[idx]; +} + +/* get LIMB_BITS at bit position 'pos' in tab */ +static inline limb_t get_bits(const limb_t *tab, limb_t len, slimb_t pos) +{ + limb_t i, a0, a1; + int p; + + i = pos >> LIMB_LOG2_BITS; + p = pos & (LIMB_BITS - 1); + if (i < len) + a0 = tab[i]; + else + a0 = 0; + if (p == 0) { + return a0; + } else { + i++; + if (i < len) + a1 = tab[i]; + else + a1 = 0; + return (a0 >> p) | (a1 << (LIMB_BITS - p)); + } +} + +static inline limb_t get_bit(const limb_t *tab, limb_t len, slimb_t pos) +{ + slimb_t i; + i = pos >> LIMB_LOG2_BITS; + if (i < 0 || i >= len) + return 0; + return (tab[i] >> (pos & (LIMB_BITS - 1))) & 1; +} + +static inline limb_t limb_mask(int start, int last) +{ + limb_t v; + int n; + n = last - start + 1; + if (n == LIMB_BITS) + v = -1; + else + v = (((limb_t)1 << n) - 1) << start; + return v; +} + +static limb_t mp_scan_nz(const limb_t *tab, mp_size_t n) +{ + mp_size_t i; + for(i = 0; i < n; i++) { + if (tab[i] != 0) + return 1; + } + return 0; +} + +/* return != 0 if one bit between 0 and bit_pos inclusive is not zero. */ +static inline limb_t scan_bit_nz(const bf_t *r, slimb_t bit_pos) +{ + slimb_t pos; + limb_t v; + + pos = bit_pos >> LIMB_LOG2_BITS; + if (pos < 0) + return 0; + v = r->tab[pos] & limb_mask(0, bit_pos & (LIMB_BITS - 1)); + if (v != 0) + return 1; + pos--; + while (pos >= 0) { + if (r->tab[pos] != 0) + return 1; + pos--; + } + return 0; +} + +/* return the addend for rounding. Note that prec can be <= 0 (for + BF_FLAG_RADPNT_PREC) */ +static int bf_get_rnd_add(int *pret, const bf_t *r, limb_t l, + slimb_t prec, int rnd_mode) +{ + int add_one, inexact; + limb_t bit1, bit0; + + if (rnd_mode == BF_RNDF) { + bit0 = 1; /* faithful rounding does not honor the INEXACT flag */ + } else { + /* starting limb for bit 'prec + 1' */ + bit0 = scan_bit_nz(r, l * LIMB_BITS - 1 - bf_max(0, prec + 1)); + } + + /* get the bit at 'prec' */ + bit1 = get_bit(r->tab, l, l * LIMB_BITS - 1 - prec); + inexact = (bit1 | bit0) != 0; + + add_one = 0; + switch(rnd_mode) { + case BF_RNDZ: + break; + case BF_RNDN: + if (bit1) { + if (bit0) { + add_one = 1; + } else { + /* round to even */ + add_one = + get_bit(r->tab, l, l * LIMB_BITS - 1 - (prec - 1)); + } + } + break; + case BF_RNDD: + case BF_RNDU: + if (r->sign == (rnd_mode == BF_RNDD)) + add_one = inexact; + break; + case BF_RNDA: + add_one = inexact; + break; + case BF_RNDNA: + case BF_RNDF: + add_one = bit1; + break; + default: + abort(); + } + + if (inexact) + *pret |= BF_ST_INEXACT; + return add_one; +} + +static int bf_set_overflow(bf_t *r, int sign, limb_t prec, bf_flags_t flags) +{ + slimb_t i, l, e_max; + int rnd_mode; + + rnd_mode = flags & BF_RND_MASK; + if (prec == BF_PREC_INF || + rnd_mode == BF_RNDN || + rnd_mode == BF_RNDNA || + rnd_mode == BF_RNDA || + (rnd_mode == BF_RNDD && sign == 1) || + (rnd_mode == BF_RNDU && sign == 0)) { + bf_set_inf(r, sign); + } else { + /* set to maximum finite number */ + l = (prec + LIMB_BITS - 1) / LIMB_BITS; + if (bf_resize(r, l)) { + bf_set_nan(r); + return BF_ST_MEM_ERROR; + } + r->tab[0] = limb_mask((-prec) & (LIMB_BITS - 1), + LIMB_BITS - 1); + for(i = 1; i < l; i++) + r->tab[i] = (limb_t)-1; + e_max = (limb_t)1 << (bf_get_exp_bits(flags) - 1); + r->expn = e_max; + r->sign = sign; + } + return BF_ST_OVERFLOW | BF_ST_INEXACT; +} + +/* round to prec1 bits assuming 'r' is non zero and finite. 'r' is + assumed to have length 'l' (1 <= l <= r->len). Note: 'prec1' can be + infinite (BF_PREC_INF). 'ret' is 0 or BF_ST_INEXACT if the result + is known to be inexact. Can fail with BF_ST_MEM_ERROR in case of + overflow not returning infinity. */ +static int __bf_round(bf_t *r, limb_t prec1, bf_flags_t flags, limb_t l, + int ret) +{ + limb_t v, a; + int shift, add_one, rnd_mode; + slimb_t i, bit_pos, pos, e_min, e_max, e_range, prec; + + /* e_min and e_max are computed to match the IEEE 754 conventions */ + e_range = (limb_t)1 << (bf_get_exp_bits(flags) - 1); + e_min = -e_range + 3; + e_max = e_range; + + if (flags & BF_FLAG_RADPNT_PREC) { + /* 'prec' is the precision after the radix point */ + if (prec1 != BF_PREC_INF) + prec = r->expn + prec1; + else + prec = prec1; + } else if (unlikely(r->expn < e_min) && (flags & BF_FLAG_SUBNORMAL)) { + /* restrict the precision in case of potentially subnormal + result */ + assert(prec1 != BF_PREC_INF); + prec = prec1 - (e_min - r->expn); + } else { + prec = prec1; + } + + /* round to prec bits */ + rnd_mode = flags & BF_RND_MASK; + add_one = bf_get_rnd_add(&ret, r, l, prec, rnd_mode); + + if (prec <= 0) { + if (add_one) { + bf_resize(r, 1); /* cannot fail */ + r->tab[0] = (limb_t)1 << (LIMB_BITS - 1); + r->expn += 1 - prec; + ret |= BF_ST_UNDERFLOW | BF_ST_INEXACT; + return ret; + } else { + goto underflow; + } + } else if (add_one) { + limb_t carry; + + /* add one starting at digit 'prec - 1' */ + bit_pos = l * LIMB_BITS - 1 - (prec - 1); + pos = bit_pos >> LIMB_LOG2_BITS; + carry = (limb_t)1 << (bit_pos & (LIMB_BITS - 1)); + + for(i = pos; i < l; i++) { + v = r->tab[i] + carry; + carry = (v < carry); + r->tab[i] = v; + if (carry == 0) + break; + } + if (carry) { + /* shift right by one digit */ + v = 1; + for(i = l - 1; i >= pos; i--) { + a = r->tab[i]; + r->tab[i] = (a >> 1) | (v << (LIMB_BITS - 1)); + v = a; + } + r->expn++; + } + } + + /* check underflow */ + if (unlikely(r->expn < e_min)) { + if (flags & BF_FLAG_SUBNORMAL) { + /* if inexact, also set the underflow flag */ + if (ret & BF_ST_INEXACT) + ret |= BF_ST_UNDERFLOW; + } else { + underflow: + ret |= BF_ST_UNDERFLOW | BF_ST_INEXACT; + bf_set_zero(r, r->sign); + return ret; + } + } + + /* check overflow */ + if (unlikely(r->expn > e_max)) + return bf_set_overflow(r, r->sign, prec1, flags); + + /* keep the bits starting at 'prec - 1' */ + bit_pos = l * LIMB_BITS - 1 - (prec - 1); + i = bit_pos >> LIMB_LOG2_BITS; + if (i >= 0) { + shift = bit_pos & (LIMB_BITS - 1); + if (shift != 0) + r->tab[i] &= limb_mask(shift, LIMB_BITS - 1); + } else { + i = 0; + } + /* remove trailing zeros */ + while (r->tab[i] == 0) + i++; + if (i > 0) { + l -= i; + memmove(r->tab, r->tab + i, l * sizeof(limb_t)); + } + bf_resize(r, l); /* cannot fail */ + return ret; +} + +/* 'r' must be a finite number. */ +int bf_normalize_and_round(bf_t *r, limb_t prec1, bf_flags_t flags) +{ + limb_t l, v, a; + int shift, ret; + slimb_t i; + + // bf_print_str("bf_renorm", r); + l = r->len; + while (l > 0 && r->tab[l - 1] == 0) + l--; + if (l == 0) { + /* zero */ + r->expn = BF_EXP_ZERO; + bf_resize(r, 0); /* cannot fail */ + ret = 0; + } else { + r->expn -= (r->len - l) * LIMB_BITS; + /* shift to have the MSB set to '1' */ + v = r->tab[l - 1]; + shift = clz(v); + if (shift != 0) { + v = 0; + for(i = 0; i < l; i++) { + a = r->tab[i]; + r->tab[i] = (a << shift) | (v >> (LIMB_BITS - shift)); + v = a; + } + r->expn -= shift; + } + ret = __bf_round(r, prec1, flags, l, 0); + } + // bf_print_str("r_final", r); + return ret; +} + +/* return true if rounding can be done at precision 'prec' assuming + the exact result r is such that |r-a| <= 2^(EXP(a)-k). */ +/* XXX: check the case where the exponent would be incremented by the + rounding */ +int bf_can_round(const bf_t *a, slimb_t prec, bf_rnd_t rnd_mode, slimb_t k) +{ + BOOL is_rndn; + slimb_t bit_pos, n; + limb_t bit; + + if (a->expn == BF_EXP_INF || a->expn == BF_EXP_NAN) + return FALSE; + if (rnd_mode == BF_RNDF) { + return (k >= (prec + 1)); + } + if (a->expn == BF_EXP_ZERO) + return FALSE; + is_rndn = (rnd_mode == BF_RNDN || rnd_mode == BF_RNDNA); + if (k < (prec + 2)) + return FALSE; + bit_pos = a->len * LIMB_BITS - 1 - prec; + n = k - prec; + /* bit pattern for RNDN or RNDNA: 0111.. or 1000... + for other rounding modes: 000... or 111... + */ + bit = get_bit(a->tab, a->len, bit_pos); + bit_pos--; + n--; + bit ^= is_rndn; + /* XXX: slow, but a few iterations on average */ + while (n != 0) { + if (get_bit(a->tab, a->len, bit_pos) != bit) + return TRUE; + bit_pos--; + n--; + } + return FALSE; +} + +/* Cannot fail with BF_ST_MEM_ERROR. */ +int bf_round(bf_t *r, limb_t prec, bf_flags_t flags) +{ + if (r->len == 0) + return 0; + return __bf_round(r, prec, flags, r->len, 0); +} + +/* for debugging */ +static __maybe_unused void dump_limbs(const char *str, const limb_t *tab, limb_t n) +{ + limb_t i; + printf("%s: len=%" PRId_LIMB "\n", str, n); + for(i = 0; i < n; i++) { + printf("%" PRId_LIMB ": " FMT_LIMB "\n", + i, tab[i]); + } +} + +void mp_print_str(const char *str, const limb_t *tab, limb_t n) +{ + slimb_t i; + printf("%s= 0x", str); + for(i = n - 1; i >= 0; i--) { + if (i != (n - 1)) + printf("_"); + printf(FMT_LIMB, tab[i]); + } + printf("\n"); +} + +static __maybe_unused void mp_print_str_h(const char *str, + const limb_t *tab, limb_t n, + limb_t high) +{ + slimb_t i; + printf("%s= 0x", str); + printf(FMT_LIMB, high); + for(i = n - 1; i >= 0; i--) { + printf("_"); + printf(FMT_LIMB, tab[i]); + } + printf("\n"); +} + +/* for debugging */ +void bf_print_str(const char *str, const bf_t *a) +{ + slimb_t i; + printf("%s=", str); + + if (a->expn == BF_EXP_NAN) { + printf("NaN"); + } else { + if (a->sign) + putchar('-'); + if (a->expn == BF_EXP_ZERO) { + putchar('0'); + } else if (a->expn == BF_EXP_INF) { + printf("Inf"); + } else { + printf("0x0."); + for(i = a->len - 1; i >= 0; i--) + printf(FMT_LIMB, a->tab[i]); + printf("p%" PRId_LIMB, a->expn); + } + } + printf("\n"); +} + +/* compare the absolute value of 'a' and 'b'. Return < 0 if a < b, 0 + if a = b and > 0 otherwise. */ +int bf_cmpu(const bf_t *a, const bf_t *b) +{ + slimb_t i; + limb_t len, v1, v2; + + if (a->expn != b->expn) { + if (a->expn < b->expn) + return -1; + else + return 1; + } + len = bf_max(a->len, b->len); + for(i = len - 1; i >= 0; i--) { + v1 = get_limbz(a, a->len - len + i); + v2 = get_limbz(b, b->len - len + i); + if (v1 != v2) { + if (v1 < v2) + return -1; + else + return 1; + } + } + return 0; +} + +/* Full order: -0 < 0, NaN == NaN and NaN is larger than all other numbers */ +int bf_cmp_full(const bf_t *a, const bf_t *b) +{ + int res; + + if (a->expn == BF_EXP_NAN || b->expn == BF_EXP_NAN) { + if (a->expn == b->expn) + res = 0; + else if (a->expn == BF_EXP_NAN) + res = 1; + else + res = -1; + } else if (a->sign != b->sign) { + res = 1 - 2 * a->sign; + } else { + res = bf_cmpu(a, b); + if (a->sign) + res = -res; + } + return res; +} + +/* Standard floating point comparison: return 2 if one of the operands + is NaN (unordered) or -1, 0, 1 depending on the ordering assuming + -0 == +0 */ +int bf_cmp(const bf_t *a, const bf_t *b) +{ + int res; + + if (a->expn == BF_EXP_NAN || b->expn == BF_EXP_NAN) { + res = 2; + } else if (a->sign != b->sign) { + if (a->expn == BF_EXP_ZERO && b->expn == BF_EXP_ZERO) + res = 0; + else + res = 1 - 2 * a->sign; + } else { + res = bf_cmpu(a, b); + if (a->sign) + res = -res; + } + return res; +} + +/* Compute the number of bits 'n' matching the pattern: + a= X1000..0 + b= X0111..1 + + When computing a-b, the result will have at least n leading zero + bits. + + Precondition: a > b and a.expn - b.expn = 0 or 1 +*/ +static limb_t count_cancelled_bits(const bf_t *a, const bf_t *b) +{ + slimb_t bit_offset, b_offset, n; + int p, p1; + limb_t v1, v2, mask; + + bit_offset = a->len * LIMB_BITS - 1; + b_offset = (b->len - a->len) * LIMB_BITS - (LIMB_BITS - 1) + + a->expn - b->expn; + n = 0; + + /* first search the equals bits */ + for(;;) { + v1 = get_limbz(a, bit_offset >> LIMB_LOG2_BITS); + v2 = get_bits(b->tab, b->len, bit_offset + b_offset); + // printf("v1=" FMT_LIMB " v2=" FMT_LIMB "\n", v1, v2); + if (v1 != v2) + break; + n += LIMB_BITS; + bit_offset -= LIMB_BITS; + } + /* find the position of the first different bit */ + p = clz(v1 ^ v2) + 1; + n += p; + /* then search for '0' in a and '1' in b */ + p = LIMB_BITS - p; + if (p > 0) { + /* search in the trailing p bits of v1 and v2 */ + mask = limb_mask(0, p - 1); + p1 = bf_min(clz(v1 & mask), clz((~v2) & mask)) - (LIMB_BITS - p); + n += p1; + if (p1 != p) + goto done; + } + bit_offset -= LIMB_BITS; + for(;;) { + v1 = get_limbz(a, bit_offset >> LIMB_LOG2_BITS); + v2 = get_bits(b->tab, b->len, bit_offset + b_offset); + // printf("v1=" FMT_LIMB " v2=" FMT_LIMB "\n", v1, v2); + if (v1 != 0 || v2 != -1) { + /* different: count the matching bits */ + p1 = bf_min(clz(v1), clz(~v2)); + n += p1; + break; + } + n += LIMB_BITS; + bit_offset -= LIMB_BITS; + } + done: + return n; +} + +static int bf_add_internal(bf_t *r, const bf_t *a, const bf_t *b, limb_t prec, + bf_flags_t flags, int b_neg) +{ + const bf_t *tmp; + int is_sub, ret, cmp_res, a_sign, b_sign; + + a_sign = a->sign; + b_sign = b->sign ^ b_neg; + is_sub = a_sign ^ b_sign; + cmp_res = bf_cmpu(a, b); + if (cmp_res < 0) { + tmp = a; + a = b; + b = tmp; + a_sign = b_sign; /* b_sign is never used later */ + } + /* abs(a) >= abs(b) */ + if (cmp_res == 0 && is_sub && a->expn < BF_EXP_INF) { + /* zero result */ + bf_set_zero(r, (flags & BF_RND_MASK) == BF_RNDD); + ret = 0; + } else if (a->len == 0 || b->len == 0) { + ret = 0; + if (a->expn >= BF_EXP_INF) { + if (a->expn == BF_EXP_NAN) { + /* at least one operand is NaN */ + bf_set_nan(r); + } else if (b->expn == BF_EXP_INF && is_sub) { + /* infinities with different signs */ + bf_set_nan(r); + ret = BF_ST_INVALID_OP; + } else { + bf_set_inf(r, a_sign); + } + } else { + /* at least one zero and not subtract */ + bf_set(r, a); + r->sign = a_sign; + goto renorm; + } + } else { + slimb_t d, a_offset, b_bit_offset, i, cancelled_bits; + limb_t carry, v1, v2, u, r_len, carry1, precl, tot_len, z, sub_mask; + + r->sign = a_sign; + r->expn = a->expn; + d = a->expn - b->expn; + /* must add more precision for the leading cancelled bits in + subtraction */ + if (is_sub) { + if (d <= 1) + cancelled_bits = count_cancelled_bits(a, b); + else + cancelled_bits = 1; + } else { + cancelled_bits = 0; + } + + /* add two extra bits for rounding */ + precl = (cancelled_bits + prec + 2 + LIMB_BITS - 1) / LIMB_BITS; + tot_len = bf_max(a->len, b->len + (d + LIMB_BITS - 1) / LIMB_BITS); + r_len = bf_min(precl, tot_len); + if (bf_resize(r, r_len)) + goto fail; + a_offset = a->len - r_len; + b_bit_offset = (b->len - r_len) * LIMB_BITS + d; + + /* compute the bits before for the rounding */ + carry = is_sub; + z = 0; + sub_mask = -is_sub; + i = r_len - tot_len; + while (i < 0) { + slimb_t ap, bp; + BOOL inflag; + + ap = a_offset + i; + bp = b_bit_offset + i * LIMB_BITS; + inflag = FALSE; + if (ap >= 0 && ap < a->len) { + v1 = a->tab[ap]; + inflag = TRUE; + } else { + v1 = 0; + } + if (bp + LIMB_BITS > 0 && bp < (slimb_t)(b->len * LIMB_BITS)) { + v2 = get_bits(b->tab, b->len, bp); + inflag = TRUE; + } else { + v2 = 0; + } + if (!inflag) { + /* outside 'a' and 'b': go directly to the next value + inside a or b so that the running time does not + depend on the exponent difference */ + i = 0; + if (ap < 0) + i = bf_min(i, -a_offset); + /* b_bit_offset + i * LIMB_BITS + LIMB_BITS >= 1 + equivalent to + i >= ceil(-b_bit_offset + 1 - LIMB_BITS) / LIMB_BITS) + */ + if (bp + LIMB_BITS <= 0) + i = bf_min(i, (-b_bit_offset) >> LIMB_LOG2_BITS); + } else { + i++; + } + v2 ^= sub_mask; + u = v1 + v2; + carry1 = u < v1; + u += carry; + carry = (u < carry) | carry1; + z |= u; + } + /* and the result */ + for(i = 0; i < r_len; i++) { + v1 = get_limbz(a, a_offset + i); + v2 = get_bits(b->tab, b->len, b_bit_offset + i * LIMB_BITS); + v2 ^= sub_mask; + u = v1 + v2; + carry1 = u < v1; + u += carry; + carry = (u < carry) | carry1; + r->tab[i] = u; + } + /* set the extra bits for the rounding */ + r->tab[0] |= (z != 0); + + /* carry is only possible in add case */ + if (!is_sub && carry) { + if (bf_resize(r, r_len + 1)) + goto fail; + r->tab[r_len] = 1; + r->expn += LIMB_BITS; + } + renorm: + ret = bf_normalize_and_round(r, prec, flags); + } + return ret; + fail: + bf_set_nan(r); + return BF_ST_MEM_ERROR; +} + +static int __bf_add(bf_t *r, const bf_t *a, const bf_t *b, limb_t prec, + bf_flags_t flags) +{ + return bf_add_internal(r, a, b, prec, flags, 0); +} + +static int __bf_sub(bf_t *r, const bf_t *a, const bf_t *b, limb_t prec, + bf_flags_t flags) +{ + return bf_add_internal(r, a, b, prec, flags, 1); +} + +limb_t mp_add(limb_t *res, const limb_t *op1, const limb_t *op2, + limb_t n, limb_t carry) +{ + slimb_t i; + limb_t k, a, v, k1; + + k = carry; + for(i=0;i<n;i++) { + v = op1[i]; + a = v + op2[i]; + k1 = a < v; + a = a + k; + k = (a < k) | k1; + res[i] = a; + } + return k; +} + +limb_t mp_add_ui(limb_t *tab, limb_t b, size_t n) +{ + size_t i; + limb_t k, a; + + k=b; + for(i=0;i<n;i++) { + if (k == 0) + break; + a = tab[i] + k; + k = (a < k); + tab[i] = a; + } + return k; +} + +limb_t mp_sub(limb_t *res, const limb_t *op1, const limb_t *op2, + mp_size_t n, limb_t carry) +{ + int i; + limb_t k, a, v, k1; + + k = carry; + for(i=0;i<n;i++) { + v = op1[i]; + a = v - op2[i]; + k1 = a > v; + v = a - k; + k = (v > a) | k1; + res[i] = v; + } + return k; +} + +/* compute 0 - op2 */ +static limb_t mp_neg(limb_t *res, const limb_t *op2, mp_size_t n, limb_t carry) +{ + int i; + limb_t k, a, v, k1; + + k = carry; + for(i=0;i<n;i++) { + v = 0; + a = v - op2[i]; + k1 = a > v; + v = a - k; + k = (v > a) | k1; + res[i] = v; + } + return k; +} + +limb_t mp_sub_ui(limb_t *tab, limb_t b, mp_size_t n) +{ + mp_size_t i; + limb_t k, a, v; + + k=b; + for(i=0;i<n;i++) { + v = tab[i]; + a = v - k; + k = a > v; + tab[i] = a; + if (k == 0) + break; + } + return k; +} + +/* r = (a + high*B^n) >> shift. Return the remainder r (0 <= r < 2^shift). + 1 <= shift <= LIMB_BITS - 1 */ +static limb_t mp_shr(limb_t *tab_r, const limb_t *tab, mp_size_t n, + int shift, limb_t high) +{ + mp_size_t i; + limb_t l, a; + + assert(shift >= 1 && shift < LIMB_BITS); + l = high; + for(i = n - 1; i >= 0; i--) { + a = tab[i]; + tab_r[i] = (a >> shift) | (l << (LIMB_BITS - shift)); + l = a; + } + return l & (((limb_t)1 << shift) - 1); +} + +/* tabr[] = taba[] * b + l. Return the high carry */ +static limb_t mp_mul1(limb_t *tabr, const limb_t *taba, limb_t n, + limb_t b, limb_t l) +{ + limb_t i; + dlimb_t t; + + for(i = 0; i < n; i++) { + t = (dlimb_t)taba[i] * (dlimb_t)b + l; + tabr[i] = t; + l = t >> LIMB_BITS; + } + return l; +} + +/* tabr[] += taba[] * b, return the high word. */ +static limb_t mp_add_mul1(limb_t *tabr, const limb_t *taba, limb_t n, + limb_t b) +{ + limb_t i, l; + dlimb_t t; + + l = 0; + for(i = 0; i < n; i++) { + t = (dlimb_t)taba[i] * (dlimb_t)b + l + tabr[i]; + tabr[i] = t; + l = t >> LIMB_BITS; + } + return l; +} + +/* size of the result : op1_size + op2_size. */ +static void mp_mul_basecase(limb_t *result, + const limb_t *op1, limb_t op1_size, + const limb_t *op2, limb_t op2_size) +{ + limb_t i, r; + + result[op1_size] = mp_mul1(result, op1, op1_size, op2[0], 0); + for(i=1;i<op2_size;i++) { + r = mp_add_mul1(result + i, op1, op1_size, op2[i]); + result[i + op1_size] = r; + } +} + +/* return 0 if OK, -1 if memory error */ +/* XXX: change API so that result can be allocated */ +int mp_mul(bf_context_t *s, limb_t *result, + const limb_t *op1, limb_t op1_size, + const limb_t *op2, limb_t op2_size) +{ +#ifdef USE_FFT_MUL + if (unlikely(bf_min(op1_size, op2_size) >= FFT_MUL_THRESHOLD)) { + bf_t r_s, *r = &r_s; + r->tab = result; + /* XXX: optimize memory usage in API */ + if (fft_mul(s, r, (limb_t *)op1, op1_size, + (limb_t *)op2, op2_size, FFT_MUL_R_NORESIZE)) + return -1; + } else +#endif + { + mp_mul_basecase(result, op1, op1_size, op2, op2_size); + } + return 0; +} + +/* tabr[] -= taba[] * b. Return the value to substract to the high + word. */ +static limb_t mp_sub_mul1(limb_t *tabr, const limb_t *taba, limb_t n, + limb_t b) +{ + limb_t i, l; + dlimb_t t; + + l = 0; + for(i = 0; i < n; i++) { + t = tabr[i] - (dlimb_t)taba[i] * (dlimb_t)b - l; + tabr[i] = t; + l = -(t >> LIMB_BITS); + } + return l; +} + +/* WARNING: d must be >= 2^(LIMB_BITS-1) */ +static inline limb_t udiv1norm_init(limb_t d) +{ + limb_t a0, a1; + a1 = -d - 1; + a0 = -1; + return (((dlimb_t)a1 << LIMB_BITS) | a0) / d; +} + +/* return the quotient and the remainder in '*pr'of 'a1*2^LIMB_BITS+a0 + / d' with 0 <= a1 < d. */ +static inline limb_t udiv1norm(limb_t *pr, limb_t a1, limb_t a0, + limb_t d, limb_t d_inv) +{ + limb_t n1m, n_adj, q, r, ah; + dlimb_t a; + n1m = ((slimb_t)a0 >> (LIMB_BITS - 1)); + n_adj = a0 + (n1m & d); + a = (dlimb_t)d_inv * (a1 - n1m) + n_adj; + q = (a >> LIMB_BITS) + a1; + /* compute a - q * r and update q so that the remainder is\ + between 0 and d - 1 */ + a = ((dlimb_t)a1 << LIMB_BITS) | a0; + a = a - (dlimb_t)q * d - d; + ah = a >> LIMB_BITS; + q += 1 + ah; + r = (limb_t)a + (ah & d); + *pr = r; + return q; +} + +/* b must be >= 1 << (LIMB_BITS - 1) */ +static limb_t mp_div1norm(limb_t *tabr, const limb_t *taba, limb_t n, + limb_t b, limb_t r) +{ + slimb_t i; + + if (n >= UDIV1NORM_THRESHOLD) { + limb_t b_inv; + b_inv = udiv1norm_init(b); + for(i = n - 1; i >= 0; i--) { + tabr[i] = udiv1norm(&r, r, taba[i], b, b_inv); + } + } else { + dlimb_t a1; + for(i = n - 1; i >= 0; i--) { + a1 = ((dlimb_t)r << LIMB_BITS) | taba[i]; + tabr[i] = a1 / b; + r = a1 % b; + } + } + return r; +} + +static int mp_divnorm_large(bf_context_t *s, + limb_t *tabq, limb_t *taba, limb_t na, + const limb_t *tabb, limb_t nb); + +/* base case division: divides taba[0..na-1] by tabb[0..nb-1]. tabb[nb + - 1] must be >= 1 << (LIMB_BITS - 1). na - nb must be >= 0. 'taba' + is modified and contains the remainder (nb limbs). tabq[0..na-nb] + contains the quotient with tabq[na - nb] <= 1. */ +static int mp_divnorm(bf_context_t *s, limb_t *tabq, limb_t *taba, limb_t na, + const limb_t *tabb, limb_t nb) +{ + limb_t r, a, c, q, v, b1, b1_inv, n, dummy_r; + slimb_t i, j; + + b1 = tabb[nb - 1]; + if (nb == 1) { + taba[0] = mp_div1norm(tabq, taba, na, b1, 0); + return 0; + } + n = na - nb; + if (bf_min(n, nb) >= DIVNORM_LARGE_THRESHOLD) { + return mp_divnorm_large(s, tabq, taba, na, tabb, nb); + } + + if (n >= UDIV1NORM_THRESHOLD) + b1_inv = udiv1norm_init(b1); + else + b1_inv = 0; + + /* first iteration: the quotient is only 0 or 1 */ + q = 1; + for(j = nb - 1; j >= 0; j--) { + if (taba[n + j] != tabb[j]) { + if (taba[n + j] < tabb[j]) + q = 0; + break; + } + } + tabq[n] = q; + if (q) { + mp_sub(taba + n, taba + n, tabb, nb, 0); + } + + for(i = n - 1; i >= 0; i--) { + if (unlikely(taba[i + nb] >= b1)) { + q = -1; + } else if (b1_inv) { + q = udiv1norm(&dummy_r, taba[i + nb], taba[i + nb - 1], b1, b1_inv); + } else { + dlimb_t al; + al = ((dlimb_t)taba[i + nb] << LIMB_BITS) | taba[i + nb - 1]; + q = al / b1; + r = al % b1; + } + r = mp_sub_mul1(taba + i, tabb, nb, q); + + v = taba[i + nb]; + a = v - r; + c = (a > v); + taba[i + nb] = a; + + if (c != 0) { + /* negative result */ + for(;;) { + q--; + c = mp_add(taba + i, taba + i, tabb, nb, 0); + /* propagate carry and test if positive result */ + if (c != 0) { + if (++taba[i + nb] == 0) { + break; + } + } + } + } + tabq[i] = q; + } + return 0; +} + +/* compute r=B^(2*n)/a such as a*r < B^(2*n) < a*r + 2 with n >= 1. 'a' + has n limbs with a[n-1] >= B/2 and 'r' has n+1 limbs with r[n] = 1. + + See Modern Computer Arithmetic by Richard P. Brent and Paul + Zimmermann, algorithm 3.5 */ +int mp_recip(bf_context_t *s, limb_t *tabr, const limb_t *taba, limb_t n) +{ + mp_size_t l, h, k, i; + limb_t *tabxh, *tabt, c, *tabu; + + if (n <= 2) { + /* return ceil(B^(2*n)/a) - 1 */ + /* XXX: could avoid allocation */ + tabu = bf_malloc(s, sizeof(limb_t) * (2 * n + 1)); + tabt = bf_malloc(s, sizeof(limb_t) * (n + 2)); + if (!tabt || !tabu) + goto fail; + for(i = 0; i < 2 * n; i++) + tabu[i] = 0; + tabu[2 * n] = 1; + if (mp_divnorm(s, tabt, tabu, 2 * n + 1, taba, n)) + goto fail; + for(i = 0; i < n + 1; i++) + tabr[i] = tabt[i]; + if (mp_scan_nz(tabu, n) == 0) { + /* only happens for a=B^n/2 */ + mp_sub_ui(tabr, 1, n + 1); + } + } else { + l = (n - 1) / 2; + h = n - l; + /* n=2p -> l=p-1, h = p + 1, k = p + 3 + n=2p+1-> l=p, h = p + 1; k = p + 2 + */ + tabt = bf_malloc(s, sizeof(limb_t) * (n + h + 1)); + tabu = bf_malloc(s, sizeof(limb_t) * (n + 2 * h - l + 2)); + if (!tabt || !tabu) + goto fail; + tabxh = tabr + l; + if (mp_recip(s, tabxh, taba + l, h)) + goto fail; + if (mp_mul(s, tabt, taba, n, tabxh, h + 1)) /* n + h + 1 limbs */ + goto fail; + while (tabt[n + h] != 0) { + mp_sub_ui(tabxh, 1, h + 1); + c = mp_sub(tabt, tabt, taba, n, 0); + mp_sub_ui(tabt + n, c, h + 1); + } + /* T = B^(n+h) - T */ + mp_neg(tabt, tabt, n + h + 1, 0); + tabt[n + h]++; + if (mp_mul(s, tabu, tabt + l, n + h + 1 - l, tabxh, h + 1)) + goto fail; + /* n + 2*h - l + 2 limbs */ + k = 2 * h - l; + for(i = 0; i < l; i++) + tabr[i] = tabu[i + k]; + mp_add(tabr + l, tabr + l, tabu + 2 * h, h, 0); + } + bf_free(s, tabt); + bf_free(s, tabu); + return 0; + fail: + bf_free(s, tabt); + bf_free(s, tabu); + return -1; +} + +/* return -1, 0 or 1 */ +static int mp_cmp(const limb_t *taba, const limb_t *tabb, mp_size_t n) +{ + mp_size_t i; + for(i = n - 1; i >= 0; i--) { + if (taba[i] != tabb[i]) { + if (taba[i] < tabb[i]) + return -1; + else + return 1; + } + } + return 0; +} + +//#define DEBUG_DIVNORM_LARGE +//#define DEBUG_DIVNORM_LARGE2 + +/* subquadratic divnorm */ +static int mp_divnorm_large(bf_context_t *s, + limb_t *tabq, limb_t *taba, limb_t na, + const limb_t *tabb, limb_t nb) +{ + limb_t *tabb_inv, nq, *tabt, i, n; + nq = na - nb; +#ifdef DEBUG_DIVNORM_LARGE + printf("na=%d nb=%d nq=%d\n", (int)na, (int)nb, (int)nq); + mp_print_str("a", taba, na); + mp_print_str("b", tabb, nb); +#endif + assert(nq >= 1); + n = nq; + if (nq < nb) + n++; + tabb_inv = bf_malloc(s, sizeof(limb_t) * (n + 1)); + tabt = bf_malloc(s, sizeof(limb_t) * 2 * (n + 1)); + if (!tabb_inv || !tabt) + goto fail; + + if (n >= nb) { + for(i = 0; i < n - nb; i++) + tabt[i] = 0; + for(i = 0; i < nb; i++) + tabt[i + n - nb] = tabb[i]; + } else { + /* truncate B: need to increment it so that the approximate + inverse is smaller that the exact inverse */ + for(i = 0; i < n; i++) + tabt[i] = tabb[i + nb - n]; + if (mp_add_ui(tabt, 1, n)) { + /* tabt = B^n : tabb_inv = B^n */ + memset(tabb_inv, 0, n * sizeof(limb_t)); + tabb_inv[n] = 1; + goto recip_done; + } + } + if (mp_recip(s, tabb_inv, tabt, n)) + goto fail; + recip_done: + /* Q=A*B^-1 */ + if (mp_mul(s, tabt, tabb_inv, n + 1, taba + na - (n + 1), n + 1)) + goto fail; + + for(i = 0; i < nq + 1; i++) + tabq[i] = tabt[i + 2 * (n + 1) - (nq + 1)]; +#ifdef DEBUG_DIVNORM_LARGE + mp_print_str("q", tabq, nq + 1); +#endif + + bf_free(s, tabt); + bf_free(s, tabb_inv); + tabb_inv = NULL; + + /* R=A-B*Q */ + tabt = bf_malloc(s, sizeof(limb_t) * (na + 1)); + if (!tabt) + goto fail; + if (mp_mul(s, tabt, tabq, nq + 1, tabb, nb)) + goto fail; + /* we add one more limb for the result */ + mp_sub(taba, taba, tabt, nb + 1, 0); + bf_free(s, tabt); + /* the approximated quotient is smaller than than the exact one, + hence we may have to increment it */ +#ifdef DEBUG_DIVNORM_LARGE2 + int cnt = 0; + static int cnt_max; +#endif + for(;;) { + if (taba[nb] == 0 && mp_cmp(taba, tabb, nb) < 0) + break; + taba[nb] -= mp_sub(taba, taba, tabb, nb, 0); + mp_add_ui(tabq, 1, nq + 1); +#ifdef DEBUG_DIVNORM_LARGE2 + cnt++; +#endif + } +#ifdef DEBUG_DIVNORM_LARGE2 + if (cnt > cnt_max) { + cnt_max = cnt; + printf("\ncnt=%d nq=%d nb=%d\n", cnt_max, (int)nq, (int)nb); + } +#endif + return 0; + fail: + bf_free(s, tabb_inv); + bf_free(s, tabt); + return -1; +} + +int bf_mul(bf_t *r, const bf_t *a, const bf_t *b, limb_t prec, + bf_flags_t flags) +{ + int ret, r_sign; + + if (a->len < b->len) { + const bf_t *tmp = a; + a = b; + b = tmp; + } + r_sign = a->sign ^ b->sign; + /* here b->len <= a->len */ + if (b->len == 0) { + if (a->expn == BF_EXP_NAN || b->expn == BF_EXP_NAN) { + bf_set_nan(r); + ret = 0; + } else if (a->expn == BF_EXP_INF || b->expn == BF_EXP_INF) { + if ((a->expn == BF_EXP_INF && b->expn == BF_EXP_ZERO) || + (a->expn == BF_EXP_ZERO && b->expn == BF_EXP_INF)) { + bf_set_nan(r); + ret = BF_ST_INVALID_OP; + } else { + bf_set_inf(r, r_sign); + ret = 0; + } + } else { + bf_set_zero(r, r_sign); + ret = 0; + } + } else { + bf_t tmp, *r1 = NULL; + limb_t a_len, b_len, precl; + limb_t *a_tab, *b_tab; + + a_len = a->len; + b_len = b->len; + + if ((flags & BF_RND_MASK) == BF_RNDF) { + /* faithful rounding does not require using the full inputs */ + precl = (prec + 2 + LIMB_BITS - 1) / LIMB_BITS; + a_len = bf_min(a_len, precl); + b_len = bf_min(b_len, precl); + } + a_tab = a->tab + a->len - a_len; + b_tab = b->tab + b->len - b_len; + +#ifdef USE_FFT_MUL + if (b_len >= FFT_MUL_THRESHOLD) { + int mul_flags = 0; + if (r == a) + mul_flags |= FFT_MUL_R_OVERLAP_A; + if (r == b) + mul_flags |= FFT_MUL_R_OVERLAP_B; + if (fft_mul(r->ctx, r, a_tab, a_len, b_tab, b_len, mul_flags)) + goto fail; + } else +#endif + { + if (r == a || r == b) { + bf_init(r->ctx, &tmp); + r1 = r; + r = &tmp; + } + if (bf_resize(r, a_len + b_len)) { + fail: + bf_set_nan(r); + ret = BF_ST_MEM_ERROR; + goto done; + } + mp_mul_basecase(r->tab, a_tab, a_len, b_tab, b_len); + } + r->sign = r_sign; + r->expn = a->expn + b->expn; + ret = bf_normalize_and_round(r, prec, flags); + done: + if (r == &tmp) + bf_move(r1, &tmp); + } + return ret; +} + +/* multiply 'r' by 2^e */ +int bf_mul_2exp(bf_t *r, slimb_t e, limb_t prec, bf_flags_t flags) +{ + slimb_t e_max; + if (r->len == 0) + return 0; + e_max = ((limb_t)1 << BF_EXT_EXP_BITS_MAX) - 1; + e = bf_max(e, -e_max); + e = bf_min(e, e_max); + r->expn += e; + return __bf_round(r, prec, flags, r->len, 0); +} + +/* Return e such as a=m*2^e with m odd integer. return 0 if a is zero, + Infinite or Nan. */ +slimb_t bf_get_exp_min(const bf_t *a) +{ + slimb_t i; + limb_t v; + int k; + + for(i = 0; i < a->len; i++) { + v = a->tab[i]; + if (v != 0) { + k = ctz(v); + return a->expn - (a->len - i) * LIMB_BITS + k; + } + } + return 0; +} + +/* a and b must be finite numbers with a >= 0 and b > 0. 'q' is the + integer defined as floor(a/b) and r = a - q * b. */ +static void bf_tdivremu(bf_t *q, bf_t *r, + const bf_t *a, const bf_t *b) +{ + if (bf_cmpu(a, b) < 0) { + bf_set_ui(q, 0); + bf_set(r, a); + } else { + bf_div(q, a, b, bf_max(a->expn - b->expn + 1, 2), BF_RNDZ); + bf_rint(q, BF_RNDZ); + bf_mul(r, q, b, BF_PREC_INF, BF_RNDZ); + bf_sub(r, a, r, BF_PREC_INF, BF_RNDZ); + } +} + +static int __bf_div(bf_t *r, const bf_t *a, const bf_t *b, limb_t prec, + bf_flags_t flags) +{ + bf_context_t *s = r->ctx; + int ret, r_sign; + limb_t n, nb, precl; + + r_sign = a->sign ^ b->sign; + if (a->expn >= BF_EXP_INF || b->expn >= BF_EXP_INF) { + if (a->expn == BF_EXP_NAN || b->expn == BF_EXP_NAN) { + bf_set_nan(r); + return 0; + } else if (a->expn == BF_EXP_INF && b->expn == BF_EXP_INF) { + bf_set_nan(r); + return BF_ST_INVALID_OP; + } else if (a->expn == BF_EXP_INF) { + bf_set_inf(r, r_sign); + return 0; + } else { + bf_set_zero(r, r_sign); + return 0; + } + } else if (a->expn == BF_EXP_ZERO) { + if (b->expn == BF_EXP_ZERO) { + bf_set_nan(r); + return BF_ST_INVALID_OP; + } else { + bf_set_zero(r, r_sign); + return 0; + } + } else if (b->expn == BF_EXP_ZERO) { + bf_set_inf(r, r_sign); + return BF_ST_DIVIDE_ZERO; + } + + /* number of limbs of the quotient (2 extra bits for rounding) */ + precl = (prec + 2 + LIMB_BITS - 1) / LIMB_BITS; + nb = b->len; + n = bf_max(a->len, precl); + + { + limb_t *taba, na; + slimb_t d; + + na = n + nb; + +#if LIMB_LOG2_BITS == 6 + if (na >= (SIZE_MAX / sizeof(limb_t)) - 1) { + return BF_ST_MEM_ERROR; /* Return memory error status */ + } +#endif + + taba = bf_malloc(s, (na + 1) * sizeof(limb_t)); + if (!taba) + goto fail; + d = na - a->len; + memset(taba, 0, d * sizeof(limb_t)); + memcpy(taba + d, a->tab, a->len * sizeof(limb_t)); + if (bf_resize(r, n + 1)) + goto fail1; + if (mp_divnorm(s, r->tab, taba, na, b->tab, nb)) { + fail1: + bf_free(s, taba); + goto fail; + } + /* see if non zero remainder */ + if (mp_scan_nz(taba, nb)) + r->tab[0] |= 1; + bf_free(r->ctx, taba); + r->expn = a->expn - b->expn + LIMB_BITS; + r->sign = r_sign; + ret = bf_normalize_and_round(r, prec, flags); + } + return ret; + fail: + bf_set_nan(r); + return BF_ST_MEM_ERROR; +} + +/* division and remainder. + + rnd_mode is the rounding mode for the quotient. The additional + rounding mode BF_RND_EUCLIDIAN is supported. + + 'q' is an integer. 'r' is rounded with prec and flags (prec can be + BF_PREC_INF). +*/ +int bf_divrem(bf_t *q, bf_t *r, const bf_t *a, const bf_t *b, + limb_t prec, bf_flags_t flags, int rnd_mode) +{ + bf_t a1_s, *a1 = &a1_s; + bf_t b1_s, *b1 = &b1_s; + int q_sign, ret; + BOOL is_ceil, is_rndn; + + assert(q != a && q != b); + assert(r != a && r != b); + assert(q != r); + + if (a->len == 0 || b->len == 0) { + bf_set_zero(q, 0); + if (a->expn == BF_EXP_NAN || b->expn == BF_EXP_NAN) { + bf_set_nan(r); + return 0; + } else if (a->expn == BF_EXP_INF || b->expn == BF_EXP_ZERO) { + bf_set_nan(r); + return BF_ST_INVALID_OP; + } else { + bf_set(r, a); + return bf_round(r, prec, flags); + } + } + + q_sign = a->sign ^ b->sign; + is_rndn = (rnd_mode == BF_RNDN || rnd_mode == BF_RNDNA); + switch(rnd_mode) { + default: + case BF_RNDZ: + case BF_RNDN: + case BF_RNDNA: + is_ceil = FALSE; + break; + case BF_RNDD: + is_ceil = q_sign; + break; + case BF_RNDU: + is_ceil = q_sign ^ 1; + break; + case BF_RNDA: + is_ceil = TRUE; + break; + case BF_DIVREM_EUCLIDIAN: + is_ceil = a->sign; + break; + } + + a1->expn = a->expn; + a1->tab = a->tab; + a1->len = a->len; + a1->sign = 0; + + b1->expn = b->expn; + b1->tab = b->tab; + b1->len = b->len; + b1->sign = 0; + + /* XXX: could improve to avoid having a large 'q' */ + bf_tdivremu(q, r, a1, b1); + if (bf_is_nan(q) || bf_is_nan(r)) + goto fail; + + if (r->len != 0) { + if (is_rndn) { + int res; + b1->expn--; + res = bf_cmpu(r, b1); + b1->expn++; + if (res > 0 || + (res == 0 && + (rnd_mode == BF_RNDNA || + get_bit(q->tab, q->len, q->len * LIMB_BITS - q->expn)))) { + goto do_sub_r; + } + } else if (is_ceil) { + do_sub_r: + ret = bf_add_si(q, q, 1, BF_PREC_INF, BF_RNDZ); + ret |= bf_sub(r, r, b1, BF_PREC_INF, BF_RNDZ); + if (ret & BF_ST_MEM_ERROR) + goto fail; + } + } + + r->sign ^= a->sign; + q->sign = q_sign; + return bf_round(r, prec, flags); + fail: + bf_set_nan(q); + bf_set_nan(r); + return BF_ST_MEM_ERROR; +} + +int bf_rem(bf_t *r, const bf_t *a, const bf_t *b, limb_t prec, + bf_flags_t flags, int rnd_mode) +{ + bf_t q_s, *q = &q_s; + int ret; + + bf_init(r->ctx, q); + ret = bf_divrem(q, r, a, b, prec, flags, rnd_mode); + bf_delete(q); + return ret; +} + +static inline int bf_get_limb(slimb_t *pres, const bf_t *a, int flags) +{ +#if LIMB_BITS == 32 + return bf_get_int32(pres, a, flags); +#else + return bf_get_int64(pres, a, flags); +#endif +} + +int bf_remquo(slimb_t *pq, bf_t *r, const bf_t *a, const bf_t *b, limb_t prec, + bf_flags_t flags, int rnd_mode) +{ + bf_t q_s, *q = &q_s; + int ret; + + bf_init(r->ctx, q); + ret = bf_divrem(q, r, a, b, prec, flags, rnd_mode); + bf_get_limb(pq, q, BF_GET_INT_MOD); + bf_delete(q); + return ret; +} + +static __maybe_unused inline limb_t mul_mod(limb_t a, limb_t b, limb_t m) +{ + dlimb_t t; + t = (dlimb_t)a * (dlimb_t)b; + return t % m; +} + +#if defined(USE_MUL_CHECK) +static limb_t mp_mod1(const limb_t *tab, limb_t n, limb_t m, limb_t r) +{ + slimb_t i; + dlimb_t t; + + for(i = n - 1; i >= 0; i--) { + t = ((dlimb_t)r << LIMB_BITS) | tab[i]; + r = t % m; + } + return r; +} +#endif + +static const uint16_t sqrt_table[192] = { +128,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,144,145,146,147,148,149,150,150,151,152,153,154,155,155,156,157,158,159,160,160,161,162,163,163,164,165,166,167,167,168,169,170,170,171,172,173,173,174,175,176,176,177,178,178,179,180,181,181,182,183,183,184,185,185,186,187,187,188,189,189,190,191,192,192,193,193,194,195,195,196,197,197,198,199,199,200,201,201,202,203,203,204,204,205,206,206,207,208,208,209,209,210,211,211,212,212,213,214,214,215,215,216,217,217,218,218,219,219,220,221,221,222,222,223,224,224,225,225,226,226,227,227,228,229,229,230,230,231,231,232,232,233,234,234,235,235,236,236,237,237,238,238,239,240,240,241,241,242,242,243,243,244,244,245,245,246,246,247,247,248,248,249,249,250,250,251,251,252,252,253,253,254,254,255, +}; + +/* a >= 2^(LIMB_BITS - 2). Return (s, r) with s=floor(sqrt(a)) and + r=a-s^2. 0 <= r <= 2 * s */ +static limb_t mp_sqrtrem1(limb_t *pr, limb_t a) +{ + limb_t s1, r1, s, r, q, u, num; + + /* use a table for the 16 -> 8 bit sqrt */ + s1 = sqrt_table[(a >> (LIMB_BITS - 8)) - 64]; + r1 = (a >> (LIMB_BITS - 16)) - s1 * s1; + if (r1 > 2 * s1) { + r1 -= 2 * s1 + 1; + s1++; + } + + /* one iteration to get a 32 -> 16 bit sqrt */ + num = (r1 << 8) | ((a >> (LIMB_BITS - 32 + 8)) & 0xff); + q = num / (2 * s1); /* q <= 2^8 */ + u = num % (2 * s1); + s = (s1 << 8) + q; + r = (u << 8) | ((a >> (LIMB_BITS - 32)) & 0xff); + r -= q * q; + if ((slimb_t)r < 0) { + s--; + r += 2 * s + 1; + } + +#if LIMB_BITS == 64 + s1 = s; + r1 = r; + /* one more iteration for 64 -> 32 bit sqrt */ + num = (r1 << 16) | ((a >> (LIMB_BITS - 64 + 16)) & 0xffff); + q = num / (2 * s1); /* q <= 2^16 */ + u = num % (2 * s1); + s = (s1 << 16) + q; + r = (u << 16) | ((a >> (LIMB_BITS - 64)) & 0xffff); + r -= q * q; + if ((slimb_t)r < 0) { + s--; + r += 2 * s + 1; + } +#endif + *pr = r; + return s; +} + +/* return floor(sqrt(a)) */ +limb_t bf_isqrt(limb_t a) +{ + limb_t s, r; + int k; + + if (a == 0) + return 0; + k = clz(a) & ~1; + s = mp_sqrtrem1(&r, a << k); + s >>= (k >> 1); + return s; +} + +static limb_t mp_sqrtrem2(limb_t *tabs, limb_t *taba) +{ + limb_t s1, r1, s, q, u, a0, a1; + dlimb_t r, num; + int l; + + a0 = taba[0]; + a1 = taba[1]; + s1 = mp_sqrtrem1(&r1, a1); + l = LIMB_BITS / 2; + num = ((dlimb_t)r1 << l) | (a0 >> l); + q = num / (2 * s1); + u = num % (2 * s1); + s = (s1 << l) + q; + r = ((dlimb_t)u << l) | (a0 & (((limb_t)1 << l) - 1)); + if (unlikely((q >> l) != 0)) + r -= (dlimb_t)1 << LIMB_BITS; /* special case when q=2^l */ + else + r -= q * q; + if ((slimb_t)(r >> LIMB_BITS) < 0) { + s--; + r += 2 * (dlimb_t)s + 1; + } + tabs[0] = s; + taba[0] = r; + return r >> LIMB_BITS; +} + +//#define DEBUG_SQRTREM + +/* tmp_buf must contain (n / 2 + 1 limbs). *prh contains the highest + limb of the remainder. */ +static int mp_sqrtrem_rec(bf_context_t *s, limb_t *tabs, limb_t *taba, limb_t n, + limb_t *tmp_buf, limb_t *prh) +{ + limb_t l, h, rh, ql, qh, c, i; + + if (n == 1) { + *prh = mp_sqrtrem2(tabs, taba); + return 0; + } +#ifdef DEBUG_SQRTREM + mp_print_str("a", taba, 2 * n); +#endif + l = n / 2; + h = n - l; + if (mp_sqrtrem_rec(s, tabs + l, taba + 2 * l, h, tmp_buf, &qh)) + return -1; +#ifdef DEBUG_SQRTREM + mp_print_str("s1", tabs + l, h); + mp_print_str_h("r1", taba + 2 * l, h, qh); + mp_print_str_h("r2", taba + l, n, qh); +#endif + + /* the remainder is in taba + 2 * l. Its high bit is in qh */ + if (qh) { + mp_sub(taba + 2 * l, taba + 2 * l, tabs + l, h, 0); + } + /* instead of dividing by 2*s, divide by s (which is normalized) + and update q and r */ + if (mp_divnorm(s, tmp_buf, taba + l, n, tabs + l, h)) + return -1; + qh += tmp_buf[l]; + for(i = 0; i < l; i++) + tabs[i] = tmp_buf[i]; + ql = mp_shr(tabs, tabs, l, 1, qh & 1); + qh = qh >> 1; /* 0 or 1 */ + if (ql) + rh = mp_add(taba + l, taba + l, tabs + l, h, 0); + else + rh = 0; +#ifdef DEBUG_SQRTREM + mp_print_str_h("q", tabs, l, qh); + mp_print_str_h("u", taba + l, h, rh); +#endif + + mp_add_ui(tabs + l, qh, h); +#ifdef DEBUG_SQRTREM + mp_print_str_h("s2", tabs, n, sh); +#endif + + /* q = qh, tabs[l - 1 ... 0], r = taba[n - 1 ... l] */ + /* subtract q^2. if qh = 1 then q = B^l, so we can take shortcuts */ + if (qh) { + c = qh; + } else { + if (mp_mul(s, taba + n, tabs, l, tabs, l)) + return -1; + c = mp_sub(taba, taba, taba + n, 2 * l, 0); + } + rh -= mp_sub_ui(taba + 2 * l, c, n - 2 * l); + if ((slimb_t)rh < 0) { + mp_sub_ui(tabs, 1, n); + rh += mp_add_mul1(taba, tabs, n, 2); + rh += mp_add_ui(taba, 1, n); + } + *prh = rh; + return 0; +} + +/* 'taba' has 2*n limbs with n >= 1 and taba[2*n-1] >= 2 ^ (LIMB_BITS + - 2). Return (s, r) with s=floor(sqrt(a)) and r=a-s^2. 0 <= r <= 2 + * s. tabs has n limbs. r is returned in the lower n limbs of + taba. Its r[n] is the returned value of the function. */ +/* Algorithm from the article "Karatsuba Square Root" by Paul Zimmermann and + inspirated from its GMP implementation */ +int mp_sqrtrem(bf_context_t *s, limb_t *tabs, limb_t *taba, limb_t n) +{ + limb_t tmp_buf1[8]; + limb_t *tmp_buf; + mp_size_t n2; + int ret; + n2 = n / 2 + 1; + if (n2 <= countof(tmp_buf1)) { + tmp_buf = tmp_buf1; + } else { + tmp_buf = bf_malloc(s, sizeof(limb_t) * n2); + if (!tmp_buf) + return -1; + } + ret = mp_sqrtrem_rec(s, tabs, taba, n, tmp_buf, taba + n); + if (tmp_buf != tmp_buf1) + bf_free(s, tmp_buf); + return ret; +} + +/* Integer square root with remainder. 'a' must be an integer. r = + floor(sqrt(a)) and rem = a - r^2. BF_ST_INEXACT is set if the result + is inexact. 'rem' can be NULL if the remainder is not needed. */ +int bf_sqrtrem(bf_t *r, bf_t *rem1, const bf_t *a) +{ + int ret; + + if (a->len == 0) { + if (a->expn == BF_EXP_NAN) { + bf_set_nan(r); + } else if (a->expn == BF_EXP_INF && a->sign) { + goto invalid_op; + } else { + bf_set(r, a); + } + if (rem1) + bf_set_ui(rem1, 0); + ret = 0; + } else if (a->sign) { + invalid_op: + bf_set_nan(r); + if (rem1) + bf_set_ui(rem1, 0); + ret = BF_ST_INVALID_OP; + } else { + bf_t rem_s, *rem; + + bf_sqrt(r, a, (a->expn + 1) / 2, BF_RNDZ); + bf_rint(r, BF_RNDZ); + /* see if the result is exact by computing the remainder */ + if (rem1) { + rem = rem1; + } else { + rem = &rem_s; + bf_init(r->ctx, rem); + } + /* XXX: could avoid recomputing the remainder */ + bf_mul(rem, r, r, BF_PREC_INF, BF_RNDZ); + bf_neg(rem); + bf_add(rem, rem, a, BF_PREC_INF, BF_RNDZ); + if (bf_is_nan(rem)) { + ret = BF_ST_MEM_ERROR; + goto done; + } + if (rem->len != 0) { + ret = BF_ST_INEXACT; + } else { + ret = 0; + } + done: + if (!rem1) + bf_delete(rem); + } + return ret; +} + +int bf_sqrt(bf_t *r, const bf_t *a, limb_t prec, bf_flags_t flags) +{ + bf_context_t *s = a->ctx; + int ret; + + assert(r != a); + + if (a->len == 0) { + if (a->expn == BF_EXP_NAN) { + bf_set_nan(r); + } else if (a->expn == BF_EXP_INF && a->sign) { + goto invalid_op; + } else { + bf_set(r, a); + } + ret = 0; + } else if (a->sign) { + invalid_op: + bf_set_nan(r); + ret = BF_ST_INVALID_OP; + } else { + limb_t *a1; + slimb_t n, n1; + limb_t res; + + /* convert the mantissa to an integer with at least 2 * + prec + 4 bits */ + n = (2 * (prec + 2) + 2 * LIMB_BITS - 1) / (2 * LIMB_BITS); + if (bf_resize(r, n)) + goto fail; + a1 = bf_malloc(s, sizeof(limb_t) * 2 * n); + if (!a1) + goto fail; + n1 = bf_min(2 * n, a->len); + memset(a1, 0, (2 * n - n1) * sizeof(limb_t)); + memcpy(a1 + 2 * n - n1, a->tab + a->len - n1, n1 * sizeof(limb_t)); + if (a->expn & 1) { + res = mp_shr(a1, a1, 2 * n, 1, 0); + } else { + res = 0; + } + if (mp_sqrtrem(s, r->tab, a1, n)) { + bf_free(s, a1); + goto fail; + } + if (!res) { + res = mp_scan_nz(a1, n + 1); + } + bf_free(s, a1); + if (!res) { + res = mp_scan_nz(a->tab, a->len - n1); + } + if (res != 0) + r->tab[0] |= 1; + r->sign = 0; + r->expn = (a->expn + 1) >> 1; + ret = bf_round(r, prec, flags); + } + return ret; + fail: + bf_set_nan(r); + return BF_ST_MEM_ERROR; +} + +static no_inline int bf_op2(bf_t *r, const bf_t *a, const bf_t *b, limb_t prec, + bf_flags_t flags, bf_op2_func_t *func) +{ + bf_t tmp; + int ret; + + if (r == a || r == b) { + bf_init(r->ctx, &tmp); + ret = func(&tmp, a, b, prec, flags); + bf_move(r, &tmp); + } else { + ret = func(r, a, b, prec, flags); + } + return ret; +} + +int bf_add(bf_t *r, const bf_t *a, const bf_t *b, limb_t prec, + bf_flags_t flags) +{ + return bf_op2(r, a, b, prec, flags, __bf_add); +} + +int bf_sub(bf_t *r, const bf_t *a, const bf_t *b, limb_t prec, + bf_flags_t flags) +{ + return bf_op2(r, a, b, prec, flags, __bf_sub); +} + +int bf_div(bf_t *r, const bf_t *a, const bf_t *b, limb_t prec, + bf_flags_t flags) +{ + return bf_op2(r, a, b, prec, flags, __bf_div); +} + +int bf_mul_ui(bf_t *r, const bf_t *a, uint64_t b1, limb_t prec, + bf_flags_t flags) +{ + bf_t b; + int ret; + bf_init(r->ctx, &b); + ret = bf_set_ui(&b, b1); + ret |= bf_mul(r, a, &b, prec, flags); + bf_delete(&b); + return ret; +} + +int bf_mul_si(bf_t *r, const bf_t *a, int64_t b1, limb_t prec, + bf_flags_t flags) +{ + bf_t b; + int ret; + bf_init(r->ctx, &b); + ret = bf_set_si(&b, b1); + ret |= bf_mul(r, a, &b, prec, flags); + bf_delete(&b); + return ret; +} + +int bf_add_si(bf_t *r, const bf_t *a, int64_t b1, limb_t prec, + bf_flags_t flags) +{ + bf_t b; + int ret; + + bf_init(r->ctx, &b); + ret = bf_set_si(&b, b1); + ret |= bf_add(r, a, &b, prec, flags); + bf_delete(&b); + return ret; +} + +static int bf_pow_ui(bf_t *r, const bf_t *a, limb_t b, limb_t prec, + bf_flags_t flags) +{ + int ret, n_bits, i; + + assert(r != a); + if (b == 0) + return bf_set_ui(r, 1); + ret = bf_set(r, a); + n_bits = LIMB_BITS - clz(b); + for(i = n_bits - 2; i >= 0; i--) { + ret |= bf_mul(r, r, r, prec, flags); + if ((b >> i) & 1) + ret |= bf_mul(r, r, a, prec, flags); + } + return ret; +} + +static int bf_pow_ui_ui(bf_t *r, limb_t a1, limb_t b, + limb_t prec, bf_flags_t flags) +{ + bf_t a; + int ret; + + if (a1 == 10 && b <= LIMB_DIGITS) { + /* use precomputed powers. We do not round at this point + because we expect the caller to do it */ + ret = bf_set_ui(r, mp_pow_dec[b]); + } else { + bf_init(r->ctx, &a); + ret = bf_set_ui(&a, a1); + ret |= bf_pow_ui(r, &a, b, prec, flags); + bf_delete(&a); + } + return ret; +} + +/* convert to integer (infinite precision) */ +int bf_rint(bf_t *r, int rnd_mode) +{ + return bf_round(r, 0, rnd_mode | BF_FLAG_RADPNT_PREC); +} + +/* logical operations */ +#define BF_LOGIC_OR 0 +#define BF_LOGIC_XOR 1 +#define BF_LOGIC_AND 2 + +static inline limb_t bf_logic_op1(limb_t a, limb_t b, int op) +{ + switch(op) { + case BF_LOGIC_OR: + return a | b; + case BF_LOGIC_XOR: + return a ^ b; + default: + case BF_LOGIC_AND: + return a & b; + } +} + +static int bf_logic_op(bf_t *r, const bf_t *a1, const bf_t *b1, int op) +{ + bf_t b1_s, a1_s, *a, *b; + limb_t a_sign, b_sign, r_sign; + slimb_t l, i, a_bit_offset, b_bit_offset; + limb_t v1, v2, v1_mask, v2_mask, r_mask; + int ret; + + assert(r != a1 && r != b1); + + if (a1->expn <= 0) + a_sign = 0; /* minus zero is considered as positive */ + else + a_sign = a1->sign; + + if (b1->expn <= 0) + b_sign = 0; /* minus zero is considered as positive */ + else + b_sign = b1->sign; + + if (a_sign) { + a = &a1_s; + bf_init(r->ctx, a); + if (bf_add_si(a, a1, 1, BF_PREC_INF, BF_RNDZ)) { + b = NULL; + goto fail; + } + } else { + a = (bf_t *)a1; + } + + if (b_sign) { + b = &b1_s; + bf_init(r->ctx, b); + if (bf_add_si(b, b1, 1, BF_PREC_INF, BF_RNDZ)) + goto fail; + } else { + b = (bf_t *)b1; + } + + r_sign = bf_logic_op1(a_sign, b_sign, op); + if (op == BF_LOGIC_AND && r_sign == 0) { + /* no need to compute extra zeros for and */ + if (a_sign == 0 && b_sign == 0) + l = bf_min(a->expn, b->expn); + else if (a_sign == 0) + l = a->expn; + else + l = b->expn; + } else { + l = bf_max(a->expn, b->expn); + } + /* Note: a or b can be zero */ + l = (bf_max(l, 1) + LIMB_BITS - 1) / LIMB_BITS; + if (bf_resize(r, l)) + goto fail; + a_bit_offset = a->len * LIMB_BITS - a->expn; + b_bit_offset = b->len * LIMB_BITS - b->expn; + v1_mask = -a_sign; + v2_mask = -b_sign; + r_mask = -r_sign; + for(i = 0; i < l; i++) { + v1 = get_bits(a->tab, a->len, a_bit_offset + i * LIMB_BITS) ^ v1_mask; + v2 = get_bits(b->tab, b->len, b_bit_offset + i * LIMB_BITS) ^ v2_mask; + r->tab[i] = bf_logic_op1(v1, v2, op) ^ r_mask; + } + r->expn = l * LIMB_BITS; + r->sign = r_sign; + bf_normalize_and_round(r, BF_PREC_INF, BF_RNDZ); /* cannot fail */ + if (r_sign) { + if (bf_add_si(r, r, -1, BF_PREC_INF, BF_RNDZ)) + goto fail; + } + ret = 0; + done: + if (a == &a1_s) + bf_delete(a); + if (b == &b1_s) + bf_delete(b); + return ret; + fail: + bf_set_nan(r); + ret = BF_ST_MEM_ERROR; + goto done; +} + +/* 'a' and 'b' must be integers. Return 0 or BF_ST_MEM_ERROR. */ +int bf_logic_or(bf_t *r, const bf_t *a, const bf_t *b) +{ + return bf_logic_op(r, a, b, BF_LOGIC_OR); +} + +/* 'a' and 'b' must be integers. Return 0 or BF_ST_MEM_ERROR. */ +int bf_logic_xor(bf_t *r, const bf_t *a, const bf_t *b) +{ + return bf_logic_op(r, a, b, BF_LOGIC_XOR); +} + +/* 'a' and 'b' must be integers. Return 0 or BF_ST_MEM_ERROR. */ +int bf_logic_and(bf_t *r, const bf_t *a, const bf_t *b) +{ + return bf_logic_op(r, a, b, BF_LOGIC_AND); +} + +/* conversion between fixed size types */ + +typedef union { + double d; + uint64_t u; +} Float64Union; + +int bf_get_float64(const bf_t *a, double *pres, bf_rnd_t rnd_mode) +{ + Float64Union u; + int e, ret; + uint64_t m; + + ret = 0; + if (a->expn == BF_EXP_NAN) { + u.u = 0x7ff8000000000000; /* quiet nan */ + } else { + bf_t b_s, *b = &b_s; + + bf_init(a->ctx, b); + bf_set(b, a); + if (bf_is_finite(b)) { + ret = bf_round(b, 53, rnd_mode | BF_FLAG_SUBNORMAL | bf_set_exp_bits(11)); + } + if (b->expn == BF_EXP_INF) { + e = (1 << 11) - 1; + m = 0; + } else if (b->expn == BF_EXP_ZERO) { + e = 0; + m = 0; + } else { + e = b->expn + 1023 - 1; +#if LIMB_BITS == 32 + if (b->len == 2) { + m = ((uint64_t)b->tab[1] << 32) | b->tab[0]; + } else { + m = ((uint64_t)b->tab[0] << 32); + } +#else + m = b->tab[0]; +#endif + if (e <= 0) { + /* subnormal */ + m = m >> (12 - e); + e = 0; + } else { + m = (m << 1) >> 12; + } + } + u.u = m | ((uint64_t)e << 52) | ((uint64_t)b->sign << 63); + bf_delete(b); + } + *pres = u.d; + return ret; +} + +int bf_set_float64(bf_t *a, double d) +{ + Float64Union u; + uint64_t m; + int shift, e, sgn; + + u.d = d; + sgn = u.u >> 63; + e = (u.u >> 52) & ((1 << 11) - 1); + m = u.u & (((uint64_t)1 << 52) - 1); + if (e == ((1 << 11) - 1)) { + if (m != 0) { + bf_set_nan(a); + } else { + bf_set_inf(a, sgn); + } + } else if (e == 0) { + if (m == 0) { + bf_set_zero(a, sgn); + } else { + /* subnormal number */ + m <<= 12; + shift = clz64(m); + m <<= shift; + e = -shift; + goto norm; + } + } else { + m = (m << 11) | ((uint64_t)1 << 63); + norm: + a->expn = e - 1023 + 1; +#if LIMB_BITS == 32 + if (bf_resize(a, 2)) + goto fail; + a->tab[0] = m; + a->tab[1] = m >> 32; +#else + if (bf_resize(a, 1)) + goto fail; + a->tab[0] = m; +#endif + a->sign = sgn; + } + return 0; +fail: + bf_set_nan(a); + return BF_ST_MEM_ERROR; +} + +/* The rounding mode is always BF_RNDZ. Return BF_ST_INVALID_OP if there + is an overflow and 0 otherwise. */ +int bf_get_int32(int *pres, const bf_t *a, int flags) +{ + uint32_t v; + int ret; + if (a->expn >= BF_EXP_INF) { + ret = BF_ST_INVALID_OP; + if (flags & BF_GET_INT_MOD) { + v = 0; + } else if (a->expn == BF_EXP_INF) { + v = (uint32_t)INT32_MAX + a->sign; + } else { + v = INT32_MAX; + } + } else if (a->expn <= 0) { + v = 0; + ret = 0; + } else if (a->expn <= 31) { + v = a->tab[a->len - 1] >> (LIMB_BITS - a->expn); + if (a->sign) + v = -v; + ret = 0; + } else if (!(flags & BF_GET_INT_MOD)) { + ret = BF_ST_INVALID_OP; + if (a->sign) { + v = (uint32_t)INT32_MAX + 1; + if (a->expn == 32 && + (a->tab[a->len - 1] >> (LIMB_BITS - 32)) == v) { + ret = 0; + } + } else { + v = INT32_MAX; + } + } else { + v = get_bits(a->tab, a->len, a->len * LIMB_BITS - a->expn); + if (a->sign) + v = -v; + ret = 0; + } + *pres = v; + return ret; +} + +/* The rounding mode is always BF_RNDZ. Return BF_ST_INVALID_OP if there + is an overflow and 0 otherwise. */ +int bf_get_int64(int64_t *pres, const bf_t *a, int flags) +{ + uint64_t v; + int ret; + if (a->expn >= BF_EXP_INF) { + ret = BF_ST_INVALID_OP; + if (flags & BF_GET_INT_MOD) { + v = 0; + } else if (a->expn == BF_EXP_INF) { + v = (uint64_t)INT64_MAX + a->sign; + } else { + v = INT64_MAX; + } + } else if (a->expn <= 0) { + v = 0; + ret = 0; + } else if (a->expn <= 63) { +#if LIMB_BITS == 32 + if (a->expn <= 32) + v = a->tab[a->len - 1] >> (LIMB_BITS - a->expn); + else + v = (((uint64_t)a->tab[a->len - 1] << 32) | + get_limbz(a, a->len - 2)) >> (64 - a->expn); +#else + v = a->tab[a->len - 1] >> (LIMB_BITS - a->expn); +#endif + if (a->sign) + v = -v; + ret = 0; + } else if (!(flags & BF_GET_INT_MOD)) { + ret = BF_ST_INVALID_OP; + if (a->sign) { + uint64_t v1; + v = (uint64_t)INT64_MAX + 1; + if (a->expn == 64) { + v1 = a->tab[a->len - 1]; +#if LIMB_BITS == 32 + v1 = (v1 << 32) | get_limbz(a, a->len - 2); +#endif + if (v1 == v) + ret = 0; + } + } else { + v = INT64_MAX; + } + } else { + slimb_t bit_pos = a->len * LIMB_BITS - a->expn; + v = get_bits(a->tab, a->len, bit_pos); +#if LIMB_BITS == 32 + v |= (uint64_t)get_bits(a->tab, a->len, bit_pos + 32) << 32; +#endif + if (a->sign) + v = -v; + ret = 0; + } + *pres = v; + return ret; +} + +/* The rounding mode is always BF_RNDZ. Return BF_ST_INVALID_OP if there + is an overflow and 0 otherwise. */ +int bf_get_uint64(uint64_t *pres, const bf_t *a) +{ + uint64_t v; + int ret; + if (a->expn == BF_EXP_NAN) { + goto overflow; + } else if (a->expn <= 0) { + v = 0; + ret = 0; + } else if (a->sign) { + v = 0; + ret = BF_ST_INVALID_OP; + } else if (a->expn <= 64) { +#if LIMB_BITS == 32 + if (a->expn <= 32) + v = a->tab[a->len - 1] >> (LIMB_BITS - a->expn); + else + v = (((uint64_t)a->tab[a->len - 1] << 32) | + get_limbz(a, a->len - 2)) >> (64 - a->expn); +#else + v = a->tab[a->len - 1] >> (LIMB_BITS - a->expn); +#endif + ret = 0; + } else { + overflow: + v = UINT64_MAX; + ret = BF_ST_INVALID_OP; + } + *pres = v; + return ret; +} + +/* base conversion from radix */ + +static const uint8_t digits_per_limb_table[BF_RADIX_MAX - 1] = { +#if LIMB_BITS == 32 +32,20,16,13,12,11,10,10, 9, 9, 8, 8, 8, 8, 8, 7, 7, 7, 7, 7, 7, 7, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, +#else +64,40,32,27,24,22,21,20,19,18,17,17,16,16,16,15,15,15,14,14,14,14,13,13,13,13,13,13,13,12,12,12,12,12,12, +#endif +}; + +static limb_t get_limb_radix(int radix) +{ + int i, k; + limb_t radixl; + + k = digits_per_limb_table[radix - 2]; + radixl = radix; + for(i = 1; i < k; i++) + radixl *= radix; + return radixl; +} + +/* return != 0 if error */ +static int bf_integer_from_radix_rec(bf_t *r, const limb_t *tab, + limb_t n, int level, limb_t n0, + limb_t radix, bf_t *pow_tab) +{ + int ret; + if (n == 1) { + ret = bf_set_ui(r, tab[0]); + } else { + bf_t T_s, *T = &T_s, *B; + limb_t n1, n2; + + n2 = (((n0 * 2) >> (level + 1)) + 1) / 2; + n1 = n - n2; + // printf("level=%d n0=%ld n1=%ld n2=%ld\n", level, n0, n1, n2); + B = &pow_tab[level]; + if (B->len == 0) { + ret = bf_pow_ui_ui(B, radix, n2, BF_PREC_INF, BF_RNDZ); + if (ret) + return ret; + } + ret = bf_integer_from_radix_rec(r, tab + n2, n1, level + 1, n0, + radix, pow_tab); + if (ret) + return ret; + ret = bf_mul(r, r, B, BF_PREC_INF, BF_RNDZ); + if (ret) + return ret; + bf_init(r->ctx, T); + ret = bf_integer_from_radix_rec(T, tab, n2, level + 1, n0, + radix, pow_tab); + if (!ret) + ret = bf_add(r, r, T, BF_PREC_INF, BF_RNDZ); + bf_delete(T); + } + return ret; + // bf_print_str(" r=", r); +} + +/* return 0 if OK != 0 if memory error */ +static int bf_integer_from_radix(bf_t *r, const limb_t *tab, + limb_t n, limb_t radix) +{ + bf_context_t *s = r->ctx; + int pow_tab_len, i, ret; + limb_t radixl; + bf_t *pow_tab; + + radixl = get_limb_radix(radix); + pow_tab_len = ceil_log2(n) + 2; /* XXX: check */ + pow_tab = bf_malloc(s, sizeof(pow_tab[0]) * pow_tab_len); + if (!pow_tab) + return -1; + for(i = 0; i < pow_tab_len; i++) + bf_init(r->ctx, &pow_tab[i]); + ret = bf_integer_from_radix_rec(r, tab, n, 0, n, radixl, pow_tab); + for(i = 0; i < pow_tab_len; i++) { + bf_delete(&pow_tab[i]); + } + bf_free(s, pow_tab); + return ret; +} + +/* compute and round T * radix^expn. */ +int bf_mul_pow_radix(bf_t *r, const bf_t *T, limb_t radix, + slimb_t expn, limb_t prec, bf_flags_t flags) +{ + int ret, expn_sign, overflow; + slimb_t e, extra_bits, prec1, ziv_extra_bits; + bf_t B_s, *B = &B_s; + + if (T->len == 0) { + return bf_set(r, T); + } else if (expn == 0) { + ret = bf_set(r, T); + ret |= bf_round(r, prec, flags); + return ret; + } + + e = expn; + expn_sign = 0; + if (e < 0) { + e = -e; + expn_sign = 1; + } + bf_init(r->ctx, B); + if (prec == BF_PREC_INF) { + /* infinite precision: only used if the result is known to be exact */ + ret = bf_pow_ui_ui(B, radix, e, BF_PREC_INF, BF_RNDN); + if (expn_sign) { + ret |= bf_div(r, T, B, T->len * LIMB_BITS, BF_RNDN); + } else { + ret |= bf_mul(r, T, B, BF_PREC_INF, BF_RNDN); + } + } else { + ziv_extra_bits = 16; + for(;;) { + prec1 = prec + ziv_extra_bits; + /* XXX: correct overflow/underflow handling */ + /* XXX: rigorous error analysis needed */ + extra_bits = ceil_log2(e) * 2 + 1; + ret = bf_pow_ui_ui(B, radix, e, prec1 + extra_bits, BF_RNDN | BF_FLAG_EXT_EXP); + overflow = !bf_is_finite(B); + /* XXX: if bf_pow_ui_ui returns an exact result, can stop + after the next operation */ + if (expn_sign) + ret |= bf_div(r, T, B, prec1 + extra_bits, BF_RNDN | BF_FLAG_EXT_EXP); + else + ret |= bf_mul(r, T, B, prec1 + extra_bits, BF_RNDN | BF_FLAG_EXT_EXP); + if (ret & BF_ST_MEM_ERROR) + break; + if ((ret & BF_ST_INEXACT) && + !bf_can_round(r, prec, flags & BF_RND_MASK, prec1) && + !overflow) { + /* and more precision and retry */ + ziv_extra_bits = ziv_extra_bits + (ziv_extra_bits / 2); + } else { + /* XXX: need to use __bf_round() to pass the inexact + flag for the subnormal case */ + ret = bf_round(r, prec, flags) | (ret & BF_ST_INEXACT); + break; + } + } + } + bf_delete(B); + return ret; +} + +static inline int bf_to_digit(int c) +{ + if (c >= '0' && c <= '9') + return c - '0'; + else if (c >= 'A' && c <= 'Z') + return c - 'A' + 10; + else if (c >= 'a' && c <= 'z') + return c - 'a' + 10; + else + return 36; +} + +/* add a limb at 'pos' and decrement pos. new space is created if + needed. Return 0 if OK, -1 if memory error */ +static int bf_add_limb(bf_t *a, slimb_t *ppos, limb_t v) +{ + slimb_t pos; + pos = *ppos; + if (unlikely(pos < 0)) { + limb_t new_size, d, *new_tab; + new_size = bf_max(a->len + 1, a->len * 3 / 2); + new_tab = bf_realloc(a->ctx, a->tab, sizeof(limb_t) * new_size); + if (!new_tab) + return -1; + a->tab = new_tab; + d = new_size - a->len; + memmove(a->tab + d, a->tab, a->len * sizeof(limb_t)); + a->len = new_size; + pos += d; + } + a->tab[pos--] = v; + *ppos = pos; + return 0; +} + +static int bf_tolower(int c) +{ + if (c >= 'A' && c <= 'Z') + c = c - 'A' + 'a'; + return c; +} + +static int strcasestart(const char *str, const char *val, const char **ptr) +{ + const char *p, *q; + p = str; + q = val; + while (*q != '\0') { + if (bf_tolower(*p) != *q) + return 0; + p++; + q++; + } + if (ptr) + *ptr = p; + return 1; +} + +static int bf_atof_internal(bf_t *r, slimb_t *pexponent, + const char *str, const char **pnext, int radix, + limb_t prec, bf_flags_t flags, BOOL is_dec) +{ + const char *p, *p_start; + int is_neg, radix_bits, exp_is_neg, ret, digits_per_limb, shift; + limb_t cur_limb; + slimb_t pos, expn, int_len, digit_count; + BOOL has_decpt, is_bin_exp; + bf_t a_s, *a; + + *pexponent = 0; + p = str; + if (!(flags & BF_ATOF_NO_NAN_INF) && radix <= 16 && + strcasestart(p, "nan", &p)) { + bf_set_nan(r); + ret = 0; + goto done; + } + is_neg = 0; + + if (p[0] == '+') { + p++; + p_start = p; + } else if (p[0] == '-') { + is_neg = 1; + p++; + p_start = p; + } else { + p_start = p; + } + if (p[0] == '0') { + if ((p[1] == 'x' || p[1] == 'X') && + (radix == 0 || radix == 16) && + !(flags & BF_ATOF_NO_HEX)) { + radix = 16; + p += 2; + } else if ((p[1] == 'o' || p[1] == 'O') && + radix == 0 && (flags & BF_ATOF_BIN_OCT)) { + p += 2; + radix = 8; + } else if ((p[1] == 'b' || p[1] == 'B') && + radix == 0 && (flags & BF_ATOF_BIN_OCT)) { + p += 2; + radix = 2; + } else { + goto no_prefix; + } + /* there must be a digit after the prefix */ + if (bf_to_digit((uint8_t)*p) >= radix) { + bf_set_nan(r); + ret = 0; + goto done; + } + no_prefix: ; + } else { + if (!(flags & BF_ATOF_NO_NAN_INF) && radix <= 16 && + strcasestart(p, "inf", &p)) { + bf_set_inf(r, is_neg); + ret = 0; + goto done; + } + } + + if (radix == 0) + radix = 10; + if (is_dec) { + assert(radix == 10); + radix_bits = 0; + a = r; + } else if ((radix & (radix - 1)) != 0) { + radix_bits = 0; /* base is not a power of two */ + a = &a_s; + bf_init(r->ctx, a); + } else { + radix_bits = ceil_log2(radix); + a = r; + } + + /* skip leading zeros */ + /* XXX: could also skip zeros after the decimal point */ + while (*p == '0') + p++; + + if (radix_bits) { + shift = digits_per_limb = LIMB_BITS; + } else { + radix_bits = 0; + shift = digits_per_limb = digits_per_limb_table[radix - 2]; + } + cur_limb = 0; + bf_resize(a, 1); + pos = 0; + has_decpt = FALSE; + int_len = digit_count = 0; + for(;;) { + limb_t c; + if (*p == '.' && (p > p_start || bf_to_digit(p[1]) < radix)) { + if (has_decpt) + break; + has_decpt = TRUE; + int_len = digit_count; + p++; + } + c = bf_to_digit(*p); + if (c >= radix) + break; + digit_count++; + p++; + if (radix_bits) { + shift -= radix_bits; + if (shift <= 0) { + cur_limb |= c >> (-shift); + if (bf_add_limb(a, &pos, cur_limb)) + goto mem_error; + if (shift < 0) + cur_limb = c << (LIMB_BITS + shift); + else + cur_limb = 0; + shift += LIMB_BITS; + } else { + cur_limb |= c << shift; + } + } else { + cur_limb = cur_limb * radix + c; + shift--; + if (shift == 0) { + if (bf_add_limb(a, &pos, cur_limb)) + goto mem_error; + shift = digits_per_limb; + cur_limb = 0; + } + } + } + if (!has_decpt) + int_len = digit_count; + + /* add the last limb and pad with zeros */ + if (shift != digits_per_limb) { + if (radix_bits == 0) { + while (shift != 0) { + cur_limb *= radix; + shift--; + } + } + if (bf_add_limb(a, &pos, cur_limb)) { + mem_error: + ret = BF_ST_MEM_ERROR; + if (!radix_bits) + bf_delete(a); + bf_set_nan(r); + goto done; + } + } + + /* reset the next limbs to zero (we prefer to reallocate in the + renormalization) */ + memset(a->tab, 0, (pos + 1) * sizeof(limb_t)); + + if (p == p_start) { + ret = 0; + if (!radix_bits) + bf_delete(a); + bf_set_nan(r); + goto done; + } + + /* parse the exponent, if any */ + expn = 0; + is_bin_exp = FALSE; + if (((radix == 10 && (*p == 'e' || *p == 'E')) || + (radix != 10 && (*p == '@' || + (radix_bits && (*p == 'p' || *p == 'P'))))) && + p > p_start) { + is_bin_exp = (*p == 'p' || *p == 'P'); + p++; + exp_is_neg = 0; + if (*p == '+') { + p++; + } else if (*p == '-') { + exp_is_neg = 1; + p++; + } + for(;;) { + int c; + c = bf_to_digit(*p); + if (c >= 10) + break; + if (unlikely(expn > ((BF_RAW_EXP_MAX - 2 - 9) / 10))) { + /* exponent overflow */ + if (exp_is_neg) { + bf_set_zero(r, is_neg); + ret = BF_ST_UNDERFLOW | BF_ST_INEXACT; + } else { + bf_set_inf(r, is_neg); + ret = BF_ST_OVERFLOW | BF_ST_INEXACT; + } + goto done; + } + p++; + expn = expn * 10 + c; + } + if (exp_is_neg) + expn = -expn; + } + if (is_dec) { + a->expn = expn + int_len; + a->sign = is_neg; + ret = bfdec_normalize_and_round((bfdec_t *)a, prec, flags); + } else if (radix_bits) { + /* XXX: may overflow */ + if (!is_bin_exp) + expn *= radix_bits; + a->expn = expn + (int_len * radix_bits); + a->sign = is_neg; + ret = bf_normalize_and_round(a, prec, flags); + } else { + limb_t l; + pos++; + l = a->len - pos; /* number of limbs */ + if (l == 0) { + bf_set_zero(r, is_neg); + ret = 0; + } else { + bf_t T_s, *T = &T_s; + + expn -= l * digits_per_limb - int_len; + bf_init(r->ctx, T); + if (bf_integer_from_radix(T, a->tab + pos, l, radix)) { + bf_set_nan(r); + ret = BF_ST_MEM_ERROR; + } else { + T->sign = is_neg; + if (flags & BF_ATOF_EXPONENT) { + /* return the exponent */ + *pexponent = expn; + ret = bf_set(r, T); + } else { + ret = bf_mul_pow_radix(r, T, radix, expn, prec, flags); + } + } + bf_delete(T); + } + bf_delete(a); + } + done: + if (pnext) + *pnext = p; + return ret; +} + +/* + Return (status, n, exp). 'status' is the floating point status. 'n' + is the parsed number. + + If (flags & BF_ATOF_EXPONENT) and if the radix is not a power of + two, the parsed number is equal to r * + (*pexponent)^radix. Otherwise *pexponent = 0. +*/ +int bf_atof2(bf_t *r, slimb_t *pexponent, + const char *str, const char **pnext, int radix, + limb_t prec, bf_flags_t flags) +{ + return bf_atof_internal(r, pexponent, str, pnext, radix, prec, flags, + FALSE); +} + +int bf_atof(bf_t *r, const char *str, const char **pnext, int radix, + limb_t prec, bf_flags_t flags) +{ + slimb_t dummy_exp; + return bf_atof_internal(r, &dummy_exp, str, pnext, radix, prec, flags, FALSE); +} + +/* base conversion to radix */ + +#if LIMB_BITS == 64 +#define RADIXL_10 UINT64_C(10000000000000000000) +#else +#define RADIXL_10 UINT64_C(1000000000) +#endif + +static const uint32_t inv_log2_radix[BF_RADIX_MAX - 1][LIMB_BITS / 32 + 1] = { +#if LIMB_BITS == 32 +{ 0x80000000, 0x00000000,}, +{ 0x50c24e60, 0xd4d4f4a7,}, +{ 0x40000000, 0x00000000,}, +{ 0x372068d2, 0x0a1ee5ca,}, +{ 0x3184648d, 0xb8153e7a,}, +{ 0x2d983275, 0x9d5369c4,}, +{ 0x2aaaaaaa, 0xaaaaaaab,}, +{ 0x28612730, 0x6a6a7a54,}, +{ 0x268826a1, 0x3ef3fde6,}, +{ 0x25001383, 0xbac8a744,}, +{ 0x23b46706, 0x82c0c709,}, +{ 0x229729f1, 0xb2c83ded,}, +{ 0x219e7ffd, 0xa5ad572b,}, +{ 0x20c33b88, 0xda7c29ab,}, +{ 0x20000000, 0x00000000,}, +{ 0x1f50b57e, 0xac5884b3,}, +{ 0x1eb22cc6, 0x8aa6e26f,}, +{ 0x1e21e118, 0x0c5daab2,}, +{ 0x1d9dcd21, 0x439834e4,}, +{ 0x1d244c78, 0x367a0d65,}, +{ 0x1cb40589, 0xac173e0c,}, +{ 0x1c4bd95b, 0xa8d72b0d,}, +{ 0x1bead768, 0x98f8ce4c,}, +{ 0x1b903469, 0x050f72e5,}, +{ 0x1b3b433f, 0x2eb06f15,}, +{ 0x1aeb6f75, 0x9c46fc38,}, +{ 0x1aa038eb, 0x0e3bfd17,}, +{ 0x1a593062, 0xb38d8c56,}, +{ 0x1a15f4c3, 0x2b95a2e6,}, +{ 0x19d630dc, 0xcc7ddef9,}, +{ 0x19999999, 0x9999999a,}, +{ 0x195fec80, 0x8a609431,}, +{ 0x1928ee7b, 0x0b4f22f9,}, +{ 0x18f46acf, 0x8c06e318,}, +{ 0x18c23246, 0xdc0a9f3d,}, +#else +{ 0x80000000, 0x00000000, 0x00000000,}, +{ 0x50c24e60, 0xd4d4f4a7, 0x021f57bc,}, +{ 0x40000000, 0x00000000, 0x00000000,}, +{ 0x372068d2, 0x0a1ee5ca, 0x19ea911b,}, +{ 0x3184648d, 0xb8153e7a, 0x7fc2d2e1,}, +{ 0x2d983275, 0x9d5369c4, 0x4dec1661,}, +{ 0x2aaaaaaa, 0xaaaaaaaa, 0xaaaaaaab,}, +{ 0x28612730, 0x6a6a7a53, 0x810fabde,}, +{ 0x268826a1, 0x3ef3fde6, 0x23e2566b,}, +{ 0x25001383, 0xbac8a744, 0x385a3349,}, +{ 0x23b46706, 0x82c0c709, 0x3f891718,}, +{ 0x229729f1, 0xb2c83ded, 0x15fba800,}, +{ 0x219e7ffd, 0xa5ad572a, 0xe169744b,}, +{ 0x20c33b88, 0xda7c29aa, 0x9bddee52,}, +{ 0x20000000, 0x00000000, 0x00000000,}, +{ 0x1f50b57e, 0xac5884b3, 0x70e28eee,}, +{ 0x1eb22cc6, 0x8aa6e26f, 0x06d1a2a2,}, +{ 0x1e21e118, 0x0c5daab1, 0x81b4f4bf,}, +{ 0x1d9dcd21, 0x439834e3, 0x81667575,}, +{ 0x1d244c78, 0x367a0d64, 0xc8204d6d,}, +{ 0x1cb40589, 0xac173e0c, 0x3b7b16ba,}, +{ 0x1c4bd95b, 0xa8d72b0d, 0x5879f25a,}, +{ 0x1bead768, 0x98f8ce4c, 0x66cc2858,}, +{ 0x1b903469, 0x050f72e5, 0x0cf5488e,}, +{ 0x1b3b433f, 0x2eb06f14, 0x8c89719c,}, +{ 0x1aeb6f75, 0x9c46fc37, 0xab5fc7e9,}, +{ 0x1aa038eb, 0x0e3bfd17, 0x1bd62080,}, +{ 0x1a593062, 0xb38d8c56, 0x7998ab45,}, +{ 0x1a15f4c3, 0x2b95a2e6, 0x46aed6a0,}, +{ 0x19d630dc, 0xcc7ddef9, 0x5aadd61b,}, +{ 0x19999999, 0x99999999, 0x9999999a,}, +{ 0x195fec80, 0x8a609430, 0xe1106014,}, +{ 0x1928ee7b, 0x0b4f22f9, 0x5f69791d,}, +{ 0x18f46acf, 0x8c06e318, 0x4d2aeb2c,}, +{ 0x18c23246, 0xdc0a9f3d, 0x3fe16970,}, +#endif +}; + +static const limb_t log2_radix[BF_RADIX_MAX - 1] = { +#if LIMB_BITS == 32 +0x20000000, +0x32b80347, +0x40000000, +0x4a4d3c26, +0x52b80347, +0x59d5d9fd, +0x60000000, +0x6570068e, +0x6a4d3c26, +0x6eb3a9f0, +0x72b80347, +0x766a008e, +0x79d5d9fd, +0x7d053f6d, +0x80000000, +0x82cc7edf, +0x8570068e, +0x87ef05ae, +0x8a4d3c26, +0x8c8ddd45, +0x8eb3a9f0, +0x90c10501, +0x92b80347, +0x949a784c, +0x966a008e, +0x982809d6, +0x99d5d9fd, +0x9b74948f, +0x9d053f6d, +0x9e88c6b3, +0xa0000000, +0xa16bad37, +0xa2cc7edf, +0xa4231623, +0xa570068e, +#else +0x2000000000000000, +0x32b803473f7ad0f4, +0x4000000000000000, +0x4a4d3c25e68dc57f, +0x52b803473f7ad0f4, +0x59d5d9fd5010b366, +0x6000000000000000, +0x6570068e7ef5a1e8, +0x6a4d3c25e68dc57f, +0x6eb3a9f01975077f, +0x72b803473f7ad0f4, +0x766a008e4788cbcd, +0x79d5d9fd5010b366, +0x7d053f6d26089673, +0x8000000000000000, +0x82cc7edf592262d0, +0x8570068e7ef5a1e8, +0x87ef05ae409a0289, +0x8a4d3c25e68dc57f, +0x8c8ddd448f8b845a, +0x8eb3a9f01975077f, +0x90c10500d63aa659, +0x92b803473f7ad0f4, +0x949a784bcd1b8afe, +0x966a008e4788cbcd, +0x982809d5be7072dc, +0x99d5d9fd5010b366, +0x9b74948f5532da4b, +0x9d053f6d26089673, +0x9e88c6b3626a72aa, +0xa000000000000000, +0xa16bad3758efd873, +0xa2cc7edf592262d0, +0xa4231623369e78e6, +0xa570068e7ef5a1e8, +#endif +}; + +/* compute floor(a*b) or ceil(a*b) with b = log2(radix) or + b=1/log2(radix). For is_inv = 0, strict accuracy is not guaranteed + when radix is not a power of two. */ +slimb_t bf_mul_log2_radix(slimb_t a1, unsigned int radix, int is_inv, + int is_ceil1) +{ + int is_neg; + limb_t a; + BOOL is_ceil; + + is_ceil = is_ceil1; + a = a1; + if (a1 < 0) { + a = -a; + is_neg = 1; + } else { + is_neg = 0; + } + is_ceil ^= is_neg; + if ((radix & (radix - 1)) == 0) { + int radix_bits; + /* radix is a power of two */ + radix_bits = ceil_log2(radix); + if (is_inv) { + if (is_ceil) + a += radix_bits - 1; + a = a / radix_bits; + } else { + a = a * radix_bits; + } + } else { + const uint32_t *tab; + limb_t b0, b1; + dlimb_t t; + + if (is_inv) { + tab = inv_log2_radix[radix - 2]; +#if LIMB_BITS == 32 + b1 = tab[0]; + b0 = tab[1]; +#else + b1 = ((limb_t)tab[0] << 32) | tab[1]; + b0 = (limb_t)tab[2] << 32; +#endif + t = (dlimb_t)b0 * (dlimb_t)a; + t = (dlimb_t)b1 * (dlimb_t)a + (t >> LIMB_BITS); + a = t >> (LIMB_BITS - 1); + } else { + b0 = log2_radix[radix - 2]; + t = (dlimb_t)b0 * (dlimb_t)a; + a = t >> (LIMB_BITS - 3); + } + /* a = floor(result) and 'result' cannot be an integer */ + a += is_ceil; + } + if (is_neg) + a = -a; + return a; +} + +/* 'n' is the number of output limbs */ +static int bf_integer_to_radix_rec(bf_t *pow_tab, + limb_t *out, const bf_t *a, limb_t n, + int level, limb_t n0, limb_t radixl, + unsigned int radixl_bits) +{ + limb_t n1, n2, q_prec; + int ret; + + assert(n >= 1); + if (n == 1) { + out[0] = get_bits(a->tab, a->len, a->len * LIMB_BITS - a->expn); + } else if (n == 2) { + dlimb_t t; + slimb_t pos; + pos = a->len * LIMB_BITS - a->expn; + t = ((dlimb_t)get_bits(a->tab, a->len, pos + LIMB_BITS) << LIMB_BITS) | + get_bits(a->tab, a->len, pos); + if (likely(radixl == RADIXL_10)) { + /* use division by a constant when possible */ + out[0] = t % RADIXL_10; + out[1] = t / RADIXL_10; + } else { + out[0] = t % radixl; + out[1] = t / radixl; + } + } else { + bf_t Q, R, *B, *B_inv; + int q_add; + bf_init(a->ctx, &Q); + bf_init(a->ctx, &R); + n2 = (((n0 * 2) >> (level + 1)) + 1) / 2; + n1 = n - n2; + B = &pow_tab[2 * level]; + B_inv = &pow_tab[2 * level + 1]; + ret = 0; + if (B->len == 0) { + /* compute BASE^n2 */ + ret |= bf_pow_ui_ui(B, radixl, n2, BF_PREC_INF, BF_RNDZ); + /* we use enough bits for the maximum possible 'n1' value, + i.e. n2 + 1 */ + ret |= bf_set_ui(&R, 1); + ret |= bf_div(B_inv, &R, B, (n2 + 1) * radixl_bits + 2, BF_RNDN); + } + // printf("%d: n1=% " PRId64 " n2=%" PRId64 "\n", level, n1, n2); + q_prec = n1 * radixl_bits; + ret |= bf_mul(&Q, a, B_inv, q_prec, BF_RNDN); + ret |= bf_rint(&Q, BF_RNDZ); + + ret |= bf_mul(&R, &Q, B, BF_PREC_INF, BF_RNDZ); + ret |= bf_sub(&R, a, &R, BF_PREC_INF, BF_RNDZ); + + if (ret & BF_ST_MEM_ERROR) + goto fail; + /* adjust if necessary */ + q_add = 0; + while (R.sign && R.len != 0) { + if (bf_add(&R, &R, B, BF_PREC_INF, BF_RNDZ)) + goto fail; + q_add--; + } + while (bf_cmpu(&R, B) >= 0) { + if (bf_sub(&R, &R, B, BF_PREC_INF, BF_RNDZ)) + goto fail; + q_add++; + } + if (q_add != 0) { + if (bf_add_si(&Q, &Q, q_add, BF_PREC_INF, BF_RNDZ)) + goto fail; + } + if (bf_integer_to_radix_rec(pow_tab, out + n2, &Q, n1, level + 1, n0, + radixl, radixl_bits)) + goto fail; + if (bf_integer_to_radix_rec(pow_tab, out, &R, n2, level + 1, n0, + radixl, radixl_bits)) { + fail: + bf_delete(&Q); + bf_delete(&R); + return -1; + } + bf_delete(&Q); + bf_delete(&R); + } + return 0; +} + +/* return 0 if OK != 0 if memory error */ +static int bf_integer_to_radix(bf_t *r, const bf_t *a, limb_t radixl) +{ + bf_context_t *s = r->ctx; + limb_t r_len; + bf_t *pow_tab; + int i, pow_tab_len, ret; + + r_len = r->len; + pow_tab_len = (ceil_log2(r_len) + 2) * 2; /* XXX: check */ + pow_tab = bf_malloc(s, sizeof(pow_tab[0]) * pow_tab_len); + if (!pow_tab) + return -1; + for(i = 0; i < pow_tab_len; i++) + bf_init(r->ctx, &pow_tab[i]); + + ret = bf_integer_to_radix_rec(pow_tab, r->tab, a, r_len, 0, r_len, radixl, + ceil_log2(radixl)); + + for(i = 0; i < pow_tab_len; i++) { + bf_delete(&pow_tab[i]); + } + bf_free(s, pow_tab); + return ret; +} + +/* a must be >= 0. 'P' is the wanted number of digits in radix + 'radix'. 'r' is the mantissa represented as an integer. *pE + contains the exponent. Return != 0 if memory error. */ +static int bf_convert_to_radix(bf_t *r, slimb_t *pE, + const bf_t *a, int radix, + limb_t P, bf_rnd_t rnd_mode, + BOOL is_fixed_exponent) +{ + slimb_t E, e, prec, extra_bits, ziv_extra_bits, prec0; + bf_t B_s, *B = &B_s; + int e_sign, ret, res; + + if (a->len == 0) { + /* zero case */ + *pE = 0; + return bf_set(r, a); + } + + if (is_fixed_exponent) { + E = *pE; + } else { + /* compute the new exponent */ + E = 1 + bf_mul_log2_radix(a->expn - 1, radix, TRUE, FALSE); + } + // bf_print_str("a", a); + // printf("E=%ld P=%ld radix=%d\n", E, P, radix); + + for(;;) { + e = P - E; + e_sign = 0; + if (e < 0) { + e = -e; + e_sign = 1; + } + /* Note: precision for log2(radix) is not critical here */ + prec0 = bf_mul_log2_radix(P, radix, FALSE, TRUE); + ziv_extra_bits = 16; + for(;;) { + prec = prec0 + ziv_extra_bits; + /* XXX: rigorous error analysis needed */ + extra_bits = ceil_log2(e) * 2 + 1; + ret = bf_pow_ui_ui(r, radix, e, prec + extra_bits, + BF_RNDN | BF_FLAG_EXT_EXP); + if (!e_sign) + ret |= bf_mul(r, r, a, prec + extra_bits, + BF_RNDN | BF_FLAG_EXT_EXP); + else + ret |= bf_div(r, a, r, prec + extra_bits, + BF_RNDN | BF_FLAG_EXT_EXP); + if (ret & BF_ST_MEM_ERROR) + return BF_ST_MEM_ERROR; + /* if the result is not exact, check that it can be safely + rounded to an integer */ + if ((ret & BF_ST_INEXACT) && + !bf_can_round(r, r->expn, rnd_mode, prec)) { + /* and more precision and retry */ + ziv_extra_bits = ziv_extra_bits + (ziv_extra_bits / 2); + continue; + } else { + ret = bf_rint(r, rnd_mode); + if (ret & BF_ST_MEM_ERROR) + return BF_ST_MEM_ERROR; + break; + } + } + if (is_fixed_exponent) + break; + /* check that the result is < B^P */ + /* XXX: do a fast approximate test first ? */ + bf_init(r->ctx, B); + ret = bf_pow_ui_ui(B, radix, P, BF_PREC_INF, BF_RNDZ); + if (ret) { + bf_delete(B); + return ret; + } + res = bf_cmpu(r, B); + bf_delete(B); + if (res < 0) + break; + /* try a larger exponent */ + E++; + } + *pE = E; + return 0; +} + +static void limb_to_a(char *buf, limb_t n, unsigned int radix, int len) +{ + int digit, i; + + if (radix == 10) { + /* specific case with constant divisor */ + for(i = len - 1; i >= 0; i--) { + digit = (limb_t)n % 10; + n = (limb_t)n / 10; + buf[i] = digit + '0'; + } + } else { + for(i = len - 1; i >= 0; i--) { + digit = (limb_t)n % radix; + n = (limb_t)n / radix; + if (digit < 10) + digit += '0'; + else + digit += 'a' - 10; + buf[i] = digit; + } + } +} + +/* for power of 2 radixes */ +static void limb_to_a2(char *buf, limb_t n, unsigned int radix_bits, int len) +{ + int digit, i; + unsigned int mask; + + mask = (1 << radix_bits) - 1; + for(i = len - 1; i >= 0; i--) { + digit = n & mask; + n >>= radix_bits; + if (digit < 10) + digit += '0'; + else + digit += 'a' - 10; + buf[i] = digit; + } +} + +/* 'a' must be an integer if the is_dec = FALSE or if the radix is not + a power of two. A dot is added before the 'dot_pos' digit. dot_pos + = n_digits does not display the dot. 0 <= dot_pos <= + n_digits. n_digits >= 1. */ +static void output_digits(DynBuf *s, const bf_t *a1, int radix, limb_t n_digits, + limb_t dot_pos, BOOL is_dec) +{ + limb_t i, v, l; + slimb_t pos, pos_incr; + int digits_per_limb, buf_pos, radix_bits, first_buf_pos; + char buf[65]; + bf_t a_s, *a; + + if (is_dec) { + digits_per_limb = LIMB_DIGITS; + a = (bf_t *)a1; + radix_bits = 0; + pos = a->len; + pos_incr = 1; + first_buf_pos = 0; + } else if ((radix & (radix - 1)) == 0) { + a = (bf_t *)a1; + radix_bits = ceil_log2(radix); + digits_per_limb = LIMB_BITS / radix_bits; + pos_incr = digits_per_limb * radix_bits; + /* digits are aligned relative to the radix point */ + pos = a->len * LIMB_BITS + smod(-a->expn, radix_bits); + first_buf_pos = 0; + } else { + limb_t n, radixl; + + digits_per_limb = digits_per_limb_table[radix - 2]; + radixl = get_limb_radix(radix); + a = &a_s; + bf_init(a1->ctx, a); + n = (n_digits + digits_per_limb - 1) / digits_per_limb; + if (bf_resize(a, n)) { + dbuf_set_error(s); + goto done; + } + if (bf_integer_to_radix(a, a1, radixl)) { + dbuf_set_error(s); + goto done; + } + radix_bits = 0; + pos = n; + pos_incr = 1; + first_buf_pos = pos * digits_per_limb - n_digits; + } + buf_pos = digits_per_limb; + i = 0; + while (i < n_digits) { + if (buf_pos == digits_per_limb) { + pos -= pos_incr; + if (radix_bits == 0) { + v = get_limbz(a, pos); + limb_to_a(buf, v, radix, digits_per_limb); + } else { + v = get_bits(a->tab, a->len, pos); + limb_to_a2(buf, v, radix_bits, digits_per_limb); + } + buf_pos = first_buf_pos; + first_buf_pos = 0; + } + if (i < dot_pos) { + l = dot_pos; + } else { + if (i == dot_pos) + dbuf_putc(s, '.'); + l = n_digits; + } + l = bf_min(digits_per_limb - buf_pos, l - i); + dbuf_put(s, (uint8_t *)(buf + buf_pos), l); + buf_pos += l; + i += l; + } + done: + if (a != a1) + bf_delete(a); +} + +static void *bf_dbuf_realloc(void *opaque, void *ptr, size_t size) +{ + bf_context_t *s = opaque; + return bf_realloc(s, ptr, size); +} + +/* return the length in bytes. A trailing '\0' is added */ +static char *bf_ftoa_internal(size_t *plen, const bf_t *a2, int radix, + limb_t prec, bf_flags_t flags, BOOL is_dec) +{ + bf_context_t *ctx = a2->ctx; + DynBuf s_s, *s = &s_s; + int radix_bits; + + // bf_print_str("ftoa", a2); + // printf("radix=%d\n", radix); + dbuf_init2(s, ctx, bf_dbuf_realloc); + if (a2->expn == BF_EXP_NAN) { + dbuf_putstr(s, "NaN"); + } else { + if (a2->sign) + dbuf_putc(s, '-'); + if (a2->expn == BF_EXP_INF) { + if (flags & BF_FTOA_JS_QUIRKS) + dbuf_putstr(s, "Infinity"); + else + dbuf_putstr(s, "Inf"); + } else { + int fmt, ret; + slimb_t n_digits, n, i, n_max, n1; + bf_t a1_s, *a1 = &a1_s; + + if ((radix & (radix - 1)) != 0) + radix_bits = 0; + else + radix_bits = ceil_log2(radix); + + fmt = flags & BF_FTOA_FORMAT_MASK; + bf_init(ctx, a1); + if (fmt == BF_FTOA_FORMAT_FRAC) { + if (is_dec || radix_bits != 0) { + if (bf_set(a1, a2)) + goto fail1; +#ifdef USE_BF_DEC + if (is_dec) { + if (bfdec_round((bfdec_t *)a1, prec, (flags & BF_RND_MASK) | BF_FLAG_RADPNT_PREC) & BF_ST_MEM_ERROR) + goto fail1; + n = a1->expn; + } else +#endif + { + if (bf_round(a1, prec * radix_bits, (flags & BF_RND_MASK) | BF_FLAG_RADPNT_PREC) & BF_ST_MEM_ERROR) + goto fail1; + n = ceil_div(a1->expn, radix_bits); + } + if (flags & BF_FTOA_ADD_PREFIX) { + if (radix == 16) + dbuf_putstr(s, "0x"); + else if (radix == 8) + dbuf_putstr(s, "0o"); + else if (radix == 2) + dbuf_putstr(s, "0b"); + } + if (a1->expn == BF_EXP_ZERO) { + dbuf_putstr(s, "0"); + if (prec > 0) { + dbuf_putstr(s, "."); + for(i = 0; i < prec; i++) { + dbuf_putc(s, '0'); + } + } + } else { + n_digits = prec + n; + if (n <= 0) { + /* 0.x */ + dbuf_putstr(s, "0."); + for(i = 0; i < -n; i++) { + dbuf_putc(s, '0'); + } + if (n_digits > 0) { + output_digits(s, a1, radix, n_digits, n_digits, is_dec); + } + } else { + output_digits(s, a1, radix, n_digits, n, is_dec); + } + } + } else { + size_t pos, start; + bf_t a_s, *a = &a_s; + + /* make a positive number */ + a->tab = a2->tab; + a->len = a2->len; + a->expn = a2->expn; + a->sign = 0; + + /* one more digit for the rounding */ + n = 1 + bf_mul_log2_radix(bf_max(a->expn, 0), radix, TRUE, TRUE); + n_digits = n + prec; + n1 = n; + if (bf_convert_to_radix(a1, &n1, a, radix, n_digits, + flags & BF_RND_MASK, TRUE)) + goto fail1; + start = s->size; + output_digits(s, a1, radix, n_digits, n, is_dec); + /* remove leading zeros because we allocated one more digit */ + pos = start; + while ((pos + 1) < s->size && s->buf[pos] == '0' && + s->buf[pos + 1] != '.') + pos++; + if (pos > start) { + memmove(s->buf + start, s->buf + pos, s->size - pos); + s->size -= (pos - start); + } + } + } else { +#ifdef USE_BF_DEC + if (is_dec) { + if (bf_set(a1, a2)) + goto fail1; + if (fmt == BF_FTOA_FORMAT_FIXED) { + n_digits = prec; + n_max = n_digits; + if (bfdec_round((bfdec_t *)a1, prec, (flags & BF_RND_MASK)) & BF_ST_MEM_ERROR) + goto fail1; + } else { + /* prec is ignored */ + prec = n_digits = a1->len * LIMB_DIGITS; + /* remove the trailing zero digits */ + while (n_digits > 1 && + get_digit(a1->tab, a1->len, prec - n_digits) == 0) { + n_digits--; + } + n_max = n_digits + 4; + } + n = a1->expn; + } else +#endif + if (radix_bits != 0) { + if (bf_set(a1, a2)) + goto fail1; + if (fmt == BF_FTOA_FORMAT_FIXED) { + slimb_t prec_bits; + n_digits = prec; + n_max = n_digits; + /* align to the radix point */ + prec_bits = prec * radix_bits - + smod(-a1->expn, radix_bits); + if (bf_round(a1, prec_bits, + (flags & BF_RND_MASK)) & BF_ST_MEM_ERROR) + goto fail1; + } else { + limb_t digit_mask; + slimb_t pos; + /* position of the digit before the most + significant digit in bits */ + pos = a1->len * LIMB_BITS + + smod(-a1->expn, radix_bits); + n_digits = ceil_div(pos, radix_bits); + /* remove the trailing zero digits */ + digit_mask = ((limb_t)1 << radix_bits) - 1; + while (n_digits > 1 && + (get_bits(a1->tab, a1->len, pos - n_digits * radix_bits) & digit_mask) == 0) { + n_digits--; + } + n_max = n_digits + 4; + } + n = ceil_div(a1->expn, radix_bits); + } else { + bf_t a_s, *a = &a_s; + + /* make a positive number */ + a->tab = a2->tab; + a->len = a2->len; + a->expn = a2->expn; + a->sign = 0; + + if (fmt == BF_FTOA_FORMAT_FIXED) { + n_digits = prec; + n_max = n_digits; + } else { + slimb_t n_digits_max, n_digits_min; + + assert(prec != BF_PREC_INF); + n_digits = 1 + bf_mul_log2_radix(prec, radix, TRUE, TRUE); + /* max number of digits for non exponential + notation. The rational is to have the same rule + as JS i.e. n_max = 21 for 64 bit float in base 10. */ + n_max = n_digits + 4; + if (fmt == BF_FTOA_FORMAT_FREE_MIN) { + bf_t b_s, *b = &b_s; + + /* find the minimum number of digits by + dichotomy. */ + /* XXX: inefficient */ + n_digits_max = n_digits; + n_digits_min = 1; + bf_init(ctx, b); + while (n_digits_min < n_digits_max) { + n_digits = (n_digits_min + n_digits_max) / 2; + if (bf_convert_to_radix(a1, &n, a, radix, n_digits, + flags & BF_RND_MASK, FALSE)) { + bf_delete(b); + goto fail1; + } + /* convert back to a number and compare */ + ret = bf_mul_pow_radix(b, a1, radix, n - n_digits, + prec, + (flags & ~BF_RND_MASK) | + BF_RNDN); + if (ret & BF_ST_MEM_ERROR) { + bf_delete(b); + goto fail1; + } + if (bf_cmpu(b, a) == 0) { + n_digits_max = n_digits; + } else { + n_digits_min = n_digits + 1; + } + } + bf_delete(b); + n_digits = n_digits_max; + } + } + if (bf_convert_to_radix(a1, &n, a, radix, n_digits, + flags & BF_RND_MASK, FALSE)) { + fail1: + bf_delete(a1); + goto fail; + } + } + if (a1->expn == BF_EXP_ZERO && + fmt != BF_FTOA_FORMAT_FIXED && + !(flags & BF_FTOA_FORCE_EXP)) { + /* just output zero */ + dbuf_putstr(s, "0"); + } else { + if (flags & BF_FTOA_ADD_PREFIX) { + if (radix == 16) + dbuf_putstr(s, "0x"); + else if (radix == 8) + dbuf_putstr(s, "0o"); + else if (radix == 2) + dbuf_putstr(s, "0b"); + } + if (a1->expn == BF_EXP_ZERO) + n = 1; + if ((flags & BF_FTOA_FORCE_EXP) || + n <= -6 || n > n_max) { + const char *fmt; + /* exponential notation */ + output_digits(s, a1, radix, n_digits, 1, is_dec); + if (radix_bits != 0 && radix <= 16) { + if (flags & BF_FTOA_JS_QUIRKS) + fmt = "p%+" PRId_LIMB; + else + fmt = "p%" PRId_LIMB; + dbuf_printf(s, fmt, (n - 1) * radix_bits); + } else { + if (flags & BF_FTOA_JS_QUIRKS) + fmt = "%c%+" PRId_LIMB; + else + fmt = "%c%" PRId_LIMB; + dbuf_printf(s, fmt, + radix <= 10 ? 'e' : '@', n - 1); + } + } else if (n <= 0) { + /* 0.x */ + dbuf_putstr(s, "0."); + for(i = 0; i < -n; i++) { + dbuf_putc(s, '0'); + } + output_digits(s, a1, radix, n_digits, n_digits, is_dec); + } else { + if (n_digits <= n) { + /* no dot */ + output_digits(s, a1, radix, n_digits, n_digits, is_dec); + for(i = 0; i < (n - n_digits); i++) + dbuf_putc(s, '0'); + } else { + output_digits(s, a1, radix, n_digits, n, is_dec); + } + } + } + } + bf_delete(a1); + } + } + dbuf_putc(s, '\0'); + if (dbuf_error(s)) + goto fail; + if (plen) + *plen = s->size - 1; + return (char *)s->buf; + fail: + bf_free(ctx, s->buf); + if (plen) + *plen = 0; + return NULL; +} + +char *bf_ftoa(size_t *plen, const bf_t *a, int radix, limb_t prec, + bf_flags_t flags) +{ + return bf_ftoa_internal(plen, a, radix, prec, flags, FALSE); +} + +/***************************************************************/ +/* transcendental functions */ + +/* Note: the algorithm is from MPFR */ +static void bf_const_log2_rec(bf_t *T, bf_t *P, bf_t *Q, limb_t n1, + limb_t n2, BOOL need_P) +{ + bf_context_t *s = T->ctx; + if ((n2 - n1) == 1) { + if (n1 == 0) { + bf_set_ui(P, 3); + } else { + bf_set_ui(P, n1); + P->sign = 1; + } + bf_set_ui(Q, 2 * n1 + 1); + Q->expn += 2; + bf_set(T, P); + } else { + limb_t m; + bf_t T1_s, *T1 = &T1_s; + bf_t P1_s, *P1 = &P1_s; + bf_t Q1_s, *Q1 = &Q1_s; + + m = n1 + ((n2 - n1) >> 1); + bf_const_log2_rec(T, P, Q, n1, m, TRUE); + bf_init(s, T1); + bf_init(s, P1); + bf_init(s, Q1); + bf_const_log2_rec(T1, P1, Q1, m, n2, need_P); + bf_mul(T, T, Q1, BF_PREC_INF, BF_RNDZ); + bf_mul(T1, T1, P, BF_PREC_INF, BF_RNDZ); + bf_add(T, T, T1, BF_PREC_INF, BF_RNDZ); + if (need_P) + bf_mul(P, P, P1, BF_PREC_INF, BF_RNDZ); + bf_mul(Q, Q, Q1, BF_PREC_INF, BF_RNDZ); + bf_delete(T1); + bf_delete(P1); + bf_delete(Q1); + } +} + +/* compute log(2) with faithful rounding at precision 'prec' */ +static void bf_const_log2_internal(bf_t *T, limb_t prec) +{ + limb_t w, N; + bf_t P_s, *P = &P_s; + bf_t Q_s, *Q = &Q_s; + + w = prec + 15; + N = w / 3 + 1; + bf_init(T->ctx, P); + bf_init(T->ctx, Q); + bf_const_log2_rec(T, P, Q, 0, N, FALSE); + bf_div(T, T, Q, prec, BF_RNDN); + bf_delete(P); + bf_delete(Q); +} + +/* PI constant */ + +#define CHUD_A 13591409 +#define CHUD_B 545140134 +#define CHUD_C 640320 +#define CHUD_BITS_PER_TERM 47 + +static void chud_bs(bf_t *P, bf_t *Q, bf_t *G, int64_t a, int64_t b, int need_g, + limb_t prec) +{ + bf_context_t *s = P->ctx; + int64_t c; + + if (a == (b - 1)) { + bf_t T0, T1; + + bf_init(s, &T0); + bf_init(s, &T1); + bf_set_ui(G, 2 * b - 1); + bf_mul_ui(G, G, 6 * b - 1, prec, BF_RNDN); + bf_mul_ui(G, G, 6 * b - 5, prec, BF_RNDN); + bf_set_ui(&T0, CHUD_B); + bf_mul_ui(&T0, &T0, b, prec, BF_RNDN); + bf_set_ui(&T1, CHUD_A); + bf_add(&T0, &T0, &T1, prec, BF_RNDN); + bf_mul(P, G, &T0, prec, BF_RNDN); + P->sign = b & 1; + + bf_set_ui(Q, b); + bf_mul_ui(Q, Q, b, prec, BF_RNDN); + bf_mul_ui(Q, Q, b, prec, BF_RNDN); + bf_mul_ui(Q, Q, (uint64_t)CHUD_C * CHUD_C * CHUD_C / 24, prec, BF_RNDN); + bf_delete(&T0); + bf_delete(&T1); + } else { + bf_t P2, Q2, G2; + + bf_init(s, &P2); + bf_init(s, &Q2); + bf_init(s, &G2); + + c = (a + b) / 2; + chud_bs(P, Q, G, a, c, 1, prec); + chud_bs(&P2, &Q2, &G2, c, b, need_g, prec); + + /* Q = Q1 * Q2 */ + /* G = G1 * G2 */ + /* P = P1 * Q2 + P2 * G1 */ + bf_mul(&P2, &P2, G, prec, BF_RNDN); + if (!need_g) + bf_set_ui(G, 0); + bf_mul(P, P, &Q2, prec, BF_RNDN); + bf_add(P, P, &P2, prec, BF_RNDN); + bf_delete(&P2); + + bf_mul(Q, Q, &Q2, prec, BF_RNDN); + bf_delete(&Q2); + if (need_g) + bf_mul(G, G, &G2, prec, BF_RNDN); + bf_delete(&G2); + } +} + +/* compute Pi with faithful rounding at precision 'prec' using the + Chudnovsky formula */ +static void bf_const_pi_internal(bf_t *Q, limb_t prec) +{ + bf_context_t *s = Q->ctx; + int64_t n, prec1; + bf_t P, G; + + /* number of serie terms */ + n = prec / CHUD_BITS_PER_TERM + 1; + /* XXX: precision analysis */ + prec1 = prec + 32; + + bf_init(s, &P); + bf_init(s, &G); + + chud_bs(&P, Q, &G, 0, n, 0, BF_PREC_INF); + + bf_mul_ui(&G, Q, CHUD_A, prec1, BF_RNDN); + bf_add(&P, &G, &P, prec1, BF_RNDN); + bf_div(Q, Q, &P, prec1, BF_RNDF); + + bf_set_ui(&P, CHUD_C); + bf_sqrt(&G, &P, prec1, BF_RNDF); + bf_mul_ui(&G, &G, (uint64_t)CHUD_C / 12, prec1, BF_RNDF); + bf_mul(Q, Q, &G, prec, BF_RNDN); + bf_delete(&P); + bf_delete(&G); +} + +static int bf_const_get(bf_t *T, limb_t prec, bf_flags_t flags, + BFConstCache *c, + void (*func)(bf_t *res, limb_t prec), int sign) +{ + limb_t ziv_extra_bits, prec1; + + ziv_extra_bits = 32; + for(;;) { + prec1 = prec + ziv_extra_bits; + if (c->prec < prec1) { + if (c->val.len == 0) + bf_init(T->ctx, &c->val); + func(&c->val, prec1); + c->prec = prec1; + } else { + prec1 = c->prec; + } + bf_set(T, &c->val); + T->sign = sign; + if (!bf_can_round(T, prec, flags & BF_RND_MASK, prec1)) { + /* and more precision and retry */ + ziv_extra_bits = ziv_extra_bits + (ziv_extra_bits / 2); + } else { + break; + } + } + return bf_round(T, prec, flags); +} + +static void bf_const_free(BFConstCache *c) +{ + bf_delete(&c->val); + memset(c, 0, sizeof(*c)); +} + +int bf_const_log2(bf_t *T, limb_t prec, bf_flags_t flags) +{ + bf_context_t *s = T->ctx; + return bf_const_get(T, prec, flags, &s->log2_cache, bf_const_log2_internal, 0); +} + +/* return rounded pi * (1 - 2 * sign) */ +static int bf_const_pi_signed(bf_t *T, int sign, limb_t prec, bf_flags_t flags) +{ + bf_context_t *s = T->ctx; + return bf_const_get(T, prec, flags, &s->pi_cache, bf_const_pi_internal, + sign); +} + +int bf_const_pi(bf_t *T, limb_t prec, bf_flags_t flags) +{ + return bf_const_pi_signed(T, 0, prec, flags); +} + +void bf_clear_cache(bf_context_t *s) +{ +#ifdef USE_FFT_MUL + fft_clear_cache(s); +#endif + bf_const_free(&s->log2_cache); + bf_const_free(&s->pi_cache); +} + +/* ZivFunc should compute the result 'r' with faithful rounding at + precision 'prec'. For efficiency purposes, the final bf_round() + does not need to be done in the function. */ +typedef int ZivFunc(bf_t *r, const bf_t *a, limb_t prec, void *opaque); + +static int bf_ziv_rounding(bf_t *r, const bf_t *a, + limb_t prec, bf_flags_t flags, + ZivFunc *f, void *opaque) +{ + int rnd_mode, ret; + slimb_t prec1, ziv_extra_bits; + + rnd_mode = flags & BF_RND_MASK; + if (rnd_mode == BF_RNDF) { + /* no need to iterate */ + f(r, a, prec, opaque); + ret = 0; + } else { + ziv_extra_bits = 32; + for(;;) { + prec1 = prec + ziv_extra_bits; + ret = f(r, a, prec1, opaque); + if (ret & (BF_ST_OVERFLOW | BF_ST_UNDERFLOW | BF_ST_MEM_ERROR)) { + /* overflow or underflow should never happen because + it indicates the rounding cannot be done correctly, + but we do not catch all the cases */ + return ret; + } + /* if the result is exact, we can stop */ + if (!(ret & BF_ST_INEXACT)) { + ret = 0; + break; + } + if (bf_can_round(r, prec, rnd_mode, prec1)) { + ret = BF_ST_INEXACT; + break; + } + ziv_extra_bits = ziv_extra_bits * 2; + // printf("ziv_extra_bits=%" PRId64 "\n", (int64_t)ziv_extra_bits); + } + } + if (r->len == 0) + return ret; + else + return __bf_round(r, prec, flags, r->len, ret); +} + +/* add (1 - 2*e_sign) * 2^e */ +static int bf_add_epsilon(bf_t *r, const bf_t *a, slimb_t e, int e_sign, + limb_t prec, int flags) +{ + bf_t T_s, *T = &T_s; + int ret; + /* small argument case: result = 1 + epsilon * sign(x) */ + bf_init(a->ctx, T); + bf_set_ui(T, 1); + T->sign = e_sign; + T->expn += e; + ret = bf_add(r, r, T, prec, flags); + bf_delete(T); + return ret; +} + +/* Compute the exponential using faithful rounding at precision 'prec'. + Note: the algorithm is from MPFR */ +static int bf_exp_internal(bf_t *r, const bf_t *a, limb_t prec, void *opaque) +{ + bf_context_t *s = r->ctx; + bf_t T_s, *T = &T_s; + slimb_t n, K, l, i, prec1; + + assert(r != a); + + /* argument reduction: + T = a - n*log(2) with 0 <= T < log(2) and n integer. + */ + bf_init(s, T); + if (a->expn <= -1) { + /* 0 <= abs(a) <= 0.5 */ + if (a->sign) + n = -1; + else + n = 0; + } else { + bf_const_log2(T, LIMB_BITS, BF_RNDZ); + bf_div(T, a, T, LIMB_BITS, BF_RNDD); + bf_get_limb(&n, T, 0); + } + + K = bf_isqrt((prec + 1) / 2); + l = (prec - 1) / K + 1; + /* XXX: precision analysis ? */ + prec1 = prec + (K + 2 * l + 18) + K + 8; + if (a->expn > 0) + prec1 += a->expn; + // printf("n=%ld K=%ld prec1=%ld\n", n, K, prec1); + + bf_const_log2(T, prec1, BF_RNDF); + bf_mul_si(T, T, n, prec1, BF_RNDN); + bf_sub(T, a, T, prec1, BF_RNDN); + + /* reduce the range of T */ + bf_mul_2exp(T, -K, BF_PREC_INF, BF_RNDZ); + + /* Taylor expansion around zero : + 1 + x + x^2/2 + ... + x^n/n! + = (1 + x * (1 + x/2 * (1 + ... (x/n)))) + */ + { + bf_t U_s, *U = &U_s; + + bf_init(s, U); + bf_set_ui(r, 1); + for(i = l ; i >= 1; i--) { + bf_set_ui(U, i); + bf_div(U, T, U, prec1, BF_RNDN); + bf_mul(r, r, U, prec1, BF_RNDN); + bf_add_si(r, r, 1, prec1, BF_RNDN); + } + bf_delete(U); + } + bf_delete(T); + + /* undo the range reduction */ + for(i = 0; i < K; i++) { + bf_mul(r, r, r, prec1, BF_RNDN | BF_FLAG_EXT_EXP); + } + + /* undo the argument reduction */ + bf_mul_2exp(r, n, BF_PREC_INF, BF_RNDZ | BF_FLAG_EXT_EXP); + + return BF_ST_INEXACT; +} + +/* crude overflow and underflow tests for exp(a). a_low <= a <= a_high */ +static int check_exp_underflow_overflow(bf_context_t *s, bf_t *r, + const bf_t *a_low, const bf_t *a_high, + limb_t prec, bf_flags_t flags) +{ + bf_t T_s, *T = &T_s; + bf_t log2_s, *log2 = &log2_s; + slimb_t e_min, e_max; + + if (a_high->expn <= 0) + return 0; + + e_max = (limb_t)1 << (bf_get_exp_bits(flags) - 1); + e_min = -e_max + 3; + if (flags & BF_FLAG_SUBNORMAL) + e_min -= (prec - 1); + + bf_init(s, T); + bf_init(s, log2); + bf_const_log2(log2, LIMB_BITS, BF_RNDU); + bf_mul_ui(T, log2, e_max, LIMB_BITS, BF_RNDU); + /* a_low > e_max * log(2) implies exp(a) > e_max */ + if (bf_cmp_lt(T, a_low) > 0) { + /* overflow */ + bf_delete(T); + bf_delete(log2); + return bf_set_overflow(r, 0, prec, flags); + } + /* a_high < (e_min - 2) * log(2) implies exp(a) < (e_min - 2) */ + bf_const_log2(log2, LIMB_BITS, BF_RNDD); + bf_mul_si(T, log2, e_min - 2, LIMB_BITS, BF_RNDD); + if (bf_cmp_lt(a_high, T)) { + int rnd_mode = flags & BF_RND_MASK; + + /* underflow */ + bf_delete(T); + bf_delete(log2); + if (rnd_mode == BF_RNDU) { + /* set the smallest value */ + bf_set_ui(r, 1); + r->expn = e_min; + } else { + bf_set_zero(r, 0); + } + return BF_ST_UNDERFLOW | BF_ST_INEXACT; + } + bf_delete(log2); + bf_delete(T); + return 0; +} + +int bf_exp(bf_t *r, const bf_t *a, limb_t prec, bf_flags_t flags) +{ + bf_context_t *s = r->ctx; + int ret; + assert(r != a); + if (a->len == 0) { + if (a->expn == BF_EXP_NAN) { + bf_set_nan(r); + } else if (a->expn == BF_EXP_INF) { + if (a->sign) + bf_set_zero(r, 0); + else + bf_set_inf(r, 0); + } else { + bf_set_ui(r, 1); + } + return 0; + } + + ret = check_exp_underflow_overflow(s, r, a, a, prec, flags); + if (ret) + return ret; + if (a->expn < 0 && (-a->expn) >= (prec + 2)) { + /* small argument case: result = 1 + epsilon * sign(x) */ + bf_set_ui(r, 1); + return bf_add_epsilon(r, r, -(prec + 2), a->sign, prec, flags); + } + + return bf_ziv_rounding(r, a, prec, flags, bf_exp_internal, NULL); +} + +static int bf_log_internal(bf_t *r, const bf_t *a, limb_t prec, void *opaque) +{ + bf_context_t *s = r->ctx; + bf_t T_s, *T = &T_s; + bf_t U_s, *U = &U_s; + bf_t V_s, *V = &V_s; + slimb_t n, prec1, l, i, K; + + assert(r != a); + + bf_init(s, T); + /* argument reduction 1 */ + /* T=a*2^n with 2/3 <= T <= 4/3 */ + { + bf_t U_s, *U = &U_s; + bf_set(T, a); + n = T->expn; + T->expn = 0; + /* U= ~ 2/3 */ + bf_init(s, U); + bf_set_ui(U, 0xaaaaaaaa); + U->expn = 0; + if (bf_cmp_lt(T, U)) { + T->expn++; + n--; + } + bf_delete(U); + } + // printf("n=%ld\n", n); + // bf_print_str("T", T); + + /* XXX: precision analysis */ + /* number of iterations for argument reduction 2 */ + K = bf_isqrt((prec + 1) / 2); + /* order of Taylor expansion */ + l = prec / (2 * K) + 1; + /* precision of the intermediate computations */ + prec1 = prec + K + 2 * l + 32; + + bf_init(s, U); + bf_init(s, V); + + /* Note: cancellation occurs here, so we use more precision (XXX: + reduce the precision by computing the exact cancellation) */ + bf_add_si(T, T, -1, BF_PREC_INF, BF_RNDN); + + /* argument reduction 2 */ + for(i = 0; i < K; i++) { + /* T = T / (1 + sqrt(1 + T)) */ + bf_add_si(U, T, 1, prec1, BF_RNDN); + bf_sqrt(V, U, prec1, BF_RNDF); + bf_add_si(U, V, 1, prec1, BF_RNDN); + bf_div(T, T, U, prec1, BF_RNDN); + } + + { + bf_t Y_s, *Y = &Y_s; + bf_t Y2_s, *Y2 = &Y2_s; + bf_init(s, Y); + bf_init(s, Y2); + + /* compute ln(1+x) = ln((1+y)/(1-y)) with y=x/(2+x) + = y + y^3/3 + ... + y^(2*l + 1) / (2*l+1) + with Y=Y^2 + = y*(1+Y/3+Y^2/5+...) = y*(1+Y*(1/3+Y*(1/5 + ...))) + */ + bf_add_si(Y, T, 2, prec1, BF_RNDN); + bf_div(Y, T, Y, prec1, BF_RNDN); + + bf_mul(Y2, Y, Y, prec1, BF_RNDN); + bf_set_ui(r, 0); + for(i = l; i >= 1; i--) { + bf_set_ui(U, 1); + bf_set_ui(V, 2 * i + 1); + bf_div(U, U, V, prec1, BF_RNDN); + bf_add(r, r, U, prec1, BF_RNDN); + bf_mul(r, r, Y2, prec1, BF_RNDN); + } + bf_add_si(r, r, 1, prec1, BF_RNDN); + bf_mul(r, r, Y, prec1, BF_RNDN); + bf_delete(Y); + bf_delete(Y2); + } + bf_delete(V); + bf_delete(U); + + /* multiplication by 2 for the Taylor expansion and undo the + argument reduction 2*/ + bf_mul_2exp(r, K + 1, BF_PREC_INF, BF_RNDZ); + + /* undo the argument reduction 1 */ + bf_const_log2(T, prec1, BF_RNDF); + bf_mul_si(T, T, n, prec1, BF_RNDN); + bf_add(r, r, T, prec1, BF_RNDN); + + bf_delete(T); + return BF_ST_INEXACT; +} + +int bf_log(bf_t *r, const bf_t *a, limb_t prec, bf_flags_t flags) +{ + bf_context_t *s = r->ctx; + bf_t T_s, *T = &T_s; + + assert(r != a); + if (a->len == 0) { + if (a->expn == BF_EXP_NAN) { + bf_set_nan(r); + return 0; + } else if (a->expn == BF_EXP_INF) { + if (a->sign) { + bf_set_nan(r); + return BF_ST_INVALID_OP; + } else { + bf_set_inf(r, 0); + return 0; + } + } else { + bf_set_inf(r, 1); + return 0; + } + } + if (a->sign) { + bf_set_nan(r); + return BF_ST_INVALID_OP; + } + bf_init(s, T); + bf_set_ui(T, 1); + if (bf_cmp_eq(a, T)) { + bf_set_zero(r, 0); + bf_delete(T); + return 0; + } + bf_delete(T); + + return bf_ziv_rounding(r, a, prec, flags, bf_log_internal, NULL); +} + +/* x and y finite and x > 0 */ +static int bf_pow_generic(bf_t *r, const bf_t *x, limb_t prec, void *opaque) +{ + bf_context_t *s = r->ctx; + const bf_t *y = opaque; + bf_t T_s, *T = &T_s; + limb_t prec1; + + bf_init(s, T); + /* XXX: proof for the added precision */ + prec1 = prec + 32; + bf_log(T, x, prec1, BF_RNDF | BF_FLAG_EXT_EXP); + bf_mul(T, T, y, prec1, BF_RNDF | BF_FLAG_EXT_EXP); + if (bf_is_nan(T)) + bf_set_nan(r); + else + bf_exp_internal(r, T, prec1, NULL); /* no overflow/underlow test needed */ + bf_delete(T); + return BF_ST_INEXACT; +} + +/* x and y finite, x > 0, y integer and y fits on one limb */ +static int bf_pow_int(bf_t *r, const bf_t *x, limb_t prec, void *opaque) +{ + bf_context_t *s = r->ctx; + const bf_t *y = opaque; + bf_t T_s, *T = &T_s; + limb_t prec1; + int ret; + slimb_t y1; + + bf_get_limb(&y1, y, 0); + if (y1 < 0) + y1 = -y1; + /* XXX: proof for the added precision */ + prec1 = prec + ceil_log2(y1) * 2 + 8; + ret = bf_pow_ui(r, x, y1 < 0 ? -y1 : y1, prec1, BF_RNDN | BF_FLAG_EXT_EXP); + if (y->sign) { + bf_init(s, T); + bf_set_ui(T, 1); + ret |= bf_div(r, T, r, prec1, BF_RNDN | BF_FLAG_EXT_EXP); + bf_delete(T); + } + return ret; +} + +/* x must be a finite non zero float. Return TRUE if there is a + floating point number r such as x=r^(2^n) and return this floating + point number 'r'. Otherwise return FALSE and r is undefined. */ +static BOOL check_exact_power2n(bf_t *r, const bf_t *x, slimb_t n) +{ + bf_context_t *s = r->ctx; + bf_t T_s, *T = &T_s; + slimb_t e, i, er; + limb_t v; + + /* x = m*2^e with m odd integer */ + e = bf_get_exp_min(x); + /* fast check on the exponent */ + if (n > (LIMB_BITS - 1)) { + if (e != 0) + return FALSE; + er = 0; + } else { + if ((e & (((limb_t)1 << n) - 1)) != 0) + return FALSE; + er = e >> n; + } + /* every perfect odd square = 1 modulo 8 */ + v = get_bits(x->tab, x->len, x->len * LIMB_BITS - x->expn + e); + if ((v & 7) != 1) + return FALSE; + + bf_init(s, T); + bf_set(T, x); + T->expn -= e; + for(i = 0; i < n; i++) { + if (i != 0) + bf_set(T, r); + if (bf_sqrtrem(r, NULL, T) != 0) + return FALSE; + } + r->expn += er; + return TRUE; +} + +/* prec = BF_PREC_INF is accepted for x and y integers and y >= 0 */ +int bf_pow(bf_t *r, const bf_t *x, const bf_t *y, limb_t prec, bf_flags_t flags) +{ + bf_context_t *s = r->ctx; + bf_t T_s, *T = &T_s; + bf_t ytmp_s; + BOOL y_is_int, y_is_odd; + int r_sign, ret, rnd_mode; + slimb_t y_emin; + + if (x->len == 0 || y->len == 0) { + if (y->expn == BF_EXP_ZERO) { + /* pow(x, 0) = 1 */ + bf_set_ui(r, 1); + } else if (x->expn == BF_EXP_NAN) { + bf_set_nan(r); + } else { + int cmp_x_abs_1; + bf_set_ui(r, 1); + cmp_x_abs_1 = bf_cmpu(x, r); + if (cmp_x_abs_1 == 0 && (flags & BF_POW_JS_QUIRKS) && + (y->expn >= BF_EXP_INF)) { + bf_set_nan(r); + } else if (cmp_x_abs_1 == 0 && + (!x->sign || y->expn != BF_EXP_NAN)) { + /* pow(1, y) = 1 even if y = NaN */ + /* pow(-1, +/-inf) = 1 */ + } else if (y->expn == BF_EXP_NAN) { + bf_set_nan(r); + } else if (y->expn == BF_EXP_INF) { + if (y->sign == (cmp_x_abs_1 > 0)) { + bf_set_zero(r, 0); + } else { + bf_set_inf(r, 0); + } + } else { + y_emin = bf_get_exp_min(y); + y_is_odd = (y_emin == 0); + if (y->sign == (x->expn == BF_EXP_ZERO)) { + bf_set_inf(r, y_is_odd & x->sign); + if (y->sign) { + /* pow(0, y) with y < 0 */ + return BF_ST_DIVIDE_ZERO; + } + } else { + bf_set_zero(r, y_is_odd & x->sign); + } + } + } + return 0; + } + bf_init(s, T); + bf_set(T, x); + y_emin = bf_get_exp_min(y); + y_is_int = (y_emin >= 0); + rnd_mode = flags & BF_RND_MASK; + if (x->sign) { + if (!y_is_int) { + bf_set_nan(r); + bf_delete(T); + return BF_ST_INVALID_OP; + } + y_is_odd = (y_emin == 0); + r_sign = y_is_odd; + /* change the directed rounding mode if the sign of the result + is changed */ + if (r_sign && (rnd_mode == BF_RNDD || rnd_mode == BF_RNDU)) + flags ^= 1; + bf_neg(T); + } else { + r_sign = 0; + } + + bf_set_ui(r, 1); + if (bf_cmp_eq(T, r)) { + /* abs(x) = 1: nothing more to do */ + ret = 0; + } else { + /* check the overflow/underflow cases */ + { + bf_t al_s, *al = &al_s; + bf_t ah_s, *ah = &ah_s; + limb_t precl = LIMB_BITS; + + bf_init(s, al); + bf_init(s, ah); + /* compute bounds of log(abs(x)) * y with a low precision */ + /* XXX: compute bf_log() once */ + /* XXX: add a fast test before this slow test */ + bf_log(al, T, precl, BF_RNDD); + bf_log(ah, T, precl, BF_RNDU); + bf_mul(al, al, y, precl, BF_RNDD ^ y->sign); + bf_mul(ah, ah, y, precl, BF_RNDU ^ y->sign); + ret = check_exp_underflow_overflow(s, r, al, ah, prec, flags); + bf_delete(al); + bf_delete(ah); + if (ret) + goto done; + } + + if (y_is_int) { + slimb_t T_bits, e; + int_pow: + T_bits = T->expn - bf_get_exp_min(T); + if (T_bits == 1) { + /* pow(2^b, y) = 2^(b*y) */ + bf_mul_si(T, y, T->expn - 1, LIMB_BITS, BF_RNDZ); + bf_get_limb(&e, T, 0); + bf_set_ui(r, 1); + ret = bf_mul_2exp(r, e, prec, flags); + } else if (prec == BF_PREC_INF) { + slimb_t y1; + /* specific case for infinite precision (integer case) */ + bf_get_limb(&y1, y, 0); + assert(!y->sign); + /* x must be an integer, so abs(x) >= 2 */ + if (y1 >= ((slimb_t)1 << BF_EXP_BITS_MAX)) { + bf_delete(T); + return bf_set_overflow(r, 0, BF_PREC_INF, flags); + } + ret = bf_pow_ui(r, T, y1, BF_PREC_INF, BF_RNDZ); + } else { + if (y->expn <= 31) { + /* small enough power: use exponentiation in all cases */ + } else if (y->sign) { + /* cannot be exact */ + goto general_case; + } else { + if (rnd_mode == BF_RNDF) + goto general_case; /* no need to track exact results */ + /* see if the result has a chance to be exact: + if x=a*2^b (a odd), x^y=a^y*2^(b*y) + x^y needs a precision of at least floor_log2(a)*y bits + */ + bf_mul_si(r, y, T_bits - 1, LIMB_BITS, BF_RNDZ); + bf_get_limb(&e, r, 0); + if (prec < e) + goto general_case; + } + ret = bf_ziv_rounding(r, T, prec, flags, bf_pow_int, (void *)y); + } + } else { + if (rnd_mode != BF_RNDF) { + bf_t *y1; + if (y_emin < 0 && check_exact_power2n(r, T, -y_emin)) { + /* the problem is reduced to a power to an integer */ + bf_set(T, r); + y1 = &ytmp_s; + y1->tab = y->tab; + y1->len = y->len; + y1->sign = y->sign; + y1->expn = y->expn - y_emin; + y = y1; + goto int_pow; + } + } + general_case: + ret = bf_ziv_rounding(r, T, prec, flags, bf_pow_generic, (void *)y); + } + } + done: + bf_delete(T); + r->sign = r_sign; + return ret; +} + +/* compute sqrt(-2*x-x^2) to get |sin(x)| from cos(x) - 1. */ +static void bf_sqrt_sin(bf_t *r, const bf_t *x, limb_t prec1) +{ + bf_context_t *s = r->ctx; + bf_t T_s, *T = &T_s; + bf_init(s, T); + bf_set(T, x); + bf_mul(r, T, T, prec1, BF_RNDN); + bf_mul_2exp(T, 1, BF_PREC_INF, BF_RNDZ); + bf_add(T, T, r, prec1, BF_RNDN); + bf_neg(T); + bf_sqrt(r, T, prec1, BF_RNDF); + bf_delete(T); +} + +static int bf_sincos(bf_t *s, bf_t *c, const bf_t *a, limb_t prec) +{ + bf_context_t *s1 = a->ctx; + bf_t T_s, *T = &T_s; + bf_t U_s, *U = &U_s; + bf_t r_s, *r = &r_s; + slimb_t K, prec1, i, l, mod, prec2; + int is_neg; + + assert(c != a && s != a); + + bf_init(s1, T); + bf_init(s1, U); + bf_init(s1, r); + + /* XXX: precision analysis */ + K = bf_isqrt(prec / 2); + l = prec / (2 * K) + 1; + prec1 = prec + 2 * K + l + 8; + + /* after the modulo reduction, -pi/4 <= T <= pi/4 */ + if (a->expn <= -1) { + /* abs(a) <= 0.25: no modulo reduction needed */ + bf_set(T, a); + mod = 0; + } else { + slimb_t cancel; + cancel = 0; + for(;;) { + prec2 = prec1 + a->expn + cancel; + bf_const_pi(U, prec2, BF_RNDF); + bf_mul_2exp(U, -1, BF_PREC_INF, BF_RNDZ); + bf_remquo(&mod, T, a, U, prec2, BF_RNDN, BF_RNDN); + // printf("T.expn=%ld prec2=%ld\n", T->expn, prec2); + if (mod == 0 || (T->expn != BF_EXP_ZERO && + (T->expn + prec2) >= (prec1 - 1))) + break; + /* increase the number of bits until the precision is good enough */ + cancel = bf_max(-T->expn, (cancel + 1) * 3 / 2); + } + mod &= 3; + } + + is_neg = T->sign; + + /* compute cosm1(x) = cos(x) - 1 */ + bf_mul(T, T, T, prec1, BF_RNDN); + bf_mul_2exp(T, -2 * K, BF_PREC_INF, BF_RNDZ); + + /* Taylor expansion: + -x^2/2 + x^4/4! - x^6/6! + ... + */ + bf_set_ui(r, 1); + for(i = l ; i >= 1; i--) { + bf_set_ui(U, 2 * i - 1); + bf_mul_ui(U, U, 2 * i, BF_PREC_INF, BF_RNDZ); + bf_div(U, T, U, prec1, BF_RNDN); + bf_mul(r, r, U, prec1, BF_RNDN); + bf_neg(r); + if (i != 1) + bf_add_si(r, r, 1, prec1, BF_RNDN); + } + bf_delete(U); + + /* undo argument reduction: + cosm1(2*x)= 2*(2*cosm1(x)+cosm1(x)^2) + */ + for(i = 0; i < K; i++) { + bf_mul(T, r, r, prec1, BF_RNDN); + bf_mul_2exp(r, 1, BF_PREC_INF, BF_RNDZ); + bf_add(r, r, T, prec1, BF_RNDN); + bf_mul_2exp(r, 1, BF_PREC_INF, BF_RNDZ); + } + bf_delete(T); + + if (c) { + if ((mod & 1) == 0) { + bf_add_si(c, r, 1, prec1, BF_RNDN); + } else { + bf_sqrt_sin(c, r, prec1); + c->sign = is_neg ^ 1; + } + c->sign ^= mod >> 1; + } + if (s) { + if ((mod & 1) == 0) { + bf_sqrt_sin(s, r, prec1); + s->sign = is_neg; + } else { + bf_add_si(s, r, 1, prec1, BF_RNDN); + } + s->sign ^= mod >> 1; + } + bf_delete(r); + return BF_ST_INEXACT; +} + +static int bf_cos_internal(bf_t *r, const bf_t *a, limb_t prec, void *opaque) +{ + return bf_sincos(NULL, r, a, prec); +} + +int bf_cos(bf_t *r, const bf_t *a, limb_t prec, bf_flags_t flags) +{ + if (a->len == 0) { + if (a->expn == BF_EXP_NAN) { + bf_set_nan(r); + return 0; + } else if (a->expn == BF_EXP_INF) { + bf_set_nan(r); + return BF_ST_INVALID_OP; + } else { + bf_set_ui(r, 1); + return 0; + } + } + + /* small argument case: result = 1+r(x) with r(x) = -x^2/2 + + O(X^4). We assume r(x) < 2^(2*EXP(x) - 1). */ + if (a->expn < 0) { + slimb_t e; + e = 2 * a->expn - 1; + if (e < -(prec + 2)) { + bf_set_ui(r, 1); + return bf_add_epsilon(r, r, e, 1, prec, flags); + } + } + + return bf_ziv_rounding(r, a, prec, flags, bf_cos_internal, NULL); +} + +static int bf_sin_internal(bf_t *r, const bf_t *a, limb_t prec, void *opaque) +{ + return bf_sincos(r, NULL, a, prec); +} + +int bf_sin(bf_t *r, const bf_t *a, limb_t prec, bf_flags_t flags) +{ + if (a->len == 0) { + if (a->expn == BF_EXP_NAN) { + bf_set_nan(r); + return 0; + } else if (a->expn == BF_EXP_INF) { + bf_set_nan(r); + return BF_ST_INVALID_OP; + } else { + bf_set_zero(r, a->sign); + return 0; + } + } + + /* small argument case: result = x+r(x) with r(x) = -x^3/6 + + O(X^5). We assume r(x) < 2^(3*EXP(x) - 2). */ + if (a->expn < 0) { + slimb_t e; + e = sat_add(2 * a->expn, a->expn - 2); + if (e < a->expn - bf_max(prec + 2, a->len * LIMB_BITS + 2)) { + bf_set(r, a); + return bf_add_epsilon(r, r, e, 1 - a->sign, prec, flags); + } + } + + return bf_ziv_rounding(r, a, prec, flags, bf_sin_internal, NULL); +} + +static int bf_tan_internal(bf_t *r, const bf_t *a, limb_t prec, void *opaque) +{ + bf_context_t *s = r->ctx; + bf_t T_s, *T = &T_s; + limb_t prec1; + + /* XXX: precision analysis */ + prec1 = prec + 8; + bf_init(s, T); + bf_sincos(r, T, a, prec1); + bf_div(r, r, T, prec1, BF_RNDF); + bf_delete(T); + return BF_ST_INEXACT; +} + +int bf_tan(bf_t *r, const bf_t *a, limb_t prec, bf_flags_t flags) +{ + assert(r != a); + if (a->len == 0) { + if (a->expn == BF_EXP_NAN) { + bf_set_nan(r); + return 0; + } else if (a->expn == BF_EXP_INF) { + bf_set_nan(r); + return BF_ST_INVALID_OP; + } else { + bf_set_zero(r, a->sign); + return 0; + } + } + + /* small argument case: result = x+r(x) with r(x) = x^3/3 + + O(X^5). We assume r(x) < 2^(3*EXP(x) - 1). */ + if (a->expn < 0) { + slimb_t e; + e = sat_add(2 * a->expn, a->expn - 1); + if (e < a->expn - bf_max(prec + 2, a->len * LIMB_BITS + 2)) { + bf_set(r, a); + return bf_add_epsilon(r, r, e, a->sign, prec, flags); + } + } + + return bf_ziv_rounding(r, a, prec, flags, bf_tan_internal, NULL); +} + +/* if add_pi2 is true, add pi/2 to the result (used for acos(x) to + avoid cancellation) */ +static int bf_atan_internal(bf_t *r, const bf_t *a, limb_t prec, + void *opaque) +{ + bf_context_t *s = r->ctx; + BOOL add_pi2 = (BOOL)(intptr_t)opaque; + bf_t T_s, *T = &T_s; + bf_t U_s, *U = &U_s; + bf_t V_s, *V = &V_s; + bf_t X2_s, *X2 = &X2_s; + int cmp_1; + slimb_t prec1, i, K, l; + + /* XXX: precision analysis */ + K = bf_isqrt((prec + 1) / 2); + l = prec / (2 * K) + 1; + prec1 = prec + K + 2 * l + 32; + // printf("prec=%d K=%d l=%d prec1=%d\n", (int)prec, (int)K, (int)l, (int)prec1); + + bf_init(s, T); + cmp_1 = (a->expn >= 1); /* a >= 1 */ + if (cmp_1) { + bf_set_ui(T, 1); + bf_div(T, T, a, prec1, BF_RNDN); + } else { + bf_set(T, a); + } + + /* abs(T) <= 1 */ + + /* argument reduction */ + + bf_init(s, U); + bf_init(s, V); + bf_init(s, X2); + for(i = 0; i < K; i++) { + /* T = T / (1 + sqrt(1 + T^2)) */ + bf_mul(U, T, T, prec1, BF_RNDN); + bf_add_si(U, U, 1, prec1, BF_RNDN); + bf_sqrt(V, U, prec1, BF_RNDN); + bf_add_si(V, V, 1, prec1, BF_RNDN); + bf_div(T, T, V, prec1, BF_RNDN); + } + + /* Taylor series: + x - x^3/3 + ... + (-1)^ l * y^(2*l + 1) / (2*l+1) + */ + bf_mul(X2, T, T, prec1, BF_RNDN); + bf_set_ui(r, 0); + for(i = l; i >= 1; i--) { + bf_set_si(U, 1); + bf_set_ui(V, 2 * i + 1); + bf_div(U, U, V, prec1, BF_RNDN); + bf_neg(r); + bf_add(r, r, U, prec1, BF_RNDN); + bf_mul(r, r, X2, prec1, BF_RNDN); + } + bf_neg(r); + bf_add_si(r, r, 1, prec1, BF_RNDN); + bf_mul(r, r, T, prec1, BF_RNDN); + + /* undo the argument reduction */ + bf_mul_2exp(r, K, BF_PREC_INF, BF_RNDZ); + + bf_delete(U); + bf_delete(V); + bf_delete(X2); + + i = add_pi2; + if (cmp_1 > 0) { + /* undo the inversion : r = sign(a)*PI/2 - r */ + bf_neg(r); + i += 1 - 2 * a->sign; + } + /* add i*(pi/2) with -1 <= i <= 2 */ + if (i != 0) { + bf_const_pi(T, prec1, BF_RNDF); + if (i != 2) + bf_mul_2exp(T, -1, BF_PREC_INF, BF_RNDZ); + T->sign = (i < 0); + bf_add(r, T, r, prec1, BF_RNDN); + } + + bf_delete(T); + return BF_ST_INEXACT; +} + +int bf_atan(bf_t *r, const bf_t *a, limb_t prec, bf_flags_t flags) +{ + bf_context_t *s = r->ctx; + bf_t T_s, *T = &T_s; + int res; + + if (a->len == 0) { + if (a->expn == BF_EXP_NAN) { + bf_set_nan(r); + return 0; + } else if (a->expn == BF_EXP_INF) { + /* -PI/2 or PI/2 */ + bf_const_pi_signed(r, a->sign, prec, flags); + bf_mul_2exp(r, -1, BF_PREC_INF, BF_RNDZ); + return BF_ST_INEXACT; + } else { + bf_set_zero(r, a->sign); + return 0; + } + } + + bf_init(s, T); + bf_set_ui(T, 1); + res = bf_cmpu(a, T); + bf_delete(T); + if (res == 0) { + /* short cut: abs(a) == 1 -> +/-pi/4 */ + bf_const_pi_signed(r, a->sign, prec, flags); + bf_mul_2exp(r, -2, BF_PREC_INF, BF_RNDZ); + return BF_ST_INEXACT; + } + + /* small argument case: result = x+r(x) with r(x) = -x^3/3 + + O(X^5). We assume r(x) < 2^(3*EXP(x) - 1). */ + if (a->expn < 0) { + slimb_t e; + e = sat_add(2 * a->expn, a->expn - 1); + if (e < a->expn - bf_max(prec + 2, a->len * LIMB_BITS + 2)) { + bf_set(r, a); + return bf_add_epsilon(r, r, e, 1 - a->sign, prec, flags); + } + } + + return bf_ziv_rounding(r, a, prec, flags, bf_atan_internal, (void *)FALSE); +} + +static int bf_atan2_internal(bf_t *r, const bf_t *y, limb_t prec, void *opaque) +{ + bf_context_t *s = r->ctx; + const bf_t *x = opaque; + bf_t T_s, *T = &T_s; + limb_t prec1; + int ret; + + if (y->expn == BF_EXP_NAN || x->expn == BF_EXP_NAN) { + bf_set_nan(r); + return 0; + } + + /* compute atan(y/x) assumming inf/inf = 1 and 0/0 = 0 */ + bf_init(s, T); + prec1 = prec + 32; + if (y->expn == BF_EXP_INF && x->expn == BF_EXP_INF) { + bf_set_ui(T, 1); + T->sign = y->sign ^ x->sign; + } else if (y->expn == BF_EXP_ZERO && x->expn == BF_EXP_ZERO) { + bf_set_zero(T, y->sign ^ x->sign); + } else { + bf_div(T, y, x, prec1, BF_RNDF); + } + ret = bf_atan(r, T, prec1, BF_RNDF); + + if (x->sign) { + /* if x < 0 (it includes -0), return sign(y)*pi + atan(y/x) */ + bf_const_pi(T, prec1, BF_RNDF); + T->sign = y->sign; + bf_add(r, r, T, prec1, BF_RNDN); + ret |= BF_ST_INEXACT; + } + + bf_delete(T); + return ret; +} + +int bf_atan2(bf_t *r, const bf_t *y, const bf_t *x, + limb_t prec, bf_flags_t flags) +{ + return bf_ziv_rounding(r, y, prec, flags, bf_atan2_internal, (void *)x); +} + +static int bf_asin_internal(bf_t *r, const bf_t *a, limb_t prec, void *opaque) +{ + bf_context_t *s = r->ctx; + BOOL is_acos = (BOOL)(intptr_t)opaque; + bf_t T_s, *T = &T_s; + limb_t prec1, prec2; + + /* asin(x) = atan(x/sqrt(1-x^2)) + acos(x) = pi/2 - asin(x) */ + prec1 = prec + 8; + /* increase the precision in x^2 to compensate the cancellation in + (1-x^2) if x is close to 1 */ + /* XXX: use less precision when possible */ + if (a->expn >= 0) + prec2 = BF_PREC_INF; + else + prec2 = prec1; + bf_init(s, T); + bf_mul(T, a, a, prec2, BF_RNDN); + bf_neg(T); + bf_add_si(T, T, 1, prec2, BF_RNDN); + + bf_sqrt(r, T, prec1, BF_RNDN); + bf_div(T, a, r, prec1, BF_RNDN); + if (is_acos) + bf_neg(T); + bf_atan_internal(r, T, prec1, (void *)(intptr_t)is_acos); + bf_delete(T); + return BF_ST_INEXACT; +} + +int bf_asin(bf_t *r, const bf_t *a, limb_t prec, bf_flags_t flags) +{ + bf_context_t *s = r->ctx; + bf_t T_s, *T = &T_s; + int res; + + if (a->len == 0) { + if (a->expn == BF_EXP_NAN) { + bf_set_nan(r); + return 0; + } else if (a->expn == BF_EXP_INF) { + bf_set_nan(r); + return BF_ST_INVALID_OP; + } else { + bf_set_zero(r, a->sign); + return 0; + } + } + bf_init(s, T); + bf_set_ui(T, 1); + res = bf_cmpu(a, T); + bf_delete(T); + if (res > 0) { + bf_set_nan(r); + return BF_ST_INVALID_OP; + } + + /* small argument case: result = x+r(x) with r(x) = x^3/6 + + O(X^5). We assume r(x) < 2^(3*EXP(x) - 2). */ + if (a->expn < 0) { + slimb_t e; + e = sat_add(2 * a->expn, a->expn - 2); + if (e < a->expn - bf_max(prec + 2, a->len * LIMB_BITS + 2)) { + bf_set(r, a); + return bf_add_epsilon(r, r, e, a->sign, prec, flags); + } + } + + return bf_ziv_rounding(r, a, prec, flags, bf_asin_internal, (void *)FALSE); +} + +int bf_acos(bf_t *r, const bf_t *a, limb_t prec, bf_flags_t flags) +{ + bf_context_t *s = r->ctx; + bf_t T_s, *T = &T_s; + int res; + + if (a->len == 0) { + if (a->expn == BF_EXP_NAN) { + bf_set_nan(r); + return 0; + } else if (a->expn == BF_EXP_INF) { + bf_set_nan(r); + return BF_ST_INVALID_OP; + } else { + bf_const_pi(r, prec, flags); + bf_mul_2exp(r, -1, BF_PREC_INF, BF_RNDZ); + return BF_ST_INEXACT; + } + } + bf_init(s, T); + bf_set_ui(T, 1); + res = bf_cmpu(a, T); + bf_delete(T); + if (res > 0) { + bf_set_nan(r); + return BF_ST_INVALID_OP; + } else if (res == 0 && a->sign == 0) { + bf_set_zero(r, 0); + return 0; + } + + return bf_ziv_rounding(r, a, prec, flags, bf_asin_internal, (void *)TRUE); +} + +/***************************************************************/ +/* decimal floating point numbers */ + +#ifdef USE_BF_DEC + +#define adddq(r1, r0, a1, a0) \ + do { \ + limb_t __t = r0; \ + r0 += (a0); \ + r1 += (a1) + (r0 < __t); \ + } while (0) + +#define subdq(r1, r0, a1, a0) \ + do { \ + limb_t __t = r0; \ + r0 -= (a0); \ + r1 -= (a1) + (r0 > __t); \ + } while (0) + +#if LIMB_BITS == 64 + +/* Note: we assume __int128 is available */ +/* uint128_t defined in libbf.h */ +#define muldq(r1, r0, a, b) \ + do { \ + uint128_t __t; \ + __t = (uint128_t)(a) * (uint128_t)(b); \ + r0 = __t; \ + r1 = __t >> 64; \ + } while (0) + +#define divdq(q, r, a1, a0, b) \ + do { \ + uint128_t __t; \ + limb_t __b = (b); \ + __t = ((uint128_t)(a1) << 64) | (a0); \ + q = __t / __b; \ + r = __t % __b; \ + } while (0) + +#else + +#define muldq(r1, r0, a, b) \ + do { \ + uint64_t __t; \ + __t = (uint64_t)(a) * (uint64_t)(b); \ + r0 = __t; \ + r1 = __t >> 32; \ + } while (0) + +#define divdq(q, r, a1, a0, b) \ + do { \ + uint64_t __t; \ + limb_t __b = (b); \ + __t = ((uint64_t)(a1) << 32) | (a0); \ + q = __t / __b; \ + r = __t % __b; \ + } while (0) + +#endif /* LIMB_BITS != 64 */ + +#if LIMB_DIGITS == 19 + +/* WARNING: hardcoded for b = 1e19. It is assumed that: + 0 <= a1 < 2^63 */ +#define divdq_base(q, r, a1, a0)\ +do {\ + uint64_t __a0, __a1, __t0, __t1, __b = BF_DEC_BASE; \ + __a0 = a0;\ + __a1 = a1;\ + __t0 = __a1;\ + __t0 = shld(__t0, __a0, 1);\ + muldq(q, __t1, __t0, UINT64_C(17014118346046923173)); \ + muldq(__t1, __t0, q, __b);\ + subdq(__a1, __a0, __t1, __t0);\ + subdq(__a1, __a0, 1, __b * 2); \ + __t0 = (slimb_t)__a1 >> 1; \ + q += 2 + __t0;\ + adddq(__a1, __a0, 0, __b & __t0);\ + q += __a1; \ + __a0 += __b & __a1; \ + r = __a0;\ +} while(0) + +#elif LIMB_DIGITS == 9 + +/* WARNING: hardcoded for b = 1e9. It is assumed that: + 0 <= a1 < 2^29 */ +#define divdq_base(q, r, a1, a0)\ +do {\ + uint32_t __t0, __t1, __b = BF_DEC_BASE; \ + __t0 = a1;\ + __t1 = a0;\ + __t0 = (__t0 << 3) | (__t1 >> (32 - 3)); \ + muldq(q, __t1, __t0, 2305843009U);\ + r = a0 - q * __b;\ + __t1 = (r >= __b);\ + q += __t1;\ + if (__t1)\ + r -= __b;\ +} while(0) + +#endif + +/* fast integer division by a fixed constant */ + +typedef struct FastDivData { + limb_t m1; /* multiplier */ + int8_t shift1; + int8_t shift2; +} FastDivData; + +/* From "Division by Invariant Integers using Multiplication" by + Torborn Granlund and Peter L. Montgomery */ +/* d must be != 0 */ +static inline __maybe_unused void fast_udiv_init(FastDivData *s, limb_t d) +{ + int l; + limb_t q, r, m1; + if (d == 1) + l = 0; + else + l = 64 - clz64(d - 1); + divdq(q, r, ((limb_t)1 << l) - d, 0, d); + (void)r; + m1 = q + 1; + // printf("d=%lu l=%d m1=0x%016lx\n", d, l, m1); + s->m1 = m1; + s->shift1 = l; + if (s->shift1 > 1) + s->shift1 = 1; + s->shift2 = l - 1; + if (s->shift2 < 0) + s->shift2 = 0; +} + +static inline limb_t fast_udiv(limb_t a, const FastDivData *s) +{ + limb_t t0, t1; + muldq(t1, t0, s->m1, a); + t0 = (a - t1) >> s->shift1; + return (t1 + t0) >> s->shift2; +} + +/* contains 10^i */ +const limb_t mp_pow_dec[LIMB_DIGITS + 1] = { + 1U, + 10U, + 100U, + 1000U, + 10000U, + 100000U, + 1000000U, + 10000000U, + 100000000U, + 1000000000U, +#if LIMB_BITS == 64 + 10000000000U, + 100000000000U, + 1000000000000U, + 10000000000000U, + 100000000000000U, + 1000000000000000U, + 10000000000000000U, + 100000000000000000U, + 1000000000000000000U, + 10000000000000000000U, +#endif +}; + +/* precomputed from fast_udiv_init(10^i) */ +static const FastDivData mp_pow_div[LIMB_DIGITS + 1] = { +#if LIMB_BITS == 32 + { 0x00000001, 0, 0 }, + { 0x9999999a, 1, 3 }, + { 0x47ae147b, 1, 6 }, + { 0x0624dd30, 1, 9 }, + { 0xa36e2eb2, 1, 13 }, + { 0x4f8b588f, 1, 16 }, + { 0x0c6f7a0c, 1, 19 }, + { 0xad7f29ac, 1, 23 }, + { 0x5798ee24, 1, 26 }, + { 0x12e0be83, 1, 29 }, +#else + { 0x0000000000000001, 0, 0 }, + { 0x999999999999999a, 1, 3 }, + { 0x47ae147ae147ae15, 1, 6 }, + { 0x0624dd2f1a9fbe77, 1, 9 }, + { 0xa36e2eb1c432ca58, 1, 13 }, + { 0x4f8b588e368f0847, 1, 16 }, + { 0x0c6f7a0b5ed8d36c, 1, 19 }, + { 0xad7f29abcaf48579, 1, 23 }, + { 0x5798ee2308c39dfa, 1, 26 }, + { 0x12e0be826d694b2f, 1, 29 }, + { 0xb7cdfd9d7bdbab7e, 1, 33 }, + { 0x5fd7fe17964955fe, 1, 36 }, + { 0x19799812dea11198, 1, 39 }, + { 0xc25c268497681c27, 1, 43 }, + { 0x6849b86a12b9b01f, 1, 46 }, + { 0x203af9ee756159b3, 1, 49 }, + { 0xcd2b297d889bc2b7, 1, 53 }, + { 0x70ef54646d496893, 1, 56 }, + { 0x2725dd1d243aba0f, 1, 59 }, + { 0xd83c94fb6d2ac34d, 1, 63 }, +#endif +}; + +/* divide by 10^shift with 0 <= shift <= LIMB_DIGITS */ +static inline limb_t fast_shr_dec(limb_t a, int shift) +{ + return fast_udiv(a, &mp_pow_div[shift]); +} + +/* division and remainder by 10^shift */ +#define fast_shr_rem_dec(q, r, a, shift) q = fast_shr_dec(a, shift), r = a - q * mp_pow_dec[shift] + +limb_t mp_add_dec(limb_t *res, const limb_t *op1, const limb_t *op2, + mp_size_t n, limb_t carry) +{ + limb_t base = BF_DEC_BASE; + mp_size_t i; + limb_t k, a, v; + + k=carry; + for(i=0;i<n;i++) { + /* XXX: reuse the trick in add_mod */ + v = op1[i]; + a = v + op2[i] + k - base; + k = a <= v; + if (!k) + a += base; + res[i]=a; + } + return k; +} + +limb_t mp_add_ui_dec(limb_t *tab, limb_t b, mp_size_t n) +{ + limb_t base = BF_DEC_BASE; + mp_size_t i; + limb_t k, a, v; + + k=b; + for(i=0;i<n;i++) { + v = tab[i]; + a = v + k - base; + k = a <= v; + if (!k) + a += base; + tab[i] = a; + if (k == 0) + break; + } + return k; +} + +limb_t mp_sub_dec(limb_t *res, const limb_t *op1, const limb_t *op2, + mp_size_t n, limb_t carry) +{ + limb_t base = BF_DEC_BASE; + mp_size_t i; + limb_t k, v, a; + + k=carry; + for(i=0;i<n;i++) { + v = op1[i]; + a = v - op2[i] - k; + k = a > v; + if (k) + a += base; + res[i] = a; + } + return k; +} + +limb_t mp_sub_ui_dec(limb_t *tab, limb_t b, mp_size_t n) +{ + limb_t base = BF_DEC_BASE; + mp_size_t i; + limb_t k, v, a; + + k=b; + for(i=0;i<n;i++) { + v = tab[i]; + a = v - k; + k = a > v; + if (k) + a += base; + tab[i]=a; + if (k == 0) + break; + } + return k; +} + +/* taba[] = taba[] * b + l. 0 <= b, l <= base - 1. Return the high carry */ +limb_t mp_mul1_dec(limb_t *tabr, const limb_t *taba, mp_size_t n, + limb_t b, limb_t l) +{ + mp_size_t i; + limb_t t0, t1, r; + + for(i = 0; i < n; i++) { + muldq(t1, t0, taba[i], b); + adddq(t1, t0, 0, l); + divdq_base(l, r, t1, t0); + tabr[i] = r; + } + return l; +} + +/* tabr[] += taba[] * b. 0 <= b <= base - 1. Return the value to add + to the high word */ +limb_t mp_add_mul1_dec(limb_t *tabr, const limb_t *taba, mp_size_t n, + limb_t b) +{ + mp_size_t i; + limb_t l, t0, t1, r; + + l = 0; + for(i = 0; i < n; i++) { + muldq(t1, t0, taba[i], b); + adddq(t1, t0, 0, l); + adddq(t1, t0, 0, tabr[i]); + divdq_base(l, r, t1, t0); + tabr[i] = r; + } + return l; +} + +/* tabr[] -= taba[] * b. 0 <= b <= base - 1. Return the value to + substract to the high word. */ +limb_t mp_sub_mul1_dec(limb_t *tabr, const limb_t *taba, mp_size_t n, + limb_t b) +{ + limb_t base = BF_DEC_BASE; + mp_size_t i; + limb_t l, t0, t1, r, a, v, c; + + /* XXX: optimize */ + l = 0; + for(i = 0; i < n; i++) { + muldq(t1, t0, taba[i], b); + adddq(t1, t0, 0, l); + divdq_base(l, r, t1, t0); + v = tabr[i]; + a = v - r; + c = a > v; + if (c) + a += base; + /* never bigger than base because r = 0 when l = base - 1 */ + l += c; + tabr[i] = a; + } + return l; +} + +/* size of the result : op1_size + op2_size. */ +void mp_mul_basecase_dec(limb_t *result, + const limb_t *op1, mp_size_t op1_size, + const limb_t *op2, mp_size_t op2_size) +{ + mp_size_t i; + limb_t r; + + result[op1_size] = mp_mul1_dec(result, op1, op1_size, op2[0], 0); + + for(i=1;i<op2_size;i++) { + r = mp_add_mul1_dec(result + i, op1, op1_size, op2[i]); + result[i + op1_size] = r; + } +} + +/* taba[] = (taba[] + r*base^na) / b. 0 <= b < base. 0 <= r < + b. Return the remainder. */ +limb_t mp_div1_dec(limb_t *tabr, const limb_t *taba, mp_size_t na, + limb_t b, limb_t r) +{ + limb_t base = BF_DEC_BASE; + mp_size_t i; + limb_t t0, t1, q; + int shift; + +#if (BF_DEC_BASE % 2) == 0 + if (b == 2) { + limb_t base_div2; + /* Note: only works if base is even */ + base_div2 = base >> 1; + if (r) + r = base_div2; + for(i = na - 1; i >= 0; i--) { + t0 = taba[i]; + tabr[i] = (t0 >> 1) + r; + r = 0; + if (t0 & 1) + r = base_div2; + } + if (r) + r = 1; + } else +#endif + if (na >= UDIV1NORM_THRESHOLD) { + shift = clz(b); + if (shift == 0) { + /* normalized case: b >= 2^(LIMB_BITS-1) */ + limb_t b_inv; + b_inv = udiv1norm_init(b); + for(i = na - 1; i >= 0; i--) { + muldq(t1, t0, r, base); + adddq(t1, t0, 0, taba[i]); + q = udiv1norm(&r, t1, t0, b, b_inv); + tabr[i] = q; + } + } else { + limb_t b_inv; + b <<= shift; + b_inv = udiv1norm_init(b); + for(i = na - 1; i >= 0; i--) { + muldq(t1, t0, r, base); + adddq(t1, t0, 0, taba[i]); + t1 = (t1 << shift) | (t0 >> (LIMB_BITS - shift)); + t0 <<= shift; + q = udiv1norm(&r, t1, t0, b, b_inv); + r >>= shift; + tabr[i] = q; + } + } + } else { + for(i = na - 1; i >= 0; i--) { + muldq(t1, t0, r, base); + adddq(t1, t0, 0, taba[i]); + divdq(q, r, t1, t0, b); + tabr[i] = q; + } + } + return r; +} + +static __maybe_unused void mp_print_str_dec(const char *str, + const limb_t *tab, slimb_t n) +{ + slimb_t i; + printf("%s=", str); + for(i = n - 1; i >= 0; i--) { + if (i != n - 1) + printf("_"); + printf("%0*" PRIu_LIMB, LIMB_DIGITS, tab[i]); + } + printf("\n"); +} + +static __maybe_unused void mp_print_str_h_dec(const char *str, + const limb_t *tab, slimb_t n, + limb_t high) +{ + slimb_t i; + printf("%s=", str); + printf("%0*" PRIu_LIMB, LIMB_DIGITS, high); + for(i = n - 1; i >= 0; i--) { + printf("_"); + printf("%0*" PRIu_LIMB, LIMB_DIGITS, tab[i]); + } + printf("\n"); +} + +//#define DEBUG_DIV_SLOW + +#define DIV_STATIC_ALLOC_LEN 16 + +/* return q = a / b and r = a % b. + + taba[na] must be allocated if tabb1[nb - 1] < B / 2. tabb1[nb - 1] + must be != zero. na must be >= nb. 's' can be NULL if tabb1[nb - 1] + >= B / 2. + + The remainder is is returned in taba and contains nb libms. tabq + contains na - nb + 1 limbs. No overlap is permitted. + + Running time of the standard method: (na - nb + 1) * nb + Return 0 if OK, -1 if memory alloc error +*/ +/* XXX: optimize */ +static int mp_div_dec(bf_context_t *s, limb_t *tabq, + limb_t *taba, mp_size_t na, + const limb_t *tabb1, mp_size_t nb) +{ + limb_t base = BF_DEC_BASE; + limb_t r, mult, t0, t1, a, c, q, v, *tabb; + mp_size_t i, j; + limb_t static_tabb[DIV_STATIC_ALLOC_LEN]; + +#ifdef DEBUG_DIV_SLOW + mp_print_str_dec("a", taba, na); + mp_print_str_dec("b", tabb1, nb); +#endif + + /* normalize tabb */ + r = tabb1[nb - 1]; + assert(r != 0); + i = na - nb; + if (r >= BF_DEC_BASE / 2) { + mult = 1; + tabb = (limb_t *)tabb1; + q = 1; + for(j = nb - 1; j >= 0; j--) { + if (taba[i + j] != tabb[j]) { + if (taba[i + j] < tabb[j]) + q = 0; + break; + } + } + tabq[i] = q; + if (q) { + mp_sub_dec(taba + i, taba + i, tabb, nb, 0); + } + i--; + } else { + mult = base / (r + 1); + if (likely(nb <= DIV_STATIC_ALLOC_LEN)) { + tabb = static_tabb; + } else { + tabb = bf_malloc(s, sizeof(limb_t) * nb); + if (!tabb) + return -1; + } + mp_mul1_dec(tabb, tabb1, nb, mult, 0); + taba[na] = mp_mul1_dec(taba, taba, na, mult, 0); + } + +#ifdef DEBUG_DIV_SLOW + printf("mult=" FMT_LIMB "\n", mult); + mp_print_str_dec("a_norm", taba, na + 1); + mp_print_str_dec("b_norm", tabb, nb); +#endif + + for(; i >= 0; i--) { + if (unlikely(taba[i + nb] >= tabb[nb - 1])) { + /* XXX: check if it is really possible */ + q = base - 1; + } else { + muldq(t1, t0, taba[i + nb], base); + adddq(t1, t0, 0, taba[i + nb - 1]); + divdq(q, r, t1, t0, tabb[nb - 1]); + } + // printf("i=%d q1=%ld\n", i, q); + + r = mp_sub_mul1_dec(taba + i, tabb, nb, q); + // mp_dump("r1", taba + i, nb, bd); + // printf("r2=%ld\n", r); + + v = taba[i + nb]; + a = v - r; + c = a > v; + if (c) + a += base; + taba[i + nb] = a; + + if (c != 0) { + /* negative result */ + for(;;) { + q--; + c = mp_add_dec(taba + i, taba + i, tabb, nb, 0); + /* propagate carry and test if positive result */ + if (c != 0) { + if (++taba[i + nb] == base) { + break; + } + } + } + } + tabq[i] = q; + } + +#ifdef DEBUG_DIV_SLOW + mp_print_str_dec("q", tabq, na - nb + 1); + mp_print_str_dec("r", taba, nb); +#endif + + /* remove the normalization */ + if (mult != 1) { + mp_div1_dec(taba, taba, nb, mult, 0); + if (unlikely(tabb != static_tabb)) + bf_free(s, tabb); + } + return 0; +} + +/* divide by 10^shift */ +static limb_t mp_shr_dec(limb_t *tab_r, const limb_t *tab, mp_size_t n, + limb_t shift, limb_t high) +{ + mp_size_t i; + limb_t l, a, q, r; + + assert(shift >= 1 && shift < LIMB_DIGITS); + l = high; + for(i = n - 1; i >= 0; i--) { + a = tab[i]; + fast_shr_rem_dec(q, r, a, shift); + tab_r[i] = q + l * mp_pow_dec[LIMB_DIGITS - shift]; + l = r; + } + return l; +} + +/* multiply by 10^shift */ +static limb_t mp_shl_dec(limb_t *tab_r, const limb_t *tab, mp_size_t n, + limb_t shift, limb_t low) +{ + mp_size_t i; + limb_t l, a, q, r; + + assert(shift >= 1 && shift < LIMB_DIGITS); + l = low; + for(i = 0; i < n; i++) { + a = tab[i]; + fast_shr_rem_dec(q, r, a, LIMB_DIGITS - shift); + tab_r[i] = r * mp_pow_dec[shift] + l; + l = q; + } + return l; +} + +static limb_t mp_sqrtrem2_dec(limb_t *tabs, limb_t *taba) +{ + int k; + dlimb_t a, b, r; + limb_t taba1[2], s, r0, r1; + + /* convert to binary and normalize */ + a = (dlimb_t)taba[1] * BF_DEC_BASE + taba[0]; + k = clz(a >> LIMB_BITS) & ~1; + b = a << k; + taba1[0] = b; + taba1[1] = b >> LIMB_BITS; + mp_sqrtrem2(&s, taba1); + s >>= (k >> 1); + /* convert the remainder back to decimal */ + r = a - (dlimb_t)s * (dlimb_t)s; + divdq_base(r1, r0, r >> LIMB_BITS, r); + taba[0] = r0; + tabs[0] = s; + return r1; +} + +//#define DEBUG_SQRTREM_DEC + +/* tmp_buf must contain (n / 2 + 1 limbs) */ +static limb_t mp_sqrtrem_rec_dec(limb_t *tabs, limb_t *taba, limb_t n, + limb_t *tmp_buf) +{ + limb_t l, h, rh, ql, qh, c, i; + + if (n == 1) + return mp_sqrtrem2_dec(tabs, taba); +#ifdef DEBUG_SQRTREM_DEC + mp_print_str_dec("a", taba, 2 * n); +#endif + l = n / 2; + h = n - l; + qh = mp_sqrtrem_rec_dec(tabs + l, taba + 2 * l, h, tmp_buf); +#ifdef DEBUG_SQRTREM_DEC + mp_print_str_dec("s1", tabs + l, h); + mp_print_str_h_dec("r1", taba + 2 * l, h, qh); + mp_print_str_h_dec("r2", taba + l, n, qh); +#endif + + /* the remainder is in taba + 2 * l. Its high bit is in qh */ + if (qh) { + mp_sub_dec(taba + 2 * l, taba + 2 * l, tabs + l, h, 0); + } + /* instead of dividing by 2*s, divide by s (which is normalized) + and update q and r */ + mp_div_dec(NULL, tmp_buf, taba + l, n, tabs + l, h); + qh += tmp_buf[l]; + for(i = 0; i < l; i++) + tabs[i] = tmp_buf[i]; + ql = mp_div1_dec(tabs, tabs, l, 2, qh & 1); + qh = qh >> 1; /* 0 or 1 */ + if (ql) + rh = mp_add_dec(taba + l, taba + l, tabs + l, h, 0); + else + rh = 0; +#ifdef DEBUG_SQRTREM_DEC + mp_print_str_h_dec("q", tabs, l, qh); + mp_print_str_h_dec("u", taba + l, h, rh); +#endif + + mp_add_ui_dec(tabs + l, qh, h); +#ifdef DEBUG_SQRTREM_DEC + mp_print_str_dec("s2", tabs, n); +#endif + + /* q = qh, tabs[l - 1 ... 0], r = taba[n - 1 ... l] */ + /* subtract q^2. if qh = 1 then q = B^l, so we can take shortcuts */ + if (qh) { + c = qh; + } else { + mp_mul_basecase_dec(taba + n, tabs, l, tabs, l); + c = mp_sub_dec(taba, taba, taba + n, 2 * l, 0); + } + rh -= mp_sub_ui_dec(taba + 2 * l, c, n - 2 * l); + if ((slimb_t)rh < 0) { + mp_sub_ui_dec(tabs, 1, n); + rh += mp_add_mul1_dec(taba, tabs, n, 2); + rh += mp_add_ui_dec(taba, 1, n); + } + return rh; +} + +/* 'taba' has 2*n limbs with n >= 1 and taba[2*n-1] >= B/4. Return (s, + r) with s=floor(sqrt(a)) and r=a-s^2. 0 <= r <= 2 * s. tabs has n + limbs. r is returned in the lower n limbs of taba. Its r[n] is the + returned value of the function. */ +int mp_sqrtrem_dec(bf_context_t *s, limb_t *tabs, limb_t *taba, limb_t n) +{ + limb_t tmp_buf1[8]; + limb_t *tmp_buf; + mp_size_t n2; + n2 = n / 2 + 1; + if (n2 <= countof(tmp_buf1)) { + tmp_buf = tmp_buf1; + } else { + tmp_buf = bf_malloc(s, sizeof(limb_t) * n2); + if (!tmp_buf) + return -1; + } + taba[n] = mp_sqrtrem_rec_dec(tabs, taba, n, tmp_buf); + if (tmp_buf != tmp_buf1) + bf_free(s, tmp_buf); + return 0; +} + +/* return the number of leading zero digits, from 0 to LIMB_DIGITS */ +static int clz_dec(limb_t a) +{ + if (a == 0) + return LIMB_DIGITS; + switch(LIMB_BITS - 1 - clz(a)) { + case 0: /* 1-1 */ + return LIMB_DIGITS - 1; + case 1: /* 2-3 */ + return LIMB_DIGITS - 1; + case 2: /* 4-7 */ + return LIMB_DIGITS - 1; + case 3: /* 8-15 */ + if (a < 10) + return LIMB_DIGITS - 1; + else + return LIMB_DIGITS - 2; + case 4: /* 16-31 */ + return LIMB_DIGITS - 2; + case 5: /* 32-63 */ + return LIMB_DIGITS - 2; + case 6: /* 64-127 */ + if (a < 100) + return LIMB_DIGITS - 2; + else + return LIMB_DIGITS - 3; + case 7: /* 128-255 */ + return LIMB_DIGITS - 3; + case 8: /* 256-511 */ + return LIMB_DIGITS - 3; + case 9: /* 512-1023 */ + if (a < 1000) + return LIMB_DIGITS - 3; + else + return LIMB_DIGITS - 4; + case 10: /* 1024-2047 */ + return LIMB_DIGITS - 4; + case 11: /* 2048-4095 */ + return LIMB_DIGITS - 4; + case 12: /* 4096-8191 */ + return LIMB_DIGITS - 4; + case 13: /* 8192-16383 */ + if (a < 10000) + return LIMB_DIGITS - 4; + else + return LIMB_DIGITS - 5; + case 14: /* 16384-32767 */ + return LIMB_DIGITS - 5; + case 15: /* 32768-65535 */ + return LIMB_DIGITS - 5; + case 16: /* 65536-131071 */ + if (a < 100000) + return LIMB_DIGITS - 5; + else + return LIMB_DIGITS - 6; + case 17: /* 131072-262143 */ + return LIMB_DIGITS - 6; + case 18: /* 262144-524287 */ + return LIMB_DIGITS - 6; + case 19: /* 524288-1048575 */ + if (a < 1000000) + return LIMB_DIGITS - 6; + else + return LIMB_DIGITS - 7; + case 20: /* 1048576-2097151 */ + return LIMB_DIGITS - 7; + case 21: /* 2097152-4194303 */ + return LIMB_DIGITS - 7; + case 22: /* 4194304-8388607 */ + return LIMB_DIGITS - 7; + case 23: /* 8388608-16777215 */ + if (a < 10000000) + return LIMB_DIGITS - 7; + else + return LIMB_DIGITS - 8; + case 24: /* 16777216-33554431 */ + return LIMB_DIGITS - 8; + case 25: /* 33554432-67108863 */ + return LIMB_DIGITS - 8; + case 26: /* 67108864-134217727 */ + if (a < 100000000) + return LIMB_DIGITS - 8; + else + return LIMB_DIGITS - 9; +#if LIMB_BITS == 64 + case 27: /* 134217728-268435455 */ + return LIMB_DIGITS - 9; + case 28: /* 268435456-536870911 */ + return LIMB_DIGITS - 9; + case 29: /* 536870912-1073741823 */ + if (a < 1000000000) + return LIMB_DIGITS - 9; + else + return LIMB_DIGITS - 10; + case 30: /* 1073741824-2147483647 */ + return LIMB_DIGITS - 10; + case 31: /* 2147483648-4294967295 */ + return LIMB_DIGITS - 10; + case 32: /* 4294967296-8589934591 */ + return LIMB_DIGITS - 10; + case 33: /* 8589934592-17179869183 */ + if (a < 10000000000) + return LIMB_DIGITS - 10; + else + return LIMB_DIGITS - 11; + case 34: /* 17179869184-34359738367 */ + return LIMB_DIGITS - 11; + case 35: /* 34359738368-68719476735 */ + return LIMB_DIGITS - 11; + case 36: /* 68719476736-137438953471 */ + if (a < 100000000000) + return LIMB_DIGITS - 11; + else + return LIMB_DIGITS - 12; + case 37: /* 137438953472-274877906943 */ + return LIMB_DIGITS - 12; + case 38: /* 274877906944-549755813887 */ + return LIMB_DIGITS - 12; + case 39: /* 549755813888-1099511627775 */ + if (a < 1000000000000) + return LIMB_DIGITS - 12; + else + return LIMB_DIGITS - 13; + case 40: /* 1099511627776-2199023255551 */ + return LIMB_DIGITS - 13; + case 41: /* 2199023255552-4398046511103 */ + return LIMB_DIGITS - 13; + case 42: /* 4398046511104-8796093022207 */ + return LIMB_DIGITS - 13; + case 43: /* 8796093022208-17592186044415 */ + if (a < 10000000000000) + return LIMB_DIGITS - 13; + else + return LIMB_DIGITS - 14; + case 44: /* 17592186044416-35184372088831 */ + return LIMB_DIGITS - 14; + case 45: /* 35184372088832-70368744177663 */ + return LIMB_DIGITS - 14; + case 46: /* 70368744177664-140737488355327 */ + if (a < 100000000000000) + return LIMB_DIGITS - 14; + else + return LIMB_DIGITS - 15; + case 47: /* 140737488355328-281474976710655 */ + return LIMB_DIGITS - 15; + case 48: /* 281474976710656-562949953421311 */ + return LIMB_DIGITS - 15; + case 49: /* 562949953421312-1125899906842623 */ + if (a < 1000000000000000) + return LIMB_DIGITS - 15; + else + return LIMB_DIGITS - 16; + case 50: /* 1125899906842624-2251799813685247 */ + return LIMB_DIGITS - 16; + case 51: /* 2251799813685248-4503599627370495 */ + return LIMB_DIGITS - 16; + case 52: /* 4503599627370496-9007199254740991 */ + return LIMB_DIGITS - 16; + case 53: /* 9007199254740992-18014398509481983 */ + if (a < 10000000000000000) + return LIMB_DIGITS - 16; + else + return LIMB_DIGITS - 17; + case 54: /* 18014398509481984-36028797018963967 */ + return LIMB_DIGITS - 17; + case 55: /* 36028797018963968-72057594037927935 */ + return LIMB_DIGITS - 17; + case 56: /* 72057594037927936-144115188075855871 */ + if (a < 100000000000000000) + return LIMB_DIGITS - 17; + else + return LIMB_DIGITS - 18; + case 57: /* 144115188075855872-288230376151711743 */ + return LIMB_DIGITS - 18; + case 58: /* 288230376151711744-576460752303423487 */ + return LIMB_DIGITS - 18; + case 59: /* 576460752303423488-1152921504606846975 */ + if (a < 1000000000000000000) + return LIMB_DIGITS - 18; + else + return LIMB_DIGITS - 19; +#endif + default: + return 0; + } +} + +/* for debugging */ +void bfdec_print_str(const char *str, const bfdec_t *a) +{ + slimb_t i; + printf("%s=", str); + + if (a->expn == BF_EXP_NAN) { + printf("NaN"); + } else { + if (a->sign) + putchar('-'); + if (a->expn == BF_EXP_ZERO) { + putchar('0'); + } else if (a->expn == BF_EXP_INF) { + printf("Inf"); + } else { + printf("0."); + for(i = a->len - 1; i >= 0; i--) + printf("%0*" PRIu_LIMB, LIMB_DIGITS, a->tab[i]); + printf("e%" PRId_LIMB, a->expn); + } + } + printf("\n"); +} + +/* return != 0 if one digit between 0 and bit_pos inclusive is not zero. */ +static inline limb_t scan_digit_nz(const bfdec_t *r, slimb_t bit_pos) +{ + slimb_t pos; + limb_t v, q; + int shift; + + if (bit_pos < 0) + return 0; + pos = (limb_t)bit_pos / LIMB_DIGITS; + shift = (limb_t)bit_pos % LIMB_DIGITS; + fast_shr_rem_dec(q, v, r->tab[pos], shift + 1); + (void)q; + if (v != 0) + return 1; + pos--; + while (pos >= 0) { + if (r->tab[pos] != 0) + return 1; + pos--; + } + return 0; +} + +static limb_t get_digit(const limb_t *tab, limb_t len, slimb_t pos) +{ + slimb_t i; + int shift; + i = floor_div(pos, LIMB_DIGITS); + if (i < 0 || i >= len) + return 0; + shift = pos - i * LIMB_DIGITS; + return fast_shr_dec(tab[i], shift) % 10; +} + +/* return the addend for rounding. Note that prec can be <= 0 for bf_rint() */ +static int bfdec_get_rnd_add(int *pret, const bfdec_t *r, limb_t l, + slimb_t prec, int rnd_mode) +{ + int add_one, inexact; + limb_t digit1, digit0; + + // bfdec_print_str("get_rnd_add", r); + if (rnd_mode == BF_RNDF) { + digit0 = 1; /* faithful rounding does not honor the INEXACT flag */ + } else { + /* starting limb for bit 'prec + 1' */ + digit0 = scan_digit_nz(r, l * LIMB_DIGITS - 1 - bf_max(0, prec + 1)); + } + + /* get the digit at 'prec' */ + digit1 = get_digit(r->tab, l, l * LIMB_DIGITS - 1 - prec); + inexact = (digit1 | digit0) != 0; + + add_one = 0; + switch(rnd_mode) { + case BF_RNDZ: + break; + case BF_RNDN: + if (digit1 == 5) { + if (digit0) { + add_one = 1; + } else { + /* round to even */ + add_one = + get_digit(r->tab, l, l * LIMB_DIGITS - 1 - (prec - 1)) & 1; + } + } else if (digit1 > 5) { + add_one = 1; + } + break; + case BF_RNDD: + case BF_RNDU: + if (r->sign == (rnd_mode == BF_RNDD)) + add_one = inexact; + break; + case BF_RNDNA: + case BF_RNDF: + add_one = (digit1 >= 5); + break; + case BF_RNDA: + add_one = inexact; + break; + default: + abort(); + } + + if (inexact) + *pret |= BF_ST_INEXACT; + return add_one; +} + +/* round to prec1 bits assuming 'r' is non zero and finite. 'r' is + assumed to have length 'l' (1 <= l <= r->len). prec1 can be + BF_PREC_INF. BF_FLAG_SUBNORMAL is not supported. Cannot fail with + BF_ST_MEM_ERROR. + */ +static int __bfdec_round(bfdec_t *r, limb_t prec1, bf_flags_t flags, limb_t l) +{ + int shift, add_one, rnd_mode, ret; + slimb_t i, bit_pos, pos, e_min, e_max, e_range, prec; + + /* XXX: align to IEEE 754 2008 for decimal numbers ? */ + e_range = (limb_t)1 << (bf_get_exp_bits(flags) - 1); + e_min = -e_range + 3; + e_max = e_range; + + if (flags & BF_FLAG_RADPNT_PREC) { + /* 'prec' is the precision after the decimal point */ + if (prec1 != BF_PREC_INF) + prec = r->expn + prec1; + else + prec = prec1; + } else if (unlikely(r->expn < e_min) && (flags & BF_FLAG_SUBNORMAL)) { + /* restrict the precision in case of potentially subnormal + result */ + assert(prec1 != BF_PREC_INF); + prec = prec1 - (e_min - r->expn); + } else { + prec = prec1; + } + + /* round to prec bits */ + rnd_mode = flags & BF_RND_MASK; + ret = 0; + add_one = bfdec_get_rnd_add(&ret, r, l, prec, rnd_mode); + + if (prec <= 0) { + if (add_one) { + bfdec_resize(r, 1); /* cannot fail because r is non zero */ + r->tab[0] = BF_DEC_BASE / 10; + r->expn += 1 - prec; + ret |= BF_ST_UNDERFLOW | BF_ST_INEXACT; + return ret; + } else { + goto underflow; + } + } else if (add_one) { + limb_t carry; + + /* add one starting at digit 'prec - 1' */ + bit_pos = l * LIMB_DIGITS - 1 - (prec - 1); + pos = bit_pos / LIMB_DIGITS; + carry = mp_pow_dec[bit_pos % LIMB_DIGITS]; + carry = mp_add_ui_dec(r->tab + pos, carry, l - pos); + if (carry) { + /* shift right by one digit */ + mp_shr_dec(r->tab + pos, r->tab + pos, l - pos, 1, 1); + r->expn++; + } + } + + /* check underflow */ + if (unlikely(r->expn < e_min)) { + if (flags & BF_FLAG_SUBNORMAL) { + /* if inexact, also set the underflow flag */ + if (ret & BF_ST_INEXACT) + ret |= BF_ST_UNDERFLOW; + } else { + underflow: + bfdec_set_zero(r, r->sign); + ret |= BF_ST_UNDERFLOW | BF_ST_INEXACT; + return ret; + } + } + + /* check overflow */ + if (unlikely(r->expn > e_max)) { + bfdec_set_inf(r, r->sign); + ret |= BF_ST_OVERFLOW | BF_ST_INEXACT; + return ret; + } + + /* keep the bits starting at 'prec - 1' */ + bit_pos = l * LIMB_DIGITS - 1 - (prec - 1); + i = floor_div(bit_pos, LIMB_DIGITS); + if (i >= 0) { + shift = smod(bit_pos, LIMB_DIGITS); + if (shift != 0) { + r->tab[i] = fast_shr_dec(r->tab[i], shift) * + mp_pow_dec[shift]; + } + } else { + i = 0; + } + /* remove trailing zeros */ + while (r->tab[i] == 0) + i++; + if (i > 0) { + l -= i; + memmove(r->tab, r->tab + i, l * sizeof(limb_t)); + } + bfdec_resize(r, l); /* cannot fail */ + return ret; +} + +/* Cannot fail with BF_ST_MEM_ERROR. */ +int bfdec_round(bfdec_t *r, limb_t prec, bf_flags_t flags) +{ + if (r->len == 0) + return 0; + return __bfdec_round(r, prec, flags, r->len); +} + +/* 'r' must be a finite number. Cannot fail with BF_ST_MEM_ERROR. */ +int bfdec_normalize_and_round(bfdec_t *r, limb_t prec1, bf_flags_t flags) +{ + limb_t l, v; + int shift, ret; + + // bfdec_print_str("bf_renorm", r); + l = r->len; + while (l > 0 && r->tab[l - 1] == 0) + l--; + if (l == 0) { + /* zero */ + r->expn = BF_EXP_ZERO; + bfdec_resize(r, 0); /* cannot fail */ + ret = 0; + } else { + r->expn -= (r->len - l) * LIMB_DIGITS; + /* shift to have the MSB set to '1' */ + v = r->tab[l - 1]; + shift = clz_dec(v); + if (shift != 0) { + mp_shl_dec(r->tab, r->tab, l, shift, 0); + r->expn -= shift; + } + ret = __bfdec_round(r, prec1, flags, l); + } + // bf_print_str("r_final", r); + return ret; +} + +int bfdec_set_ui(bfdec_t *r, uint64_t v) +{ +#if LIMB_BITS == 32 + if (v >= BF_DEC_BASE * BF_DEC_BASE) { + if (bfdec_resize(r, 3)) + goto fail; + r->tab[0] = v % BF_DEC_BASE; + v /= BF_DEC_BASE; + r->tab[1] = v % BF_DEC_BASE; + r->tab[2] = v / BF_DEC_BASE; + r->expn = 3 * LIMB_DIGITS; + } else +#endif + if (v >= BF_DEC_BASE) { + if (bfdec_resize(r, 2)) + goto fail; + r->tab[0] = v % BF_DEC_BASE; + r->tab[1] = v / BF_DEC_BASE; + r->expn = 2 * LIMB_DIGITS; + } else { + if (bfdec_resize(r, 1)) + goto fail; + r->tab[0] = v; + r->expn = LIMB_DIGITS; + } + r->sign = 0; + return bfdec_normalize_and_round(r, BF_PREC_INF, 0); + fail: + bfdec_set_nan(r); + return BF_ST_MEM_ERROR; +} + +int bfdec_set_si(bfdec_t *r, int64_t v) +{ + int ret; + if (v < 0) { + ret = bfdec_set_ui(r, -v); + r->sign = 1; + } else { + ret = bfdec_set_ui(r, v); + } + return ret; +} + +static int bfdec_add_internal(bfdec_t *r, const bfdec_t *a, const bfdec_t *b, limb_t prec, bf_flags_t flags, int b_neg) +{ + bf_context_t *s = r->ctx; + int is_sub, cmp_res, a_sign, b_sign, ret; + + a_sign = a->sign; + b_sign = b->sign ^ b_neg; + is_sub = a_sign ^ b_sign; + cmp_res = bfdec_cmpu(a, b); + if (cmp_res < 0) { + const bfdec_t *tmp; + tmp = a; + a = b; + b = tmp; + a_sign = b_sign; /* b_sign is never used later */ + } + /* abs(a) >= abs(b) */ + if (cmp_res == 0 && is_sub && a->expn < BF_EXP_INF) { + /* zero result */ + bfdec_set_zero(r, (flags & BF_RND_MASK) == BF_RNDD); + ret = 0; + } else if (a->len == 0 || b->len == 0) { + ret = 0; + if (a->expn >= BF_EXP_INF) { + if (a->expn == BF_EXP_NAN) { + /* at least one operand is NaN */ + bfdec_set_nan(r); + ret = 0; + } else if (b->expn == BF_EXP_INF && is_sub) { + /* infinities with different signs */ + bfdec_set_nan(r); + ret = BF_ST_INVALID_OP; + } else { + bfdec_set_inf(r, a_sign); + } + } else { + /* at least one zero and not subtract */ + if (bfdec_set(r, a)) + return BF_ST_MEM_ERROR; + r->sign = a_sign; + goto renorm; + } + } else { + slimb_t d, a_offset, b_offset, i, r_len; + limb_t carry; + limb_t *b1_tab; + int b_shift; + mp_size_t b1_len; + + d = a->expn - b->expn; + + /* XXX: not efficient in time and memory if the precision is + not infinite */ + r_len = bf_max(a->len, b->len + (d + LIMB_DIGITS - 1) / LIMB_DIGITS); + if (bfdec_resize(r, r_len)) + goto fail; + r->sign = a_sign; + r->expn = a->expn; + + a_offset = r_len - a->len; + for(i = 0; i < a_offset; i++) + r->tab[i] = 0; + for(i = 0; i < a->len; i++) + r->tab[a_offset + i] = a->tab[i]; + + b_shift = d % LIMB_DIGITS; + if (b_shift == 0) { + b1_len = b->len; + b1_tab = (limb_t *)b->tab; + } else { + b1_len = b->len + 1; + b1_tab = bf_malloc(s, sizeof(limb_t) * b1_len); + if (!b1_tab) + goto fail; + b1_tab[0] = mp_shr_dec(b1_tab + 1, b->tab, b->len, b_shift, 0) * + mp_pow_dec[LIMB_DIGITS - b_shift]; + } + b_offset = r_len - (b->len + (d + LIMB_DIGITS - 1) / LIMB_DIGITS); + + if (is_sub) { + carry = mp_sub_dec(r->tab + b_offset, r->tab + b_offset, + b1_tab, b1_len, 0); + if (carry != 0) { + carry = mp_sub_ui_dec(r->tab + b_offset + b1_len, carry, + r_len - (b_offset + b1_len)); + assert(carry == 0); + } + } else { + carry = mp_add_dec(r->tab + b_offset, r->tab + b_offset, + b1_tab, b1_len, 0); + if (carry != 0) { + carry = mp_add_ui_dec(r->tab + b_offset + b1_len, carry, + r_len - (b_offset + b1_len)); + } + if (carry != 0) { + if (bfdec_resize(r, r_len + 1)) { + if (b_shift != 0) + bf_free(s, b1_tab); + goto fail; + } + r->tab[r_len] = 1; + r->expn += LIMB_DIGITS; + } + } + if (b_shift != 0) + bf_free(s, b1_tab); + renorm: + ret = bfdec_normalize_and_round(r, prec, flags); + } + return ret; + fail: + bfdec_set_nan(r); + return BF_ST_MEM_ERROR; +} + +static int __bfdec_add(bfdec_t *r, const bfdec_t *a, const bfdec_t *b, limb_t prec, + bf_flags_t flags) +{ + return bfdec_add_internal(r, a, b, prec, flags, 0); +} + +static int __bfdec_sub(bfdec_t *r, const bfdec_t *a, const bfdec_t *b, limb_t prec, + bf_flags_t flags) +{ + return bfdec_add_internal(r, a, b, prec, flags, 1); +} + +int bfdec_add(bfdec_t *r, const bfdec_t *a, const bfdec_t *b, limb_t prec, + bf_flags_t flags) +{ + return bf_op2((bf_t *)r, (bf_t *)a, (bf_t *)b, prec, flags, + (bf_op2_func_t *)__bfdec_add); +} + +int bfdec_sub(bfdec_t *r, const bfdec_t *a, const bfdec_t *b, limb_t prec, + bf_flags_t flags) +{ + return bf_op2((bf_t *)r, (bf_t *)a, (bf_t *)b, prec, flags, + (bf_op2_func_t *)__bfdec_sub); +} + +int bfdec_mul(bfdec_t *r, const bfdec_t *a, const bfdec_t *b, limb_t prec, + bf_flags_t flags) +{ + int ret, r_sign; + + if (a->len < b->len) { + const bfdec_t *tmp = a; + a = b; + b = tmp; + } + r_sign = a->sign ^ b->sign; + /* here b->len <= a->len */ + if (b->len == 0) { + if (a->expn == BF_EXP_NAN || b->expn == BF_EXP_NAN) { + bfdec_set_nan(r); + ret = 0; + } else if (a->expn == BF_EXP_INF || b->expn == BF_EXP_INF) { + if ((a->expn == BF_EXP_INF && b->expn == BF_EXP_ZERO) || + (a->expn == BF_EXP_ZERO && b->expn == BF_EXP_INF)) { + bfdec_set_nan(r); + ret = BF_ST_INVALID_OP; + } else { + bfdec_set_inf(r, r_sign); + ret = 0; + } + } else { + bfdec_set_zero(r, r_sign); + ret = 0; + } + } else { + bfdec_t tmp, *r1 = NULL; + limb_t a_len, b_len; + limb_t *a_tab, *b_tab; + + a_len = a->len; + b_len = b->len; + a_tab = a->tab; + b_tab = b->tab; + + if (r == a || r == b) { + bfdec_init(r->ctx, &tmp); + r1 = r; + r = &tmp; + } + if (bfdec_resize(r, a_len + b_len)) { + bfdec_set_nan(r); + ret = BF_ST_MEM_ERROR; + goto done; + } + mp_mul_basecase_dec(r->tab, a_tab, a_len, b_tab, b_len); + r->sign = r_sign; + r->expn = a->expn + b->expn; + ret = bfdec_normalize_and_round(r, prec, flags); + done: + if (r == &tmp) + bfdec_move(r1, &tmp); + } + return ret; +} + +int bfdec_mul_si(bfdec_t *r, const bfdec_t *a, int64_t b1, limb_t prec, + bf_flags_t flags) +{ + bfdec_t b; + int ret; + bfdec_init(r->ctx, &b); + ret = bfdec_set_si(&b, b1); + ret |= bfdec_mul(r, a, &b, prec, flags); + bfdec_delete(&b); + return ret; +} + +int bfdec_add_si(bfdec_t *r, const bfdec_t *a, int64_t b1, limb_t prec, + bf_flags_t flags) +{ + bfdec_t b; + int ret; + + bfdec_init(r->ctx, &b); + ret = bfdec_set_si(&b, b1); + ret |= bfdec_add(r, a, &b, prec, flags); + bfdec_delete(&b); + return ret; +} + +static int __bfdec_div(bfdec_t *r, const bfdec_t *a, const bfdec_t *b, + limb_t prec, bf_flags_t flags) +{ + int ret, r_sign; + limb_t n, nb, precl; + + r_sign = a->sign ^ b->sign; + if (a->expn >= BF_EXP_INF || b->expn >= BF_EXP_INF) { + if (a->expn == BF_EXP_NAN || b->expn == BF_EXP_NAN) { + bfdec_set_nan(r); + return 0; + } else if (a->expn == BF_EXP_INF && b->expn == BF_EXP_INF) { + bfdec_set_nan(r); + return BF_ST_INVALID_OP; + } else if (a->expn == BF_EXP_INF) { + bfdec_set_inf(r, r_sign); + return 0; + } else { + bfdec_set_zero(r, r_sign); + return 0; + } + } else if (a->expn == BF_EXP_ZERO) { + if (b->expn == BF_EXP_ZERO) { + bfdec_set_nan(r); + return BF_ST_INVALID_OP; + } else { + bfdec_set_zero(r, r_sign); + return 0; + } + } else if (b->expn == BF_EXP_ZERO) { + bfdec_set_inf(r, r_sign); + return BF_ST_DIVIDE_ZERO; + } + + nb = b->len; + if (prec == BF_PREC_INF) { + /* infinite precision: return BF_ST_INVALID_OP if not an exact + result */ + /* XXX: check */ + precl = nb + 1; + } else if (flags & BF_FLAG_RADPNT_PREC) { + /* number of digits after the decimal point */ + /* XXX: check (2 extra digits for rounding + 2 digits) */ + precl = (bf_max(a->expn - b->expn, 0) + 2 + + prec + 2 + LIMB_DIGITS - 1) / LIMB_DIGITS; + } else { + /* number of limbs of the quotient (2 extra digits for rounding) */ + precl = (prec + 2 + LIMB_DIGITS - 1) / LIMB_DIGITS; + } + n = bf_max(a->len, precl); + + { + limb_t *taba, na, i; + slimb_t d; + + na = n + nb; + taba = bf_malloc(r->ctx, (na + 1) * sizeof(limb_t)); + if (!taba) + goto fail; + d = na - a->len; + memset(taba, 0, d * sizeof(limb_t)); + memcpy(taba + d, a->tab, a->len * sizeof(limb_t)); + if (bfdec_resize(r, n + 1)) + goto fail1; + if (mp_div_dec(r->ctx, r->tab, taba, na, b->tab, nb)) { + fail1: + bf_free(r->ctx, taba); + goto fail; + } + /* see if non zero remainder */ + for(i = 0; i < nb; i++) { + if (taba[i] != 0) + break; + } + bf_free(r->ctx, taba); + if (i != nb) { + if (prec == BF_PREC_INF) { + bfdec_set_nan(r); + return BF_ST_INVALID_OP; + } else { + r->tab[0] |= 1; + } + } + r->expn = a->expn - b->expn + LIMB_DIGITS; + r->sign = r_sign; + ret = bfdec_normalize_and_round(r, prec, flags); + } + return ret; + fail: + bfdec_set_nan(r); + return BF_ST_MEM_ERROR; +} + +int bfdec_div(bfdec_t *r, const bfdec_t *a, const bfdec_t *b, limb_t prec, + bf_flags_t flags) +{ + return bf_op2((bf_t *)r, (bf_t *)a, (bf_t *)b, prec, flags, + (bf_op2_func_t *)__bfdec_div); +} + +/* a and b must be finite numbers with a >= 0 and b > 0. 'q' is the + integer defined as floor(a/b) and r = a - q * b. */ +static void bfdec_tdivremu(bf_context_t *s, bfdec_t *q, bfdec_t *r, + const bfdec_t *a, const bfdec_t *b) +{ + if (bfdec_cmpu(a, b) < 0) { + bfdec_set_ui(q, 0); + bfdec_set(r, a); + } else { + bfdec_div(q, a, b, 0, BF_RNDZ | BF_FLAG_RADPNT_PREC); + bfdec_mul(r, q, b, BF_PREC_INF, BF_RNDZ); + bfdec_sub(r, a, r, BF_PREC_INF, BF_RNDZ); + } +} + +/* division and remainder. + + rnd_mode is the rounding mode for the quotient. The additional + rounding mode BF_RND_EUCLIDIAN is supported. + + 'q' is an integer. 'r' is rounded with prec and flags (prec can be + BF_PREC_INF). +*/ +int bfdec_divrem(bfdec_t *q, bfdec_t *r, const bfdec_t *a, const bfdec_t *b, + limb_t prec, bf_flags_t flags, int rnd_mode) +{ + bf_context_t *s = q->ctx; + bfdec_t a1_s, *a1 = &a1_s; + bfdec_t b1_s, *b1 = &b1_s; + bfdec_t r1_s, *r1 = &r1_s; + int q_sign, res; + BOOL is_ceil, is_rndn; + + assert(q != a && q != b); + assert(r != a && r != b); + assert(q != r); + + if (a->len == 0 || b->len == 0) { + bfdec_set_zero(q, 0); + if (a->expn == BF_EXP_NAN || b->expn == BF_EXP_NAN) { + bfdec_set_nan(r); + return 0; + } else if (a->expn == BF_EXP_INF || b->expn == BF_EXP_ZERO) { + bfdec_set_nan(r); + return BF_ST_INVALID_OP; + } else { + bfdec_set(r, a); + return bfdec_round(r, prec, flags); + } + } + + q_sign = a->sign ^ b->sign; + is_rndn = (rnd_mode == BF_RNDN || rnd_mode == BF_RNDNA); + switch(rnd_mode) { + default: + case BF_RNDZ: + case BF_RNDN: + case BF_RNDNA: + is_ceil = FALSE; + break; + case BF_RNDD: + is_ceil = q_sign; + break; + case BF_RNDU: + is_ceil = q_sign ^ 1; + break; + case BF_RNDA: + is_ceil = TRUE; + break; + case BF_DIVREM_EUCLIDIAN: + is_ceil = a->sign; + break; + } + + a1->expn = a->expn; + a1->tab = a->tab; + a1->len = a->len; + a1->sign = 0; + + b1->expn = b->expn; + b1->tab = b->tab; + b1->len = b->len; + b1->sign = 0; + + // bfdec_print_str("a1", a1); + // bfdec_print_str("b1", b1); + /* XXX: could improve to avoid having a large 'q' */ + bfdec_tdivremu(s, q, r, a1, b1); + if (bfdec_is_nan(q) || bfdec_is_nan(r)) + goto fail; + // bfdec_print_str("q", q); + // bfdec_print_str("r", r); + + if (r->len != 0) { + if (is_rndn) { + bfdec_init(s, r1); + if (bfdec_set(r1, r)) + goto fail; + if (bfdec_mul_si(r1, r1, 2, BF_PREC_INF, BF_RNDZ)) { + bfdec_delete(r1); + goto fail; + } + res = bfdec_cmpu(r1, b); + bfdec_delete(r1); + if (res > 0 || + (res == 0 && + (rnd_mode == BF_RNDNA || + (get_digit(q->tab, q->len, q->len * LIMB_DIGITS - q->expn) & 1) != 0))) { + goto do_sub_r; + } + } else if (is_ceil) { + do_sub_r: + res = bfdec_add_si(q, q, 1, BF_PREC_INF, BF_RNDZ); + res |= bfdec_sub(r, r, b1, BF_PREC_INF, BF_RNDZ); + if (res & BF_ST_MEM_ERROR) + goto fail; + } + } + + r->sign ^= a->sign; + q->sign = q_sign; + return bfdec_round(r, prec, flags); + fail: + bfdec_set_nan(q); + bfdec_set_nan(r); + return BF_ST_MEM_ERROR; +} + +int bfdec_rem(bfdec_t *r, const bfdec_t *a, const bfdec_t *b, limb_t prec, + bf_flags_t flags, int rnd_mode) +{ + bfdec_t q_s, *q = &q_s; + int ret; + + bfdec_init(r->ctx, q); + ret = bfdec_divrem(q, r, a, b, prec, flags, rnd_mode); + bfdec_delete(q); + return ret; +} + +/* convert to integer (infinite precision) */ +int bfdec_rint(bfdec_t *r, int rnd_mode) +{ + return bfdec_round(r, 0, rnd_mode | BF_FLAG_RADPNT_PREC); +} + +int bfdec_sqrt(bfdec_t *r, const bfdec_t *a, limb_t prec, bf_flags_t flags) +{ + bf_context_t *s = a->ctx; + int ret, k; + limb_t *a1, v; + slimb_t n, n1, prec1; + limb_t res; + + assert(r != a); + + if (a->len == 0) { + if (a->expn == BF_EXP_NAN) { + bfdec_set_nan(r); + } else if (a->expn == BF_EXP_INF && a->sign) { + goto invalid_op; + } else { + bfdec_set(r, a); + } + ret = 0; + } else if (a->sign || prec == BF_PREC_INF) { + invalid_op: + bfdec_set_nan(r); + ret = BF_ST_INVALID_OP; + } else { + if (flags & BF_FLAG_RADPNT_PREC) { + prec1 = bf_max(floor_div(a->expn + 1, 2) + prec, 1); + } else { + prec1 = prec; + } + /* convert the mantissa to an integer with at least 2 * + prec + 4 digits */ + n = (2 * (prec1 + 2) + 2 * LIMB_DIGITS - 1) / (2 * LIMB_DIGITS); + if (bfdec_resize(r, n)) + goto fail; + a1 = bf_malloc(s, sizeof(limb_t) * 2 * n); + if (!a1) + goto fail; + n1 = bf_min(2 * n, a->len); + memset(a1, 0, (2 * n - n1) * sizeof(limb_t)); + memcpy(a1 + 2 * n - n1, a->tab + a->len - n1, n1 * sizeof(limb_t)); + if (a->expn & 1) { + res = mp_shr_dec(a1, a1, 2 * n, 1, 0); + } else { + res = 0; + } + /* normalize so that a1 >= B^(2*n)/4. Not need for n = 1 + because mp_sqrtrem2_dec already does it */ + k = 0; + if (n > 1) { + v = a1[2 * n - 1]; + while (v < BF_DEC_BASE / 4) { + k++; + v *= 4; + } + if (k != 0) + mp_mul1_dec(a1, a1, 2 * n, 1 << (2 * k), 0); + } + if (mp_sqrtrem_dec(s, r->tab, a1, n)) { + bf_free(s, a1); + goto fail; + } + if (k != 0) + mp_div1_dec(r->tab, r->tab, n, 1 << k, 0); + if (!res) { + res = mp_scan_nz(a1, n + 1); + } + bf_free(s, a1); + if (!res) { + res = mp_scan_nz(a->tab, a->len - n1); + } + if (res != 0) + r->tab[0] |= 1; + r->sign = 0; + r->expn = (a->expn + 1) >> 1; + ret = bfdec_round(r, prec, flags); + } + return ret; + fail: + bfdec_set_nan(r); + return BF_ST_MEM_ERROR; +} + +/* The rounding mode is always BF_RNDZ. Return BF_ST_OVERFLOW if there + is an overflow and 0 otherwise. No memory error is possible. */ +int bfdec_get_int32(int *pres, const bfdec_t *a) +{ + uint32_t v; + int ret; + if (a->expn >= BF_EXP_INF) { + ret = 0; + if (a->expn == BF_EXP_INF) { + v = (uint32_t)INT32_MAX + a->sign; + /* XXX: return overflow ? */ + } else { + v = INT32_MAX; + } + } else if (a->expn <= 0) { + v = 0; + ret = 0; + } else if (a->expn <= 9) { + v = fast_shr_dec(a->tab[a->len - 1], LIMB_DIGITS - a->expn); + if (a->sign) + v = -v; + ret = 0; + } else if (a->expn == 10) { + uint64_t v1; + uint32_t v_max; +#if LIMB_BITS == 64 + v1 = fast_shr_dec(a->tab[a->len - 1], LIMB_DIGITS - a->expn); +#else + v1 = (uint64_t)a->tab[a->len - 1] * 10 + + get_digit(a->tab, a->len, (a->len - 1) * LIMB_DIGITS - 1); +#endif + v_max = (uint32_t)INT32_MAX + a->sign; + if (v1 > v_max) { + v = v_max; + ret = BF_ST_OVERFLOW; + } else { + v = v1; + if (a->sign) + v = -v; + ret = 0; + } + } else { + v = (uint32_t)INT32_MAX + a->sign; + ret = BF_ST_OVERFLOW; + } + *pres = v; + return ret; +} + +/* power to an integer with infinite precision */ +int bfdec_pow_ui(bfdec_t *r, const bfdec_t *a, limb_t b) +{ + int ret, n_bits, i; + + assert(r != a); + if (b == 0) + return bfdec_set_ui(r, 1); + ret = bfdec_set(r, a); + n_bits = LIMB_BITS - clz(b); + for(i = n_bits - 2; i >= 0; i--) { + ret |= bfdec_mul(r, r, r, BF_PREC_INF, BF_RNDZ); + if ((b >> i) & 1) + ret |= bfdec_mul(r, r, a, BF_PREC_INF, BF_RNDZ); + } + return ret; +} + +char *bfdec_ftoa(size_t *plen, const bfdec_t *a, limb_t prec, bf_flags_t flags) +{ + return bf_ftoa_internal(plen, (const bf_t *)a, 10, prec, flags, TRUE); +} + +int bfdec_atof(bfdec_t *r, const char *str, const char **pnext, + limb_t prec, bf_flags_t flags) +{ + slimb_t dummy_exp; + return bf_atof_internal((bf_t *)r, &dummy_exp, str, pnext, 10, prec, + flags, TRUE); +} + +#endif /* USE_BF_DEC */ + +#ifdef USE_FFT_MUL +/***************************************************************/ +/* Integer multiplication with FFT */ + +/* or LIMB_BITS at bit position 'pos' in tab */ +static inline void put_bits(limb_t *tab, limb_t len, slimb_t pos, limb_t val) +{ + limb_t i; + int p; + + i = pos >> LIMB_LOG2_BITS; + p = pos & (LIMB_BITS - 1); + if (i < len) + tab[i] |= val << p; + if (p != 0) { + i++; + if (i < len) { + tab[i] |= val >> (LIMB_BITS - p); + } + } +} + +#if defined(__AVX2__) + +typedef double NTTLimb; + +/* we must have: modulo >= 1 << NTT_MOD_LOG2_MIN */ +#define NTT_MOD_LOG2_MIN 50 +#define NTT_MOD_LOG2_MAX 51 +#define NB_MODS 5 +#define NTT_PROOT_2EXP 39 +static const int ntt_int_bits[NB_MODS] = { 254, 203, 152, 101, 50, }; + +static const limb_t ntt_mods[NB_MODS] = { 0x00073a8000000001, 0x0007858000000001, 0x0007a38000000001, 0x0007a68000000001, 0x0007fd8000000001, +}; + +static const limb_t ntt_proot[2][NB_MODS] = { + { 0x00056198d44332c8, 0x0002eb5d640aad39, 0x00047e31eaa35fd0, 0x0005271ac118a150, 0x00075e0ce8442bd5, }, + { 0x000461169761bcc5, 0x0002dac3cb2da688, 0x0004abc97751e3bf, 0x000656778fc8c485, 0x0000dc6469c269fa, }, +}; + +static const limb_t ntt_mods_cr[NB_MODS * (NB_MODS - 1) / 2] = { + 0x00020e4da740da8e, 0x0004c3dc09c09c1d, 0x000063bd097b4271, 0x000799d8f18f18fd, + 0x0005384222222264, 0x000572b07c1f07fe, 0x00035cd08888889a, + 0x00066015555557e3, 0x000725960b60b623, + 0x0002fc1fa1d6ce12, +}; + +#else + +typedef limb_t NTTLimb; + +#if LIMB_BITS == 64 + +#define NTT_MOD_LOG2_MIN 61 +#define NTT_MOD_LOG2_MAX 62 +#define NB_MODS 5 +#define NTT_PROOT_2EXP 51 +static const int ntt_int_bits[NB_MODS] = { 307, 246, 185, 123, 61, }; + +static const limb_t ntt_mods[NB_MODS] = { 0x28d8000000000001, 0x2a88000000000001, 0x2ed8000000000001, 0x3508000000000001, 0x3aa8000000000001, +}; + +static const limb_t ntt_proot[2][NB_MODS] = { + { 0x1b8ea61034a2bea7, 0x21a9762de58206fb, 0x02ca782f0756a8ea, 0x278384537a3e50a1, 0x106e13fee74ce0ab, }, + { 0x233513af133e13b8, 0x1d13140d1c6f75f1, 0x12cde57f97e3eeda, 0x0d6149e23cbe654f, 0x36cd204f522a1379, }, +}; + +static const limb_t ntt_mods_cr[NB_MODS * (NB_MODS - 1) / 2] = { + 0x08a9ed097b425eea, 0x18a44aaaaaaaaab3, 0x2493f57f57f57f5d, 0x126b8d0649a7f8d4, + 0x09d80ed7303b5ccc, 0x25b8bcf3cf3cf3d5, 0x2ce6ce63398ce638, + 0x0e31fad40a57eb59, 0x02a3529fd4a7f52f, + 0x3a5493e93e93e94a, +}; + +#elif LIMB_BITS == 32 + +/* we must have: modulo >= 1 << NTT_MOD_LOG2_MIN */ +#define NTT_MOD_LOG2_MIN 29 +#define NTT_MOD_LOG2_MAX 30 +#define NB_MODS 5 +#define NTT_PROOT_2EXP 20 +static const int ntt_int_bits[NB_MODS] = { 148, 119, 89, 59, 29, }; + +static const limb_t ntt_mods[NB_MODS] = { 0x0000000032b00001, 0x0000000033700001, 0x0000000036d00001, 0x0000000037300001, 0x000000003e500001, +}; + +static const limb_t ntt_proot[2][NB_MODS] = { + { 0x0000000032525f31, 0x0000000005eb3b37, 0x00000000246eda9f, 0x0000000035f25901, 0x00000000022f5768, }, + { 0x00000000051eba1a, 0x00000000107be10e, 0x000000001cd574e0, 0x00000000053806e6, 0x000000002cd6bf98, }, +}; + +static const limb_t ntt_mods_cr[NB_MODS * (NB_MODS - 1) / 2] = { + 0x000000000449559a, 0x000000001eba6ca9, 0x000000002ec18e46, 0x000000000860160b, + 0x000000000d321307, 0x000000000bf51120, 0x000000000f662938, + 0x000000000932ab3e, 0x000000002f40eef8, + 0x000000002e760905, +}; + +#endif /* LIMB_BITS */ + +#endif /* !AVX2 */ + +#if defined(__AVX2__) +#define NTT_TRIG_K_MAX 18 +#else +#define NTT_TRIG_K_MAX 19 +#endif + +typedef struct BFNTTState { + bf_context_t *ctx; + + /* used for mul_mod_fast() */ + limb_t ntt_mods_div[NB_MODS]; + + limb_t ntt_proot_pow[NB_MODS][2][NTT_PROOT_2EXP + 1]; + limb_t ntt_proot_pow_inv[NB_MODS][2][NTT_PROOT_2EXP + 1]; + NTTLimb *ntt_trig[NB_MODS][2][NTT_TRIG_K_MAX + 1]; + /* 1/2^n mod m */ + limb_t ntt_len_inv[NB_MODS][NTT_PROOT_2EXP + 1][2]; +#if defined(__AVX2__) + __m256d ntt_mods_cr_vec[NB_MODS * (NB_MODS - 1) / 2]; + __m256d ntt_mods_vec[NB_MODS]; + __m256d ntt_mods_inv_vec[NB_MODS]; +#else + limb_t ntt_mods_cr_inv[NB_MODS * (NB_MODS - 1) / 2]; +#endif +} BFNTTState; + +static NTTLimb *get_trig(BFNTTState *s, int k, int inverse, int m_idx); + +/* add modulo with up to (LIMB_BITS-1) bit modulo */ +static inline limb_t add_mod(limb_t a, limb_t b, limb_t m) +{ + limb_t r; + r = a + b; + if (r >= m) + r -= m; + return r; +} + +/* sub modulo with up to LIMB_BITS bit modulo */ +static inline limb_t sub_mod(limb_t a, limb_t b, limb_t m) +{ + limb_t r; + r = a - b; + if (r > a) + r += m; + return r; +} + +/* return (r0+r1*B) mod m + precondition: 0 <= r0+r1*B < 2^(64+NTT_MOD_LOG2_MIN) +*/ +static inline limb_t mod_fast(dlimb_t r, + limb_t m, limb_t m_inv) +{ + limb_t a1, q, t0, r1, r0; + + a1 = r >> NTT_MOD_LOG2_MIN; + + q = ((dlimb_t)a1 * m_inv) >> LIMB_BITS; + r = r - (dlimb_t)q * m - m * 2; + r1 = r >> LIMB_BITS; + t0 = (slimb_t)r1 >> 1; + r += m & t0; + r0 = r; + r1 = r >> LIMB_BITS; + r0 += m & r1; + return r0; +} + +/* faster version using precomputed modulo inverse. + precondition: 0 <= a * b < 2^(64+NTT_MOD_LOG2_MIN) */ +static inline limb_t mul_mod_fast(limb_t a, limb_t b, + limb_t m, limb_t m_inv) +{ + dlimb_t r; + r = (dlimb_t)a * (dlimb_t)b; + return mod_fast(r, m, m_inv); +} + +static inline limb_t init_mul_mod_fast(limb_t m) +{ + dlimb_t t; + assert(m < (limb_t)1 << NTT_MOD_LOG2_MAX); + assert(m >= (limb_t)1 << NTT_MOD_LOG2_MIN); + t = (dlimb_t)1 << (LIMB_BITS + NTT_MOD_LOG2_MIN); + return t / m; +} + +/* Faster version used when the multiplier is constant. 0 <= a < 2^64, + 0 <= b < m. */ +static inline limb_t mul_mod_fast2(limb_t a, limb_t b, + limb_t m, limb_t b_inv) +{ + limb_t r, q; + + q = ((dlimb_t)a * (dlimb_t)b_inv) >> LIMB_BITS; + r = a * b - q * m; + if (r >= m) + r -= m; + return r; +} + +/* Faster version used when the multiplier is constant. 0 <= a < 2^64, + 0 <= b < m. Let r = a * b mod m. The return value is 'r' or 'r + + m'. */ +static inline limb_t mul_mod_fast3(limb_t a, limb_t b, + limb_t m, limb_t b_inv) +{ + limb_t r, q; + + q = ((dlimb_t)a * (dlimb_t)b_inv) >> LIMB_BITS; + r = a * b - q * m; + return r; +} + +static inline limb_t init_mul_mod_fast2(limb_t b, limb_t m) +{ + return ((dlimb_t)b << LIMB_BITS) / m; +} + +#ifdef __AVX2__ + +static inline limb_t ntt_limb_to_int(NTTLimb a, limb_t m) +{ + slimb_t v; + v = a; + if (v < 0) + v += m; + if (v >= m) + v -= m; + return v; +} + +static inline NTTLimb int_to_ntt_limb(limb_t a, limb_t m) +{ + return (slimb_t)a; +} + +static inline NTTLimb int_to_ntt_limb2(limb_t a, limb_t m) +{ + if (a >= (m / 2)) + a -= m; + return (slimb_t)a; +} + +/* return r + m if r < 0 otherwise r. */ +static inline __m256d ntt_mod1(__m256d r, __m256d m) +{ + return _mm256_blendv_pd(r, r + m, r); +} + +/* input: abs(r) < 2 * m. Output: abs(r) < m */ +static inline __m256d ntt_mod(__m256d r, __m256d mf, __m256d m2f) +{ + return _mm256_blendv_pd(r, r + m2f, r) - mf; +} + +/* input: abs(a*b) < 2 * m^2, output: abs(r) < m */ +static inline __m256d ntt_mul_mod(__m256d a, __m256d b, __m256d mf, + __m256d m_inv) +{ + __m256d r, q, ab1, ab0, qm0, qm1; + ab1 = a * b; + q = _mm256_round_pd(ab1 * m_inv, 0); /* round to nearest */ + qm1 = q * mf; + qm0 = _mm256_fmsub_pd(q, mf, qm1); /* low part */ + ab0 = _mm256_fmsub_pd(a, b, ab1); /* low part */ + r = (ab1 - qm1) + (ab0 - qm0); + return r; +} + +static void *bf_aligned_malloc(bf_context_t *s, size_t size, size_t align) +{ + void *ptr; + void **ptr1; + ptr = bf_malloc(s, size + sizeof(void *) + align - 1); + if (!ptr) + return NULL; + ptr1 = (void **)(((uintptr_t)ptr + sizeof(void *) + align - 1) & + ~(align - 1)); + ptr1[-1] = ptr; + return ptr1; +} + +static void bf_aligned_free(bf_context_t *s, void *ptr) +{ + if (!ptr) + return; + bf_free(s, ((void **)ptr)[-1]); +} + +static void *ntt_malloc(BFNTTState *s, size_t size) +{ + return bf_aligned_malloc(s->ctx, size, 64); +} + +static void ntt_free(BFNTTState *s, void *ptr) +{ + bf_aligned_free(s->ctx, ptr); +} + +static no_inline int ntt_fft(BFNTTState *s, + NTTLimb *out_buf, NTTLimb *in_buf, + NTTLimb *tmp_buf, int fft_len_log2, + int inverse, int m_idx) +{ + limb_t nb_blocks, fft_per_block, p, k, n, stride_in, i, j; + NTTLimb *tab_in, *tab_out, *tmp, *trig; + __m256d m_inv, mf, m2f, c, a0, a1, b0, b1; + limb_t m; + int l; + + m = ntt_mods[m_idx]; + + m_inv = _mm256_set1_pd(1.0 / (double)m); + mf = _mm256_set1_pd(m); + m2f = _mm256_set1_pd(m * 2); + + n = (limb_t)1 << fft_len_log2; + assert(n >= 8); + stride_in = n / 2; + + tab_in = in_buf; + tab_out = tmp_buf; + trig = get_trig(s, fft_len_log2, inverse, m_idx); + if (!trig) + return -1; + p = 0; + for(k = 0; k < stride_in; k += 4) { + a0 = _mm256_load_pd(&tab_in[k]); + a1 = _mm256_load_pd(&tab_in[k + stride_in]); + c = _mm256_load_pd(trig); + trig += 4; + b0 = ntt_mod(a0 + a1, mf, m2f); + b1 = ntt_mul_mod(a0 - a1, c, mf, m_inv); + a0 = _mm256_permute2f128_pd(b0, b1, 0x20); + a1 = _mm256_permute2f128_pd(b0, b1, 0x31); + a0 = _mm256_permute4x64_pd(a0, 0xd8); + a1 = _mm256_permute4x64_pd(a1, 0xd8); + _mm256_store_pd(&tab_out[p], a0); + _mm256_store_pd(&tab_out[p + 4], a1); + p += 2 * 4; + } + tmp = tab_in; + tab_in = tab_out; + tab_out = tmp; + + trig = get_trig(s, fft_len_log2 - 1, inverse, m_idx); + if (!trig) + return -1; + p = 0; + for(k = 0; k < stride_in; k += 4) { + a0 = _mm256_load_pd(&tab_in[k]); + a1 = _mm256_load_pd(&tab_in[k + stride_in]); + c = _mm256_setr_pd(trig[0], trig[0], trig[1], trig[1]); + trig += 2; + b0 = ntt_mod(a0 + a1, mf, m2f); + b1 = ntt_mul_mod(a0 - a1, c, mf, m_inv); + a0 = _mm256_permute2f128_pd(b0, b1, 0x20); + a1 = _mm256_permute2f128_pd(b0, b1, 0x31); + _mm256_store_pd(&tab_out[p], a0); + _mm256_store_pd(&tab_out[p + 4], a1); + p += 2 * 4; + } + tmp = tab_in; + tab_in = tab_out; + tab_out = tmp; + + nb_blocks = n / 4; + fft_per_block = 4; + + l = fft_len_log2 - 2; + while (nb_blocks != 2) { + nb_blocks >>= 1; + p = 0; + k = 0; + trig = get_trig(s, l, inverse, m_idx); + if (!trig) + return -1; + for(i = 0; i < nb_blocks; i++) { + c = _mm256_set1_pd(trig[0]); + trig++; + for(j = 0; j < fft_per_block; j += 4) { + a0 = _mm256_load_pd(&tab_in[k + j]); + a1 = _mm256_load_pd(&tab_in[k + j + stride_in]); + b0 = ntt_mod(a0 + a1, mf, m2f); + b1 = ntt_mul_mod(a0 - a1, c, mf, m_inv); + _mm256_store_pd(&tab_out[p + j], b0); + _mm256_store_pd(&tab_out[p + j + fft_per_block], b1); + } + k += fft_per_block; + p += 2 * fft_per_block; + } + fft_per_block <<= 1; + l--; + tmp = tab_in; + tab_in = tab_out; + tab_out = tmp; + } + + tab_out = out_buf; + for(k = 0; k < stride_in; k += 4) { + a0 = _mm256_load_pd(&tab_in[k]); + a1 = _mm256_load_pd(&tab_in[k + stride_in]); + b0 = ntt_mod(a0 + a1, mf, m2f); + b1 = ntt_mod(a0 - a1, mf, m2f); + _mm256_store_pd(&tab_out[k], b0); + _mm256_store_pd(&tab_out[k + stride_in], b1); + } + return 0; +} + +static void ntt_vec_mul(BFNTTState *s, + NTTLimb *tab1, NTTLimb *tab2, limb_t fft_len_log2, + int k_tot, int m_idx) +{ + limb_t i, c_inv, n, m; + __m256d m_inv, mf, a, b, c; + + m = ntt_mods[m_idx]; + c_inv = s->ntt_len_inv[m_idx][k_tot][0]; + m_inv = _mm256_set1_pd(1.0 / (double)m); + mf = _mm256_set1_pd(m); + c = _mm256_set1_pd(int_to_ntt_limb(c_inv, m)); + n = (limb_t)1 << fft_len_log2; + for(i = 0; i < n; i += 4) { + a = _mm256_load_pd(&tab1[i]); + b = _mm256_load_pd(&tab2[i]); + a = ntt_mul_mod(a, b, mf, m_inv); + a = ntt_mul_mod(a, c, mf, m_inv); + _mm256_store_pd(&tab1[i], a); + } +} + +static no_inline void mul_trig(NTTLimb *buf, + limb_t n, limb_t c1, limb_t m, limb_t m_inv1) +{ + limb_t i, c2, c3, c4; + __m256d c, c_mul, a0, mf, m_inv; + assert(n >= 2); + + mf = _mm256_set1_pd(m); + m_inv = _mm256_set1_pd(1.0 / (double)m); + + c2 = mul_mod_fast(c1, c1, m, m_inv1); + c3 = mul_mod_fast(c2, c1, m, m_inv1); + c4 = mul_mod_fast(c2, c2, m, m_inv1); + c = _mm256_setr_pd(1, int_to_ntt_limb(c1, m), + int_to_ntt_limb(c2, m), int_to_ntt_limb(c3, m)); + c_mul = _mm256_set1_pd(int_to_ntt_limb(c4, m)); + for(i = 0; i < n; i += 4) { + a0 = _mm256_load_pd(&buf[i]); + a0 = ntt_mul_mod(a0, c, mf, m_inv); + _mm256_store_pd(&buf[i], a0); + c = ntt_mul_mod(c, c_mul, mf, m_inv); + } +} + +#else + +static void *ntt_malloc(BFNTTState *s, size_t size) +{ + return bf_malloc(s->ctx, size); +} + +static void ntt_free(BFNTTState *s, void *ptr) +{ + bf_free(s->ctx, ptr); +} + +static inline limb_t ntt_limb_to_int(NTTLimb a, limb_t m) +{ + if (a >= m) + a -= m; + return a; +} + +static inline NTTLimb int_to_ntt_limb(slimb_t a, limb_t m) +{ + return a; +} + +static no_inline int ntt_fft(BFNTTState *s, NTTLimb *out_buf, NTTLimb *in_buf, + NTTLimb *tmp_buf, int fft_len_log2, + int inverse, int m_idx) +{ + limb_t nb_blocks, fft_per_block, p, k, n, stride_in, i, j, m, m2; + NTTLimb *tab_in, *tab_out, *tmp, a0, a1, b0, b1, c, *trig, c_inv; + int l; + + m = ntt_mods[m_idx]; + m2 = 2 * m; + n = (limb_t)1 << fft_len_log2; + nb_blocks = n; + fft_per_block = 1; + stride_in = n / 2; + tab_in = in_buf; + tab_out = tmp_buf; + l = fft_len_log2; + while (nb_blocks != 2) { + nb_blocks >>= 1; + p = 0; + k = 0; + trig = get_trig(s, l, inverse, m_idx); + if (!trig) + return -1; + for(i = 0; i < nb_blocks; i++) { + c = trig[0]; + c_inv = trig[1]; + trig += 2; + for(j = 0; j < fft_per_block; j++) { + a0 = tab_in[k + j]; + a1 = tab_in[k + j + stride_in]; + b0 = add_mod(a0, a1, m2); + b1 = a0 - a1 + m2; + b1 = mul_mod_fast3(b1, c, m, c_inv); + tab_out[p + j] = b0; + tab_out[p + j + fft_per_block] = b1; + } + k += fft_per_block; + p += 2 * fft_per_block; + } + fft_per_block <<= 1; + l--; + tmp = tab_in; + tab_in = tab_out; + tab_out = tmp; + } + /* no twiddle in last step */ + tab_out = out_buf; + for(k = 0; k < stride_in; k++) { + a0 = tab_in[k]; + a1 = tab_in[k + stride_in]; + b0 = add_mod(a0, a1, m2); + b1 = sub_mod(a0, a1, m2); + tab_out[k] = b0; + tab_out[k + stride_in] = b1; + } + return 0; +} + +static void ntt_vec_mul(BFNTTState *s, + NTTLimb *tab1, NTTLimb *tab2, int fft_len_log2, + int k_tot, int m_idx) +{ + limb_t i, norm, norm_inv, a, n, m, m_inv; + + m = ntt_mods[m_idx]; + m_inv = s->ntt_mods_div[m_idx]; + norm = s->ntt_len_inv[m_idx][k_tot][0]; + norm_inv = s->ntt_len_inv[m_idx][k_tot][1]; + n = (limb_t)1 << fft_len_log2; + for(i = 0; i < n; i++) { + a = tab1[i]; + /* need to reduce the range so that the product is < + 2^(LIMB_BITS+NTT_MOD_LOG2_MIN) */ + if (a >= m) + a -= m; + a = mul_mod_fast(a, tab2[i], m, m_inv); + a = mul_mod_fast3(a, norm, m, norm_inv); + tab1[i] = a; + } +} + +static no_inline void mul_trig(NTTLimb *buf, + limb_t n, limb_t c_mul, limb_t m, limb_t m_inv) +{ + limb_t i, c0, c_mul_inv; + + c0 = 1; + c_mul_inv = init_mul_mod_fast2(c_mul, m); + for(i = 0; i < n; i++) { + buf[i] = mul_mod_fast(buf[i], c0, m, m_inv); + c0 = mul_mod_fast2(c0, c_mul, m, c_mul_inv); + } +} + +#endif /* !AVX2 */ + +static no_inline NTTLimb *get_trig(BFNTTState *s, + int k, int inverse, int m_idx) +{ + NTTLimb *tab; + limb_t i, n2, c, c_mul, m, c_mul_inv; + + if (k > NTT_TRIG_K_MAX) + return NULL; + + tab = s->ntt_trig[m_idx][inverse][k]; + if (tab) + return tab; + n2 = (limb_t)1 << (k - 1); + m = ntt_mods[m_idx]; +#ifdef __AVX2__ + tab = ntt_malloc(s, sizeof(NTTLimb) * n2); +#else + tab = ntt_malloc(s, sizeof(NTTLimb) * n2 * 2); +#endif + if (!tab) + return NULL; + c = 1; + c_mul = s->ntt_proot_pow[m_idx][inverse][k]; + c_mul_inv = s->ntt_proot_pow_inv[m_idx][inverse][k]; + for(i = 0; i < n2; i++) { +#ifdef __AVX2__ + tab[i] = int_to_ntt_limb2(c, m); +#else + tab[2 * i] = int_to_ntt_limb(c, m); + tab[2 * i + 1] = init_mul_mod_fast2(c, m); +#endif + c = mul_mod_fast2(c, c_mul, m, c_mul_inv); + } + s->ntt_trig[m_idx][inverse][k] = tab; + return tab; +} + +void fft_clear_cache(bf_context_t *s1) +{ + int m_idx, inverse, k; + BFNTTState *s = s1->ntt_state; + if (s) { + for(m_idx = 0; m_idx < NB_MODS; m_idx++) { + for(inverse = 0; inverse < 2; inverse++) { + for(k = 0; k < NTT_TRIG_K_MAX + 1; k++) { + if (s->ntt_trig[m_idx][inverse][k]) { + ntt_free(s, s->ntt_trig[m_idx][inverse][k]); + s->ntt_trig[m_idx][inverse][k] = NULL; + } + } + } + } +#if defined(__AVX2__) + bf_aligned_free(s1, s); +#else + bf_free(s1, s); +#endif + s1->ntt_state = NULL; + } +} + +#define STRIP_LEN 16 + +/* dst = buf1, src = buf2 */ +static int ntt_fft_partial(BFNTTState *s, NTTLimb *buf1, + int k1, int k2, limb_t n1, limb_t n2, int inverse, + limb_t m_idx) +{ + limb_t i, j, c_mul, c0, m, m_inv, strip_len, l; + NTTLimb *buf2, *buf3; + + buf2 = NULL; + buf3 = ntt_malloc(s, sizeof(NTTLimb) * n1); + if (!buf3) + goto fail; + if (k2 == 0) { + if (ntt_fft(s, buf1, buf1, buf3, k1, inverse, m_idx)) + goto fail; + } else { + strip_len = STRIP_LEN; + buf2 = ntt_malloc(s, sizeof(NTTLimb) * n1 * strip_len); + if (!buf2) + goto fail; + m = ntt_mods[m_idx]; + m_inv = s->ntt_mods_div[m_idx]; + c0 = s->ntt_proot_pow[m_idx][inverse][k1 + k2]; + c_mul = 1; + assert((n2 % strip_len) == 0); + for(j = 0; j < n2; j += strip_len) { + for(i = 0; i < n1; i++) { + for(l = 0; l < strip_len; l++) { + buf2[i + l * n1] = buf1[i * n2 + (j + l)]; + } + } + for(l = 0; l < strip_len; l++) { + if (inverse) + mul_trig(buf2 + l * n1, n1, c_mul, m, m_inv); + if (ntt_fft(s, buf2 + l * n1, buf2 + l * n1, buf3, k1, inverse, m_idx)) + goto fail; + if (!inverse) + mul_trig(buf2 + l * n1, n1, c_mul, m, m_inv); + c_mul = mul_mod_fast(c_mul, c0, m, m_inv); + } + + for(i = 0; i < n1; i++) { + for(l = 0; l < strip_len; l++) { + buf1[i * n2 + (j + l)] = buf2[i + l *n1]; + } + } + } + ntt_free(s, buf2); + } + ntt_free(s, buf3); + return 0; + fail: + ntt_free(s, buf2); + ntt_free(s, buf3); + return -1; +} + + +/* dst = buf1, src = buf2, tmp = buf3 */ +static int ntt_conv(BFNTTState *s, NTTLimb *buf1, NTTLimb *buf2, + int k, int k_tot, limb_t m_idx) +{ + limb_t n1, n2, i; + int k1, k2; + + if (k <= NTT_TRIG_K_MAX) { + k1 = k; + } else { + /* recursive split of the FFT */ + k1 = bf_min(k / 2, NTT_TRIG_K_MAX); + } + k2 = k - k1; + n1 = (limb_t)1 << k1; + n2 = (limb_t)1 << k2; + + if (ntt_fft_partial(s, buf1, k1, k2, n1, n2, 0, m_idx)) + return -1; + if (ntt_fft_partial(s, buf2, k1, k2, n1, n2, 0, m_idx)) + return -1; + if (k2 == 0) { + ntt_vec_mul(s, buf1, buf2, k, k_tot, m_idx); + } else { + for(i = 0; i < n1; i++) { + ntt_conv(s, buf1 + i * n2, buf2 + i * n2, k2, k_tot, m_idx); + } + } + if (ntt_fft_partial(s, buf1, k1, k2, n1, n2, 1, m_idx)) + return -1; + return 0; +} + + +static no_inline void limb_to_ntt(BFNTTState *s, + NTTLimb *tabr, limb_t fft_len, + const limb_t *taba, limb_t a_len, int dpl, + int first_m_idx, int nb_mods) +{ + slimb_t i, n; + dlimb_t a, b; + int j, shift; + limb_t base_mask1, a0, a1, a2, r, m, m_inv; + + memset(tabr, 0, sizeof(NTTLimb) * fft_len * nb_mods); + shift = dpl & (LIMB_BITS - 1); + if (shift == 0) + base_mask1 = -1; + else + base_mask1 = ((limb_t)1 << shift) - 1; + n = bf_min(fft_len, (a_len * LIMB_BITS + dpl - 1) / dpl); + for(i = 0; i < n; i++) { + a0 = get_bits(taba, a_len, i * dpl); + if (dpl <= LIMB_BITS) { + a0 &= base_mask1; + a = a0; + } else { + a1 = get_bits(taba, a_len, i * dpl + LIMB_BITS); + if (dpl <= (LIMB_BITS + NTT_MOD_LOG2_MIN)) { + a = a0 | ((dlimb_t)(a1 & base_mask1) << LIMB_BITS); + } else { + if (dpl > 2 * LIMB_BITS) { + a2 = get_bits(taba, a_len, i * dpl + LIMB_BITS * 2) & + base_mask1; + } else { + a1 &= base_mask1; + a2 = 0; + } + // printf("a=0x%016lx%016lx%016lx\n", a2, a1, a0); + a = (a0 >> (LIMB_BITS - NTT_MOD_LOG2_MAX + NTT_MOD_LOG2_MIN)) | + ((dlimb_t)a1 << (NTT_MOD_LOG2_MAX - NTT_MOD_LOG2_MIN)) | + ((dlimb_t)a2 << (LIMB_BITS + NTT_MOD_LOG2_MAX - NTT_MOD_LOG2_MIN)); + a0 &= ((limb_t)1 << (LIMB_BITS - NTT_MOD_LOG2_MAX + NTT_MOD_LOG2_MIN)) - 1; + } + } + for(j = 0; j < nb_mods; j++) { + m = ntt_mods[first_m_idx + j]; + m_inv = s->ntt_mods_div[first_m_idx + j]; + r = mod_fast(a, m, m_inv); + if (dpl > (LIMB_BITS + NTT_MOD_LOG2_MIN)) { + b = ((dlimb_t)r << (LIMB_BITS - NTT_MOD_LOG2_MAX + NTT_MOD_LOG2_MIN)) | a0; + r = mod_fast(b, m, m_inv); + } + tabr[i + j * fft_len] = int_to_ntt_limb(r, m); + } + } +} + +#if defined(__AVX2__) + +#define VEC_LEN 4 + +typedef union { + __m256d v; + double d[4]; +} VecUnion; + +static no_inline void ntt_to_limb(BFNTTState *s, limb_t *tabr, limb_t r_len, + const NTTLimb *buf, int fft_len_log2, int dpl, + int nb_mods) +{ + const limb_t *mods = ntt_mods + NB_MODS - nb_mods; + const __m256d *mods_cr_vec, *mf, *m_inv; + VecUnion y[NB_MODS]; + limb_t u[NB_MODS], carry[NB_MODS], fft_len, base_mask1, r; + slimb_t i, len, pos; + int j, k, l, shift, n_limb1, p; + dlimb_t t; + + j = NB_MODS * (NB_MODS - 1) / 2 - nb_mods * (nb_mods - 1) / 2; + mods_cr_vec = s->ntt_mods_cr_vec + j; + mf = s->ntt_mods_vec + NB_MODS - nb_mods; + m_inv = s->ntt_mods_inv_vec + NB_MODS - nb_mods; + + shift = dpl & (LIMB_BITS - 1); + if (shift == 0) + base_mask1 = -1; + else + base_mask1 = ((limb_t)1 << shift) - 1; + n_limb1 = ((unsigned)dpl - 1) / LIMB_BITS; + for(j = 0; j < NB_MODS; j++) + carry[j] = 0; + for(j = 0; j < NB_MODS; j++) + u[j] = 0; /* avoid warnings */ + memset(tabr, 0, sizeof(limb_t) * r_len); + fft_len = (limb_t)1 << fft_len_log2; + len = bf_min(fft_len, (r_len * LIMB_BITS + dpl - 1) / dpl); + len = (len + VEC_LEN - 1) & ~(VEC_LEN - 1); + i = 0; + while (i < len) { + for(j = 0; j < nb_mods; j++) + y[j].v = *(__m256d *)&buf[i + fft_len * j]; + + /* Chinese remainder to get mixed radix representation */ + l = 0; + for(j = 0; j < nb_mods - 1; j++) { + y[j].v = ntt_mod1(y[j].v, mf[j]); + for(k = j + 1; k < nb_mods; k++) { + y[k].v = ntt_mul_mod(y[k].v - y[j].v, + mods_cr_vec[l], mf[k], m_inv[k]); + l++; + } + } + y[j].v = ntt_mod1(y[j].v, mf[j]); + + for(p = 0; p < VEC_LEN; p++) { + /* back to normal representation */ + u[0] = (int64_t)y[nb_mods - 1].d[p]; + l = 1; + for(j = nb_mods - 2; j >= 1; j--) { + r = (int64_t)y[j].d[p]; + for(k = 0; k < l; k++) { + t = (dlimb_t)u[k] * mods[j] + r; + r = t >> LIMB_BITS; + u[k] = t; + } + u[l] = r; + l++; + } + /* XXX: for nb_mods = 5, l should be 4 */ + + /* last step adds the carry */ + r = (int64_t)y[0].d[p]; + for(k = 0; k < l; k++) { + t = (dlimb_t)u[k] * mods[j] + r + carry[k]; + r = t >> LIMB_BITS; + u[k] = t; + } + u[l] = r + carry[l]; + + /* write the digits */ + pos = i * dpl; + for(j = 0; j < n_limb1; j++) { + put_bits(tabr, r_len, pos, u[j]); + pos += LIMB_BITS; + } + put_bits(tabr, r_len, pos, u[n_limb1] & base_mask1); + /* shift by dpl digits and set the carry */ + if (shift == 0) { + for(j = n_limb1 + 1; j < nb_mods; j++) + carry[j - (n_limb1 + 1)] = u[j]; + } else { + for(j = n_limb1; j < nb_mods - 1; j++) { + carry[j - n_limb1] = (u[j] >> shift) | + (u[j + 1] << (LIMB_BITS - shift)); + } + carry[nb_mods - 1 - n_limb1] = u[nb_mods - 1] >> shift; + } + i++; + } + } +} +#else +static no_inline void ntt_to_limb(BFNTTState *s, limb_t *tabr, limb_t r_len, + const NTTLimb *buf, int fft_len_log2, int dpl, + int nb_mods) +{ + const limb_t *mods = ntt_mods + NB_MODS - nb_mods; + const limb_t *mods_cr, *mods_cr_inv; + limb_t y[NB_MODS], u[NB_MODS], carry[NB_MODS], fft_len, base_mask1, r; + slimb_t i, len, pos; + int j, k, l, shift, n_limb1; + dlimb_t t; + + j = NB_MODS * (NB_MODS - 1) / 2 - nb_mods * (nb_mods - 1) / 2; + mods_cr = ntt_mods_cr + j; + mods_cr_inv = s->ntt_mods_cr_inv + j; + + shift = dpl & (LIMB_BITS - 1); + if (shift == 0) + base_mask1 = -1; + else + base_mask1 = ((limb_t)1 << shift) - 1; + n_limb1 = ((unsigned)dpl - 1) / LIMB_BITS; + for(j = 0; j < NB_MODS; j++) + carry[j] = 0; + for(j = 0; j < NB_MODS; j++) + u[j] = 0; /* avoid warnings */ + memset(tabr, 0, sizeof(limb_t) * r_len); + fft_len = (limb_t)1 << fft_len_log2; + len = bf_min(fft_len, (r_len * LIMB_BITS + dpl - 1) / dpl); + for(i = 0; i < len; i++) { + for(j = 0; j < nb_mods; j++) { + y[j] = ntt_limb_to_int(buf[i + fft_len * j], mods[j]); + } + + /* Chinese remainder to get mixed radix representation */ + l = 0; + for(j = 0; j < nb_mods - 1; j++) { + for(k = j + 1; k < nb_mods; k++) { + limb_t m; + m = mods[k]; + /* Note: there is no overflow in the sub_mod() because + the modulos are sorted by increasing order */ + y[k] = mul_mod_fast2(y[k] - y[j] + m, + mods_cr[l], m, mods_cr_inv[l]); + l++; + } + } + + /* back to normal representation */ + u[0] = y[nb_mods - 1]; + l = 1; + for(j = nb_mods - 2; j >= 1; j--) { + r = y[j]; + for(k = 0; k < l; k++) { + t = (dlimb_t)u[k] * mods[j] + r; + r = t >> LIMB_BITS; + u[k] = t; + } + u[l] = r; + l++; + } + + /* last step adds the carry */ + r = y[0]; + for(k = 0; k < l; k++) { + t = (dlimb_t)u[k] * mods[j] + r + carry[k]; + r = t >> LIMB_BITS; + u[k] = t; + } + u[l] = r + carry[l]; + + /* write the digits */ + pos = i * dpl; + for(j = 0; j < n_limb1; j++) { + put_bits(tabr, r_len, pos, u[j]); + pos += LIMB_BITS; + } + put_bits(tabr, r_len, pos, u[n_limb1] & base_mask1); + /* shift by dpl digits and set the carry */ + if (shift == 0) { + for(j = n_limb1 + 1; j < nb_mods; j++) + carry[j - (n_limb1 + 1)] = u[j]; + } else { + for(j = n_limb1; j < nb_mods - 1; j++) { + carry[j - n_limb1] = (u[j] >> shift) | + (u[j + 1] << (LIMB_BITS - shift)); + } + carry[nb_mods - 1 - n_limb1] = u[nb_mods - 1] >> shift; + } + } +} +#endif + +static int ntt_static_init(bf_context_t *s1) +{ + BFNTTState *s; + int inverse, i, j, k, l; + limb_t c, c_inv, c_inv2, m, m_inv; + + if (s1->ntt_state) + return 0; +#if defined(__AVX2__) + s = bf_aligned_malloc(s1, sizeof(*s), 64); +#else + s = bf_malloc(s1, sizeof(*s)); +#endif + if (!s) + return -1; + memset(s, 0, sizeof(*s)); + s1->ntt_state = s; + s->ctx = s1; + + for(j = 0; j < NB_MODS; j++) { + m = ntt_mods[j]; + m_inv = init_mul_mod_fast(m); + s->ntt_mods_div[j] = m_inv; +#if defined(__AVX2__) + s->ntt_mods_vec[j] = _mm256_set1_pd(m); + s->ntt_mods_inv_vec[j] = _mm256_set1_pd(1.0 / (double)m); +#endif + c_inv2 = (m + 1) / 2; /* 1/2 */ + c_inv = 1; + for(i = 0; i <= NTT_PROOT_2EXP; i++) { + s->ntt_len_inv[j][i][0] = c_inv; + s->ntt_len_inv[j][i][1] = init_mul_mod_fast2(c_inv, m); + c_inv = mul_mod_fast(c_inv, c_inv2, m, m_inv); + } + + for(inverse = 0; inverse < 2; inverse++) { + c = ntt_proot[inverse][j]; + for(i = 0; i < NTT_PROOT_2EXP; i++) { + s->ntt_proot_pow[j][inverse][NTT_PROOT_2EXP - i] = c; + s->ntt_proot_pow_inv[j][inverse][NTT_PROOT_2EXP - i] = + init_mul_mod_fast2(c, m); + c = mul_mod_fast(c, c, m, m_inv); + } + } + } + + l = 0; + for(j = 0; j < NB_MODS - 1; j++) { + for(k = j + 1; k < NB_MODS; k++) { +#if defined(__AVX2__) + s->ntt_mods_cr_vec[l] = _mm256_set1_pd(int_to_ntt_limb2(ntt_mods_cr[l], + ntt_mods[k])); +#else + s->ntt_mods_cr_inv[l] = init_mul_mod_fast2(ntt_mods_cr[l], + ntt_mods[k]); +#endif + l++; + } + } + return 0; +} + +int bf_get_fft_size(int *pdpl, int *pnb_mods, limb_t len) +{ + int dpl, fft_len_log2, n_bits, nb_mods, dpl_found, fft_len_log2_found; + int int_bits, nb_mods_found; + limb_t cost, min_cost; + + min_cost = -1; + dpl_found = 0; + nb_mods_found = 4; + fft_len_log2_found = 0; + for(nb_mods = 3; nb_mods <= NB_MODS; nb_mods++) { + int_bits = ntt_int_bits[NB_MODS - nb_mods]; + dpl = bf_min((int_bits - 4) / 2, + 2 * LIMB_BITS + 2 * NTT_MOD_LOG2_MIN - NTT_MOD_LOG2_MAX); + for(;;) { + fft_len_log2 = ceil_log2((len * LIMB_BITS + dpl - 1) / dpl); + if (fft_len_log2 > NTT_PROOT_2EXP) + goto next; + n_bits = fft_len_log2 + 2 * dpl; + if (n_bits <= int_bits) { + cost = ((limb_t)(fft_len_log2 + 1) << fft_len_log2) * nb_mods; + // printf("n=%d dpl=%d: cost=%" PRId64 "\n", nb_mods, dpl, (int64_t)cost); + if (cost < min_cost) { + min_cost = cost; + dpl_found = dpl; + nb_mods_found = nb_mods; + fft_len_log2_found = fft_len_log2; + } + break; + } + dpl--; + if (dpl == 0) + break; + } + next: ; + } + if (!dpl_found) + abort(); + /* limit dpl if possible to reduce fixed cost of limb/NTT conversion */ + if (dpl_found > (LIMB_BITS + NTT_MOD_LOG2_MIN) && + ((limb_t)(LIMB_BITS + NTT_MOD_LOG2_MIN) << fft_len_log2_found) >= + len * LIMB_BITS) { + dpl_found = LIMB_BITS + NTT_MOD_LOG2_MIN; + } + *pnb_mods = nb_mods_found; + *pdpl = dpl_found; + return fft_len_log2_found; +} + +/* return 0 if OK, -1 if memory error */ +static no_inline int fft_mul(bf_context_t *s1, + bf_t *res, limb_t *a_tab, limb_t a_len, + limb_t *b_tab, limb_t b_len, int mul_flags) +{ + BFNTTState *s; + int dpl, fft_len_log2, j, nb_mods, reduced_mem; + slimb_t len, fft_len; + NTTLimb *buf1, *buf2, *ptr; +#if defined(USE_MUL_CHECK) + limb_t ha, hb, hr, h_ref; +#endif + + if (ntt_static_init(s1)) + return -1; + s = s1->ntt_state; + + /* find the optimal number of digits per limb (dpl) */ + len = a_len + b_len; + fft_len_log2 = bf_get_fft_size(&dpl, &nb_mods, len); + fft_len = (uint64_t)1 << fft_len_log2; + // printf("len=%" PRId64 " fft_len_log2=%d dpl=%d\n", len, fft_len_log2, dpl); +#if defined(USE_MUL_CHECK) + ha = mp_mod1(a_tab, a_len, BF_CHKSUM_MOD, 0); + hb = mp_mod1(b_tab, b_len, BF_CHKSUM_MOD, 0); +#endif + if ((mul_flags & (FFT_MUL_R_OVERLAP_A | FFT_MUL_R_OVERLAP_B)) == 0) { + if (!(mul_flags & FFT_MUL_R_NORESIZE)) + bf_resize(res, 0); + } else if (mul_flags & FFT_MUL_R_OVERLAP_B) { + limb_t *tmp_tab, tmp_len; + /* it is better to free 'b' first */ + tmp_tab = a_tab; + a_tab = b_tab; + b_tab = tmp_tab; + tmp_len = a_len; + a_len = b_len; + b_len = tmp_len; + } + buf1 = ntt_malloc(s, sizeof(NTTLimb) * fft_len * nb_mods); + if (!buf1) + return -1; + limb_to_ntt(s, buf1, fft_len, a_tab, a_len, dpl, + NB_MODS - nb_mods, nb_mods); + if ((mul_flags & (FFT_MUL_R_OVERLAP_A | FFT_MUL_R_OVERLAP_B)) == + FFT_MUL_R_OVERLAP_A) { + if (!(mul_flags & FFT_MUL_R_NORESIZE)) + bf_resize(res, 0); + } + reduced_mem = (fft_len_log2 >= 14); + if (!reduced_mem) { + buf2 = ntt_malloc(s, sizeof(NTTLimb) * fft_len * nb_mods); + if (!buf2) + goto fail; + limb_to_ntt(s, buf2, fft_len, b_tab, b_len, dpl, + NB_MODS - nb_mods, nb_mods); + if (!(mul_flags & FFT_MUL_R_NORESIZE)) + bf_resize(res, 0); /* in case res == b */ + } else { + buf2 = ntt_malloc(s, sizeof(NTTLimb) * fft_len); + if (!buf2) + goto fail; + } + for(j = 0; j < nb_mods; j++) { + if (reduced_mem) { + limb_to_ntt(s, buf2, fft_len, b_tab, b_len, dpl, + NB_MODS - nb_mods + j, 1); + ptr = buf2; + } else { + ptr = buf2 + fft_len * j; + } + if (ntt_conv(s, buf1 + fft_len * j, ptr, + fft_len_log2, fft_len_log2, j + NB_MODS - nb_mods)) + goto fail; + } + if (!(mul_flags & FFT_MUL_R_NORESIZE)) + bf_resize(res, 0); /* in case res == b and reduced mem */ + ntt_free(s, buf2); + buf2 = NULL; + if (!(mul_flags & FFT_MUL_R_NORESIZE)) { + if (bf_resize(res, len)) + goto fail; + } + ntt_to_limb(s, res->tab, len, buf1, fft_len_log2, dpl, nb_mods); + ntt_free(s, buf1); +#if defined(USE_MUL_CHECK) + hr = mp_mod1(res->tab, len, BF_CHKSUM_MOD, 0); + h_ref = mul_mod(ha, hb, BF_CHKSUM_MOD); + if (hr != h_ref) { + printf("ntt_mul_error: len=%" PRId_LIMB " fft_len_log2=%d dpl=%d nb_mods=%d\n", + len, fft_len_log2, dpl, nb_mods); + // printf("ha=0x" FMT_LIMB" hb=0x" FMT_LIMB " hr=0x" FMT_LIMB " expected=0x" FMT_LIMB "\n", ha, hb, hr, h_ref); + exit(1); + } +#endif + return 0; + fail: + ntt_free(s, buf1); + ntt_free(s, buf2); + return -1; +} + +#else /* USE_FFT_MUL */ + +int bf_get_fft_size(int *pdpl, int *pnb_mods, limb_t len) +{ + return 0; +} + +#endif /* !USE_FFT_MUL */ + +#undef malloc +#undef free +#undef realloc diff --git a/third_party/quickjs/libbf.h b/third_party/quickjs/libbf.h new file mode 100644 index 0000000000..3586532e88 --- /dev/null +++ b/third_party/quickjs/libbf.h @@ -0,0 +1,545 @@ +/* + * Tiny arbitrary precision floating point library + * + * Copyright (c) 2017-2021 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#ifndef LIBBF_H +#define LIBBF_H + +#include <stddef.h> +#include <stdint.h> + +#ifdef __cplusplus +extern "C" { +#endif + +#if INTPTR_MAX >= INT64_MAX && !defined(_WIN32) && !defined(__TINYC__) +#define LIMB_LOG2_BITS 6 +#else +#define LIMB_LOG2_BITS 5 +#endif + +#define LIMB_BITS (1 << LIMB_LOG2_BITS) + +#if LIMB_BITS == 64 +#ifndef INT128_MAX +__extension__ typedef __int128 int128_t; +__extension__ typedef unsigned __int128 uint128_t; +#endif +typedef int64_t slimb_t; +typedef uint64_t limb_t; +typedef uint128_t dlimb_t; +#define BF_RAW_EXP_MIN INT64_MIN +#define BF_RAW_EXP_MAX INT64_MAX + +#define LIMB_DIGITS 19 +#define BF_DEC_BASE UINT64_C(10000000000000000000) + +#else + +typedef int32_t slimb_t; +typedef uint32_t limb_t; +typedef uint64_t dlimb_t; +#define BF_RAW_EXP_MIN INT32_MIN +#define BF_RAW_EXP_MAX INT32_MAX + +#define LIMB_DIGITS 9 +#define BF_DEC_BASE 1000000000U + +#endif + +/* in bits */ +/* minimum number of bits for the exponent */ +#define BF_EXP_BITS_MIN 3 +/* maximum number of bits for the exponent */ +#define BF_EXP_BITS_MAX (LIMB_BITS - 3) +/* extended range for exponent, used internally */ +#define BF_EXT_EXP_BITS_MAX (BF_EXP_BITS_MAX + 1) +/* minimum possible precision */ +#define BF_PREC_MIN 2 +/* minimum possible precision */ +#define BF_PREC_MAX (((limb_t)1 << (LIMB_BITS - 2)) - 2) +/* some operations support infinite precision */ +#define BF_PREC_INF (BF_PREC_MAX + 1) /* infinite precision */ + +#if LIMB_BITS == 64 +#define BF_CHKSUM_MOD (UINT64_C(975620677) * UINT64_C(9795002197)) +#else +#define BF_CHKSUM_MOD 975620677U +#endif + +#define BF_EXP_ZERO BF_RAW_EXP_MIN +#define BF_EXP_INF (BF_RAW_EXP_MAX - 1) +#define BF_EXP_NAN BF_RAW_EXP_MAX + +/* +/-zero is represented with expn = BF_EXP_ZERO and len = 0, + +/-infinity is represented with expn = BF_EXP_INF and len = 0, + NaN is represented with expn = BF_EXP_NAN and len = 0 (sign is ignored) + */ +typedef struct { + struct bf_context_t *ctx; + int sign; + slimb_t expn; + limb_t len; + limb_t *tab; +} bf_t; + +typedef struct { + /* must be kept identical to bf_t */ + struct bf_context_t *ctx; + int sign; + slimb_t expn; + limb_t len; + limb_t *tab; +} bfdec_t; + +typedef enum { + BF_RNDN, /* round to nearest, ties to even */ + BF_RNDZ, /* round to zero */ + BF_RNDD, /* round to -inf (the code relies on (BF_RNDD xor BF_RNDU) = 1) */ + BF_RNDU, /* round to +inf */ + BF_RNDNA, /* round to nearest, ties away from zero */ + BF_RNDA, /* round away from zero */ + BF_RNDF, /* faithful rounding (nondeterministic, either RNDD or RNDU, + inexact flag is always set) */ +} bf_rnd_t; + +/* allow subnormal numbers. Only available if the number of exponent + bits is <= BF_EXP_BITS_USER_MAX and prec != BF_PREC_INF. */ +#define BF_FLAG_SUBNORMAL (1 << 3) +/* 'prec' is the precision after the radix point instead of the whole + mantissa. Can only be used with bf_round() and + bfdec_[add|sub|mul|div|sqrt|round](). */ +#define BF_FLAG_RADPNT_PREC (1 << 4) + +#define BF_RND_MASK 0x7 +#define BF_EXP_BITS_SHIFT 5 +#define BF_EXP_BITS_MASK 0x3f + +/* shortcut for bf_set_exp_bits(BF_EXT_EXP_BITS_MAX) */ +#define BF_FLAG_EXT_EXP (BF_EXP_BITS_MASK << BF_EXP_BITS_SHIFT) + +/* contains the rounding mode and number of exponents bits */ +typedef uint32_t bf_flags_t; + +typedef void *bf_realloc_func_t(void *opaque, void *ptr, size_t size); + +typedef struct { + bf_t val; + limb_t prec; +} BFConstCache; + +typedef struct bf_context_t { + void *realloc_opaque; + bf_realloc_func_t *realloc_func; + BFConstCache log2_cache; + BFConstCache pi_cache; + struct BFNTTState *ntt_state; +} bf_context_t; + +static inline int bf_get_exp_bits(bf_flags_t flags) +{ + int e; + e = (flags >> BF_EXP_BITS_SHIFT) & BF_EXP_BITS_MASK; + if (e == BF_EXP_BITS_MASK) + return BF_EXP_BITS_MAX + 1; + else + return BF_EXP_BITS_MAX - e; +} + +static inline bf_flags_t bf_set_exp_bits(int n) +{ + return ((BF_EXP_BITS_MAX - n) & BF_EXP_BITS_MASK) << BF_EXP_BITS_SHIFT; +} + +/* returned status */ +#define BF_ST_INVALID_OP (1 << 0) +#define BF_ST_DIVIDE_ZERO (1 << 1) +#define BF_ST_OVERFLOW (1 << 2) +#define BF_ST_UNDERFLOW (1 << 3) +#define BF_ST_INEXACT (1 << 4) +/* indicate that a memory allocation error occured. NaN is returned */ +#define BF_ST_MEM_ERROR (1 << 5) + +#define BF_RADIX_MAX 36 /* maximum radix for bf_atof() and bf_ftoa() */ + +static inline slimb_t bf_max(slimb_t a, slimb_t b) +{ + if (a > b) + return a; + else + return b; +} + +static inline slimb_t bf_min(slimb_t a, slimb_t b) +{ + if (a < b) + return a; + else + return b; +} + +void bf_context_init(bf_context_t *s, bf_realloc_func_t *realloc_func, + void *realloc_opaque); +void bf_context_end(bf_context_t *s); +/* free memory allocated for the bf cache data */ +void bf_clear_cache(bf_context_t *s); + +static inline void *bf_realloc(bf_context_t *s, void *ptr, size_t size) +{ + return s->realloc_func(s->realloc_opaque, ptr, size); +} + +/* 'size' must be != 0 */ +static inline void *bf_malloc(bf_context_t *s, size_t size) +{ + return bf_realloc(s, NULL, size); +} + +static inline void bf_free(bf_context_t *s, void *ptr) +{ + /* must test ptr otherwise equivalent to malloc(0) */ + if (ptr) + bf_realloc(s, ptr, 0); +} + +void bf_init(bf_context_t *s, bf_t *r); + +static inline void bf_delete(bf_t *r) +{ + bf_context_t *s = r->ctx; + /* we accept to delete a zeroed bf_t structure */ + if (s && r->tab) { + bf_realloc(s, r->tab, 0); + } +} + +static inline void bf_neg(bf_t *r) +{ + r->sign ^= 1; +} + +static inline int bf_is_finite(const bf_t *a) +{ + return (a->expn < BF_EXP_INF); +} + +static inline int bf_is_nan(const bf_t *a) +{ + return (a->expn == BF_EXP_NAN); +} + +static inline int bf_is_zero(const bf_t *a) +{ + return (a->expn == BF_EXP_ZERO); +} + +static inline void bf_memcpy(bf_t *r, const bf_t *a) +{ + *r = *a; +} + +int bf_set_ui(bf_t *r, uint64_t a); +int bf_set_si(bf_t *r, int64_t a); +void bf_set_nan(bf_t *r); +void bf_set_zero(bf_t *r, int is_neg); +void bf_set_inf(bf_t *r, int is_neg); +int bf_set(bf_t *r, const bf_t *a); +void bf_move(bf_t *r, bf_t *a); +int bf_get_float64(const bf_t *a, double *pres, bf_rnd_t rnd_mode); +int bf_set_float64(bf_t *a, double d); + +int bf_cmpu(const bf_t *a, const bf_t *b); +int bf_cmp_full(const bf_t *a, const bf_t *b); +int bf_cmp(const bf_t *a, const bf_t *b); +static inline int bf_cmp_eq(const bf_t *a, const bf_t *b) +{ + return bf_cmp(a, b) == 0; +} + +static inline int bf_cmp_le(const bf_t *a, const bf_t *b) +{ + return bf_cmp(a, b) <= 0; +} + +static inline int bf_cmp_lt(const bf_t *a, const bf_t *b) +{ + return bf_cmp(a, b) < 0; +} + +int bf_add(bf_t *r, const bf_t *a, const bf_t *b, limb_t prec, bf_flags_t flags); +int bf_sub(bf_t *r, const bf_t *a, const bf_t *b, limb_t prec, bf_flags_t flags); +int bf_add_si(bf_t *r, const bf_t *a, int64_t b1, limb_t prec, bf_flags_t flags); +int bf_mul(bf_t *r, const bf_t *a, const bf_t *b, limb_t prec, bf_flags_t flags); +int bf_mul_ui(bf_t *r, const bf_t *a, uint64_t b1, limb_t prec, bf_flags_t flags); +int bf_mul_si(bf_t *r, const bf_t *a, int64_t b1, limb_t prec, + bf_flags_t flags); +int bf_mul_2exp(bf_t *r, slimb_t e, limb_t prec, bf_flags_t flags); +int bf_div(bf_t *r, const bf_t *a, const bf_t *b, limb_t prec, bf_flags_t flags); +#define BF_DIVREM_EUCLIDIAN BF_RNDF +int bf_divrem(bf_t *q, bf_t *r, const bf_t *a, const bf_t *b, + limb_t prec, bf_flags_t flags, int rnd_mode); +int bf_rem(bf_t *r, const bf_t *a, const bf_t *b, limb_t prec, + bf_flags_t flags, int rnd_mode); +int bf_remquo(slimb_t *pq, bf_t *r, const bf_t *a, const bf_t *b, limb_t prec, + bf_flags_t flags, int rnd_mode); +/* round to integer with infinite precision */ +int bf_rint(bf_t *r, int rnd_mode); +int bf_round(bf_t *r, limb_t prec, bf_flags_t flags); +int bf_sqrtrem(bf_t *r, bf_t *rem1, const bf_t *a); +int bf_sqrt(bf_t *r, const bf_t *a, limb_t prec, bf_flags_t flags); +slimb_t bf_get_exp_min(const bf_t *a); +int bf_logic_or(bf_t *r, const bf_t *a, const bf_t *b); +int bf_logic_xor(bf_t *r, const bf_t *a, const bf_t *b); +int bf_logic_and(bf_t *r, const bf_t *a, const bf_t *b); + +/* additional flags for bf_atof */ +/* do not accept hex radix prefix (0x or 0X) if radix = 0 or radix = 16 */ +#define BF_ATOF_NO_HEX (1 << 16) +/* accept binary (0b or 0B) or octal (0o or 0O) radix prefix if radix = 0 */ +#define BF_ATOF_BIN_OCT (1 << 17) +/* Do not parse NaN or Inf */ +#define BF_ATOF_NO_NAN_INF (1 << 18) +/* return the exponent separately */ +#define BF_ATOF_EXPONENT (1 << 19) + +int bf_atof(bf_t *a, const char *str, const char **pnext, int radix, + limb_t prec, bf_flags_t flags); +/* this version accepts prec = BF_PREC_INF and returns the radix + exponent */ +int bf_atof2(bf_t *r, slimb_t *pexponent, + const char *str, const char **pnext, int radix, + limb_t prec, bf_flags_t flags); +int bf_mul_pow_radix(bf_t *r, const bf_t *T, limb_t radix, + slimb_t expn, limb_t prec, bf_flags_t flags); + + +/* Conversion of floating point number to string. Return a null + terminated string or NULL if memory error. *plen contains its + length if plen != NULL. The exponent letter is "e" for base 10, + "p" for bases 2, 8, 16 with a binary exponent and "@" for the other + bases. */ + +#define BF_FTOA_FORMAT_MASK (3 << 16) + +/* fixed format: prec significant digits rounded with (flags & + BF_RND_MASK). Exponential notation is used if too many zeros are + needed.*/ +#define BF_FTOA_FORMAT_FIXED (0 << 16) +/* fractional format: prec digits after the decimal point rounded with + (flags & BF_RND_MASK) */ +#define BF_FTOA_FORMAT_FRAC (1 << 16) +/* free format: + + For binary radices with bf_ftoa() and for bfdec_ftoa(): use the minimum + number of digits to represent 'a'. The precision and the rounding + mode are ignored. + + For the non binary radices with bf_ftoa(): use as many digits as + necessary so that bf_atof() return the same number when using + precision 'prec', rounding to nearest and the subnormal + configuration of 'flags'. The result is meaningful only if 'a' is + already rounded to 'prec' bits. If the subnormal flag is set, the + exponent in 'flags' must also be set to the desired exponent range. +*/ +#define BF_FTOA_FORMAT_FREE (2 << 16) +/* same as BF_FTOA_FORMAT_FREE but uses the minimum number of digits + (takes more computation time). Identical to BF_FTOA_FORMAT_FREE for + binary radices with bf_ftoa() and for bfdec_ftoa(). */ +#define BF_FTOA_FORMAT_FREE_MIN (3 << 16) + +/* force exponential notation for fixed or free format */ +#define BF_FTOA_FORCE_EXP (1 << 20) +/* add 0x prefix for base 16, 0o prefix for base 8 or 0b prefix for + base 2 if non zero value */ +#define BF_FTOA_ADD_PREFIX (1 << 21) +/* return "Infinity" instead of "Inf" and add a "+" for positive + exponents */ +#define BF_FTOA_JS_QUIRKS (1 << 22) + +char *bf_ftoa(size_t *plen, const bf_t *a, int radix, limb_t prec, + bf_flags_t flags); + +/* modulo 2^n instead of saturation. NaN and infinity return 0 */ +#define BF_GET_INT_MOD (1 << 0) +int bf_get_int32(int *pres, const bf_t *a, int flags); +int bf_get_int64(int64_t *pres, const bf_t *a, int flags); +int bf_get_uint64(uint64_t *pres, const bf_t *a); + +/* the following functions are exported for testing only. */ +void mp_print_str(const char *str, const limb_t *tab, limb_t n); +void bf_print_str(const char *str, const bf_t *a); +int bf_resize(bf_t *r, limb_t len); +int bf_get_fft_size(int *pdpl, int *pnb_mods, limb_t len); +int bf_normalize_and_round(bf_t *r, limb_t prec1, bf_flags_t flags); +int bf_can_round(const bf_t *a, slimb_t prec, bf_rnd_t rnd_mode, slimb_t k); +slimb_t bf_mul_log2_radix(slimb_t a1, unsigned int radix, int is_inv, + int is_ceil1); +int mp_mul(bf_context_t *s, limb_t *result, + const limb_t *op1, limb_t op1_size, + const limb_t *op2, limb_t op2_size); +limb_t mp_add(limb_t *res, const limb_t *op1, const limb_t *op2, + limb_t n, limb_t carry); +limb_t mp_add_ui(limb_t *tab, limb_t b, size_t n); +int mp_sqrtrem(bf_context_t *s, limb_t *tabs, limb_t *taba, limb_t n); +int mp_recip(bf_context_t *s, limb_t *tabr, const limb_t *taba, limb_t n); +limb_t bf_isqrt(limb_t a); + +/* transcendental functions */ +int bf_const_log2(bf_t *T, limb_t prec, bf_flags_t flags); +int bf_const_pi(bf_t *T, limb_t prec, bf_flags_t flags); +int bf_exp(bf_t *r, const bf_t *a, limb_t prec, bf_flags_t flags); +int bf_log(bf_t *r, const bf_t *a, limb_t prec, bf_flags_t flags); +#define BF_POW_JS_QUIRKS (1 << 16) /* (+/-1)^(+/-Inf) = NaN, 1^NaN = NaN */ +int bf_pow(bf_t *r, const bf_t *x, const bf_t *y, limb_t prec, bf_flags_t flags); +int bf_cos(bf_t *r, const bf_t *a, limb_t prec, bf_flags_t flags); +int bf_sin(bf_t *r, const bf_t *a, limb_t prec, bf_flags_t flags); +int bf_tan(bf_t *r, const bf_t *a, limb_t prec, bf_flags_t flags); +int bf_atan(bf_t *r, const bf_t *a, limb_t prec, bf_flags_t flags); +int bf_atan2(bf_t *r, const bf_t *y, const bf_t *x, + limb_t prec, bf_flags_t flags); +int bf_asin(bf_t *r, const bf_t *a, limb_t prec, bf_flags_t flags); +int bf_acos(bf_t *r, const bf_t *a, limb_t prec, bf_flags_t flags); + +/* decimal floating point */ + +static inline void bfdec_init(bf_context_t *s, bfdec_t *r) +{ + bf_init(s, (bf_t *)r); +} +static inline void bfdec_delete(bfdec_t *r) +{ + bf_delete((bf_t *)r); +} + +static inline void bfdec_neg(bfdec_t *r) +{ + r->sign ^= 1; +} + +static inline int bfdec_is_finite(const bfdec_t *a) +{ + return (a->expn < BF_EXP_INF); +} + +static inline int bfdec_is_nan(const bfdec_t *a) +{ + return (a->expn == BF_EXP_NAN); +} + +static inline int bfdec_is_zero(const bfdec_t *a) +{ + return (a->expn == BF_EXP_ZERO); +} + +static inline void bfdec_memcpy(bfdec_t *r, const bfdec_t *a) +{ + bf_memcpy((bf_t *)r, (const bf_t *)a); +} + +int bfdec_set_ui(bfdec_t *r, uint64_t a); +int bfdec_set_si(bfdec_t *r, int64_t a); + +static inline void bfdec_set_nan(bfdec_t *r) +{ + bf_set_nan((bf_t *)r); +} +static inline void bfdec_set_zero(bfdec_t *r, int is_neg) +{ + bf_set_zero((bf_t *)r, is_neg); +} +static inline void bfdec_set_inf(bfdec_t *r, int is_neg) +{ + bf_set_inf((bf_t *)r, is_neg); +} +static inline int bfdec_set(bfdec_t *r, const bfdec_t *a) +{ + return bf_set((bf_t *)r, (bf_t *)a); +} +static inline void bfdec_move(bfdec_t *r, bfdec_t *a) +{ + bf_move((bf_t *)r, (bf_t *)a); +} +static inline int bfdec_cmpu(const bfdec_t *a, const bfdec_t *b) +{ + return bf_cmpu((const bf_t *)a, (const bf_t *)b); +} +static inline int bfdec_cmp_full(const bfdec_t *a, const bfdec_t *b) +{ + return bf_cmp_full((const bf_t *)a, (const bf_t *)b); +} +static inline int bfdec_cmp(const bfdec_t *a, const bfdec_t *b) +{ + return bf_cmp((const bf_t *)a, (const bf_t *)b); +} +static inline int bfdec_cmp_eq(const bfdec_t *a, const bfdec_t *b) +{ + return bfdec_cmp(a, b) == 0; +} +static inline int bfdec_cmp_le(const bfdec_t *a, const bfdec_t *b) +{ + return bfdec_cmp(a, b) <= 0; +} +static inline int bfdec_cmp_lt(const bfdec_t *a, const bfdec_t *b) +{ + return bfdec_cmp(a, b) < 0; +} + +int bfdec_add(bfdec_t *r, const bfdec_t *a, const bfdec_t *b, limb_t prec, + bf_flags_t flags); +int bfdec_sub(bfdec_t *r, const bfdec_t *a, const bfdec_t *b, limb_t prec, + bf_flags_t flags); +int bfdec_add_si(bfdec_t *r, const bfdec_t *a, int64_t b1, limb_t prec, + bf_flags_t flags); +int bfdec_mul(bfdec_t *r, const bfdec_t *a, const bfdec_t *b, limb_t prec, + bf_flags_t flags); +int bfdec_mul_si(bfdec_t *r, const bfdec_t *a, int64_t b1, limb_t prec, + bf_flags_t flags); +int bfdec_div(bfdec_t *r, const bfdec_t *a, const bfdec_t *b, limb_t prec, + bf_flags_t flags); +int bfdec_divrem(bfdec_t *q, bfdec_t *r, const bfdec_t *a, const bfdec_t *b, + limb_t prec, bf_flags_t flags, int rnd_mode); +int bfdec_rem(bfdec_t *r, const bfdec_t *a, const bfdec_t *b, limb_t prec, + bf_flags_t flags, int rnd_mode); +int bfdec_rint(bfdec_t *r, int rnd_mode); +int bfdec_sqrt(bfdec_t *r, const bfdec_t *a, limb_t prec, bf_flags_t flags); +int bfdec_round(bfdec_t *r, limb_t prec, bf_flags_t flags); +int bfdec_get_int32(int *pres, const bfdec_t *a); +int bfdec_pow_ui(bfdec_t *r, const bfdec_t *a, limb_t b); + +char *bfdec_ftoa(size_t *plen, const bfdec_t *a, limb_t prec, bf_flags_t flags); +int bfdec_atof(bfdec_t *r, const char *str, const char **pnext, + limb_t prec, bf_flags_t flags); + +/* the following functions are exported for testing only. */ +extern const limb_t mp_pow_dec[LIMB_DIGITS + 1]; +void bfdec_print_str(const char *str, const bfdec_t *a); +static inline int bfdec_resize(bfdec_t *r, limb_t len) +{ + return bf_resize((bf_t *)r, len); +} +int bfdec_normalize_and_round(bfdec_t *r, limb_t prec1, bf_flags_t flags); + +#ifdef __cplusplus +} /* extern "C" { */ +#endif + +#endif /* LIBBF_H */ diff --git a/third_party/quickjs/libregexp-opcode.h b/third_party/quickjs/libregexp-opcode.h new file mode 100644 index 0000000000..5c1714ab0c --- /dev/null +++ b/third_party/quickjs/libregexp-opcode.h @@ -0,0 +1,58 @@ +/* + * Regular Expression Engine + * + * Copyright (c) 2017-2018 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifdef DEF + +DEF(invalid, 1) /* never used */ +DEF(char8, 2) /* 7 bits in fact */ +DEF(char16, 3) +DEF(char32, 5) +DEF(dot, 1) +DEF(any, 1) /* same as dot but match any character including line terminator */ +DEF(line_start, 1) +DEF(line_end, 1) +DEF(goto, 5) +DEF(split_goto_first, 5) +DEF(split_next_first, 5) +DEF(match, 1) +DEF(save_start, 2) /* save start position */ +DEF(save_end, 2) /* save end position, must come after saved_start */ +DEF(save_reset, 3) /* reset save positions */ +DEF(loop, 5) /* decrement the top the stack and goto if != 0 */ +DEF(push_i32, 5) /* push integer on the stack */ +DEF(drop, 1) +DEF(word_boundary, 1) +DEF(not_word_boundary, 1) +DEF(back_reference, 2) +DEF(backward_back_reference, 2) /* must come after back_reference */ +DEF(range, 3) /* variable length */ +DEF(range32, 3) /* variable length */ +DEF(lookahead, 5) +DEF(negative_lookahead, 5) +DEF(push_char_pos, 1) /* push the character position on the stack */ +DEF(check_advance, 1) /* pop one stack element and check that it is different from the character position */ +DEF(prev, 1) /* go to the previous char */ +DEF(simple_greedy_quant, 17) + +#endif /* DEF */ diff --git a/third_party/quickjs/libregexp.c b/third_party/quickjs/libregexp.c new file mode 100644 index 0000000000..799c8f640f --- /dev/null +++ b/third_party/quickjs/libregexp.c @@ -0,0 +1,2640 @@ +/* + * Regular Expression Engine + * + * Copyright (c) 2017-2018 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include <stdlib.h> +#include <stdio.h> +#include <stdarg.h> +#include <inttypes.h> +#include <string.h> +#include <assert.h> + +#include "cutils.h" +#include "libregexp.h" + +/* + TODO: + + - Add a lock step execution mode (=linear time execution guaranteed) + when the regular expression is "simple" i.e. no backreference nor + complicated lookahead. The opcodes are designed for this execution + model. +*/ + +#if defined(TEST) +#define DUMP_REOP +#endif + +typedef enum { +#define DEF(id, size) REOP_ ## id, +#include "libregexp-opcode.h" +#undef DEF + REOP_COUNT, +} REOPCodeEnum; + +#define CAPTURE_COUNT_MAX 255 +#define STACK_SIZE_MAX 255 + +/* unicode code points */ +#define CP_LS 0x2028 +#define CP_PS 0x2029 + +#define TMP_BUF_SIZE 128 + +// invariant: is_unicode ^ unicode_sets (or neither, but not both) +typedef struct { + DynBuf byte_code; + const uint8_t *buf_ptr; + const uint8_t *buf_end; + const uint8_t *buf_start; + int re_flags; + BOOL is_unicode; + BOOL unicode_sets; + BOOL ignore_case; + BOOL dotall; + int capture_count; + int total_capture_count; /* -1 = not computed yet */ + int has_named_captures; /* -1 = don't know, 0 = no, 1 = yes */ + void *opaque; + DynBuf group_names; + union { + char error_msg[TMP_BUF_SIZE]; + char tmp_buf[TMP_BUF_SIZE]; + } u; +} REParseState; + +typedef struct { +#ifdef DUMP_REOP + const char *name; +#endif + uint8_t size; +} REOpCode; + +static const REOpCode reopcode_info[REOP_COUNT] = { +#ifdef DUMP_REOP +#define DEF(id, size) { #id, size }, +#else +#define DEF(id, size) { size }, +#endif +#include "libregexp-opcode.h" +#undef DEF +}; + +#define RE_HEADER_FLAGS 0 +#define RE_HEADER_CAPTURE_COUNT 2 +#define RE_HEADER_STACK_SIZE 3 +#define RE_HEADER_BYTECODE_LEN 4 + +#define RE_HEADER_LEN 8 + +static inline int lre_is_digit(int c) { + return c >= '0' && c <= '9'; +} + +/* insert 'len' bytes at position 'pos'. Return < 0 if error. */ +static int dbuf_insert(DynBuf *s, int pos, int len) +{ + if (dbuf_realloc(s, s->size + len)) + return -1; + memmove(s->buf + pos + len, s->buf + pos, s->size - pos); + s->size += len; + return 0; +} + +static const uint16_t char_range_d[] = { + 1, + 0x0030, 0x0039 + 1, +}; + +/* code point ranges for Zs,Zl or Zp property */ +static const uint16_t char_range_s[] = { + 10, + 0x0009, 0x000D + 1, + 0x0020, 0x0020 + 1, + 0x00A0, 0x00A0 + 1, + 0x1680, 0x1680 + 1, + 0x2000, 0x200A + 1, + /* 2028;LINE SEPARATOR;Zl;0;WS;;;;;N;;;;; */ + /* 2029;PARAGRAPH SEPARATOR;Zp;0;B;;;;;N;;;;; */ + 0x2028, 0x2029 + 1, + 0x202F, 0x202F + 1, + 0x205F, 0x205F + 1, + 0x3000, 0x3000 + 1, + /* FEFF;ZERO WIDTH NO-BREAK SPACE;Cf;0;BN;;;;;N;BYTE ORDER MARK;;;; */ + 0xFEFF, 0xFEFF + 1, +}; + +BOOL lre_is_space(int c) +{ + int i, n, low, high; + n = (countof(char_range_s) - 1) / 2; + for(i = 0; i < n; i++) { + low = char_range_s[2 * i + 1]; + if (c < low) + return FALSE; + high = char_range_s[2 * i + 2]; + if (c < high) + return TRUE; + } + return FALSE; +} + +uint32_t const lre_id_start_table_ascii[4] = { + /* $ A-Z _ a-z */ + 0x00000000, 0x00000010, 0x87FFFFFE, 0x07FFFFFE +}; + +uint32_t const lre_id_continue_table_ascii[4] = { + /* $ 0-9 A-Z _ a-z */ + 0x00000000, 0x03FF0010, 0x87FFFFFE, 0x07FFFFFE +}; + + +static const uint16_t char_range_w[] = { + 4, + 0x0030, 0x0039 + 1, + 0x0041, 0x005A + 1, + 0x005F, 0x005F + 1, + 0x0061, 0x007A + 1, +}; + +#define CLASS_RANGE_BASE 0x40000000 + +typedef enum { + CHAR_RANGE_d, + CHAR_RANGE_D, + CHAR_RANGE_s, + CHAR_RANGE_S, + CHAR_RANGE_w, + CHAR_RANGE_W, +} CharRangeEnum; + +static const uint16_t *char_range_table[] = { + char_range_d, + char_range_s, + char_range_w, +}; + +static int cr_init_char_range(REParseState *s, CharRange *cr, uint32_t c) +{ + BOOL invert; + const uint16_t *c_pt; + int len, i; + + invert = c & 1; + c_pt = char_range_table[c >> 1]; + len = *c_pt++; + cr_init(cr, s->opaque, lre_realloc); + for(i = 0; i < len * 2; i++) { + if (cr_add_point(cr, c_pt[i])) + goto fail; + } + if (invert) { + if (cr_invert(cr)) + goto fail; + } + return 0; + fail: + cr_free(cr); + return -1; +} + +#ifdef DUMP_REOP +static __maybe_unused void lre_dump_bytecode(const uint8_t *buf, + int buf_len) +{ + int pos, len, opcode, bc_len, re_flags, i; + uint32_t val; + + assert(buf_len >= RE_HEADER_LEN); + + re_flags = lre_get_flags(buf); + bc_len = get_u32(buf + RE_HEADER_BYTECODE_LEN); + assert(bc_len + RE_HEADER_LEN <= buf_len); + printf("flags: 0x%x capture_count=%d stack_size=%d\n", + re_flags, buf[RE_HEADER_CAPTURE_COUNT], buf[RE_HEADER_STACK_SIZE]); + if (re_flags & LRE_FLAG_NAMED_GROUPS) { + const char *p; + p = (char *)buf + RE_HEADER_LEN + bc_len; + printf("named groups: "); + for(i = 1; i < buf[RE_HEADER_CAPTURE_COUNT]; i++) { + if (i != 1) + printf(","); + printf("<%s>", p); + p += strlen(p) + 1; + } + printf("\n"); + assert(p == (char *)(buf + buf_len)); + } + printf("bytecode_len=%d\n", bc_len); + + buf += RE_HEADER_LEN; + pos = 0; + while (pos < bc_len) { + printf("%5u: ", pos); + opcode = buf[pos]; + len = reopcode_info[opcode].size; + if (opcode >= REOP_COUNT) { + printf(" invalid opcode=0x%02x\n", opcode); + break; + } + if ((pos + len) > bc_len) { + printf(" buffer overflow (opcode=0x%02x)\n", opcode); + break; + } + printf("%s", reopcode_info[opcode].name); + switch(opcode) { + case REOP_char8: + val = get_u8(buf + pos + 1); + goto printchar; + case REOP_char16: + val = get_u16(buf + pos + 1); + goto printchar; + case REOP_char32: + val = get_u32(buf + pos + 1); + printchar: + if (val >= ' ' && val <= 126) + printf(" '%c'", val); + else + printf(" 0x%08x", val); + break; + case REOP_goto: + case REOP_split_goto_first: + case REOP_split_next_first: + case REOP_loop: + case REOP_lookahead: + case REOP_negative_lookahead: + val = get_u32(buf + pos + 1); + val += (pos + 5); + printf(" %u", val); + break; + case REOP_simple_greedy_quant: + printf(" %u %u %u %u", + get_u32(buf + pos + 1) + (pos + 17), + get_u32(buf + pos + 1 + 4), + get_u32(buf + pos + 1 + 8), + get_u32(buf + pos + 1 + 12)); + break; + case REOP_save_start: + case REOP_save_end: + case REOP_back_reference: + case REOP_backward_back_reference: + printf(" %u", buf[pos + 1]); + break; + case REOP_save_reset: + printf(" %u %u", buf[pos + 1], buf[pos + 2]); + break; + case REOP_push_i32: + val = get_u32(buf + pos + 1); + printf(" %d", val); + break; + case REOP_range: + { + int n, i; + n = get_u16(buf + pos + 1); + len += n * 4; + for(i = 0; i < n * 2; i++) { + val = get_u16(buf + pos + 3 + i * 2); + printf(" 0x%04x", val); + } + } + break; + case REOP_range32: + { + int n, i; + n = get_u16(buf + pos + 1); + len += n * 8; + for(i = 0; i < n * 2; i++) { + val = get_u32(buf + pos + 3 + i * 4); + printf(" 0x%08x", val); + } + } + break; + default: + break; + } + printf("\n"); + pos += len; + } +} +#endif + +static void re_emit_op(REParseState *s, int op) +{ + dbuf_putc(&s->byte_code, op); +} + +/* return the offset of the u32 value */ +static int re_emit_op_u32(REParseState *s, int op, uint32_t val) +{ + int pos; + dbuf_putc(&s->byte_code, op); + pos = s->byte_code.size; + dbuf_put_u32(&s->byte_code, val); + return pos; +} + +static int re_emit_goto(REParseState *s, int op, uint32_t val) +{ + int pos; + dbuf_putc(&s->byte_code, op); + pos = s->byte_code.size; + dbuf_put_u32(&s->byte_code, val - (pos + 4)); + return pos; +} + +static void re_emit_op_u8(REParseState *s, int op, uint32_t val) +{ + dbuf_putc(&s->byte_code, op); + dbuf_putc(&s->byte_code, val); +} + +static void re_emit_op_u16(REParseState *s, int op, uint32_t val) +{ + dbuf_putc(&s->byte_code, op); + dbuf_put_u16(&s->byte_code, val); +} + +static int __attribute__((format(printf, 2, 3))) re_parse_error(REParseState *s, const char *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + vsnprintf(s->u.error_msg, sizeof(s->u.error_msg), fmt, ap); + va_end(ap); + return -1; +} + +static int re_parse_out_of_memory(REParseState *s) +{ + return re_parse_error(s, "out of memory"); +} + +/* If allow_overflow is false, return -1 in case of + overflow. Otherwise return INT32_MAX. */ +static int parse_digits(const uint8_t **pp, BOOL allow_overflow) +{ + const uint8_t *p; + uint64_t v; + int c; + + p = *pp; + v = 0; + for(;;) { + c = *p; + if (c < '0' || c > '9') + break; + v = v * 10 + c - '0'; + if (v >= INT32_MAX) { + if (allow_overflow) + v = INT32_MAX; + else + return -1; + } + p++; + } + *pp = p; + return v; +} + +static int re_parse_expect(REParseState *s, const uint8_t **pp, int c) +{ + const uint8_t *p; + p = *pp; + if (*p != c) + return re_parse_error(s, "expecting '%c'", c); + p++; + *pp = p; + return 0; +} + +/* Parse an escape sequence, *pp points after the '\': + allow_utf16 value: + 0 : no UTF-16 escapes allowed + 1 : UTF-16 escapes allowed + 2 : UTF-16 escapes allowed and escapes of surrogate pairs are + converted to a unicode character (unicode regexp case). + + Return the unicode char and update *pp if recognized, + return -1 if malformed escape, + return -2 otherwise. */ +int lre_parse_escape(const uint8_t **pp, int allow_utf16) +{ + const uint8_t *p; + uint32_t c; + + p = *pp; + c = *p++; + switch(c) { + case 'b': + c = '\b'; + break; + case 'f': + c = '\f'; + break; + case 'n': + c = '\n'; + break; + case 'r': + c = '\r'; + break; + case 't': + c = '\t'; + break; + case 'v': + c = '\v'; + break; + case 'x': + case 'u': + { + int h, n, i; + uint32_t c1; + + if (*p == '{' && allow_utf16) { + p++; + c = 0; + for(;;) { + h = from_hex(*p++); + if (h < 0) + return -1; + c = (c << 4) | h; + if (c > 0x10FFFF) + return -1; + if (*p == '}') + break; + } + p++; + } else { + if (c == 'x') { + n = 2; + } else { + n = 4; + } + + c = 0; + for(i = 0; i < n; i++) { + h = from_hex(*p++); + if (h < 0) { + return -1; + } + c = (c << 4) | h; + } + if (is_hi_surrogate(c) && + allow_utf16 == 2 && p[0] == '\\' && p[1] == 'u') { + /* convert an escaped surrogate pair into a + unicode char */ + c1 = 0; + for(i = 0; i < 4; i++) { + h = from_hex(p[2 + i]); + if (h < 0) + break; + c1 = (c1 << 4) | h; + } + if (i == 4 && is_lo_surrogate(c1)) { + p += 6; + c = from_surrogate(c, c1); + } + } + } + } + break; + case '0': case '1': case '2': case '3': + case '4': case '5': case '6': case '7': + c -= '0'; + if (allow_utf16 == 2) { + /* only accept \0 not followed by digit */ + if (c != 0 || lre_is_digit(*p)) + return -1; + } else { + /* parse a legacy octal sequence */ + uint32_t v; + v = *p - '0'; + if (v > 7) + break; + c = (c << 3) | v; + p++; + if (c >= 32) + break; + v = *p - '0'; + if (v > 7) + break; + c = (c << 3) | v; + p++; + } + break; + default: + return -2; + } + *pp = p; + return c; +} + +/* XXX: we use the same chars for name and value */ +static BOOL is_unicode_char(int c) +{ + return ((c >= '0' && c <= '9') || + (c >= 'A' && c <= 'Z') || + (c >= 'a' && c <= 'z') || + (c == '_')); +} + +static int parse_unicode_property(REParseState *s, CharRange *cr, + const uint8_t **pp, BOOL is_inv) +{ + const uint8_t *p; + char name[64], value[64]; + char *q; + BOOL script_ext; + int ret; + + p = *pp; + if (*p != '{') + return re_parse_error(s, "expecting '{' after \\p"); + p++; + q = name; + while (is_unicode_char(*p)) { + if ((q - name) >= sizeof(name) - 1) + goto unknown_property_name; + *q++ = *p++; + } + *q = '\0'; + q = value; + if (*p == '=') { + p++; + while (is_unicode_char(*p)) { + if ((q - value) >= sizeof(value) - 1) + return re_parse_error(s, "unknown unicode property value"); + *q++ = *p++; + } + } + *q = '\0'; + if (*p != '}') + return re_parse_error(s, "expecting '}'"); + p++; + // printf("name=%s value=%s\n", name, value); + + if (!strcmp(name, "Script") || !strcmp(name, "sc")) { + script_ext = FALSE; + goto do_script; + } else if (!strcmp(name, "Script_Extensions") || !strcmp(name, "scx")) { + script_ext = TRUE; + do_script: + cr_init(cr, s->opaque, lre_realloc); + ret = unicode_script(cr, value, script_ext); + if (ret) { + cr_free(cr); + if (ret == -2) + return re_parse_error(s, "unknown unicode script"); + else + goto out_of_memory; + } + } else if (!strcmp(name, "General_Category") || !strcmp(name, "gc")) { + cr_init(cr, s->opaque, lre_realloc); + ret = unicode_general_category(cr, value); + if (ret) { + cr_free(cr); + if (ret == -2) + return re_parse_error(s, "unknown unicode general category"); + else + goto out_of_memory; + } + } else if (value[0] == '\0') { + cr_init(cr, s->opaque, lre_realloc); + ret = unicode_general_category(cr, name); + if (ret == -1) { + cr_free(cr); + goto out_of_memory; + } + if (ret < 0) { + ret = unicode_prop(cr, name); + if (ret) { + cr_free(cr); + if (ret == -2) + goto unknown_property_name; + else + goto out_of_memory; + } + } + } else { + unknown_property_name: + return re_parse_error(s, "unknown unicode property name"); + } + + if (is_inv) { + if (cr_invert(cr)) { + cr_free(cr); + return -1; + } + } + *pp = p; + return 0; + out_of_memory: + return re_parse_out_of_memory(s); +} + +/* return -1 if error otherwise the character or a class range + (CLASS_RANGE_BASE). In case of class range, 'cr' is + initialized. Otherwise, it is ignored. */ +static int get_class_atom(REParseState *s, CharRange *cr, + const uint8_t **pp, BOOL inclass) +{ + const uint8_t *p, *p_next; + uint32_t c; + int ret; + + p = *pp; + + c = *p; + switch(c) { + case '\\': + p++; + if (p >= s->buf_end) + goto unexpected_end; + c = *p++; + switch(c) { + case 'd': + c = CHAR_RANGE_d; + goto class_range; + case 'D': + c = CHAR_RANGE_D; + goto class_range; + case 's': + c = CHAR_RANGE_s; + goto class_range; + case 'S': + c = CHAR_RANGE_S; + goto class_range; + case 'w': + c = CHAR_RANGE_w; + goto class_range; + case 'W': + c = CHAR_RANGE_W; + class_range: + if (cr_init_char_range(s, cr, c)) + return -1; + c = CLASS_RANGE_BASE; + break; + case 'c': + c = *p; + if ((c >= 'a' && c <= 'z') || + (c >= 'A' && c <= 'Z') || + (((c >= '0' && c <= '9') || c == '_') && + inclass && !s->is_unicode)) { /* Annex B.1.4 */ + c &= 0x1f; + p++; + } else if (s->is_unicode) { + goto invalid_escape; + } else { + /* otherwise return '\' and 'c' */ + p--; + c = '\\'; + } + break; + case 'p': + case 'P': + if (s->is_unicode) { + if (parse_unicode_property(s, cr, &p, (c == 'P'))) + return -1; + c = CLASS_RANGE_BASE; + break; + } + /* fall thru */ + default: + p--; + ret = lre_parse_escape(&p, s->is_unicode * 2); + if (ret >= 0) { + c = ret; + } else { + if (ret == -2 && *p != '\0' && strchr("^$\\.*+?()[]{}|/", *p)) { + /* always valid to escape these characters */ + goto normal_char; + } else if (s->is_unicode) { + // special case: allowed inside [] but not outside + if (ret == -2 && *p == '-' && inclass) + goto normal_char; + invalid_escape: + return re_parse_error(s, "invalid escape sequence in regular expression"); + } else { + /* just ignore the '\' */ + goto normal_char; + } + } + break; + } + break; + case '\0': + if (p >= s->buf_end) { + unexpected_end: + return re_parse_error(s, "unexpected end"); + } + /* fall thru */ + default: + normal_char: + p++; + if (c >= 0x80) { + c = utf8_decode(p - 1, &p_next); + if (p_next == p) + return re_parse_error(s, "invalid UTF-8 sequence"); + p = p_next; + if (c > 0xFFFF && !s->is_unicode) { + // TODO(chqrlie): should handle non BMP-1 code points in + // the calling function and no require the source string + // to be CESU-8 encoded if not s->is_unicode + return re_parse_error(s, "malformed unicode char"); + } + } + break; + } + *pp = p; + return c; +} + +static int re_emit_range(REParseState *s, const CharRange *cr) +{ + int len, i; + uint32_t high; + + len = (unsigned)cr->len / 2; + if (len >= 65535) + return re_parse_error(s, "too many ranges"); + if (len == 0) { + /* not sure it can really happen. Emit a match that is always + false */ + re_emit_op_u32(s, REOP_char32, -1); + } else { + high = cr->points[cr->len - 1]; + if (high == UINT32_MAX) + high = cr->points[cr->len - 2]; + if (high <= 0xffff) { + /* can use 16 bit ranges with the conversion that 0xffff = + infinity */ + re_emit_op_u16(s, REOP_range, len); + for(i = 0; i < cr->len; i += 2) { + dbuf_put_u16(&s->byte_code, cr->points[i]); + high = cr->points[i + 1] - 1; + if (high == UINT32_MAX - 1) + high = 0xffff; + dbuf_put_u16(&s->byte_code, high); + } + } else { + re_emit_op_u16(s, REOP_range32, len); + for(i = 0; i < cr->len; i += 2) { + dbuf_put_u32(&s->byte_code, cr->points[i]); + dbuf_put_u32(&s->byte_code, cr->points[i + 1] - 1); + } + } + } + return 0; +} + +// s->unicode turns patterns like []] into syntax errors +// s->unicode_sets turns more patterns into errors, like [a-] or [[] +static int re_parse_char_class(REParseState *s, const uint8_t **pp) +{ + const uint8_t *p; + uint32_t c1, c2; + CharRange cr_s, *cr = &cr_s; + CharRange cr1_s, *cr1 = &cr1_s; + BOOL invert; + + cr_init(cr, s->opaque, lre_realloc); + p = *pp; + p++; /* skip '[' */ + + if (s->unicode_sets) { + static const char verboten[] = + "()[{}/-|" "\0" + "&&!!##$$%%**++,,..::;;<<==>>??@@``~~" "\0" + "^^^_^^"; + const char *s = verboten; + int n = 1; + do { + if (!memcmp(s, p, n)) + if (p[n] == ']') + goto invalid_class_range; + s += n; + if (!*s) { + s++; + n++; + } + } while (n < 4); + } + + invert = FALSE; + if (*p == '^') { + p++; + invert = TRUE; + } + + for(;;) { + if (*p == ']') + break; + c1 = get_class_atom(s, cr1, &p, TRUE); + if ((int)c1 < 0) + goto fail; + if (*p == '-' && p[1] == ']' && s->unicode_sets) { + if (c1 >= CLASS_RANGE_BASE) + cr_free(cr1); + goto invalid_class_range; + } + if (*p == '-' && p[1] != ']') { + const uint8_t *p0 = p + 1; + if (c1 >= CLASS_RANGE_BASE) { + if (s->is_unicode) { + cr_free(cr1); + goto invalid_class_range; + } + /* Annex B: match '-' character */ + goto class_atom; + } + c2 = get_class_atom(s, cr1, &p0, TRUE); + if ((int)c2 < 0) + goto fail; + if (c2 >= CLASS_RANGE_BASE) { + cr_free(cr1); + if (s->is_unicode) { + goto invalid_class_range; + } + /* Annex B: match '-' character */ + goto class_atom; + } + p = p0; + if (c2 < c1) { + invalid_class_range: + re_parse_error(s, "invalid class range"); + goto fail; + } + if (cr_union_interval(cr, c1, c2)) + goto memory_error; + } else { + class_atom: + if (c1 >= CLASS_RANGE_BASE) { + int ret; + ret = cr_union1(cr, cr1->points, cr1->len); + cr_free(cr1); + if (ret) + goto memory_error; + } else { + if (cr_union_interval(cr, c1, c1)) + goto memory_error; + } + } + } + if (s->ignore_case) { + if (cr_regexp_canonicalize(cr, s->is_unicode)) + goto memory_error; + } + if (invert) { + if (cr_invert(cr)) + goto memory_error; + } + if (re_emit_range(s, cr)) + goto fail; + cr_free(cr); + p++; /* skip ']' */ + *pp = p; + return 0; + memory_error: + re_parse_out_of_memory(s); + fail: + cr_free(cr); + return -1; +} + +/* Return: + - true if the opcodes may not advance the char pointer + - false if the opcodes always advance the char pointer +*/ +static BOOL re_need_check_advance(const uint8_t *bc_buf, int bc_buf_len) +{ + int pos, opcode, len; + uint32_t val; + BOOL ret; + + ret = TRUE; + pos = 0; + + while (pos < bc_buf_len) { + opcode = bc_buf[pos]; + len = reopcode_info[opcode].size; + switch(opcode) { + case REOP_range: + val = get_u16(bc_buf + pos + 1); + len += val * 4; + goto simple_char; + case REOP_range32: + val = get_u16(bc_buf + pos + 1); + len += val * 8; + goto simple_char; + case REOP_char32: + case REOP_char16: + case REOP_char8: + case REOP_dot: + case REOP_any: + simple_char: + ret = FALSE; + break; + case REOP_line_start: + case REOP_line_end: + case REOP_push_i32: + case REOP_push_char_pos: + case REOP_drop: + case REOP_word_boundary: + case REOP_not_word_boundary: + case REOP_prev: + /* no effect */ + break; + case REOP_save_start: + case REOP_save_end: + case REOP_save_reset: + case REOP_back_reference: + case REOP_backward_back_reference: + break; + default: + /* safe behvior: we cannot predict the outcome */ + return TRUE; + } + pos += len; + } + return ret; +} + +/* return -1 if a simple quantifier cannot be used. Otherwise return + the number of characters in the atom. */ +static int re_is_simple_quantifier(const uint8_t *bc_buf, int bc_buf_len) +{ + int pos, opcode, len, count; + uint32_t val; + + count = 0; + pos = 0; + while (pos < bc_buf_len) { + opcode = bc_buf[pos]; + len = reopcode_info[opcode].size; + switch(opcode) { + case REOP_range: + val = get_u16(bc_buf + pos + 1); + len += val * 4; + goto simple_char; + case REOP_range32: + val = get_u16(bc_buf + pos + 1); + len += val * 8; + goto simple_char; + case REOP_char32: + case REOP_char16: + case REOP_char8: + case REOP_dot: + case REOP_any: + simple_char: + count++; + break; + case REOP_line_start: + case REOP_line_end: + case REOP_word_boundary: + case REOP_not_word_boundary: + break; + default: + return -1; + } + pos += len; + } + return count; +} + +/* '*pp' is the first char after '<' */ +static int re_parse_group_name(char *buf, int buf_size, const uint8_t **pp) +{ + const uint8_t *p, *p_next; + uint32_t c, d; + char *q; + + p = *pp; + q = buf; + for(;;) { + c = *p++; + if (c == '\\') { + if (*p != 'u') + return -1; + c = lre_parse_escape(&p, 2); // accept surrogate pairs + if ((int)c < 0) + return -1; + } else if (c == '>') { + break; + } else if (c >= 0x80) { + c = utf8_decode(p - 1, &p_next); + if (p_next == p) + return -1; + p = p_next; + if (is_hi_surrogate(c)) { + d = utf8_decode(p, &p_next); + if (is_lo_surrogate(d)) { + c = from_surrogate(c, d); + p = p_next; + } + } + } + if (q == buf) { + if (!lre_js_is_ident_first(c)) + return -1; + } else { + if (!lre_js_is_ident_next(c)) + return -1; + } + if ((q - buf + UTF8_CHAR_LEN_MAX + 1) > buf_size) + return -1; + if (c < 0x80) { + *q++ = c; + } else { + q += utf8_encode((uint8_t*)q, c); + } + } + if (q == buf) + return -1; + *q = '\0'; + *pp = p; + return 0; +} + +/* if capture_name = NULL: return the number of captures + 1. + Otherwise, return the capture index corresponding to capture_name + or -1 if none */ +static int re_parse_captures(REParseState *s, int *phas_named_captures, + const char *capture_name) +{ + const uint8_t *p; + int capture_index; + char name[TMP_BUF_SIZE]; + + capture_index = 1; + *phas_named_captures = 0; + for (p = s->buf_start; p < s->buf_end; p++) { + switch (*p) { + case '(': + if (p[1] == '?') { + if (p[2] == '<' && p[3] != '=' && p[3] != '!') { + *phas_named_captures = 1; + /* potential named capture */ + if (capture_name) { + p += 3; + if (re_parse_group_name(name, sizeof(name), &p) == 0) { + if (!strcmp(name, capture_name)) + return capture_index; + } + } + capture_index++; + if (capture_index >= CAPTURE_COUNT_MAX) + goto done; + } + } else { + capture_index++; + if (capture_index >= CAPTURE_COUNT_MAX) + goto done; + } + break; + case '\\': + p++; + break; + case '[': + for (p += 1 + (*p == ']'); p < s->buf_end && *p != ']'; p++) { + if (*p == '\\') + p++; + } + break; + } + } + done: + if (capture_name) + return -1; + else + return capture_index; +} + +static int re_count_captures(REParseState *s) +{ + if (s->total_capture_count < 0) { + s->total_capture_count = re_parse_captures(s, &s->has_named_captures, + NULL); + } + return s->total_capture_count; +} + +static BOOL re_has_named_captures(REParseState *s) +{ + if (s->has_named_captures < 0) + re_count_captures(s); + return s->has_named_captures; +} + +static int find_group_name(REParseState *s, const char *name) +{ + const char *p, *buf_end; + size_t len, name_len; + int capture_index; + + p = (char *)s->group_names.buf; + if (!p) return -1; + buf_end = (char *)s->group_names.buf + s->group_names.size; + name_len = strlen(name); + capture_index = 1; + while (p < buf_end) { + len = strlen(p); + if (len == name_len && memcmp(name, p, name_len) == 0) + return capture_index; + p += len + 1; + capture_index++; + } + return -1; +} + +static int re_parse_disjunction(REParseState *s, BOOL is_backward_dir); + +static int re_parse_term(REParseState *s, BOOL is_backward_dir) +{ + const uint8_t *p; + int c, last_atom_start, quant_min, quant_max, last_capture_count; + BOOL greedy, add_zero_advance_check, is_neg, is_backward_lookahead; + CharRange cr_s, *cr = &cr_s; + + last_atom_start = -1; + last_capture_count = 0; + p = s->buf_ptr; + c = *p; + switch(c) { + case '^': + p++; + re_emit_op(s, REOP_line_start); + break; + case '$': + p++; + re_emit_op(s, REOP_line_end); + break; + case '.': + p++; + last_atom_start = s->byte_code.size; + last_capture_count = s->capture_count; + if (is_backward_dir) + re_emit_op(s, REOP_prev); + re_emit_op(s, s->dotall ? REOP_any : REOP_dot); + if (is_backward_dir) + re_emit_op(s, REOP_prev); + break; + case '{': + if (s->is_unicode) { + return re_parse_error(s, "syntax error"); + } else if (!lre_is_digit(p[1])) { + /* Annex B: we accept '{' not followed by digits as a + normal atom */ + goto parse_class_atom; + } else { + const uint8_t *p1 = p + 1; + /* Annex B: error if it is like a repetition count */ + parse_digits(&p1, TRUE); + if (*p1 == ',') { + p1++; + if (lre_is_digit(*p1)) { + parse_digits(&p1, TRUE); + } + } + if (*p1 != '}') { + goto parse_class_atom; + } + } + /* fall thru */ + case '*': + case '+': + case '?': + return re_parse_error(s, "nothing to repeat"); + case '(': + if (p[1] == '?') { + if (p[2] == ':') { + p += 3; + last_atom_start = s->byte_code.size; + last_capture_count = s->capture_count; + s->buf_ptr = p; + if (re_parse_disjunction(s, is_backward_dir)) + return -1; + p = s->buf_ptr; + if (re_parse_expect(s, &p, ')')) + return -1; + } else if ((p[2] == '=' || p[2] == '!')) { + is_neg = (p[2] == '!'); + is_backward_lookahead = FALSE; + p += 3; + goto lookahead; + } else if (p[2] == '<' && + (p[3] == '=' || p[3] == '!')) { + int pos; + is_neg = (p[3] == '!'); + is_backward_lookahead = TRUE; + p += 4; + /* lookahead */ + lookahead: + /* Annex B allows lookahead to be used as an atom for + the quantifiers */ + if (!s->is_unicode && !is_backward_lookahead) { + last_atom_start = s->byte_code.size; + last_capture_count = s->capture_count; + } + pos = re_emit_op_u32(s, REOP_lookahead + is_neg, 0); + s->buf_ptr = p; + if (re_parse_disjunction(s, is_backward_lookahead)) + return -1; + p = s->buf_ptr; + if (re_parse_expect(s, &p, ')')) + return -1; + re_emit_op(s, REOP_match); + /* jump after the 'match' after the lookahead is successful */ + if (dbuf_error(&s->byte_code)) + return -1; + put_u32(s->byte_code.buf + pos, s->byte_code.size - (pos + 4)); + } else if (p[2] == '<') { + p += 3; + if (re_parse_group_name(s->u.tmp_buf, sizeof(s->u.tmp_buf), + &p)) { + return re_parse_error(s, "invalid group name"); + } + if (find_group_name(s, s->u.tmp_buf) > 0) { + return re_parse_error(s, "duplicate group name"); + } + /* group name with a trailing zero */ + dbuf_put(&s->group_names, (uint8_t *)s->u.tmp_buf, + strlen(s->u.tmp_buf) + 1); + s->has_named_captures = 1; + goto parse_capture; + } else { + return re_parse_error(s, "invalid group"); + } + } else { + int capture_index; + p++; + /* capture without group name */ + dbuf_putc(&s->group_names, 0); + parse_capture: + if (s->capture_count >= CAPTURE_COUNT_MAX) + return re_parse_error(s, "too many captures"); + last_atom_start = s->byte_code.size; + last_capture_count = s->capture_count; + capture_index = s->capture_count++; + re_emit_op_u8(s, REOP_save_start + is_backward_dir, + capture_index); + + s->buf_ptr = p; + if (re_parse_disjunction(s, is_backward_dir)) + return -1; + p = s->buf_ptr; + + re_emit_op_u8(s, REOP_save_start + 1 - is_backward_dir, + capture_index); + + if (re_parse_expect(s, &p, ')')) + return -1; + } + break; + case '\\': + switch(p[1]) { + case 'b': + case 'B': + re_emit_op(s, REOP_word_boundary + (p[1] != 'b')); + p += 2; + break; + case 'k': + { + const uint8_t *p1; + int dummy_res; + + p1 = p; + if (p1[2] != '<') { + /* annex B: we tolerate invalid group names in non + unicode mode if there is no named capture + definition */ + if (s->is_unicode || re_has_named_captures(s)) + return re_parse_error(s, "expecting group name"); + else + goto parse_class_atom; + } + p1 += 3; + if (re_parse_group_name(s->u.tmp_buf, sizeof(s->u.tmp_buf), + &p1)) { + if (s->is_unicode || re_has_named_captures(s)) + return re_parse_error(s, "invalid group name"); + else + goto parse_class_atom; + } + c = find_group_name(s, s->u.tmp_buf); + if (c < 0) { + /* no capture name parsed before, try to look + after (inefficient, but hopefully not common */ + c = re_parse_captures(s, &dummy_res, s->u.tmp_buf); + if (c < 0) { + if (s->is_unicode || re_has_named_captures(s)) + return re_parse_error(s, "group name not defined"); + else + goto parse_class_atom; + } + } + p = p1; + } + goto emit_back_reference; + case '0': + p += 2; + c = 0; + if (s->is_unicode) { + if (lre_is_digit(*p)) { + return re_parse_error(s, "invalid decimal escape in regular expression"); + } + } else { + /* Annex B.1.4: accept legacy octal */ + if (*p >= '0' && *p <= '7') { + c = *p++ - '0'; + if (*p >= '0' && *p <= '7') { + c = (c << 3) + *p++ - '0'; + } + } + } + goto normal_char; + case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': + case '9': + { + const uint8_t *q = ++p; + + c = parse_digits(&p, FALSE); + if (c < 0 || (c >= s->capture_count && c >= re_count_captures(s))) { + if (!s->is_unicode) { + /* Annex B.1.4: accept legacy octal */ + p = q; + if (*p <= '7') { + c = 0; + if (*p <= '3') + c = *p++ - '0'; + if (*p >= '0' && *p <= '7') { + c = (c << 3) + *p++ - '0'; + if (*p >= '0' && *p <= '7') { + c = (c << 3) + *p++ - '0'; + } + } + } else { + c = *p++; + } + goto normal_char; + } + return re_parse_error(s, "back reference out of range in regular expression"); + } + emit_back_reference: + last_atom_start = s->byte_code.size; + last_capture_count = s->capture_count; + re_emit_op_u8(s, REOP_back_reference + is_backward_dir, c); + } + break; + default: + goto parse_class_atom; + } + break; + case '[': + last_atom_start = s->byte_code.size; + last_capture_count = s->capture_count; + if (is_backward_dir) + re_emit_op(s, REOP_prev); + if (re_parse_char_class(s, &p)) + return -1; + if (is_backward_dir) + re_emit_op(s, REOP_prev); + break; + case ']': + case '}': + if (s->is_unicode) + return re_parse_error(s, "syntax error"); + goto parse_class_atom; + default: + parse_class_atom: + c = get_class_atom(s, cr, &p, FALSE); + if ((int)c < 0) + return -1; + normal_char: + last_atom_start = s->byte_code.size; + last_capture_count = s->capture_count; + if (is_backward_dir) + re_emit_op(s, REOP_prev); + if (c >= CLASS_RANGE_BASE) { + int ret; + /* Note: canonicalization is not needed */ + ret = re_emit_range(s, cr); + cr_free(cr); + if (ret) + return -1; + } else { + if (s->ignore_case) + c = lre_canonicalize(c, s->is_unicode); + if (c <= 0x7f) + re_emit_op_u8(s, REOP_char8, c); + else if (c <= 0xffff) + re_emit_op_u16(s, REOP_char16, c); + else + re_emit_op_u32(s, REOP_char32, c); + } + if (is_backward_dir) + re_emit_op(s, REOP_prev); + break; + } + + /* quantifier */ + if (last_atom_start >= 0) { + c = *p; + switch(c) { + case '*': + p++; + quant_min = 0; + quant_max = INT32_MAX; + goto quantifier; + case '+': + p++; + quant_min = 1; + quant_max = INT32_MAX; + goto quantifier; + case '?': + p++; + quant_min = 0; + quant_max = 1; + goto quantifier; + case '{': + { + const uint8_t *p1 = p; + /* As an extension (see ES6 annex B), we accept '{' not + followed by digits as a normal atom */ + if (!lre_is_digit(p[1])) { + if (s->is_unicode) + goto invalid_quant_count; + break; + } + p++; + quant_min = parse_digits(&p, TRUE); + quant_max = quant_min; + if (*p == ',') { + p++; + if (lre_is_digit(*p)) { + quant_max = parse_digits(&p, TRUE); + if (quant_max < quant_min) { + invalid_quant_count: + return re_parse_error(s, "invalid repetition count"); + } + } else { + quant_max = INT32_MAX; /* infinity */ + } + } + if (*p != '}' && !s->is_unicode) { + /* Annex B: normal atom if invalid '{' syntax */ + p = p1; + break; + } + if (re_parse_expect(s, &p, '}')) + return -1; + } + quantifier: + greedy = TRUE; + if (*p == '?') { + p++; + greedy = FALSE; + } + if (last_atom_start < 0) { + return re_parse_error(s, "nothing to repeat"); + } + if (greedy) { + int len, pos; + + if (quant_max > 0) { + /* specific optimization for simple quantifiers */ + if (dbuf_error(&s->byte_code)) + goto out_of_memory; + len = re_is_simple_quantifier(s->byte_code.buf + last_atom_start, + s->byte_code.size - last_atom_start); + if (len > 0) { + re_emit_op(s, REOP_match); + + if (dbuf_insert(&s->byte_code, last_atom_start, 17)) + goto out_of_memory; + pos = last_atom_start; + s->byte_code.buf[pos++] = REOP_simple_greedy_quant; + put_u32(&s->byte_code.buf[pos], + s->byte_code.size - last_atom_start - 17); + pos += 4; + put_u32(&s->byte_code.buf[pos], quant_min); + pos += 4; + put_u32(&s->byte_code.buf[pos], quant_max); + pos += 4; + put_u32(&s->byte_code.buf[pos], len); + pos += 4; + goto done; + } + } + + if (dbuf_error(&s->byte_code)) + goto out_of_memory; + } + /* the spec tells that if there is no advance when + running the atom after the first quant_min times, + then there is no match. We remove this test when we + are sure the atom always advances the position. */ + add_zero_advance_check = re_need_check_advance(s->byte_code.buf + last_atom_start, + s->byte_code.size - last_atom_start); + + { + int len, pos; + len = s->byte_code.size - last_atom_start; + if (quant_min == 0) { + /* need to reset the capture in case the atom is + not executed */ + if (last_capture_count != s->capture_count) { + if (dbuf_insert(&s->byte_code, last_atom_start, 3)) + goto out_of_memory; + s->byte_code.buf[last_atom_start++] = REOP_save_reset; + s->byte_code.buf[last_atom_start++] = last_capture_count; + s->byte_code.buf[last_atom_start++] = s->capture_count - 1; + } + if (quant_max == 0) { + s->byte_code.size = last_atom_start; + } else if (quant_max == 1 || quant_max == INT32_MAX) { + BOOL has_goto = (quant_max == INT32_MAX); + if (dbuf_insert(&s->byte_code, last_atom_start, 5 + add_zero_advance_check)) + goto out_of_memory; + s->byte_code.buf[last_atom_start] = REOP_split_goto_first + + greedy; + put_u32(s->byte_code.buf + last_atom_start + 1, + len + 5 * has_goto + add_zero_advance_check * 2); + if (add_zero_advance_check) { + s->byte_code.buf[last_atom_start + 1 + 4] = REOP_push_char_pos; + re_emit_op(s, REOP_check_advance); + } + if (has_goto) + re_emit_goto(s, REOP_goto, last_atom_start); + } else { + if (dbuf_insert(&s->byte_code, last_atom_start, 10 + add_zero_advance_check)) + goto out_of_memory; + pos = last_atom_start; + s->byte_code.buf[pos++] = REOP_push_i32; + put_u32(s->byte_code.buf + pos, quant_max); + pos += 4; + s->byte_code.buf[pos++] = REOP_split_goto_first + greedy; + put_u32(s->byte_code.buf + pos, len + 5 + add_zero_advance_check * 2); + pos += 4; + if (add_zero_advance_check) { + s->byte_code.buf[pos++] = REOP_push_char_pos; + re_emit_op(s, REOP_check_advance); + } + re_emit_goto(s, REOP_loop, last_atom_start + 5); + re_emit_op(s, REOP_drop); + } + } else if (quant_min == 1 && quant_max == INT32_MAX && + !add_zero_advance_check) { + re_emit_goto(s, REOP_split_next_first - greedy, + last_atom_start); + } else { + if (quant_min == 1) { + /* nothing to add */ + } else { + if (dbuf_insert(&s->byte_code, last_atom_start, 5)) + goto out_of_memory; + s->byte_code.buf[last_atom_start] = REOP_push_i32; + put_u32(s->byte_code.buf + last_atom_start + 1, + quant_min); + last_atom_start += 5; + re_emit_goto(s, REOP_loop, last_atom_start); + re_emit_op(s, REOP_drop); + } + if (quant_max == INT32_MAX) { + pos = s->byte_code.size; + re_emit_op_u32(s, REOP_split_goto_first + greedy, + len + 5 + add_zero_advance_check * 2); + if (add_zero_advance_check) + re_emit_op(s, REOP_push_char_pos); + /* copy the atom */ + dbuf_put_self(&s->byte_code, last_atom_start, len); + if (add_zero_advance_check) + re_emit_op(s, REOP_check_advance); + re_emit_goto(s, REOP_goto, pos); + } else if (quant_max > quant_min) { + re_emit_op_u32(s, REOP_push_i32, quant_max - quant_min); + pos = s->byte_code.size; + re_emit_op_u32(s, REOP_split_goto_first + greedy, + len + 5 + add_zero_advance_check * 2); + if (add_zero_advance_check) + re_emit_op(s, REOP_push_char_pos); + /* copy the atom */ + dbuf_put_self(&s->byte_code, last_atom_start, len); + if (add_zero_advance_check) + re_emit_op(s, REOP_check_advance); + re_emit_goto(s, REOP_loop, pos); + re_emit_op(s, REOP_drop); + } + } + last_atom_start = -1; + } + break; + default: + break; + } + } + done: + s->buf_ptr = p; + return 0; + out_of_memory: + return re_parse_out_of_memory(s); +} + +static int re_parse_alternative(REParseState *s, BOOL is_backward_dir) +{ + const uint8_t *p; + int ret; + size_t start, term_start, end, term_size; + + start = s->byte_code.size; + for(;;) { + p = s->buf_ptr; + if (p >= s->buf_end) + break; + if (*p == '|' || *p == ')') + break; + term_start = s->byte_code.size; + ret = re_parse_term(s, is_backward_dir); + if (ret) + return ret; + if (is_backward_dir) { + /* reverse the order of the terms (XXX: inefficient, but + speed is not really critical here) */ + end = s->byte_code.size; + term_size = end - term_start; + if (dbuf_realloc(&s->byte_code, end + term_size)) + return -1; + memmove(s->byte_code.buf + start + term_size, + s->byte_code.buf + start, + end - start); + memcpy(s->byte_code.buf + start, s->byte_code.buf + end, + term_size); + } + } + return 0; +} + +static int re_parse_disjunction(REParseState *s, BOOL is_backward_dir) +{ + int start, len, pos; + + if (lre_check_stack_overflow(s->opaque, 0)) + return re_parse_error(s, "stack overflow"); + + start = s->byte_code.size; + if (re_parse_alternative(s, is_backward_dir)) + return -1; + while (*s->buf_ptr == '|') { + s->buf_ptr++; + + len = s->byte_code.size - start; + + /* insert a split before the first alternative */ + if (dbuf_insert(&s->byte_code, start, 5)) { + return re_parse_out_of_memory(s); + } + s->byte_code.buf[start] = REOP_split_next_first; + put_u32(s->byte_code.buf + start + 1, len + 5); + + pos = re_emit_op_u32(s, REOP_goto, 0); + + if (re_parse_alternative(s, is_backward_dir)) + return -1; + + /* patch the goto */ + len = s->byte_code.size - (pos + 4); + put_u32(s->byte_code.buf + pos, len); + } + return 0; +} + +/* the control flow is recursive so the analysis can be linear */ +static int lre_compute_stack_size(const uint8_t *bc_buf, int bc_buf_len) +{ + int stack_size, stack_size_max, pos, opcode, len; + uint32_t val; + + stack_size = 0; + stack_size_max = 0; + bc_buf += RE_HEADER_LEN; + bc_buf_len -= RE_HEADER_LEN; + pos = 0; + while (pos < bc_buf_len) { + opcode = bc_buf[pos]; + len = reopcode_info[opcode].size; + assert(opcode < REOP_COUNT); + assert((pos + len) <= bc_buf_len); + switch(opcode) { + case REOP_push_i32: + case REOP_push_char_pos: + stack_size++; + if (stack_size > stack_size_max) { + if (stack_size > STACK_SIZE_MAX) + return -1; + stack_size_max = stack_size; + } + break; + case REOP_drop: + case REOP_check_advance: + assert(stack_size > 0); + stack_size--; + break; + case REOP_range: + val = get_u16(bc_buf + pos + 1); + len += val * 4; + break; + case REOP_range32: + val = get_u16(bc_buf + pos + 1); + len += val * 8; + break; + } + pos += len; + } + return stack_size_max; +} + +/* 'buf' must be a zero terminated UTF-8 string of length buf_len. + Return NULL if error and allocate an error message in *perror_msg, + otherwise the compiled bytecode and its length in plen. +*/ +uint8_t *lre_compile(int *plen, char *error_msg, int error_msg_size, + const char *buf, size_t buf_len, int re_flags, + void *opaque) +{ + REParseState s_s, *s = &s_s; + int stack_size; + BOOL is_sticky; + + memset(s, 0, sizeof(*s)); + s->opaque = opaque; + s->buf_ptr = (const uint8_t *)buf; + s->buf_end = s->buf_ptr + buf_len; + s->buf_start = s->buf_ptr; + s->re_flags = re_flags; + s->is_unicode = ((re_flags & LRE_FLAG_UNICODE) != 0); + is_sticky = ((re_flags & LRE_FLAG_STICKY) != 0); + s->ignore_case = ((re_flags & LRE_FLAG_IGNORECASE) != 0); + s->dotall = ((re_flags & LRE_FLAG_DOTALL) != 0); + s->unicode_sets = ((re_flags & LRE_FLAG_UNICODE_SETS) != 0); + s->capture_count = 1; + s->total_capture_count = -1; + s->has_named_captures = -1; + + dbuf_init2(&s->byte_code, opaque, lre_realloc); + dbuf_init2(&s->group_names, opaque, lre_realloc); + + dbuf_put_u16(&s->byte_code, re_flags); /* first element is the flags */ + dbuf_putc(&s->byte_code, 0); /* second element is the number of captures */ + dbuf_putc(&s->byte_code, 0); /* stack size */ + dbuf_put_u32(&s->byte_code, 0); /* bytecode length */ + + if (!is_sticky) { + /* iterate thru all positions (about the same as .*?( ... ) ) + . We do it without an explicit loop so that lock step + thread execution will be possible in an optimized + implementation */ + re_emit_op_u32(s, REOP_split_goto_first, 1 + 5); + re_emit_op(s, REOP_any); + re_emit_op_u32(s, REOP_goto, -(5 + 1 + 5)); + } + re_emit_op_u8(s, REOP_save_start, 0); + + if (re_parse_disjunction(s, FALSE)) { + error: + dbuf_free(&s->byte_code); + dbuf_free(&s->group_names); + pstrcpy(error_msg, error_msg_size, s->u.error_msg); + *plen = 0; + return NULL; + } + + re_emit_op_u8(s, REOP_save_end, 0); + + re_emit_op(s, REOP_match); + + if (*s->buf_ptr != '\0') { + re_parse_error(s, "extraneous characters at the end"); + goto error; + } + + if (dbuf_error(&s->byte_code)) { + re_parse_out_of_memory(s); + goto error; + } + + stack_size = lre_compute_stack_size(s->byte_code.buf, s->byte_code.size); + if (stack_size < 0) { + re_parse_error(s, "too many imbricated quantifiers"); + goto error; + } + + s->byte_code.buf[RE_HEADER_CAPTURE_COUNT] = s->capture_count; + s->byte_code.buf[RE_HEADER_STACK_SIZE] = stack_size; + put_u32(s->byte_code.buf + RE_HEADER_BYTECODE_LEN, + s->byte_code.size - RE_HEADER_LEN); + + /* add the named groups if needed */ + if (s->group_names.size > (s->capture_count - 1)) { + dbuf_put(&s->byte_code, s->group_names.buf, s->group_names.size); + put_u16(s->byte_code.buf + RE_HEADER_FLAGS, + LRE_FLAG_NAMED_GROUPS | lre_get_flags(s->byte_code.buf)); + } + dbuf_free(&s->group_names); + +#ifdef DUMP_REOP + lre_dump_bytecode(s->byte_code.buf, s->byte_code.size); +#endif + + error_msg[0] = '\0'; + *plen = s->byte_code.size; + return s->byte_code.buf; +} + +static BOOL is_line_terminator(uint32_t c) +{ + return (c == '\n' || c == '\r' || c == CP_LS || c == CP_PS); +} + +static BOOL is_word_char(uint32_t c) +{ + return ((c >= '0' && c <= '9') || + (c >= 'a' && c <= 'z') || + (c >= 'A' && c <= 'Z') || + (c == '_')); +} + +#define GET_CHAR(c, cptr, cbuf_end, cbuf_type) \ + do { \ + if (cbuf_type == 0) { \ + c = *cptr++; \ + } else { \ + const uint16_t *_p = (const uint16_t *)cptr; \ + const uint16_t *_end = (const uint16_t *)cbuf_end; \ + c = *_p++; \ + if (is_hi_surrogate(c)) \ + if (cbuf_type == 2) \ + if (_p < _end) \ + if (is_lo_surrogate(*_p)) \ + c = from_surrogate(c, *_p++); \ + cptr = (const void *)_p; \ + } \ + } while (0) + +#define PEEK_CHAR(c, cptr, cbuf_end, cbuf_type) \ + do { \ + if (cbuf_type == 0) { \ + c = cptr[0]; \ + } else { \ + const uint16_t *_p = (const uint16_t *)cptr; \ + const uint16_t *_end = (const uint16_t *)cbuf_end; \ + c = *_p++; \ + if (is_hi_surrogate(c)) \ + if (cbuf_type == 2) \ + if (_p < _end) \ + if (is_lo_surrogate(*_p)) \ + c = from_surrogate(c, *_p); \ + } \ + } while (0) + +#define PEEK_PREV_CHAR(c, cptr, cbuf_start, cbuf_type) \ + do { \ + if (cbuf_type == 0) { \ + c = cptr[-1]; \ + } else { \ + const uint16_t *_p = (const uint16_t *)cptr - 1; \ + const uint16_t *_start = (const uint16_t *)cbuf_start; \ + c = *_p; \ + if (is_lo_surrogate(c)) \ + if (cbuf_type == 2) \ + if (_p > _start) \ + if (is_hi_surrogate(_p[-1])) \ + c = from_surrogate(*--_p, c); \ + } \ + } while (0) + +#define GET_PREV_CHAR(c, cptr, cbuf_start, cbuf_type) \ + do { \ + if (cbuf_type == 0) { \ + cptr--; \ + c = cptr[0]; \ + } else { \ + const uint16_t *_p = (const uint16_t *)cptr - 1; \ + const uint16_t *_start = (const uint16_t *)cbuf_start; \ + c = *_p; \ + if (is_lo_surrogate(c)) \ + if (cbuf_type == 2) \ + if (_p > _start) \ + if (is_hi_surrogate(_p[-1])) \ + c = from_surrogate(*--_p, c); \ + cptr = (const void *)_p; \ + } \ + } while (0) + +#define PREV_CHAR(cptr, cbuf_start, cbuf_type) \ + do { \ + if (cbuf_type == 0) { \ + cptr--; \ + } else { \ + const uint16_t *_p = (const uint16_t *)cptr - 1; \ + const uint16_t *_start = (const uint16_t *)cbuf_start; \ + if (is_lo_surrogate(*_p)) \ + if (cbuf_type == 2) \ + if (_p > _start) \ + if (is_hi_surrogate(_p[-1])) \ + _p--; \ + cptr = (const void *)_p; \ + } \ + } while (0) + +typedef uintptr_t StackInt; + +typedef enum { + RE_EXEC_STATE_SPLIT, + RE_EXEC_STATE_LOOKAHEAD, + RE_EXEC_STATE_NEGATIVE_LOOKAHEAD, + RE_EXEC_STATE_GREEDY_QUANT, +} REExecStateEnum; + +typedef struct REExecState { + REExecStateEnum type : 8; + uint8_t stack_len; + size_t count; /* only used for RE_EXEC_STATE_GREEDY_QUANT */ + const uint8_t *cptr; + const uint8_t *pc; + void *buf[]; +} REExecState; + +typedef struct { + const uint8_t *cbuf; + const uint8_t *cbuf_end; + /* 0 = 8 bit chars, 1 = 16 bit chars, 2 = 16 bit chars, UTF-16 */ + int cbuf_type; + int capture_count; + int stack_size_max; + BOOL multi_line; + BOOL ignore_case; + BOOL is_unicode; + void *opaque; /* used for stack overflow check */ + + size_t state_size; + uint8_t *state_stack; + size_t state_stack_size; + size_t state_stack_len; +} REExecContext; + +static int push_state(REExecContext *s, + uint8_t **capture, + StackInt *stack, size_t stack_len, + const uint8_t *pc, const uint8_t *cptr, + REExecStateEnum type, size_t count) +{ + REExecState *rs; + uint8_t *new_stack; + size_t new_size, i, n; + StackInt *stack_buf; + + if (unlikely((s->state_stack_len + 1) > s->state_stack_size)) { + /* reallocate the stack */ + new_size = s->state_stack_size * 3 / 2; + if (new_size < 8) + new_size = 8; + new_stack = lre_realloc(s->opaque, s->state_stack, new_size * s->state_size); + if (!new_stack) + return -1; + s->state_stack_size = new_size; + s->state_stack = new_stack; + } + rs = (REExecState *)(s->state_stack + s->state_stack_len * s->state_size); + s->state_stack_len++; + rs->type = type; + rs->count = count; + rs->stack_len = stack_len; + rs->cptr = cptr; + rs->pc = pc; + n = 2 * s->capture_count; + for(i = 0; i < n; i++) + rs->buf[i] = capture[i]; + stack_buf = (StackInt *)(rs->buf + n); + for(i = 0; i < stack_len; i++) + stack_buf[i] = stack[i]; + return 0; +} + +/* return 1 if match, 0 if not match or -1 if error. */ +static intptr_t lre_exec_backtrack(REExecContext *s, uint8_t **capture, + StackInt *stack, int stack_len, + const uint8_t *pc, const uint8_t *cptr, + BOOL no_recurse) +{ + int opcode, ret; + int cbuf_type; + uint32_t val, c; + const uint8_t *cbuf_end; + + cbuf_type = s->cbuf_type; + cbuf_end = s->cbuf_end; + + for(;;) { + // printf("top=%p: pc=%d\n", th_list.top, (int)(pc - (bc_buf + RE_HEADER_LEN))); + opcode = *pc++; + switch(opcode) { + case REOP_match: + { + REExecState *rs; + if (no_recurse) + return (intptr_t)cptr; + ret = 1; + goto recurse; + no_match: + if (no_recurse) + return 0; + ret = 0; + recurse: + for(;;) { + if (s->state_stack_len == 0) + return ret; + rs = (REExecState *)(s->state_stack + + (s->state_stack_len - 1) * s->state_size); + if (rs->type == RE_EXEC_STATE_SPLIT) { + if (!ret) { + pop_state: + memcpy(capture, rs->buf, + sizeof(capture[0]) * 2 * s->capture_count); + pop_state1: + pc = rs->pc; + cptr = rs->cptr; + stack_len = rs->stack_len; + memcpy(stack, rs->buf + 2 * s->capture_count, + stack_len * sizeof(stack[0])); + s->state_stack_len--; + break; + } + } else if (rs->type == RE_EXEC_STATE_GREEDY_QUANT) { + if (!ret) { + uint32_t char_count, i; + memcpy(capture, rs->buf, + sizeof(capture[0]) * 2 * s->capture_count); + stack_len = rs->stack_len; + memcpy(stack, rs->buf + 2 * s->capture_count, + stack_len * sizeof(stack[0])); + pc = rs->pc; + cptr = rs->cptr; + /* go backward */ + char_count = get_u32(pc + 12); + for(i = 0; i < char_count; i++) { + PREV_CHAR(cptr, s->cbuf, cbuf_type); + } + pc = (pc + 16) + (int)get_u32(pc); + rs->cptr = cptr; + rs->count--; + if (rs->count == 0) { + s->state_stack_len--; + } + break; + } + } else { + ret = ((rs->type == RE_EXEC_STATE_LOOKAHEAD && ret) || + (rs->type == RE_EXEC_STATE_NEGATIVE_LOOKAHEAD && !ret)); + if (ret) { + /* keep the capture in case of positive lookahead */ + if (rs->type == RE_EXEC_STATE_LOOKAHEAD) + goto pop_state1; + else + goto pop_state; + } + } + s->state_stack_len--; + } + } + break; + case REOP_char32: + val = get_u32(pc); + pc += 4; + goto test_char; + case REOP_char16: + val = get_u16(pc); + pc += 2; + goto test_char; + case REOP_char8: + val = get_u8(pc); + pc += 1; + test_char: + if (cptr >= cbuf_end) + goto no_match; + GET_CHAR(c, cptr, cbuf_end, cbuf_type); + if (s->ignore_case) { + c = lre_canonicalize(c, s->is_unicode); + } + if (val != c) + goto no_match; + break; + case REOP_split_goto_first: + case REOP_split_next_first: + { + const uint8_t *pc1; + + val = get_u32(pc); + pc += 4; + if (opcode == REOP_split_next_first) { + pc1 = pc + (int)val; + } else { + pc1 = pc; + pc = pc + (int)val; + } + ret = push_state(s, capture, stack, stack_len, + pc1, cptr, RE_EXEC_STATE_SPLIT, 0); + if (ret < 0) + return -1; + break; + } + case REOP_lookahead: + case REOP_negative_lookahead: + val = get_u32(pc); + pc += 4; + ret = push_state(s, capture, stack, stack_len, + pc + (int)val, cptr, + RE_EXEC_STATE_LOOKAHEAD + opcode - REOP_lookahead, + 0); + if (ret < 0) + return -1; + break; + + case REOP_goto: + val = get_u32(pc); + pc += 4 + (int)val; + break; + case REOP_line_start: + if (cptr == s->cbuf) + break; + if (!s->multi_line) + goto no_match; + PEEK_PREV_CHAR(c, cptr, s->cbuf, cbuf_type); + if (!is_line_terminator(c)) + goto no_match; + break; + case REOP_line_end: + if (cptr == cbuf_end) + break; + if (!s->multi_line) + goto no_match; + PEEK_CHAR(c, cptr, cbuf_end, cbuf_type); + if (!is_line_terminator(c)) + goto no_match; + break; + case REOP_dot: + if (cptr == cbuf_end) + goto no_match; + GET_CHAR(c, cptr, cbuf_end, cbuf_type); + if (is_line_terminator(c)) + goto no_match; + break; + case REOP_any: + if (cptr == cbuf_end) + goto no_match; + GET_CHAR(c, cptr, cbuf_end, cbuf_type); + break; + case REOP_save_start: + case REOP_save_end: + val = *pc++; + assert(val < s->capture_count); + capture[2 * val + opcode - REOP_save_start] = (uint8_t *)cptr; + break; + case REOP_save_reset: + { + uint32_t val2; + val = pc[0]; + val2 = pc[1]; + pc += 2; + assert(val2 < s->capture_count); + while (val <= val2) { + capture[2 * val] = NULL; + capture[2 * val + 1] = NULL; + val++; + } + } + break; + case REOP_push_i32: + val = get_u32(pc); + pc += 4; + stack[stack_len++] = val; + break; + case REOP_drop: + stack_len--; + break; + case REOP_loop: + val = get_u32(pc); + pc += 4; + if (--stack[stack_len - 1] != 0) { + pc += (int)val; + } + break; + case REOP_push_char_pos: + stack[stack_len++] = (uintptr_t)cptr; + break; + case REOP_check_advance: + if (stack[--stack_len] == (uintptr_t)cptr) + goto no_match; + break; + case REOP_word_boundary: + case REOP_not_word_boundary: + { + BOOL v1, v2; + /* char before */ + if (cptr == s->cbuf) { + v1 = FALSE; + } else { + PEEK_PREV_CHAR(c, cptr, s->cbuf, cbuf_type); + v1 = is_word_char(c); + } + /* current char */ + if (cptr >= cbuf_end) { + v2 = FALSE; + } else { + PEEK_CHAR(c, cptr, cbuf_end, cbuf_type); + v2 = is_word_char(c); + } + if (v1 ^ v2 ^ (REOP_not_word_boundary - opcode)) + goto no_match; + } + break; + case REOP_back_reference: + case REOP_backward_back_reference: + { + const uint8_t *cptr1, *cptr1_end, *cptr1_start; + uint32_t c1, c2; + + val = *pc++; + if (val >= s->capture_count) + goto no_match; + cptr1_start = capture[2 * val]; + cptr1_end = capture[2 * val + 1]; + if (!cptr1_start || !cptr1_end) + break; + if (opcode == REOP_back_reference) { + cptr1 = cptr1_start; + while (cptr1 < cptr1_end) { + if (cptr >= cbuf_end) + goto no_match; + GET_CHAR(c1, cptr1, cptr1_end, cbuf_type); + GET_CHAR(c2, cptr, cbuf_end, cbuf_type); + if (s->ignore_case) { + c1 = lre_canonicalize(c1, s->is_unicode); + c2 = lre_canonicalize(c2, s->is_unicode); + } + if (c1 != c2) + goto no_match; + } + } else { + cptr1 = cptr1_end; + while (cptr1 > cptr1_start) { + if (cptr == s->cbuf) + goto no_match; + GET_PREV_CHAR(c1, cptr1, cptr1_start, cbuf_type); + GET_PREV_CHAR(c2, cptr, s->cbuf, cbuf_type); + if (s->ignore_case) { + c1 = lre_canonicalize(c1, s->is_unicode); + c2 = lre_canonicalize(c2, s->is_unicode); + } + if (c1 != c2) + goto no_match; + } + } + } + break; + case REOP_range: + { + int n; + uint32_t low, high, idx_min, idx_max, idx; + + n = get_u16(pc); /* n must be >= 1 */ + pc += 2; + if (cptr >= cbuf_end) + goto no_match; + GET_CHAR(c, cptr, cbuf_end, cbuf_type); + if (s->ignore_case) { + c = lre_canonicalize(c, s->is_unicode); + } + idx_min = 0; + low = get_u16(pc + 0 * 4); + if (c < low) + goto no_match; + idx_max = n - 1; + high = get_u16(pc + idx_max * 4 + 2); + /* 0xffff in for last value means +infinity */ + if (unlikely(c >= 0xffff) && high == 0xffff) + goto range_match; + if (c > high) + goto no_match; + while (idx_min <= idx_max) { + idx = (idx_min + idx_max) / 2; + low = get_u16(pc + idx * 4); + high = get_u16(pc + idx * 4 + 2); + if (c < low) + idx_max = idx - 1; + else if (c > high) + idx_min = idx + 1; + else + goto range_match; + } + goto no_match; + range_match: + pc += 4 * n; + } + break; + case REOP_range32: + { + int n; + uint32_t low, high, idx_min, idx_max, idx; + + n = get_u16(pc); /* n must be >= 1 */ + pc += 2; + if (cptr >= cbuf_end) + goto no_match; + GET_CHAR(c, cptr, cbuf_end, cbuf_type); + if (s->ignore_case) { + c = lre_canonicalize(c, s->is_unicode); + } + idx_min = 0; + low = get_u32(pc + 0 * 8); + if (c < low) + goto no_match; + idx_max = n - 1; + high = get_u32(pc + idx_max * 8 + 4); + if (c > high) + goto no_match; + while (idx_min <= idx_max) { + idx = (idx_min + idx_max) / 2; + low = get_u32(pc + idx * 8); + high = get_u32(pc + idx * 8 + 4); + if (c < low) + idx_max = idx - 1; + else if (c > high) + idx_min = idx + 1; + else + goto range32_match; + } + goto no_match; + range32_match: + pc += 8 * n; + } + break; + case REOP_prev: + /* go to the previous char */ + if (cptr == s->cbuf) + goto no_match; + PREV_CHAR(cptr, s->cbuf, cbuf_type); + break; + case REOP_simple_greedy_quant: + { + uint32_t next_pos, quant_min, quant_max; + size_t q; + intptr_t res; + const uint8_t *pc1; + + next_pos = get_u32(pc); + quant_min = get_u32(pc + 4); + quant_max = get_u32(pc + 8); + pc += 16; + pc1 = pc; + pc += (int)next_pos; + + q = 0; + for(;;) { + res = lre_exec_backtrack(s, capture, stack, stack_len, + pc1, cptr, TRUE); + if (res == -1) + return res; + if (!res) + break; + cptr = (uint8_t *)res; + q++; + if (q >= quant_max && quant_max != INT32_MAX) + break; + } + if (q < quant_min) + goto no_match; + if (q > quant_min) { + /* will examine all matches down to quant_min */ + ret = push_state(s, capture, stack, stack_len, + pc1 - 16, cptr, + RE_EXEC_STATE_GREEDY_QUANT, + q - quant_min); + if (ret < 0) + return -1; + } + } + break; + default: + abort(); + } + } +} + +/* Return 1 if match, 0 if not match or -1 if error. cindex is the + starting position of the match and must be such as 0 <= cindex <= + clen. */ +int lre_exec(uint8_t **capture, + const uint8_t *bc_buf, const uint8_t *cbuf, int cindex, int clen, + int cbuf_type, void *opaque) +{ + REExecContext s_s, *s = &s_s; + int re_flags, i, alloca_size, ret; + StackInt *stack_buf; + + re_flags = lre_get_flags(bc_buf); + s->multi_line = (re_flags & LRE_FLAG_MULTILINE) != 0; + s->ignore_case = (re_flags & LRE_FLAG_IGNORECASE) != 0; + s->is_unicode = (re_flags & LRE_FLAG_UNICODE) != 0; + s->capture_count = bc_buf[RE_HEADER_CAPTURE_COUNT]; + s->stack_size_max = bc_buf[RE_HEADER_STACK_SIZE]; + s->cbuf = cbuf; + s->cbuf_end = cbuf + (clen << cbuf_type); + s->cbuf_type = cbuf_type; + if (s->cbuf_type == 1 && s->is_unicode) + s->cbuf_type = 2; + s->opaque = opaque; + + s->state_size = sizeof(REExecState) + + s->capture_count * sizeof(capture[0]) * 2 + + s->stack_size_max * sizeof(stack_buf[0]); + s->state_stack = NULL; + s->state_stack_len = 0; + s->state_stack_size = 0; + + for(i = 0; i < s->capture_count * 2; i++) + capture[i] = NULL; + alloca_size = s->stack_size_max * sizeof(stack_buf[0]); + stack_buf = alloca(alloca_size); + ret = lre_exec_backtrack(s, capture, stack_buf, 0, bc_buf + RE_HEADER_LEN, + cbuf + (cindex << cbuf_type), FALSE); + lre_realloc(s->opaque, s->state_stack, 0); + return ret; +} + +int lre_get_capture_count(const uint8_t *bc_buf) +{ + return bc_buf[RE_HEADER_CAPTURE_COUNT]; +} + +int lre_get_flags(const uint8_t *bc_buf) +{ + return get_u16(bc_buf + RE_HEADER_FLAGS); +} + +/* Return NULL if no group names. Otherwise, return a pointer to + 'capture_count - 1' zero terminated UTF-8 strings. */ +const char *lre_get_groupnames(const uint8_t *bc_buf) +{ + uint32_t re_bytecode_len; + if ((lre_get_flags(bc_buf) & LRE_FLAG_NAMED_GROUPS) == 0) + return NULL; + re_bytecode_len = get_u32(bc_buf + RE_HEADER_BYTECODE_LEN); + return (const char *)(bc_buf + RE_HEADER_LEN + re_bytecode_len); +} + +void lre_byte_swap(uint8_t *buf, size_t len, BOOL is_byte_swapped) +{ + uint8_t *p, *pe; + uint32_t n, r, nw; + + p = buf; + if (len < RE_HEADER_LEN) + abort(); + + // format is: + // <header> + // <bytecode> + // <capture group name 1> + // <capture group name 2> + // etc. + inplace_bswap16(&p[RE_HEADER_FLAGS]); + + n = get_u32(&p[RE_HEADER_BYTECODE_LEN]); + inplace_bswap32(&p[RE_HEADER_BYTECODE_LEN]); + if (is_byte_swapped) + n = bswap32(n); + if (n > len - RE_HEADER_LEN) + abort(); + + p = &buf[RE_HEADER_LEN]; + pe = &p[n]; + + while (p < pe) { + n = reopcode_info[*p].size; + switch (n) { + case 1: + case 2: + break; + case 3: + switch (*p) { + case REOP_save_reset: // has two 8 bit arguments + break; + case REOP_range32: // variable length + nw = get_u16(&p[1]); // number of pairs of uint32_t + if (is_byte_swapped) + n = bswap16(n); + for (r = 3 + 8 * nw; n < r; n += 4) + inplace_bswap32(&p[n]); + goto doswap16; + case REOP_range: // variable length + nw = get_u16(&p[1]); // number of pairs of uint16_t + if (is_byte_swapped) + n = bswap16(n); + for (r = 3 + 4 * nw; n < r; n += 2) + inplace_bswap16(&p[n]); + goto doswap16; + default: + doswap16: + inplace_bswap16(&p[1]); + break; + } + break; + case 5: + inplace_bswap32(&p[1]); + break; + case 17: + assert(*p == REOP_simple_greedy_quant); + inplace_bswap32(&p[1]); + inplace_bswap32(&p[5]); + inplace_bswap32(&p[9]); + inplace_bswap32(&p[13]); + break; + default: + abort(); + } + p = &p[n]; + } +} + +#ifdef TEST + +BOOL lre_check_stack_overflow(void *opaque, size_t alloca_size) +{ + return FALSE; +} + +void *lre_realloc(void *opaque, void *ptr, size_t size) +{ + return realloc(ptr, size); +} + +int main(int argc, char **argv) +{ + int len, flags, ret, i; + uint8_t *bc; + char error_msg[64]; + uint8_t *capture[CAPTURE_COUNT_MAX * 2]; + const char *input; + int input_len, capture_count; + + if (argc < 4) { + printf("usage: %s regexp flags input\n", argv[0]); + exit(1); + } + flags = atoi(argv[2]); + bc = lre_compile(&len, error_msg, sizeof(error_msg), argv[1], + strlen(argv[1]), flags, NULL); + if (!bc) { + fprintf(stderr, "error: %s\n", error_msg); + exit(1); + } + + input = argv[3]; + input_len = strlen(input); + + ret = lre_exec(capture, bc, (uint8_t *)input, 0, input_len, 0, NULL); + printf("ret=%d\n", ret); + if (ret == 1) { + capture_count = lre_get_capture_count(bc); + for(i = 0; i < 2 * capture_count; i++) { + uint8_t *ptr; + ptr = capture[i]; + printf("%d: ", i); + if (!ptr) + printf("<nil>"); + else + printf("%u", (int)(ptr - (uint8_t *)input)); + printf("\n"); + } + } + return 0; +} +#endif diff --git a/third_party/quickjs/libregexp.h b/third_party/quickjs/libregexp.h new file mode 100644 index 0000000000..c2e664a248 --- /dev/null +++ b/third_party/quickjs/libregexp.h @@ -0,0 +1,95 @@ +/* + * Regular Expression Engine + * + * Copyright (c) 2017-2018 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#ifndef LIBREGEXP_H +#define LIBREGEXP_H + +#include <stddef.h> + +#include "libunicode.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define LRE_BOOL int /* for documentation purposes */ + +#define LRE_FLAG_GLOBAL (1 << 0) +#define LRE_FLAG_IGNORECASE (1 << 1) +#define LRE_FLAG_MULTILINE (1 << 2) +#define LRE_FLAG_DOTALL (1 << 3) +#define LRE_FLAG_UNICODE (1 << 4) +#define LRE_FLAG_STICKY (1 << 5) +#define LRE_FLAG_INDICES (1 << 6) /* Unused by libregexp, just recorded. */ +#define LRE_FLAG_NAMED_GROUPS (1 << 7) /* named groups are present in the regexp */ +#define LRE_FLAG_UNICODE_SETS (1 << 8) + +uint8_t *lre_compile(int *plen, char *error_msg, int error_msg_size, + const char *buf, size_t buf_len, int re_flags, + void *opaque); +int lre_get_capture_count(const uint8_t *bc_buf); +int lre_get_flags(const uint8_t *bc_buf); +const char *lre_get_groupnames(const uint8_t *bc_buf); +int lre_exec(uint8_t **capture, + const uint8_t *bc_buf, const uint8_t *cbuf, int cindex, int clen, + int cbuf_type, void *opaque); + +int lre_parse_escape(const uint8_t **pp, int allow_utf16); +LRE_BOOL lre_is_space(int c); + +void lre_byte_swap(uint8_t *buf, size_t len, LRE_BOOL is_byte_swapped); + +/* must be provided by the user */ +LRE_BOOL lre_check_stack_overflow(void *opaque, size_t alloca_size); +void *lre_realloc(void *opaque, void *ptr, size_t size); + +/* JS identifier test */ +extern uint32_t const lre_id_start_table_ascii[4]; +extern uint32_t const lre_id_continue_table_ascii[4]; + +static inline int lre_js_is_ident_first(int c) +{ + if ((uint32_t)c < 128) { + return (lre_id_start_table_ascii[c >> 5] >> (c & 31)) & 1; + } else { + return lre_is_id_start(c); + } +} + +static inline int lre_js_is_ident_next(int c) +{ + if ((uint32_t)c < 128) { + return (lre_id_continue_table_ascii[c >> 5] >> (c & 31)) & 1; + } else { + /* ZWNJ and ZWJ are accepted in identifiers */ + return lre_is_id_continue(c) || c == 0x200C || c == 0x200D; + } +} + +#undef LRE_BOOL + +#ifdef __cplusplus +} /* extern "C" { */ +#endif + +#endif /* LIBREGEXP_H */ diff --git a/third_party/quickjs/libunicode-table.h b/third_party/quickjs/libunicode-table.h new file mode 100644 index 0000000000..b48a3a7460 --- /dev/null +++ b/third_party/quickjs/libunicode-table.h @@ -0,0 +1,4658 @@ +/* Compressed unicode tables */ +/* Automatically generated file - do not edit */ + +#include <stdint.h> + +static const uint32_t case_conv_table1[378] = { + 0x00209a30, 0x00309a00, 0x005a8173, 0x00601730, + 0x006c0730, 0x006f81b3, 0x00701700, 0x007c0700, + 0x007f8100, 0x00803040, 0x009801c3, 0x00988190, + 0x00990640, 0x009c9040, 0x00a481b4, 0x00a52e40, + 0x00bc0130, 0x00bc8640, 0x00bf8170, 0x00c00100, + 0x00c08130, 0x00c10440, 0x00c30130, 0x00c38240, + 0x00c48230, 0x00c58240, 0x00c70130, 0x00c78130, + 0x00c80130, 0x00c88240, 0x00c98130, 0x00ca0130, + 0x00ca8100, 0x00cb0130, 0x00cb8130, 0x00cc0240, + 0x00cd0100, 0x00cd8101, 0x00ce0130, 0x00ce8130, + 0x00cf0100, 0x00cf8130, 0x00d00640, 0x00d30130, + 0x00d38240, 0x00d48130, 0x00d60240, 0x00d70130, + 0x00d78240, 0x00d88230, 0x00d98440, 0x00db8130, + 0x00dc0240, 0x00de0240, 0x00df8100, 0x00e20350, + 0x00e38350, 0x00e50350, 0x00e69040, 0x00ee8100, + 0x00ef1240, 0x00f801b4, 0x00f88350, 0x00fa0240, + 0x00fb0130, 0x00fb8130, 0x00fc2840, 0x01100130, + 0x01111240, 0x011d0131, 0x011d8240, 0x011e8130, + 0x011f0131, 0x011f8201, 0x01208240, 0x01218130, + 0x01220130, 0x01228130, 0x01230a40, 0x01280101, + 0x01288101, 0x01290101, 0x01298100, 0x012a0100, + 0x012b0200, 0x012c8100, 0x012d8100, 0x012e0101, + 0x01300100, 0x01308101, 0x01318100, 0x01320101, + 0x01328101, 0x01330101, 0x01340100, 0x01348100, + 0x01350101, 0x01358101, 0x01360101, 0x01378100, + 0x01388101, 0x01390100, 0x013a8100, 0x013e8101, + 0x01400100, 0x01410101, 0x01418100, 0x01438101, + 0x01440100, 0x01448100, 0x01450200, 0x01460100, + 0x01490100, 0x014e8101, 0x014f0101, 0x01a28173, + 0x01b80440, 0x01bb0240, 0x01bd8300, 0x01bf8130, + 0x01c30130, 0x01c40330, 0x01c60130, 0x01c70230, + 0x01c801d0, 0x01c89130, 0x01d18930, 0x01d60100, + 0x01d68300, 0x01d801d3, 0x01d89100, 0x01e10173, + 0x01e18900, 0x01e60100, 0x01e68200, 0x01e78130, + 0x01e80173, 0x01e88173, 0x01ea8173, 0x01eb0173, + 0x01eb8100, 0x01ec1840, 0x01f80173, 0x01f88173, + 0x01f90100, 0x01f98100, 0x01fa01a0, 0x01fa8173, + 0x01fb8240, 0x01fc8130, 0x01fd0240, 0x01fe8330, + 0x02001030, 0x02082030, 0x02182000, 0x02281000, + 0x02302240, 0x02453640, 0x02600130, 0x02608e40, + 0x02678100, 0x02686040, 0x0298a630, 0x02b0a600, + 0x02c381b5, 0x08502631, 0x08638131, 0x08668131, + 0x08682b00, 0x087e8300, 0x09d05011, 0x09f80610, + 0x09fc0620, 0x0e400174, 0x0e408174, 0x0e410174, + 0x0e418174, 0x0e420174, 0x0e428174, 0x0e430174, + 0x0e438180, 0x0e440180, 0x0e448240, 0x0e482b30, + 0x0e5e8330, 0x0ebc8101, 0x0ebe8101, 0x0ec70101, + 0x0f007e40, 0x0f3f1840, 0x0f4b01b5, 0x0f4b81b6, + 0x0f4c01b6, 0x0f4c81b6, 0x0f4d01b7, 0x0f4d8180, + 0x0f4f0130, 0x0f506040, 0x0f800800, 0x0f840830, + 0x0f880600, 0x0f8c0630, 0x0f900800, 0x0f940830, + 0x0f980800, 0x0f9c0830, 0x0fa00600, 0x0fa40630, + 0x0fa801b0, 0x0fa88100, 0x0fa901d3, 0x0fa98100, + 0x0faa01d3, 0x0faa8100, 0x0fab01d3, 0x0fab8100, + 0x0fac8130, 0x0fad8130, 0x0fae8130, 0x0faf8130, + 0x0fb00800, 0x0fb40830, 0x0fb80200, 0x0fb90400, + 0x0fbb0201, 0x0fbc0201, 0x0fbd0201, 0x0fbe0201, + 0x0fc008b7, 0x0fc40867, 0x0fc808b8, 0x0fcc0868, + 0x0fd008b8, 0x0fd40868, 0x0fd80200, 0x0fd901b9, + 0x0fd981b1, 0x0fda01b9, 0x0fdb01b1, 0x0fdb81d7, + 0x0fdc0230, 0x0fdd0230, 0x0fde0161, 0x0fdf0173, + 0x0fe101b9, 0x0fe181b2, 0x0fe201ba, 0x0fe301b2, + 0x0fe381d8, 0x0fe40430, 0x0fe60162, 0x0fe80201, + 0x0fe901d0, 0x0fe981d0, 0x0feb01b0, 0x0feb81d0, + 0x0fec0230, 0x0fed0230, 0x0ff00201, 0x0ff101d3, + 0x0ff181d3, 0x0ff201ba, 0x0ff28101, 0x0ff301b0, + 0x0ff381d3, 0x0ff40231, 0x0ff50230, 0x0ff60131, + 0x0ff901ba, 0x0ff981b2, 0x0ffa01bb, 0x0ffb01b2, + 0x0ffb81d9, 0x0ffc0230, 0x0ffd0230, 0x0ffe0162, + 0x109301a0, 0x109501a0, 0x109581a0, 0x10990131, + 0x10a70101, 0x10b01031, 0x10b81001, 0x10c18240, + 0x125b1a31, 0x12681a01, 0x16003031, 0x16183001, + 0x16300240, 0x16310130, 0x16318130, 0x16320130, + 0x16328100, 0x16330100, 0x16338640, 0x16368130, + 0x16370130, 0x16378130, 0x16380130, 0x16390240, + 0x163a8240, 0x163f0230, 0x16406440, 0x16758440, + 0x16790240, 0x16802600, 0x16938100, 0x16968100, + 0x53202e40, 0x53401c40, 0x53910e40, 0x53993e40, + 0x53bc8440, 0x53be8130, 0x53bf0a40, 0x53c58240, + 0x53c68130, 0x53c80440, 0x53ca0101, 0x53cb1440, + 0x53d50130, 0x53d58130, 0x53d60130, 0x53d68130, + 0x53d70130, 0x53d80130, 0x53d88130, 0x53d90130, + 0x53d98131, 0x53da1040, 0x53e20131, 0x53e28130, + 0x53e30130, 0x53e38440, 0x53e58130, 0x53e60240, + 0x53e80240, 0x53eb0640, 0x53ee0130, 0x53fa8240, + 0x55a98101, 0x55b85020, 0x7d8001b2, 0x7d8081b2, + 0x7d8101b2, 0x7d8181da, 0x7d8201da, 0x7d8281b3, + 0x7d8301b3, 0x7d8981bb, 0x7d8a01bb, 0x7d8a81bb, + 0x7d8b01bc, 0x7d8b81bb, 0x7f909a31, 0x7fa09a01, + 0x82002831, 0x82142801, 0x82582431, 0x826c2401, + 0x82b80b31, 0x82be0f31, 0x82c60731, 0x82ca0231, + 0x82cb8b01, 0x82d18f01, 0x82d98701, 0x82dd8201, + 0x86403331, 0x86603301, 0x86a81631, 0x86b81601, + 0x8c502031, 0x8c602001, 0xb7202031, 0xb7302001, + 0xf4802231, 0xf4912201, +}; + +static const uint8_t case_conv_table2[378] = { + 0x01, 0x00, 0x9c, 0x06, 0x07, 0x4d, 0x03, 0x04, + 0x10, 0x00, 0x8f, 0x0b, 0x00, 0x00, 0x11, 0x00, + 0x08, 0x00, 0x53, 0x4b, 0x52, 0x00, 0x53, 0x00, + 0x54, 0x00, 0x3b, 0x55, 0x56, 0x00, 0x58, 0x5a, + 0x40, 0x5f, 0x5e, 0x00, 0x47, 0x52, 0x63, 0x65, + 0x43, 0x66, 0x00, 0x68, 0x00, 0x6a, 0x00, 0x6c, + 0x00, 0x6e, 0x00, 0x70, 0x00, 0x00, 0x41, 0x00, + 0x00, 0x00, 0x00, 0x1a, 0x00, 0x93, 0x00, 0x00, + 0x20, 0x36, 0x00, 0x28, 0x00, 0x24, 0x00, 0x24, + 0x25, 0x2d, 0x00, 0x13, 0x6d, 0x6f, 0x00, 0x29, + 0x27, 0x2a, 0x14, 0x16, 0x18, 0x1b, 0x1c, 0x41, + 0x1e, 0x42, 0x1f, 0x4e, 0x3c, 0x40, 0x22, 0x21, + 0x44, 0x21, 0x43, 0x26, 0x28, 0x27, 0x29, 0x23, + 0x2b, 0x4b, 0x2d, 0x46, 0x2f, 0x4c, 0x31, 0x4d, + 0x33, 0x47, 0x45, 0x99, 0x00, 0x00, 0x97, 0x91, + 0x7f, 0x80, 0x85, 0x86, 0x12, 0x82, 0x84, 0x78, + 0x79, 0x12, 0x7d, 0xa3, 0x7e, 0x7a, 0x7b, 0x8c, + 0x92, 0x98, 0xa6, 0xa0, 0x87, 0x00, 0x9a, 0xa1, + 0x95, 0x77, 0x33, 0x95, 0x00, 0x90, 0x00, 0x76, + 0x9b, 0x9a, 0x99, 0x98, 0x00, 0x00, 0xa0, 0x00, + 0x9e, 0x00, 0xa3, 0xa2, 0x15, 0x31, 0x32, 0x33, + 0xb7, 0xb8, 0x55, 0xac, 0xab, 0x12, 0x14, 0x1e, + 0x21, 0x22, 0x22, 0x2a, 0x34, 0x35, 0x00, 0xa8, + 0xa9, 0x39, 0x22, 0x4c, 0x00, 0x00, 0x97, 0x01, + 0x5a, 0xda, 0x1d, 0x36, 0x05, 0x00, 0xc7, 0xc6, + 0xc9, 0xc8, 0xcb, 0xca, 0xcd, 0xcc, 0xcf, 0xce, + 0xc4, 0xd8, 0x45, 0xd9, 0x42, 0xda, 0x46, 0xdb, + 0xd1, 0xd3, 0xd5, 0xd7, 0xdd, 0xdc, 0xf1, 0xf9, + 0x01, 0x11, 0x0a, 0x12, 0x80, 0x9f, 0x00, 0x21, + 0x80, 0xa3, 0xf0, 0x00, 0xc0, 0x40, 0xc6, 0x60, + 0xea, 0xde, 0xe6, 0x99, 0xc0, 0x00, 0x00, 0x06, + 0x60, 0xdf, 0x29, 0x00, 0x15, 0x12, 0x06, 0x16, + 0xfb, 0xe0, 0x09, 0x15, 0x12, 0x84, 0x0b, 0xc6, + 0x16, 0x02, 0xe2, 0x06, 0xc0, 0x40, 0x00, 0x46, + 0x60, 0xe1, 0xe3, 0x6d, 0x37, 0x38, 0x39, 0x18, + 0x17, 0x1a, 0x19, 0x00, 0x1d, 0x1c, 0x1f, 0x1e, + 0x00, 0x61, 0xba, 0x67, 0x45, 0x48, 0x00, 0x50, + 0x64, 0x4f, 0x51, 0x00, 0x00, 0x49, 0x00, 0x00, + 0x00, 0xa5, 0xa6, 0xa7, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xb9, 0x00, 0x00, 0x5c, 0x00, 0x4a, 0x00, + 0x5d, 0x57, 0x59, 0x62, 0x60, 0x72, 0x6b, 0x71, + 0x54, 0x00, 0x3e, 0x69, 0xbb, 0x00, 0x5b, 0x00, + 0x00, 0x00, 0x25, 0x00, 0x48, 0xaa, 0x8a, 0x8b, + 0x8c, 0xab, 0xac, 0x58, 0x58, 0xaf, 0x94, 0xb0, + 0x6f, 0xb2, 0x63, 0x62, 0x65, 0x64, 0x67, 0x66, + 0x6c, 0x6d, 0x6e, 0x6f, 0x68, 0x69, 0x6a, 0x6b, + 0x71, 0x70, 0x73, 0x72, 0x75, 0x74, 0x77, 0x76, + 0x79, 0x78, +}; + +static const uint16_t case_conv_ext[58] = { + 0x0399, 0x0308, 0x0301, 0x03a5, 0x0313, 0x0300, 0x0342, 0x0391, + 0x0397, 0x03a9, 0x0046, 0x0049, 0x004c, 0x0053, 0x0069, 0x0307, + 0x02bc, 0x004e, 0x004a, 0x030c, 0x0535, 0x0552, 0x0048, 0x0331, + 0x0054, 0x0057, 0x030a, 0x0059, 0x0041, 0x02be, 0x1f08, 0x1f80, + 0x1f28, 0x1f90, 0x1f68, 0x1fa0, 0x1fba, 0x0386, 0x1fb3, 0x1fca, + 0x0389, 0x1fc3, 0x03a1, 0x1ffa, 0x038f, 0x1ff3, 0x0544, 0x0546, + 0x053b, 0x054e, 0x053d, 0x03b8, 0x0462, 0xa64a, 0x1e60, 0x03c9, + 0x006b, 0x00e5, +}; + +static const uint8_t unicode_prop_Cased1_table[193] = { + 0x40, 0xa9, 0x80, 0x8e, 0x80, 0xfc, 0x80, 0xd3, + 0x80, 0x9b, 0x81, 0x8d, 0x02, 0x80, 0xe1, 0x80, + 0x91, 0x85, 0x9a, 0x01, 0x00, 0x01, 0x11, 0x03, + 0x04, 0x08, 0x01, 0x08, 0x30, 0x08, 0x01, 0x15, + 0x20, 0x00, 0x39, 0x99, 0x31, 0x9d, 0x84, 0x40, + 0x94, 0x80, 0xd6, 0x82, 0xa6, 0x80, 0x41, 0x62, + 0x80, 0xa6, 0x80, 0x4b, 0x72, 0x80, 0x4c, 0x02, + 0xf8, 0x02, 0x80, 0x8f, 0x80, 0xb0, 0x40, 0xdb, + 0x08, 0x80, 0x41, 0xd0, 0x80, 0x8c, 0x80, 0x8f, + 0x8c, 0xe4, 0x03, 0x01, 0x89, 0x00, 0x14, 0x28, + 0x10, 0x11, 0x02, 0x01, 0x18, 0x0b, 0x24, 0x4b, + 0x26, 0x01, 0x01, 0x86, 0xe5, 0x80, 0x60, 0x79, + 0xb6, 0x81, 0x40, 0x91, 0x81, 0xbd, 0x88, 0x94, + 0x05, 0x80, 0x98, 0x80, 0xa2, 0x00, 0x80, 0x9b, + 0x12, 0x82, 0x43, 0x34, 0xa2, 0x06, 0x80, 0x8d, + 0x60, 0x5c, 0x15, 0x01, 0x10, 0xa9, 0x80, 0x88, + 0x60, 0xcc, 0x44, 0xd4, 0x80, 0xc6, 0x01, 0x08, + 0x09, 0x0b, 0x80, 0x8b, 0x00, 0x06, 0x80, 0xc0, + 0x03, 0x0f, 0x06, 0x80, 0x9b, 0x03, 0x04, 0x00, + 0x16, 0x80, 0x41, 0x53, 0x81, 0x98, 0x80, 0x98, + 0x80, 0x9e, 0x80, 0x98, 0x80, 0x9e, 0x80, 0x98, + 0x80, 0x9e, 0x80, 0x98, 0x80, 0x9e, 0x80, 0x98, + 0x07, 0x47, 0x33, 0x89, 0x80, 0x93, 0x2d, 0x41, + 0x04, 0xbd, 0x50, 0xc1, 0x99, 0x85, 0x99, 0x85, + 0x99, +}; + +static const uint8_t unicode_prop_Cased1_index[18] = { + 0xb9, 0x02, 0x80, 0xa0, 0x1e, 0x40, 0x9e, 0xa6, + 0x40, 0xbb, 0x07, 0x01, 0xdb, 0xd6, 0x01, 0x8a, + 0xf1, 0x01, +}; + +static const uint8_t unicode_prop_Case_Ignorable_table[764] = { + 0xa6, 0x05, 0x80, 0x8a, 0x80, 0xa2, 0x00, 0x80, + 0xc6, 0x03, 0x00, 0x03, 0x01, 0x81, 0x41, 0xf6, + 0x40, 0xbf, 0x19, 0x18, 0x88, 0x08, 0x80, 0x40, + 0xfa, 0x86, 0x40, 0xce, 0x04, 0x80, 0xb0, 0xac, + 0x00, 0x01, 0x01, 0x00, 0xab, 0x80, 0x8a, 0x85, + 0x89, 0x8a, 0x00, 0xa2, 0x80, 0x89, 0x94, 0x8f, + 0x80, 0xe4, 0x38, 0x89, 0x03, 0xa0, 0x00, 0x80, + 0x9d, 0x9a, 0xda, 0x8a, 0xb9, 0x8a, 0x18, 0x08, + 0x97, 0x97, 0xaa, 0x82, 0xab, 0x06, 0x0c, 0x88, + 0xa8, 0xb9, 0xb6, 0x00, 0x03, 0x3b, 0x02, 0x86, + 0x89, 0x81, 0x8c, 0x80, 0x8e, 0x80, 0xb9, 0x03, + 0x1f, 0x80, 0x93, 0x81, 0x99, 0x01, 0x81, 0xb8, + 0x03, 0x0b, 0x09, 0x12, 0x80, 0x9d, 0x0a, 0x80, + 0x8a, 0x81, 0xb8, 0x03, 0x20, 0x0b, 0x80, 0x93, + 0x81, 0x95, 0x28, 0x80, 0xb9, 0x01, 0x00, 0x1f, + 0x06, 0x81, 0x8a, 0x81, 0x9d, 0x80, 0xbc, 0x80, + 0x8b, 0x80, 0xb1, 0x02, 0x80, 0xb6, 0x00, 0x14, + 0x10, 0x1e, 0x81, 0x8a, 0x81, 0x9c, 0x80, 0xb9, + 0x01, 0x05, 0x04, 0x81, 0x93, 0x81, 0x9b, 0x81, + 0xb8, 0x0b, 0x1f, 0x80, 0x93, 0x81, 0x9c, 0x80, + 0xc7, 0x06, 0x10, 0x80, 0xd9, 0x01, 0x86, 0x8a, + 0x88, 0xe1, 0x01, 0x88, 0x88, 0x00, 0x86, 0xc8, + 0x81, 0x9a, 0x00, 0x00, 0x80, 0xb6, 0x8d, 0x04, + 0x01, 0x84, 0x8a, 0x80, 0xa3, 0x88, 0x80, 0xe5, + 0x18, 0x28, 0x09, 0x81, 0x98, 0x0b, 0x82, 0x8f, + 0x83, 0x8c, 0x01, 0x0d, 0x80, 0x8e, 0x80, 0xdd, + 0x80, 0x42, 0x5f, 0x82, 0x43, 0xb1, 0x82, 0x9c, + 0x81, 0x9d, 0x81, 0x9d, 0x81, 0xbf, 0x08, 0x37, + 0x01, 0x8a, 0x10, 0x20, 0xac, 0x84, 0xb2, 0x80, + 0xc0, 0x81, 0xa1, 0x80, 0xf5, 0x13, 0x81, 0x88, + 0x05, 0x82, 0x40, 0xda, 0x09, 0x80, 0xb9, 0x00, + 0x30, 0x00, 0x01, 0x3d, 0x89, 0x08, 0xa6, 0x07, + 0x9e, 0xb0, 0x83, 0xaf, 0x00, 0x20, 0x04, 0x80, + 0xa7, 0x88, 0x8b, 0x81, 0x9f, 0x19, 0x08, 0x82, + 0xb7, 0x00, 0x0a, 0x00, 0x82, 0xb9, 0x39, 0x81, + 0xbf, 0x85, 0xd1, 0x10, 0x8c, 0x06, 0x18, 0x28, + 0x11, 0xb1, 0xbe, 0x8c, 0x80, 0xa1, 0xe4, 0x41, + 0xbc, 0x00, 0x82, 0x8a, 0x82, 0x8c, 0x82, 0x8c, + 0x82, 0x8c, 0x81, 0x8b, 0x27, 0x81, 0x89, 0x01, + 0x01, 0x84, 0xb0, 0x20, 0x89, 0x00, 0x8c, 0x80, + 0x8f, 0x8c, 0xb2, 0xa0, 0x4b, 0x8a, 0x81, 0xf0, + 0x82, 0xfc, 0x80, 0x8e, 0x80, 0xdf, 0x9f, 0xae, + 0x80, 0x41, 0xd4, 0x80, 0xa3, 0x1a, 0x24, 0x80, + 0xdc, 0x85, 0xdc, 0x82, 0x60, 0x6f, 0x15, 0x80, + 0x44, 0xe1, 0x85, 0x41, 0x0d, 0x80, 0xe1, 0x18, + 0x89, 0x00, 0x9b, 0x83, 0xcf, 0x81, 0x8d, 0xa1, + 0xcd, 0x80, 0x96, 0x82, 0xe6, 0x12, 0x0f, 0x02, + 0x03, 0x80, 0x98, 0x0c, 0x80, 0x40, 0x96, 0x81, + 0x99, 0x91, 0x8c, 0x80, 0xa5, 0x87, 0x98, 0x8a, + 0xad, 0x82, 0xaf, 0x01, 0x19, 0x81, 0x90, 0x80, + 0x94, 0x81, 0xc1, 0x29, 0x09, 0x81, 0x8b, 0x07, + 0x80, 0xa2, 0x80, 0x8a, 0x80, 0xb2, 0x00, 0x11, + 0x0c, 0x08, 0x80, 0x9a, 0x80, 0x8d, 0x0c, 0x08, + 0x80, 0xe3, 0x84, 0x88, 0x82, 0xf8, 0x01, 0x03, + 0x80, 0x60, 0x4f, 0x2f, 0x80, 0x40, 0x92, 0x90, + 0x42, 0x3c, 0x8f, 0x10, 0x8b, 0x8f, 0xa1, 0x01, + 0x80, 0x40, 0xa8, 0x06, 0x05, 0x80, 0x8a, 0x80, + 0xa2, 0x00, 0x80, 0xae, 0x80, 0xac, 0x81, 0xc2, + 0x80, 0x94, 0x82, 0x42, 0x00, 0x80, 0x40, 0xe1, + 0x80, 0x40, 0x94, 0x84, 0x44, 0x04, 0x28, 0xa9, + 0x80, 0x88, 0x42, 0x45, 0x10, 0x0c, 0x83, 0xa7, + 0x13, 0x80, 0x40, 0xa4, 0x81, 0x42, 0x3c, 0x83, + 0xa5, 0x80, 0x99, 0x20, 0x80, 0x41, 0x3a, 0x81, + 0xce, 0x83, 0xc5, 0x8a, 0xb0, 0x83, 0xfa, 0x80, + 0xb5, 0x8e, 0xa8, 0x01, 0x81, 0x89, 0x82, 0xb0, + 0x19, 0x09, 0x03, 0x80, 0x89, 0x80, 0xb1, 0x82, + 0xa3, 0x20, 0x87, 0xbd, 0x80, 0x8b, 0x81, 0xb3, + 0x88, 0x89, 0x19, 0x80, 0xde, 0x11, 0x00, 0x0d, + 0x01, 0x80, 0x40, 0x9c, 0x02, 0x87, 0x94, 0x81, + 0xb8, 0x0a, 0x80, 0xa4, 0x32, 0x84, 0xc5, 0x85, + 0x8c, 0x00, 0x00, 0x80, 0x8d, 0x81, 0xd4, 0x39, + 0x10, 0x80, 0x96, 0x80, 0xd3, 0x28, 0x03, 0x08, + 0x81, 0x40, 0xed, 0x1d, 0x08, 0x81, 0x9a, 0x81, + 0xd4, 0x39, 0x00, 0x81, 0xe9, 0x00, 0x01, 0x28, + 0x80, 0xe4, 0x00, 0x01, 0x18, 0x84, 0x41, 0x02, + 0x88, 0x01, 0x40, 0xff, 0x08, 0x03, 0x80, 0x40, + 0x8f, 0x19, 0x0b, 0x80, 0x9f, 0x89, 0xa7, 0x29, + 0x1f, 0x80, 0x88, 0x29, 0x82, 0xad, 0x8c, 0x01, + 0x41, 0x95, 0x30, 0x28, 0x80, 0xd1, 0x95, 0x0e, + 0x01, 0x01, 0xf9, 0x2a, 0x00, 0x08, 0x30, 0x80, + 0xc7, 0x0a, 0x00, 0x80, 0x41, 0x5a, 0x81, 0x8a, + 0x81, 0xb3, 0x24, 0x00, 0x80, 0x96, 0x80, 0x54, + 0xd4, 0x90, 0x85, 0x8e, 0x60, 0x2c, 0xc7, 0x8b, + 0x12, 0x49, 0xbf, 0x84, 0xba, 0x86, 0x88, 0x83, + 0x41, 0xfb, 0x82, 0xa7, 0x81, 0x41, 0xe1, 0x80, + 0xbe, 0x90, 0xbf, 0x08, 0x81, 0x60, 0x40, 0x0a, + 0x18, 0x30, 0x81, 0x4c, 0x9d, 0x08, 0x83, 0x52, + 0x5b, 0xad, 0x81, 0x96, 0x42, 0x1f, 0x82, 0x88, + 0x8f, 0x0e, 0x9d, 0x83, 0x40, 0x93, 0x82, 0x47, + 0xba, 0xb6, 0x83, 0xb1, 0x38, 0x8d, 0x80, 0x95, + 0x20, 0x8e, 0x45, 0x4f, 0x30, 0x90, 0x0e, 0x01, + 0x04, 0x84, 0xbd, 0xa0, 0x80, 0x40, 0x9f, 0x8d, + 0x41, 0x6f, 0x80, 0xbc, 0x83, 0x41, 0xfa, 0x84, + 0x40, 0xfd, 0x81, 0x42, 0xdf, 0x86, 0xec, 0x87, + 0x4a, 0xae, 0x84, 0x6c, 0x0c, 0x00, 0x80, 0x9d, + 0xdf, 0xff, 0x40, 0xef, +}; + +static const uint8_t unicode_prop_Case_Ignorable_index[72] = { + 0xbe, 0x05, 0x00, 0xfe, 0x07, 0x00, 0x52, 0x0a, + 0xa0, 0xc1, 0x0b, 0x00, 0x82, 0x0d, 0x00, 0x3f, + 0x10, 0x80, 0xd4, 0x17, 0x40, 0xcf, 0x1a, 0x20, + 0xf5, 0x1c, 0x00, 0x80, 0x20, 0x00, 0x16, 0xa0, + 0x00, 0xc6, 0xa8, 0x00, 0xc2, 0xaa, 0x60, 0x56, + 0xfe, 0x20, 0xb1, 0x07, 0x01, 0x02, 0x10, 0x01, + 0x42, 0x12, 0x41, 0xc4, 0x14, 0x21, 0xe1, 0x19, + 0x81, 0x48, 0x1d, 0x01, 0x44, 0x6b, 0x01, 0x83, + 0xd1, 0x21, 0x3e, 0xe1, 0x01, 0xf0, 0x01, 0x0e, +}; + +static const uint8_t unicode_prop_ID_Start_table[1133] = { + 0xc0, 0x99, 0x85, 0x99, 0xae, 0x80, 0x89, 0x03, + 0x04, 0x96, 0x80, 0x9e, 0x80, 0x41, 0xc9, 0x83, + 0x8b, 0x8d, 0x26, 0x00, 0x80, 0x40, 0x80, 0x20, + 0x09, 0x18, 0x05, 0x00, 0x10, 0x00, 0x93, 0x80, + 0xd2, 0x80, 0x40, 0x8a, 0x87, 0x40, 0xa5, 0x80, + 0xa5, 0x08, 0x85, 0xa8, 0xc6, 0x9a, 0x1b, 0xac, + 0xaa, 0xa2, 0x08, 0xe2, 0x00, 0x8e, 0x0e, 0x81, + 0x89, 0x11, 0x80, 0x8f, 0x00, 0x9d, 0x9c, 0xd8, + 0x8a, 0x80, 0x97, 0xa0, 0x88, 0x0b, 0x04, 0x95, + 0x18, 0x88, 0x02, 0x80, 0x96, 0x98, 0x86, 0x8a, + 0x84, 0x97, 0x05, 0x90, 0xa9, 0xb9, 0xb5, 0x10, + 0x91, 0x06, 0x89, 0x8e, 0x8f, 0x1f, 0x09, 0x81, + 0x95, 0x06, 0x00, 0x13, 0x10, 0x8f, 0x80, 0x8c, + 0x08, 0x82, 0x8d, 0x81, 0x89, 0x07, 0x2b, 0x09, + 0x95, 0x06, 0x01, 0x01, 0x01, 0x9e, 0x18, 0x80, + 0x92, 0x82, 0x8f, 0x88, 0x02, 0x80, 0x95, 0x06, + 0x01, 0x04, 0x10, 0x91, 0x80, 0x8e, 0x81, 0x96, + 0x80, 0x8a, 0x39, 0x09, 0x95, 0x06, 0x01, 0x04, + 0x10, 0x9d, 0x08, 0x82, 0x8e, 0x80, 0x90, 0x00, + 0x2a, 0x10, 0x1a, 0x08, 0x00, 0x0a, 0x0a, 0x12, + 0x8b, 0x95, 0x80, 0xb3, 0x38, 0x10, 0x96, 0x80, + 0x8f, 0x10, 0x99, 0x11, 0x01, 0x81, 0x9d, 0x03, + 0x38, 0x10, 0x96, 0x80, 0x89, 0x04, 0x10, 0x9e, + 0x08, 0x81, 0x8e, 0x81, 0x90, 0x88, 0x02, 0x80, + 0xa8, 0x08, 0x8f, 0x04, 0x17, 0x82, 0x97, 0x2c, + 0x91, 0x82, 0x97, 0x80, 0x88, 0x00, 0x0e, 0xb9, + 0xaf, 0x01, 0x8b, 0x86, 0xb9, 0x08, 0x00, 0x20, + 0x97, 0x00, 0x80, 0x89, 0x01, 0x88, 0x01, 0x20, + 0x80, 0x94, 0x83, 0x9f, 0x80, 0xbe, 0x38, 0xa3, + 0x9a, 0x84, 0xf2, 0xaa, 0x93, 0x80, 0x8f, 0x2b, + 0x1a, 0x02, 0x0e, 0x13, 0x8c, 0x8b, 0x80, 0x90, + 0xa5, 0x00, 0x20, 0x81, 0xaa, 0x80, 0x41, 0x4c, + 0x03, 0x0e, 0x00, 0x03, 0x81, 0xa8, 0x03, 0x81, + 0xa0, 0x03, 0x0e, 0x00, 0x03, 0x81, 0x8e, 0x80, + 0xb8, 0x03, 0x81, 0xc2, 0xa4, 0x8f, 0x8f, 0xd5, + 0x0d, 0x82, 0x42, 0x6b, 0x81, 0x90, 0x80, 0x99, + 0x84, 0xca, 0x82, 0x8a, 0x86, 0x91, 0x8c, 0x92, + 0x8d, 0x91, 0x8d, 0x8c, 0x02, 0x8e, 0xb3, 0xa2, + 0x03, 0x80, 0xc2, 0xd8, 0x86, 0xa8, 0x00, 0x84, + 0xc5, 0x89, 0x9e, 0xb0, 0x9d, 0x0c, 0x8a, 0xab, + 0x83, 0x99, 0xb5, 0x96, 0x88, 0xb4, 0xd1, 0x80, + 0xdc, 0xae, 0x90, 0x87, 0xb5, 0x9d, 0x8c, 0x81, + 0x89, 0xab, 0x99, 0xa3, 0xa8, 0x82, 0x89, 0xa3, + 0x81, 0x8a, 0x84, 0xaa, 0x0a, 0xa8, 0x18, 0x28, + 0x0a, 0x04, 0x40, 0xbf, 0xbf, 0x41, 0x15, 0x0d, + 0x81, 0xa5, 0x0d, 0x0f, 0x00, 0x00, 0x00, 0x80, + 0x9e, 0x81, 0xb4, 0x06, 0x00, 0x12, 0x06, 0x13, + 0x0d, 0x83, 0x8c, 0x22, 0x06, 0xf3, 0x80, 0x8c, + 0x80, 0x8f, 0x8c, 0xe4, 0x03, 0x01, 0x89, 0x00, + 0x0d, 0x28, 0x00, 0x00, 0x80, 0x8f, 0x0b, 0x24, + 0x18, 0x90, 0xa8, 0x4a, 0x76, 0x40, 0xe4, 0x2b, + 0x11, 0x8b, 0xa5, 0x00, 0x20, 0x81, 0xb7, 0x30, + 0x8f, 0x96, 0x88, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x86, 0x42, 0x25, 0x82, 0x98, 0x88, + 0x34, 0x0c, 0x83, 0xd5, 0x1c, 0x80, 0xd9, 0x03, + 0x84, 0xaa, 0x80, 0xdd, 0x90, 0x9f, 0xaf, 0x8f, + 0x41, 0xff, 0x59, 0xbf, 0xbf, 0x60, 0x56, 0x8c, + 0xc2, 0xad, 0x81, 0x41, 0x0c, 0x82, 0x8f, 0x89, + 0x81, 0x93, 0xae, 0x8f, 0x9e, 0x81, 0xcf, 0xa6, + 0x88, 0x81, 0xe6, 0x81, 0xc2, 0x09, 0x00, 0x07, + 0x94, 0x8f, 0x02, 0x03, 0x80, 0x96, 0x9c, 0xb3, + 0x8d, 0xb1, 0xbd, 0x2a, 0x00, 0x81, 0x8a, 0x9b, + 0x89, 0x96, 0x98, 0x9c, 0x86, 0xae, 0x9b, 0x80, + 0x8f, 0x20, 0x89, 0x89, 0x20, 0xa8, 0x96, 0x10, + 0x87, 0x93, 0x96, 0x10, 0x82, 0xb1, 0x00, 0x11, + 0x0c, 0x08, 0x00, 0x97, 0x11, 0x8a, 0x32, 0x8b, + 0x29, 0x29, 0x85, 0x88, 0x30, 0x30, 0xaa, 0x80, + 0x8d, 0x85, 0xf2, 0x9c, 0x60, 0x2b, 0xa3, 0x8b, + 0x96, 0x83, 0xb0, 0x60, 0x21, 0x03, 0x41, 0x6d, + 0x81, 0xe9, 0xa5, 0x86, 0x8b, 0x24, 0x00, 0x89, + 0x80, 0x8c, 0x04, 0x00, 0x01, 0x01, 0x80, 0xeb, + 0xa0, 0x41, 0x6a, 0x91, 0xbf, 0x81, 0xb5, 0xa7, + 0x8b, 0xf3, 0x20, 0x40, 0x86, 0xa3, 0x99, 0x85, + 0x99, 0x8a, 0xd8, 0x15, 0x0d, 0x0d, 0x0a, 0xa2, + 0x8b, 0x80, 0x99, 0x80, 0x92, 0x01, 0x80, 0x8e, + 0x81, 0x8d, 0xa1, 0xfa, 0xc4, 0xb4, 0x41, 0x0a, + 0x9c, 0x82, 0xb0, 0xae, 0x9f, 0x8c, 0x9d, 0x84, + 0xa5, 0x89, 0x9d, 0x81, 0xa3, 0x1f, 0x04, 0xa9, + 0x40, 0x9d, 0x91, 0xa3, 0x83, 0xa3, 0x83, 0xa7, + 0x87, 0xb3, 0x8b, 0x8a, 0x80, 0x8e, 0x06, 0x01, + 0x80, 0x8a, 0x80, 0x8e, 0x06, 0x01, 0x82, 0xb3, + 0x8b, 0x41, 0x36, 0x88, 0x95, 0x89, 0x87, 0x97, + 0x28, 0xa9, 0x80, 0x88, 0xc4, 0x29, 0x00, 0xab, + 0x01, 0x10, 0x81, 0x96, 0x89, 0x96, 0x88, 0x9e, + 0xc0, 0x92, 0x01, 0x89, 0x95, 0x89, 0x99, 0xc5, + 0xb7, 0x29, 0xbf, 0x80, 0x8e, 0x18, 0x10, 0x9c, + 0xa9, 0x9c, 0x82, 0x9c, 0xa2, 0x38, 0x9b, 0x9a, + 0xb5, 0x89, 0x95, 0x89, 0x92, 0x8c, 0x91, 0xed, + 0xc8, 0xb6, 0xb2, 0x8c, 0xb2, 0x8c, 0xa3, 0xa5, + 0x9b, 0x88, 0x96, 0x40, 0xf9, 0xa9, 0x29, 0x8f, + 0x82, 0xba, 0x9c, 0x89, 0x07, 0x95, 0xa9, 0x91, + 0xad, 0x94, 0x9a, 0x96, 0x8b, 0xb4, 0xb8, 0x09, + 0x80, 0x8c, 0xac, 0x9f, 0x98, 0x99, 0xa3, 0x9c, + 0x01, 0x07, 0xa2, 0x10, 0x8b, 0xaf, 0x8d, 0x83, + 0x94, 0x00, 0x80, 0xa2, 0x91, 0x80, 0x98, 0x92, + 0x81, 0xbe, 0x30, 0x00, 0x18, 0x8e, 0x80, 0x89, + 0x86, 0xae, 0xa5, 0x39, 0x09, 0x95, 0x06, 0x01, + 0x04, 0x10, 0x91, 0x80, 0x8b, 0x84, 0x9d, 0x89, + 0x00, 0x08, 0x80, 0xa5, 0x00, 0x98, 0x00, 0x80, + 0xab, 0xb4, 0x91, 0x83, 0x93, 0x82, 0x9d, 0xaf, + 0x93, 0x08, 0x80, 0x40, 0xb7, 0xae, 0xa8, 0x83, + 0xa3, 0xaf, 0x93, 0x80, 0xba, 0xaa, 0x8c, 0x80, + 0xc6, 0x9a, 0xa4, 0x86, 0x40, 0xb8, 0xab, 0xf3, + 0xbf, 0x9e, 0x39, 0x01, 0x38, 0x08, 0x97, 0x8e, + 0x00, 0x80, 0xdd, 0x39, 0xa6, 0x8f, 0x00, 0x80, + 0x9b, 0x80, 0x89, 0xa7, 0x30, 0x94, 0x80, 0x8a, + 0xad, 0x92, 0x80, 0x91, 0xc8, 0x40, 0xc6, 0xa0, + 0x9e, 0x88, 0x80, 0xa4, 0x90, 0x80, 0xb0, 0x9d, + 0xef, 0x30, 0x08, 0xa5, 0x94, 0x80, 0x98, 0x28, + 0x08, 0x9f, 0x8d, 0x80, 0x41, 0x46, 0x92, 0x8e, + 0x00, 0x8c, 0x80, 0xa1, 0xfb, 0x80, 0xce, 0x43, + 0x99, 0xe5, 0xee, 0x90, 0x40, 0xc3, 0x4a, 0x4b, + 0xe0, 0x8e, 0x44, 0x2f, 0x90, 0x85, 0x98, 0x4f, + 0x9a, 0x84, 0x42, 0x46, 0x5a, 0xb8, 0x9d, 0x46, + 0xe1, 0x42, 0x38, 0x86, 0x9e, 0x90, 0xce, 0x90, + 0x9d, 0x91, 0xaf, 0x8f, 0x83, 0x9e, 0x94, 0x84, + 0x92, 0x41, 0xaf, 0xac, 0x40, 0xd2, 0xbf, 0xff, + 0xca, 0x20, 0xc1, 0x8c, 0xbf, 0x08, 0x80, 0x9b, + 0x57, 0xf7, 0x87, 0x44, 0xd5, 0xa8, 0x89, 0x60, + 0x22, 0xe6, 0x18, 0x30, 0x08, 0x41, 0x22, 0x8e, + 0x80, 0x9c, 0x11, 0x80, 0x8d, 0x1f, 0x41, 0x8b, + 0x49, 0x03, 0xea, 0x84, 0x8c, 0x82, 0x88, 0x86, + 0x89, 0x57, 0x65, 0xd4, 0x80, 0xc6, 0x01, 0x08, + 0x09, 0x0b, 0x80, 0x8b, 0x00, 0x06, 0x80, 0xc0, + 0x03, 0x0f, 0x06, 0x80, 0x9b, 0x03, 0x04, 0x00, + 0x16, 0x80, 0x41, 0x53, 0x81, 0x98, 0x80, 0x98, + 0x80, 0x9e, 0x80, 0x98, 0x80, 0x9e, 0x80, 0x98, + 0x80, 0x9e, 0x80, 0x98, 0x80, 0x9e, 0x80, 0x98, + 0x07, 0x47, 0x33, 0x9e, 0x2d, 0x41, 0x04, 0xbd, + 0x40, 0x91, 0xac, 0x89, 0x86, 0x8f, 0x80, 0x41, + 0x40, 0x9d, 0x91, 0xab, 0x41, 0xe3, 0x9b, 0x40, + 0xe3, 0x9d, 0x08, 0x41, 0xee, 0x30, 0x18, 0x08, + 0x8e, 0x80, 0x40, 0xc4, 0xba, 0xc3, 0x30, 0x44, + 0xb3, 0x18, 0x9a, 0x01, 0x00, 0x08, 0x80, 0x89, + 0x03, 0x00, 0x00, 0x28, 0x18, 0x00, 0x00, 0x02, + 0x01, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x00, 0x0b, 0x06, 0x03, 0x03, 0x00, 0x80, 0x89, + 0x80, 0x90, 0x22, 0x04, 0x80, 0x90, 0x51, 0x43, + 0x60, 0xa6, 0xdf, 0x9f, 0x50, 0x39, 0x85, 0x40, + 0xdd, 0x81, 0x56, 0x81, 0x8d, 0x5d, 0x30, 0x8e, + 0x42, 0x6d, 0x49, 0xa1, 0x42, 0x1d, 0x45, 0xe1, + 0x53, 0x4a, 0x84, 0x50, 0x5f, +}; + +static const uint8_t unicode_prop_ID_Start_index[108] = { + 0xf6, 0x03, 0x20, 0xa6, 0x07, 0x00, 0xa9, 0x09, + 0x20, 0xb1, 0x0a, 0x00, 0xba, 0x0b, 0x20, 0x3b, + 0x0d, 0x20, 0xc7, 0x0e, 0x20, 0x49, 0x12, 0x00, + 0x9b, 0x16, 0x00, 0xac, 0x19, 0x00, 0xc0, 0x1d, + 0x80, 0x80, 0x20, 0x20, 0x70, 0x2d, 0x00, 0x00, + 0x32, 0x00, 0xdd, 0xa7, 0x00, 0x4c, 0xaa, 0x20, + 0xc7, 0xd7, 0x20, 0xfc, 0xfd, 0x20, 0x9d, 0x02, + 0x21, 0x96, 0x05, 0x01, 0x9f, 0x08, 0x01, 0x49, + 0x0c, 0x21, 0x76, 0x10, 0x21, 0xa9, 0x12, 0x01, + 0xb0, 0x14, 0x01, 0x42, 0x19, 0x41, 0x90, 0x1c, + 0x01, 0xf1, 0x2f, 0x21, 0x90, 0x6b, 0x21, 0x33, + 0xb1, 0x21, 0x06, 0xd5, 0x01, 0xc3, 0xd7, 0x01, + 0xff, 0xe7, 0x21, 0x63, 0xee, 0x01, 0x5e, 0xee, + 0x42, 0xb0, 0x23, 0x03, +}; + +static const uint8_t unicode_prop_ID_Continue1_table[695] = { + 0xaf, 0x89, 0xa4, 0x80, 0xd6, 0x80, 0x42, 0x47, + 0xef, 0x96, 0x80, 0x40, 0xfa, 0x84, 0x41, 0x08, + 0xac, 0x00, 0x01, 0x01, 0x00, 0xc7, 0x8a, 0xaf, + 0x9e, 0x28, 0xe4, 0x31, 0x29, 0x08, 0x19, 0x89, + 0x96, 0x80, 0x9d, 0x9a, 0xda, 0x8a, 0x8e, 0x89, + 0xa0, 0x88, 0x88, 0x80, 0x97, 0x18, 0x88, 0x02, + 0x04, 0xaa, 0x82, 0xba, 0x88, 0xa9, 0x97, 0x80, + 0xa0, 0xb5, 0x10, 0x91, 0x06, 0x89, 0x09, 0x89, + 0x90, 0x82, 0xb7, 0x00, 0x31, 0x09, 0x82, 0x88, + 0x80, 0x89, 0x09, 0x89, 0x8d, 0x01, 0x82, 0xb7, + 0x00, 0x23, 0x09, 0x12, 0x80, 0x93, 0x8b, 0x10, + 0x8a, 0x82, 0xb7, 0x00, 0x38, 0x10, 0x82, 0x93, + 0x09, 0x89, 0x89, 0x28, 0x82, 0xb7, 0x00, 0x31, + 0x09, 0x16, 0x82, 0x89, 0x09, 0x89, 0x91, 0x80, + 0xba, 0x22, 0x10, 0x83, 0x88, 0x80, 0x8d, 0x89, + 0x8f, 0x84, 0xb6, 0x00, 0x30, 0x10, 0x1e, 0x81, + 0x8a, 0x09, 0x89, 0x90, 0x82, 0xb7, 0x00, 0x30, + 0x10, 0x1e, 0x81, 0x8a, 0x09, 0x89, 0x10, 0x8b, + 0x83, 0xb6, 0x08, 0x30, 0x10, 0x83, 0x88, 0x80, + 0x89, 0x09, 0x89, 0x90, 0x82, 0xc5, 0x03, 0x28, + 0x00, 0x3d, 0x89, 0x09, 0xbc, 0x01, 0x86, 0x8b, + 0x38, 0x89, 0xd6, 0x01, 0x88, 0x8a, 0x30, 0x89, + 0xbd, 0x0d, 0x89, 0x8a, 0x00, 0x00, 0x03, 0x81, + 0xb0, 0x93, 0x01, 0x84, 0x8a, 0x80, 0xa3, 0x88, + 0x80, 0xe3, 0x93, 0x80, 0x89, 0x8b, 0x1b, 0x10, + 0x11, 0x32, 0x83, 0x8c, 0x8b, 0x80, 0x8e, 0x42, + 0xbe, 0x82, 0x88, 0x88, 0x43, 0x9f, 0x83, 0x9b, + 0x82, 0x9c, 0x81, 0x9d, 0x81, 0xbf, 0x9f, 0x88, + 0x01, 0x89, 0xa0, 0x10, 0x8a, 0x40, 0x8e, 0x80, + 0xf5, 0x8b, 0x83, 0x8b, 0x89, 0x89, 0xff, 0x8a, + 0xbb, 0x84, 0xb8, 0x89, 0x80, 0x9c, 0x81, 0x8a, + 0x85, 0x89, 0x95, 0x8d, 0x80, 0x8f, 0xb0, 0x84, + 0xae, 0x90, 0x8a, 0x89, 0x90, 0x88, 0x8b, 0x82, + 0x9d, 0x8c, 0x81, 0x89, 0xab, 0x8d, 0xaf, 0x93, + 0x87, 0x89, 0x85, 0x89, 0xf5, 0x10, 0x94, 0x18, + 0x28, 0x0a, 0x40, 0xc5, 0xbf, 0x42, 0x0b, 0x81, + 0xb0, 0x81, 0x92, 0x80, 0xfa, 0x8c, 0x18, 0x82, + 0x8b, 0x4b, 0xfd, 0x82, 0x40, 0x8c, 0x80, 0xdf, + 0x9f, 0x42, 0x29, 0x85, 0xe8, 0x81, 0xdf, 0x80, + 0x60, 0x75, 0x23, 0x89, 0xc4, 0x03, 0x89, 0x9f, + 0x81, 0xcf, 0x81, 0x41, 0x0f, 0x02, 0x03, 0x80, + 0x96, 0x23, 0x80, 0xd2, 0x81, 0xb1, 0x91, 0x89, + 0x89, 0x85, 0x91, 0x8c, 0x8a, 0x9b, 0x87, 0x98, + 0x8c, 0xab, 0x83, 0xae, 0x8d, 0x8e, 0x89, 0x8a, + 0x80, 0x89, 0x89, 0xae, 0x8d, 0x8b, 0x07, 0x09, + 0x89, 0xa0, 0x82, 0xb1, 0x00, 0x11, 0x0c, 0x08, + 0x80, 0xa8, 0x24, 0x81, 0x40, 0xeb, 0x38, 0x09, + 0x89, 0x60, 0x4f, 0x23, 0x80, 0x42, 0xe0, 0x8f, + 0x8f, 0x8f, 0x11, 0x97, 0x82, 0x40, 0xbf, 0x89, + 0xa4, 0x80, 0xa4, 0x80, 0x42, 0x96, 0x80, 0x40, + 0xe1, 0x80, 0x40, 0x94, 0x84, 0x41, 0x24, 0x89, + 0x45, 0x56, 0x10, 0x0c, 0x83, 0xa7, 0x13, 0x80, + 0x40, 0xa4, 0x81, 0x42, 0x3c, 0x1f, 0x89, 0x85, + 0x89, 0x9e, 0x84, 0x41, 0x3c, 0x81, 0xce, 0x83, + 0xc5, 0x8a, 0xb0, 0x83, 0xf9, 0x82, 0xb4, 0x8e, + 0x9e, 0x8a, 0x09, 0x89, 0x83, 0xac, 0x8a, 0x30, + 0xac, 0x89, 0x2a, 0xa3, 0x8d, 0x80, 0x89, 0x21, + 0xab, 0x80, 0x8b, 0x82, 0xaf, 0x8d, 0x3b, 0x80, + 0x8b, 0xd1, 0x8b, 0x28, 0x08, 0x40, 0x9c, 0x8b, + 0x84, 0x89, 0x2b, 0xb6, 0x08, 0x31, 0x09, 0x82, + 0x88, 0x80, 0x89, 0x09, 0x32, 0x84, 0xc2, 0x88, + 0x00, 0x08, 0x03, 0x04, 0x00, 0x8d, 0x81, 0xd1, + 0x91, 0x88, 0x89, 0x18, 0xd0, 0x93, 0x8b, 0x89, + 0x40, 0xd4, 0x31, 0x88, 0x9a, 0x81, 0xd1, 0x90, + 0x8e, 0x89, 0xd0, 0x8c, 0x87, 0x89, 0x85, 0x93, + 0xb8, 0x8e, 0x83, 0x89, 0x40, 0xf1, 0x8e, 0x40, + 0xa4, 0x89, 0xc5, 0x28, 0x09, 0x18, 0x00, 0x81, + 0x8b, 0x89, 0xf6, 0x31, 0x32, 0x80, 0x9b, 0x89, + 0xa7, 0x30, 0x1f, 0x80, 0x88, 0x8a, 0xad, 0x8f, + 0x41, 0x55, 0x89, 0xb4, 0x38, 0x87, 0x8f, 0x89, + 0xb7, 0x95, 0x80, 0x8d, 0xf9, 0x2a, 0x00, 0x08, + 0x30, 0x07, 0x89, 0xaf, 0x20, 0x08, 0x27, 0x89, + 0x41, 0x48, 0x83, 0x88, 0x08, 0x80, 0xaf, 0x32, + 0x84, 0x8c, 0x8a, 0x54, 0xe4, 0x05, 0x8e, 0x60, + 0x2c, 0xc7, 0x9b, 0x49, 0x25, 0x89, 0xd5, 0x89, + 0xa5, 0x84, 0xba, 0x86, 0x98, 0x89, 0x42, 0x15, + 0x89, 0x41, 0xd4, 0x00, 0xb6, 0x33, 0xd0, 0x80, + 0x8a, 0x81, 0x60, 0x4c, 0xaa, 0x81, 0x50, 0x50, + 0x89, 0x42, 0x05, 0xad, 0x81, 0x96, 0x42, 0x1d, + 0x22, 0x2f, 0x39, 0x86, 0x9d, 0x83, 0x40, 0x93, + 0x82, 0x45, 0x88, 0xb1, 0x41, 0xff, 0xb6, 0x83, + 0xb1, 0x38, 0x8d, 0x80, 0x95, 0x20, 0x8e, 0x45, + 0x4f, 0x30, 0x90, 0x0e, 0x01, 0x04, 0xe3, 0x80, + 0x40, 0x9f, 0x86, 0x88, 0x89, 0x41, 0x63, 0x80, + 0xbc, 0x8d, 0x41, 0xf1, 0x8d, 0x40, 0xf3, 0x08, + 0x89, 0x42, 0xd4, 0x86, 0xec, 0x34, 0x89, 0x52, + 0x95, 0x89, 0x6c, 0x05, 0x05, 0x40, 0xef, +}; + +static const uint8_t unicode_prop_ID_Continue1_index[66] = { + 0xfa, 0x06, 0x00, 0x70, 0x09, 0x00, 0xf0, 0x0a, + 0x40, 0x57, 0x0c, 0x00, 0xf0, 0x0d, 0x60, 0xc7, + 0x0f, 0x20, 0xea, 0x17, 0x40, 0x05, 0x1b, 0x00, + 0x0e, 0x20, 0x00, 0xa0, 0xa6, 0x20, 0xe6, 0xa9, + 0x20, 0x10, 0xfe, 0x00, 0x40, 0x0a, 0x01, 0xc3, + 0x10, 0x01, 0x4e, 0x13, 0x01, 0x41, 0x16, 0x01, + 0x0b, 0x1a, 0x01, 0xaa, 0x1d, 0x01, 0x7a, 0x6d, + 0x21, 0x45, 0xd2, 0x21, 0xaf, 0xe2, 0x01, 0xf0, + 0x01, 0x0e, +}; + +static const uint8_t unicode_prop_White_Space_table[22] = { + 0x88, 0x84, 0x91, 0x80, 0xe3, 0x80, 0x99, 0x80, + 0x55, 0xde, 0x80, 0x49, 0x7e, 0x8a, 0x9c, 0x0c, + 0x80, 0xae, 0x80, 0x4f, 0x9f, 0x80, +}; + +static const uint8_t unicode_prop_White_Space_index[3] = { + 0x01, 0x30, 0x00, +}; + +static const uint8_t unicode_cc_table[916] = { + 0xb2, 0xcf, 0xd4, 0x00, 0xe8, 0x03, 0xdc, 0x00, + 0xe8, 0x00, 0xd8, 0x04, 0xdc, 0x01, 0xca, 0x03, + 0xdc, 0x01, 0xca, 0x0a, 0xdc, 0x04, 0x01, 0x03, + 0xdc, 0xc7, 0x00, 0xf0, 0xc0, 0x02, 0xdc, 0xc2, + 0x01, 0xdc, 0x80, 0xc2, 0x03, 0xdc, 0xc0, 0x00, + 0xe8, 0x01, 0xdc, 0xc0, 0x41, 0xe9, 0x00, 0xea, + 0x41, 0xe9, 0x00, 0xea, 0x00, 0xe9, 0xcc, 0xb0, + 0xe2, 0xc4, 0xb0, 0xd8, 0x00, 0xdc, 0xc3, 0x00, + 0xdc, 0xc2, 0x00, 0xde, 0x00, 0xdc, 0xc5, 0x05, + 0xdc, 0xc1, 0x00, 0xdc, 0xc1, 0x00, 0xde, 0x00, + 0xe4, 0xc0, 0x49, 0x0a, 0x43, 0x13, 0x80, 0x00, + 0x17, 0x80, 0x41, 0x18, 0x80, 0xc0, 0x00, 0xdc, + 0x80, 0x00, 0x12, 0xb0, 0x17, 0xc7, 0x42, 0x1e, + 0xaf, 0x47, 0x1b, 0xc1, 0x01, 0xdc, 0xc4, 0x00, + 0xdc, 0xc1, 0x00, 0xdc, 0x8f, 0x00, 0x23, 0xb0, + 0x34, 0xc6, 0x81, 0xc3, 0x00, 0xdc, 0xc0, 0x81, + 0xc1, 0x80, 0x00, 0xdc, 0xc1, 0x00, 0xdc, 0xa2, + 0x00, 0x24, 0x9d, 0xc0, 0x00, 0xdc, 0xc1, 0x00, + 0xdc, 0xc1, 0x02, 0xdc, 0xc0, 0x01, 0xdc, 0xc0, + 0x00, 0xdc, 0xc2, 0x00, 0xdc, 0xc0, 0x00, 0xdc, + 0xc0, 0x00, 0xdc, 0xc0, 0x00, 0xdc, 0xc1, 0xb0, + 0x6f, 0xc6, 0x00, 0xdc, 0xc0, 0x88, 0x00, 0xdc, + 0x97, 0xc3, 0x80, 0xc8, 0x80, 0xc2, 0x80, 0xc4, + 0xaa, 0x02, 0xdc, 0xb0, 0x0a, 0xc1, 0x02, 0xdc, + 0xc3, 0xa9, 0xc4, 0x04, 0xdc, 0xcd, 0x80, 0x00, + 0xdc, 0xc1, 0x00, 0xdc, 0xc1, 0x00, 0xdc, 0xc2, + 0x02, 0xdc, 0x42, 0x1b, 0xc2, 0x00, 0xdc, 0xc1, + 0x01, 0xdc, 0xc4, 0xb0, 0x0b, 0x00, 0x07, 0x8f, + 0x00, 0x09, 0x82, 0xc0, 0x00, 0xdc, 0xc1, 0xb0, + 0x36, 0x00, 0x07, 0x8f, 0x00, 0x09, 0xaf, 0xc0, + 0xb0, 0x0c, 0x00, 0x07, 0x8f, 0x00, 0x09, 0xb0, + 0x3d, 0x00, 0x07, 0x8f, 0x00, 0x09, 0xb0, 0x3d, + 0x00, 0x07, 0x8f, 0x00, 0x09, 0xb0, 0x4e, 0x00, + 0x09, 0xb0, 0x3d, 0x00, 0x07, 0x8f, 0x00, 0x09, + 0x86, 0x00, 0x54, 0x00, 0x5b, 0xb0, 0x34, 0x00, + 0x07, 0x8f, 0x00, 0x09, 0xb0, 0x3c, 0x01, 0x09, + 0x8f, 0x00, 0x09, 0xb0, 0x4b, 0x00, 0x09, 0xb0, + 0x3c, 0x01, 0x67, 0x00, 0x09, 0x8c, 0x03, 0x6b, + 0xb0, 0x3b, 0x01, 0x76, 0x00, 0x09, 0x8c, 0x03, + 0x7a, 0xb0, 0x1b, 0x01, 0xdc, 0x9a, 0x00, 0xdc, + 0x80, 0x00, 0xdc, 0x80, 0x00, 0xd8, 0xb0, 0x06, + 0x41, 0x81, 0x80, 0x00, 0x84, 0x84, 0x03, 0x82, + 0x81, 0x00, 0x82, 0x80, 0xc1, 0x00, 0x09, 0x80, + 0xc1, 0xb0, 0x0d, 0x00, 0xdc, 0xb0, 0x3f, 0x00, + 0x07, 0x80, 0x01, 0x09, 0xb0, 0x21, 0x00, 0xdc, + 0xb2, 0x9e, 0xc2, 0xb3, 0x83, 0x01, 0x09, 0x9d, + 0x00, 0x09, 0xb0, 0x6c, 0x00, 0x09, 0x89, 0xc0, + 0xb0, 0x9a, 0x00, 0xe4, 0xb0, 0x5e, 0x00, 0xde, + 0xc0, 0x00, 0xdc, 0xb0, 0xaa, 0xc0, 0x00, 0xdc, + 0xb0, 0x16, 0x00, 0x09, 0x93, 0xc7, 0x81, 0x00, + 0xdc, 0xaf, 0xc4, 0x05, 0xdc, 0xc1, 0x00, 0xdc, + 0x80, 0x01, 0xdc, 0xc1, 0x01, 0xdc, 0xc4, 0x00, + 0xdc, 0xc3, 0xb0, 0x34, 0x00, 0x07, 0x8e, 0x00, + 0x09, 0xa5, 0xc0, 0x00, 0xdc, 0xc6, 0xb0, 0x05, + 0x01, 0x09, 0xb0, 0x09, 0x00, 0x07, 0x8a, 0x01, + 0x09, 0xb0, 0x12, 0x00, 0x07, 0xb0, 0x67, 0xc2, + 0x41, 0x00, 0x04, 0xdc, 0xc1, 0x03, 0xdc, 0xc0, + 0x41, 0x00, 0x05, 0x01, 0x83, 0x00, 0xdc, 0x85, + 0xc0, 0x82, 0xc1, 0xb0, 0x95, 0xc1, 0x00, 0xdc, + 0xc6, 0x00, 0xdc, 0xc1, 0x00, 0xea, 0x00, 0xd6, + 0x00, 0xdc, 0x00, 0xca, 0xe4, 0x00, 0xe8, 0x01, + 0xe4, 0x00, 0xdc, 0x00, 0xda, 0xc0, 0x00, 0xe9, + 0x00, 0xdc, 0xc0, 0x00, 0xdc, 0xb2, 0x9f, 0xc1, + 0x01, 0x01, 0xc3, 0x02, 0x01, 0xc1, 0x83, 0xc0, + 0x82, 0x01, 0x01, 0xc0, 0x00, 0xdc, 0xc0, 0x01, + 0x01, 0x03, 0xdc, 0xc0, 0xb8, 0x03, 0xcd, 0xc2, + 0xb0, 0x5c, 0x00, 0x09, 0xb0, 0x2f, 0xdf, 0xb1, + 0xf9, 0x00, 0xda, 0x00, 0xe4, 0x00, 0xe8, 0x00, + 0xde, 0x01, 0xe0, 0xb0, 0x38, 0x01, 0x08, 0xb8, + 0x6d, 0xa3, 0xc0, 0x83, 0xc9, 0x9f, 0xc1, 0xb0, + 0x1f, 0xc1, 0xb0, 0xe3, 0x00, 0x09, 0xa4, 0x00, + 0x09, 0xb0, 0x66, 0x00, 0x09, 0x9a, 0xd1, 0xb0, + 0x08, 0x02, 0xdc, 0xa4, 0x00, 0x09, 0xb0, 0x2e, + 0x00, 0x07, 0x8b, 0x00, 0x09, 0xb0, 0xbe, 0xc0, + 0x80, 0xc1, 0x00, 0xdc, 0x81, 0xc1, 0x84, 0xc1, + 0x80, 0xc0, 0xb0, 0x03, 0x00, 0x09, 0xb0, 0xc5, + 0x00, 0x09, 0xb8, 0x46, 0xff, 0x00, 0x1a, 0xb2, + 0xd0, 0xc6, 0x06, 0xdc, 0xc1, 0xb3, 0x9c, 0x00, + 0xdc, 0xb0, 0xb1, 0x00, 0xdc, 0xb0, 0x64, 0xc4, + 0xb6, 0x61, 0x00, 0xdc, 0x80, 0xc0, 0xa7, 0xc0, + 0x00, 0x01, 0x00, 0xdc, 0x83, 0x00, 0x09, 0xb0, + 0x74, 0xc0, 0x00, 0xdc, 0xb2, 0x0c, 0xc3, 0xb0, + 0x10, 0xc4, 0xb1, 0x0c, 0xc1, 0xb0, 0x1f, 0x02, + 0xdc, 0xb0, 0x15, 0x01, 0xdc, 0xc2, 0x00, 0xdc, + 0xc0, 0x03, 0xdc, 0xb0, 0x00, 0xc0, 0x00, 0xdc, + 0xc0, 0x00, 0xdc, 0xb0, 0x8f, 0x00, 0x09, 0xa8, + 0x00, 0x09, 0x8d, 0x00, 0x09, 0xb0, 0x08, 0x00, + 0x09, 0x00, 0x07, 0xb0, 0x14, 0xc2, 0xaf, 0x01, + 0x09, 0xb0, 0x0d, 0x00, 0x07, 0xb0, 0x1b, 0x00, + 0x09, 0x88, 0x00, 0x07, 0xb0, 0x39, 0x00, 0x09, + 0x00, 0x07, 0xb0, 0x81, 0x00, 0x07, 0x00, 0x09, + 0xb0, 0x1f, 0x01, 0x07, 0x8f, 0x00, 0x09, 0x97, + 0xc6, 0x82, 0xc4, 0xb0, 0x28, 0x02, 0x09, 0xb0, + 0x40, 0x00, 0x09, 0x82, 0x00, 0x07, 0x96, 0xc0, + 0xb0, 0x32, 0x00, 0x09, 0x00, 0x07, 0xb0, 0xca, + 0x00, 0x09, 0x00, 0x07, 0xb0, 0x4d, 0x00, 0x09, + 0xb0, 0x45, 0x00, 0x09, 0x00, 0x07, 0xb0, 0x42, + 0x00, 0x09, 0xb0, 0xdc, 0x00, 0x09, 0x00, 0x07, + 0xb0, 0xd1, 0x01, 0x09, 0x83, 0x00, 0x07, 0xb0, + 0x6b, 0x00, 0x09, 0xb0, 0x22, 0x00, 0x09, 0x91, + 0x00, 0x09, 0xb0, 0x20, 0x00, 0x09, 0xb1, 0x74, + 0x00, 0x09, 0xb0, 0xd1, 0x00, 0x07, 0x80, 0x01, + 0x09, 0xb0, 0x20, 0x00, 0x09, 0xb1, 0x78, 0x01, + 0x09, 0xb8, 0x39, 0xbb, 0x00, 0x09, 0xb8, 0x01, + 0x8f, 0x04, 0x01, 0xb0, 0x0a, 0xc6, 0xb4, 0x88, + 0x01, 0x06, 0xb8, 0x44, 0x7b, 0x00, 0x01, 0xb8, + 0x0c, 0x95, 0x01, 0xd8, 0x02, 0x01, 0x82, 0x00, + 0xe2, 0x04, 0xd8, 0x87, 0x07, 0xdc, 0x81, 0xc4, + 0x01, 0xdc, 0x9d, 0xc3, 0xb0, 0x63, 0xc2, 0xb8, + 0x05, 0x8a, 0xc6, 0x80, 0xd0, 0x81, 0xc6, 0x80, + 0xc1, 0x80, 0xc4, 0xb0, 0x33, 0xc0, 0xb0, 0x6f, + 0xc6, 0xb1, 0x46, 0xc0, 0xb0, 0x0c, 0xc3, 0xb1, + 0xcb, 0x01, 0xe8, 0x00, 0xdc, 0xc0, 0xb0, 0xcd, + 0xc0, 0x00, 0xdc, 0xb2, 0xaf, 0x06, 0xdc, 0xb0, + 0x3c, 0xc5, 0x00, 0x07, +}; + +static const uint8_t unicode_cc_index[87] = { + 0x4d, 0x03, 0x00, 0x97, 0x05, 0x20, 0xc6, 0x05, + 0x00, 0xe7, 0x06, 0x00, 0x45, 0x07, 0x00, 0x9c, + 0x08, 0x00, 0x4d, 0x09, 0x00, 0x3c, 0x0b, 0x00, + 0x3d, 0x0d, 0x00, 0x36, 0x0f, 0x00, 0x38, 0x10, + 0x20, 0x3a, 0x19, 0x00, 0xcb, 0x1a, 0x20, 0xd3, + 0x1c, 0x00, 0xcf, 0x1d, 0x00, 0xe2, 0x20, 0x00, + 0x2e, 0x30, 0x20, 0x2b, 0xa9, 0x20, 0xed, 0xab, + 0x00, 0x39, 0x0a, 0x01, 0x4c, 0x0f, 0x01, 0x35, + 0x11, 0x21, 0x66, 0x13, 0x01, 0x40, 0x16, 0x01, + 0x47, 0x1a, 0x01, 0xf0, 0x6a, 0x21, 0x8a, 0xd1, + 0x01, 0xec, 0xe4, 0x21, 0x4b, 0xe9, 0x01, +}; + +static const uint32_t unicode_decomp_table1[709] = { + 0x00280081, 0x002a0097, 0x002a8081, 0x002bc097, + 0x002c8115, 0x002d0097, 0x002d4081, 0x002e0097, + 0x002e4115, 0x002f0199, 0x00302016, 0x00400842, + 0x00448a42, 0x004a0442, 0x004c0096, 0x004c8117, + 0x004d0242, 0x004e4342, 0x004fc12f, 0x0050c342, + 0x005240bf, 0x00530342, 0x00550942, 0x005a0842, + 0x005e0096, 0x005e4342, 0x005fc081, 0x00680142, + 0x006bc142, 0x00710185, 0x0071c317, 0x00734844, + 0x00778344, 0x00798342, 0x007b02be, 0x007c4197, + 0x007d0142, 0x007e0444, 0x00800e42, 0x00878142, + 0x00898744, 0x00ac0483, 0x00b60317, 0x00b80283, + 0x00d00214, 0x00d10096, 0x00dd0080, 0x00de8097, + 0x00df8080, 0x00e10097, 0x00e1413e, 0x00e1c080, + 0x00e204be, 0x00ea83ae, 0x00f282ae, 0x00f401ad, + 0x00f4c12e, 0x00f54103, 0x00fc0303, 0x00fe4081, + 0x0100023e, 0x0101c0be, 0x010301be, 0x010640be, + 0x010e40be, 0x0114023e, 0x0115c0be, 0x011701be, + 0x011d8144, 0x01304144, 0x01340244, 0x01358144, + 0x01368344, 0x01388344, 0x013a8644, 0x013e0144, + 0x0161c085, 0x018882ae, 0x019d422f, 0x01b00184, + 0x01b4c084, 0x024a4084, 0x024c4084, 0x024d0084, + 0x0256042e, 0x0272c12e, 0x02770120, 0x0277c084, + 0x028cc084, 0x028d8084, 0x029641ae, 0x02978084, + 0x02d20084, 0x02d2c12e, 0x02d70120, 0x02e50084, + 0x02f281ae, 0x03120084, 0x03300084, 0x0331c122, + 0x0332812e, 0x035281ae, 0x03768084, 0x037701ae, + 0x038cc085, 0x03acc085, 0x03b7012f, 0x03c30081, + 0x03d0c084, 0x03d34084, 0x03d48084, 0x03d5c084, + 0x03d70084, 0x03da4084, 0x03dcc084, 0x03dd412e, + 0x03ddc085, 0x03de0084, 0x03de4085, 0x03e04084, + 0x03e4c084, 0x03e74084, 0x03e88084, 0x03e9c084, + 0x03eb0084, 0x03ee4084, 0x04098084, 0x043f0081, + 0x06c18484, 0x06c48084, 0x06cec184, 0x06d00120, + 0x06d0c084, 0x074b0383, 0x074cc41f, 0x074f1783, + 0x075e0081, 0x0766d283, 0x07801d44, 0x078e8942, + 0x07931844, 0x079f0d42, 0x07a58216, 0x07a68085, + 0x07a6c0be, 0x07a80d44, 0x07aea044, 0x07c00122, + 0x07c08344, 0x07c20122, 0x07c28344, 0x07c40122, + 0x07c48244, 0x07c60122, 0x07c68244, 0x07c8113e, + 0x07d08244, 0x07d20122, 0x07d28244, 0x07d40122, + 0x07d48344, 0x07d64c3e, 0x07dc4080, 0x07dc80be, + 0x07dcc080, 0x07dd00be, 0x07dd4080, 0x07dd80be, + 0x07ddc080, 0x07de00be, 0x07de4080, 0x07de80be, + 0x07dec080, 0x07df00be, 0x07df4080, 0x07e00820, + 0x07e40820, 0x07e80820, 0x07ec05be, 0x07eec080, + 0x07ef00be, 0x07ef4097, 0x07ef8080, 0x07efc117, + 0x07f0443e, 0x07f24080, 0x07f280be, 0x07f2c080, + 0x07f303be, 0x07f4c080, 0x07f582ae, 0x07f6c080, + 0x07f7433e, 0x07f8c080, 0x07f903ae, 0x07fac080, + 0x07fb013e, 0x07fb8102, 0x07fc83be, 0x07fe4080, + 0x07fe80be, 0x07fec080, 0x07ff00be, 0x07ff4080, + 0x07ff8097, 0x0800011e, 0x08008495, 0x08044081, + 0x0805c097, 0x08090081, 0x08094097, 0x08098099, + 0x080bc081, 0x080cc085, 0x080d00b1, 0x080d8085, + 0x080dc0b1, 0x080f0197, 0x0811c197, 0x0815c0b3, + 0x0817c081, 0x081c0595, 0x081ec081, 0x081f0215, + 0x0820051f, 0x08228583, 0x08254415, 0x082a0097, + 0x08400119, 0x08408081, 0x0840c0bf, 0x08414119, + 0x0841c081, 0x084240bf, 0x0842852d, 0x08454081, + 0x08458097, 0x08464295, 0x08480097, 0x08484099, + 0x08488097, 0x08490081, 0x08498080, 0x084a0081, + 0x084a8102, 0x084b0495, 0x084d421f, 0x084e4081, + 0x084ec099, 0x084f0283, 0x08514295, 0x08540119, + 0x0854809b, 0x0854c619, 0x0857c097, 0x08580081, + 0x08584097, 0x08588099, 0x0858c097, 0x08590081, + 0x08594097, 0x08598099, 0x0859c09b, 0x085a0097, + 0x085a4081, 0x085a8097, 0x085ac099, 0x085b0295, + 0x085c4097, 0x085c8099, 0x085cc097, 0x085d0081, + 0x085d4097, 0x085d8099, 0x085dc09b, 0x085e0097, + 0x085e4081, 0x085e8097, 0x085ec099, 0x085f0215, + 0x08624099, 0x0866813e, 0x086b80be, 0x087341be, + 0x088100be, 0x088240be, 0x088300be, 0x088901be, + 0x088b0085, 0x088b40b1, 0x088bc085, 0x088c00b1, + 0x089040be, 0x089100be, 0x0891c1be, 0x089801be, + 0x089b42be, 0x089d0144, 0x089e0144, 0x08a00144, + 0x08a10144, 0x08a20144, 0x08ab023e, 0x08b80244, + 0x08ba8220, 0x08ca411e, 0x0918049f, 0x091a4523, + 0x091cc097, 0x091d04a5, 0x091f452b, 0x0921c09b, + 0x092204a1, 0x09244525, 0x0926c099, 0x09270d25, + 0x092d8d1f, 0x09340d1f, 0x093a8081, 0x0a8300b3, + 0x0a9d0099, 0x0a9d4097, 0x0a9d8099, 0x0ab700be, + 0x0b1f0115, 0x0b5bc081, 0x0ba7c081, 0x0bbcc081, + 0x0bc004ad, 0x0bc244ad, 0x0bc484ad, 0x0bc6f383, + 0x0be0852d, 0x0be31d03, 0x0bf1882d, 0x0c000081, + 0x0c0d8283, 0x0c130b84, 0x0c194284, 0x0c1c0122, + 0x0c1cc122, 0x0c1d8122, 0x0c1e4122, 0x0c1f0122, + 0x0c250084, 0x0c26c123, 0x0c278084, 0x0c27c085, + 0x0c2b0b84, 0x0c314284, 0x0c340122, 0x0c34c122, + 0x0c358122, 0x0c364122, 0x0c370122, 0x0c3d0084, + 0x0c3dc220, 0x0c3f8084, 0x0c3fc085, 0x0c4c4a2d, + 0x0c51451f, 0x0c53ca9f, 0x0c5915ad, 0x0c648703, + 0x0c800741, 0x0c838089, 0x0c83c129, 0x0c8441a9, + 0x0c850089, 0x0c854129, 0x0c85c2a9, 0x0c870089, + 0x0c87408f, 0x0c87808d, 0x0c881241, 0x0c910203, + 0x0c940099, 0x0c9444a3, 0x0c968323, 0x0c98072d, + 0x0c9b84af, 0x0c9dc2a1, 0x0c9f00b5, 0x0c9f40b3, + 0x0c9f8085, 0x0ca01883, 0x0cac4223, 0x0cad4523, + 0x0cafc097, 0x0cb004a1, 0x0cb241a5, 0x0cb30097, + 0x0cb34099, 0x0cb38097, 0x0cb3c099, 0x0cb417ad, + 0x0cbfc085, 0x0cc001b3, 0x0cc0c0b1, 0x0cc100b3, + 0x0cc14131, 0x0cc1c0b5, 0x0cc200b3, 0x0cc241b1, + 0x0cc30133, 0x0cc38131, 0x0cc40085, 0x0cc440b1, + 0x0cc48133, 0x0cc50085, 0x0cc540b5, 0x0cc580b7, + 0x0cc5c0b5, 0x0cc600b1, 0x0cc64135, 0x0cc6c0b3, + 0x0cc701b1, 0x0cc7c0b3, 0x0cc800b5, 0x0cc840b3, + 0x0cc881b1, 0x0cc9422f, 0x0cca4131, 0x0ccac0b5, + 0x0ccb00b1, 0x0ccb40b3, 0x0ccb80b5, 0x0ccbc0b1, + 0x0ccc012f, 0x0ccc80b5, 0x0cccc0b3, 0x0ccd00b5, + 0x0ccd40b1, 0x0ccd80b5, 0x0ccdc085, 0x0cce02b1, + 0x0ccf40b3, 0x0ccf80b1, 0x0ccfc085, 0x0cd001b1, + 0x0cd0c0b3, 0x0cd101b1, 0x0cd1c0b5, 0x0cd200b3, + 0x0cd24085, 0x0cd280b5, 0x0cd2c085, 0x0cd30133, + 0x0cd381b1, 0x0cd440b3, 0x0cd48085, 0x0cd4c0b1, + 0x0cd500b3, 0x0cd54085, 0x0cd580b5, 0x0cd5c0b1, + 0x0cd60521, 0x0cd88525, 0x0cdb02a5, 0x0cdc4099, + 0x0cdc8117, 0x0cdd0099, 0x0cdd4197, 0x0cde0127, + 0x0cde8285, 0x0cdfc089, 0x0ce0043f, 0x0ce20099, + 0x0ce2409b, 0x0ce283bf, 0x0ce44219, 0x0ce54205, + 0x0ce6433f, 0x0ce7c131, 0x0ce84085, 0x0ce881b1, + 0x0ce94085, 0x0ce98107, 0x0cea0089, 0x0cea4097, + 0x0cea8219, 0x0ceb809d, 0x0cebc08d, 0x0cec083f, + 0x0cf00105, 0x0cf0809b, 0x0cf0c197, 0x0cf1809b, + 0x0cf1c099, 0x0cf20517, 0x0cf48099, 0x0cf4c117, + 0x0cf54119, 0x0cf5c097, 0x0cf6009b, 0x0cf64099, + 0x0cf68217, 0x0cf78119, 0x0cf804a1, 0x0cfa4525, + 0x0cfcc525, 0x0cff4125, 0x0cffc099, 0x29a70103, + 0x29dc0081, 0x29fc8195, 0x29fe0103, 0x2ad70203, + 0x2ada4081, 0x3e401482, 0x3e4a7f82, 0x3e6a3f82, + 0x3e8aa102, 0x3e9b0110, 0x3e9c2f82, 0x3eb3c590, + 0x3ec00197, 0x3ec0c119, 0x3ec1413f, 0x3ec4c2af, + 0x3ec74184, 0x3ec804ad, 0x3eca4081, 0x3eca8304, + 0x3ecc03a0, 0x3ece02a0, 0x3ecf8084, 0x3ed00120, + 0x3ed0c120, 0x3ed184ae, 0x3ed3c085, 0x3ed4312d, + 0x3ef4cbad, 0x3efa892f, 0x3eff022d, 0x3f002f2f, + 0x3f1782a5, 0x3f18c0b1, 0x3f1907af, 0x3f1cffaf, + 0x3f3c81a5, 0x3f3d64af, 0x3f542031, 0x3f649b31, + 0x3f7c0131, 0x3f7c83b3, 0x3f7e40b1, 0x3f7e80bd, + 0x3f7ec0bb, 0x3f7f00b3, 0x3f840503, 0x3f8c01ad, + 0x3f8cc315, 0x3f8e462d, 0x3f91cc03, 0x3f97c695, + 0x3f9c01af, 0x3f9d0085, 0x3f9d852f, 0x3fa03aad, + 0x3fbd442f, 0x3fc06f1f, 0x3fd7c11f, 0x3fd85fad, + 0x3fe80081, 0x3fe84f1f, 0x3ff0831f, 0x3ff2831f, + 0x3ff4831f, 0x3ff6819f, 0x3ff80783, 0x41724092, + 0x41790092, 0x41e04d83, 0x41e70f91, 0x44268192, + 0x442ac092, 0x444b8112, 0x44d2c112, 0x44e0c192, + 0x44e38092, 0x44e44092, 0x44f14212, 0x452ec212, + 0x456e8112, 0x464e0092, 0x58484412, 0x5b5a0192, + 0x73358d1f, 0x733c051f, 0x74578392, 0x746ec312, + 0x75000d1f, 0x75068d1f, 0x750d0d1f, 0x7513839f, + 0x7515891f, 0x751a0d1f, 0x75208d1f, 0x75271015, + 0x752f439f, 0x7531459f, 0x75340d1f, 0x753a8d1f, + 0x75410395, 0x7543441f, 0x7545839f, 0x75478d1f, + 0x754e0795, 0x7552839f, 0x75548d1f, 0x755b0d1f, + 0x75618d1f, 0x75680d1f, 0x756e8d1f, 0x75750d1f, + 0x757b8d1f, 0x75820d1f, 0x75888d1f, 0x758f0d1f, + 0x75958d1f, 0x759c0d1f, 0x75a28d1f, 0x75a90103, + 0x75aa089f, 0x75ae4081, 0x75ae839f, 0x75b04081, + 0x75b08c9f, 0x75b6c081, 0x75b7032d, 0x75b8889f, + 0x75bcc081, 0x75bd039f, 0x75bec081, 0x75bf0c9f, + 0x75c54081, 0x75c5832d, 0x75c7089f, 0x75cb4081, + 0x75cb839f, 0x75cd4081, 0x75cd8c9f, 0x75d3c081, + 0x75d4032d, 0x75d5889f, 0x75d9c081, 0x75da039f, + 0x75dbc081, 0x75dc0c9f, 0x75e24081, 0x75e2832d, + 0x75e4089f, 0x75e84081, 0x75e8839f, 0x75ea4081, + 0x75ea8c9f, 0x75f0c081, 0x75f1042d, 0x75f3851f, + 0x75f6051f, 0x75f8851f, 0x75fb051f, 0x75fd851f, + 0x780c049f, 0x780e419f, 0x780f059f, 0x7811c203, + 0x7812d0ad, 0x781b0103, 0x7b80022d, 0x7b814dad, + 0x7b884203, 0x7b89c081, 0x7b8a452d, 0x7b8d0403, + 0x7b908081, 0x7b91dc03, 0x7ba0052d, 0x7ba2c8ad, + 0x7ba84483, 0x7baac8ad, 0x7c400097, 0x7c404521, + 0x7c440d25, 0x7c4a8087, 0x7c4ac115, 0x7c4b4117, + 0x7c4c0d1f, 0x7c528217, 0x7c538099, 0x7c53c097, + 0x7c5a8197, 0x7c640097, 0x7c80012f, 0x7c808081, + 0x7c841603, 0x7c9004c1, 0x7c940103, 0x7efc051f, + 0xbe0001ac, 0xbe00d110, 0xbe0947ac, 0xbe0d3910, + 0xbe29872c, 0xbe2d022c, 0xbe2e3790, 0xbe49ff90, + 0xbe69bc10, +}; + +static const uint16_t unicode_decomp_table2[709] = { + 0x0020, 0x0000, 0x0061, 0x0002, 0x0004, 0x0006, 0x03bc, 0x0008, + 0x000a, 0x000c, 0x0015, 0x0095, 0x00a5, 0x00b9, 0x00c1, 0x00c3, + 0x00c7, 0x00cb, 0x00d1, 0x00d7, 0x00dd, 0x00e0, 0x00e6, 0x00f8, + 0x0108, 0x010a, 0x0073, 0x0110, 0x0112, 0x0114, 0x0120, 0x012c, + 0x0144, 0x014d, 0x0153, 0x0162, 0x0168, 0x016a, 0x0176, 0x0192, + 0x0194, 0x01a9, 0x01bb, 0x01c7, 0x01d1, 0x01d5, 0x02b9, 0x01d7, + 0x003b, 0x01d9, 0x01db, 0x00b7, 0x01e1, 0x01fc, 0x020c, 0x0218, + 0x021d, 0x0223, 0x0227, 0x03a3, 0x0233, 0x023f, 0x0242, 0x024b, + 0x024e, 0x0251, 0x025d, 0x0260, 0x0269, 0x026c, 0x026f, 0x0275, + 0x0278, 0x0281, 0x028a, 0x029c, 0x029f, 0x02a3, 0x02af, 0x02b9, + 0x02c5, 0x02c9, 0x02cd, 0x02d1, 0x02d5, 0x02e7, 0x02ed, 0x02f1, + 0x02f5, 0x02f9, 0x02fd, 0x0305, 0x0309, 0x030d, 0x0313, 0x0317, + 0x031b, 0x0323, 0x0327, 0x032b, 0x032f, 0x0335, 0x033d, 0x0341, + 0x0349, 0x034d, 0x0351, 0x0f0b, 0x0357, 0x035b, 0x035f, 0x0363, + 0x0367, 0x036b, 0x036f, 0x0373, 0x0379, 0x037d, 0x0381, 0x0385, + 0x0389, 0x038d, 0x0391, 0x0395, 0x0399, 0x039d, 0x03a1, 0x10dc, + 0x03a5, 0x03c9, 0x03cd, 0x03d9, 0x03dd, 0x03e1, 0x03ef, 0x03f1, + 0x043d, 0x044f, 0x0499, 0x04f0, 0x0502, 0x054a, 0x0564, 0x056c, + 0x0570, 0x0573, 0x059a, 0x05fa, 0x05fe, 0x0607, 0x060b, 0x0614, + 0x0618, 0x061e, 0x0622, 0x0628, 0x068e, 0x0694, 0x0698, 0x069e, + 0x06a2, 0x06ab, 0x03ac, 0x06f3, 0x03ad, 0x06f6, 0x03ae, 0x06f9, + 0x03af, 0x06fc, 0x03cc, 0x06ff, 0x03cd, 0x0702, 0x03ce, 0x0705, + 0x0709, 0x070d, 0x0711, 0x0386, 0x0732, 0x0735, 0x03b9, 0x0737, + 0x073b, 0x0388, 0x0753, 0x0389, 0x0756, 0x0390, 0x076b, 0x038a, + 0x0777, 0x03b0, 0x0789, 0x038e, 0x0799, 0x079f, 0x07a3, 0x038c, + 0x07b8, 0x038f, 0x07bb, 0x00b4, 0x07be, 0x07c0, 0x07c2, 0x2010, + 0x07cb, 0x002e, 0x07cd, 0x07cf, 0x0020, 0x07d2, 0x07d6, 0x07db, + 0x07df, 0x07e4, 0x07ea, 0x07f0, 0x0020, 0x07f6, 0x2212, 0x0801, + 0x0805, 0x0807, 0x081d, 0x0825, 0x0827, 0x0043, 0x082d, 0x0830, + 0x0190, 0x0836, 0x0839, 0x004e, 0x0845, 0x0847, 0x084c, 0x084e, + 0x0851, 0x005a, 0x03a9, 0x005a, 0x0853, 0x0857, 0x0860, 0x0069, + 0x0862, 0x0865, 0x086f, 0x0874, 0x087a, 0x087e, 0x08a2, 0x0049, + 0x08a4, 0x08a6, 0x08a9, 0x0056, 0x08ab, 0x08ad, 0x08b0, 0x08b4, + 0x0058, 0x08b6, 0x08b8, 0x08bb, 0x08c0, 0x08c2, 0x08c5, 0x0076, + 0x08c7, 0x08c9, 0x08cc, 0x08d0, 0x0078, 0x08d2, 0x08d4, 0x08d7, + 0x08db, 0x08de, 0x08e4, 0x08e7, 0x08f0, 0x08f3, 0x08f6, 0x08f9, + 0x0902, 0x0906, 0x090b, 0x090f, 0x0914, 0x0917, 0x091a, 0x0923, + 0x092c, 0x093b, 0x093e, 0x0941, 0x0944, 0x0947, 0x094a, 0x0956, + 0x095c, 0x0960, 0x0962, 0x0964, 0x0968, 0x096a, 0x0970, 0x0978, + 0x097c, 0x0980, 0x0986, 0x0989, 0x098f, 0x0991, 0x0030, 0x0993, + 0x0999, 0x099c, 0x099e, 0x09a1, 0x09a4, 0x2d61, 0x6bcd, 0x9f9f, + 0x09a6, 0x09b1, 0x09bc, 0x09c7, 0x0a95, 0x0aa1, 0x0b15, 0x0020, + 0x0b27, 0x0b31, 0x0b8d, 0x0ba1, 0x0ba5, 0x0ba9, 0x0bad, 0x0bb1, + 0x0bb5, 0x0bb9, 0x0bbd, 0x0bc1, 0x0bc5, 0x0c21, 0x0c35, 0x0c39, + 0x0c3d, 0x0c41, 0x0c45, 0x0c49, 0x0c4d, 0x0c51, 0x0c55, 0x0c59, + 0x0c6f, 0x0c71, 0x0c73, 0x0ca0, 0x0cbc, 0x0cdc, 0x0ce4, 0x0cec, + 0x0cf4, 0x0cfc, 0x0d04, 0x0d0c, 0x0d14, 0x0d22, 0x0d2e, 0x0d7a, + 0x0d82, 0x0d85, 0x0d89, 0x0d8d, 0x0d9d, 0x0db1, 0x0db5, 0x0dbc, + 0x0dc2, 0x0dc6, 0x0e28, 0x0e2c, 0x0e30, 0x0e32, 0x0e36, 0x0e3c, + 0x0e3e, 0x0e41, 0x0e43, 0x0e46, 0x0e77, 0x0e7b, 0x0e89, 0x0e8e, + 0x0e94, 0x0e9c, 0x0ea3, 0x0ea9, 0x0eb4, 0x0ebe, 0x0ec6, 0x0eca, + 0x0ecf, 0x0ed9, 0x0edd, 0x0ee4, 0x0eec, 0x0ef3, 0x0ef8, 0x0f04, + 0x0f0a, 0x0f15, 0x0f1b, 0x0f22, 0x0f28, 0x0f33, 0x0f3d, 0x0f45, + 0x0f4c, 0x0f51, 0x0f57, 0x0f5e, 0x0f63, 0x0f69, 0x0f70, 0x0f76, + 0x0f7d, 0x0f82, 0x0f89, 0x0f8d, 0x0f9e, 0x0fa4, 0x0fa9, 0x0fad, + 0x0fb8, 0x0fbe, 0x0fc9, 0x0fd0, 0x0fd6, 0x0fda, 0x0fe1, 0x0fe5, + 0x0fef, 0x0ffa, 0x1000, 0x1004, 0x1009, 0x100f, 0x1013, 0x101a, + 0x101f, 0x1023, 0x1029, 0x102f, 0x1032, 0x1036, 0x1039, 0x103f, + 0x1045, 0x1059, 0x1061, 0x1079, 0x107c, 0x1080, 0x1095, 0x10a1, + 0x10b1, 0x10c3, 0x10cb, 0x10cf, 0x10da, 0x10de, 0x10ea, 0x10f2, + 0x10f4, 0x1100, 0x1105, 0x1111, 0x1141, 0x1149, 0x114d, 0x1153, + 0x1157, 0x115a, 0x116e, 0x1171, 0x1175, 0x117b, 0x117d, 0x1181, + 0x1184, 0x118c, 0x1192, 0x1196, 0x119c, 0x11a2, 0x11a8, 0x11ab, + 0xa76f, 0x11af, 0x11b2, 0x11b6, 0x028d, 0x11be, 0x1210, 0x130e, + 0x140c, 0x1490, 0x1495, 0x1553, 0x156c, 0x1572, 0x1578, 0x157e, + 0x158a, 0x1596, 0x002b, 0x15a1, 0x15b9, 0x15bd, 0x15c1, 0x15c5, + 0x15c9, 0x15cd, 0x15e1, 0x15e5, 0x1649, 0x1662, 0x1688, 0x168e, + 0x174c, 0x1752, 0x1757, 0x1777, 0x1877, 0x187d, 0x1911, 0x19d3, + 0x1a77, 0x1a7f, 0x1a9d, 0x1aa2, 0x1ab6, 0x1ac0, 0x1ac6, 0x1ada, + 0x1adf, 0x1ae5, 0x1af3, 0x1b23, 0x1b30, 0x1b38, 0x1b3c, 0x1b52, + 0x1bc9, 0x1bdb, 0x1bdd, 0x1bdf, 0x3164, 0x1c20, 0x1c22, 0x1c24, + 0x1c26, 0x1c28, 0x1c2a, 0x1c48, 0x1c4d, 0x1c52, 0x1c88, 0x1cce, + 0x1cdc, 0x1ce1, 0x1cea, 0x1cf3, 0x1d01, 0x1d06, 0x1d0b, 0x1d1d, + 0x1d2f, 0x1d38, 0x1d3d, 0x1d61, 0x1d6f, 0x1d71, 0x1d73, 0x1d93, + 0x1dae, 0x1db0, 0x1db2, 0x1db4, 0x1db6, 0x1db8, 0x1dba, 0x1dbc, + 0x1ddc, 0x1dde, 0x1de0, 0x1de2, 0x1de4, 0x1deb, 0x1ded, 0x1def, + 0x1df1, 0x1e00, 0x1e02, 0x1e04, 0x1e06, 0x1e08, 0x1e0a, 0x1e0c, + 0x1e0e, 0x1e10, 0x1e12, 0x1e14, 0x1e16, 0x1e18, 0x1e1a, 0x1e1c, + 0x1e20, 0x03f4, 0x1e22, 0x2207, 0x1e24, 0x2202, 0x1e26, 0x1e2e, + 0x03f4, 0x1e30, 0x2207, 0x1e32, 0x2202, 0x1e34, 0x1e3c, 0x03f4, + 0x1e3e, 0x2207, 0x1e40, 0x2202, 0x1e42, 0x1e4a, 0x03f4, 0x1e4c, + 0x2207, 0x1e4e, 0x2202, 0x1e50, 0x1e58, 0x03f4, 0x1e5a, 0x2207, + 0x1e5c, 0x2202, 0x1e5e, 0x1e68, 0x1e6a, 0x1e6c, 0x1e6e, 0x1e70, + 0x1e72, 0x1e74, 0x1e76, 0x1e78, 0x1e80, 0x1ea3, 0x1ea7, 0x1ead, + 0x1eca, 0x062d, 0x1ed2, 0x1ede, 0x062c, 0x1eee, 0x1f5e, 0x1f6a, + 0x1f7d, 0x1f8f, 0x1fa2, 0x1fa4, 0x1fa8, 0x1fae, 0x1fb4, 0x1fb6, + 0x1fba, 0x1fbc, 0x1fc4, 0x1fc7, 0x1fc9, 0x1fcf, 0x1fd1, 0x30b5, + 0x1fd7, 0x202f, 0x2045, 0x2049, 0x204b, 0x2050, 0x209d, 0x20ae, + 0x21af, 0x21bf, 0x21c5, 0x22bf, 0x23dd, +}; + +static const uint8_t unicode_decomp_data[9451] = { + 0x20, 0x88, 0x20, 0x84, 0x32, 0x33, 0x20, 0x81, + 0x20, 0xa7, 0x31, 0x6f, 0x31, 0xd0, 0x34, 0x31, + 0xd0, 0x32, 0x33, 0xd0, 0x34, 0x41, 0x80, 0x41, + 0x81, 0x41, 0x82, 0x41, 0x83, 0x41, 0x88, 0x41, + 0x8a, 0x00, 0x00, 0x43, 0xa7, 0x45, 0x80, 0x45, + 0x81, 0x45, 0x82, 0x45, 0x88, 0x49, 0x80, 0x49, + 0x81, 0x49, 0x82, 0x49, 0x88, 0x00, 0x00, 0x4e, + 0x83, 0x4f, 0x80, 0x4f, 0x81, 0x4f, 0x82, 0x4f, + 0x83, 0x4f, 0x88, 0x00, 0x00, 0x00, 0x00, 0x55, + 0x80, 0x55, 0x81, 0x55, 0x82, 0x55, 0x88, 0x59, + 0x81, 0x00, 0x00, 0x00, 0x00, 0x61, 0x80, 0x61, + 0x81, 0x61, 0x82, 0x61, 0x83, 0x61, 0x88, 0x61, + 0x8a, 0x00, 0x00, 0x63, 0xa7, 0x65, 0x80, 0x65, + 0x81, 0x65, 0x82, 0x65, 0x88, 0x69, 0x80, 0x69, + 0x81, 0x69, 0x82, 0x69, 0x88, 0x00, 0x00, 0x6e, + 0x83, 0x6f, 0x80, 0x6f, 0x81, 0x6f, 0x82, 0x6f, + 0x83, 0x6f, 0x88, 0x00, 0x00, 0x00, 0x00, 0x75, + 0x80, 0x75, 0x81, 0x75, 0x82, 0x75, 0x88, 0x79, + 0x81, 0x00, 0x00, 0x79, 0x88, 0x41, 0x84, 0x41, + 0x86, 0x41, 0xa8, 0x43, 0x81, 0x43, 0x82, 0x43, + 0x87, 0x43, 0x8c, 0x44, 0x8c, 0x45, 0x84, 0x45, + 0x86, 0x45, 0x87, 0x45, 0xa8, 0x45, 0x8c, 0x47, + 0x82, 0x47, 0x86, 0x47, 0x87, 0x47, 0xa7, 0x48, + 0x82, 0x49, 0x83, 0x49, 0x84, 0x49, 0x86, 0x49, + 0xa8, 0x49, 0x87, 0x49, 0x4a, 0x69, 0x6a, 0x4a, + 0x82, 0x4b, 0xa7, 0x4c, 0x81, 0x4c, 0xa7, 0x4c, + 0x8c, 0x4c, 0x00, 0x00, 0x6b, 0x20, 0x6b, 0x4e, + 0x81, 0x4e, 0xa7, 0x4e, 0x8c, 0xbc, 0x02, 0x6e, + 0x4f, 0x84, 0x4f, 0x86, 0x4f, 0x8b, 0x52, 0x81, + 0x52, 0xa7, 0x52, 0x8c, 0x53, 0x81, 0x53, 0x82, + 0x53, 0xa7, 0x53, 0x8c, 0x54, 0xa7, 0x54, 0x8c, + 0x55, 0x83, 0x55, 0x84, 0x55, 0x86, 0x55, 0x8a, + 0x55, 0x8b, 0x55, 0xa8, 0x57, 0x82, 0x59, 0x82, + 0x59, 0x88, 0x5a, 0x81, 0x5a, 0x87, 0x5a, 0x8c, + 0x4f, 0x9b, 0x55, 0x9b, 0x44, 0x00, 0x7d, 0x01, + 0x44, 0x00, 0x7e, 0x01, 0x64, 0x00, 0x7e, 0x01, + 0x4c, 0x4a, 0x4c, 0x6a, 0x6c, 0x6a, 0x4e, 0x4a, + 0x4e, 0x6a, 0x6e, 0x6a, 0x41, 0x00, 0x8c, 0x49, + 0x00, 0x8c, 0x4f, 0x00, 0x8c, 0x55, 0x00, 0x8c, + 0xdc, 0x00, 0x84, 0xdc, 0x00, 0x81, 0xdc, 0x00, + 0x8c, 0xdc, 0x00, 0x80, 0xc4, 0x00, 0x84, 0x26, + 0x02, 0x84, 0xc6, 0x00, 0x84, 0x47, 0x8c, 0x4b, + 0x8c, 0x4f, 0xa8, 0xea, 0x01, 0x84, 0xeb, 0x01, + 0x84, 0xb7, 0x01, 0x8c, 0x92, 0x02, 0x8c, 0x6a, + 0x00, 0x8c, 0x44, 0x5a, 0x44, 0x7a, 0x64, 0x7a, + 0x47, 0x81, 0x4e, 0x00, 0x80, 0xc5, 0x00, 0x81, + 0xc6, 0x00, 0x81, 0xd8, 0x00, 0x81, 0x41, 0x8f, + 0x41, 0x91, 0x45, 0x8f, 0x45, 0x91, 0x49, 0x8f, + 0x49, 0x91, 0x4f, 0x8f, 0x4f, 0x91, 0x52, 0x8f, + 0x52, 0x91, 0x55, 0x8f, 0x55, 0x91, 0x53, 0xa6, + 0x54, 0xa6, 0x48, 0x8c, 0x41, 0x00, 0x87, 0x45, + 0x00, 0xa7, 0xd6, 0x00, 0x84, 0xd5, 0x00, 0x84, + 0x4f, 0x00, 0x87, 0x2e, 0x02, 0x84, 0x59, 0x00, + 0x84, 0x68, 0x00, 0x66, 0x02, 0x6a, 0x00, 0x72, + 0x00, 0x79, 0x02, 0x7b, 0x02, 0x81, 0x02, 0x77, + 0x00, 0x79, 0x00, 0x20, 0x86, 0x20, 0x87, 0x20, + 0x8a, 0x20, 0xa8, 0x20, 0x83, 0x20, 0x8b, 0x63, + 0x02, 0x6c, 0x00, 0x73, 0x00, 0x78, 0x00, 0x95, + 0x02, 0x80, 0x81, 0x00, 0x93, 0x88, 0x81, 0x20, + 0xc5, 0x20, 0x81, 0xa8, 0x00, 0x81, 0x91, 0x03, + 0x81, 0x95, 0x03, 0x81, 0x97, 0x03, 0x81, 0x99, + 0x03, 0x81, 0x00, 0x00, 0x00, 0x9f, 0x03, 0x81, + 0x00, 0x00, 0x00, 0xa5, 0x03, 0x81, 0xa9, 0x03, + 0x81, 0xca, 0x03, 0x81, 0x01, 0x03, 0x98, 0x07, + 0xa4, 0x07, 0xb0, 0x00, 0xb4, 0x00, 0xb6, 0x00, + 0xb8, 0x00, 0xca, 0x00, 0x01, 0x03, 0xb8, 0x07, + 0xc4, 0x07, 0xbe, 0x00, 0xc4, 0x00, 0xc8, 0x00, + 0xa5, 0x03, 0x0d, 0x13, 0x00, 0x01, 0x03, 0xd1, + 0x00, 0xd1, 0x07, 0xc6, 0x03, 0xc0, 0x03, 0xba, + 0x03, 0xc1, 0x03, 0xc2, 0x03, 0x00, 0x00, 0x98, + 0x03, 0xb5, 0x03, 0x15, 0x04, 0x80, 0x15, 0x04, + 0x88, 0x00, 0x00, 0x00, 0x13, 0x04, 0x81, 0x06, + 0x04, 0x88, 0x1a, 0x04, 0x81, 0x18, 0x04, 0x80, + 0x23, 0x04, 0x86, 0x18, 0x04, 0x86, 0x38, 0x04, + 0x86, 0x35, 0x04, 0x80, 0x35, 0x04, 0x88, 0x00, + 0x00, 0x00, 0x33, 0x04, 0x81, 0x56, 0x04, 0x88, + 0x3a, 0x04, 0x81, 0x38, 0x04, 0x80, 0x43, 0x04, + 0x86, 0x74, 0x04, 0x8f, 0x16, 0x04, 0x86, 0x10, + 0x04, 0x86, 0x10, 0x04, 0x88, 0x15, 0x04, 0x86, + 0xd8, 0x04, 0x88, 0x16, 0x04, 0x88, 0x17, 0x04, + 0x88, 0x18, 0x04, 0x84, 0x18, 0x04, 0x88, 0x1e, + 0x04, 0x88, 0xe8, 0x04, 0x88, 0x2d, 0x04, 0x88, + 0x23, 0x04, 0x84, 0x23, 0x04, 0x88, 0x23, 0x04, + 0x8b, 0x27, 0x04, 0x88, 0x2b, 0x04, 0x88, 0x65, + 0x05, 0x82, 0x05, 0x27, 0x06, 0x00, 0x2c, 0x00, + 0x2d, 0x21, 0x2d, 0x00, 0x2e, 0x23, 0x2d, 0x27, + 0x06, 0x00, 0x4d, 0x21, 0x4d, 0xa0, 0x4d, 0x23, + 0x4d, 0xd5, 0x06, 0x54, 0x06, 0x00, 0x00, 0x00, + 0x00, 0xc1, 0x06, 0x54, 0x06, 0xd2, 0x06, 0x54, + 0x06, 0x28, 0x09, 0x3c, 0x09, 0x30, 0x09, 0x3c, + 0x09, 0x33, 0x09, 0x3c, 0x09, 0x15, 0x09, 0x00, + 0x27, 0x01, 0x27, 0x02, 0x27, 0x07, 0x27, 0x0c, + 0x27, 0x0d, 0x27, 0x16, 0x27, 0x1a, 0x27, 0xbe, + 0x09, 0x09, 0x00, 0x09, 0x19, 0xa1, 0x09, 0xbc, + 0x09, 0xaf, 0x09, 0xbc, 0x09, 0x32, 0x0a, 0x3c, + 0x0a, 0x38, 0x0a, 0x3c, 0x0a, 0x16, 0x0a, 0x00, + 0x26, 0x01, 0x26, 0x06, 0x26, 0x2b, 0x0a, 0x3c, + 0x0a, 0x47, 0x0b, 0x56, 0x0b, 0x3e, 0x0b, 0x09, + 0x00, 0x09, 0x19, 0x21, 0x0b, 0x3c, 0x0b, 0x92, + 0x0b, 0xd7, 0x0b, 0xbe, 0x0b, 0x08, 0x00, 0x09, + 0x00, 0x08, 0x19, 0x46, 0x0c, 0x56, 0x0c, 0xbf, + 0x0c, 0xd5, 0x0c, 0xc6, 0x0c, 0xd5, 0x0c, 0xc2, + 0x0c, 0x04, 0x00, 0x08, 0x13, 0x3e, 0x0d, 0x08, + 0x00, 0x09, 0x00, 0x08, 0x19, 0xd9, 0x0d, 0xca, + 0x0d, 0xca, 0x0d, 0x0f, 0x05, 0x12, 0x00, 0x0f, + 0x15, 0x4d, 0x0e, 0x32, 0x0e, 0xcd, 0x0e, 0xb2, + 0x0e, 0x99, 0x0e, 0x12, 0x00, 0x12, 0x08, 0x42, + 0x0f, 0xb7, 0x0f, 0x4c, 0x0f, 0xb7, 0x0f, 0x51, + 0x0f, 0xb7, 0x0f, 0x56, 0x0f, 0xb7, 0x0f, 0x5b, + 0x0f, 0xb7, 0x0f, 0x40, 0x0f, 0xb5, 0x0f, 0x71, + 0x0f, 0x72, 0x0f, 0x71, 0x0f, 0x00, 0x03, 0x41, + 0x0f, 0xb2, 0x0f, 0x81, 0x0f, 0xb3, 0x0f, 0x80, + 0x0f, 0xb3, 0x0f, 0x81, 0x0f, 0x71, 0x0f, 0x80, + 0x0f, 0x92, 0x0f, 0xb7, 0x0f, 0x9c, 0x0f, 0xb7, + 0x0f, 0xa1, 0x0f, 0xb7, 0x0f, 0xa6, 0x0f, 0xb7, + 0x0f, 0xab, 0x0f, 0xb7, 0x0f, 0x90, 0x0f, 0xb5, + 0x0f, 0x25, 0x10, 0x2e, 0x10, 0x05, 0x1b, 0x35, + 0x1b, 0x00, 0x00, 0x00, 0x00, 0x07, 0x1b, 0x35, + 0x1b, 0x00, 0x00, 0x00, 0x00, 0x09, 0x1b, 0x35, + 0x1b, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x1b, 0x35, + 0x1b, 0x00, 0x00, 0x00, 0x00, 0x0d, 0x1b, 0x35, + 0x1b, 0x11, 0x1b, 0x35, 0x1b, 0x3a, 0x1b, 0x35, + 0x1b, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x1b, 0x35, + 0x1b, 0x3e, 0x1b, 0x35, 0x1b, 0x42, 0x1b, 0x35, + 0x1b, 0x41, 0x00, 0xc6, 0x00, 0x42, 0x00, 0x00, + 0x00, 0x44, 0x00, 0x45, 0x00, 0x8e, 0x01, 0x47, + 0x00, 0x4f, 0x00, 0x22, 0x02, 0x50, 0x00, 0x52, + 0x00, 0x54, 0x00, 0x55, 0x00, 0x57, 0x00, 0x61, + 0x00, 0x50, 0x02, 0x51, 0x02, 0x02, 0x1d, 0x62, + 0x00, 0x64, 0x00, 0x65, 0x00, 0x59, 0x02, 0x5b, + 0x02, 0x5c, 0x02, 0x67, 0x00, 0x00, 0x00, 0x6b, + 0x00, 0x6d, 0x00, 0x4b, 0x01, 0x6f, 0x00, 0x54, + 0x02, 0x16, 0x1d, 0x17, 0x1d, 0x70, 0x00, 0x74, + 0x00, 0x75, 0x00, 0x1d, 0x1d, 0x6f, 0x02, 0x76, + 0x00, 0x25, 0x1d, 0xb2, 0x03, 0xb3, 0x03, 0xb4, + 0x03, 0xc6, 0x03, 0xc7, 0x03, 0x69, 0x00, 0x72, + 0x00, 0x75, 0x00, 0x76, 0x00, 0xb2, 0x03, 0xb3, + 0x03, 0xc1, 0x03, 0xc6, 0x03, 0xc7, 0x03, 0x52, + 0x02, 0x63, 0x00, 0x55, 0x02, 0xf0, 0x00, 0x5c, + 0x02, 0x66, 0x00, 0x5f, 0x02, 0x61, 0x02, 0x65, + 0x02, 0x68, 0x02, 0x69, 0x02, 0x6a, 0x02, 0x7b, + 0x1d, 0x9d, 0x02, 0x6d, 0x02, 0x85, 0x1d, 0x9f, + 0x02, 0x71, 0x02, 0x70, 0x02, 0x72, 0x02, 0x73, + 0x02, 0x74, 0x02, 0x75, 0x02, 0x78, 0x02, 0x82, + 0x02, 0x83, 0x02, 0xab, 0x01, 0x89, 0x02, 0x8a, + 0x02, 0x1c, 0x1d, 0x8b, 0x02, 0x8c, 0x02, 0x7a, + 0x00, 0x90, 0x02, 0x91, 0x02, 0x92, 0x02, 0xb8, + 0x03, 0x41, 0x00, 0xa5, 0x42, 0x00, 0x87, 0x42, + 0x00, 0xa3, 0x42, 0x00, 0xb1, 0xc7, 0x00, 0x81, + 0x44, 0x00, 0x87, 0x44, 0x00, 0xa3, 0x44, 0x00, + 0xb1, 0x44, 0x00, 0xa7, 0x44, 0x00, 0xad, 0x12, + 0x01, 0x80, 0x12, 0x01, 0x81, 0x45, 0x00, 0xad, + 0x45, 0x00, 0xb0, 0x28, 0x02, 0x86, 0x46, 0x00, + 0x87, 0x47, 0x00, 0x84, 0x48, 0x00, 0x87, 0x48, + 0x00, 0xa3, 0x48, 0x00, 0x88, 0x48, 0x00, 0xa7, + 0x48, 0x00, 0xae, 0x49, 0x00, 0xb0, 0xcf, 0x00, + 0x81, 0x4b, 0x00, 0x81, 0x4b, 0x00, 0xa3, 0x4b, + 0x00, 0xb1, 0x4c, 0x00, 0xa3, 0x36, 0x1e, 0x84, + 0x4c, 0xb1, 0x4c, 0xad, 0x4d, 0x81, 0x4d, 0x87, + 0x4d, 0xa3, 0x4e, 0x87, 0x4e, 0xa3, 0x4e, 0xb1, + 0x4e, 0xad, 0xd5, 0x00, 0x81, 0xd5, 0x00, 0x88, + 0x4c, 0x01, 0x80, 0x4c, 0x01, 0x81, 0x50, 0x00, + 0x81, 0x50, 0x00, 0x87, 0x52, 0x00, 0x87, 0x52, + 0x00, 0xa3, 0x5a, 0x1e, 0x84, 0x52, 0x00, 0xb1, + 0x53, 0x00, 0x87, 0x53, 0x00, 0xa3, 0x5a, 0x01, + 0x87, 0x60, 0x01, 0x87, 0x62, 0x1e, 0x87, 0x54, + 0x00, 0x87, 0x54, 0x00, 0xa3, 0x54, 0x00, 0xb1, + 0x54, 0x00, 0xad, 0x55, 0x00, 0xa4, 0x55, 0x00, + 0xb0, 0x55, 0x00, 0xad, 0x68, 0x01, 0x81, 0x6a, + 0x01, 0x88, 0x56, 0x83, 0x56, 0xa3, 0x57, 0x80, + 0x57, 0x81, 0x57, 0x88, 0x57, 0x87, 0x57, 0xa3, + 0x58, 0x87, 0x58, 0x88, 0x59, 0x87, 0x5a, 0x82, + 0x5a, 0xa3, 0x5a, 0xb1, 0x68, 0xb1, 0x74, 0x88, + 0x77, 0x8a, 0x79, 0x8a, 0x61, 0x00, 0xbe, 0x02, + 0x7f, 0x01, 0x87, 0x41, 0x00, 0xa3, 0x41, 0x00, + 0x89, 0xc2, 0x00, 0x81, 0xc2, 0x00, 0x80, 0xc2, + 0x00, 0x89, 0xc2, 0x00, 0x83, 0xa0, 0x1e, 0x82, + 0x02, 0x01, 0x81, 0x02, 0x01, 0x80, 0x02, 0x01, + 0x89, 0x02, 0x01, 0x83, 0xa0, 0x1e, 0x86, 0x45, + 0x00, 0xa3, 0x45, 0x00, 0x89, 0x45, 0x00, 0x83, + 0xca, 0x00, 0x81, 0xca, 0x00, 0x80, 0xca, 0x00, + 0x89, 0xca, 0x00, 0x83, 0xb8, 0x1e, 0x82, 0x49, + 0x00, 0x89, 0x49, 0x00, 0xa3, 0x4f, 0x00, 0xa3, + 0x4f, 0x00, 0x89, 0xd4, 0x00, 0x81, 0xd4, 0x00, + 0x80, 0xd4, 0x00, 0x89, 0xd4, 0x00, 0x83, 0xcc, + 0x1e, 0x82, 0xa0, 0x01, 0x81, 0xa0, 0x01, 0x80, + 0xa0, 0x01, 0x89, 0xa0, 0x01, 0x83, 0xa0, 0x01, + 0xa3, 0x55, 0x00, 0xa3, 0x55, 0x00, 0x89, 0xaf, + 0x01, 0x81, 0xaf, 0x01, 0x80, 0xaf, 0x01, 0x89, + 0xaf, 0x01, 0x83, 0xaf, 0x01, 0xa3, 0x59, 0x00, + 0x80, 0x59, 0x00, 0xa3, 0x59, 0x00, 0x89, 0x59, + 0x00, 0x83, 0xb1, 0x03, 0x13, 0x03, 0x00, 0x1f, + 0x80, 0x00, 0x1f, 0x81, 0x00, 0x1f, 0xc2, 0x91, + 0x03, 0x13, 0x03, 0x08, 0x1f, 0x80, 0x08, 0x1f, + 0x81, 0x08, 0x1f, 0xc2, 0xb5, 0x03, 0x13, 0x03, + 0x10, 0x1f, 0x80, 0x10, 0x1f, 0x81, 0x95, 0x03, + 0x13, 0x03, 0x18, 0x1f, 0x80, 0x18, 0x1f, 0x81, + 0xb7, 0x03, 0x93, 0xb7, 0x03, 0x94, 0x20, 0x1f, + 0x80, 0x21, 0x1f, 0x80, 0x20, 0x1f, 0x81, 0x21, + 0x1f, 0x81, 0x20, 0x1f, 0xc2, 0x21, 0x1f, 0xc2, + 0x97, 0x03, 0x93, 0x97, 0x03, 0x94, 0x28, 0x1f, + 0x80, 0x29, 0x1f, 0x80, 0x28, 0x1f, 0x81, 0x29, + 0x1f, 0x81, 0x28, 0x1f, 0xc2, 0x29, 0x1f, 0xc2, + 0xb9, 0x03, 0x93, 0xb9, 0x03, 0x94, 0x30, 0x1f, + 0x80, 0x31, 0x1f, 0x80, 0x30, 0x1f, 0x81, 0x31, + 0x1f, 0x81, 0x30, 0x1f, 0xc2, 0x31, 0x1f, 0xc2, + 0x99, 0x03, 0x93, 0x99, 0x03, 0x94, 0x38, 0x1f, + 0x80, 0x39, 0x1f, 0x80, 0x38, 0x1f, 0x81, 0x39, + 0x1f, 0x81, 0x38, 0x1f, 0xc2, 0x39, 0x1f, 0xc2, + 0xbf, 0x03, 0x93, 0xbf, 0x03, 0x94, 0x40, 0x1f, + 0x80, 0x40, 0x1f, 0x81, 0x9f, 0x03, 0x13, 0x03, + 0x48, 0x1f, 0x80, 0x48, 0x1f, 0x81, 0xc5, 0x03, + 0x13, 0x03, 0x50, 0x1f, 0x80, 0x50, 0x1f, 0x81, + 0x50, 0x1f, 0xc2, 0xa5, 0x03, 0x94, 0x00, 0x00, + 0x00, 0x59, 0x1f, 0x80, 0x00, 0x00, 0x00, 0x59, + 0x1f, 0x81, 0x00, 0x00, 0x00, 0x59, 0x1f, 0xc2, + 0xc9, 0x03, 0x93, 0xc9, 0x03, 0x94, 0x60, 0x1f, + 0x80, 0x61, 0x1f, 0x80, 0x60, 0x1f, 0x81, 0x61, + 0x1f, 0x81, 0x60, 0x1f, 0xc2, 0x61, 0x1f, 0xc2, + 0xa9, 0x03, 0x93, 0xa9, 0x03, 0x94, 0x68, 0x1f, + 0x80, 0x69, 0x1f, 0x80, 0x68, 0x1f, 0x81, 0x69, + 0x1f, 0x81, 0x68, 0x1f, 0xc2, 0x69, 0x1f, 0xc2, + 0xb1, 0x03, 0x80, 0xb5, 0x03, 0x80, 0xb7, 0x03, + 0x80, 0xb9, 0x03, 0x80, 0xbf, 0x03, 0x80, 0xc5, + 0x03, 0x80, 0xc9, 0x03, 0x80, 0x00, 0x1f, 0x45, + 0x03, 0x20, 0x1f, 0x45, 0x03, 0x60, 0x1f, 0x45, + 0x03, 0xb1, 0x03, 0x86, 0xb1, 0x03, 0x84, 0x70, + 0x1f, 0xc5, 0xb1, 0x03, 0xc5, 0xac, 0x03, 0xc5, + 0x00, 0x00, 0x00, 0xb1, 0x03, 0xc2, 0xb6, 0x1f, + 0xc5, 0x91, 0x03, 0x86, 0x91, 0x03, 0x84, 0x91, + 0x03, 0x80, 0x91, 0x03, 0xc5, 0x20, 0x93, 0x20, + 0x93, 0x20, 0xc2, 0xa8, 0x00, 0xc2, 0x74, 0x1f, + 0xc5, 0xb7, 0x03, 0xc5, 0xae, 0x03, 0xc5, 0x00, + 0x00, 0x00, 0xb7, 0x03, 0xc2, 0xc6, 0x1f, 0xc5, + 0x95, 0x03, 0x80, 0x97, 0x03, 0x80, 0x97, 0x03, + 0xc5, 0xbf, 0x1f, 0x80, 0xbf, 0x1f, 0x81, 0xbf, + 0x1f, 0xc2, 0xb9, 0x03, 0x86, 0xb9, 0x03, 0x84, + 0xca, 0x03, 0x80, 0x00, 0x03, 0xb9, 0x42, 0xca, + 0x42, 0x99, 0x06, 0x99, 0x04, 0x99, 0x00, 0xfe, + 0x1f, 0x80, 0xfe, 0x1f, 0x81, 0xfe, 0x1f, 0xc2, + 0xc5, 0x03, 0x86, 0xc5, 0x03, 0x84, 0xcb, 0x03, + 0x80, 0x00, 0x03, 0xc1, 0x13, 0xc1, 0x14, 0xc5, + 0x42, 0xcb, 0x42, 0xa5, 0x06, 0xa5, 0x04, 0xa5, + 0x00, 0xa1, 0x03, 0x94, 0xa8, 0x00, 0x80, 0x85, + 0x03, 0x60, 0x00, 0x7c, 0x1f, 0xc5, 0xc9, 0x03, + 0xc5, 0xce, 0x03, 0xc5, 0x00, 0x00, 0x00, 0xc9, + 0x03, 0xc2, 0xf6, 0x1f, 0xc5, 0x9f, 0x03, 0x80, + 0xa9, 0x03, 0x80, 0xa9, 0x03, 0xc5, 0x20, 0x94, + 0x02, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0xb3, 0x2e, 0x2e, 0x2e, + 0x2e, 0x2e, 0x32, 0x20, 0x32, 0x20, 0x32, 0x20, + 0x00, 0x00, 0x00, 0x35, 0x20, 0x35, 0x20, 0x35, + 0x20, 0x00, 0x00, 0x00, 0x21, 0x21, 0x00, 0x00, + 0x20, 0x85, 0x3f, 0x3f, 0x3f, 0x21, 0x21, 0x3f, + 0x32, 0x20, 0x00, 0x00, 0x00, 0x00, 0x30, 0x69, + 0x00, 0x00, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, + 0x2b, 0x3d, 0x28, 0x29, 0x6e, 0x30, 0x00, 0x2b, + 0x00, 0x12, 0x22, 0x3d, 0x00, 0x28, 0x00, 0x29, + 0x00, 0x00, 0x00, 0x61, 0x00, 0x65, 0x00, 0x6f, + 0x00, 0x78, 0x00, 0x59, 0x02, 0x68, 0x6b, 0x6c, + 0x6d, 0x6e, 0x70, 0x73, 0x74, 0x52, 0x73, 0x61, + 0x2f, 0x63, 0x61, 0x2f, 0x73, 0xb0, 0x00, 0x43, + 0x63, 0x2f, 0x6f, 0x63, 0x2f, 0x75, 0xb0, 0x00, + 0x46, 0x48, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x20, + 0xdf, 0x01, 0x01, 0x04, 0x24, 0x4e, 0x6f, 0x50, + 0x51, 0x52, 0x52, 0x52, 0x53, 0x4d, 0x54, 0x45, + 0x4c, 0x54, 0x4d, 0x4b, 0x00, 0xc5, 0x00, 0x42, + 0x43, 0x00, 0x65, 0x45, 0x46, 0x00, 0x4d, 0x6f, + 0xd0, 0x05, 0x46, 0x41, 0x58, 0xc0, 0x03, 0xb3, + 0x03, 0x93, 0x03, 0xa0, 0x03, 0x11, 0x22, 0x44, + 0x64, 0x65, 0x69, 0x6a, 0x31, 0xd0, 0x37, 0x31, + 0xd0, 0x39, 0x31, 0xd0, 0x31, 0x30, 0x31, 0xd0, + 0x33, 0x32, 0xd0, 0x33, 0x31, 0xd0, 0x35, 0x32, + 0xd0, 0x35, 0x33, 0xd0, 0x35, 0x34, 0xd0, 0x35, + 0x31, 0xd0, 0x36, 0x35, 0xd0, 0x36, 0x31, 0xd0, + 0x38, 0x33, 0xd0, 0x38, 0x35, 0xd0, 0x38, 0x37, + 0xd0, 0x38, 0x31, 0xd0, 0x49, 0x49, 0x49, 0x49, + 0x49, 0x49, 0x56, 0x56, 0x49, 0x56, 0x49, 0x49, + 0x56, 0x49, 0x49, 0x49, 0x49, 0x58, 0x58, 0x49, + 0x58, 0x49, 0x49, 0x4c, 0x43, 0x44, 0x4d, 0x69, + 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x76, 0x76, + 0x69, 0x76, 0x69, 0x69, 0x76, 0x69, 0x69, 0x69, + 0x69, 0x78, 0x78, 0x69, 0x78, 0x69, 0x69, 0x6c, + 0x63, 0x64, 0x6d, 0x30, 0xd0, 0x33, 0x90, 0x21, + 0xb8, 0x92, 0x21, 0xb8, 0x94, 0x21, 0xb8, 0xd0, + 0x21, 0xb8, 0xd4, 0x21, 0xb8, 0xd2, 0x21, 0xb8, + 0x03, 0x22, 0xb8, 0x08, 0x22, 0xb8, 0x0b, 0x22, + 0xb8, 0x23, 0x22, 0xb8, 0x00, 0x00, 0x00, 0x25, + 0x22, 0xb8, 0x2b, 0x22, 0x2b, 0x22, 0x2b, 0x22, + 0x00, 0x00, 0x00, 0x2e, 0x22, 0x2e, 0x22, 0x2e, + 0x22, 0x00, 0x00, 0x00, 0x3c, 0x22, 0xb8, 0x43, + 0x22, 0xb8, 0x45, 0x22, 0xb8, 0x00, 0x00, 0x00, + 0x48, 0x22, 0xb8, 0x3d, 0x00, 0xb8, 0x00, 0x00, + 0x00, 0x61, 0x22, 0xb8, 0x4d, 0x22, 0xb8, 0x3c, + 0x00, 0xb8, 0x3e, 0x00, 0xb8, 0x64, 0x22, 0xb8, + 0x65, 0x22, 0xb8, 0x72, 0x22, 0xb8, 0x76, 0x22, + 0xb8, 0x7a, 0x22, 0xb8, 0x82, 0x22, 0xb8, 0x86, + 0x22, 0xb8, 0xa2, 0x22, 0xb8, 0xa8, 0x22, 0xb8, + 0xa9, 0x22, 0xb8, 0xab, 0x22, 0xb8, 0x7c, 0x22, + 0xb8, 0x91, 0x22, 0xb8, 0xb2, 0x22, 0x38, 0x03, + 0x08, 0x30, 0x31, 0x00, 0x31, 0x00, 0x30, 0x00, + 0x32, 0x30, 0x28, 0x00, 0x31, 0x00, 0x29, 0x00, + 0x28, 0x00, 0x31, 0x00, 0x30, 0x00, 0x29, 0x00, + 0x28, 0x32, 0x30, 0x29, 0x31, 0x00, 0x2e, 0x00, + 0x31, 0x00, 0x30, 0x00, 0x2e, 0x00, 0x32, 0x30, + 0x2e, 0x28, 0x00, 0x61, 0x00, 0x29, 0x00, 0x41, + 0x00, 0x61, 0x00, 0x2b, 0x22, 0x00, 0x00, 0x00, + 0x00, 0x3a, 0x3a, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, + 0x3d, 0xdd, 0x2a, 0xb8, 0x6a, 0x56, 0x00, 0x4e, + 0x00, 0x28, 0x36, 0x3f, 0x59, 0x85, 0x8c, 0xa0, + 0xba, 0x3f, 0x51, 0x00, 0x26, 0x2c, 0x43, 0x57, + 0x6c, 0xa1, 0xb6, 0xc1, 0x9b, 0x52, 0x00, 0x5e, + 0x7a, 0x7f, 0x9d, 0xa6, 0xc1, 0xce, 0xe7, 0xb6, + 0x53, 0xc8, 0x53, 0xe3, 0x53, 0xd7, 0x56, 0x1f, + 0x57, 0xeb, 0x58, 0x02, 0x59, 0x0a, 0x59, 0x15, + 0x59, 0x27, 0x59, 0x73, 0x59, 0x50, 0x5b, 0x80, + 0x5b, 0xf8, 0x5b, 0x0f, 0x5c, 0x22, 0x5c, 0x38, + 0x5c, 0x6e, 0x5c, 0x71, 0x5c, 0xdb, 0x5d, 0xe5, + 0x5d, 0xf1, 0x5d, 0xfe, 0x5d, 0x72, 0x5e, 0x7a, + 0x5e, 0x7f, 0x5e, 0xf4, 0x5e, 0xfe, 0x5e, 0x0b, + 0x5f, 0x13, 0x5f, 0x50, 0x5f, 0x61, 0x5f, 0x73, + 0x5f, 0xc3, 0x5f, 0x08, 0x62, 0x36, 0x62, 0x4b, + 0x62, 0x2f, 0x65, 0x34, 0x65, 0x87, 0x65, 0x97, + 0x65, 0xa4, 0x65, 0xb9, 0x65, 0xe0, 0x65, 0xe5, + 0x65, 0xf0, 0x66, 0x08, 0x67, 0x28, 0x67, 0x20, + 0x6b, 0x62, 0x6b, 0x79, 0x6b, 0xb3, 0x6b, 0xcb, + 0x6b, 0xd4, 0x6b, 0xdb, 0x6b, 0x0f, 0x6c, 0x14, + 0x6c, 0x34, 0x6c, 0x6b, 0x70, 0x2a, 0x72, 0x36, + 0x72, 0x3b, 0x72, 0x3f, 0x72, 0x47, 0x72, 0x59, + 0x72, 0x5b, 0x72, 0xac, 0x72, 0x84, 0x73, 0x89, + 0x73, 0xdc, 0x74, 0xe6, 0x74, 0x18, 0x75, 0x1f, + 0x75, 0x28, 0x75, 0x30, 0x75, 0x8b, 0x75, 0x92, + 0x75, 0x76, 0x76, 0x7d, 0x76, 0xae, 0x76, 0xbf, + 0x76, 0xee, 0x76, 0xdb, 0x77, 0xe2, 0x77, 0xf3, + 0x77, 0x3a, 0x79, 0xb8, 0x79, 0xbe, 0x79, 0x74, + 0x7a, 0xcb, 0x7a, 0xf9, 0x7a, 0x73, 0x7c, 0xf8, + 0x7c, 0x36, 0x7f, 0x51, 0x7f, 0x8a, 0x7f, 0xbd, + 0x7f, 0x01, 0x80, 0x0c, 0x80, 0x12, 0x80, 0x33, + 0x80, 0x7f, 0x80, 0x89, 0x80, 0xe3, 0x81, 0x00, + 0x07, 0x10, 0x19, 0x29, 0x38, 0x3c, 0x8b, 0x8f, + 0x95, 0x4d, 0x86, 0x6b, 0x86, 0x40, 0x88, 0x4c, + 0x88, 0x63, 0x88, 0x7e, 0x89, 0x8b, 0x89, 0xd2, + 0x89, 0x00, 0x8a, 0x37, 0x8c, 0x46, 0x8c, 0x55, + 0x8c, 0x78, 0x8c, 0x9d, 0x8c, 0x64, 0x8d, 0x70, + 0x8d, 0xb3, 0x8d, 0xab, 0x8e, 0xca, 0x8e, 0x9b, + 0x8f, 0xb0, 0x8f, 0xb5, 0x8f, 0x91, 0x90, 0x49, + 0x91, 0xc6, 0x91, 0xcc, 0x91, 0xd1, 0x91, 0x77, + 0x95, 0x80, 0x95, 0x1c, 0x96, 0xb6, 0x96, 0xb9, + 0x96, 0xe8, 0x96, 0x51, 0x97, 0x5e, 0x97, 0x62, + 0x97, 0x69, 0x97, 0xcb, 0x97, 0xed, 0x97, 0xf3, + 0x97, 0x01, 0x98, 0xa8, 0x98, 0xdb, 0x98, 0xdf, + 0x98, 0x96, 0x99, 0x99, 0x99, 0xac, 0x99, 0xa8, + 0x9a, 0xd8, 0x9a, 0xdf, 0x9a, 0x25, 0x9b, 0x2f, + 0x9b, 0x32, 0x9b, 0x3c, 0x9b, 0x5a, 0x9b, 0xe5, + 0x9c, 0x75, 0x9e, 0x7f, 0x9e, 0xa5, 0x9e, 0x00, + 0x16, 0x1e, 0x28, 0x2c, 0x54, 0x58, 0x69, 0x6e, + 0x7b, 0x96, 0xa5, 0xad, 0xe8, 0xf7, 0xfb, 0x12, + 0x30, 0x00, 0x00, 0x41, 0x53, 0x44, 0x53, 0x45, + 0x53, 0x4b, 0x30, 0x99, 0x30, 0x00, 0x00, 0x00, + 0x00, 0x4d, 0x30, 0x99, 0x30, 0x00, 0x00, 0x00, + 0x00, 0x4f, 0x30, 0x99, 0x30, 0x00, 0x00, 0x00, + 0x00, 0x51, 0x30, 0x99, 0x30, 0x00, 0x00, 0x00, + 0x00, 0x53, 0x30, 0x99, 0x30, 0x00, 0x00, 0x00, + 0x00, 0x55, 0x30, 0x99, 0x30, 0x00, 0x00, 0x00, + 0x00, 0x57, 0x30, 0x99, 0x30, 0x00, 0x00, 0x00, + 0x00, 0x59, 0x30, 0x99, 0x30, 0x00, 0x00, 0x00, + 0x00, 0x5b, 0x30, 0x99, 0x30, 0x00, 0x00, 0x00, + 0x00, 0x5d, 0x30, 0x99, 0x30, 0x00, 0x00, 0x00, + 0x00, 0x5f, 0x30, 0x99, 0x30, 0x00, 0x00, 0x00, + 0x00, 0x61, 0x30, 0x99, 0x30, 0x64, 0x30, 0x99, + 0x30, 0x00, 0x00, 0x00, 0x00, 0x66, 0x30, 0x99, + 0x30, 0x00, 0x00, 0x00, 0x00, 0x68, 0x30, 0x99, + 0x30, 0x6f, 0x30, 0x99, 0x30, 0x72, 0x30, 0x99, + 0x30, 0x75, 0x30, 0x99, 0x30, 0x78, 0x30, 0x99, + 0x30, 0x7b, 0x30, 0x99, 0x30, 0x46, 0x30, 0x99, + 0x30, 0x20, 0x00, 0x99, 0x30, 0x9d, 0x30, 0x99, + 0x30, 0x88, 0x30, 0x8a, 0x30, 0xab, 0x30, 0x99, + 0x30, 0x00, 0x00, 0x00, 0x00, 0xad, 0x30, 0x99, + 0x30, 0x00, 0x00, 0x00, 0x00, 0xaf, 0x30, 0x99, + 0x30, 0x00, 0x00, 0x00, 0x00, 0xb1, 0x30, 0x99, + 0x30, 0x00, 0x00, 0x00, 0x00, 0xb3, 0x30, 0x99, + 0x30, 0x00, 0x00, 0x00, 0x00, 0xb5, 0x30, 0x99, + 0x30, 0x00, 0x00, 0x00, 0x00, 0xb7, 0x30, 0x99, + 0x30, 0x00, 0x00, 0x00, 0x00, 0xb9, 0x30, 0x99, + 0x30, 0x00, 0x00, 0x00, 0x00, 0xbb, 0x30, 0x99, + 0x30, 0x00, 0x00, 0x00, 0x00, 0xbd, 0x30, 0x99, + 0x30, 0x00, 0x00, 0x00, 0x00, 0xbf, 0x30, 0x99, + 0x30, 0x00, 0x00, 0x00, 0x00, 0xc1, 0x30, 0x99, + 0x30, 0xc4, 0x30, 0x99, 0x30, 0x00, 0x00, 0x00, + 0x00, 0xc6, 0x30, 0x99, 0x30, 0x00, 0x00, 0x00, + 0x00, 0xc8, 0x30, 0x99, 0x30, 0xcf, 0x30, 0x99, + 0x30, 0xd2, 0x30, 0x99, 0x30, 0xd5, 0x30, 0x99, + 0x30, 0xd8, 0x30, 0x99, 0x30, 0xdb, 0x30, 0x99, + 0x30, 0xa6, 0x30, 0x99, 0x30, 0xef, 0x30, 0x99, + 0x30, 0xfd, 0x30, 0x99, 0x30, 0xb3, 0x30, 0xc8, + 0x30, 0x00, 0x11, 0x00, 0x01, 0xaa, 0x02, 0xac, + 0xad, 0x03, 0x04, 0x05, 0xb0, 0xb1, 0xb2, 0xb3, + 0xb4, 0xb5, 0x1a, 0x06, 0x07, 0x08, 0x21, 0x09, + 0x11, 0x61, 0x11, 0x14, 0x11, 0x4c, 0x00, 0x01, + 0xb3, 0xb4, 0xb8, 0xba, 0xbf, 0xc3, 0xc5, 0x08, + 0xc9, 0xcb, 0x09, 0x0a, 0x0c, 0x0e, 0x0f, 0x13, + 0x15, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1e, 0x22, + 0x2c, 0x33, 0x38, 0xdd, 0xde, 0x43, 0x44, 0x45, + 0x70, 0x71, 0x74, 0x7d, 0x7e, 0x80, 0x8a, 0x8d, + 0x00, 0x4e, 0x8c, 0x4e, 0x09, 0x4e, 0xdb, 0x56, + 0x0a, 0x4e, 0x2d, 0x4e, 0x0b, 0x4e, 0x32, 0x75, + 0x59, 0x4e, 0x19, 0x4e, 0x01, 0x4e, 0x29, 0x59, + 0x30, 0x57, 0xba, 0x4e, 0x28, 0x00, 0x29, 0x00, + 0x00, 0x11, 0x02, 0x11, 0x03, 0x11, 0x05, 0x11, + 0x06, 0x11, 0x07, 0x11, 0x09, 0x11, 0x0b, 0x11, + 0x0c, 0x11, 0x0e, 0x11, 0x0f, 0x11, 0x10, 0x11, + 0x11, 0x11, 0x12, 0x11, 0x28, 0x00, 0x00, 0x11, + 0x61, 0x11, 0x29, 0x00, 0x28, 0x00, 0x02, 0x11, + 0x61, 0x11, 0x29, 0x00, 0x28, 0x00, 0x05, 0x11, + 0x61, 0x11, 0x29, 0x00, 0x28, 0x00, 0x09, 0x11, + 0x61, 0x11, 0x29, 0x00, 0x28, 0x00, 0x0b, 0x11, + 0x61, 0x11, 0x29, 0x00, 0x28, 0x00, 0x0e, 0x11, + 0x61, 0x11, 0x29, 0x00, 0x28, 0x00, 0x0c, 0x11, + 0x6e, 0x11, 0x29, 0x00, 0x28, 0x00, 0x0b, 0x11, + 0x69, 0x11, 0x0c, 0x11, 0x65, 0x11, 0xab, 0x11, + 0x29, 0x00, 0x28, 0x00, 0x0b, 0x11, 0x69, 0x11, + 0x12, 0x11, 0x6e, 0x11, 0x29, 0x00, 0x28, 0x00, + 0x29, 0x00, 0x00, 0x4e, 0x8c, 0x4e, 0x09, 0x4e, + 0xdb, 0x56, 0x94, 0x4e, 0x6d, 0x51, 0x03, 0x4e, + 0x6b, 0x51, 0x5d, 0x4e, 0x41, 0x53, 0x08, 0x67, + 0x6b, 0x70, 0x34, 0x6c, 0x28, 0x67, 0xd1, 0x91, + 0x1f, 0x57, 0xe5, 0x65, 0x2a, 0x68, 0x09, 0x67, + 0x3e, 0x79, 0x0d, 0x54, 0x79, 0x72, 0xa1, 0x8c, + 0x5d, 0x79, 0xb4, 0x52, 0xe3, 0x4e, 0x7c, 0x54, + 0x66, 0x5b, 0xe3, 0x76, 0x01, 0x4f, 0xc7, 0x8c, + 0x54, 0x53, 0x6d, 0x79, 0x11, 0x4f, 0xea, 0x81, + 0xf3, 0x81, 0x4f, 0x55, 0x7c, 0x5e, 0x87, 0x65, + 0x8f, 0x7b, 0x50, 0x54, 0x45, 0x32, 0x00, 0x31, + 0x00, 0x33, 0x00, 0x30, 0x00, 0x00, 0x11, 0x00, + 0x02, 0x03, 0x05, 0x06, 0x07, 0x09, 0x0b, 0x0c, + 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x00, 0x11, 0x00, + 0x61, 0x02, 0x61, 0x03, 0x61, 0x05, 0x61, 0x06, + 0x61, 0x07, 0x61, 0x09, 0x61, 0x0b, 0x61, 0x0c, + 0x61, 0x0e, 0x11, 0x61, 0x11, 0x00, 0x11, 0x0e, + 0x61, 0xb7, 0x00, 0x69, 0x0b, 0x11, 0x01, 0x63, + 0x00, 0x69, 0x0b, 0x11, 0x6e, 0x11, 0x00, 0x4e, + 0x8c, 0x4e, 0x09, 0x4e, 0xdb, 0x56, 0x94, 0x4e, + 0x6d, 0x51, 0x03, 0x4e, 0x6b, 0x51, 0x5d, 0x4e, + 0x41, 0x53, 0x08, 0x67, 0x6b, 0x70, 0x34, 0x6c, + 0x28, 0x67, 0xd1, 0x91, 0x1f, 0x57, 0xe5, 0x65, + 0x2a, 0x68, 0x09, 0x67, 0x3e, 0x79, 0x0d, 0x54, + 0x79, 0x72, 0xa1, 0x8c, 0x5d, 0x79, 0xb4, 0x52, + 0xd8, 0x79, 0x37, 0x75, 0x73, 0x59, 0x69, 0x90, + 0x2a, 0x51, 0x70, 0x53, 0xe8, 0x6c, 0x05, 0x98, + 0x11, 0x4f, 0x99, 0x51, 0x63, 0x6b, 0x0a, 0x4e, + 0x2d, 0x4e, 0x0b, 0x4e, 0xe6, 0x5d, 0xf3, 0x53, + 0x3b, 0x53, 0x97, 0x5b, 0x66, 0x5b, 0xe3, 0x76, + 0x01, 0x4f, 0xc7, 0x8c, 0x54, 0x53, 0x1c, 0x59, + 0x33, 0x00, 0x36, 0x00, 0x34, 0x00, 0x30, 0x00, + 0x35, 0x30, 0x31, 0x00, 0x08, 0x67, 0x31, 0x00, + 0x30, 0x00, 0x08, 0x67, 0x48, 0x67, 0x65, 0x72, + 0x67, 0x65, 0x56, 0x4c, 0x54, 0x44, 0xa2, 0x30, + 0x00, 0x02, 0x04, 0x06, 0x08, 0x09, 0x0b, 0x0d, + 0x0f, 0x11, 0x13, 0x15, 0x17, 0x19, 0x1b, 0x1d, + 0x1f, 0x22, 0x24, 0x26, 0x28, 0x29, 0x2a, 0x2b, + 0x2c, 0x2d, 0x30, 0x33, 0x36, 0x39, 0x3c, 0x3d, + 0x3e, 0x3f, 0x40, 0x42, 0x44, 0x46, 0x47, 0x48, + 0x49, 0x4a, 0x4b, 0x4d, 0x4e, 0x4f, 0x50, 0xe4, + 0x4e, 0x8c, 0x54, 0xa1, 0x30, 0x01, 0x30, 0x5b, + 0x27, 0x01, 0x4a, 0x34, 0x00, 0x01, 0x52, 0x39, + 0x01, 0xa2, 0x30, 0x00, 0x5a, 0x49, 0xa4, 0x30, + 0x00, 0x27, 0x4f, 0x0c, 0xa4, 0x30, 0x00, 0x4f, + 0x1d, 0x02, 0x05, 0x4f, 0xa8, 0x30, 0x00, 0x11, + 0x07, 0x54, 0x21, 0xa8, 0x30, 0x00, 0x54, 0x03, + 0x54, 0xa4, 0x30, 0x06, 0x4f, 0x15, 0x06, 0x58, + 0x3c, 0x07, 0x00, 0x46, 0xab, 0x30, 0x00, 0x3e, + 0x18, 0x1d, 0x00, 0x42, 0x3f, 0x51, 0xac, 0x30, + 0x00, 0x41, 0x47, 0x00, 0x47, 0x32, 0xae, 0x30, + 0xac, 0x30, 0xae, 0x30, 0x00, 0x1d, 0x4e, 0xad, + 0x30, 0x00, 0x38, 0x3d, 0x4f, 0x01, 0x3e, 0x13, + 0x4f, 0xad, 0x30, 0xed, 0x30, 0xad, 0x30, 0x00, + 0x40, 0x03, 0x3c, 0x33, 0xad, 0x30, 0x00, 0x40, + 0x34, 0x4f, 0x1b, 0x3e, 0xad, 0x30, 0x00, 0x40, + 0x42, 0x16, 0x1b, 0xb0, 0x30, 0x00, 0x39, 0x30, + 0xa4, 0x30, 0x0c, 0x45, 0x3c, 0x24, 0x4f, 0x0b, + 0x47, 0x18, 0x00, 0x49, 0xaf, 0x30, 0x00, 0x3e, + 0x4d, 0x1e, 0xb1, 0x30, 0x00, 0x4b, 0x08, 0x02, + 0x3a, 0x19, 0x02, 0x4b, 0x2c, 0xa4, 0x30, 0x11, + 0x00, 0x0b, 0x47, 0xb5, 0x30, 0x00, 0x3e, 0x0c, + 0x47, 0x2b, 0xb0, 0x30, 0x07, 0x3a, 0x43, 0x00, + 0xb9, 0x30, 0x02, 0x3a, 0x08, 0x02, 0x3a, 0x0f, + 0x07, 0x43, 0x00, 0xb7, 0x30, 0x10, 0x00, 0x12, + 0x34, 0x11, 0x3c, 0x13, 0x17, 0xa4, 0x30, 0x2a, + 0x1f, 0x24, 0x2b, 0x00, 0x20, 0xbb, 0x30, 0x16, + 0x41, 0x00, 0x38, 0x0d, 0xc4, 0x30, 0x0d, 0x38, + 0x00, 0xd0, 0x30, 0x00, 0x2c, 0x1c, 0x1b, 0xa2, + 0x30, 0x32, 0x00, 0x17, 0x26, 0x49, 0xaf, 0x30, + 0x25, 0x00, 0x3c, 0xb3, 0x30, 0x21, 0x00, 0x20, + 0x38, 0xa1, 0x30, 0x34, 0x00, 0x48, 0x22, 0x28, + 0xa3, 0x30, 0x32, 0x00, 0x59, 0x25, 0xa7, 0x30, + 0x2f, 0x1c, 0x10, 0x00, 0x44, 0xd5, 0x30, 0x00, + 0x14, 0x1e, 0xaf, 0x30, 0x29, 0x00, 0x10, 0x4d, + 0x3c, 0xda, 0x30, 0xbd, 0x30, 0xb8, 0x30, 0x22, + 0x13, 0x1a, 0x20, 0x33, 0x0c, 0x22, 0x3b, 0x01, + 0x22, 0x44, 0x00, 0x21, 0x44, 0x07, 0xa4, 0x30, + 0x39, 0x00, 0x4f, 0x24, 0xc8, 0x30, 0x14, 0x23, + 0x00, 0xdb, 0x30, 0xf3, 0x30, 0xc9, 0x30, 0x14, + 0x2a, 0x00, 0x12, 0x33, 0x22, 0x12, 0x33, 0x2a, + 0xa4, 0x30, 0x3a, 0x00, 0x0b, 0x49, 0xa4, 0x30, + 0x3a, 0x00, 0x47, 0x3a, 0x1f, 0x2b, 0x3a, 0x47, + 0x0b, 0xb7, 0x30, 0x27, 0x3c, 0x00, 0x30, 0x3c, + 0xaf, 0x30, 0x30, 0x00, 0x3e, 0x44, 0xdf, 0x30, + 0xea, 0x30, 0xd0, 0x30, 0x0f, 0x1a, 0x00, 0x2c, + 0x1b, 0xe1, 0x30, 0xac, 0x30, 0xac, 0x30, 0x35, + 0x00, 0x1c, 0x47, 0x35, 0x50, 0x1c, 0x3f, 0xa2, + 0x30, 0x42, 0x5a, 0x27, 0x42, 0x5a, 0x49, 0x44, + 0x00, 0x51, 0xc3, 0x30, 0x27, 0x00, 0x05, 0x28, + 0xea, 0x30, 0xe9, 0x30, 0xd4, 0x30, 0x17, 0x00, + 0x28, 0xd6, 0x30, 0x15, 0x26, 0x00, 0x15, 0xec, + 0x30, 0xe0, 0x30, 0xb2, 0x30, 0x3a, 0x41, 0x16, + 0x00, 0x41, 0xc3, 0x30, 0x2c, 0x00, 0x05, 0x30, + 0x00, 0xb9, 0x70, 0x31, 0x00, 0x30, 0x00, 0xb9, + 0x70, 0x32, 0x00, 0x30, 0x00, 0xb9, 0x70, 0x68, + 0x50, 0x61, 0x64, 0x61, 0x41, 0x55, 0x62, 0x61, + 0x72, 0x6f, 0x56, 0x70, 0x63, 0x64, 0x6d, 0x64, + 0x00, 0x6d, 0x00, 0xb2, 0x00, 0x49, 0x00, 0x55, + 0x00, 0x73, 0x5e, 0x10, 0x62, 0x2d, 0x66, 0x8c, + 0x54, 0x27, 0x59, 0x63, 0x6b, 0x0e, 0x66, 0xbb, + 0x6c, 0x2a, 0x68, 0x0f, 0x5f, 0x1a, 0x4f, 0x3e, + 0x79, 0x70, 0x00, 0x41, 0x6e, 0x00, 0x41, 0xbc, + 0x03, 0x41, 0x6d, 0x00, 0x41, 0x6b, 0x00, 0x41, + 0x4b, 0x00, 0x42, 0x4d, 0x00, 0x42, 0x47, 0x00, + 0x42, 0x63, 0x61, 0x6c, 0x6b, 0x63, 0x61, 0x6c, + 0x70, 0x00, 0x46, 0x6e, 0x00, 0x46, 0xbc, 0x03, + 0x46, 0xbc, 0x03, 0x67, 0x6d, 0x00, 0x67, 0x6b, + 0x00, 0x67, 0x48, 0x00, 0x7a, 0x6b, 0x48, 0x7a, + 0x4d, 0x48, 0x7a, 0x47, 0x48, 0x7a, 0x54, 0x48, + 0x7a, 0xbc, 0x03, 0x13, 0x21, 0x6d, 0x00, 0x13, + 0x21, 0x64, 0x00, 0x13, 0x21, 0x6b, 0x00, 0x13, + 0x21, 0x66, 0x00, 0x6d, 0x6e, 0x00, 0x6d, 0xbc, + 0x03, 0x6d, 0x6d, 0x00, 0x6d, 0x63, 0x00, 0x6d, + 0x6b, 0x00, 0x6d, 0x63, 0x00, 0x0a, 0x0a, 0x4f, + 0x00, 0x0a, 0x4f, 0x6d, 0x00, 0xb2, 0x00, 0x63, + 0x00, 0x08, 0x0a, 0x4f, 0x0a, 0x0a, 0x50, 0x00, + 0x0a, 0x50, 0x6d, 0x00, 0xb3, 0x00, 0x6b, 0x00, + 0x6d, 0x00, 0xb3, 0x00, 0x6d, 0x00, 0x15, 0x22, + 0x73, 0x00, 0x6d, 0x00, 0x15, 0x22, 0x73, 0x00, + 0xb2, 0x00, 0x50, 0x61, 0x6b, 0x50, 0x61, 0x4d, + 0x50, 0x61, 0x47, 0x50, 0x61, 0x72, 0x61, 0x64, + 0x72, 0x61, 0x64, 0xd1, 0x73, 0x72, 0x00, 0x61, + 0x00, 0x64, 0x00, 0x15, 0x22, 0x73, 0x00, 0xb2, + 0x00, 0x70, 0x00, 0x73, 0x6e, 0x00, 0x73, 0xbc, + 0x03, 0x73, 0x6d, 0x00, 0x73, 0x70, 0x00, 0x56, + 0x6e, 0x00, 0x56, 0xbc, 0x03, 0x56, 0x6d, 0x00, + 0x56, 0x6b, 0x00, 0x56, 0x4d, 0x00, 0x56, 0x70, + 0x00, 0x57, 0x6e, 0x00, 0x57, 0xbc, 0x03, 0x57, + 0x6d, 0x00, 0x57, 0x6b, 0x00, 0x57, 0x4d, 0x00, + 0x57, 0x6b, 0x00, 0xa9, 0x03, 0x4d, 0x00, 0xa9, + 0x03, 0x61, 0x2e, 0x6d, 0x2e, 0x42, 0x71, 0x63, + 0x63, 0x63, 0x64, 0x43, 0xd1, 0x6b, 0x67, 0x43, + 0x6f, 0x2e, 0x64, 0x42, 0x47, 0x79, 0x68, 0x61, + 0x48, 0x50, 0x69, 0x6e, 0x4b, 0x4b, 0x4b, 0x4d, + 0x6b, 0x74, 0x6c, 0x6d, 0x6c, 0x6e, 0x6c, 0x6f, + 0x67, 0x6c, 0x78, 0x6d, 0x62, 0x6d, 0x69, 0x6c, + 0x6d, 0x6f, 0x6c, 0x50, 0x48, 0x70, 0x2e, 0x6d, + 0x2e, 0x50, 0x50, 0x4d, 0x50, 0x52, 0x73, 0x72, + 0x53, 0x76, 0x57, 0x62, 0x56, 0xd1, 0x6d, 0x41, + 0xd1, 0x6d, 0x31, 0x00, 0xe5, 0x65, 0x31, 0x00, + 0x30, 0x00, 0xe5, 0x65, 0x32, 0x00, 0x30, 0x00, + 0xe5, 0x65, 0x33, 0x00, 0x30, 0x00, 0xe5, 0x65, + 0x67, 0x61, 0x6c, 0x4a, 0x04, 0x4c, 0x04, 0x43, + 0x46, 0x51, 0x26, 0x01, 0x53, 0x01, 0x27, 0xa7, + 0x37, 0xab, 0x6b, 0x02, 0x52, 0xab, 0x48, 0x8c, + 0xf4, 0x66, 0xca, 0x8e, 0xc8, 0x8c, 0xd1, 0x6e, + 0x32, 0x4e, 0xe5, 0x53, 0x9c, 0x9f, 0x9c, 0x9f, + 0x51, 0x59, 0xd1, 0x91, 0x87, 0x55, 0x48, 0x59, + 0xf6, 0x61, 0x69, 0x76, 0x85, 0x7f, 0x3f, 0x86, + 0xba, 0x87, 0xf8, 0x88, 0x8f, 0x90, 0x02, 0x6a, + 0x1b, 0x6d, 0xd9, 0x70, 0xde, 0x73, 0x3d, 0x84, + 0x6a, 0x91, 0xf1, 0x99, 0x82, 0x4e, 0x75, 0x53, + 0x04, 0x6b, 0x1b, 0x72, 0x2d, 0x86, 0x1e, 0x9e, + 0x50, 0x5d, 0xeb, 0x6f, 0xcd, 0x85, 0x64, 0x89, + 0xc9, 0x62, 0xd8, 0x81, 0x1f, 0x88, 0xca, 0x5e, + 0x17, 0x67, 0x6a, 0x6d, 0xfc, 0x72, 0xce, 0x90, + 0x86, 0x4f, 0xb7, 0x51, 0xde, 0x52, 0xc4, 0x64, + 0xd3, 0x6a, 0x10, 0x72, 0xe7, 0x76, 0x01, 0x80, + 0x06, 0x86, 0x5c, 0x86, 0xef, 0x8d, 0x32, 0x97, + 0x6f, 0x9b, 0xfa, 0x9d, 0x8c, 0x78, 0x7f, 0x79, + 0xa0, 0x7d, 0xc9, 0x83, 0x04, 0x93, 0x7f, 0x9e, + 0xd6, 0x8a, 0xdf, 0x58, 0x04, 0x5f, 0x60, 0x7c, + 0x7e, 0x80, 0x62, 0x72, 0xca, 0x78, 0xc2, 0x8c, + 0xf7, 0x96, 0xd8, 0x58, 0x62, 0x5c, 0x13, 0x6a, + 0xda, 0x6d, 0x0f, 0x6f, 0x2f, 0x7d, 0x37, 0x7e, + 0x4b, 0x96, 0xd2, 0x52, 0x8b, 0x80, 0xdc, 0x51, + 0xcc, 0x51, 0x1c, 0x7a, 0xbe, 0x7d, 0xf1, 0x83, + 0x75, 0x96, 0x80, 0x8b, 0xcf, 0x62, 0x02, 0x6a, + 0xfe, 0x8a, 0x39, 0x4e, 0xe7, 0x5b, 0x12, 0x60, + 0x87, 0x73, 0x70, 0x75, 0x17, 0x53, 0xfb, 0x78, + 0xbf, 0x4f, 0xa9, 0x5f, 0x0d, 0x4e, 0xcc, 0x6c, + 0x78, 0x65, 0x22, 0x7d, 0xc3, 0x53, 0x5e, 0x58, + 0x01, 0x77, 0x49, 0x84, 0xaa, 0x8a, 0xba, 0x6b, + 0xb0, 0x8f, 0x88, 0x6c, 0xfe, 0x62, 0xe5, 0x82, + 0xa0, 0x63, 0x65, 0x75, 0xae, 0x4e, 0x69, 0x51, + 0xc9, 0x51, 0x81, 0x68, 0xe7, 0x7c, 0x6f, 0x82, + 0xd2, 0x8a, 0xcf, 0x91, 0xf5, 0x52, 0x42, 0x54, + 0x73, 0x59, 0xec, 0x5e, 0xc5, 0x65, 0xfe, 0x6f, + 0x2a, 0x79, 0xad, 0x95, 0x6a, 0x9a, 0x97, 0x9e, + 0xce, 0x9e, 0x9b, 0x52, 0xc6, 0x66, 0x77, 0x6b, + 0x62, 0x8f, 0x74, 0x5e, 0x90, 0x61, 0x00, 0x62, + 0x9a, 0x64, 0x23, 0x6f, 0x49, 0x71, 0x89, 0x74, + 0xca, 0x79, 0xf4, 0x7d, 0x6f, 0x80, 0x26, 0x8f, + 0xee, 0x84, 0x23, 0x90, 0x4a, 0x93, 0x17, 0x52, + 0xa3, 0x52, 0xbd, 0x54, 0xc8, 0x70, 0xc2, 0x88, + 0xaa, 0x8a, 0xc9, 0x5e, 0xf5, 0x5f, 0x7b, 0x63, + 0xae, 0x6b, 0x3e, 0x7c, 0x75, 0x73, 0xe4, 0x4e, + 0xf9, 0x56, 0xe7, 0x5b, 0xba, 0x5d, 0x1c, 0x60, + 0xb2, 0x73, 0x69, 0x74, 0x9a, 0x7f, 0x46, 0x80, + 0x34, 0x92, 0xf6, 0x96, 0x48, 0x97, 0x18, 0x98, + 0x8b, 0x4f, 0xae, 0x79, 0xb4, 0x91, 0xb8, 0x96, + 0xe1, 0x60, 0x86, 0x4e, 0xda, 0x50, 0xee, 0x5b, + 0x3f, 0x5c, 0x99, 0x65, 0x02, 0x6a, 0xce, 0x71, + 0x42, 0x76, 0xfc, 0x84, 0x7c, 0x90, 0x8d, 0x9f, + 0x88, 0x66, 0x2e, 0x96, 0x89, 0x52, 0x7b, 0x67, + 0xf3, 0x67, 0x41, 0x6d, 0x9c, 0x6e, 0x09, 0x74, + 0x59, 0x75, 0x6b, 0x78, 0x10, 0x7d, 0x5e, 0x98, + 0x6d, 0x51, 0x2e, 0x62, 0x78, 0x96, 0x2b, 0x50, + 0x19, 0x5d, 0xea, 0x6d, 0x2a, 0x8f, 0x8b, 0x5f, + 0x44, 0x61, 0x17, 0x68, 0x87, 0x73, 0x86, 0x96, + 0x29, 0x52, 0x0f, 0x54, 0x65, 0x5c, 0x13, 0x66, + 0x4e, 0x67, 0xa8, 0x68, 0xe5, 0x6c, 0x06, 0x74, + 0xe2, 0x75, 0x79, 0x7f, 0xcf, 0x88, 0xe1, 0x88, + 0xcc, 0x91, 0xe2, 0x96, 0x3f, 0x53, 0xba, 0x6e, + 0x1d, 0x54, 0xd0, 0x71, 0x98, 0x74, 0xfa, 0x85, + 0xa3, 0x96, 0x57, 0x9c, 0x9f, 0x9e, 0x97, 0x67, + 0xcb, 0x6d, 0xe8, 0x81, 0xcb, 0x7a, 0x20, 0x7b, + 0x92, 0x7c, 0xc0, 0x72, 0x99, 0x70, 0x58, 0x8b, + 0xc0, 0x4e, 0x36, 0x83, 0x3a, 0x52, 0x07, 0x52, + 0xa6, 0x5e, 0xd3, 0x62, 0xd6, 0x7c, 0x85, 0x5b, + 0x1e, 0x6d, 0xb4, 0x66, 0x3b, 0x8f, 0x4c, 0x88, + 0x4d, 0x96, 0x8b, 0x89, 0xd3, 0x5e, 0x40, 0x51, + 0xc0, 0x55, 0x00, 0x00, 0x00, 0x00, 0x5a, 0x58, + 0x00, 0x00, 0x74, 0x66, 0x00, 0x00, 0x00, 0x00, + 0xde, 0x51, 0x2a, 0x73, 0xca, 0x76, 0x3c, 0x79, + 0x5e, 0x79, 0x65, 0x79, 0x8f, 0x79, 0x56, 0x97, + 0xbe, 0x7c, 0xbd, 0x7f, 0x00, 0x00, 0x12, 0x86, + 0x00, 0x00, 0xf8, 0x8a, 0x00, 0x00, 0x00, 0x00, + 0x38, 0x90, 0xfd, 0x90, 0xef, 0x98, 0xfc, 0x98, + 0x28, 0x99, 0xb4, 0x9d, 0xde, 0x90, 0xb7, 0x96, + 0xae, 0x4f, 0xe7, 0x50, 0x4d, 0x51, 0xc9, 0x52, + 0xe4, 0x52, 0x51, 0x53, 0x9d, 0x55, 0x06, 0x56, + 0x68, 0x56, 0x40, 0x58, 0xa8, 0x58, 0x64, 0x5c, + 0x6e, 0x5c, 0x94, 0x60, 0x68, 0x61, 0x8e, 0x61, + 0xf2, 0x61, 0x4f, 0x65, 0xe2, 0x65, 0x91, 0x66, + 0x85, 0x68, 0x77, 0x6d, 0x1a, 0x6e, 0x22, 0x6f, + 0x6e, 0x71, 0x2b, 0x72, 0x22, 0x74, 0x91, 0x78, + 0x3e, 0x79, 0x49, 0x79, 0x48, 0x79, 0x50, 0x79, + 0x56, 0x79, 0x5d, 0x79, 0x8d, 0x79, 0x8e, 0x79, + 0x40, 0x7a, 0x81, 0x7a, 0xc0, 0x7b, 0xf4, 0x7d, + 0x09, 0x7e, 0x41, 0x7e, 0x72, 0x7f, 0x05, 0x80, + 0xed, 0x81, 0x79, 0x82, 0x79, 0x82, 0x57, 0x84, + 0x10, 0x89, 0x96, 0x89, 0x01, 0x8b, 0x39, 0x8b, + 0xd3, 0x8c, 0x08, 0x8d, 0xb6, 0x8f, 0x38, 0x90, + 0xe3, 0x96, 0xff, 0x97, 0x3b, 0x98, 0x75, 0x60, + 0xee, 0x42, 0x18, 0x82, 0x02, 0x26, 0x4e, 0xb5, + 0x51, 0x68, 0x51, 0x80, 0x4f, 0x45, 0x51, 0x80, + 0x51, 0xc7, 0x52, 0xfa, 0x52, 0x9d, 0x55, 0x55, + 0x55, 0x99, 0x55, 0xe2, 0x55, 0x5a, 0x58, 0xb3, + 0x58, 0x44, 0x59, 0x54, 0x59, 0x62, 0x5a, 0x28, + 0x5b, 0xd2, 0x5e, 0xd9, 0x5e, 0x69, 0x5f, 0xad, + 0x5f, 0xd8, 0x60, 0x4e, 0x61, 0x08, 0x61, 0x8e, + 0x61, 0x60, 0x61, 0xf2, 0x61, 0x34, 0x62, 0xc4, + 0x63, 0x1c, 0x64, 0x52, 0x64, 0x56, 0x65, 0x74, + 0x66, 0x17, 0x67, 0x1b, 0x67, 0x56, 0x67, 0x79, + 0x6b, 0xba, 0x6b, 0x41, 0x6d, 0xdb, 0x6e, 0xcb, + 0x6e, 0x22, 0x6f, 0x1e, 0x70, 0x6e, 0x71, 0xa7, + 0x77, 0x35, 0x72, 0xaf, 0x72, 0x2a, 0x73, 0x71, + 0x74, 0x06, 0x75, 0x3b, 0x75, 0x1d, 0x76, 0x1f, + 0x76, 0xca, 0x76, 0xdb, 0x76, 0xf4, 0x76, 0x4a, + 0x77, 0x40, 0x77, 0xcc, 0x78, 0xb1, 0x7a, 0xc0, + 0x7b, 0x7b, 0x7c, 0x5b, 0x7d, 0xf4, 0x7d, 0x3e, + 0x7f, 0x05, 0x80, 0x52, 0x83, 0xef, 0x83, 0x79, + 0x87, 0x41, 0x89, 0x86, 0x89, 0x96, 0x89, 0xbf, + 0x8a, 0xf8, 0x8a, 0xcb, 0x8a, 0x01, 0x8b, 0xfe, + 0x8a, 0xed, 0x8a, 0x39, 0x8b, 0x8a, 0x8b, 0x08, + 0x8d, 0x38, 0x8f, 0x72, 0x90, 0x99, 0x91, 0x76, + 0x92, 0x7c, 0x96, 0xe3, 0x96, 0x56, 0x97, 0xdb, + 0x97, 0xff, 0x97, 0x0b, 0x98, 0x3b, 0x98, 0x12, + 0x9b, 0x9c, 0x9f, 0x4a, 0x28, 0x44, 0x28, 0xd5, + 0x33, 0x9d, 0x3b, 0x18, 0x40, 0x39, 0x40, 0x49, + 0x52, 0xd0, 0x5c, 0xd3, 0x7e, 0x43, 0x9f, 0x8e, + 0x9f, 0x2a, 0xa0, 0x02, 0x66, 0x66, 0x66, 0x69, + 0x66, 0x6c, 0x66, 0x66, 0x69, 0x66, 0x66, 0x6c, + 0x7f, 0x01, 0x74, 0x73, 0x00, 0x74, 0x65, 0x05, + 0x0f, 0x11, 0x0f, 0x00, 0x0f, 0x06, 0x19, 0x11, + 0x0f, 0x08, 0xd9, 0x05, 0xb4, 0x05, 0x00, 0x00, + 0x00, 0x00, 0xf2, 0x05, 0xb7, 0x05, 0xd0, 0x05, + 0x12, 0x00, 0x03, 0x04, 0x0b, 0x0c, 0x0d, 0x18, + 0x1a, 0xe9, 0x05, 0xc1, 0x05, 0xe9, 0x05, 0xc2, + 0x05, 0x49, 0xfb, 0xc1, 0x05, 0x49, 0xfb, 0xc2, + 0x05, 0xd0, 0x05, 0xb7, 0x05, 0xd0, 0x05, 0xb8, + 0x05, 0xd0, 0x05, 0xbc, 0x05, 0xd8, 0x05, 0xbc, + 0x05, 0xde, 0x05, 0xbc, 0x05, 0xe0, 0x05, 0xbc, + 0x05, 0xe3, 0x05, 0xbc, 0x05, 0xb9, 0x05, 0x2d, + 0x03, 0x2e, 0x03, 0x2f, 0x03, 0x30, 0x03, 0x31, + 0x03, 0x1c, 0x00, 0x18, 0x06, 0x22, 0x06, 0x2b, + 0x06, 0xd0, 0x05, 0xdc, 0x05, 0x71, 0x06, 0x00, + 0x00, 0x0a, 0x0a, 0x0a, 0x0a, 0x0d, 0x0d, 0x0d, + 0x0d, 0x0f, 0x0f, 0x0f, 0x0f, 0x09, 0x09, 0x09, + 0x09, 0x0e, 0x0e, 0x0e, 0x0e, 0x08, 0x08, 0x08, + 0x08, 0x33, 0x33, 0x33, 0x33, 0x35, 0x35, 0x35, + 0x35, 0x13, 0x13, 0x13, 0x13, 0x12, 0x12, 0x12, + 0x12, 0x15, 0x15, 0x15, 0x15, 0x16, 0x16, 0x16, + 0x16, 0x1c, 0x1c, 0x1b, 0x1b, 0x1d, 0x1d, 0x17, + 0x17, 0x27, 0x27, 0x20, 0x20, 0x38, 0x38, 0x38, + 0x38, 0x3e, 0x3e, 0x3e, 0x3e, 0x42, 0x42, 0x42, + 0x42, 0x40, 0x40, 0x40, 0x40, 0x49, 0x49, 0x4a, + 0x4a, 0x4a, 0x4a, 0x4f, 0x4f, 0x50, 0x50, 0x50, + 0x50, 0x4d, 0x4d, 0x4d, 0x4d, 0x61, 0x61, 0x62, + 0x62, 0x49, 0x06, 0x64, 0x64, 0x64, 0x64, 0x7e, + 0x7e, 0x7d, 0x7d, 0x7f, 0x7f, 0x2e, 0x82, 0x82, + 0x7c, 0x7c, 0x80, 0x80, 0x87, 0x87, 0x87, 0x87, + 0x00, 0x00, 0x26, 0x06, 0x00, 0x01, 0x00, 0x01, + 0x00, 0xaf, 0x00, 0xaf, 0x00, 0x22, 0x00, 0x22, + 0x00, 0xa1, 0x00, 0xa1, 0x00, 0xa0, 0x00, 0xa0, + 0x00, 0xa2, 0x00, 0xa2, 0x00, 0xaa, 0x00, 0xaa, + 0x00, 0xaa, 0x00, 0x23, 0x00, 0x23, 0x00, 0x23, + 0xcc, 0x06, 0x00, 0x00, 0x00, 0x00, 0x26, 0x06, + 0x00, 0x06, 0x00, 0x07, 0x00, 0x1f, 0x00, 0x23, + 0x00, 0x24, 0x02, 0x06, 0x02, 0x07, 0x02, 0x08, + 0x02, 0x1f, 0x02, 0x23, 0x02, 0x24, 0x04, 0x06, + 0x04, 0x07, 0x04, 0x08, 0x04, 0x1f, 0x04, 0x23, + 0x04, 0x24, 0x05, 0x06, 0x05, 0x1f, 0x05, 0x23, + 0x05, 0x24, 0x06, 0x07, 0x06, 0x1f, 0x07, 0x06, + 0x07, 0x1f, 0x08, 0x06, 0x08, 0x07, 0x08, 0x1f, + 0x0d, 0x06, 0x0d, 0x07, 0x0d, 0x08, 0x0d, 0x1f, + 0x0f, 0x07, 0x0f, 0x1f, 0x10, 0x06, 0x10, 0x07, + 0x10, 0x08, 0x10, 0x1f, 0x11, 0x07, 0x11, 0x1f, + 0x12, 0x1f, 0x13, 0x06, 0x13, 0x1f, 0x14, 0x06, + 0x14, 0x1f, 0x1b, 0x06, 0x1b, 0x07, 0x1b, 0x08, + 0x1b, 0x1f, 0x1b, 0x23, 0x1b, 0x24, 0x1c, 0x07, + 0x1c, 0x1f, 0x1c, 0x23, 0x1c, 0x24, 0x1d, 0x01, + 0x1d, 0x06, 0x1d, 0x07, 0x1d, 0x08, 0x1d, 0x1e, + 0x1d, 0x1f, 0x1d, 0x23, 0x1d, 0x24, 0x1e, 0x06, + 0x1e, 0x07, 0x1e, 0x08, 0x1e, 0x1f, 0x1e, 0x23, + 0x1e, 0x24, 0x1f, 0x06, 0x1f, 0x07, 0x1f, 0x08, + 0x1f, 0x1f, 0x1f, 0x23, 0x1f, 0x24, 0x20, 0x06, + 0x20, 0x07, 0x20, 0x08, 0x20, 0x1f, 0x20, 0x23, + 0x20, 0x24, 0x21, 0x06, 0x21, 0x1f, 0x21, 0x23, + 0x21, 0x24, 0x24, 0x06, 0x24, 0x07, 0x24, 0x08, + 0x24, 0x1f, 0x24, 0x23, 0x24, 0x24, 0x0a, 0x4a, + 0x0b, 0x4a, 0x23, 0x4a, 0x20, 0x00, 0x4c, 0x06, + 0x51, 0x06, 0x51, 0x06, 0xff, 0x00, 0x1f, 0x26, + 0x06, 0x00, 0x0b, 0x00, 0x0c, 0x00, 0x1f, 0x00, + 0x20, 0x00, 0x23, 0x00, 0x24, 0x02, 0x0b, 0x02, + 0x0c, 0x02, 0x1f, 0x02, 0x20, 0x02, 0x23, 0x02, + 0x24, 0x04, 0x0b, 0x04, 0x0c, 0x04, 0x1f, 0x26, + 0x06, 0x04, 0x20, 0x04, 0x23, 0x04, 0x24, 0x05, + 0x0b, 0x05, 0x0c, 0x05, 0x1f, 0x05, 0x20, 0x05, + 0x23, 0x05, 0x24, 0x1b, 0x23, 0x1b, 0x24, 0x1c, + 0x23, 0x1c, 0x24, 0x1d, 0x01, 0x1d, 0x1e, 0x1d, + 0x1f, 0x1d, 0x23, 0x1d, 0x24, 0x1e, 0x1f, 0x1e, + 0x23, 0x1e, 0x24, 0x1f, 0x01, 0x1f, 0x1f, 0x20, + 0x0b, 0x20, 0x0c, 0x20, 0x1f, 0x20, 0x20, 0x20, + 0x23, 0x20, 0x24, 0x23, 0x4a, 0x24, 0x0b, 0x24, + 0x0c, 0x24, 0x1f, 0x24, 0x20, 0x24, 0x23, 0x24, + 0x24, 0x00, 0x06, 0x00, 0x07, 0x00, 0x08, 0x00, + 0x1f, 0x00, 0x21, 0x02, 0x06, 0x02, 0x07, 0x02, + 0x08, 0x02, 0x1f, 0x02, 0x21, 0x04, 0x06, 0x04, + 0x07, 0x04, 0x08, 0x04, 0x1f, 0x04, 0x21, 0x05, + 0x1f, 0x06, 0x07, 0x06, 0x1f, 0x07, 0x06, 0x07, + 0x1f, 0x08, 0x06, 0x08, 0x1f, 0x0d, 0x06, 0x0d, + 0x07, 0x0d, 0x08, 0x0d, 0x1f, 0x0f, 0x07, 0x0f, + 0x08, 0x0f, 0x1f, 0x10, 0x06, 0x10, 0x07, 0x10, + 0x08, 0x10, 0x1f, 0x11, 0x07, 0x12, 0x1f, 0x13, + 0x06, 0x13, 0x1f, 0x14, 0x06, 0x14, 0x1f, 0x1b, + 0x06, 0x1b, 0x07, 0x1b, 0x08, 0x1b, 0x1f, 0x1c, + 0x07, 0x1c, 0x1f, 0x1d, 0x06, 0x1d, 0x07, 0x1d, + 0x08, 0x1d, 0x1e, 0x1d, 0x1f, 0x1e, 0x06, 0x1e, + 0x07, 0x1e, 0x08, 0x1e, 0x1f, 0x1e, 0x21, 0x1f, + 0x06, 0x1f, 0x07, 0x1f, 0x08, 0x1f, 0x1f, 0x20, + 0x06, 0x20, 0x07, 0x20, 0x08, 0x20, 0x1f, 0x20, + 0x21, 0x21, 0x06, 0x21, 0x1f, 0x21, 0x4a, 0x24, + 0x06, 0x24, 0x07, 0x24, 0x08, 0x24, 0x1f, 0x24, + 0x21, 0x00, 0x1f, 0x00, 0x21, 0x02, 0x1f, 0x02, + 0x21, 0x04, 0x1f, 0x04, 0x21, 0x05, 0x1f, 0x05, + 0x21, 0x0d, 0x1f, 0x0d, 0x21, 0x0e, 0x1f, 0x0e, + 0x21, 0x1d, 0x1e, 0x1d, 0x1f, 0x1e, 0x1f, 0x20, + 0x1f, 0x20, 0x21, 0x24, 0x1f, 0x24, 0x21, 0x40, + 0x06, 0x4e, 0x06, 0x51, 0x06, 0x27, 0x06, 0x10, + 0x22, 0x10, 0x23, 0x12, 0x22, 0x12, 0x23, 0x13, + 0x22, 0x13, 0x23, 0x0c, 0x22, 0x0c, 0x23, 0x0d, + 0x22, 0x0d, 0x23, 0x06, 0x22, 0x06, 0x23, 0x05, + 0x22, 0x05, 0x23, 0x07, 0x22, 0x07, 0x23, 0x0e, + 0x22, 0x0e, 0x23, 0x0f, 0x22, 0x0f, 0x23, 0x0d, + 0x05, 0x0d, 0x06, 0x0d, 0x07, 0x0d, 0x1e, 0x0d, + 0x0a, 0x0c, 0x0a, 0x0e, 0x0a, 0x0f, 0x0a, 0x10, + 0x22, 0x10, 0x23, 0x12, 0x22, 0x12, 0x23, 0x13, + 0x22, 0x13, 0x23, 0x0c, 0x22, 0x0c, 0x23, 0x0d, + 0x22, 0x0d, 0x23, 0x06, 0x22, 0x06, 0x23, 0x05, + 0x22, 0x05, 0x23, 0x07, 0x22, 0x07, 0x23, 0x0e, + 0x22, 0x0e, 0x23, 0x0f, 0x22, 0x0f, 0x23, 0x0d, + 0x05, 0x0d, 0x06, 0x0d, 0x07, 0x0d, 0x1e, 0x0d, + 0x0a, 0x0c, 0x0a, 0x0e, 0x0a, 0x0f, 0x0a, 0x0d, + 0x05, 0x0d, 0x06, 0x0d, 0x07, 0x0d, 0x1e, 0x0c, + 0x20, 0x0d, 0x20, 0x10, 0x1e, 0x0c, 0x05, 0x0c, + 0x06, 0x0c, 0x07, 0x0d, 0x05, 0x0d, 0x06, 0x0d, + 0x07, 0x10, 0x1e, 0x11, 0x1e, 0x00, 0x24, 0x00, + 0x24, 0x2a, 0x06, 0x00, 0x02, 0x1b, 0x00, 0x03, + 0x02, 0x00, 0x03, 0x02, 0x00, 0x03, 0x1b, 0x00, + 0x04, 0x1b, 0x00, 0x1b, 0x02, 0x00, 0x1b, 0x03, + 0x00, 0x1b, 0x04, 0x02, 0x1b, 0x03, 0x02, 0x1b, + 0x03, 0x03, 0x1b, 0x20, 0x03, 0x1b, 0x1f, 0x09, + 0x03, 0x02, 0x09, 0x02, 0x03, 0x09, 0x02, 0x1f, + 0x09, 0x1b, 0x03, 0x09, 0x1b, 0x03, 0x09, 0x1b, + 0x02, 0x09, 0x1b, 0x1b, 0x09, 0x1b, 0x1b, 0x0b, + 0x03, 0x03, 0x0b, 0x03, 0x03, 0x0b, 0x1b, 0x1b, + 0x0a, 0x03, 0x1b, 0x0a, 0x03, 0x1b, 0x0a, 0x02, + 0x20, 0x0a, 0x1b, 0x04, 0x0a, 0x1b, 0x04, 0x0a, + 0x1b, 0x1b, 0x0a, 0x1b, 0x1b, 0x0c, 0x03, 0x1f, + 0x0c, 0x04, 0x1b, 0x0c, 0x04, 0x1b, 0x0d, 0x1b, + 0x03, 0x0d, 0x1b, 0x03, 0x0d, 0x1b, 0x1b, 0x0d, + 0x1b, 0x20, 0x0f, 0x02, 0x1b, 0x0f, 0x1b, 0x1b, + 0x0f, 0x1b, 0x1b, 0x0f, 0x1b, 0x1f, 0x10, 0x1b, + 0x1b, 0x10, 0x1b, 0x20, 0x10, 0x1b, 0x1f, 0x17, + 0x04, 0x1b, 0x17, 0x04, 0x1b, 0x18, 0x1b, 0x03, + 0x18, 0x1b, 0x1b, 0x1a, 0x03, 0x1b, 0x1a, 0x03, + 0x20, 0x1a, 0x03, 0x1f, 0x1a, 0x02, 0x02, 0x1a, + 0x02, 0x02, 0x1a, 0x04, 0x1b, 0x1a, 0x04, 0x1b, + 0x1a, 0x1b, 0x03, 0x1a, 0x1b, 0x03, 0x1b, 0x03, + 0x02, 0x1b, 0x03, 0x1b, 0x1b, 0x03, 0x20, 0x1b, + 0x02, 0x03, 0x1b, 0x02, 0x1b, 0x1b, 0x04, 0x02, + 0x1b, 0x04, 0x1b, 0x28, 0x06, 0x1d, 0x04, 0x06, + 0x1f, 0x1d, 0x04, 0x1f, 0x1d, 0x1d, 0x1e, 0x05, + 0x1d, 0x1e, 0x05, 0x21, 0x1e, 0x04, 0x1d, 0x1e, + 0x04, 0x1d, 0x1e, 0x04, 0x21, 0x1e, 0x1d, 0x22, + 0x1e, 0x1d, 0x21, 0x22, 0x1d, 0x1d, 0x22, 0x1d, + 0x1d, 0x00, 0x06, 0x22, 0x02, 0x04, 0x22, 0x02, + 0x04, 0x21, 0x02, 0x06, 0x22, 0x02, 0x06, 0x21, + 0x02, 0x1d, 0x22, 0x02, 0x1d, 0x21, 0x04, 0x1d, + 0x22, 0x04, 0x05, 0x21, 0x04, 0x1d, 0x21, 0x0b, + 0x06, 0x21, 0x0d, 0x05, 0x22, 0x0c, 0x05, 0x22, + 0x0e, 0x05, 0x22, 0x1c, 0x04, 0x22, 0x1c, 0x1d, + 0x22, 0x22, 0x05, 0x22, 0x22, 0x04, 0x22, 0x22, + 0x1d, 0x22, 0x1d, 0x1d, 0x22, 0x1a, 0x1d, 0x22, + 0x1e, 0x05, 0x22, 0x1a, 0x1d, 0x05, 0x1c, 0x05, + 0x1d, 0x11, 0x1d, 0x22, 0x1b, 0x1d, 0x22, 0x1e, + 0x04, 0x05, 0x1d, 0x06, 0x22, 0x1c, 0x04, 0x1d, + 0x1b, 0x1d, 0x1d, 0x1c, 0x04, 0x1d, 0x1e, 0x04, + 0x05, 0x04, 0x05, 0x22, 0x05, 0x04, 0x22, 0x1d, + 0x04, 0x22, 0x19, 0x1d, 0x22, 0x00, 0x05, 0x22, + 0x1b, 0x1d, 0x1d, 0x11, 0x04, 0x1d, 0x0d, 0x1d, + 0x1d, 0x0b, 0x06, 0x22, 0x1e, 0x04, 0x22, 0x35, + 0x06, 0x00, 0x0f, 0x9d, 0x0d, 0x0f, 0x9d, 0x27, + 0x06, 0x00, 0x1d, 0x1d, 0x20, 0x00, 0x1c, 0x01, + 0x0a, 0x1e, 0x06, 0x1e, 0x08, 0x0e, 0x1d, 0x12, + 0x1e, 0x0a, 0x0c, 0x21, 0x1d, 0x12, 0x1d, 0x23, + 0x20, 0x21, 0x0c, 0x1d, 0x1e, 0x35, 0x06, 0x00, + 0x0f, 0x14, 0x27, 0x06, 0x0e, 0x1d, 0x22, 0xff, + 0x00, 0x1d, 0x1d, 0x20, 0xff, 0x12, 0x1d, 0x23, + 0x20, 0xff, 0x21, 0x0c, 0x1d, 0x1e, 0x27, 0x06, + 0x05, 0x1d, 0xff, 0x05, 0x1d, 0x00, 0x1d, 0x20, + 0x27, 0x06, 0x0a, 0xa5, 0x00, 0x1d, 0x2c, 0x00, + 0x01, 0x30, 0x02, 0x30, 0x3a, 0x00, 0x3b, 0x00, + 0x21, 0x00, 0x3f, 0x00, 0x16, 0x30, 0x17, 0x30, + 0x26, 0x20, 0x13, 0x20, 0x12, 0x01, 0x00, 0x5f, + 0x5f, 0x28, 0x29, 0x7b, 0x7d, 0x08, 0x30, 0x0c, + 0x0d, 0x08, 0x09, 0x02, 0x03, 0x00, 0x01, 0x04, + 0x05, 0x06, 0x07, 0x5b, 0x00, 0x5d, 0x00, 0x3e, + 0x20, 0x3e, 0x20, 0x3e, 0x20, 0x3e, 0x20, 0x5f, + 0x00, 0x5f, 0x00, 0x5f, 0x00, 0x2c, 0x00, 0x01, + 0x30, 0x2e, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x3a, + 0x00, 0x3f, 0x00, 0x21, 0x00, 0x14, 0x20, 0x28, + 0x00, 0x29, 0x00, 0x7b, 0x00, 0x7d, 0x00, 0x14, + 0x30, 0x15, 0x30, 0x23, 0x26, 0x2a, 0x2b, 0x2d, + 0x3c, 0x3e, 0x3d, 0x00, 0x5c, 0x24, 0x25, 0x40, + 0x40, 0x06, 0xff, 0x0b, 0x00, 0x0b, 0xff, 0x0c, + 0x20, 0x00, 0x4d, 0x06, 0x40, 0x06, 0xff, 0x0e, + 0x00, 0x0e, 0xff, 0x0f, 0x00, 0x0f, 0xff, 0x10, + 0x00, 0x10, 0xff, 0x11, 0x00, 0x11, 0xff, 0x12, + 0x00, 0x12, 0x21, 0x06, 0x00, 0x01, 0x01, 0x02, + 0x02, 0x03, 0x03, 0x04, 0x04, 0x05, 0x05, 0x05, + 0x05, 0x06, 0x06, 0x07, 0x07, 0x07, 0x07, 0x08, + 0x08, 0x09, 0x09, 0x09, 0x09, 0x0a, 0x0a, 0x0a, + 0x0a, 0x0b, 0x0b, 0x0b, 0x0b, 0x0c, 0x0c, 0x0c, + 0x0c, 0x0d, 0x0d, 0x0d, 0x0d, 0x0e, 0x0e, 0x0f, + 0x0f, 0x10, 0x10, 0x11, 0x11, 0x12, 0x12, 0x12, + 0x12, 0x13, 0x13, 0x13, 0x13, 0x14, 0x14, 0x14, + 0x14, 0x15, 0x15, 0x15, 0x15, 0x16, 0x16, 0x16, + 0x16, 0x17, 0x17, 0x17, 0x17, 0x18, 0x18, 0x18, + 0x18, 0x19, 0x19, 0x19, 0x19, 0x20, 0x20, 0x20, + 0x20, 0x21, 0x21, 0x21, 0x21, 0x22, 0x22, 0x22, + 0x22, 0x23, 0x23, 0x23, 0x23, 0x24, 0x24, 0x24, + 0x24, 0x25, 0x25, 0x25, 0x25, 0x26, 0x26, 0x26, + 0x26, 0x27, 0x27, 0x28, 0x28, 0x29, 0x29, 0x29, + 0x29, 0x22, 0x06, 0x22, 0x00, 0x22, 0x00, 0x22, + 0x01, 0x22, 0x01, 0x22, 0x03, 0x22, 0x03, 0x22, + 0x05, 0x22, 0x05, 0x21, 0x00, 0x85, 0x29, 0x01, + 0x30, 0x01, 0x0b, 0x0c, 0x00, 0xfa, 0xf1, 0xa0, + 0xa2, 0xa4, 0xa6, 0xa8, 0xe2, 0xe4, 0xe6, 0xc2, + 0xfb, 0xa1, 0xa3, 0xa5, 0xa7, 0xa9, 0xaa, 0xac, + 0xae, 0xb0, 0xb2, 0xb4, 0xb6, 0xb8, 0xba, 0xbc, + 0xbe, 0xc0, 0xc3, 0xc5, 0xc7, 0xc9, 0xca, 0xcb, + 0xcc, 0xcd, 0xce, 0xd1, 0xd4, 0xd7, 0xda, 0xdd, + 0xde, 0xdf, 0xe0, 0xe1, 0xe3, 0xe5, 0xe7, 0xe8, + 0xe9, 0xea, 0xeb, 0xec, 0xee, 0xf2, 0x98, 0x99, + 0x31, 0x31, 0x4f, 0x31, 0x55, 0x31, 0x5b, 0x31, + 0x61, 0x31, 0xa2, 0x00, 0xa3, 0x00, 0xac, 0x00, + 0xaf, 0x00, 0xa6, 0x00, 0xa5, 0x00, 0xa9, 0x20, + 0x00, 0x00, 0x02, 0x25, 0x90, 0x21, 0x91, 0x21, + 0x92, 0x21, 0x93, 0x21, 0xa0, 0x25, 0xcb, 0x25, + 0xd2, 0x05, 0x07, 0x03, 0x01, 0xda, 0x05, 0x07, + 0x03, 0x01, 0xd0, 0x02, 0xd1, 0x02, 0xe6, 0x00, + 0x99, 0x02, 0x53, 0x02, 0x00, 0x00, 0xa3, 0x02, + 0x66, 0xab, 0xa5, 0x02, 0xa4, 0x02, 0x56, 0x02, + 0x57, 0x02, 0x91, 0x1d, 0x58, 0x02, 0x5e, 0x02, + 0xa9, 0x02, 0x64, 0x02, 0x62, 0x02, 0x60, 0x02, + 0x9b, 0x02, 0x27, 0x01, 0x9c, 0x02, 0x67, 0x02, + 0x84, 0x02, 0xaa, 0x02, 0xab, 0x02, 0x6c, 0x02, + 0x04, 0xdf, 0x8e, 0xa7, 0x6e, 0x02, 0x05, 0xdf, + 0x8e, 0x02, 0x06, 0xdf, 0xf8, 0x00, 0x76, 0x02, + 0x77, 0x02, 0x71, 0x00, 0x7a, 0x02, 0x08, 0xdf, + 0x7d, 0x02, 0x7e, 0x02, 0x80, 0x02, 0xa8, 0x02, + 0xa6, 0x02, 0x67, 0xab, 0xa7, 0x02, 0x88, 0x02, + 0x71, 0x2c, 0x00, 0x00, 0x8f, 0x02, 0xa1, 0x02, + 0xa2, 0x02, 0x98, 0x02, 0xc0, 0x01, 0xc1, 0x01, + 0xc2, 0x01, 0x0a, 0xdf, 0x1e, 0xdf, 0x41, 0x04, + 0x40, 0x00, 0x00, 0x00, 0x00, 0x14, 0x99, 0x10, + 0xba, 0x10, 0x00, 0x00, 0x00, 0x00, 0x9b, 0x10, + 0xba, 0x10, 0x05, 0x05, 0xa5, 0x10, 0xba, 0x10, + 0x05, 0x31, 0x11, 0x27, 0x11, 0x32, 0x11, 0x27, + 0x11, 0x55, 0x47, 0x13, 0x3e, 0x13, 0x47, 0x13, + 0x57, 0x13, 0x55, 0x82, 0x13, 0xc9, 0x13, 0x00, + 0x00, 0x00, 0x00, 0x84, 0x13, 0xbb, 0x13, 0x05, + 0x05, 0x8b, 0x13, 0xc2, 0x13, 0x05, 0x90, 0x13, + 0xc9, 0x13, 0x05, 0xc2, 0x13, 0xc2, 0x13, 0x00, + 0x00, 0x00, 0x00, 0xc2, 0x13, 0xb8, 0x13, 0xc2, + 0x13, 0xc9, 0x13, 0x05, 0x55, 0xb9, 0x14, 0xba, + 0x14, 0xb9, 0x14, 0xb0, 0x14, 0x00, 0x00, 0x00, + 0x00, 0xb9, 0x14, 0xbd, 0x14, 0x55, 0x50, 0xb8, + 0x15, 0xaf, 0x15, 0xb9, 0x15, 0xaf, 0x15, 0x55, + 0x35, 0x19, 0x30, 0x19, 0x05, 0x1e, 0x61, 0x1e, + 0x61, 0x1e, 0x61, 0x29, 0x61, 0x1e, 0x61, 0x1f, + 0x61, 0x29, 0x61, 0x1f, 0x61, 0x1e, 0x61, 0x20, + 0x61, 0x21, 0x61, 0x1f, 0x61, 0x22, 0x61, 0x1f, + 0x61, 0x21, 0x61, 0x20, 0x61, 0x55, 0x55, 0x55, + 0x55, 0x67, 0x6d, 0x67, 0x6d, 0x63, 0x6d, 0x67, + 0x6d, 0x69, 0x6d, 0x67, 0x6d, 0x55, 0x05, 0x41, + 0x00, 0x30, 0x00, 0x57, 0xd1, 0x65, 0xd1, 0x58, + 0xd1, 0x65, 0xd1, 0x5f, 0xd1, 0x6e, 0xd1, 0x5f, + 0xd1, 0x6f, 0xd1, 0x5f, 0xd1, 0x70, 0xd1, 0x5f, + 0xd1, 0x71, 0xd1, 0x5f, 0xd1, 0x72, 0xd1, 0x55, + 0x55, 0x55, 0x05, 0xb9, 0xd1, 0x65, 0xd1, 0xba, + 0xd1, 0x65, 0xd1, 0xbb, 0xd1, 0x6e, 0xd1, 0xbc, + 0xd1, 0x6e, 0xd1, 0xbb, 0xd1, 0x6f, 0xd1, 0xbc, + 0xd1, 0x6f, 0xd1, 0x55, 0x55, 0x55, 0x41, 0x00, + 0x61, 0x00, 0x41, 0x00, 0x61, 0x00, 0x69, 0x00, + 0x41, 0x00, 0x61, 0x00, 0x41, 0x00, 0x43, 0x44, + 0x00, 0x00, 0x47, 0x00, 0x00, 0x4a, 0x4b, 0x00, + 0x00, 0x4e, 0x4f, 0x50, 0x51, 0x00, 0x53, 0x54, + 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x61, 0x62, + 0x63, 0x64, 0x00, 0x66, 0x68, 0x00, 0x70, 0x00, + 0x41, 0x00, 0x61, 0x00, 0x41, 0x42, 0x00, 0x44, + 0x45, 0x46, 0x47, 0x4a, 0x00, 0x53, 0x00, 0x61, + 0x00, 0x41, 0x42, 0x00, 0x44, 0x45, 0x46, 0x47, + 0x00, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x00, 0x4f, + 0x53, 0x00, 0x61, 0x00, 0x41, 0x00, 0x61, 0x00, + 0x41, 0x00, 0x61, 0x00, 0x41, 0x00, 0x61, 0x00, + 0x41, 0x00, 0x61, 0x00, 0x41, 0x00, 0x61, 0x00, + 0x41, 0x00, 0x61, 0x00, 0x31, 0x01, 0x37, 0x02, + 0x91, 0x03, 0xa3, 0x03, 0xb1, 0x03, 0xd1, 0x03, + 0x24, 0x00, 0x1f, 0x04, 0x20, 0x05, 0x91, 0x03, + 0xa3, 0x03, 0xb1, 0x03, 0xd1, 0x03, 0x24, 0x00, + 0x1f, 0x04, 0x20, 0x05, 0x91, 0x03, 0xa3, 0x03, + 0xb1, 0x03, 0xd1, 0x03, 0x24, 0x00, 0x1f, 0x04, + 0x20, 0x05, 0x91, 0x03, 0xa3, 0x03, 0xb1, 0x03, + 0xd1, 0x03, 0x24, 0x00, 0x1f, 0x04, 0x20, 0x05, + 0x91, 0x03, 0xa3, 0x03, 0xb1, 0x03, 0xd1, 0x03, + 0x24, 0x00, 0x1f, 0x04, 0x20, 0x05, 0x0b, 0x0c, + 0x30, 0x00, 0x30, 0x00, 0x30, 0x00, 0x30, 0x00, + 0x30, 0x00, 0x30, 0x04, 0x3a, 0x04, 0x3e, 0x04, + 0x4b, 0x04, 0x4d, 0x04, 0x4e, 0x04, 0x89, 0xa6, + 0x30, 0x04, 0xa9, 0x26, 0x28, 0xb9, 0x7f, 0x9f, + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x0a, 0x0b, 0x0e, 0x0f, 0x11, 0x13, 0x14, + 0x15, 0x16, 0x17, 0x18, 0x1a, 0x1b, 0x61, 0x26, + 0x25, 0x2f, 0x7b, 0x51, 0xa6, 0xb1, 0x04, 0x27, + 0x06, 0x00, 0x01, 0x05, 0x08, 0x2a, 0x06, 0x1e, + 0x08, 0x03, 0x0d, 0x20, 0x19, 0x1a, 0x1b, 0x1c, + 0x09, 0x0f, 0x17, 0x0b, 0x18, 0x07, 0x0a, 0x00, + 0x01, 0x04, 0x06, 0x0c, 0x0e, 0x10, 0x44, 0x90, + 0x77, 0x45, 0x28, 0x06, 0x2c, 0x06, 0x00, 0x00, + 0x47, 0x06, 0x33, 0x06, 0x17, 0x10, 0x11, 0x12, + 0x13, 0x00, 0x06, 0x0e, 0x02, 0x0f, 0x34, 0x06, + 0x2a, 0x06, 0x2b, 0x06, 0x2e, 0x06, 0x00, 0x00, + 0x36, 0x06, 0x00, 0x00, 0x3a, 0x06, 0x2d, 0x06, + 0x00, 0x00, 0x4a, 0x06, 0x00, 0x00, 0x44, 0x06, + 0x00, 0x00, 0x46, 0x06, 0x33, 0x06, 0x39, 0x06, + 0x00, 0x00, 0x35, 0x06, 0x42, 0x06, 0x00, 0x00, + 0x34, 0x06, 0x00, 0x00, 0x00, 0x00, 0x2e, 0x06, + 0x00, 0x00, 0x36, 0x06, 0x00, 0x00, 0x3a, 0x06, + 0x00, 0x00, 0xba, 0x06, 0x00, 0x00, 0x6f, 0x06, + 0x00, 0x00, 0x28, 0x06, 0x2c, 0x06, 0x00, 0x00, + 0x47, 0x06, 0x00, 0x00, 0x00, 0x00, 0x2d, 0x06, + 0x37, 0x06, 0x4a, 0x06, 0x43, 0x06, 0x00, 0x00, + 0x45, 0x06, 0x46, 0x06, 0x33, 0x06, 0x39, 0x06, + 0x41, 0x06, 0x35, 0x06, 0x42, 0x06, 0x00, 0x00, + 0x34, 0x06, 0x2a, 0x06, 0x2b, 0x06, 0x2e, 0x06, + 0x00, 0x00, 0x36, 0x06, 0x38, 0x06, 0x3a, 0x06, + 0x6e, 0x06, 0x00, 0x00, 0xa1, 0x06, 0x27, 0x06, + 0x00, 0x01, 0x05, 0x08, 0x20, 0x21, 0x0b, 0x06, + 0x10, 0x23, 0x2a, 0x06, 0x1a, 0x1b, 0x1c, 0x09, + 0x0f, 0x17, 0x0b, 0x18, 0x07, 0x0a, 0x00, 0x01, + 0x04, 0x06, 0x0c, 0x0e, 0x10, 0x28, 0x06, 0x2c, + 0x06, 0x2f, 0x06, 0x00, 0x00, 0x48, 0x06, 0x32, + 0x06, 0x2d, 0x06, 0x37, 0x06, 0x4a, 0x06, 0x2a, + 0x06, 0x1a, 0x1b, 0x1c, 0x09, 0x0f, 0x17, 0x0b, + 0x18, 0x07, 0x0a, 0x00, 0x01, 0x04, 0x06, 0x0c, + 0x0e, 0x10, 0x30, 0x2e, 0x30, 0x00, 0x2c, 0x00, + 0x28, 0x00, 0x41, 0x00, 0x29, 0x00, 0x14, 0x30, + 0x53, 0x00, 0x15, 0x30, 0x43, 0x52, 0x43, 0x44, + 0x57, 0x5a, 0x41, 0x00, 0x48, 0x56, 0x4d, 0x56, + 0x53, 0x44, 0x53, 0x53, 0x50, 0x50, 0x56, 0x57, + 0x43, 0x4d, 0x43, 0x4d, 0x44, 0x4d, 0x52, 0x44, + 0x4a, 0x4b, 0x30, 0x30, 0x00, 0x68, 0x68, 0x4b, + 0x62, 0x57, 0x5b, 0xcc, 0x53, 0xc7, 0x30, 0x8c, + 0x4e, 0x1a, 0x59, 0xe3, 0x89, 0x29, 0x59, 0xa4, + 0x4e, 0x20, 0x66, 0x21, 0x71, 0x99, 0x65, 0x4d, + 0x52, 0x8c, 0x5f, 0x8d, 0x51, 0xb0, 0x65, 0x1d, + 0x52, 0x42, 0x7d, 0x1f, 0x75, 0xa9, 0x8c, 0xf0, + 0x58, 0x39, 0x54, 0x14, 0x6f, 0x95, 0x62, 0x55, + 0x63, 0x00, 0x4e, 0x09, 0x4e, 0x4a, 0x90, 0xe6, + 0x5d, 0x2d, 0x4e, 0xf3, 0x53, 0x07, 0x63, 0x70, + 0x8d, 0x53, 0x62, 0x81, 0x79, 0x7a, 0x7a, 0x08, + 0x54, 0x80, 0x6e, 0x09, 0x67, 0x08, 0x67, 0x33, + 0x75, 0x72, 0x52, 0xb6, 0x55, 0x4d, 0x91, 0x14, + 0x30, 0x15, 0x30, 0x2c, 0x67, 0x09, 0x4e, 0x8c, + 0x4e, 0x89, 0x5b, 0xb9, 0x70, 0x53, 0x62, 0xd7, + 0x76, 0xdd, 0x52, 0x57, 0x65, 0x97, 0x5f, 0xef, + 0x53, 0x30, 0x00, 0x38, 0x4e, 0x05, 0x00, 0x09, + 0x22, 0x01, 0x60, 0x4f, 0xae, 0x4f, 0xbb, 0x4f, + 0x02, 0x50, 0x7a, 0x50, 0x99, 0x50, 0xe7, 0x50, + 0xcf, 0x50, 0x9e, 0x34, 0x3a, 0x06, 0x4d, 0x51, + 0x54, 0x51, 0x64, 0x51, 0x77, 0x51, 0x1c, 0x05, + 0xb9, 0x34, 0x67, 0x51, 0x8d, 0x51, 0x4b, 0x05, + 0x97, 0x51, 0xa4, 0x51, 0xcc, 0x4e, 0xac, 0x51, + 0xb5, 0x51, 0xdf, 0x91, 0xf5, 0x51, 0x03, 0x52, + 0xdf, 0x34, 0x3b, 0x52, 0x46, 0x52, 0x72, 0x52, + 0x77, 0x52, 0x15, 0x35, 0x02, 0x00, 0x20, 0x80, + 0x80, 0x00, 0x08, 0x00, 0x00, 0xc7, 0x52, 0x00, + 0x02, 0x1d, 0x33, 0x3e, 0x3f, 0x50, 0x82, 0x8a, + 0x93, 0xac, 0xb6, 0xb8, 0xb8, 0xb8, 0x2c, 0x0a, + 0x70, 0x70, 0xca, 0x53, 0xdf, 0x53, 0x63, 0x0b, + 0xeb, 0x53, 0xf1, 0x53, 0x06, 0x54, 0x9e, 0x54, + 0x38, 0x54, 0x48, 0x54, 0x68, 0x54, 0xa2, 0x54, + 0xf6, 0x54, 0x10, 0x55, 0x53, 0x55, 0x63, 0x55, + 0x84, 0x55, 0x84, 0x55, 0x99, 0x55, 0xab, 0x55, + 0xb3, 0x55, 0xc2, 0x55, 0x16, 0x57, 0x06, 0x56, + 0x17, 0x57, 0x51, 0x56, 0x74, 0x56, 0x07, 0x52, + 0xee, 0x58, 0xce, 0x57, 0xf4, 0x57, 0x0d, 0x58, + 0x8b, 0x57, 0x32, 0x58, 0x31, 0x58, 0xac, 0x58, + 0xe4, 0x14, 0xf2, 0x58, 0xf7, 0x58, 0x06, 0x59, + 0x1a, 0x59, 0x22, 0x59, 0x62, 0x59, 0xa8, 0x16, + 0xea, 0x16, 0xec, 0x59, 0x1b, 0x5a, 0x27, 0x5a, + 0xd8, 0x59, 0x66, 0x5a, 0xee, 0x36, 0xfc, 0x36, + 0x08, 0x5b, 0x3e, 0x5b, 0x3e, 0x5b, 0xc8, 0x19, + 0xc3, 0x5b, 0xd8, 0x5b, 0xe7, 0x5b, 0xf3, 0x5b, + 0x18, 0x1b, 0xff, 0x5b, 0x06, 0x5c, 0x53, 0x5f, + 0x22, 0x5c, 0x81, 0x37, 0x60, 0x5c, 0x6e, 0x5c, + 0xc0, 0x5c, 0x8d, 0x5c, 0xe4, 0x1d, 0x43, 0x5d, + 0xe6, 0x1d, 0x6e, 0x5d, 0x6b, 0x5d, 0x7c, 0x5d, + 0xe1, 0x5d, 0xe2, 0x5d, 0x2f, 0x38, 0xfd, 0x5d, + 0x28, 0x5e, 0x3d, 0x5e, 0x69, 0x5e, 0x62, 0x38, + 0x83, 0x21, 0x7c, 0x38, 0xb0, 0x5e, 0xb3, 0x5e, + 0xb6, 0x5e, 0xca, 0x5e, 0x92, 0xa3, 0xfe, 0x5e, + 0x31, 0x23, 0x31, 0x23, 0x01, 0x82, 0x22, 0x5f, + 0x22, 0x5f, 0xc7, 0x38, 0xb8, 0x32, 0xda, 0x61, + 0x62, 0x5f, 0x6b, 0x5f, 0xe3, 0x38, 0x9a, 0x5f, + 0xcd, 0x5f, 0xd7, 0x5f, 0xf9, 0x5f, 0x81, 0x60, + 0x3a, 0x39, 0x1c, 0x39, 0x94, 0x60, 0xd4, 0x26, + 0xc7, 0x60, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x08, 0x00, 0x0a, 0x00, 0x00, + 0x02, 0x08, 0x00, 0x80, 0x08, 0x00, 0x00, 0x08, + 0x80, 0x28, 0x80, 0x02, 0x00, 0x00, 0x02, 0x48, + 0x61, 0x00, 0x04, 0x06, 0x04, 0x32, 0x46, 0x6a, + 0x5c, 0x67, 0x96, 0xaa, 0xae, 0xc8, 0xd3, 0x5d, + 0x62, 0x00, 0x54, 0x77, 0xf3, 0x0c, 0x2b, 0x3d, + 0x63, 0xfc, 0x62, 0x68, 0x63, 0x83, 0x63, 0xe4, + 0x63, 0xf1, 0x2b, 0x22, 0x64, 0xc5, 0x63, 0xa9, + 0x63, 0x2e, 0x3a, 0x69, 0x64, 0x7e, 0x64, 0x9d, + 0x64, 0x77, 0x64, 0x6c, 0x3a, 0x4f, 0x65, 0x6c, + 0x65, 0x0a, 0x30, 0xe3, 0x65, 0xf8, 0x66, 0x49, + 0x66, 0x19, 0x3b, 0x91, 0x66, 0x08, 0x3b, 0xe4, + 0x3a, 0x92, 0x51, 0x95, 0x51, 0x00, 0x67, 0x9c, + 0x66, 0xad, 0x80, 0xd9, 0x43, 0x17, 0x67, 0x1b, + 0x67, 0x21, 0x67, 0x5e, 0x67, 0x53, 0x67, 0xc3, + 0x33, 0x49, 0x3b, 0xfa, 0x67, 0x85, 0x67, 0x52, + 0x68, 0x85, 0x68, 0x6d, 0x34, 0x8e, 0x68, 0x1f, + 0x68, 0x14, 0x69, 0x9d, 0x3b, 0x42, 0x69, 0xa3, + 0x69, 0xea, 0x69, 0xa8, 0x6a, 0xa3, 0x36, 0xdb, + 0x6a, 0x18, 0x3c, 0x21, 0x6b, 0xa7, 0x38, 0x54, + 0x6b, 0x4e, 0x3c, 0x72, 0x6b, 0x9f, 0x6b, 0xba, + 0x6b, 0xbb, 0x6b, 0x8d, 0x3a, 0x0b, 0x1d, 0xfa, + 0x3a, 0x4e, 0x6c, 0xbc, 0x3c, 0xbf, 0x6c, 0xcd, + 0x6c, 0x67, 0x6c, 0x16, 0x6d, 0x3e, 0x6d, 0x77, + 0x6d, 0x41, 0x6d, 0x69, 0x6d, 0x78, 0x6d, 0x85, + 0x6d, 0x1e, 0x3d, 0x34, 0x6d, 0x2f, 0x6e, 0x6e, + 0x6e, 0x33, 0x3d, 0xcb, 0x6e, 0xc7, 0x6e, 0xd1, + 0x3e, 0xf9, 0x6d, 0x6e, 0x6f, 0x5e, 0x3f, 0x8e, + 0x3f, 0xc6, 0x6f, 0x39, 0x70, 0x1e, 0x70, 0x1b, + 0x70, 0x96, 0x3d, 0x4a, 0x70, 0x7d, 0x70, 0x77, + 0x70, 0xad, 0x70, 0x25, 0x05, 0x45, 0x71, 0x63, + 0x42, 0x9c, 0x71, 0xab, 0x43, 0x28, 0x72, 0x35, + 0x72, 0x50, 0x72, 0x08, 0x46, 0x80, 0x72, 0x95, + 0x72, 0x35, 0x47, 0x02, 0x20, 0x00, 0x00, 0x20, + 0x00, 0x00, 0x00, 0x00, 0x08, 0x80, 0x00, 0x00, + 0x02, 0x02, 0x80, 0x8a, 0x00, 0x00, 0x20, 0x00, + 0x08, 0x0a, 0x00, 0x80, 0x88, 0x80, 0x20, 0x14, + 0x48, 0x7a, 0x73, 0x8b, 0x73, 0xac, 0x3e, 0xa5, + 0x73, 0xb8, 0x3e, 0xb8, 0x3e, 0x47, 0x74, 0x5c, + 0x74, 0x71, 0x74, 0x85, 0x74, 0xca, 0x74, 0x1b, + 0x3f, 0x24, 0x75, 0x36, 0x4c, 0x3e, 0x75, 0x92, + 0x4c, 0x70, 0x75, 0x9f, 0x21, 0x10, 0x76, 0xa1, + 0x4f, 0xb8, 0x4f, 0x44, 0x50, 0xfc, 0x3f, 0x08, + 0x40, 0xf4, 0x76, 0xf3, 0x50, 0xf2, 0x50, 0x19, + 0x51, 0x33, 0x51, 0x1e, 0x77, 0x1f, 0x77, 0x1f, + 0x77, 0x4a, 0x77, 0x39, 0x40, 0x8b, 0x77, 0x46, + 0x40, 0x96, 0x40, 0x1d, 0x54, 0x4e, 0x78, 0x8c, + 0x78, 0xcc, 0x78, 0xe3, 0x40, 0x26, 0x56, 0x56, + 0x79, 0x9a, 0x56, 0xc5, 0x56, 0x8f, 0x79, 0xeb, + 0x79, 0x2f, 0x41, 0x40, 0x7a, 0x4a, 0x7a, 0x4f, + 0x7a, 0x7c, 0x59, 0xa7, 0x5a, 0xa7, 0x5a, 0xee, + 0x7a, 0x02, 0x42, 0xab, 0x5b, 0xc6, 0x7b, 0xc9, + 0x7b, 0x27, 0x42, 0x80, 0x5c, 0xd2, 0x7c, 0xa0, + 0x42, 0xe8, 0x7c, 0xe3, 0x7c, 0x00, 0x7d, 0x86, + 0x5f, 0x63, 0x7d, 0x01, 0x43, 0xc7, 0x7d, 0x02, + 0x7e, 0x45, 0x7e, 0x34, 0x43, 0x28, 0x62, 0x47, + 0x62, 0x59, 0x43, 0xd9, 0x62, 0x7a, 0x7f, 0x3e, + 0x63, 0x95, 0x7f, 0xfa, 0x7f, 0x05, 0x80, 0xda, + 0x64, 0x23, 0x65, 0x60, 0x80, 0xa8, 0x65, 0x70, + 0x80, 0x5f, 0x33, 0xd5, 0x43, 0xb2, 0x80, 0x03, + 0x81, 0x0b, 0x44, 0x3e, 0x81, 0xb5, 0x5a, 0xa7, + 0x67, 0xb5, 0x67, 0x93, 0x33, 0x9c, 0x33, 0x01, + 0x82, 0x04, 0x82, 0x9e, 0x8f, 0x6b, 0x44, 0x91, + 0x82, 0x8b, 0x82, 0x9d, 0x82, 0xb3, 0x52, 0xb1, + 0x82, 0xb3, 0x82, 0xbd, 0x82, 0xe6, 0x82, 0x3c, + 0x6b, 0xe5, 0x82, 0x1d, 0x83, 0x63, 0x83, 0xad, + 0x83, 0x23, 0x83, 0xbd, 0x83, 0xe7, 0x83, 0x57, + 0x84, 0x53, 0x83, 0xca, 0x83, 0xcc, 0x83, 0xdc, + 0x83, 0x36, 0x6c, 0x6b, 0x6d, 0x02, 0x00, 0x00, + 0x20, 0x22, 0x2a, 0xa0, 0x0a, 0x00, 0x20, 0x80, + 0x28, 0x00, 0xa8, 0x20, 0x20, 0x00, 0x02, 0x80, + 0x22, 0x02, 0x8a, 0x08, 0x00, 0xaa, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x28, 0xd5, 0x6c, 0x2b, + 0x45, 0xf1, 0x84, 0xf3, 0x84, 0x16, 0x85, 0xca, + 0x73, 0x64, 0x85, 0x2c, 0x6f, 0x5d, 0x45, 0x61, + 0x45, 0xb1, 0x6f, 0xd2, 0x70, 0x6b, 0x45, 0x50, + 0x86, 0x5c, 0x86, 0x67, 0x86, 0x69, 0x86, 0xa9, + 0x86, 0x88, 0x86, 0x0e, 0x87, 0xe2, 0x86, 0x79, + 0x87, 0x28, 0x87, 0x6b, 0x87, 0x86, 0x87, 0xd7, + 0x45, 0xe1, 0x87, 0x01, 0x88, 0xf9, 0x45, 0x60, + 0x88, 0x63, 0x88, 0x67, 0x76, 0xd7, 0x88, 0xde, + 0x88, 0x35, 0x46, 0xfa, 0x88, 0xbb, 0x34, 0xae, + 0x78, 0x66, 0x79, 0xbe, 0x46, 0xc7, 0x46, 0xa0, + 0x8a, 0xed, 0x8a, 0x8a, 0x8b, 0x55, 0x8c, 0xa8, + 0x7c, 0xab, 0x8c, 0xc1, 0x8c, 0x1b, 0x8d, 0x77, + 0x8d, 0x2f, 0x7f, 0x04, 0x08, 0xcb, 0x8d, 0xbc, + 0x8d, 0xf0, 0x8d, 0xde, 0x08, 0xd4, 0x8e, 0x38, + 0x8f, 0xd2, 0x85, 0xed, 0x85, 0x94, 0x90, 0xf1, + 0x90, 0x11, 0x91, 0x2e, 0x87, 0x1b, 0x91, 0x38, + 0x92, 0xd7, 0x92, 0xd8, 0x92, 0x7c, 0x92, 0xf9, + 0x93, 0x15, 0x94, 0xfa, 0x8b, 0x8b, 0x95, 0x95, + 0x49, 0xb7, 0x95, 0x77, 0x8d, 0xe6, 0x49, 0xc3, + 0x96, 0xb2, 0x5d, 0x23, 0x97, 0x45, 0x91, 0x1a, + 0x92, 0x6e, 0x4a, 0x76, 0x4a, 0xe0, 0x97, 0x0a, + 0x94, 0xb2, 0x4a, 0x96, 0x94, 0x0b, 0x98, 0x0b, + 0x98, 0x29, 0x98, 0xb6, 0x95, 0xe2, 0x98, 0x33, + 0x4b, 0x29, 0x99, 0xa7, 0x99, 0xc2, 0x99, 0xfe, + 0x99, 0xce, 0x4b, 0x30, 0x9b, 0x12, 0x9b, 0x40, + 0x9c, 0xfd, 0x9c, 0xce, 0x4c, 0xed, 0x4c, 0x67, + 0x9d, 0xce, 0xa0, 0xf8, 0x4c, 0x05, 0xa1, 0x0e, + 0xa2, 0x91, 0xa2, 0xbb, 0x9e, 0x56, 0x4d, 0xf9, + 0x9e, 0xfe, 0x9e, 0x05, 0x9f, 0x0f, 0x9f, 0x16, + 0x9f, 0x3b, 0x9f, 0x00, 0xa6, 0x02, 0x88, 0xa0, + 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x28, 0x00, + 0x08, 0xa0, 0x80, 0xa0, 0x80, 0x00, 0x80, 0x80, + 0x00, 0x0a, 0x88, 0x80, 0x00, 0x80, 0x00, 0x20, + 0x2a, 0x00, 0x80, +}; + +static const uint16_t unicode_comp_table[965] = { + 0x4a01, 0x49c0, 0x4a02, 0x0280, 0x0281, 0x0282, 0x0283, 0x02c0, + 0x02c2, 0x0a00, 0x0284, 0x2442, 0x0285, 0x07c0, 0x0980, 0x0982, + 0x2440, 0x2280, 0x02c4, 0x2282, 0x2284, 0x2286, 0x02c6, 0x02c8, + 0x02ca, 0x02cc, 0x0287, 0x228a, 0x02ce, 0x228c, 0x2290, 0x2292, + 0x228e, 0x0288, 0x0289, 0x028a, 0x2482, 0x0300, 0x0302, 0x0304, + 0x028b, 0x2480, 0x0308, 0x0984, 0x0986, 0x2458, 0x0a02, 0x0306, + 0x2298, 0x229a, 0x229e, 0x0900, 0x030a, 0x22a0, 0x030c, 0x030e, + 0x0840, 0x0310, 0x0312, 0x22a2, 0x22a6, 0x09c0, 0x22a4, 0x22a8, + 0x22aa, 0x028c, 0x028d, 0x028e, 0x0340, 0x0342, 0x0344, 0x0380, + 0x028f, 0x248e, 0x07c2, 0x0988, 0x098a, 0x2490, 0x0346, 0x22ac, + 0x0400, 0x22b0, 0x0842, 0x22b2, 0x0402, 0x22b4, 0x0440, 0x0444, + 0x22b6, 0x0442, 0x22c2, 0x22c0, 0x22c4, 0x22c6, 0x22c8, 0x0940, + 0x04c0, 0x0291, 0x22ca, 0x04c4, 0x22cc, 0x04c2, 0x22d0, 0x22ce, + 0x0292, 0x0293, 0x0294, 0x0295, 0x0540, 0x0542, 0x0a08, 0x0296, + 0x2494, 0x0544, 0x07c4, 0x098c, 0x098e, 0x06c0, 0x2492, 0x0844, + 0x2308, 0x230a, 0x0580, 0x230c, 0x0584, 0x0990, 0x0992, 0x230e, + 0x0582, 0x2312, 0x0586, 0x0588, 0x2314, 0x058c, 0x2316, 0x0998, + 0x058a, 0x231e, 0x0590, 0x2320, 0x099a, 0x058e, 0x2324, 0x2322, + 0x0299, 0x029a, 0x029b, 0x05c0, 0x05c2, 0x05c4, 0x029c, 0x24ac, + 0x05c6, 0x05c8, 0x07c6, 0x0994, 0x0996, 0x0700, 0x24aa, 0x2326, + 0x05ca, 0x232a, 0x2328, 0x2340, 0x2342, 0x2344, 0x2346, 0x05cc, + 0x234a, 0x2348, 0x234c, 0x234e, 0x2350, 0x24b8, 0x029d, 0x05ce, + 0x24be, 0x0a0c, 0x2352, 0x0600, 0x24bc, 0x24ba, 0x0640, 0x2354, + 0x0642, 0x0644, 0x2356, 0x2358, 0x02a0, 0x02a1, 0x02a2, 0x02a3, + 0x02c1, 0x02c3, 0x0a01, 0x02a4, 0x2443, 0x02a5, 0x07c1, 0x0981, + 0x0983, 0x2441, 0x2281, 0x02c5, 0x2283, 0x2285, 0x2287, 0x02c7, + 0x02c9, 0x02cb, 0x02cd, 0x02a7, 0x228b, 0x02cf, 0x228d, 0x2291, + 0x2293, 0x228f, 0x02a8, 0x02a9, 0x02aa, 0x2483, 0x0301, 0x0303, + 0x0305, 0x02ab, 0x2481, 0x0309, 0x0985, 0x0987, 0x2459, 0x0a03, + 0x0307, 0x2299, 0x229b, 0x229f, 0x0901, 0x030b, 0x22a1, 0x030d, + 0x030f, 0x0841, 0x0311, 0x0313, 0x22a3, 0x22a7, 0x09c1, 0x22a5, + 0x22a9, 0x22ab, 0x2380, 0x02ac, 0x02ad, 0x02ae, 0x0341, 0x0343, + 0x0345, 0x02af, 0x248f, 0x07c3, 0x0989, 0x098b, 0x2491, 0x0347, + 0x22ad, 0x0401, 0x0884, 0x22b1, 0x0843, 0x22b3, 0x0403, 0x22b5, + 0x0441, 0x0445, 0x22b7, 0x0443, 0x22c3, 0x22c1, 0x22c5, 0x22c7, + 0x22c9, 0x0941, 0x04c1, 0x02b1, 0x22cb, 0x04c5, 0x22cd, 0x04c3, + 0x22d1, 0x22cf, 0x02b2, 0x02b3, 0x02b4, 0x02b5, 0x0541, 0x0543, + 0x0a09, 0x02b6, 0x2495, 0x0545, 0x07c5, 0x098d, 0x098f, 0x06c1, + 0x2493, 0x0845, 0x2309, 0x230b, 0x0581, 0x230d, 0x0585, 0x0991, + 0x0993, 0x230f, 0x0583, 0x2313, 0x0587, 0x0589, 0x2315, 0x058d, + 0x2317, 0x0999, 0x058b, 0x231f, 0x2381, 0x0591, 0x2321, 0x099b, + 0x058f, 0x2325, 0x2323, 0x02b9, 0x02ba, 0x02bb, 0x05c1, 0x05c3, + 0x05c5, 0x02bc, 0x24ad, 0x05c7, 0x05c9, 0x07c7, 0x0995, 0x0997, + 0x0701, 0x24ab, 0x2327, 0x05cb, 0x232b, 0x2329, 0x2341, 0x2343, + 0x2345, 0x2347, 0x05cd, 0x234b, 0x2349, 0x2382, 0x234d, 0x234f, + 0x2351, 0x24b9, 0x02bd, 0x05cf, 0x24bf, 0x0a0d, 0x2353, 0x02bf, + 0x24bd, 0x2383, 0x24bb, 0x0641, 0x2355, 0x0643, 0x0645, 0x2357, + 0x2359, 0x3101, 0x0c80, 0x2e00, 0x2446, 0x2444, 0x244a, 0x2448, + 0x0800, 0x0942, 0x0944, 0x0804, 0x2288, 0x2486, 0x2484, 0x248a, + 0x2488, 0x22ae, 0x2498, 0x2496, 0x249c, 0x249a, 0x2300, 0x0a06, + 0x2302, 0x0a04, 0x0946, 0x07ce, 0x07ca, 0x07c8, 0x07cc, 0x2447, + 0x2445, 0x244b, 0x2449, 0x0801, 0x0943, 0x0945, 0x0805, 0x2289, + 0x2487, 0x2485, 0x248b, 0x2489, 0x22af, 0x2499, 0x2497, 0x249d, + 0x249b, 0x2301, 0x0a07, 0x2303, 0x0a05, 0x0947, 0x07cf, 0x07cb, + 0x07c9, 0x07cd, 0x2450, 0x244e, 0x2454, 0x2452, 0x2451, 0x244f, + 0x2455, 0x2453, 0x2294, 0x2296, 0x2295, 0x2297, 0x2304, 0x2306, + 0x2305, 0x2307, 0x2318, 0x2319, 0x231a, 0x231b, 0x232c, 0x232d, + 0x232e, 0x232f, 0x2400, 0x24a2, 0x24a0, 0x24a6, 0x24a4, 0x24a8, + 0x24a3, 0x24a1, 0x24a7, 0x24a5, 0x24a9, 0x24b0, 0x24ae, 0x24b4, + 0x24b2, 0x24b6, 0x24b1, 0x24af, 0x24b5, 0x24b3, 0x24b7, 0x0882, + 0x0880, 0x0881, 0x0802, 0x0803, 0x229c, 0x229d, 0x0a0a, 0x0a0b, + 0x0883, 0x0b40, 0x2c8a, 0x0c81, 0x2c89, 0x2c88, 0x2540, 0x2541, + 0x2d00, 0x2e07, 0x0d00, 0x2640, 0x2641, 0x2e80, 0x0d01, 0x26c8, + 0x26c9, 0x2f00, 0x2f84, 0x0d02, 0x2f83, 0x2f82, 0x0d40, 0x26d8, + 0x26d9, 0x3186, 0x0d04, 0x2740, 0x2741, 0x3100, 0x3086, 0x0d06, + 0x3085, 0x3084, 0x0d41, 0x2840, 0x3200, 0x0d07, 0x284f, 0x2850, + 0x3280, 0x2c84, 0x2e03, 0x2857, 0x0d42, 0x2c81, 0x2c80, 0x24c0, + 0x24c1, 0x2c86, 0x2c83, 0x28c0, 0x0d43, 0x25c0, 0x25c1, 0x2940, + 0x0d44, 0x26c0, 0x26c1, 0x2e05, 0x2e02, 0x29c0, 0x0d45, 0x2f05, + 0x2f04, 0x0d80, 0x26d0, 0x26d1, 0x2f80, 0x2a40, 0x0d82, 0x26e0, + 0x26e1, 0x3080, 0x3081, 0x2ac0, 0x0d83, 0x3004, 0x3003, 0x0d81, + 0x27c0, 0x27c1, 0x3082, 0x2b40, 0x0d84, 0x2847, 0x2848, 0x3184, + 0x3181, 0x2f06, 0x0d08, 0x2f81, 0x3005, 0x0d46, 0x3083, 0x3182, + 0x0e00, 0x0e01, 0x0f40, 0x1180, 0x1182, 0x0f03, 0x0f00, 0x11c0, + 0x0f01, 0x1140, 0x1202, 0x1204, 0x0f81, 0x1240, 0x0fc0, 0x1242, + 0x0f80, 0x1244, 0x1284, 0x0f82, 0x1286, 0x1288, 0x128a, 0x12c0, + 0x1282, 0x1181, 0x1183, 0x1043, 0x1040, 0x11c1, 0x1041, 0x1141, + 0x1203, 0x1205, 0x10c1, 0x1241, 0x1000, 0x1243, 0x10c0, 0x1245, + 0x1285, 0x10c2, 0x1287, 0x1289, 0x128b, 0x12c1, 0x1283, 0x1080, + 0x1100, 0x1101, 0x1200, 0x1201, 0x1280, 0x1281, 0x1340, 0x1341, + 0x1343, 0x1342, 0x1344, 0x13c2, 0x1400, 0x13c0, 0x1440, 0x1480, + 0x14c0, 0x1540, 0x1541, 0x1740, 0x1700, 0x1741, 0x17c0, 0x1800, + 0x1802, 0x1801, 0x1840, 0x1880, 0x1900, 0x18c0, 0x18c1, 0x1901, + 0x1940, 0x1942, 0x1941, 0x1980, 0x19c0, 0x19c2, 0x19c1, 0x1c80, + 0x1cc0, 0x1dc0, 0x1f80, 0x2000, 0x2002, 0x2004, 0x2006, 0x2008, + 0x2040, 0x2080, 0x2082, 0x20c0, 0x20c1, 0x2100, 0x22b8, 0x22b9, + 0x2310, 0x2311, 0x231c, 0x231d, 0x244c, 0x2456, 0x244d, 0x2457, + 0x248c, 0x248d, 0x249e, 0x249f, 0x2500, 0x2502, 0x2504, 0x2bc0, + 0x2501, 0x2503, 0x2505, 0x2bc1, 0x2bc2, 0x2bc3, 0x2bc4, 0x2bc5, + 0x2bc6, 0x2bc7, 0x2580, 0x2582, 0x2584, 0x2bc8, 0x2581, 0x2583, + 0x2585, 0x2bc9, 0x2bca, 0x2bcb, 0x2bcc, 0x2bcd, 0x2bce, 0x2bcf, + 0x2600, 0x2602, 0x2601, 0x2603, 0x2680, 0x2682, 0x2681, 0x2683, + 0x26c2, 0x26c4, 0x26c6, 0x2c00, 0x26c3, 0x26c5, 0x26c7, 0x2c01, + 0x2c02, 0x2c03, 0x2c04, 0x2c05, 0x2c06, 0x2c07, 0x26ca, 0x26cc, + 0x26ce, 0x2c08, 0x26cb, 0x26cd, 0x26cf, 0x2c09, 0x2c0a, 0x2c0b, + 0x2c0c, 0x2c0d, 0x2c0e, 0x2c0f, 0x26d2, 0x26d4, 0x26d6, 0x26d3, + 0x26d5, 0x26d7, 0x26da, 0x26dc, 0x26de, 0x26db, 0x26dd, 0x26df, + 0x2700, 0x2702, 0x2701, 0x2703, 0x2780, 0x2782, 0x2781, 0x2783, + 0x2800, 0x2802, 0x2804, 0x2801, 0x2803, 0x2805, 0x2842, 0x2844, + 0x2846, 0x2849, 0x284b, 0x284d, 0x2c40, 0x284a, 0x284c, 0x284e, + 0x2c41, 0x2c42, 0x2c43, 0x2c44, 0x2c45, 0x2c46, 0x2c47, 0x2851, + 0x2853, 0x2855, 0x2c48, 0x2852, 0x2854, 0x2856, 0x2c49, 0x2c4a, + 0x2c4b, 0x2c4c, 0x2c4d, 0x2c4e, 0x2c4f, 0x2c82, 0x2e01, 0x3180, + 0x2c87, 0x2f01, 0x2f02, 0x2f03, 0x2e06, 0x3185, 0x3000, 0x3001, + 0x3002, 0x4640, 0x4641, 0x4680, 0x46c0, 0x46c2, 0x46c1, 0x4700, + 0x4740, 0x4780, 0x47c0, 0x47c2, 0x4900, 0x4940, 0x4980, 0x4982, + 0x4a00, 0x49c2, 0x4a03, 0x4a04, 0x4a40, 0x4a41, 0x4a80, 0x4a81, + 0x4ac0, 0x4ac1, 0x4bc0, 0x4bc1, 0x4b00, 0x4b01, 0x4b40, 0x4b41, + 0x4bc2, 0x4bc3, 0x4b80, 0x4b81, 0x4b82, 0x4b83, 0x4c00, 0x4c01, + 0x4c02, 0x4c03, 0x5600, 0x5440, 0x5442, 0x5444, 0x5446, 0x5448, + 0x544a, 0x544c, 0x544e, 0x5450, 0x5452, 0x5454, 0x5456, 0x5480, + 0x5482, 0x5484, 0x54c0, 0x54c1, 0x5500, 0x5501, 0x5540, 0x5541, + 0x5580, 0x5581, 0x55c0, 0x55c1, 0x5680, 0x58c0, 0x5700, 0x5702, + 0x5704, 0x5706, 0x5708, 0x570a, 0x570c, 0x570e, 0x5710, 0x5712, + 0x5714, 0x5716, 0x5740, 0x5742, 0x5744, 0x5780, 0x5781, 0x57c0, + 0x57c1, 0x5800, 0x5801, 0x5840, 0x5841, 0x5880, 0x5881, 0x5900, + 0x5901, 0x5902, 0x5903, 0x5940, 0x8ec0, 0x8f00, 0x8fc0, 0x8fc2, + 0x9000, 0x9040, 0x9041, 0x9080, 0x9081, 0x90c0, 0x90c2, 0x9100, + 0x9140, 0x9182, 0x9180, 0x9183, 0x91c1, 0x91c0, 0x91c3, 0x9200, + 0x9201, 0x9240, 0x9280, 0x9282, 0x9284, 0x9281, 0x9285, 0x9287, + 0x9286, 0x9283, 0x92c1, 0x92c0, 0x92c2, +}; + +typedef enum { + UNICODE_GC_Cn, + UNICODE_GC_Lu, + UNICODE_GC_Ll, + UNICODE_GC_Lt, + UNICODE_GC_Lm, + UNICODE_GC_Lo, + UNICODE_GC_Mn, + UNICODE_GC_Mc, + UNICODE_GC_Me, + UNICODE_GC_Nd, + UNICODE_GC_Nl, + UNICODE_GC_No, + UNICODE_GC_Sm, + UNICODE_GC_Sc, + UNICODE_GC_Sk, + UNICODE_GC_So, + UNICODE_GC_Pc, + UNICODE_GC_Pd, + UNICODE_GC_Ps, + UNICODE_GC_Pe, + UNICODE_GC_Pi, + UNICODE_GC_Pf, + UNICODE_GC_Po, + UNICODE_GC_Zs, + UNICODE_GC_Zl, + UNICODE_GC_Zp, + UNICODE_GC_Cc, + UNICODE_GC_Cf, + UNICODE_GC_Cs, + UNICODE_GC_Co, + UNICODE_GC_LC, + UNICODE_GC_L, + UNICODE_GC_M, + UNICODE_GC_N, + UNICODE_GC_S, + UNICODE_GC_P, + UNICODE_GC_Z, + UNICODE_GC_C, + UNICODE_GC_COUNT, +} UnicodeGCEnum; + +static const char unicode_gc_name_table[] = + "Cn,Unassigned" "\0" + "Lu,Uppercase_Letter" "\0" + "Ll,Lowercase_Letter" "\0" + "Lt,Titlecase_Letter" "\0" + "Lm,Modifier_Letter" "\0" + "Lo,Other_Letter" "\0" + "Mn,Nonspacing_Mark" "\0" + "Mc,Spacing_Mark" "\0" + "Me,Enclosing_Mark" "\0" + "Nd,Decimal_Number,digit" "\0" + "Nl,Letter_Number" "\0" + "No,Other_Number" "\0" + "Sm,Math_Symbol" "\0" + "Sc,Currency_Symbol" "\0" + "Sk,Modifier_Symbol" "\0" + "So,Other_Symbol" "\0" + "Pc,Connector_Punctuation" "\0" + "Pd,Dash_Punctuation" "\0" + "Ps,Open_Punctuation" "\0" + "Pe,Close_Punctuation" "\0" + "Pi,Initial_Punctuation" "\0" + "Pf,Final_Punctuation" "\0" + "Po,Other_Punctuation" "\0" + "Zs,Space_Separator" "\0" + "Zl,Line_Separator" "\0" + "Zp,Paragraph_Separator" "\0" + "Cc,Control,cntrl" "\0" + "Cf,Format" "\0" + "Cs,Surrogate" "\0" + "Co,Private_Use" "\0" + "LC,Cased_Letter" "\0" + "L,Letter" "\0" + "M,Mark,Combining_Mark" "\0" + "N,Number" "\0" + "S,Symbol" "\0" + "P,Punctuation,punct" "\0" + "Z,Separator" "\0" + "C,Other" "\0" +; + +static const uint8_t unicode_gc_table[4070] = { + 0xfa, 0x18, 0x17, 0x56, 0x0d, 0x56, 0x12, 0x13, + 0x16, 0x0c, 0x16, 0x11, 0x36, 0xe9, 0x02, 0x36, + 0x4c, 0x36, 0xe1, 0x12, 0x12, 0x16, 0x13, 0x0e, + 0x10, 0x0e, 0xe2, 0x12, 0x12, 0x0c, 0x13, 0x0c, + 0xfa, 0x19, 0x17, 0x16, 0x6d, 0x0f, 0x16, 0x0e, + 0x0f, 0x05, 0x14, 0x0c, 0x1b, 0x0f, 0x0e, 0x0f, + 0x0c, 0x2b, 0x0e, 0x02, 0x36, 0x0e, 0x0b, 0x05, + 0x15, 0x4b, 0x16, 0xe1, 0x0f, 0x0c, 0xc1, 0xe2, + 0x10, 0x0c, 0xe2, 0x00, 0xff, 0x30, 0x02, 0xff, + 0x08, 0x02, 0xff, 0x27, 0xbf, 0x22, 0x21, 0x02, + 0x5f, 0x5f, 0x21, 0x22, 0x61, 0x02, 0x21, 0x02, + 0x41, 0x42, 0x21, 0x02, 0x21, 0x02, 0x9f, 0x7f, + 0x02, 0x5f, 0x5f, 0x21, 0x02, 0x5f, 0x3f, 0x02, + 0x05, 0x3f, 0x22, 0x65, 0x01, 0x03, 0x02, 0x01, + 0x03, 0x02, 0x01, 0x03, 0x02, 0xff, 0x08, 0x02, + 0xff, 0x0a, 0x02, 0x01, 0x03, 0x02, 0x5f, 0x21, + 0x02, 0xff, 0x32, 0xa2, 0x21, 0x02, 0x21, 0x22, + 0x5f, 0x41, 0x02, 0xff, 0x00, 0xe2, 0x3c, 0x05, + 0xe2, 0x13, 0xe4, 0x0a, 0x6e, 0xe4, 0x04, 0xee, + 0x06, 0x84, 0xce, 0x04, 0x0e, 0x04, 0xee, 0x09, + 0xe6, 0x68, 0x7f, 0x04, 0x0e, 0x3f, 0x20, 0x04, + 0x42, 0x16, 0x01, 0x60, 0x2e, 0x01, 0x16, 0x41, + 0x00, 0x01, 0x00, 0x21, 0x02, 0xe1, 0x09, 0x00, + 0xe1, 0x01, 0xe2, 0x1b, 0x3f, 0x02, 0x41, 0x42, + 0xff, 0x10, 0x62, 0x3f, 0x0c, 0x5f, 0x3f, 0x02, + 0xe1, 0x2b, 0xe2, 0x28, 0xff, 0x1a, 0x0f, 0x86, + 0x28, 0xff, 0x2f, 0xff, 0x06, 0x02, 0xff, 0x58, + 0x00, 0xe1, 0x1e, 0x20, 0x04, 0xb6, 0xe2, 0x21, + 0x16, 0x11, 0x20, 0x2f, 0x0d, 0x00, 0xe6, 0x25, + 0x11, 0x06, 0x16, 0x26, 0x16, 0x26, 0x16, 0x06, + 0xe0, 0x00, 0xe5, 0x13, 0x60, 0x65, 0x36, 0xe0, + 0x03, 0xbb, 0x4c, 0x36, 0x0d, 0x36, 0x2f, 0xe6, + 0x03, 0x16, 0x1b, 0x56, 0xe5, 0x18, 0x04, 0xe5, + 0x02, 0xe6, 0x0d, 0xe9, 0x02, 0x76, 0x25, 0x06, + 0xe5, 0x5b, 0x16, 0x05, 0xc6, 0x1b, 0x0f, 0xa6, + 0x24, 0x26, 0x0f, 0x66, 0x25, 0xe9, 0x02, 0x45, + 0x2f, 0x05, 0xf6, 0x06, 0x00, 0x1b, 0x05, 0x06, + 0xe5, 0x16, 0xe6, 0x13, 0x20, 0xe5, 0x51, 0xe6, + 0x03, 0x05, 0xe0, 0x06, 0xe9, 0x02, 0xe5, 0x19, + 0xe6, 0x01, 0x24, 0x0f, 0x56, 0x04, 0x20, 0x06, + 0x2d, 0xe5, 0x0e, 0x66, 0x04, 0xe6, 0x01, 0x04, + 0x46, 0x04, 0x86, 0x20, 0xf6, 0x07, 0x00, 0xe5, + 0x11, 0x46, 0x20, 0x16, 0x00, 0xe5, 0x03, 0x80, + 0xe5, 0x10, 0x0e, 0xa5, 0x00, 0x3b, 0x80, 0xe6, + 0x01, 0xe5, 0x21, 0x04, 0xe6, 0x10, 0x1b, 0xe6, + 0x18, 0x07, 0xe5, 0x2e, 0x06, 0x07, 0x06, 0x05, + 0x47, 0xe6, 0x00, 0x67, 0x06, 0x27, 0x05, 0xc6, + 0xe5, 0x02, 0x26, 0x36, 0xe9, 0x02, 0x16, 0x04, + 0xe5, 0x07, 0x06, 0x27, 0x00, 0xe5, 0x00, 0x20, + 0x25, 0x20, 0xe5, 0x0e, 0x00, 0xc5, 0x00, 0x05, + 0x40, 0x65, 0x20, 0x06, 0x05, 0x47, 0x66, 0x20, + 0x27, 0x20, 0x27, 0x06, 0x05, 0xe0, 0x00, 0x07, + 0x60, 0x25, 0x00, 0x45, 0x26, 0x20, 0xe9, 0x02, + 0x25, 0x2d, 0xab, 0x0f, 0x0d, 0x05, 0x16, 0x06, + 0x20, 0x26, 0x07, 0x00, 0xa5, 0x60, 0x25, 0x20, + 0xe5, 0x0e, 0x00, 0xc5, 0x00, 0x25, 0x00, 0x25, + 0x00, 0x25, 0x20, 0x06, 0x00, 0x47, 0x26, 0x60, + 0x26, 0x20, 0x46, 0x40, 0x06, 0xc0, 0x65, 0x00, + 0x05, 0xc0, 0xe9, 0x02, 0x26, 0x45, 0x06, 0x16, + 0xe0, 0x02, 0x26, 0x07, 0x00, 0xe5, 0x01, 0x00, + 0x45, 0x00, 0xe5, 0x0e, 0x00, 0xc5, 0x00, 0x25, + 0x00, 0x85, 0x20, 0x06, 0x05, 0x47, 0x86, 0x00, + 0x26, 0x07, 0x00, 0x27, 0x06, 0x20, 0x05, 0xe0, + 0x07, 0x25, 0x26, 0x20, 0xe9, 0x02, 0x16, 0x0d, + 0xc0, 0x05, 0xa6, 0x00, 0x06, 0x27, 0x00, 0xe5, + 0x00, 0x20, 0x25, 0x20, 0xe5, 0x0e, 0x00, 0xc5, + 0x00, 0x25, 0x00, 0x85, 0x20, 0x06, 0x05, 0x07, + 0x06, 0x07, 0x66, 0x20, 0x27, 0x20, 0x27, 0x06, + 0xc0, 0x26, 0x07, 0x60, 0x25, 0x00, 0x45, 0x26, + 0x20, 0xe9, 0x02, 0x0f, 0x05, 0xab, 0xe0, 0x02, + 0x06, 0x05, 0x00, 0xa5, 0x40, 0x45, 0x00, 0x65, + 0x40, 0x25, 0x00, 0x05, 0x00, 0x25, 0x40, 0x25, + 0x40, 0x45, 0x40, 0xe5, 0x04, 0x60, 0x27, 0x06, + 0x27, 0x40, 0x47, 0x00, 0x47, 0x06, 0x20, 0x05, + 0xa0, 0x07, 0xe0, 0x06, 0xe9, 0x02, 0x4b, 0xaf, + 0x0d, 0x0f, 0x80, 0x06, 0x47, 0x06, 0xe5, 0x00, + 0x00, 0x45, 0x00, 0xe5, 0x0f, 0x00, 0xe5, 0x08, + 0x20, 0x06, 0x05, 0x46, 0x67, 0x00, 0x46, 0x00, + 0x66, 0xc0, 0x26, 0x00, 0x45, 0x20, 0x05, 0x20, + 0x25, 0x26, 0x20, 0xe9, 0x02, 0xc0, 0x16, 0xcb, + 0x0f, 0x05, 0x06, 0x27, 0x16, 0xe5, 0x00, 0x00, + 0x45, 0x00, 0xe5, 0x0f, 0x00, 0xe5, 0x02, 0x00, + 0x85, 0x20, 0x06, 0x05, 0x07, 0x06, 0x87, 0x00, + 0x06, 0x27, 0x00, 0x27, 0x26, 0xc0, 0x27, 0xa0, + 0x25, 0x00, 0x25, 0x26, 0x20, 0xe9, 0x02, 0x00, + 0x25, 0x07, 0xe0, 0x04, 0x26, 0x27, 0xe5, 0x01, + 0x00, 0x45, 0x00, 0xe5, 0x21, 0x26, 0x05, 0x47, + 0x66, 0x00, 0x47, 0x00, 0x47, 0x06, 0x05, 0x0f, + 0x60, 0x45, 0x07, 0xcb, 0x45, 0x26, 0x20, 0xe9, + 0x02, 0xeb, 0x01, 0x0f, 0xa5, 0x00, 0x06, 0x27, + 0x00, 0xe5, 0x0a, 0x40, 0xe5, 0x10, 0x00, 0xe5, + 0x01, 0x00, 0x05, 0x20, 0xc5, 0x40, 0x06, 0x60, + 0x47, 0x46, 0x00, 0x06, 0x00, 0xe7, 0x00, 0xa0, + 0xe9, 0x02, 0x20, 0x27, 0x16, 0xe0, 0x04, 0xe5, + 0x28, 0x06, 0x25, 0xc6, 0x60, 0x0d, 0xa5, 0x04, + 0xe6, 0x00, 0x16, 0xe9, 0x02, 0x36, 0xe0, 0x1d, + 0x25, 0x00, 0x05, 0x00, 0x85, 0x00, 0xe5, 0x10, + 0x00, 0x05, 0x00, 0xe5, 0x02, 0x06, 0x25, 0xe6, + 0x01, 0x05, 0x20, 0x85, 0x00, 0x04, 0x00, 0xc6, + 0x00, 0xe9, 0x02, 0x20, 0x65, 0xe0, 0x18, 0x05, + 0x4f, 0xf6, 0x07, 0x0f, 0x16, 0x4f, 0x26, 0xaf, + 0xe9, 0x02, 0xeb, 0x02, 0x0f, 0x06, 0x0f, 0x06, + 0x0f, 0x06, 0x12, 0x13, 0x12, 0x13, 0x27, 0xe5, + 0x00, 0x00, 0xe5, 0x1c, 0x60, 0xe6, 0x06, 0x07, + 0x86, 0x16, 0x26, 0x85, 0xe6, 0x03, 0x00, 0xe6, + 0x1c, 0x00, 0xef, 0x00, 0x06, 0xaf, 0x00, 0x2f, + 0x96, 0x6f, 0x36, 0xe0, 0x1d, 0xe5, 0x23, 0x27, + 0x66, 0x07, 0xa6, 0x07, 0x26, 0x27, 0x26, 0x05, + 0xe9, 0x02, 0xb6, 0xa5, 0x27, 0x26, 0x65, 0x46, + 0x05, 0x47, 0x25, 0xc7, 0x45, 0x66, 0xe5, 0x05, + 0x06, 0x27, 0x26, 0xa7, 0x06, 0x05, 0x07, 0xe9, + 0x02, 0x47, 0x06, 0x2f, 0xe1, 0x1e, 0x00, 0x01, + 0x80, 0x01, 0x20, 0xe2, 0x23, 0x16, 0x04, 0x42, + 0xe5, 0x80, 0xc1, 0x00, 0x65, 0x20, 0xc5, 0x00, + 0x05, 0x00, 0x65, 0x20, 0xe5, 0x21, 0x00, 0x65, + 0x20, 0xe5, 0x19, 0x00, 0x65, 0x20, 0xc5, 0x00, + 0x05, 0x00, 0x65, 0x20, 0xe5, 0x07, 0x00, 0xe5, + 0x31, 0x00, 0x65, 0x20, 0xe5, 0x3b, 0x20, 0x46, + 0xf6, 0x01, 0xeb, 0x0c, 0x40, 0xe5, 0x08, 0xef, + 0x02, 0xa0, 0xe1, 0x4e, 0x20, 0xa2, 0x20, 0x11, + 0xe5, 0x81, 0xe4, 0x0f, 0x16, 0xe5, 0x09, 0x17, + 0xe5, 0x12, 0x12, 0x13, 0x40, 0xe5, 0x43, 0x56, + 0x4a, 0xe5, 0x00, 0xc0, 0xe5, 0x0a, 0x46, 0x07, + 0xe0, 0x01, 0xe5, 0x0b, 0x26, 0x07, 0x36, 0xe0, + 0x01, 0xe5, 0x0a, 0x26, 0xe0, 0x04, 0xe5, 0x05, + 0x00, 0x45, 0x00, 0x26, 0xe0, 0x04, 0xe5, 0x2c, + 0x26, 0x07, 0xc6, 0xe7, 0x00, 0x06, 0x27, 0xe6, + 0x03, 0x56, 0x04, 0x56, 0x0d, 0x05, 0x06, 0x20, + 0xe9, 0x02, 0xa0, 0xeb, 0x02, 0xa0, 0xb6, 0x11, + 0x76, 0x46, 0x1b, 0x06, 0xe9, 0x02, 0xa0, 0xe5, + 0x1b, 0x04, 0xe5, 0x2d, 0xc0, 0x85, 0x26, 0xe5, + 0x1a, 0x06, 0x05, 0x80, 0xe5, 0x3e, 0xe0, 0x02, + 0xe5, 0x17, 0x00, 0x46, 0x67, 0x26, 0x47, 0x60, + 0x27, 0x06, 0xa7, 0x46, 0x60, 0x0f, 0x40, 0x36, + 0xe9, 0x02, 0xe5, 0x16, 0x20, 0x85, 0xe0, 0x03, + 0xe5, 0x24, 0x60, 0xe5, 0x12, 0xa0, 0xe9, 0x02, + 0x0b, 0x40, 0xef, 0x1a, 0xe5, 0x0f, 0x26, 0x27, + 0x06, 0x20, 0x36, 0xe5, 0x2d, 0x07, 0x06, 0x07, + 0xc6, 0x00, 0x06, 0x07, 0x06, 0x27, 0xe6, 0x00, + 0xa7, 0xe6, 0x02, 0x20, 0x06, 0xe9, 0x02, 0xa0, + 0xe9, 0x02, 0xa0, 0xd6, 0x04, 0xb6, 0x20, 0xe6, + 0x06, 0x08, 0xe6, 0x08, 0xe0, 0x29, 0x66, 0x07, + 0xe5, 0x27, 0x06, 0x07, 0x86, 0x07, 0x06, 0x87, + 0x06, 0x27, 0xe5, 0x00, 0x00, 0x36, 0xe9, 0x02, + 0xd6, 0xef, 0x02, 0xe6, 0x01, 0xef, 0x01, 0x56, + 0x26, 0x07, 0xe5, 0x16, 0x07, 0x66, 0x27, 0x26, + 0x07, 0x46, 0x25, 0xe9, 0x02, 0xe5, 0x24, 0x06, + 0x07, 0x26, 0x47, 0x06, 0x07, 0x46, 0x27, 0xe0, + 0x00, 0x76, 0xe5, 0x1c, 0xe7, 0x00, 0xe6, 0x00, + 0x27, 0x26, 0x40, 0x96, 0xe9, 0x02, 0x40, 0x45, + 0xe9, 0x02, 0xe5, 0x16, 0xa4, 0x36, 0xe2, 0x01, + 0x3f, 0x80, 0xe1, 0x23, 0x20, 0x41, 0xf6, 0x00, + 0xe0, 0x00, 0x46, 0x16, 0xe6, 0x05, 0x07, 0xc6, + 0x65, 0x06, 0xa5, 0x06, 0x25, 0x07, 0x26, 0x05, + 0x80, 0xe2, 0x24, 0xe4, 0x37, 0xe2, 0x05, 0x04, + 0xe2, 0x1a, 0xe4, 0x1d, 0xe6, 0x38, 0xff, 0x80, + 0x0e, 0xe2, 0x00, 0xff, 0x5a, 0xe2, 0x00, 0xe1, + 0x00, 0xa2, 0x20, 0xa1, 0x20, 0xe2, 0x00, 0xe1, + 0x00, 0xe2, 0x00, 0xe1, 0x00, 0xa2, 0x20, 0xa1, + 0x20, 0xe2, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, + 0x01, 0x00, 0x3f, 0xc2, 0xe1, 0x00, 0xe2, 0x06, + 0x20, 0xe2, 0x00, 0xe3, 0x00, 0xe2, 0x00, 0xe3, + 0x00, 0xe2, 0x00, 0xe3, 0x00, 0x82, 0x00, 0x22, + 0x61, 0x03, 0x0e, 0x02, 0x4e, 0x42, 0x00, 0x22, + 0x61, 0x03, 0x4e, 0x62, 0x20, 0x22, 0x61, 0x00, + 0x4e, 0xe2, 0x00, 0x81, 0x4e, 0x20, 0x42, 0x00, + 0x22, 0x61, 0x03, 0x2e, 0x00, 0xf7, 0x03, 0x9b, + 0xb1, 0x36, 0x14, 0x15, 0x12, 0x34, 0x15, 0x12, + 0x14, 0xf6, 0x00, 0x18, 0x19, 0x9b, 0x17, 0xf6, + 0x01, 0x14, 0x15, 0x76, 0x30, 0x56, 0x0c, 0x12, + 0x13, 0xf6, 0x03, 0x0c, 0x16, 0x10, 0xf6, 0x02, + 0x17, 0x9b, 0x00, 0xfb, 0x02, 0x0b, 0x04, 0x20, + 0xab, 0x4c, 0x12, 0x13, 0x04, 0xeb, 0x02, 0x4c, + 0x12, 0x13, 0x00, 0xe4, 0x05, 0x40, 0xed, 0x19, + 0xe0, 0x07, 0xe6, 0x05, 0x68, 0x06, 0x48, 0xe6, + 0x04, 0xe0, 0x07, 0x2f, 0x01, 0x6f, 0x01, 0x2f, + 0x02, 0x41, 0x22, 0x41, 0x02, 0x0f, 0x01, 0x2f, + 0x0c, 0x81, 0xaf, 0x01, 0x0f, 0x01, 0x0f, 0x01, + 0x0f, 0x61, 0x0f, 0x02, 0x61, 0x02, 0x65, 0x02, + 0x2f, 0x22, 0x21, 0x8c, 0x3f, 0x42, 0x0f, 0x0c, + 0x2f, 0x02, 0x0f, 0xeb, 0x08, 0xea, 0x1b, 0x3f, + 0x6a, 0x0b, 0x2f, 0x60, 0x8c, 0x8f, 0x2c, 0x6f, + 0x0c, 0x2f, 0x0c, 0x2f, 0x0c, 0xcf, 0x0c, 0xef, + 0x17, 0x2c, 0x2f, 0x0c, 0x0f, 0x0c, 0xef, 0x17, + 0xec, 0x80, 0x84, 0xef, 0x00, 0x12, 0x13, 0x12, + 0x13, 0xef, 0x0c, 0x2c, 0xcf, 0x12, 0x13, 0xef, + 0x49, 0x0c, 0xef, 0x16, 0xec, 0x11, 0xef, 0x20, + 0xac, 0xef, 0x40, 0xe0, 0x0e, 0xef, 0x03, 0xe0, + 0x0d, 0xeb, 0x34, 0xef, 0x46, 0xeb, 0x0e, 0xef, + 0x80, 0x2f, 0x0c, 0xef, 0x01, 0x0c, 0xef, 0x2e, + 0xec, 0x00, 0xef, 0x67, 0x0c, 0xef, 0x80, 0x70, + 0x12, 0x13, 0x12, 0x13, 0x12, 0x13, 0x12, 0x13, + 0x12, 0x13, 0x12, 0x13, 0x12, 0x13, 0xeb, 0x16, + 0xef, 0x24, 0x8c, 0x12, 0x13, 0xec, 0x17, 0x12, + 0x13, 0x12, 0x13, 0x12, 0x13, 0x12, 0x13, 0x12, + 0x13, 0xec, 0x08, 0xef, 0x80, 0x78, 0xec, 0x7b, + 0x12, 0x13, 0x12, 0x13, 0x12, 0x13, 0x12, 0x13, + 0x12, 0x13, 0x12, 0x13, 0x12, 0x13, 0x12, 0x13, + 0x12, 0x13, 0x12, 0x13, 0x12, 0x13, 0xec, 0x37, + 0x12, 0x13, 0x12, 0x13, 0xec, 0x18, 0x12, 0x13, + 0xec, 0x80, 0x7a, 0xef, 0x28, 0xec, 0x0d, 0x2f, + 0xac, 0xef, 0x1f, 0x20, 0xef, 0x18, 0x00, 0xef, + 0x61, 0xe1, 0x28, 0xe2, 0x28, 0x5f, 0x21, 0x22, + 0xdf, 0x41, 0x02, 0x3f, 0x02, 0x3f, 0x82, 0x24, + 0x41, 0x02, 0xff, 0x5a, 0x02, 0xaf, 0x7f, 0x46, + 0x3f, 0x80, 0x76, 0x0b, 0x36, 0xe2, 0x1e, 0x00, + 0x02, 0x80, 0x02, 0x20, 0xe5, 0x30, 0xc0, 0x04, + 0x16, 0xe0, 0x06, 0x06, 0xe5, 0x0f, 0xe0, 0x01, + 0xc5, 0x00, 0xc5, 0x00, 0xc5, 0x00, 0xc5, 0x00, + 0xc5, 0x00, 0xc5, 0x00, 0xc5, 0x00, 0xc5, 0x00, + 0xe6, 0x18, 0x36, 0x14, 0x15, 0x14, 0x15, 0x56, + 0x14, 0x15, 0x16, 0x14, 0x15, 0xf6, 0x01, 0x11, + 0x36, 0x11, 0x16, 0x14, 0x15, 0x36, 0x14, 0x15, + 0x12, 0x13, 0x12, 0x13, 0x12, 0x13, 0x12, 0x13, + 0x96, 0x04, 0xf6, 0x02, 0x31, 0x76, 0x11, 0x16, + 0x12, 0xf6, 0x05, 0x2f, 0x56, 0x12, 0x13, 0x12, + 0x13, 0x12, 0x13, 0x12, 0x13, 0x11, 0xe0, 0x1a, + 0xef, 0x12, 0x00, 0xef, 0x51, 0xe0, 0x04, 0xef, + 0x80, 0x4e, 0xe0, 0x12, 0xef, 0x08, 0x17, 0x56, + 0x0f, 0x04, 0x05, 0x0a, 0x12, 0x13, 0x12, 0x13, + 0x12, 0x13, 0x12, 0x13, 0x12, 0x13, 0x2f, 0x12, + 0x13, 0x12, 0x13, 0x12, 0x13, 0x12, 0x13, 0x11, + 0x12, 0x33, 0x0f, 0xea, 0x01, 0x66, 0x27, 0x11, + 0x84, 0x2f, 0x4a, 0x04, 0x05, 0x16, 0x2f, 0x00, + 0xe5, 0x4e, 0x20, 0x26, 0x2e, 0x24, 0x05, 0x11, + 0xe5, 0x52, 0x16, 0x44, 0x05, 0x80, 0xe5, 0x23, + 0x00, 0xe5, 0x56, 0x00, 0x2f, 0x6b, 0xef, 0x02, + 0xe5, 0x18, 0xef, 0x1e, 0xe0, 0x01, 0x0f, 0xe5, + 0x08, 0xef, 0x17, 0x00, 0xeb, 0x02, 0xef, 0x16, + 0xeb, 0x00, 0x0f, 0xeb, 0x07, 0xef, 0x18, 0xeb, + 0x02, 0xef, 0x1f, 0xeb, 0x07, 0xef, 0x80, 0xb8, + 0xe5, 0x99, 0x38, 0xef, 0x38, 0xe5, 0xc0, 0x11, + 0x8d, 0x04, 0xe5, 0x83, 0xef, 0x40, 0xef, 0x2f, + 0xe0, 0x01, 0xe5, 0x20, 0xa4, 0x36, 0xe5, 0x80, + 0x84, 0x04, 0x56, 0xe5, 0x08, 0xe9, 0x02, 0x25, + 0xe0, 0x0c, 0xff, 0x26, 0x05, 0x06, 0x48, 0x16, + 0xe6, 0x02, 0x16, 0x04, 0xff, 0x14, 0x24, 0x26, + 0xe5, 0x3e, 0xea, 0x02, 0x26, 0xb6, 0xe0, 0x00, + 0xee, 0x0f, 0xe4, 0x01, 0x2e, 0xff, 0x06, 0x22, + 0xff, 0x36, 0x04, 0xe2, 0x00, 0x9f, 0xff, 0x02, + 0x04, 0x2e, 0x7f, 0x05, 0x7f, 0x22, 0xff, 0x0d, + 0x61, 0x02, 0x81, 0x02, 0xff, 0x07, 0x41, 0x02, + 0x5f, 0x3f, 0x20, 0x3f, 0x00, 0x02, 0x00, 0x02, + 0xdf, 0xe0, 0x0d, 0x44, 0x3f, 0x05, 0x24, 0x02, + 0xc5, 0x06, 0x45, 0x06, 0x65, 0x06, 0xe5, 0x0f, + 0x27, 0x26, 0x07, 0x6f, 0x06, 0x40, 0xab, 0x2f, + 0x0d, 0x0f, 0xa0, 0xe5, 0x2c, 0x76, 0xe0, 0x00, + 0x27, 0xe5, 0x2a, 0xe7, 0x08, 0x26, 0xe0, 0x00, + 0x36, 0xe9, 0x02, 0xa0, 0xe6, 0x0a, 0xa5, 0x56, + 0x05, 0x16, 0x25, 0x06, 0xe9, 0x02, 0xe5, 0x14, + 0xe6, 0x00, 0x36, 0xe5, 0x0f, 0xe6, 0x03, 0x27, + 0xe0, 0x03, 0x16, 0xe5, 0x15, 0x40, 0x46, 0x07, + 0xe5, 0x27, 0x06, 0x27, 0x66, 0x27, 0x26, 0x47, + 0xf6, 0x05, 0x00, 0x04, 0xe9, 0x02, 0x60, 0x36, + 0x85, 0x06, 0x04, 0xe5, 0x01, 0xe9, 0x02, 0x85, + 0x00, 0xe5, 0x21, 0xa6, 0x27, 0x26, 0x27, 0x26, + 0xe0, 0x01, 0x45, 0x06, 0xe5, 0x00, 0x06, 0x07, + 0x20, 0xe9, 0x02, 0x20, 0x76, 0xe5, 0x08, 0x04, + 0xa5, 0x4f, 0x05, 0x07, 0x06, 0x07, 0xe5, 0x2a, + 0x06, 0x05, 0x46, 0x25, 0x26, 0x85, 0x26, 0x05, + 0x06, 0x05, 0xe0, 0x10, 0x25, 0x04, 0x36, 0xe5, + 0x03, 0x07, 0x26, 0x27, 0x36, 0x05, 0x24, 0x07, + 0x06, 0xe0, 0x02, 0xa5, 0x20, 0xa5, 0x20, 0xa5, + 0xe0, 0x01, 0xc5, 0x00, 0xc5, 0x00, 0xe2, 0x23, + 0x0e, 0x64, 0xe2, 0x01, 0x04, 0x2e, 0x60, 0xe2, + 0x48, 0xe5, 0x1b, 0x27, 0x06, 0x27, 0x06, 0x27, + 0x16, 0x07, 0x06, 0x20, 0xe9, 0x02, 0xa0, 0xe5, + 0xab, 0x1c, 0xe0, 0x04, 0xe5, 0x0f, 0x60, 0xe5, + 0x29, 0x60, 0xfc, 0x87, 0x78, 0xfd, 0x98, 0x78, + 0xe5, 0x80, 0xe6, 0x20, 0xe5, 0x62, 0xe0, 0x1e, + 0xc2, 0xe0, 0x04, 0x82, 0x80, 0x05, 0x06, 0xe5, + 0x02, 0x0c, 0xe5, 0x05, 0x00, 0x85, 0x00, 0x05, + 0x00, 0x25, 0x00, 0x25, 0x00, 0xe5, 0x64, 0xee, + 0x09, 0xe0, 0x08, 0xe5, 0x80, 0xe3, 0x13, 0x12, + 0xef, 0x08, 0xe5, 0x38, 0x20, 0xe5, 0x2e, 0xc0, + 0x0f, 0xe0, 0x18, 0xe5, 0x04, 0x0d, 0x4f, 0xe6, + 0x08, 0xd6, 0x12, 0x13, 0x16, 0xa0, 0xe6, 0x08, + 0x16, 0x31, 0x30, 0x12, 0x13, 0x12, 0x13, 0x12, + 0x13, 0x12, 0x13, 0x12, 0x13, 0x12, 0x13, 0x12, + 0x13, 0x12, 0x13, 0x36, 0x12, 0x13, 0x76, 0x50, + 0x56, 0x00, 0x76, 0x11, 0x12, 0x13, 0x12, 0x13, + 0x12, 0x13, 0x56, 0x0c, 0x11, 0x4c, 0x00, 0x16, + 0x0d, 0x36, 0x60, 0x85, 0x00, 0xe5, 0x7f, 0x20, + 0x1b, 0x00, 0x56, 0x0d, 0x56, 0x12, 0x13, 0x16, + 0x0c, 0x16, 0x11, 0x36, 0xe9, 0x02, 0x36, 0x4c, + 0x36, 0xe1, 0x12, 0x12, 0x16, 0x13, 0x0e, 0x10, + 0x0e, 0xe2, 0x12, 0x12, 0x0c, 0x13, 0x0c, 0x12, + 0x13, 0x16, 0x12, 0x13, 0x36, 0xe5, 0x02, 0x04, + 0xe5, 0x25, 0x24, 0xe5, 0x17, 0x40, 0xa5, 0x20, + 0xa5, 0x20, 0xa5, 0x20, 0x45, 0x40, 0x2d, 0x0c, + 0x0e, 0x0f, 0x2d, 0x00, 0x0f, 0x6c, 0x2f, 0xe0, + 0x02, 0x5b, 0x2f, 0x20, 0xe5, 0x04, 0x00, 0xe5, + 0x12, 0x00, 0xe5, 0x0b, 0x00, 0x25, 0x00, 0xe5, + 0x07, 0x20, 0xe5, 0x06, 0xe0, 0x1a, 0xe5, 0x73, + 0x80, 0x56, 0x60, 0xeb, 0x25, 0x40, 0xef, 0x01, + 0xea, 0x2d, 0x6b, 0xef, 0x09, 0x2b, 0x4f, 0x00, + 0xef, 0x05, 0x40, 0x0f, 0xe0, 0x27, 0xef, 0x25, + 0x06, 0xe0, 0x7a, 0xe5, 0x15, 0x40, 0xe5, 0x29, + 0xe0, 0x07, 0x06, 0xeb, 0x13, 0x60, 0xe5, 0x18, + 0x6b, 0xe0, 0x01, 0xe5, 0x0c, 0x0a, 0xe5, 0x00, + 0x0a, 0x80, 0xe5, 0x1e, 0x86, 0x80, 0xe5, 0x16, + 0x00, 0x16, 0xe5, 0x1c, 0x60, 0xe5, 0x00, 0x16, + 0x8a, 0xe0, 0x22, 0xe1, 0x20, 0xe2, 0x20, 0xe5, + 0x46, 0x20, 0xe9, 0x02, 0xa0, 0xe1, 0x1c, 0x60, + 0xe2, 0x1c, 0x60, 0xe5, 0x20, 0xe0, 0x00, 0xe5, + 0x2c, 0xe0, 0x03, 0x16, 0xe1, 0x03, 0x00, 0xe1, + 0x07, 0x00, 0xc1, 0x00, 0x21, 0x00, 0xe2, 0x03, + 0x00, 0xe2, 0x07, 0x00, 0xc2, 0x00, 0x22, 0x40, + 0xe5, 0x2c, 0xe0, 0x04, 0xe5, 0x80, 0xaf, 0xe0, + 0x01, 0xe5, 0x0e, 0xe0, 0x02, 0xe5, 0x00, 0xe0, + 0x10, 0xa4, 0x00, 0xe4, 0x22, 0x00, 0xe4, 0x01, + 0xe0, 0x3d, 0xa5, 0x20, 0x05, 0x00, 0xe5, 0x24, + 0x00, 0x25, 0x40, 0x05, 0x20, 0xe5, 0x0f, 0x00, + 0x16, 0xeb, 0x00, 0xe5, 0x0f, 0x2f, 0xcb, 0xe5, + 0x17, 0xe0, 0x00, 0xeb, 0x01, 0xe0, 0x28, 0xe5, + 0x0b, 0x00, 0x25, 0x80, 0x8b, 0xe5, 0x0e, 0xab, + 0x40, 0x16, 0xe5, 0x12, 0x80, 0x16, 0xe0, 0x38, + 0xe5, 0x30, 0x60, 0x2b, 0x25, 0xeb, 0x08, 0x20, + 0xeb, 0x26, 0x05, 0x46, 0x00, 0x26, 0x80, 0x66, + 0x65, 0x00, 0x45, 0x00, 0xe5, 0x15, 0x20, 0x46, + 0x60, 0x06, 0xeb, 0x01, 0xc0, 0xf6, 0x01, 0xc0, + 0xe5, 0x15, 0x2b, 0x16, 0xe5, 0x15, 0x4b, 0xe0, + 0x18, 0xe5, 0x00, 0x0f, 0xe5, 0x14, 0x26, 0x60, + 0x8b, 0xd6, 0xe0, 0x01, 0xe5, 0x2e, 0x40, 0xd6, + 0xe5, 0x0e, 0x20, 0xeb, 0x00, 0xe5, 0x0b, 0x80, + 0xeb, 0x00, 0xe5, 0x0a, 0xc0, 0x76, 0xe0, 0x04, + 0xcb, 0xe0, 0x48, 0xe5, 0x41, 0xe0, 0x2f, 0xe1, + 0x2b, 0xe0, 0x05, 0xe2, 0x2b, 0xc0, 0xab, 0xe5, + 0x1c, 0x66, 0xe0, 0x00, 0xe9, 0x02, 0xa0, 0xe9, + 0x02, 0x65, 0x04, 0x05, 0xe1, 0x0e, 0x40, 0x86, + 0x11, 0x04, 0xe2, 0x0e, 0xe0, 0x00, 0x2c, 0xe0, + 0x80, 0x48, 0xeb, 0x17, 0x00, 0xe5, 0x22, 0x00, + 0x26, 0x11, 0x20, 0x25, 0xe0, 0x08, 0x45, 0xe0, + 0x2f, 0x66, 0xe5, 0x15, 0xeb, 0x02, 0x05, 0xe0, + 0x00, 0xe5, 0x0e, 0xe6, 0x03, 0x6b, 0x96, 0xe0, + 0x0e, 0xe5, 0x0a, 0x66, 0x76, 0xe0, 0x1e, 0xe5, + 0x0d, 0xcb, 0xe0, 0x0c, 0xe5, 0x0f, 0xe0, 0x01, + 0x07, 0x06, 0x07, 0xe5, 0x2d, 0xe6, 0x07, 0xd6, + 0x60, 0xeb, 0x0c, 0xe9, 0x02, 0x06, 0x25, 0x26, + 0x05, 0xe0, 0x01, 0x46, 0x07, 0xe5, 0x25, 0x47, + 0x66, 0x27, 0x26, 0x36, 0x1b, 0x76, 0x06, 0xe0, + 0x02, 0x1b, 0x20, 0xe5, 0x11, 0xc0, 0xe9, 0x02, + 0xa0, 0x46, 0xe5, 0x1c, 0x86, 0x07, 0xe6, 0x00, + 0x00, 0xe9, 0x02, 0x76, 0x05, 0x27, 0x05, 0xe0, + 0x00, 0xe5, 0x1b, 0x06, 0x36, 0x05, 0xe0, 0x01, + 0x26, 0x07, 0xe5, 0x28, 0x47, 0xe6, 0x01, 0x27, + 0x65, 0x76, 0x66, 0x16, 0x07, 0x06, 0xe9, 0x02, + 0x05, 0x16, 0x05, 0x56, 0x00, 0xeb, 0x0c, 0xe0, + 0x03, 0xe5, 0x0a, 0x00, 0xe5, 0x11, 0x47, 0x46, + 0x27, 0x06, 0x07, 0x26, 0xb6, 0x06, 0x25, 0x06, + 0xe0, 0x36, 0xc5, 0x00, 0x05, 0x00, 0x65, 0x00, + 0xe5, 0x07, 0x00, 0xe5, 0x02, 0x16, 0xa0, 0xe5, + 0x27, 0x06, 0x47, 0xe6, 0x00, 0x80, 0xe9, 0x02, + 0xa0, 0x26, 0x27, 0x00, 0xe5, 0x00, 0x20, 0x25, + 0x20, 0xe5, 0x0e, 0x00, 0xc5, 0x00, 0x25, 0x00, + 0x85, 0x00, 0x26, 0x05, 0x27, 0x06, 0x67, 0x20, + 0x27, 0x20, 0x47, 0x20, 0x05, 0xa0, 0x07, 0x80, + 0x85, 0x27, 0x20, 0xc6, 0x40, 0x86, 0xe0, 0x03, + 0xe5, 0x02, 0x00, 0x05, 0x20, 0x05, 0x00, 0xe5, + 0x1e, 0x00, 0x05, 0x47, 0xa6, 0x00, 0x07, 0x20, + 0x07, 0x00, 0x67, 0x00, 0x27, 0x06, 0x07, 0x06, + 0x05, 0x06, 0x05, 0x36, 0x00, 0x36, 0xe0, 0x00, + 0x26, 0xe0, 0x15, 0xe5, 0x2d, 0x47, 0xe6, 0x00, + 0x27, 0x46, 0x07, 0x06, 0x65, 0x96, 0xe9, 0x02, + 0x36, 0x00, 0x16, 0x06, 0x45, 0xe0, 0x16, 0xe5, + 0x28, 0x47, 0xa6, 0x07, 0x06, 0x67, 0x26, 0x07, + 0x26, 0x25, 0x16, 0x05, 0xe0, 0x00, 0xe9, 0x02, + 0xe0, 0x80, 0x1e, 0xe5, 0x27, 0x47, 0x66, 0x20, + 0x67, 0x26, 0x07, 0x26, 0xf6, 0x0f, 0x65, 0x26, + 0xe0, 0x1a, 0xe5, 0x28, 0x47, 0xe6, 0x00, 0x27, + 0x06, 0x07, 0x26, 0x56, 0x05, 0xe0, 0x03, 0xe9, + 0x02, 0xa0, 0xf6, 0x05, 0xe0, 0x0b, 0xe5, 0x23, + 0x06, 0x07, 0x06, 0x27, 0xa6, 0x07, 0x06, 0x05, + 0x16, 0xa0, 0xe9, 0x02, 0xa0, 0xe9, 0x0c, 0xe0, + 0x14, 0xe5, 0x13, 0x20, 0x06, 0x07, 0x06, 0x27, + 0x66, 0x07, 0x86, 0x60, 0xe9, 0x02, 0x2b, 0x56, + 0x0f, 0xc5, 0xe0, 0x80, 0x31, 0xe5, 0x24, 0x47, + 0xe6, 0x01, 0x07, 0x26, 0x16, 0xe0, 0x5c, 0xe1, + 0x18, 0xe2, 0x18, 0xe9, 0x02, 0xeb, 0x01, 0xe0, + 0x04, 0xe5, 0x00, 0x20, 0x05, 0x20, 0xe5, 0x00, + 0x00, 0x25, 0x00, 0xe5, 0x10, 0xa7, 0x00, 0x27, + 0x20, 0x26, 0x07, 0x06, 0x05, 0x07, 0x05, 0x07, + 0x06, 0x56, 0xe0, 0x01, 0xe9, 0x02, 0xe0, 0x3e, + 0xe5, 0x00, 0x20, 0xe5, 0x1f, 0x47, 0x66, 0x20, + 0x26, 0x67, 0x06, 0x05, 0x16, 0x05, 0x07, 0xe0, + 0x13, 0x05, 0xe6, 0x02, 0xe5, 0x20, 0xa6, 0x07, + 0x05, 0x66, 0xf6, 0x00, 0x06, 0xe0, 0x00, 0x05, + 0xa6, 0x27, 0x46, 0xe5, 0x26, 0xe6, 0x05, 0x07, + 0x26, 0x56, 0x05, 0x96, 0xe0, 0x05, 0xe5, 0x41, + 0xc0, 0xf6, 0x02, 0xe0, 0x80, 0x2e, 0xe5, 0x19, + 0x16, 0xe0, 0x06, 0xe9, 0x02, 0xa0, 0xe5, 0x01, + 0x00, 0xe5, 0x1d, 0x07, 0xc6, 0x00, 0xa6, 0x07, + 0x06, 0x05, 0x96, 0xe0, 0x02, 0xe9, 0x02, 0xeb, + 0x0b, 0x40, 0x36, 0xe5, 0x16, 0x20, 0xe6, 0x0e, + 0x00, 0x07, 0xc6, 0x07, 0x26, 0x07, 0x26, 0xe0, + 0x41, 0xc5, 0x00, 0x25, 0x00, 0xe5, 0x1e, 0xa6, + 0x40, 0x06, 0x00, 0x26, 0x00, 0xc6, 0x05, 0x06, + 0xe0, 0x00, 0xe9, 0x02, 0xa0, 0xa5, 0x00, 0x25, + 0x00, 0xe5, 0x18, 0x87, 0x00, 0x26, 0x00, 0x27, + 0x06, 0x07, 0x06, 0x05, 0xc0, 0xe9, 0x02, 0xe0, + 0x80, 0xae, 0xe5, 0x0b, 0x26, 0x27, 0x36, 0xc0, + 0x26, 0x05, 0x07, 0xe5, 0x05, 0x00, 0xe5, 0x1a, + 0x27, 0x86, 0x40, 0x27, 0x06, 0x07, 0x06, 0xf6, + 0x05, 0xe9, 0x02, 0x06, 0xe0, 0x4d, 0x05, 0xe0, + 0x07, 0xeb, 0x0d, 0xef, 0x00, 0x6d, 0xef, 0x09, + 0xe0, 0x05, 0x16, 0xe5, 0x83, 0x12, 0xe0, 0x5e, + 0xea, 0x67, 0x00, 0x96, 0xe0, 0x03, 0xe5, 0x80, + 0x3c, 0xe0, 0x89, 0xc4, 0xe5, 0x59, 0x36, 0xe0, + 0x05, 0xe5, 0x83, 0xa8, 0xfb, 0x08, 0x06, 0xa5, + 0xe6, 0x07, 0xe0, 0x02, 0xe5, 0x8f, 0x13, 0x80, + 0xe5, 0x81, 0xbf, 0xe0, 0x9a, 0x31, 0xe5, 0x16, + 0xe6, 0x04, 0x47, 0x46, 0xe9, 0x02, 0xe0, 0x86, + 0x3e, 0xe5, 0x81, 0xb1, 0xc0, 0xe5, 0x17, 0x00, + 0xe9, 0x02, 0x60, 0x36, 0xe5, 0x47, 0x00, 0xe9, + 0x02, 0xa0, 0xe5, 0x16, 0x20, 0x86, 0x16, 0xe0, + 0x02, 0xe5, 0x28, 0xc6, 0x96, 0x6f, 0x64, 0x16, + 0x0f, 0xe0, 0x02, 0xe9, 0x02, 0x00, 0xcb, 0x00, + 0xe5, 0x0d, 0x80, 0xe5, 0x0b, 0xe0, 0x81, 0x28, + 0x44, 0xe5, 0x20, 0x24, 0x56, 0xe9, 0x02, 0xe0, + 0x80, 0x3e, 0xe1, 0x18, 0xe2, 0x18, 0xeb, 0x0f, + 0x76, 0xe0, 0x5d, 0xe5, 0x43, 0x60, 0x06, 0x05, + 0xe7, 0x2f, 0xc0, 0x66, 0xe4, 0x05, 0xe0, 0x38, + 0x24, 0x16, 0x04, 0x06, 0xe0, 0x03, 0x27, 0xe0, + 0x06, 0xe5, 0x97, 0x70, 0xe0, 0x00, 0xe5, 0x84, + 0x4e, 0xe0, 0x21, 0xe5, 0x02, 0xe0, 0xa2, 0x5f, + 0x64, 0x00, 0xc4, 0x00, 0x24, 0x00, 0xe5, 0x80, + 0x9b, 0xe0, 0x07, 0x05, 0xe0, 0x15, 0x45, 0x20, + 0x05, 0xe0, 0x06, 0x65, 0xe0, 0x00, 0xe5, 0x81, + 0x04, 0xe0, 0x88, 0x7c, 0xe5, 0x63, 0x80, 0xe5, + 0x05, 0x40, 0xe5, 0x01, 0xc0, 0xe5, 0x02, 0x20, + 0x0f, 0x26, 0x16, 0x7b, 0xe0, 0x8e, 0xd4, 0xef, + 0x80, 0x68, 0xe9, 0x02, 0xa0, 0xef, 0x81, 0x2c, + 0xe0, 0x44, 0xe6, 0x26, 0x20, 0xe6, 0x0f, 0xe0, + 0x01, 0xef, 0x6c, 0xe0, 0x34, 0xef, 0x80, 0x6e, + 0xe0, 0x02, 0xef, 0x1f, 0x20, 0xef, 0x34, 0x27, + 0x46, 0x4f, 0xa7, 0xfb, 0x00, 0xe6, 0x00, 0x2f, + 0xc6, 0xef, 0x16, 0x66, 0xef, 0x35, 0xe0, 0x0d, + 0xef, 0x3a, 0x46, 0x0f, 0xe0, 0x72, 0xeb, 0x0c, + 0xe0, 0x04, 0xeb, 0x0c, 0xe0, 0x04, 0xef, 0x4f, + 0xe0, 0x01, 0xeb, 0x11, 0xe0, 0x7f, 0xe1, 0x12, + 0xe2, 0x12, 0xe1, 0x12, 0xc2, 0x00, 0xe2, 0x0a, + 0xe1, 0x12, 0xe2, 0x12, 0x01, 0x00, 0x21, 0x20, + 0x01, 0x20, 0x21, 0x20, 0x61, 0x00, 0xe1, 0x00, + 0x62, 0x00, 0x02, 0x00, 0xc2, 0x00, 0xe2, 0x03, + 0xe1, 0x12, 0xe2, 0x12, 0x21, 0x00, 0x61, 0x20, + 0xe1, 0x00, 0x00, 0xc1, 0x00, 0xe2, 0x12, 0x21, + 0x00, 0x61, 0x00, 0x81, 0x00, 0x01, 0x40, 0xc1, + 0x00, 0xe2, 0x12, 0xe1, 0x12, 0xe2, 0x12, 0xe1, + 0x12, 0xe2, 0x12, 0xe1, 0x12, 0xe2, 0x12, 0xe1, + 0x12, 0xe2, 0x12, 0xe1, 0x12, 0xe2, 0x12, 0xe1, + 0x12, 0xe2, 0x14, 0x20, 0xe1, 0x11, 0x0c, 0xe2, + 0x11, 0x0c, 0xa2, 0xe1, 0x11, 0x0c, 0xe2, 0x11, + 0x0c, 0xa2, 0xe1, 0x11, 0x0c, 0xe2, 0x11, 0x0c, + 0xa2, 0xe1, 0x11, 0x0c, 0xe2, 0x11, 0x0c, 0xa2, + 0xe1, 0x11, 0x0c, 0xe2, 0x11, 0x0c, 0xa2, 0x3f, + 0x20, 0xe9, 0x2a, 0xef, 0x81, 0x78, 0xe6, 0x2f, + 0x6f, 0xe6, 0x2a, 0xef, 0x00, 0x06, 0xef, 0x06, + 0x06, 0x2f, 0x96, 0xe0, 0x07, 0x86, 0x00, 0xe6, + 0x07, 0xe0, 0x83, 0xc8, 0xe2, 0x02, 0x05, 0xe2, + 0x0c, 0xa0, 0xa2, 0xe0, 0x80, 0x4d, 0xc6, 0x00, + 0xe6, 0x09, 0x20, 0xc6, 0x00, 0x26, 0x00, 0x86, + 0x80, 0xe4, 0x36, 0xe0, 0x19, 0x06, 0xe0, 0x68, + 0xe5, 0x25, 0x40, 0xc6, 0xc4, 0x20, 0xe9, 0x02, + 0x60, 0x05, 0x0f, 0xe0, 0x80, 0xb8, 0xe5, 0x16, + 0x06, 0xe0, 0x09, 0xe5, 0x24, 0x66, 0xe9, 0x02, + 0x80, 0x0d, 0xe0, 0x81, 0x48, 0xe5, 0x13, 0x04, + 0x66, 0xe9, 0x02, 0xe0, 0x80, 0x4e, 0xe5, 0x16, + 0x26, 0x05, 0xe9, 0x02, 0x60, 0x16, 0xe0, 0x81, + 0x58, 0xc5, 0x00, 0x65, 0x00, 0x25, 0x00, 0xe5, + 0x07, 0x00, 0xe5, 0x80, 0x3d, 0x20, 0xeb, 0x01, + 0xc6, 0xe0, 0x21, 0xe1, 0x1a, 0xe2, 0x1a, 0xc6, + 0x04, 0x60, 0xe9, 0x02, 0x60, 0x36, 0xe0, 0x82, + 0x89, 0xeb, 0x33, 0x0f, 0x4b, 0x0d, 0x6b, 0xe0, + 0x44, 0xeb, 0x25, 0x0f, 0xeb, 0x07, 0xe0, 0x80, + 0x3a, 0x65, 0x00, 0xe5, 0x13, 0x00, 0x25, 0x00, + 0x05, 0x20, 0x05, 0x00, 0xe5, 0x02, 0x00, 0x65, + 0x00, 0x05, 0x00, 0x05, 0xa0, 0x05, 0x60, 0x05, + 0x00, 0x05, 0x00, 0x05, 0x00, 0x45, 0x00, 0x25, + 0x00, 0x05, 0x20, 0x05, 0x00, 0x05, 0x00, 0x05, + 0x00, 0x05, 0x00, 0x05, 0x00, 0x25, 0x00, 0x05, + 0x20, 0x65, 0x00, 0xc5, 0x00, 0x65, 0x00, 0x65, + 0x00, 0x05, 0x00, 0xe5, 0x02, 0x00, 0xe5, 0x09, + 0x80, 0x45, 0x00, 0x85, 0x00, 0xe5, 0x09, 0xe0, + 0x2c, 0x2c, 0xe0, 0x80, 0x86, 0xef, 0x24, 0x60, + 0xef, 0x5c, 0xe0, 0x04, 0xef, 0x07, 0x20, 0xef, + 0x07, 0x00, 0xef, 0x07, 0x00, 0xef, 0x1d, 0xe0, + 0x02, 0xeb, 0x05, 0xef, 0x80, 0x19, 0xe0, 0x30, + 0xef, 0x15, 0xe0, 0x05, 0xef, 0x24, 0x60, 0xef, + 0x01, 0xc0, 0x2f, 0xe0, 0x06, 0xaf, 0xe0, 0x80, + 0x12, 0xef, 0x80, 0x73, 0x8e, 0xef, 0x82, 0x50, + 0x60, 0xef, 0x09, 0x40, 0xef, 0x05, 0x40, 0xef, + 0x6f, 0x60, 0xef, 0x57, 0xa0, 0xef, 0x04, 0x60, + 0x0f, 0xe0, 0x07, 0xef, 0x04, 0x60, 0xef, 0x30, + 0xe0, 0x00, 0xef, 0x02, 0xa0, 0xef, 0x20, 0xe0, + 0x00, 0xef, 0x16, 0x20, 0xef, 0x04, 0x60, 0x2f, + 0xe0, 0x36, 0xef, 0x80, 0xcc, 0xe0, 0x04, 0xef, + 0x06, 0x20, 0xef, 0x05, 0x40, 0xef, 0x02, 0x80, + 0xef, 0x30, 0xc0, 0xef, 0x07, 0x20, 0xef, 0x03, + 0xa0, 0xef, 0x01, 0xc0, 0xef, 0x80, 0x0b, 0x00, + 0xef, 0x54, 0xe9, 0x02, 0xe0, 0x83, 0x7e, 0xe5, + 0xc0, 0x66, 0x58, 0xe0, 0x18, 0xe5, 0x8f, 0xb2, + 0xa0, 0xe5, 0x80, 0x56, 0x20, 0xe5, 0x95, 0xfa, + 0xe0, 0x06, 0xe5, 0x9c, 0xa9, 0xe0, 0x07, 0xe5, + 0x81, 0xe6, 0xe0, 0x89, 0x1a, 0xe5, 0x81, 0x96, + 0xe0, 0x85, 0x5a, 0xe5, 0x92, 0xc3, 0x80, 0xe5, + 0x8f, 0xd8, 0xe0, 0xca, 0x9b, 0xc9, 0x1b, 0xe0, + 0x16, 0xfb, 0x58, 0xe0, 0x78, 0xe6, 0x80, 0x68, + 0xe0, 0xc0, 0xbd, 0x88, 0xfd, 0xc0, 0xbf, 0x76, + 0x20, 0xfd, 0xc0, 0xbf, 0x76, 0x20, +}; + +typedef enum { + UNICODE_SCRIPT_Unknown, + UNICODE_SCRIPT_Adlam, + UNICODE_SCRIPT_Ahom, + UNICODE_SCRIPT_Anatolian_Hieroglyphs, + UNICODE_SCRIPT_Arabic, + UNICODE_SCRIPT_Armenian, + UNICODE_SCRIPT_Avestan, + UNICODE_SCRIPT_Balinese, + UNICODE_SCRIPT_Bamum, + UNICODE_SCRIPT_Bassa_Vah, + UNICODE_SCRIPT_Batak, + UNICODE_SCRIPT_Bengali, + UNICODE_SCRIPT_Bhaiksuki, + UNICODE_SCRIPT_Bopomofo, + UNICODE_SCRIPT_Brahmi, + UNICODE_SCRIPT_Braille, + UNICODE_SCRIPT_Buginese, + UNICODE_SCRIPT_Buhid, + UNICODE_SCRIPT_Canadian_Aboriginal, + UNICODE_SCRIPT_Carian, + UNICODE_SCRIPT_Caucasian_Albanian, + UNICODE_SCRIPT_Chakma, + UNICODE_SCRIPT_Cham, + UNICODE_SCRIPT_Cherokee, + UNICODE_SCRIPT_Chorasmian, + UNICODE_SCRIPT_Common, + UNICODE_SCRIPT_Coptic, + UNICODE_SCRIPT_Cuneiform, + UNICODE_SCRIPT_Cypriot, + UNICODE_SCRIPT_Cyrillic, + UNICODE_SCRIPT_Cypro_Minoan, + UNICODE_SCRIPT_Deseret, + UNICODE_SCRIPT_Devanagari, + UNICODE_SCRIPT_Dives_Akuru, + UNICODE_SCRIPT_Dogra, + UNICODE_SCRIPT_Duployan, + UNICODE_SCRIPT_Egyptian_Hieroglyphs, + UNICODE_SCRIPT_Elbasan, + UNICODE_SCRIPT_Elymaic, + UNICODE_SCRIPT_Ethiopic, + UNICODE_SCRIPT_Georgian, + UNICODE_SCRIPT_Glagolitic, + UNICODE_SCRIPT_Gothic, + UNICODE_SCRIPT_Garay, + UNICODE_SCRIPT_Grantha, + UNICODE_SCRIPT_Greek, + UNICODE_SCRIPT_Gujarati, + UNICODE_SCRIPT_Gunjala_Gondi, + UNICODE_SCRIPT_Gurmukhi, + UNICODE_SCRIPT_Gurung_Khema, + UNICODE_SCRIPT_Han, + UNICODE_SCRIPT_Hangul, + UNICODE_SCRIPT_Hanifi_Rohingya, + UNICODE_SCRIPT_Hanunoo, + UNICODE_SCRIPT_Hatran, + UNICODE_SCRIPT_Hebrew, + UNICODE_SCRIPT_Hiragana, + UNICODE_SCRIPT_Imperial_Aramaic, + UNICODE_SCRIPT_Inherited, + UNICODE_SCRIPT_Inscriptional_Pahlavi, + UNICODE_SCRIPT_Inscriptional_Parthian, + UNICODE_SCRIPT_Javanese, + UNICODE_SCRIPT_Kaithi, + UNICODE_SCRIPT_Kannada, + UNICODE_SCRIPT_Katakana, + UNICODE_SCRIPT_Kawi, + UNICODE_SCRIPT_Kayah_Li, + UNICODE_SCRIPT_Kharoshthi, + UNICODE_SCRIPT_Khmer, + UNICODE_SCRIPT_Khojki, + UNICODE_SCRIPT_Khitan_Small_Script, + UNICODE_SCRIPT_Khudawadi, + UNICODE_SCRIPT_Kirat_Rai, + UNICODE_SCRIPT_Lao, + UNICODE_SCRIPT_Latin, + UNICODE_SCRIPT_Lepcha, + UNICODE_SCRIPT_Limbu, + UNICODE_SCRIPT_Linear_A, + UNICODE_SCRIPT_Linear_B, + UNICODE_SCRIPT_Lisu, + UNICODE_SCRIPT_Lycian, + UNICODE_SCRIPT_Lydian, + UNICODE_SCRIPT_Makasar, + UNICODE_SCRIPT_Mahajani, + UNICODE_SCRIPT_Malayalam, + UNICODE_SCRIPT_Mandaic, + UNICODE_SCRIPT_Manichaean, + UNICODE_SCRIPT_Marchen, + UNICODE_SCRIPT_Masaram_Gondi, + UNICODE_SCRIPT_Medefaidrin, + UNICODE_SCRIPT_Meetei_Mayek, + UNICODE_SCRIPT_Mende_Kikakui, + UNICODE_SCRIPT_Meroitic_Cursive, + UNICODE_SCRIPT_Meroitic_Hieroglyphs, + UNICODE_SCRIPT_Miao, + UNICODE_SCRIPT_Modi, + UNICODE_SCRIPT_Mongolian, + UNICODE_SCRIPT_Mro, + UNICODE_SCRIPT_Multani, + UNICODE_SCRIPT_Myanmar, + UNICODE_SCRIPT_Nabataean, + UNICODE_SCRIPT_Nag_Mundari, + UNICODE_SCRIPT_Nandinagari, + UNICODE_SCRIPT_New_Tai_Lue, + UNICODE_SCRIPT_Newa, + UNICODE_SCRIPT_Nko, + UNICODE_SCRIPT_Nushu, + UNICODE_SCRIPT_Nyiakeng_Puachue_Hmong, + UNICODE_SCRIPT_Ogham, + UNICODE_SCRIPT_Ol_Chiki, + UNICODE_SCRIPT_Ol_Onal, + UNICODE_SCRIPT_Old_Hungarian, + UNICODE_SCRIPT_Old_Italic, + UNICODE_SCRIPT_Old_North_Arabian, + UNICODE_SCRIPT_Old_Permic, + UNICODE_SCRIPT_Old_Persian, + UNICODE_SCRIPT_Old_Sogdian, + UNICODE_SCRIPT_Old_South_Arabian, + UNICODE_SCRIPT_Old_Turkic, + UNICODE_SCRIPT_Old_Uyghur, + UNICODE_SCRIPT_Oriya, + UNICODE_SCRIPT_Osage, + UNICODE_SCRIPT_Osmanya, + UNICODE_SCRIPT_Pahawh_Hmong, + UNICODE_SCRIPT_Palmyrene, + UNICODE_SCRIPT_Pau_Cin_Hau, + UNICODE_SCRIPT_Phags_Pa, + UNICODE_SCRIPT_Phoenician, + UNICODE_SCRIPT_Psalter_Pahlavi, + UNICODE_SCRIPT_Rejang, + UNICODE_SCRIPT_Runic, + UNICODE_SCRIPT_Samaritan, + UNICODE_SCRIPT_Saurashtra, + UNICODE_SCRIPT_Sharada, + UNICODE_SCRIPT_Shavian, + UNICODE_SCRIPT_Siddham, + UNICODE_SCRIPT_SignWriting, + UNICODE_SCRIPT_Sinhala, + UNICODE_SCRIPT_Sogdian, + UNICODE_SCRIPT_Sora_Sompeng, + UNICODE_SCRIPT_Soyombo, + UNICODE_SCRIPT_Sundanese, + UNICODE_SCRIPT_Sunuwar, + UNICODE_SCRIPT_Syloti_Nagri, + UNICODE_SCRIPT_Syriac, + UNICODE_SCRIPT_Tagalog, + UNICODE_SCRIPT_Tagbanwa, + UNICODE_SCRIPT_Tai_Le, + UNICODE_SCRIPT_Tai_Tham, + UNICODE_SCRIPT_Tai_Viet, + UNICODE_SCRIPT_Takri, + UNICODE_SCRIPT_Tamil, + UNICODE_SCRIPT_Tangut, + UNICODE_SCRIPT_Telugu, + UNICODE_SCRIPT_Thaana, + UNICODE_SCRIPT_Thai, + UNICODE_SCRIPT_Tibetan, + UNICODE_SCRIPT_Tifinagh, + UNICODE_SCRIPT_Tirhuta, + UNICODE_SCRIPT_Tangsa, + UNICODE_SCRIPT_Todhri, + UNICODE_SCRIPT_Toto, + UNICODE_SCRIPT_Tulu_Tigalari, + UNICODE_SCRIPT_Ugaritic, + UNICODE_SCRIPT_Vai, + UNICODE_SCRIPT_Vithkuqi, + UNICODE_SCRIPT_Wancho, + UNICODE_SCRIPT_Warang_Citi, + UNICODE_SCRIPT_Yezidi, + UNICODE_SCRIPT_Yi, + UNICODE_SCRIPT_Zanabazar_Square, + UNICODE_SCRIPT_COUNT, +} UnicodeScriptEnum; + +static const char unicode_script_name_table[] = + "Adlam,Adlm" "\0" + "Ahom,Ahom" "\0" + "Anatolian_Hieroglyphs,Hluw" "\0" + "Arabic,Arab" "\0" + "Armenian,Armn" "\0" + "Avestan,Avst" "\0" + "Balinese,Bali" "\0" + "Bamum,Bamu" "\0" + "Bassa_Vah,Bass" "\0" + "Batak,Batk" "\0" + "Bengali,Beng" "\0" + "Bhaiksuki,Bhks" "\0" + "Bopomofo,Bopo" "\0" + "Brahmi,Brah" "\0" + "Braille,Brai" "\0" + "Buginese,Bugi" "\0" + "Buhid,Buhd" "\0" + "Canadian_Aboriginal,Cans" "\0" + "Carian,Cari" "\0" + "Caucasian_Albanian,Aghb" "\0" + "Chakma,Cakm" "\0" + "Cham,Cham" "\0" + "Cherokee,Cher" "\0" + "Chorasmian,Chrs" "\0" + "Common,Zyyy" "\0" + "Coptic,Copt,Qaac" "\0" + "Cuneiform,Xsux" "\0" + "Cypriot,Cprt" "\0" + "Cyrillic,Cyrl" "\0" + "Cypro_Minoan,Cpmn" "\0" + "Deseret,Dsrt" "\0" + "Devanagari,Deva" "\0" + "Dives_Akuru,Diak" "\0" + "Dogra,Dogr" "\0" + "Duployan,Dupl" "\0" + "Egyptian_Hieroglyphs,Egyp" "\0" + "Elbasan,Elba" "\0" + "Elymaic,Elym" "\0" + "Ethiopic,Ethi" "\0" + "Georgian,Geor" "\0" + "Glagolitic,Glag" "\0" + "Gothic,Goth" "\0" + "Garay,Gara" "\0" + "Grantha,Gran" "\0" + "Greek,Grek" "\0" + "Gujarati,Gujr" "\0" + "Gunjala_Gondi,Gong" "\0" + "Gurmukhi,Guru" "\0" + "Gurung_Khema,Gukh" "\0" + "Han,Hani" "\0" + "Hangul,Hang" "\0" + "Hanifi_Rohingya,Rohg" "\0" + "Hanunoo,Hano" "\0" + "Hatran,Hatr" "\0" + "Hebrew,Hebr" "\0" + "Hiragana,Hira" "\0" + "Imperial_Aramaic,Armi" "\0" + "Inherited,Zinh,Qaai" "\0" + "Inscriptional_Pahlavi,Phli" "\0" + "Inscriptional_Parthian,Prti" "\0" + "Javanese,Java" "\0" + "Kaithi,Kthi" "\0" + "Kannada,Knda" "\0" + "Katakana,Kana" "\0" + "Kawi,Kawi" "\0" + "Kayah_Li,Kali" "\0" + "Kharoshthi,Khar" "\0" + "Khmer,Khmr" "\0" + "Khojki,Khoj" "\0" + "Khitan_Small_Script,Kits" "\0" + "Khudawadi,Sind" "\0" + "Kirat_Rai,Krai" "\0" + "Lao,Laoo" "\0" + "Latin,Latn" "\0" + "Lepcha,Lepc" "\0" + "Limbu,Limb" "\0" + "Linear_A,Lina" "\0" + "Linear_B,Linb" "\0" + "Lisu,Lisu" "\0" + "Lycian,Lyci" "\0" + "Lydian,Lydi" "\0" + "Makasar,Maka" "\0" + "Mahajani,Mahj" "\0" + "Malayalam,Mlym" "\0" + "Mandaic,Mand" "\0" + "Manichaean,Mani" "\0" + "Marchen,Marc" "\0" + "Masaram_Gondi,Gonm" "\0" + "Medefaidrin,Medf" "\0" + "Meetei_Mayek,Mtei" "\0" + "Mende_Kikakui,Mend" "\0" + "Meroitic_Cursive,Merc" "\0" + "Meroitic_Hieroglyphs,Mero" "\0" + "Miao,Plrd" "\0" + "Modi,Modi" "\0" + "Mongolian,Mong" "\0" + "Mro,Mroo" "\0" + "Multani,Mult" "\0" + "Myanmar,Mymr" "\0" + "Nabataean,Nbat" "\0" + "Nag_Mundari,Nagm" "\0" + "Nandinagari,Nand" "\0" + "New_Tai_Lue,Talu" "\0" + "Newa,Newa" "\0" + "Nko,Nkoo" "\0" + "Nushu,Nshu" "\0" + "Nyiakeng_Puachue_Hmong,Hmnp" "\0" + "Ogham,Ogam" "\0" + "Ol_Chiki,Olck" "\0" + "Ol_Onal,Onao" "\0" + "Old_Hungarian,Hung" "\0" + "Old_Italic,Ital" "\0" + "Old_North_Arabian,Narb" "\0" + "Old_Permic,Perm" "\0" + "Old_Persian,Xpeo" "\0" + "Old_Sogdian,Sogo" "\0" + "Old_South_Arabian,Sarb" "\0" + "Old_Turkic,Orkh" "\0" + "Old_Uyghur,Ougr" "\0" + "Oriya,Orya" "\0" + "Osage,Osge" "\0" + "Osmanya,Osma" "\0" + "Pahawh_Hmong,Hmng" "\0" + "Palmyrene,Palm" "\0" + "Pau_Cin_Hau,Pauc" "\0" + "Phags_Pa,Phag" "\0" + "Phoenician,Phnx" "\0" + "Psalter_Pahlavi,Phlp" "\0" + "Rejang,Rjng" "\0" + "Runic,Runr" "\0" + "Samaritan,Samr" "\0" + "Saurashtra,Saur" "\0" + "Sharada,Shrd" "\0" + "Shavian,Shaw" "\0" + "Siddham,Sidd" "\0" + "SignWriting,Sgnw" "\0" + "Sinhala,Sinh" "\0" + "Sogdian,Sogd" "\0" + "Sora_Sompeng,Sora" "\0" + "Soyombo,Soyo" "\0" + "Sundanese,Sund" "\0" + "Sunuwar,Sunu" "\0" + "Syloti_Nagri,Sylo" "\0" + "Syriac,Syrc" "\0" + "Tagalog,Tglg" "\0" + "Tagbanwa,Tagb" "\0" + "Tai_Le,Tale" "\0" + "Tai_Tham,Lana" "\0" + "Tai_Viet,Tavt" "\0" + "Takri,Takr" "\0" + "Tamil,Taml" "\0" + "Tangut,Tang" "\0" + "Telugu,Telu" "\0" + "Thaana,Thaa" "\0" + "Thai,Thai" "\0" + "Tibetan,Tibt" "\0" + "Tifinagh,Tfng" "\0" + "Tirhuta,Tirh" "\0" + "Tangsa,Tnsa" "\0" + "Todhri,Todr" "\0" + "Toto,Toto" "\0" + "Tulu_Tigalari,Tutg" "\0" + "Ugaritic,Ugar" "\0" + "Vai,Vaii" "\0" + "Vithkuqi,Vith" "\0" + "Wancho,Wcho" "\0" + "Warang_Citi,Wara" "\0" + "Yezidi,Yezi" "\0" + "Yi,Yiii" "\0" + "Zanabazar_Square,Zanb" "\0" +; + +static const uint8_t unicode_script_table[2803] = { + 0xc0, 0x19, 0x99, 0x4a, 0x85, 0x19, 0x99, 0x4a, + 0xae, 0x19, 0x80, 0x4a, 0x8e, 0x19, 0x80, 0x4a, + 0x84, 0x19, 0x96, 0x4a, 0x80, 0x19, 0x9e, 0x4a, + 0x80, 0x19, 0xe1, 0x60, 0x4a, 0xa6, 0x19, 0x84, + 0x4a, 0x84, 0x19, 0x81, 0x0d, 0x93, 0x19, 0xe0, + 0x0f, 0x3a, 0x83, 0x2d, 0x80, 0x19, 0x82, 0x2d, + 0x01, 0x83, 0x2d, 0x80, 0x19, 0x80, 0x2d, 0x03, + 0x80, 0x2d, 0x80, 0x19, 0x80, 0x2d, 0x80, 0x19, + 0x82, 0x2d, 0x00, 0x80, 0x2d, 0x00, 0x93, 0x2d, + 0x00, 0xbe, 0x2d, 0x8d, 0x1a, 0x8f, 0x2d, 0xe0, + 0x24, 0x1d, 0x81, 0x3a, 0xe0, 0x48, 0x1d, 0x00, + 0xa5, 0x05, 0x01, 0xb1, 0x05, 0x01, 0x82, 0x05, + 0x00, 0xb6, 0x37, 0x07, 0x9a, 0x37, 0x03, 0x85, + 0x37, 0x0a, 0x84, 0x04, 0x80, 0x19, 0x85, 0x04, + 0x80, 0x19, 0x8d, 0x04, 0x80, 0x19, 0x82, 0x04, + 0x80, 0x19, 0x9f, 0x04, 0x80, 0x19, 0x89, 0x04, + 0x8a, 0x3a, 0x99, 0x04, 0x80, 0x3a, 0xe0, 0x0b, + 0x04, 0x80, 0x19, 0xa1, 0x04, 0x8d, 0x90, 0x00, + 0xbb, 0x90, 0x01, 0x82, 0x90, 0xaf, 0x04, 0xb1, + 0x9a, 0x0d, 0xba, 0x69, 0x01, 0x82, 0x69, 0xad, + 0x83, 0x01, 0x8e, 0x83, 0x00, 0x9b, 0x55, 0x01, + 0x80, 0x55, 0x00, 0x8a, 0x90, 0x04, 0x9e, 0x04, + 0x00, 0x81, 0x04, 0x04, 0xca, 0x04, 0x80, 0x19, + 0x9c, 0x04, 0xd0, 0x20, 0x83, 0x3a, 0x8e, 0x20, + 0x81, 0x19, 0x99, 0x20, 0x83, 0x0b, 0x00, 0x87, + 0x0b, 0x01, 0x81, 0x0b, 0x01, 0x95, 0x0b, 0x00, + 0x86, 0x0b, 0x00, 0x80, 0x0b, 0x02, 0x83, 0x0b, + 0x01, 0x88, 0x0b, 0x01, 0x81, 0x0b, 0x01, 0x83, + 0x0b, 0x07, 0x80, 0x0b, 0x03, 0x81, 0x0b, 0x00, + 0x84, 0x0b, 0x01, 0x98, 0x0b, 0x01, 0x82, 0x30, + 0x00, 0x85, 0x30, 0x03, 0x81, 0x30, 0x01, 0x95, + 0x30, 0x00, 0x86, 0x30, 0x00, 0x81, 0x30, 0x00, + 0x81, 0x30, 0x00, 0x81, 0x30, 0x01, 0x80, 0x30, + 0x00, 0x84, 0x30, 0x03, 0x81, 0x30, 0x01, 0x82, + 0x30, 0x02, 0x80, 0x30, 0x06, 0x83, 0x30, 0x00, + 0x80, 0x30, 0x06, 0x90, 0x30, 0x09, 0x82, 0x2e, + 0x00, 0x88, 0x2e, 0x00, 0x82, 0x2e, 0x00, 0x95, + 0x2e, 0x00, 0x86, 0x2e, 0x00, 0x81, 0x2e, 0x00, + 0x84, 0x2e, 0x01, 0x89, 0x2e, 0x00, 0x82, 0x2e, + 0x00, 0x82, 0x2e, 0x01, 0x80, 0x2e, 0x0e, 0x83, + 0x2e, 0x01, 0x8b, 0x2e, 0x06, 0x86, 0x2e, 0x00, + 0x82, 0x78, 0x00, 0x87, 0x78, 0x01, 0x81, 0x78, + 0x01, 0x95, 0x78, 0x00, 0x86, 0x78, 0x00, 0x81, + 0x78, 0x00, 0x84, 0x78, 0x01, 0x88, 0x78, 0x01, + 0x81, 0x78, 0x01, 0x82, 0x78, 0x06, 0x82, 0x78, + 0x03, 0x81, 0x78, 0x00, 0x84, 0x78, 0x01, 0x91, + 0x78, 0x09, 0x81, 0x97, 0x00, 0x85, 0x97, 0x02, + 0x82, 0x97, 0x00, 0x83, 0x97, 0x02, 0x81, 0x97, + 0x00, 0x80, 0x97, 0x00, 0x81, 0x97, 0x02, 0x81, + 0x97, 0x02, 0x82, 0x97, 0x02, 0x8b, 0x97, 0x03, + 0x84, 0x97, 0x02, 0x82, 0x97, 0x00, 0x83, 0x97, + 0x01, 0x80, 0x97, 0x05, 0x80, 0x97, 0x0d, 0x94, + 0x97, 0x04, 0x8c, 0x99, 0x00, 0x82, 0x99, 0x00, + 0x96, 0x99, 0x00, 0x8f, 0x99, 0x01, 0x88, 0x99, + 0x00, 0x82, 0x99, 0x00, 0x83, 0x99, 0x06, 0x81, + 0x99, 0x00, 0x82, 0x99, 0x01, 0x80, 0x99, 0x01, + 0x83, 0x99, 0x01, 0x89, 0x99, 0x06, 0x88, 0x99, + 0x8c, 0x3f, 0x00, 0x82, 0x3f, 0x00, 0x96, 0x3f, + 0x00, 0x89, 0x3f, 0x00, 0x84, 0x3f, 0x01, 0x88, + 0x3f, 0x00, 0x82, 0x3f, 0x00, 0x83, 0x3f, 0x06, + 0x81, 0x3f, 0x05, 0x81, 0x3f, 0x00, 0x83, 0x3f, + 0x01, 0x89, 0x3f, 0x00, 0x82, 0x3f, 0x0b, 0x8c, + 0x54, 0x00, 0x82, 0x54, 0x00, 0xb2, 0x54, 0x00, + 0x82, 0x54, 0x00, 0x85, 0x54, 0x03, 0x8f, 0x54, + 0x01, 0x99, 0x54, 0x00, 0x82, 0x89, 0x00, 0x91, + 0x89, 0x02, 0x97, 0x89, 0x00, 0x88, 0x89, 0x00, + 0x80, 0x89, 0x01, 0x86, 0x89, 0x02, 0x80, 0x89, + 0x03, 0x85, 0x89, 0x00, 0x80, 0x89, 0x00, 0x87, + 0x89, 0x05, 0x89, 0x89, 0x01, 0x82, 0x89, 0x0b, + 0xb9, 0x9b, 0x03, 0x80, 0x19, 0x9b, 0x9b, 0x24, + 0x81, 0x49, 0x00, 0x80, 0x49, 0x00, 0x84, 0x49, + 0x00, 0x97, 0x49, 0x00, 0x80, 0x49, 0x00, 0x96, + 0x49, 0x01, 0x84, 0x49, 0x00, 0x80, 0x49, 0x00, + 0x86, 0x49, 0x00, 0x89, 0x49, 0x01, 0x83, 0x49, + 0x1f, 0xc7, 0x9c, 0x00, 0xa3, 0x9c, 0x03, 0xa6, + 0x9c, 0x00, 0xa3, 0x9c, 0x00, 0x8e, 0x9c, 0x00, + 0x86, 0x9c, 0x83, 0x19, 0x81, 0x9c, 0x24, 0xe0, + 0x3f, 0x63, 0xa5, 0x28, 0x00, 0x80, 0x28, 0x04, + 0x80, 0x28, 0x01, 0xaa, 0x28, 0x80, 0x19, 0x83, + 0x28, 0xe0, 0x9f, 0x33, 0xc8, 0x27, 0x00, 0x83, + 0x27, 0x01, 0x86, 0x27, 0x00, 0x80, 0x27, 0x00, + 0x83, 0x27, 0x01, 0xa8, 0x27, 0x00, 0x83, 0x27, + 0x01, 0xa0, 0x27, 0x00, 0x83, 0x27, 0x01, 0x86, + 0x27, 0x00, 0x80, 0x27, 0x00, 0x83, 0x27, 0x01, + 0x8e, 0x27, 0x00, 0xb8, 0x27, 0x00, 0x83, 0x27, + 0x01, 0xc2, 0x27, 0x01, 0x9f, 0x27, 0x02, 0x99, + 0x27, 0x05, 0xd5, 0x17, 0x01, 0x85, 0x17, 0x01, + 0xe2, 0x1f, 0x12, 0x9c, 0x6c, 0x02, 0xca, 0x82, + 0x82, 0x19, 0x8a, 0x82, 0x06, 0x95, 0x91, 0x08, + 0x80, 0x91, 0x94, 0x35, 0x81, 0x19, 0x08, 0x93, + 0x11, 0x0b, 0x8c, 0x92, 0x00, 0x82, 0x92, 0x00, + 0x81, 0x92, 0x0b, 0xdd, 0x44, 0x01, 0x89, 0x44, + 0x05, 0x89, 0x44, 0x05, 0x81, 0x60, 0x81, 0x19, + 0x80, 0x60, 0x80, 0x19, 0x93, 0x60, 0x05, 0xd8, + 0x60, 0x06, 0xaa, 0x60, 0x04, 0xc5, 0x12, 0x09, + 0x9e, 0x4c, 0x00, 0x8b, 0x4c, 0x03, 0x8b, 0x4c, + 0x03, 0x80, 0x4c, 0x02, 0x8b, 0x4c, 0x9d, 0x93, + 0x01, 0x84, 0x93, 0x0a, 0xab, 0x67, 0x03, 0x99, + 0x67, 0x05, 0x8a, 0x67, 0x02, 0x81, 0x67, 0x9f, + 0x44, 0x9b, 0x10, 0x01, 0x81, 0x10, 0xbe, 0x94, + 0x00, 0x9c, 0x94, 0x01, 0x8a, 0x94, 0x05, 0x89, + 0x94, 0x05, 0x8d, 0x94, 0x01, 0x9e, 0x3a, 0x30, + 0xcc, 0x07, 0x00, 0xb1, 0x07, 0xbf, 0x8d, 0xb3, + 0x0a, 0x07, 0x83, 0x0a, 0xb7, 0x4b, 0x02, 0x8e, + 0x4b, 0x02, 0x82, 0x4b, 0xaf, 0x6d, 0x8a, 0x1d, + 0x04, 0xaa, 0x28, 0x01, 0x82, 0x28, 0x87, 0x8d, + 0x07, 0x82, 0x3a, 0x80, 0x19, 0x8c, 0x3a, 0x80, + 0x19, 0x86, 0x3a, 0x83, 0x19, 0x80, 0x3a, 0x85, + 0x19, 0x80, 0x3a, 0x82, 0x19, 0x81, 0x3a, 0x80, + 0x19, 0x04, 0xa5, 0x4a, 0x84, 0x2d, 0x80, 0x1d, + 0xb0, 0x4a, 0x84, 0x2d, 0x83, 0x4a, 0x84, 0x2d, + 0x8c, 0x4a, 0x80, 0x1d, 0xc5, 0x4a, 0x80, 0x2d, + 0xbf, 0x3a, 0xe0, 0x9f, 0x4a, 0x95, 0x2d, 0x01, + 0x85, 0x2d, 0x01, 0xa5, 0x2d, 0x01, 0x85, 0x2d, + 0x01, 0x87, 0x2d, 0x00, 0x80, 0x2d, 0x00, 0x80, + 0x2d, 0x00, 0x80, 0x2d, 0x00, 0x9e, 0x2d, 0x01, + 0xb4, 0x2d, 0x00, 0x8e, 0x2d, 0x00, 0x8d, 0x2d, + 0x01, 0x85, 0x2d, 0x00, 0x92, 0x2d, 0x01, 0x82, + 0x2d, 0x00, 0x88, 0x2d, 0x00, 0x8b, 0x19, 0x81, + 0x3a, 0xd6, 0x19, 0x00, 0x8a, 0x19, 0x80, 0x4a, + 0x01, 0x8a, 0x19, 0x80, 0x4a, 0x8e, 0x19, 0x00, + 0x8c, 0x4a, 0x02, 0xa0, 0x19, 0x0e, 0xa0, 0x3a, + 0x0e, 0xa5, 0x19, 0x80, 0x2d, 0x82, 0x19, 0x81, + 0x4a, 0x85, 0x19, 0x80, 0x4a, 0x9a, 0x19, 0x80, + 0x4a, 0x90, 0x19, 0xa8, 0x4a, 0x82, 0x19, 0x03, + 0xe2, 0x39, 0x19, 0x15, 0x8a, 0x19, 0x14, 0xe3, + 0x3f, 0x19, 0xe0, 0x9f, 0x0f, 0xe2, 0x13, 0x19, + 0x01, 0x9f, 0x19, 0x00, 0xe0, 0x08, 0x19, 0xdf, + 0x29, 0x9f, 0x4a, 0xe0, 0x13, 0x1a, 0x04, 0x86, + 0x1a, 0xa5, 0x28, 0x00, 0x80, 0x28, 0x04, 0x80, + 0x28, 0x01, 0xb7, 0x9d, 0x06, 0x81, 0x9d, 0x0d, + 0x80, 0x9d, 0x96, 0x27, 0x08, 0x86, 0x27, 0x00, + 0x86, 0x27, 0x00, 0x86, 0x27, 0x00, 0x86, 0x27, + 0x00, 0x86, 0x27, 0x00, 0x86, 0x27, 0x00, 0x86, + 0x27, 0x00, 0x86, 0x27, 0x00, 0x9f, 0x1d, 0xdd, + 0x19, 0x21, 0x99, 0x32, 0x00, 0xd8, 0x32, 0x0b, + 0xe0, 0x75, 0x32, 0x19, 0x94, 0x19, 0x80, 0x32, + 0x80, 0x19, 0x80, 0x32, 0x98, 0x19, 0x88, 0x32, + 0x83, 0x3a, 0x81, 0x33, 0x87, 0x19, 0x83, 0x32, + 0x83, 0x19, 0x00, 0xd5, 0x38, 0x01, 0x81, 0x3a, + 0x81, 0x19, 0x82, 0x38, 0x80, 0x19, 0xd9, 0x40, + 0x81, 0x19, 0x82, 0x40, 0x04, 0xaa, 0x0d, 0x00, + 0xdd, 0x33, 0x00, 0x8f, 0x19, 0x9f, 0x0d, 0xa5, + 0x19, 0x08, 0x80, 0x19, 0x8f, 0x40, 0x9e, 0x33, + 0x00, 0xbf, 0x19, 0x9e, 0x33, 0xd0, 0x19, 0xae, + 0x40, 0x80, 0x19, 0xd7, 0x40, 0xe0, 0x47, 0x19, + 0xf0, 0x09, 0x5f, 0x32, 0xbf, 0x19, 0xf0, 0x41, + 0x9f, 0x32, 0xe4, 0x2c, 0xa9, 0x02, 0xb6, 0xa9, + 0x08, 0xaf, 0x4f, 0xe0, 0xcb, 0xa4, 0x13, 0xdf, + 0x1d, 0xd7, 0x08, 0x07, 0xa1, 0x19, 0xe0, 0x05, + 0x4a, 0x82, 0x19, 0xc2, 0x4a, 0x01, 0x81, 0x4a, + 0x00, 0x80, 0x4a, 0x00, 0x87, 0x4a, 0x14, 0x8d, + 0x4a, 0xac, 0x8f, 0x02, 0x89, 0x19, 0x05, 0xb7, + 0x7e, 0x07, 0xc5, 0x84, 0x07, 0x8b, 0x84, 0x05, + 0x9f, 0x20, 0xad, 0x42, 0x80, 0x19, 0x80, 0x42, + 0xa3, 0x81, 0x0a, 0x80, 0x81, 0x9c, 0x33, 0x02, + 0xcd, 0x3d, 0x00, 0x80, 0x19, 0x89, 0x3d, 0x03, + 0x81, 0x3d, 0x9e, 0x63, 0x00, 0xb6, 0x16, 0x08, + 0x8d, 0x16, 0x01, 0x89, 0x16, 0x01, 0x83, 0x16, + 0x9f, 0x63, 0xc2, 0x95, 0x17, 0x84, 0x95, 0x96, + 0x5a, 0x09, 0x85, 0x27, 0x01, 0x85, 0x27, 0x01, + 0x85, 0x27, 0x08, 0x86, 0x27, 0x00, 0x86, 0x27, + 0x00, 0xaa, 0x4a, 0x80, 0x19, 0x88, 0x4a, 0x80, + 0x2d, 0x83, 0x4a, 0x81, 0x19, 0x03, 0xcf, 0x17, + 0xad, 0x5a, 0x01, 0x89, 0x5a, 0x05, 0xf0, 0x1b, + 0x43, 0x33, 0x0b, 0x96, 0x33, 0x03, 0xb0, 0x33, + 0x70, 0x10, 0xa3, 0xe1, 0x0d, 0x32, 0x01, 0xe0, + 0x09, 0x32, 0x25, 0x86, 0x4a, 0x0b, 0x84, 0x05, + 0x04, 0x99, 0x37, 0x00, 0x84, 0x37, 0x00, 0x80, + 0x37, 0x00, 0x81, 0x37, 0x00, 0x81, 0x37, 0x00, + 0x89, 0x37, 0xe0, 0x12, 0x04, 0x0f, 0xe1, 0x0a, + 0x04, 0x81, 0x19, 0xcf, 0x04, 0x01, 0xb5, 0x04, + 0x06, 0x80, 0x04, 0x1f, 0x8f, 0x04, 0x8f, 0x3a, + 0x89, 0x19, 0x05, 0x8d, 0x3a, 0x81, 0x1d, 0xa2, + 0x19, 0x00, 0x92, 0x19, 0x00, 0x83, 0x19, 0x03, + 0x84, 0x04, 0x00, 0xe0, 0x26, 0x04, 0x01, 0x80, + 0x19, 0x00, 0x9f, 0x19, 0x99, 0x4a, 0x85, 0x19, + 0x99, 0x4a, 0x8a, 0x19, 0x89, 0x40, 0x80, 0x19, + 0xac, 0x40, 0x81, 0x19, 0x9e, 0x33, 0x02, 0x85, + 0x33, 0x01, 0x85, 0x33, 0x01, 0x85, 0x33, 0x01, + 0x82, 0x33, 0x02, 0x86, 0x19, 0x00, 0x86, 0x19, + 0x09, 0x84, 0x19, 0x01, 0x8b, 0x4e, 0x00, 0x99, + 0x4e, 0x00, 0x92, 0x4e, 0x00, 0x81, 0x4e, 0x00, + 0x8e, 0x4e, 0x01, 0x8d, 0x4e, 0x21, 0xe0, 0x1a, + 0x4e, 0x04, 0x82, 0x19, 0x03, 0xac, 0x19, 0x02, + 0x88, 0x19, 0xce, 0x2d, 0x00, 0x8c, 0x19, 0x02, + 0x80, 0x2d, 0x2e, 0xac, 0x19, 0x80, 0x3a, 0x60, + 0x21, 0x9c, 0x50, 0x02, 0xb0, 0x13, 0x0e, 0x80, + 0x3a, 0x9a, 0x19, 0x03, 0xa3, 0x70, 0x08, 0x82, + 0x70, 0x9a, 0x2a, 0x04, 0xaa, 0x72, 0x04, 0x9d, + 0xa3, 0x00, 0x80, 0xa3, 0xa3, 0x73, 0x03, 0x8d, + 0x73, 0x29, 0xcf, 0x1f, 0xaf, 0x86, 0x9d, 0x7a, + 0x01, 0x89, 0x7a, 0x05, 0xa3, 0x79, 0x03, 0xa3, + 0x79, 0x03, 0xa7, 0x25, 0x07, 0xb3, 0x14, 0x0a, + 0x80, 0x14, 0x8a, 0xa5, 0x00, 0x8e, 0xa5, 0x00, + 0x86, 0xa5, 0x00, 0x81, 0xa5, 0x00, 0x8a, 0xa5, + 0x00, 0x8e, 0xa5, 0x00, 0x86, 0xa5, 0x00, 0x81, + 0xa5, 0x02, 0xb3, 0xa0, 0x0b, 0xe0, 0xd6, 0x4d, + 0x08, 0x95, 0x4d, 0x09, 0x87, 0x4d, 0x17, 0x85, + 0x4a, 0x00, 0xa9, 0x4a, 0x00, 0x88, 0x4a, 0x44, + 0x85, 0x1c, 0x01, 0x80, 0x1c, 0x00, 0xab, 0x1c, + 0x00, 0x81, 0x1c, 0x02, 0x80, 0x1c, 0x01, 0x80, + 0x1c, 0x95, 0x39, 0x00, 0x88, 0x39, 0x9f, 0x7c, + 0x9e, 0x64, 0x07, 0x88, 0x64, 0x2f, 0x92, 0x36, + 0x00, 0x81, 0x36, 0x04, 0x84, 0x36, 0x9b, 0x7f, + 0x02, 0x80, 0x7f, 0x99, 0x51, 0x04, 0x80, 0x51, + 0x3f, 0x9f, 0x5d, 0x97, 0x5c, 0x03, 0x93, 0x5c, + 0x01, 0xad, 0x5c, 0x83, 0x43, 0x00, 0x81, 0x43, + 0x04, 0x87, 0x43, 0x00, 0x82, 0x43, 0x00, 0x9c, + 0x43, 0x01, 0x82, 0x43, 0x03, 0x89, 0x43, 0x06, + 0x88, 0x43, 0x06, 0x9f, 0x75, 0x9f, 0x71, 0x1f, + 0xa6, 0x56, 0x03, 0x8b, 0x56, 0x08, 0xb5, 0x06, + 0x02, 0x86, 0x06, 0x95, 0x3c, 0x01, 0x87, 0x3c, + 0x92, 0x3b, 0x04, 0x87, 0x3b, 0x91, 0x80, 0x06, + 0x83, 0x80, 0x0b, 0x86, 0x80, 0x4f, 0xc8, 0x76, + 0x36, 0xb2, 0x6f, 0x0c, 0xb2, 0x6f, 0x06, 0x85, + 0x6f, 0xa7, 0x34, 0x07, 0x89, 0x34, 0x05, 0xa5, + 0x2b, 0x02, 0x9c, 0x2b, 0x07, 0x81, 0x2b, 0x60, + 0x6f, 0x9e, 0x04, 0x00, 0xa9, 0xa8, 0x00, 0x82, + 0xa8, 0x01, 0x81, 0xa8, 0x0f, 0x82, 0x04, 0x36, + 0x83, 0x04, 0xa7, 0x74, 0x07, 0xa9, 0x8a, 0x15, + 0x99, 0x77, 0x25, 0x9b, 0x18, 0x13, 0x96, 0x26, + 0x08, 0xcd, 0x0e, 0x03, 0xa3, 0x0e, 0x08, 0x80, + 0x0e, 0xc2, 0x3e, 0x09, 0x80, 0x3e, 0x01, 0x98, + 0x8b, 0x06, 0x89, 0x8b, 0x05, 0xb4, 0x15, 0x00, + 0x91, 0x15, 0x07, 0xa6, 0x53, 0x08, 0xdf, 0x85, + 0x00, 0x93, 0x89, 0x0a, 0x91, 0x45, 0x00, 0xae, + 0x45, 0x3d, 0x86, 0x62, 0x00, 0x80, 0x62, 0x00, + 0x83, 0x62, 0x00, 0x8e, 0x62, 0x00, 0x8a, 0x62, + 0x05, 0xba, 0x47, 0x04, 0x89, 0x47, 0x05, 0x83, + 0x2c, 0x00, 0x87, 0x2c, 0x01, 0x81, 0x2c, 0x01, + 0x95, 0x2c, 0x00, 0x86, 0x2c, 0x00, 0x81, 0x2c, + 0x00, 0x84, 0x2c, 0x00, 0x80, 0x3a, 0x88, 0x2c, + 0x01, 0x81, 0x2c, 0x01, 0x82, 0x2c, 0x01, 0x80, + 0x2c, 0x05, 0x80, 0x2c, 0x04, 0x86, 0x2c, 0x01, + 0x86, 0x2c, 0x02, 0x84, 0x2c, 0x0a, 0x89, 0xa2, + 0x00, 0x80, 0xa2, 0x01, 0x80, 0xa2, 0x00, 0xa5, + 0xa2, 0x00, 0x89, 0xa2, 0x00, 0x80, 0xa2, 0x01, + 0x80, 0xa2, 0x00, 0x83, 0xa2, 0x00, 0x89, 0xa2, + 0x00, 0x81, 0xa2, 0x07, 0x81, 0xa2, 0x1c, 0xdb, + 0x68, 0x00, 0x84, 0x68, 0x1d, 0xc7, 0x9e, 0x07, + 0x89, 0x9e, 0x60, 0x45, 0xb5, 0x87, 0x01, 0xa5, + 0x87, 0x21, 0xc4, 0x5f, 0x0a, 0x89, 0x5f, 0x05, + 0x8c, 0x60, 0x12, 0xb9, 0x96, 0x05, 0x89, 0x96, + 0x05, 0x93, 0x63, 0x1b, 0x9a, 0x02, 0x01, 0x8e, + 0x02, 0x03, 0x96, 0x02, 0x60, 0x58, 0xbb, 0x22, + 0x60, 0x03, 0xd2, 0xa7, 0x0b, 0x80, 0xa7, 0x86, + 0x21, 0x01, 0x80, 0x21, 0x01, 0x87, 0x21, 0x00, + 0x81, 0x21, 0x00, 0x9d, 0x21, 0x00, 0x81, 0x21, + 0x01, 0x8b, 0x21, 0x08, 0x89, 0x21, 0x45, 0x87, + 0x66, 0x01, 0xad, 0x66, 0x01, 0x8a, 0x66, 0x1a, + 0xc7, 0xaa, 0x07, 0xd2, 0x8c, 0x0c, 0x8f, 0x12, + 0xb8, 0x7d, 0x06, 0x89, 0x20, 0x60, 0x55, 0xa1, + 0x8e, 0x0d, 0x89, 0x8e, 0x05, 0x88, 0x0c, 0x00, + 0xac, 0x0c, 0x00, 0x8d, 0x0c, 0x09, 0x9c, 0x0c, + 0x02, 0x9f, 0x57, 0x01, 0x95, 0x57, 0x00, 0x8d, + 0x57, 0x48, 0x86, 0x58, 0x00, 0x81, 0x58, 0x00, + 0xab, 0x58, 0x02, 0x80, 0x58, 0x00, 0x81, 0x58, + 0x00, 0x88, 0x58, 0x07, 0x89, 0x58, 0x05, 0x85, + 0x2f, 0x00, 0x81, 0x2f, 0x00, 0xa4, 0x2f, 0x00, + 0x81, 0x2f, 0x00, 0x85, 0x2f, 0x06, 0x89, 0x2f, + 0x60, 0xd5, 0x98, 0x52, 0x06, 0x90, 0x41, 0x00, + 0xa8, 0x41, 0x02, 0x9c, 0x41, 0x54, 0x80, 0x4f, + 0x0e, 0xb1, 0x97, 0x0c, 0x80, 0x97, 0xe3, 0x39, + 0x1b, 0x60, 0x05, 0xe0, 0x0e, 0x1b, 0x00, 0x84, + 0x1b, 0x0a, 0xe0, 0x63, 0x1b, 0x69, 0xeb, 0xe0, + 0x02, 0x1e, 0x0c, 0xe3, 0xf5, 0x24, 0x09, 0xef, + 0x3a, 0x24, 0x04, 0xe1, 0xe6, 0x03, 0x70, 0x0a, + 0x58, 0xb9, 0x31, 0x66, 0x65, 0xe1, 0xd8, 0x08, + 0x06, 0x9e, 0x61, 0x00, 0x89, 0x61, 0x03, 0x81, + 0x61, 0xce, 0x9f, 0x00, 0x89, 0x9f, 0x05, 0x9d, + 0x09, 0x01, 0x85, 0x09, 0x09, 0xc5, 0x7b, 0x09, + 0x89, 0x7b, 0x00, 0x86, 0x7b, 0x00, 0x94, 0x7b, + 0x04, 0x92, 0x7b, 0x61, 0x4f, 0xb9, 0x48, 0x60, + 0x65, 0xda, 0x59, 0x60, 0x04, 0xca, 0x5e, 0x03, + 0xb8, 0x5e, 0x06, 0x90, 0x5e, 0x3f, 0x80, 0x98, + 0x80, 0x6a, 0x81, 0x32, 0x80, 0x46, 0x0a, 0x81, + 0x32, 0x0d, 0xf0, 0x07, 0x97, 0x98, 0x07, 0xe2, + 0x9f, 0x98, 0xe1, 0x75, 0x46, 0x28, 0x80, 0x46, + 0x88, 0x98, 0x70, 0x12, 0x86, 0x83, 0x40, 0x00, + 0x86, 0x40, 0x00, 0x81, 0x40, 0x00, 0x80, 0x40, + 0xe0, 0xbe, 0x38, 0x82, 0x40, 0x0e, 0x80, 0x38, + 0x1c, 0x82, 0x38, 0x01, 0x80, 0x40, 0x0d, 0x83, + 0x40, 0x07, 0xe1, 0x2b, 0x6a, 0x68, 0xa3, 0xe0, + 0x0a, 0x23, 0x04, 0x8c, 0x23, 0x02, 0x88, 0x23, + 0x06, 0x89, 0x23, 0x01, 0x83, 0x23, 0x83, 0x19, + 0x6e, 0xfb, 0xe0, 0x99, 0x19, 0x05, 0xe1, 0x53, + 0x19, 0x4b, 0xad, 0x3a, 0x01, 0x96, 0x3a, 0x08, + 0xe0, 0x13, 0x19, 0x3b, 0xe0, 0x95, 0x19, 0x09, + 0xa6, 0x19, 0x01, 0xbd, 0x19, 0x82, 0x3a, 0x90, + 0x19, 0x87, 0x3a, 0x81, 0x19, 0x86, 0x3a, 0x9d, + 0x19, 0x83, 0x3a, 0xbc, 0x19, 0x14, 0xc5, 0x2d, + 0x60, 0x19, 0x93, 0x19, 0x0b, 0x93, 0x19, 0x0b, + 0xd6, 0x19, 0x08, 0x98, 0x19, 0x60, 0x26, 0xd4, + 0x19, 0x00, 0xc6, 0x19, 0x00, 0x81, 0x19, 0x01, + 0x80, 0x19, 0x01, 0x81, 0x19, 0x01, 0x83, 0x19, + 0x00, 0x8b, 0x19, 0x00, 0x80, 0x19, 0x00, 0x86, + 0x19, 0x00, 0xc0, 0x19, 0x00, 0x83, 0x19, 0x01, + 0x87, 0x19, 0x00, 0x86, 0x19, 0x00, 0x9b, 0x19, + 0x00, 0x83, 0x19, 0x00, 0x84, 0x19, 0x00, 0x80, + 0x19, 0x02, 0x86, 0x19, 0x00, 0xe0, 0xf3, 0x19, + 0x01, 0xe0, 0xc3, 0x19, 0x01, 0xb1, 0x19, 0xe2, + 0x2b, 0x88, 0x0e, 0x84, 0x88, 0x00, 0x8e, 0x88, + 0x63, 0xef, 0x9e, 0x4a, 0x05, 0x85, 0x4a, 0x60, + 0x74, 0x86, 0x29, 0x00, 0x90, 0x29, 0x01, 0x86, + 0x29, 0x00, 0x81, 0x29, 0x00, 0x84, 0x29, 0x04, + 0xbd, 0x1d, 0x20, 0x80, 0x1d, 0x60, 0x0f, 0xac, + 0x6b, 0x02, 0x8d, 0x6b, 0x01, 0x89, 0x6b, 0x03, + 0x81, 0x6b, 0x60, 0xdf, 0x9e, 0xa1, 0x10, 0xb9, + 0xa6, 0x04, 0x80, 0xa6, 0x61, 0x6f, 0xa9, 0x65, + 0x60, 0x75, 0xaa, 0x6e, 0x03, 0x80, 0x6e, 0x61, + 0x7f, 0x86, 0x27, 0x00, 0x83, 0x27, 0x00, 0x81, + 0x27, 0x00, 0x8e, 0x27, 0x00, 0xe0, 0x64, 0x5b, + 0x01, 0x8f, 0x5b, 0x28, 0xcb, 0x01, 0x03, 0x89, + 0x01, 0x03, 0x81, 0x01, 0x62, 0xb0, 0xc3, 0x19, + 0x4b, 0xbc, 0x19, 0x60, 0x61, 0x83, 0x04, 0x00, + 0x9a, 0x04, 0x00, 0x81, 0x04, 0x00, 0x80, 0x04, + 0x01, 0x80, 0x04, 0x00, 0x89, 0x04, 0x00, 0x83, + 0x04, 0x00, 0x80, 0x04, 0x00, 0x80, 0x04, 0x05, + 0x80, 0x04, 0x03, 0x80, 0x04, 0x00, 0x80, 0x04, + 0x00, 0x80, 0x04, 0x00, 0x82, 0x04, 0x00, 0x81, + 0x04, 0x00, 0x80, 0x04, 0x01, 0x80, 0x04, 0x00, + 0x80, 0x04, 0x00, 0x80, 0x04, 0x00, 0x80, 0x04, + 0x00, 0x80, 0x04, 0x00, 0x81, 0x04, 0x00, 0x80, + 0x04, 0x01, 0x83, 0x04, 0x00, 0x86, 0x04, 0x00, + 0x83, 0x04, 0x00, 0x83, 0x04, 0x00, 0x80, 0x04, + 0x00, 0x89, 0x04, 0x00, 0x90, 0x04, 0x04, 0x82, + 0x04, 0x00, 0x84, 0x04, 0x00, 0x90, 0x04, 0x33, + 0x81, 0x04, 0x60, 0xad, 0xab, 0x19, 0x03, 0xe0, + 0x03, 0x19, 0x0b, 0x8e, 0x19, 0x01, 0x8e, 0x19, + 0x00, 0x8e, 0x19, 0x00, 0xa4, 0x19, 0x09, 0xe0, + 0x4d, 0x19, 0x37, 0x99, 0x19, 0x80, 0x38, 0x81, + 0x19, 0x0c, 0xab, 0x19, 0x03, 0x88, 0x19, 0x06, + 0x81, 0x19, 0x0d, 0x85, 0x19, 0x60, 0x39, 0xe3, + 0x77, 0x19, 0x03, 0x90, 0x19, 0x02, 0x8c, 0x19, + 0x02, 0xe0, 0x16, 0x19, 0x03, 0xde, 0x19, 0x05, + 0x8b, 0x19, 0x03, 0x80, 0x19, 0x0e, 0x8b, 0x19, + 0x03, 0xb7, 0x19, 0x07, 0x89, 0x19, 0x05, 0xa7, + 0x19, 0x07, 0x9d, 0x19, 0x01, 0x8b, 0x19, 0x03, + 0x81, 0x19, 0x3d, 0xe0, 0xf3, 0x19, 0x0b, 0x8d, + 0x19, 0x01, 0x8c, 0x19, 0x02, 0x89, 0x19, 0x04, + 0xb7, 0x19, 0x06, 0x8e, 0x19, 0x01, 0x8a, 0x19, + 0x05, 0x88, 0x19, 0x06, 0xe0, 0x32, 0x19, 0x00, + 0xe0, 0x05, 0x19, 0x63, 0xa5, 0xf0, 0x96, 0x7f, + 0x32, 0x1f, 0xef, 0xd9, 0x32, 0x05, 0xe0, 0x7d, + 0x32, 0x01, 0xf0, 0x06, 0x21, 0x32, 0x0d, 0xf0, + 0x0c, 0xd0, 0x32, 0x0e, 0xe2, 0x0d, 0x32, 0x69, + 0x41, 0xe1, 0xbd, 0x32, 0x65, 0x81, 0xf0, 0x02, + 0xea, 0x32, 0x04, 0xef, 0xff, 0x32, 0x7a, 0xcb, + 0xf0, 0x80, 0x19, 0x1d, 0xdf, 0x19, 0x60, 0x1f, + 0xe0, 0x8f, 0x3a, +}; + +static const uint8_t unicode_script_ext_table[1253] = { + 0x80, 0x36, 0x00, 0x00, 0x10, 0x06, 0x13, 0x1a, + 0x23, 0x25, 0x28, 0x29, 0x2f, 0x2a, 0x2d, 0x32, + 0x4a, 0x51, 0x53, 0x72, 0x86, 0x81, 0x83, 0x00, + 0x00, 0x07, 0x0b, 0x1d, 0x20, 0x4a, 0x4f, 0x9b, + 0xa1, 0x09, 0x00, 0x00, 0x02, 0x0d, 0x4a, 0x00, + 0x00, 0x02, 0x02, 0x0d, 0x4a, 0x00, 0x00, 0x00, + 0x02, 0x4a, 0x4f, 0x08, 0x00, 0x00, 0x02, 0x4a, + 0x9b, 0x00, 0x00, 0x00, 0x02, 0x0d, 0x4a, 0x25, + 0x00, 0x00, 0x08, 0x17, 0x1a, 0x1d, 0x2d, 0x4a, + 0x72, 0x8e, 0x93, 0x00, 0x08, 0x17, 0x1d, 0x2d, + 0x4a, 0x79, 0x8e, 0x93, 0xa0, 0x00, 0x04, 0x17, + 0x1d, 0x4a, 0x9d, 0x00, 0x05, 0x29, 0x4a, 0x8e, + 0x90, 0x9b, 0x00, 0x0b, 0x14, 0x17, 0x1a, 0x1d, + 0x2a, 0x2d, 0x4a, 0x79, 0x90, 0x9d, 0xa0, 0x00, + 0x06, 0x1a, 0x25, 0x29, 0x2a, 0x40, 0x4a, 0x00, + 0x04, 0x1d, 0x2d, 0x4a, 0x72, 0x00, 0x09, 0x1a, + 0x23, 0x37, 0x4a, 0x72, 0x90, 0x93, 0x9d, 0xa0, + 0x00, 0x0a, 0x05, 0x1d, 0x23, 0x2a, 0x2d, 0x37, + 0x4a, 0x72, 0x90, 0x93, 0x00, 0x02, 0x4a, 0x9d, + 0x00, 0x03, 0x23, 0x4a, 0x90, 0x00, 0x04, 0x17, + 0x1d, 0x4a, 0x79, 0x00, 0x03, 0x17, 0x4a, 0x93, + 0x00, 0x02, 0x4a, 0x8e, 0x00, 0x02, 0x27, 0x4a, + 0x00, 0x00, 0x00, 0x02, 0x4a, 0x8e, 0x00, 0x03, + 0x1d, 0x4a, 0xa0, 0x00, 0x00, 0x00, 0x04, 0x2d, + 0x4a, 0x72, 0xa0, 0x0b, 0x00, 0x00, 0x02, 0x4a, + 0x90, 0x01, 0x00, 0x00, 0x05, 0x17, 0x23, 0x40, + 0x4a, 0x90, 0x00, 0x04, 0x17, 0x23, 0x4a, 0x90, + 0x00, 0x02, 0x4a, 0x90, 0x06, 0x00, 0x00, 0x03, + 0x4a, 0x8e, 0x90, 0x00, 0x02, 0x4a, 0x90, 0x00, + 0x00, 0x00, 0x03, 0x17, 0x4a, 0x90, 0x00, 0x06, + 0x14, 0x17, 0x2a, 0x4a, 0x8e, 0x9b, 0x0f, 0x00, + 0x00, 0x01, 0x2d, 0x01, 0x00, 0x00, 0x01, 0x2d, + 0x11, 0x00, 0x00, 0x02, 0x4a, 0x79, 0x04, 0x00, + 0x00, 0x03, 0x14, 0x4a, 0xa0, 0x03, 0x00, 0x0c, + 0x01, 0x4a, 0x03, 0x00, 0x01, 0x02, 0x1a, 0x2d, + 0x80, 0x8c, 0x00, 0x00, 0x02, 0x1d, 0x72, 0x00, + 0x02, 0x1d, 0x29, 0x01, 0x02, 0x1d, 0x4a, 0x00, + 0x02, 0x1d, 0x29, 0x80, 0x80, 0x00, 0x00, 0x03, + 0x05, 0x28, 0x29, 0x80, 0x01, 0x00, 0x00, 0x07, + 0x04, 0x2b, 0x69, 0x34, 0x90, 0x9a, 0xa8, 0x0d, + 0x00, 0x00, 0x07, 0x04, 0x2b, 0x69, 0x34, 0x90, + 0x9a, 0xa8, 0x00, 0x03, 0x04, 0x90, 0x9a, 0x01, + 0x00, 0x00, 0x08, 0x01, 0x04, 0x2b, 0x69, 0x34, + 0x90, 0x9a, 0xa8, 0x1f, 0x00, 0x00, 0x09, 0x01, + 0x04, 0x55, 0x56, 0x77, 0x80, 0x34, 0x8a, 0x90, + 0x09, 0x00, 0x0a, 0x02, 0x04, 0x90, 0x09, 0x00, + 0x09, 0x03, 0x04, 0x9a, 0xa8, 0x05, 0x00, 0x00, + 0x02, 0x04, 0x90, 0x62, 0x00, 0x00, 0x02, 0x04, + 0x34, 0x81, 0xfb, 0x00, 0x00, 0x0d, 0x0b, 0x20, + 0x2c, 0x2e, 0x30, 0x3f, 0x4a, 0x54, 0x78, 0x85, + 0x97, 0x99, 0x9e, 0x00, 0x0c, 0x0b, 0x20, 0x2c, + 0x2e, 0x30, 0x3f, 0x4a, 0x54, 0x78, 0x97, 0x99, + 0x9e, 0x10, 0x00, 0x00, 0x15, 0x0b, 0x20, 0x22, + 0x2f, 0x58, 0x2c, 0x2e, 0x30, 0x3f, 0x53, 0x54, + 0x66, 0x6e, 0x78, 0x47, 0x89, 0x8f, 0x96, 0x97, + 0x99, 0x9e, 0x00, 0x17, 0x0b, 0x20, 0x22, 0x2f, + 0x58, 0x2c, 0x2e, 0x31, 0x30, 0x3f, 0x4c, 0x53, + 0x54, 0x66, 0x6e, 0x78, 0x47, 0x89, 0x8f, 0x96, + 0x97, 0x99, 0x9e, 0x09, 0x04, 0x20, 0x22, 0x3e, + 0x53, 0x75, 0x00, 0x09, 0x03, 0x0b, 0x15, 0x8f, + 0x75, 0x00, 0x09, 0x02, 0x30, 0x62, 0x75, 0x00, + 0x09, 0x02, 0x2e, 0x45, 0x80, 0x75, 0x00, 0x0d, + 0x02, 0x2c, 0x97, 0x80, 0x71, 0x00, 0x09, 0x03, + 0x3f, 0x66, 0xa2, 0x82, 0xcf, 0x00, 0x09, 0x03, + 0x15, 0x63, 0x93, 0x80, 0x30, 0x00, 0x00, 0x03, + 0x28, 0x29, 0x4a, 0x85, 0x6e, 0x00, 0x02, 0x01, + 0x82, 0x46, 0x00, 0x01, 0x04, 0x11, 0x35, 0x92, + 0x91, 0x80, 0x4a, 0x00, 0x01, 0x02, 0x60, 0x7e, + 0x00, 0x00, 0x00, 0x02, 0x60, 0x7e, 0x84, 0x49, + 0x00, 0x00, 0x04, 0x0b, 0x20, 0x2c, 0x3f, 0x00, + 0x01, 0x20, 0x00, 0x04, 0x0b, 0x20, 0x2c, 0x3f, + 0x00, 0x03, 0x20, 0x2c, 0x3f, 0x00, 0x01, 0x20, + 0x01, 0x02, 0x0b, 0x20, 0x00, 0x02, 0x20, 0x85, + 0x00, 0x02, 0x0b, 0x20, 0x00, 0x02, 0x20, 0x85, + 0x00, 0x06, 0x20, 0x3f, 0x54, 0x78, 0x97, 0x99, + 0x00, 0x01, 0x20, 0x01, 0x02, 0x20, 0x85, 0x01, + 0x01, 0x20, 0x00, 0x02, 0x20, 0x85, 0x00, 0x02, + 0x0b, 0x20, 0x06, 0x01, 0x20, 0x00, 0x02, 0x20, + 0x66, 0x00, 0x02, 0x0b, 0x20, 0x01, 0x01, 0x20, + 0x00, 0x02, 0x0b, 0x20, 0x03, 0x01, 0x20, 0x00, + 0x0b, 0x0b, 0x20, 0x2c, 0x3f, 0x54, 0x66, 0x78, + 0x89, 0x99, 0x9e, 0xa2, 0x00, 0x02, 0x20, 0x2c, + 0x00, 0x04, 0x20, 0x2c, 0x3f, 0xa2, 0x01, 0x02, + 0x0b, 0x20, 0x00, 0x01, 0x0b, 0x01, 0x02, 0x20, + 0x2c, 0x00, 0x01, 0x66, 0x80, 0x44, 0x00, 0x01, + 0x01, 0x2d, 0x35, 0x00, 0x00, 0x03, 0x1d, 0x4a, + 0x90, 0x00, 0x00, 0x00, 0x01, 0x90, 0x81, 0xb3, + 0x00, 0x00, 0x03, 0x4a, 0x60, 0x7e, 0x1e, 0x00, + 0x00, 0x02, 0x01, 0x04, 0x09, 0x00, 0x00, 0x06, + 0x13, 0x28, 0x29, 0x6f, 0x50, 0x76, 0x01, 0x00, + 0x00, 0x04, 0x13, 0x2d, 0x6f, 0x5d, 0x80, 0x11, + 0x00, 0x00, 0x03, 0x20, 0x2c, 0x4a, 0x8c, 0xa5, + 0x00, 0x00, 0x02, 0x1a, 0x4a, 0x17, 0x00, 0x00, + 0x02, 0x06, 0x76, 0x00, 0x07, 0x06, 0x13, 0x28, + 0x6f, 0x3e, 0x51, 0x83, 0x09, 0x00, 0x00, 0x01, + 0x23, 0x03, 0x00, 0x00, 0x03, 0x01, 0x04, 0x6f, + 0x00, 0x00, 0x00, 0x02, 0x1d, 0x29, 0x81, 0x2b, + 0x00, 0x0f, 0x02, 0x32, 0x98, 0x00, 0x00, 0x00, + 0x07, 0x0d, 0x33, 0x32, 0x38, 0x40, 0x60, 0xa9, + 0x00, 0x08, 0x0d, 0x33, 0x32, 0x38, 0x40, 0x60, + 0x7e, 0xa9, 0x00, 0x05, 0x0d, 0x33, 0x32, 0x38, + 0x40, 0x01, 0x00, 0x00, 0x01, 0x32, 0x00, 0x00, + 0x01, 0x08, 0x0d, 0x33, 0x32, 0x38, 0x40, 0x60, + 0x9c, 0xa9, 0x01, 0x09, 0x0d, 0x33, 0x32, 0x38, + 0x40, 0x4f, 0x60, 0x9c, 0xa9, 0x05, 0x06, 0x0d, + 0x33, 0x32, 0x38, 0x40, 0xa9, 0x00, 0x00, 0x00, + 0x05, 0x0d, 0x33, 0x32, 0x38, 0x40, 0x07, 0x06, + 0x0d, 0x33, 0x32, 0x38, 0x40, 0xa9, 0x03, 0x05, + 0x0d, 0x33, 0x32, 0x38, 0x40, 0x09, 0x00, 0x03, + 0x02, 0x0d, 0x32, 0x01, 0x00, 0x00, 0x05, 0x0d, + 0x33, 0x32, 0x38, 0x40, 0x04, 0x02, 0x38, 0x40, + 0x00, 0x00, 0x00, 0x05, 0x0d, 0x33, 0x32, 0x38, + 0x40, 0x03, 0x00, 0x01, 0x03, 0x32, 0x38, 0x40, + 0x01, 0x01, 0x32, 0x58, 0x00, 0x03, 0x02, 0x38, + 0x40, 0x02, 0x00, 0x00, 0x02, 0x38, 0x40, 0x59, + 0x00, 0x00, 0x06, 0x0d, 0x33, 0x32, 0x38, 0x40, + 0xa9, 0x00, 0x02, 0x38, 0x40, 0x80, 0x12, 0x00, + 0x0f, 0x01, 0x32, 0x1f, 0x00, 0x25, 0x01, 0x32, + 0x08, 0x00, 0x00, 0x02, 0x32, 0x98, 0x2f, 0x00, + 0x27, 0x01, 0x32, 0x37, 0x00, 0x30, 0x01, 0x32, + 0x0e, 0x00, 0x0b, 0x01, 0x32, 0x32, 0x00, 0x00, + 0x01, 0x32, 0x57, 0x00, 0x18, 0x01, 0x32, 0x09, + 0x00, 0x04, 0x01, 0x32, 0x5f, 0x00, 0x1e, 0x01, + 0x32, 0xc0, 0x31, 0xef, 0x00, 0x00, 0x02, 0x1d, + 0x29, 0x80, 0x0f, 0x00, 0x07, 0x02, 0x32, 0x4a, + 0x80, 0xa7, 0x00, 0x02, 0x10, 0x20, 0x22, 0x2e, + 0x30, 0x45, 0x3f, 0x3e, 0x53, 0x54, 0x5f, 0x66, + 0x85, 0x47, 0x96, 0x9e, 0xa2, 0x02, 0x0f, 0x20, + 0x22, 0x2e, 0x30, 0x45, 0x3f, 0x3e, 0x53, 0x5f, + 0x66, 0x85, 0x47, 0x96, 0x9e, 0xa2, 0x01, 0x0b, + 0x20, 0x22, 0x2e, 0x30, 0x45, 0x3e, 0x53, 0x5f, + 0x47, 0x96, 0x9e, 0x00, 0x0c, 0x20, 0x22, 0x2e, + 0x30, 0x45, 0x3e, 0x53, 0x5f, 0x85, 0x47, 0x96, + 0x9e, 0x00, 0x0b, 0x20, 0x22, 0x2e, 0x30, 0x45, + 0x3e, 0x53, 0x5f, 0x47, 0x96, 0x9e, 0x80, 0x36, + 0x00, 0x00, 0x03, 0x0b, 0x20, 0xa2, 0x00, 0x00, + 0x00, 0x02, 0x20, 0x97, 0x39, 0x00, 0x00, 0x03, + 0x42, 0x4a, 0x63, 0x80, 0x1f, 0x00, 0x00, 0x02, + 0x10, 0x3d, 0xc0, 0x12, 0xed, 0x00, 0x01, 0x02, + 0x04, 0x69, 0x80, 0x31, 0x00, 0x00, 0x02, 0x04, + 0x9a, 0x09, 0x00, 0x00, 0x02, 0x04, 0x9a, 0x46, + 0x00, 0x01, 0x05, 0x0d, 0x33, 0x32, 0x38, 0x40, + 0x80, 0x99, 0x00, 0x04, 0x06, 0x0d, 0x33, 0x32, + 0x38, 0x40, 0xa9, 0x09, 0x00, 0x00, 0x02, 0x38, + 0x40, 0x2c, 0x00, 0x01, 0x02, 0x38, 0x40, 0x80, + 0xdf, 0x00, 0x01, 0x03, 0x1e, 0x1c, 0x4e, 0x00, + 0x02, 0x1c, 0x4e, 0x03, 0x00, 0x2c, 0x03, 0x1c, + 0x4d, 0x4e, 0x02, 0x00, 0x08, 0x02, 0x1c, 0x4e, + 0x81, 0x1f, 0x00, 0x1b, 0x02, 0x04, 0x1a, 0x87, + 0x75, 0x00, 0x00, 0x02, 0x56, 0x77, 0x87, 0x8d, + 0x00, 0x00, 0x02, 0x2c, 0x97, 0x00, 0x00, 0x00, + 0x02, 0x2c, 0x97, 0x36, 0x00, 0x01, 0x02, 0x2c, + 0x97, 0x8c, 0x12, 0x00, 0x01, 0x02, 0x2c, 0x97, + 0x00, 0x00, 0x00, 0x02, 0x2c, 0x97, 0xc0, 0x5c, + 0x4b, 0x00, 0x03, 0x01, 0x23, 0x96, 0x3b, 0x00, + 0x11, 0x01, 0x32, 0x9e, 0x5d, 0x00, 0x01, 0x01, + 0x32, 0xce, 0xcd, 0x2d, 0x00, +}; + +static const uint8_t unicode_prop_Hyphen_table[28] = { + 0xac, 0x80, 0xfe, 0x80, 0x44, 0xdb, 0x80, 0x52, + 0x7a, 0x80, 0x48, 0x08, 0x81, 0x4e, 0x04, 0x80, + 0x42, 0xe2, 0x80, 0x60, 0xcd, 0x66, 0x80, 0x40, + 0xa8, 0x80, 0xd6, 0x80, +}; + +static const uint8_t unicode_prop_Other_Math_table[200] = { + 0xdd, 0x80, 0x43, 0x70, 0x11, 0x80, 0x99, 0x09, + 0x81, 0x5c, 0x1f, 0x80, 0x9a, 0x82, 0x8a, 0x80, + 0x9f, 0x83, 0x97, 0x81, 0x8d, 0x81, 0xc0, 0x8c, + 0x18, 0x11, 0x1c, 0x91, 0x03, 0x01, 0x89, 0x00, + 0x14, 0x28, 0x11, 0x09, 0x02, 0x05, 0x13, 0x24, + 0xca, 0x21, 0x18, 0x08, 0x08, 0x00, 0x21, 0x0b, + 0x0b, 0x91, 0x09, 0x00, 0x06, 0x00, 0x29, 0x41, + 0x21, 0x83, 0x40, 0xa7, 0x08, 0x80, 0x97, 0x80, + 0x90, 0x80, 0x41, 0xbc, 0x81, 0x8b, 0x88, 0x24, + 0x21, 0x09, 0x14, 0x8d, 0x00, 0x01, 0x85, 0x97, + 0x81, 0xb8, 0x00, 0x80, 0x9c, 0x83, 0x88, 0x81, + 0x41, 0x55, 0x81, 0x9e, 0x89, 0x41, 0x92, 0x95, + 0xbe, 0x83, 0x9f, 0x81, 0x60, 0xd4, 0x62, 0x00, + 0x03, 0x80, 0x40, 0xd2, 0x00, 0x80, 0x60, 0xd4, + 0xc0, 0xd4, 0x80, 0xc6, 0x01, 0x08, 0x09, 0x0b, + 0x80, 0x8b, 0x00, 0x06, 0x80, 0xc0, 0x03, 0x0f, + 0x06, 0x80, 0x9b, 0x03, 0x04, 0x00, 0x16, 0x80, + 0x41, 0x53, 0x81, 0x98, 0x80, 0x98, 0x80, 0x9e, + 0x80, 0x98, 0x80, 0x9e, 0x80, 0x98, 0x80, 0x9e, + 0x80, 0x98, 0x80, 0x9e, 0x80, 0x98, 0x07, 0x81, + 0xb1, 0x55, 0xff, 0x18, 0x9a, 0x01, 0x00, 0x08, + 0x80, 0x89, 0x03, 0x00, 0x00, 0x28, 0x18, 0x00, + 0x00, 0x02, 0x01, 0x00, 0x08, 0x00, 0x00, 0x00, + 0x00, 0x01, 0x00, 0x0b, 0x06, 0x03, 0x03, 0x00, + 0x80, 0x89, 0x80, 0x90, 0x22, 0x04, 0x80, 0x90, +}; + +static const uint8_t unicode_prop_Other_Alphabetic_table[443] = { + 0x43, 0x44, 0x80, 0x9c, 0x8c, 0x42, 0x3f, 0x8d, + 0x00, 0x01, 0x01, 0x00, 0xc7, 0x8a, 0xaf, 0x8c, + 0x06, 0x8f, 0x80, 0xe4, 0x33, 0x19, 0x0b, 0x80, + 0xa2, 0x80, 0x9d, 0x8f, 0xe5, 0x8a, 0xe4, 0x0a, + 0x88, 0x02, 0x03, 0xe9, 0x80, 0xbb, 0x8b, 0x16, + 0x85, 0x93, 0xb5, 0x09, 0x8e, 0x01, 0x22, 0x89, + 0x81, 0x9c, 0x82, 0xb9, 0x31, 0x09, 0x81, 0x89, + 0x80, 0x89, 0x81, 0x9c, 0x82, 0xb9, 0x23, 0x09, + 0x0b, 0x80, 0x9d, 0x0a, 0x80, 0x8a, 0x82, 0xb9, + 0x38, 0x10, 0x81, 0x94, 0x81, 0x95, 0x13, 0x82, + 0xb9, 0x31, 0x09, 0x81, 0x88, 0x81, 0x89, 0x81, + 0x9d, 0x80, 0xba, 0x22, 0x10, 0x82, 0x89, 0x80, + 0xa7, 0x84, 0xb8, 0x30, 0x10, 0x17, 0x81, 0x8a, + 0x81, 0x9c, 0x82, 0xb9, 0x30, 0x10, 0x17, 0x81, + 0x8a, 0x81, 0x8e, 0x80, 0x8b, 0x83, 0xb9, 0x30, + 0x10, 0x82, 0x89, 0x80, 0x89, 0x81, 0x9c, 0x82, + 0xca, 0x28, 0x00, 0x87, 0x91, 0x81, 0xbc, 0x01, + 0x86, 0x91, 0x80, 0xe2, 0x01, 0x28, 0x81, 0x8f, + 0x80, 0x40, 0xa2, 0x92, 0x88, 0x8a, 0x80, 0xa3, + 0xed, 0x8b, 0x00, 0x0b, 0x96, 0x1b, 0x10, 0x11, + 0x32, 0x83, 0x8c, 0x8b, 0x00, 0x89, 0x83, 0x46, + 0x73, 0x81, 0x9d, 0x81, 0x9d, 0x81, 0x9d, 0x81, + 0xc1, 0x92, 0x40, 0xbb, 0x81, 0xa1, 0x80, 0xf5, + 0x8b, 0x83, 0x88, 0x40, 0xdd, 0x84, 0xb8, 0x89, + 0x81, 0x93, 0xc9, 0x81, 0x8a, 0x82, 0xb0, 0x84, + 0xaf, 0x8e, 0xbb, 0x82, 0x9d, 0x88, 0x09, 0xb8, + 0x8a, 0xb1, 0x92, 0x41, 0x9b, 0xa1, 0x46, 0xc0, + 0xb3, 0x48, 0xf5, 0x9f, 0x60, 0x78, 0x73, 0x87, + 0xa1, 0x81, 0x41, 0x61, 0x07, 0x80, 0x96, 0x84, + 0xd7, 0x81, 0xb1, 0x8f, 0x00, 0xb8, 0x80, 0xa5, + 0x84, 0x9b, 0x8b, 0xac, 0x83, 0xaf, 0x8b, 0xa4, + 0x80, 0xc2, 0x8d, 0x8b, 0x07, 0x81, 0xac, 0x82, + 0xb1, 0x00, 0x11, 0x0c, 0x80, 0xab, 0x24, 0x80, + 0x40, 0xec, 0x87, 0x60, 0x4f, 0x32, 0x80, 0x48, + 0x56, 0x84, 0x46, 0x85, 0x10, 0x0c, 0x83, 0x43, + 0x13, 0x83, 0xc0, 0x80, 0x41, 0x40, 0x81, 0xce, + 0x80, 0x41, 0x02, 0x82, 0xb4, 0x8d, 0xac, 0x81, + 0x8a, 0x82, 0xac, 0x88, 0x88, 0x80, 0xbc, 0x82, + 0xa3, 0x8b, 0x91, 0x81, 0xb8, 0x82, 0xaf, 0x8c, + 0x8d, 0x81, 0xdb, 0x88, 0x08, 0x28, 0x08, 0x40, + 0x9c, 0x89, 0x96, 0x83, 0xb9, 0x31, 0x09, 0x81, + 0x89, 0x80, 0x89, 0x81, 0xd3, 0x88, 0x00, 0x08, + 0x03, 0x01, 0xe6, 0x8c, 0x02, 0xe9, 0x91, 0x40, + 0xec, 0x31, 0x86, 0x9c, 0x81, 0xd1, 0x8e, 0x00, + 0xe9, 0x8a, 0xe6, 0x8d, 0x41, 0x00, 0x8c, 0x40, + 0xf6, 0x28, 0x09, 0x0a, 0x00, 0x80, 0x40, 0x8d, + 0x31, 0x2b, 0x80, 0x9b, 0x89, 0xa9, 0x20, 0x83, + 0x91, 0x8a, 0xad, 0x8d, 0x41, 0x96, 0x38, 0x86, + 0xd2, 0x95, 0x80, 0x8d, 0xf9, 0x2a, 0x00, 0x08, + 0x10, 0x02, 0x80, 0xc1, 0x20, 0x08, 0x83, 0x41, + 0x5b, 0x83, 0x88, 0x08, 0x80, 0xaf, 0x32, 0x82, + 0x60, 0x41, 0xdc, 0x90, 0x4e, 0x1f, 0x00, 0xb6, + 0x33, 0xdc, 0x81, 0x60, 0x4c, 0xab, 0x80, 0x60, + 0x23, 0x60, 0x30, 0x90, 0x0e, 0x01, 0x04, 0xe3, + 0x80, 0x48, 0xb6, 0x80, 0x47, 0xe7, 0x99, 0x85, + 0x99, 0x85, 0x99, +}; + +static const uint8_t unicode_prop_Other_Lowercase_table[69] = { + 0x40, 0xa9, 0x80, 0x8e, 0x80, 0x41, 0xf4, 0x88, + 0x31, 0x9d, 0x84, 0xdf, 0x80, 0xb3, 0x80, 0x4d, + 0x80, 0x80, 0x4c, 0x2e, 0xbe, 0x8c, 0x80, 0xa1, + 0xa4, 0x42, 0xb0, 0x80, 0x8c, 0x80, 0x8f, 0x8c, + 0x40, 0xd2, 0x8f, 0x43, 0x4f, 0x99, 0x47, 0x91, + 0x81, 0x60, 0x7a, 0x1d, 0x81, 0x40, 0xd1, 0x80, + 0x40, 0x80, 0x12, 0x81, 0x43, 0x61, 0x83, 0x88, + 0x80, 0x60, 0x5c, 0x15, 0x01, 0x10, 0xa9, 0x80, + 0x88, 0x60, 0xd8, 0x74, 0xbd, +}; + +static const uint8_t unicode_prop_Other_Uppercase_table[15] = { + 0x60, 0x21, 0x5f, 0x8f, 0x43, 0x45, 0x99, 0x61, + 0xcc, 0x5f, 0x99, 0x85, 0x99, 0x85, 0x99, +}; + +static const uint8_t unicode_prop_Other_Grapheme_Extend_table[112] = { + 0x49, 0xbd, 0x80, 0x97, 0x80, 0x41, 0x65, 0x80, + 0x97, 0x80, 0xe5, 0x80, 0x97, 0x80, 0x40, 0xe7, + 0x00, 0x03, 0x08, 0x81, 0x88, 0x81, 0xe6, 0x80, + 0x97, 0x80, 0xf6, 0x80, 0x8e, 0x80, 0x49, 0x34, + 0x80, 0x9d, 0x80, 0x43, 0xff, 0x04, 0x00, 0x04, + 0x81, 0xe4, 0x80, 0xc6, 0x81, 0x44, 0x17, 0x80, + 0x50, 0x20, 0x81, 0x60, 0x79, 0x22, 0x80, 0xeb, + 0x80, 0x60, 0x55, 0xdc, 0x81, 0x52, 0x1f, 0x80, + 0xf3, 0x80, 0x41, 0x07, 0x80, 0x8d, 0x80, 0x88, + 0x80, 0xdf, 0x80, 0x88, 0x01, 0x00, 0x14, 0x80, + 0x40, 0xdf, 0x80, 0x8b, 0x80, 0x40, 0xf0, 0x80, + 0x41, 0x05, 0x80, 0x42, 0x78, 0x80, 0x8b, 0x80, + 0x46, 0x02, 0x80, 0x60, 0x50, 0xad, 0x81, 0x60, + 0x61, 0x72, 0x0d, 0x85, 0x6c, 0x2e, 0xac, 0xdf, +}; + +static const uint8_t unicode_prop_Other_Default_Ignorable_Code_Point_table[32] = { + 0x43, 0x4e, 0x80, 0x4e, 0x0e, 0x81, 0x46, 0x52, + 0x81, 0x48, 0xae, 0x80, 0x50, 0xfd, 0x80, 0x60, + 0xce, 0x3a, 0x80, 0xce, 0x88, 0x6d, 0x00, 0x06, + 0x00, 0x9d, 0xdf, 0xff, 0x40, 0xef, 0x4e, 0x0f, +}; + +static const uint8_t unicode_prop_Other_ID_Start_table[11] = { + 0x58, 0x84, 0x81, 0x48, 0x90, 0x80, 0x94, 0x80, + 0x4f, 0x6b, 0x81, +}; + +static const uint8_t unicode_prop_Other_ID_Continue_table[22] = { + 0x40, 0xb6, 0x80, 0x42, 0xce, 0x80, 0x4f, 0xe0, + 0x88, 0x46, 0x67, 0x80, 0x46, 0x30, 0x81, 0x50, + 0xec, 0x80, 0x60, 0xce, 0x68, 0x80, +}; + +static const uint8_t unicode_prop_Prepended_Concatenation_Mark_table[19] = { + 0x45, 0xff, 0x85, 0x40, 0xd6, 0x80, 0xb0, 0x80, + 0x41, 0x7f, 0x81, 0xcf, 0x80, 0x61, 0x07, 0xd9, + 0x80, 0x8e, 0x80, +}; + +static const uint8_t unicode_prop_XID_Start1_table[31] = { + 0x43, 0x79, 0x80, 0x4a, 0xb7, 0x80, 0xfe, 0x80, + 0x60, 0x21, 0xe6, 0x81, 0x60, 0xcb, 0xc0, 0x85, + 0x41, 0x95, 0x81, 0xf3, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x80, 0x41, 0x1e, 0x81, +}; + +static const uint8_t unicode_prop_XID_Continue1_table[23] = { + 0x43, 0x79, 0x80, 0x60, 0x2d, 0x1f, 0x81, 0x60, + 0xcb, 0xc0, 0x85, 0x41, 0x95, 0x81, 0xf3, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, +}; + +static const uint8_t unicode_prop_Changes_When_Titlecased1_table[22] = { + 0x41, 0xc3, 0x08, 0x08, 0x81, 0xa4, 0x81, 0x4e, + 0xdc, 0xaa, 0x0a, 0x4e, 0x87, 0x3f, 0x3f, 0x87, + 0x8b, 0x80, 0x8e, 0x80, 0xae, 0x80, +}; + +static const uint8_t unicode_prop_Changes_When_Casefolded1_table[29] = { + 0x41, 0xef, 0x80, 0x41, 0x9e, 0x80, 0x9e, 0x80, + 0x5a, 0xe4, 0x83, 0x40, 0xb5, 0x00, 0x00, 0x00, + 0x80, 0xde, 0x06, 0x06, 0x80, 0x8a, 0x09, 0x81, + 0x89, 0x10, 0x81, 0x8d, 0x80, +}; + +static const uint8_t unicode_prop_Changes_When_NFKC_Casefolded1_table[450] = { + 0x40, 0x9f, 0x06, 0x00, 0x01, 0x00, 0x01, 0x12, + 0x10, 0x82, 0xf3, 0x80, 0x8b, 0x80, 0x40, 0x84, + 0x01, 0x01, 0x80, 0xa2, 0x01, 0x80, 0x40, 0xbb, + 0x88, 0x9e, 0x29, 0x84, 0xda, 0x08, 0x81, 0x89, + 0x80, 0xa3, 0x04, 0x02, 0x04, 0x08, 0x07, 0x80, + 0x9e, 0x80, 0xa0, 0x82, 0x9c, 0x80, 0x42, 0x28, + 0x80, 0xd7, 0x83, 0x42, 0xde, 0x87, 0xfb, 0x08, + 0x80, 0xd2, 0x01, 0x80, 0xa1, 0x11, 0x80, 0x40, + 0xfc, 0x81, 0x42, 0xd4, 0x80, 0xfe, 0x80, 0xa7, + 0x81, 0xad, 0x80, 0xb5, 0x80, 0x88, 0x03, 0x03, + 0x03, 0x80, 0x8b, 0x80, 0x88, 0x00, 0x26, 0x80, + 0x90, 0x80, 0x88, 0x03, 0x03, 0x03, 0x80, 0x8b, + 0x80, 0x41, 0x41, 0x80, 0xe1, 0x81, 0x46, 0x52, + 0x81, 0xd4, 0x84, 0x45, 0x1b, 0x10, 0x8a, 0x80, + 0x91, 0x80, 0x9b, 0x8c, 0x80, 0xa1, 0xa4, 0x40, + 0xd5, 0x83, 0x40, 0xb5, 0x00, 0x00, 0x00, 0x80, + 0x99, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, + 0xb7, 0x05, 0x00, 0x13, 0x05, 0x11, 0x02, 0x0c, + 0x11, 0x00, 0x00, 0x0c, 0x15, 0x05, 0x08, 0x8f, + 0x00, 0x20, 0x8b, 0x12, 0x2a, 0x08, 0x0b, 0x00, + 0x07, 0x82, 0x8c, 0x06, 0x92, 0x81, 0x9a, 0x80, + 0x8c, 0x8a, 0x80, 0xd6, 0x18, 0x10, 0x8a, 0x01, + 0x0c, 0x0a, 0x00, 0x10, 0x11, 0x02, 0x06, 0x05, + 0x1c, 0x85, 0x8f, 0x8f, 0x8f, 0x88, 0x80, 0x40, + 0xa1, 0x08, 0x81, 0x40, 0xf7, 0x81, 0x41, 0x34, + 0xd5, 0x99, 0x9a, 0x45, 0x20, 0x80, 0xe6, 0x82, + 0xe4, 0x80, 0x41, 0x9e, 0x81, 0x40, 0xf0, 0x80, + 0x41, 0x2e, 0x80, 0xd2, 0x80, 0x8b, 0x40, 0xd5, + 0xa9, 0x80, 0xb4, 0x00, 0x82, 0xdf, 0x09, 0x80, + 0xde, 0x80, 0xb0, 0xdd, 0x82, 0x8d, 0xdf, 0x9e, + 0x80, 0xa7, 0x87, 0xae, 0x80, 0x41, 0x7f, 0x60, + 0x72, 0x9b, 0x81, 0x40, 0xd1, 0x80, 0x40, 0x80, + 0x12, 0x81, 0x43, 0x61, 0x83, 0x88, 0x80, 0x60, + 0x4d, 0x95, 0x41, 0x0d, 0x08, 0x00, 0x81, 0x89, + 0x00, 0x00, 0x09, 0x82, 0xc3, 0x81, 0xe9, 0xc2, + 0x00, 0x97, 0x04, 0x00, 0x01, 0x01, 0x80, 0xeb, + 0xa0, 0x41, 0x6a, 0x91, 0xbf, 0x81, 0xb5, 0xa7, + 0x8c, 0x82, 0x99, 0x95, 0x94, 0x81, 0x8b, 0x80, + 0x92, 0x03, 0x1a, 0x00, 0x80, 0x40, 0x86, 0x08, + 0x80, 0x9f, 0x99, 0x40, 0x83, 0x15, 0x0d, 0x0d, + 0x0a, 0x16, 0x06, 0x80, 0x88, 0x47, 0x87, 0x20, + 0xa9, 0x80, 0x88, 0x60, 0xb4, 0xe4, 0x83, 0x50, + 0x31, 0xa3, 0x44, 0x63, 0x86, 0x8d, 0x87, 0xbf, + 0x85, 0x42, 0x3e, 0xd4, 0x80, 0xc6, 0x01, 0x08, + 0x09, 0x0b, 0x80, 0x8b, 0x00, 0x06, 0x80, 0xc0, + 0x03, 0x0f, 0x06, 0x80, 0x9b, 0x03, 0x04, 0x00, + 0x16, 0x80, 0x41, 0x53, 0x81, 0x41, 0x23, 0x81, + 0xb1, 0x48, 0x2f, 0xbd, 0x4d, 0x91, 0x18, 0x9a, + 0x01, 0x00, 0x08, 0x80, 0x89, 0x03, 0x00, 0x00, + 0x28, 0x18, 0x00, 0x00, 0x02, 0x01, 0x00, 0x08, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x0b, 0x06, + 0x03, 0x03, 0x00, 0x80, 0x89, 0x80, 0x90, 0x22, + 0x04, 0x80, 0x90, 0x42, 0x43, 0x8a, 0x84, 0x9e, + 0x80, 0x9f, 0x99, 0x82, 0xa2, 0x80, 0xee, 0x82, + 0x8c, 0xab, 0x83, 0x88, 0x31, 0x49, 0x9d, 0x89, + 0x60, 0xfc, 0x05, 0x42, 0x1d, 0x6b, 0x05, 0xe1, + 0x4f, 0xff, +}; + +static const uint8_t unicode_prop_ASCII_Hex_Digit_table[5] = { + 0xaf, 0x89, 0x35, 0x99, 0x85, +}; + +static const uint8_t unicode_prop_Bidi_Control_table[10] = { + 0x46, 0x1b, 0x80, 0x59, 0xf0, 0x81, 0x99, 0x84, + 0xb6, 0x83, +}; + +static const uint8_t unicode_prop_Dash_table[58] = { + 0xac, 0x80, 0x45, 0x5b, 0x80, 0xb2, 0x80, 0x4e, + 0x40, 0x80, 0x44, 0x04, 0x80, 0x48, 0x08, 0x85, + 0xbc, 0x80, 0xa6, 0x80, 0x8e, 0x80, 0x41, 0x85, + 0x80, 0x4c, 0x03, 0x01, 0x80, 0x9e, 0x0b, 0x80, + 0x9b, 0x80, 0x41, 0xbd, 0x80, 0x92, 0x80, 0xee, + 0x80, 0x60, 0xcd, 0x8f, 0x81, 0xa4, 0x80, 0x89, + 0x80, 0x40, 0xa8, 0x80, 0x4e, 0x5f, 0x80, 0x41, + 0x3d, 0x80, +}; + +static const uint8_t unicode_prop_Deprecated_table[23] = { + 0x41, 0x48, 0x80, 0x45, 0x28, 0x80, 0x49, 0x02, + 0x00, 0x80, 0x48, 0x28, 0x81, 0x48, 0xc4, 0x85, + 0x42, 0xb8, 0x81, 0x6d, 0xdc, 0xd5, 0x80, +}; + +static const uint8_t unicode_prop_Diacritic_table[438] = { + 0xdd, 0x00, 0x80, 0xc6, 0x05, 0x03, 0x01, 0x81, + 0x41, 0xf6, 0x40, 0x9e, 0x07, 0x25, 0x90, 0x0b, + 0x80, 0x88, 0x81, 0x40, 0xfc, 0x84, 0x40, 0xd0, + 0x80, 0xb6, 0x90, 0x80, 0x9a, 0x00, 0x01, 0x00, + 0x40, 0x85, 0x3b, 0x81, 0x40, 0x85, 0x0b, 0x0a, + 0x82, 0xc2, 0x9a, 0xda, 0x8a, 0xb9, 0x8a, 0xa1, + 0x81, 0xfd, 0x87, 0xa8, 0x89, 0x8f, 0x9b, 0xbc, + 0x80, 0x8f, 0x02, 0x83, 0x9b, 0x80, 0xc9, 0x80, + 0x8f, 0x80, 0xed, 0x80, 0x8f, 0x80, 0xed, 0x80, + 0x8f, 0x80, 0xae, 0x82, 0xbb, 0x80, 0x8f, 0x06, + 0x80, 0xf6, 0x80, 0xed, 0x80, 0x8f, 0x80, 0xed, + 0x80, 0x8f, 0x80, 0xec, 0x81, 0x8f, 0x80, 0xfb, + 0x80, 0xee, 0x80, 0x8b, 0x28, 0x80, 0xea, 0x80, + 0x8c, 0x84, 0xca, 0x81, 0x9a, 0x00, 0x00, 0x03, + 0x81, 0xc1, 0x10, 0x81, 0xbd, 0x80, 0xef, 0x00, + 0x81, 0xa7, 0x0b, 0x84, 0x98, 0x30, 0x80, 0x89, + 0x81, 0x42, 0xc0, 0x82, 0x43, 0xb3, 0x81, 0x9d, + 0x80, 0x40, 0x93, 0x8a, 0x88, 0x80, 0x41, 0x5a, + 0x82, 0x41, 0x23, 0x80, 0x93, 0x39, 0x80, 0xaf, + 0x8e, 0x81, 0x8a, 0xe7, 0x80, 0x8e, 0x80, 0xa5, + 0x88, 0xb5, 0x81, 0xb9, 0x80, 0x8a, 0x81, 0xc1, + 0x81, 0xbf, 0x85, 0xd1, 0x98, 0x18, 0x28, 0x0a, + 0xb1, 0xbe, 0xd8, 0x8b, 0xa4, 0x8a, 0x41, 0xbc, + 0x00, 0x82, 0x8a, 0x82, 0x8c, 0x82, 0x8c, 0x82, + 0x8c, 0x81, 0x4c, 0xef, 0x82, 0x41, 0x3c, 0x80, + 0x41, 0xf9, 0x85, 0xe8, 0x83, 0xde, 0x80, 0x60, + 0x75, 0x71, 0x80, 0x8b, 0x08, 0x80, 0x9b, 0x81, + 0xd1, 0x81, 0x8d, 0xa1, 0xe5, 0x82, 0xec, 0x81, + 0x8b, 0x80, 0xa4, 0x80, 0x40, 0x96, 0x80, 0x9a, + 0x91, 0xb8, 0x83, 0xa3, 0x80, 0xde, 0x80, 0x8b, + 0x80, 0xa3, 0x80, 0x40, 0x94, 0x82, 0xc0, 0x83, + 0xb2, 0x80, 0xe3, 0x84, 0x88, 0x82, 0xff, 0x81, + 0x60, 0x4f, 0x2f, 0x80, 0x43, 0x00, 0x8f, 0x41, + 0x0d, 0x00, 0x80, 0xae, 0x80, 0xac, 0x81, 0xc2, + 0x80, 0x42, 0xfb, 0x80, 0x44, 0x9e, 0x28, 0xa9, + 0x80, 0x88, 0x42, 0x7c, 0x13, 0x80, 0x40, 0xa4, + 0x81, 0x42, 0x3a, 0x85, 0xa5, 0x80, 0x99, 0x84, + 0x41, 0x8e, 0x82, 0xc5, 0x8a, 0xb0, 0x83, 0x40, + 0xbf, 0x80, 0xa8, 0x80, 0xc7, 0x81, 0xf7, 0x81, + 0xbd, 0x80, 0xcb, 0x80, 0x88, 0x82, 0xe7, 0x81, + 0x40, 0xb1, 0x81, 0xcf, 0x81, 0x8f, 0x80, 0x97, + 0x32, 0x84, 0xd8, 0x10, 0x81, 0x8c, 0x81, 0xde, + 0x02, 0x80, 0xfa, 0x81, 0x40, 0xfa, 0x81, 0xfd, + 0x80, 0xf5, 0x81, 0xf2, 0x80, 0x41, 0x0c, 0x81, + 0x41, 0x01, 0x0b, 0x80, 0x40, 0x9b, 0x80, 0xd2, + 0x80, 0x91, 0x80, 0xd0, 0x80, 0x41, 0xa4, 0x80, + 0x41, 0x01, 0x00, 0x81, 0xd0, 0x80, 0x41, 0xa8, + 0x81, 0x96, 0x80, 0x54, 0xeb, 0x8e, 0x60, 0x2c, + 0xd8, 0x80, 0x49, 0xbf, 0x84, 0xba, 0x86, 0x42, + 0x33, 0x81, 0x42, 0x21, 0x90, 0xcf, 0x81, 0x60, + 0x3f, 0xfd, 0x18, 0x30, 0x81, 0x5f, 0x00, 0xad, + 0x81, 0x96, 0x42, 0x1f, 0x12, 0x2f, 0x39, 0x86, + 0x9d, 0x83, 0x4e, 0x81, 0xbd, 0x40, 0xc1, 0x86, + 0x41, 0x76, 0x80, 0xbc, 0x83, 0x42, 0xfd, 0x81, + 0x42, 0xdf, 0x86, 0xec, 0x10, 0x82, +}; + +static const uint8_t unicode_prop_Extender_table[111] = { + 0x40, 0xb6, 0x80, 0x42, 0x17, 0x81, 0x43, 0x6d, + 0x80, 0x41, 0xb8, 0x80, 0x42, 0x75, 0x80, 0x40, + 0x88, 0x80, 0xd8, 0x80, 0x42, 0xef, 0x80, 0xfe, + 0x80, 0x49, 0x42, 0x80, 0xb7, 0x80, 0x42, 0x62, + 0x80, 0x41, 0x8d, 0x80, 0xc3, 0x80, 0x53, 0x88, + 0x80, 0xaa, 0x84, 0xe6, 0x81, 0xdc, 0x82, 0x60, + 0x6f, 0x15, 0x80, 0x45, 0xf5, 0x80, 0x43, 0xc1, + 0x80, 0x95, 0x80, 0x40, 0x88, 0x80, 0xeb, 0x80, + 0x94, 0x81, 0x60, 0x54, 0x7a, 0x80, 0x48, 0x0f, + 0x81, 0x45, 0xca, 0x80, 0x9a, 0x03, 0x80, 0x44, + 0xc6, 0x80, 0x41, 0x24, 0x80, 0xf3, 0x81, 0x41, + 0xf1, 0x82, 0x44, 0xce, 0x80, 0x60, 0x50, 0xa8, + 0x81, 0x44, 0x9b, 0x08, 0x80, 0x60, 0x71, 0x57, + 0x81, 0x44, 0xb0, 0x80, 0x43, 0x53, 0x82, +}; + +static const uint8_t unicode_prop_Hex_Digit_table[12] = { + 0xaf, 0x89, 0x35, 0x99, 0x85, 0x60, 0xfe, 0xa8, + 0x89, 0x35, 0x99, 0x85, +}; + +static const uint8_t unicode_prop_IDS_Unary_Operator_table[4] = { + 0x60, 0x2f, 0xfd, 0x81, +}; + +static const uint8_t unicode_prop_IDS_Binary_Operator_table[8] = { + 0x60, 0x2f, 0xef, 0x09, 0x89, 0x41, 0xf0, 0x80, +}; + +static const uint8_t unicode_prop_IDS_Trinary_Operator_table[4] = { + 0x60, 0x2f, 0xf1, 0x81, +}; + +static const uint8_t unicode_prop_Ideographic_table[72] = { + 0x60, 0x30, 0x05, 0x81, 0x98, 0x88, 0x8d, 0x82, + 0x43, 0xc4, 0x59, 0xbf, 0xbf, 0x60, 0x51, 0xff, + 0x60, 0x58, 0xff, 0x41, 0x6d, 0x81, 0xe9, 0x60, + 0x75, 0x09, 0x80, 0x9a, 0x57, 0xf7, 0x87, 0x44, + 0xd5, 0xa8, 0x89, 0x60, 0x24, 0x66, 0x41, 0x8b, + 0x60, 0x4d, 0x03, 0x60, 0xa6, 0xdf, 0x9f, 0x50, + 0x39, 0x85, 0x40, 0xdd, 0x81, 0x56, 0x81, 0x8d, + 0x5d, 0x30, 0x8e, 0x42, 0x6d, 0x49, 0xa1, 0x42, + 0x1d, 0x45, 0xe1, 0x53, 0x4a, 0x84, 0x50, 0x5f, +}; + +static const uint8_t unicode_prop_Join_Control_table[4] = { + 0x60, 0x20, 0x0b, 0x81, +}; + +static const uint8_t unicode_prop_Logical_Order_Exception_table[15] = { + 0x4e, 0x3f, 0x84, 0xfa, 0x84, 0x4a, 0xef, 0x11, + 0x80, 0x60, 0x90, 0xf9, 0x09, 0x00, 0x81, +}; + +static const uint8_t unicode_prop_Modifier_Combining_Mark_table[16] = { + 0x46, 0x53, 0x09, 0x80, 0x40, 0x82, 0x05, 0x02, + 0x81, 0x41, 0xe0, 0x08, 0x12, 0x80, 0x9e, 0x80, +}; + +static const uint8_t unicode_prop_Noncharacter_Code_Point_table[71] = { + 0x60, 0xfd, 0xcf, 0x9f, 0x42, 0x0d, 0x81, 0x60, + 0xff, 0xfd, 0x81, 0x60, 0xff, 0xfd, 0x81, 0x60, + 0xff, 0xfd, 0x81, 0x60, 0xff, 0xfd, 0x81, 0x60, + 0xff, 0xfd, 0x81, 0x60, 0xff, 0xfd, 0x81, 0x60, + 0xff, 0xfd, 0x81, 0x60, 0xff, 0xfd, 0x81, 0x60, + 0xff, 0xfd, 0x81, 0x60, 0xff, 0xfd, 0x81, 0x60, + 0xff, 0xfd, 0x81, 0x60, 0xff, 0xfd, 0x81, 0x60, + 0xff, 0xfd, 0x81, 0x60, 0xff, 0xfd, 0x81, 0x60, + 0xff, 0xfd, 0x81, 0x60, 0xff, 0xfd, 0x81, +}; + +static const uint8_t unicode_prop_Pattern_Syntax_table[58] = { + 0xa0, 0x8e, 0x89, 0x86, 0x99, 0x18, 0x80, 0x99, + 0x83, 0xa1, 0x30, 0x00, 0x08, 0x00, 0x0b, 0x03, + 0x02, 0x80, 0x96, 0x80, 0x9e, 0x80, 0x5f, 0x17, + 0x97, 0x87, 0x8e, 0x81, 0x92, 0x80, 0x89, 0x41, + 0x30, 0x42, 0xcf, 0x40, 0x9f, 0x42, 0x75, 0x9d, + 0x44, 0x6b, 0x41, 0xff, 0xff, 0x41, 0x80, 0x13, + 0x98, 0x8e, 0x80, 0x60, 0xcd, 0x0c, 0x81, 0x41, + 0x04, 0x81, +}; + +static const uint8_t unicode_prop_Pattern_White_Space_table[11] = { + 0x88, 0x84, 0x91, 0x80, 0xe3, 0x80, 0x5f, 0x87, + 0x81, 0x97, 0x81, +}; + +static const uint8_t unicode_prop_Quotation_Mark_table[31] = { + 0xa1, 0x03, 0x80, 0x40, 0x82, 0x80, 0x8e, 0x80, + 0x5f, 0x5b, 0x87, 0x98, 0x81, 0x4e, 0x06, 0x80, + 0x41, 0xc8, 0x83, 0x8c, 0x82, 0x60, 0xce, 0x20, + 0x83, 0x40, 0xbc, 0x03, 0x80, 0xd9, 0x81, +}; + +static const uint8_t unicode_prop_Radical_table[9] = { + 0x60, 0x2e, 0x7f, 0x99, 0x80, 0xd8, 0x8b, 0x40, + 0xd5, +}; + +static const uint8_t unicode_prop_Regional_Indicator_table[4] = { + 0x61, 0xf1, 0xe5, 0x99, +}; + +static const uint8_t unicode_prop_Sentence_Terminal_table[213] = { + 0xa0, 0x80, 0x8b, 0x80, 0x8f, 0x80, 0x45, 0x48, + 0x80, 0x40, 0x92, 0x82, 0x40, 0xb3, 0x80, 0xaa, + 0x82, 0x40, 0xf5, 0x80, 0xbc, 0x00, 0x02, 0x81, + 0x41, 0x24, 0x81, 0x46, 0xe3, 0x81, 0x43, 0x15, + 0x03, 0x81, 0x43, 0x04, 0x80, 0x40, 0xc5, 0x81, + 0x40, 0x9c, 0x81, 0xac, 0x04, 0x80, 0x41, 0x39, + 0x81, 0x41, 0x61, 0x83, 0x40, 0xa1, 0x81, 0x89, + 0x09, 0x81, 0x9c, 0x82, 0x40, 0xba, 0x81, 0xc0, + 0x81, 0x43, 0xa3, 0x80, 0x96, 0x81, 0x88, 0x82, + 0x4c, 0xae, 0x82, 0x41, 0x31, 0x80, 0x8c, 0x80, + 0x95, 0x81, 0x41, 0xac, 0x80, 0x60, 0x74, 0xfb, + 0x80, 0x41, 0x0d, 0x81, 0x40, 0xe2, 0x02, 0x80, + 0x41, 0x7d, 0x81, 0xd5, 0x81, 0xde, 0x80, 0x40, + 0x97, 0x81, 0x40, 0x92, 0x82, 0x40, 0x8f, 0x81, + 0x40, 0xf8, 0x80, 0x60, 0x52, 0x25, 0x01, 0x81, + 0xba, 0x02, 0x81, 0x40, 0xa8, 0x80, 0x8b, 0x80, + 0x8f, 0x80, 0xc0, 0x80, 0x4a, 0xf3, 0x81, 0x44, + 0xfc, 0x84, 0xab, 0x83, 0x40, 0xbc, 0x81, 0xf4, + 0x83, 0xfe, 0x82, 0x40, 0x80, 0x0d, 0x80, 0x8f, + 0x81, 0xd7, 0x08, 0x81, 0xeb, 0x80, 0x41, 0x29, + 0x81, 0xf4, 0x81, 0x41, 0x74, 0x0c, 0x8e, 0xe8, + 0x81, 0x40, 0xf8, 0x82, 0x42, 0x04, 0x00, 0x80, + 0x40, 0xfa, 0x81, 0xd6, 0x81, 0x41, 0xa3, 0x81, + 0x42, 0xb3, 0x81, 0xc9, 0x81, 0x60, 0x4b, 0x28, + 0x81, 0x40, 0x84, 0x80, 0xc0, 0x81, 0x8a, 0x80, + 0x42, 0x28, 0x81, 0x41, 0x27, 0x80, 0x60, 0x4e, + 0x05, 0x80, 0x5d, 0xe7, 0x80, +}; + +static const uint8_t unicode_prop_Soft_Dotted_table[79] = { + 0xe8, 0x81, 0x40, 0xc3, 0x80, 0x41, 0x18, 0x80, + 0x9d, 0x80, 0xb3, 0x80, 0x93, 0x80, 0x41, 0x3f, + 0x80, 0xe1, 0x00, 0x80, 0x59, 0x08, 0x80, 0xb2, + 0x80, 0x8c, 0x02, 0x80, 0x40, 0x83, 0x80, 0x40, + 0x9c, 0x80, 0x41, 0xa4, 0x80, 0x40, 0xd5, 0x81, + 0x4b, 0x31, 0x80, 0x61, 0xa7, 0xa4, 0x81, 0xb1, + 0x81, 0xb1, 0x81, 0xb1, 0x81, 0xb1, 0x81, 0xb1, + 0x81, 0xb1, 0x81, 0xb1, 0x81, 0xb1, 0x81, 0xb1, + 0x81, 0xb1, 0x81, 0xb1, 0x81, 0xb1, 0x81, 0x48, + 0x85, 0x80, 0x41, 0x30, 0x81, 0x99, 0x80, +}; + +static const uint8_t unicode_prop_Terminal_Punctuation_table[264] = { + 0xa0, 0x80, 0x89, 0x00, 0x80, 0x8a, 0x0a, 0x80, + 0x43, 0x3d, 0x07, 0x80, 0x42, 0x00, 0x80, 0xb8, + 0x80, 0xc7, 0x80, 0x8d, 0x00, 0x82, 0x40, 0xb3, + 0x80, 0xaa, 0x8a, 0x00, 0x40, 0xea, 0x81, 0xb5, + 0x28, 0x87, 0x9e, 0x80, 0x41, 0x04, 0x81, 0x44, + 0xf3, 0x81, 0x40, 0xab, 0x03, 0x85, 0x41, 0x36, + 0x81, 0x43, 0x14, 0x87, 0x43, 0x04, 0x80, 0xfb, + 0x82, 0xc6, 0x81, 0x40, 0x9c, 0x12, 0x80, 0xa6, + 0x19, 0x81, 0x41, 0x39, 0x81, 0x41, 0x61, 0x83, + 0x40, 0xa1, 0x81, 0x89, 0x08, 0x82, 0x9c, 0x82, + 0x40, 0xba, 0x84, 0xbd, 0x81, 0x43, 0xa3, 0x80, + 0x96, 0x81, 0x88, 0x82, 0x4c, 0xae, 0x82, 0x41, + 0x31, 0x80, 0x8c, 0x03, 0x80, 0x89, 0x00, 0x0a, + 0x81, 0x41, 0xab, 0x81, 0x60, 0x74, 0xfa, 0x81, + 0x41, 0x0c, 0x82, 0x40, 0xe2, 0x84, 0x41, 0x7d, + 0x81, 0xd5, 0x81, 0xde, 0x80, 0x40, 0x96, 0x82, + 0x40, 0x92, 0x82, 0xfe, 0x80, 0x8f, 0x81, 0x40, + 0xf8, 0x80, 0x60, 0x52, 0x25, 0x01, 0x81, 0xb8, + 0x10, 0x83, 0x40, 0xa8, 0x80, 0x89, 0x00, 0x80, + 0x8a, 0x0a, 0x80, 0xc0, 0x01, 0x80, 0x44, 0x39, + 0x80, 0xaf, 0x80, 0x44, 0x85, 0x80, 0x40, 0xc6, + 0x80, 0x41, 0x35, 0x81, 0x40, 0x97, 0x85, 0xc3, + 0x85, 0xd8, 0x83, 0x43, 0xb7, 0x84, 0xab, 0x83, + 0x40, 0xbc, 0x86, 0xef, 0x83, 0xfe, 0x82, 0x40, + 0x80, 0x0d, 0x80, 0x8f, 0x81, 0xd7, 0x84, 0xeb, + 0x80, 0x41, 0x29, 0x81, 0xf4, 0x82, 0x8b, 0x81, + 0x41, 0x65, 0x1a, 0x8e, 0xe8, 0x81, 0x40, 0xf8, + 0x82, 0x42, 0x04, 0x00, 0x80, 0x40, 0xfa, 0x81, + 0xd6, 0x0b, 0x81, 0x41, 0x9d, 0x82, 0xac, 0x80, + 0x42, 0x84, 0x81, 0xc9, 0x81, 0x45, 0x2a, 0x84, + 0x60, 0x45, 0xf8, 0x81, 0x40, 0x84, 0x80, 0xc0, + 0x82, 0x89, 0x80, 0x42, 0x28, 0x81, 0x41, 0x26, + 0x81, 0x60, 0x4e, 0x05, 0x80, 0x5d, 0xe6, 0x83, +}; + +static const uint8_t unicode_prop_Unified_Ideograph_table[48] = { + 0x60, 0x33, 0xff, 0x59, 0xbf, 0xbf, 0x60, 0x51, + 0xff, 0x60, 0x5a, 0x0d, 0x08, 0x00, 0x81, 0x89, + 0x00, 0x00, 0x09, 0x82, 0x61, 0x05, 0xd5, 0x60, + 0xa6, 0xdf, 0x9f, 0x50, 0x39, 0x85, 0x40, 0xdd, + 0x81, 0x56, 0x81, 0x8d, 0x5d, 0x30, 0x8e, 0x42, + 0x6d, 0x51, 0xa1, 0x53, 0x4a, 0x84, 0x50, 0x5f, +}; + +static const uint8_t unicode_prop_Variation_Selector_table[13] = { + 0x58, 0x0a, 0x10, 0x80, 0x60, 0xe5, 0xef, 0x8f, + 0x6d, 0x02, 0xef, 0x40, 0xef, +}; + +static const uint8_t unicode_prop_Bidi_Mirrored_table[173] = { + 0xa7, 0x81, 0x91, 0x00, 0x80, 0x9b, 0x00, 0x80, + 0x9c, 0x00, 0x80, 0xac, 0x80, 0x8e, 0x80, 0x4e, + 0x7d, 0x83, 0x47, 0x5c, 0x81, 0x49, 0x9b, 0x81, + 0x89, 0x81, 0xb5, 0x81, 0x8d, 0x81, 0x40, 0xb0, + 0x80, 0x40, 0xbf, 0x1a, 0x2a, 0x02, 0x0a, 0x18, + 0x18, 0x00, 0x03, 0x88, 0x20, 0x80, 0x91, 0x23, + 0x88, 0x08, 0x00, 0x38, 0x9f, 0x0b, 0x20, 0x88, + 0x09, 0x92, 0x21, 0x88, 0x21, 0x0b, 0x97, 0x81, + 0x8f, 0x3b, 0x93, 0x0e, 0x81, 0x44, 0x3c, 0x8d, + 0xc9, 0x01, 0x18, 0x08, 0x14, 0x1c, 0x12, 0x8d, + 0x41, 0x92, 0x95, 0x0d, 0x80, 0x8d, 0x38, 0x35, + 0x10, 0x1c, 0x01, 0x0c, 0x18, 0x02, 0x09, 0x89, + 0x29, 0x81, 0x8b, 0x92, 0x03, 0x08, 0x00, 0x08, + 0x03, 0x21, 0x2a, 0x97, 0x81, 0x8a, 0x0b, 0x18, + 0x09, 0x0b, 0xaa, 0x0f, 0x80, 0xa7, 0x20, 0x00, + 0x14, 0x22, 0x18, 0x14, 0x00, 0x40, 0xff, 0x80, + 0x42, 0x02, 0x1a, 0x08, 0x81, 0x8d, 0x09, 0x89, + 0xaa, 0x87, 0x41, 0xaa, 0x89, 0x0f, 0x60, 0xce, + 0x3c, 0x2c, 0x81, 0x40, 0xa1, 0x81, 0x91, 0x00, + 0x80, 0x9b, 0x00, 0x80, 0x9c, 0x00, 0x00, 0x08, + 0x81, 0x60, 0xd7, 0x76, 0x80, 0xb8, 0x80, 0xb8, + 0x80, 0xb8, 0x80, 0xb8, 0x80, +}; + +static const uint8_t unicode_prop_Emoji_table[238] = { + 0xa2, 0x05, 0x04, 0x89, 0xee, 0x03, 0x80, 0x5f, + 0x8c, 0x80, 0x8b, 0x80, 0x40, 0xd7, 0x80, 0x95, + 0x80, 0xd9, 0x85, 0x8e, 0x81, 0x41, 0x6e, 0x81, + 0x8b, 0x80, 0x40, 0xa5, 0x80, 0x98, 0x8a, 0x1a, + 0x40, 0xc6, 0x80, 0x40, 0xe6, 0x81, 0x89, 0x80, + 0x88, 0x80, 0xb9, 0x18, 0x84, 0x88, 0x01, 0x01, + 0x09, 0x03, 0x01, 0x00, 0x09, 0x02, 0x02, 0x0f, + 0x14, 0x00, 0x04, 0x8b, 0x8a, 0x09, 0x00, 0x08, + 0x80, 0x91, 0x01, 0x81, 0x91, 0x28, 0x00, 0x0a, + 0x0c, 0x01, 0x0b, 0x81, 0x8a, 0x0c, 0x09, 0x04, + 0x08, 0x00, 0x81, 0x93, 0x0c, 0x28, 0x19, 0x03, + 0x01, 0x01, 0x28, 0x01, 0x00, 0x00, 0x05, 0x02, + 0x05, 0x80, 0x89, 0x81, 0x8e, 0x01, 0x03, 0x00, + 0x03, 0x10, 0x80, 0x8a, 0x81, 0xaf, 0x82, 0x88, + 0x80, 0x8d, 0x80, 0x8d, 0x80, 0x41, 0x73, 0x81, + 0x41, 0xce, 0x82, 0x92, 0x81, 0xb2, 0x03, 0x80, + 0x44, 0xd9, 0x80, 0x8b, 0x80, 0x42, 0x58, 0x00, + 0x80, 0x61, 0xbd, 0x69, 0x80, 0x40, 0xc9, 0x80, + 0x40, 0x9f, 0x81, 0x8b, 0x81, 0x8d, 0x01, 0x89, + 0xca, 0x99, 0x01, 0x96, 0x80, 0x93, 0x01, 0x88, + 0x94, 0x81, 0x40, 0xad, 0xa1, 0x81, 0xef, 0x09, + 0x02, 0x81, 0xd2, 0x0a, 0x80, 0x41, 0x06, 0x80, + 0xbe, 0x8a, 0x28, 0x97, 0x31, 0x0f, 0x8b, 0x01, + 0x19, 0x03, 0x81, 0x8c, 0x09, 0x07, 0x81, 0x88, + 0x04, 0x82, 0x8b, 0x17, 0x11, 0x00, 0x03, 0x05, + 0x02, 0x05, 0xd5, 0xaf, 0xc5, 0x27, 0x0a, 0x83, + 0x89, 0x10, 0x01, 0x10, 0x81, 0x89, 0x40, 0xe2, + 0x8b, 0x18, 0x41, 0x1a, 0xae, 0x80, 0x89, 0x80, + 0x40, 0xb8, 0xef, 0x8c, 0x82, 0x89, 0x84, 0xb7, + 0x86, 0x8e, 0x81, 0x8a, 0x85, 0x88, +}; + +static const uint8_t unicode_prop_Emoji_Component_table[28] = { + 0xa2, 0x05, 0x04, 0x89, 0x5f, 0xd2, 0x80, 0x40, + 0xd4, 0x80, 0x60, 0xdd, 0x2a, 0x80, 0x60, 0xf3, + 0xd5, 0x99, 0x41, 0xfa, 0x84, 0x45, 0xaf, 0x83, + 0x6c, 0x06, 0x6b, 0xdf, +}; + +static const uint8_t unicode_prop_Emoji_Modifier_table[4] = { + 0x61, 0xf3, 0xfa, 0x84, +}; + +static const uint8_t unicode_prop_Emoji_Modifier_Base_table[71] = { + 0x60, 0x26, 0x1c, 0x80, 0x40, 0xda, 0x80, 0x8f, + 0x83, 0x61, 0xcc, 0x76, 0x80, 0xbb, 0x11, 0x01, + 0x82, 0xf4, 0x09, 0x8a, 0x94, 0x92, 0x10, 0x1a, + 0x02, 0x30, 0x00, 0x97, 0x80, 0x40, 0xc8, 0x0b, + 0x80, 0x94, 0x03, 0x81, 0x40, 0xad, 0x12, 0x84, + 0xd2, 0x80, 0x8f, 0x82, 0x88, 0x80, 0x8a, 0x80, + 0x42, 0x3e, 0x01, 0x07, 0x3d, 0x80, 0x88, 0x89, + 0x0a, 0xb7, 0x80, 0xbc, 0x08, 0x08, 0x80, 0x90, + 0x10, 0x8c, 0x40, 0xe4, 0x82, 0xa9, 0x88, +}; + +static const uint8_t unicode_prop_Emoji_Presentation_table[144] = { + 0x60, 0x23, 0x19, 0x81, 0x40, 0xcc, 0x1a, 0x01, + 0x80, 0x42, 0x08, 0x81, 0x94, 0x81, 0xb1, 0x8b, + 0xaa, 0x80, 0x92, 0x80, 0x8c, 0x07, 0x81, 0x90, + 0x0c, 0x0f, 0x04, 0x80, 0x94, 0x06, 0x08, 0x03, + 0x01, 0x06, 0x03, 0x81, 0x9b, 0x80, 0xa2, 0x00, + 0x03, 0x10, 0x80, 0xbc, 0x82, 0x97, 0x80, 0x8d, + 0x80, 0x43, 0x5a, 0x81, 0xb2, 0x03, 0x80, 0x61, + 0xc4, 0xad, 0x80, 0x40, 0xc9, 0x80, 0x40, 0xbd, + 0x01, 0x89, 0xca, 0x99, 0x00, 0x97, 0x80, 0x93, + 0x01, 0x20, 0x82, 0x94, 0x81, 0x40, 0xad, 0xa0, + 0x8b, 0x88, 0x80, 0xc5, 0x80, 0x95, 0x8b, 0xaa, + 0x1c, 0x8b, 0x90, 0x10, 0x82, 0xc6, 0x00, 0x80, + 0x40, 0xba, 0x81, 0xbe, 0x8c, 0x18, 0x97, 0x91, + 0x80, 0x99, 0x81, 0x8c, 0x80, 0xd5, 0xd4, 0xaf, + 0xc5, 0x28, 0x12, 0x0a, 0x1b, 0x8a, 0x0e, 0x88, + 0x40, 0xe2, 0x8b, 0x18, 0x41, 0x1a, 0xae, 0x80, + 0x89, 0x80, 0x40, 0xb8, 0xef, 0x8c, 0x82, 0x89, + 0x84, 0xb7, 0x86, 0x8e, 0x81, 0x8a, 0x85, 0x88, +}; + +static const uint8_t unicode_prop_Extended_Pictographic_table[156] = { + 0x40, 0xa8, 0x03, 0x80, 0x5f, 0x8c, 0x80, 0x8b, + 0x80, 0x40, 0xd7, 0x80, 0x95, 0x80, 0xd9, 0x85, + 0x8e, 0x81, 0x41, 0x6e, 0x81, 0x8b, 0x80, 0xde, + 0x80, 0xc5, 0x80, 0x98, 0x8a, 0x1a, 0x40, 0xc6, + 0x80, 0x40, 0xe6, 0x81, 0x89, 0x80, 0x88, 0x80, + 0xb9, 0x18, 0x28, 0x8b, 0x80, 0xf1, 0x89, 0xf5, + 0x81, 0x8a, 0x00, 0x00, 0x28, 0x10, 0x28, 0x89, + 0x81, 0x8e, 0x01, 0x03, 0x00, 0x03, 0x10, 0x80, + 0x8a, 0x84, 0xac, 0x82, 0x88, 0x80, 0x8d, 0x80, + 0x8d, 0x80, 0x41, 0x73, 0x81, 0x41, 0xce, 0x82, + 0x92, 0x81, 0xb2, 0x03, 0x80, 0x44, 0xd9, 0x80, + 0x8b, 0x80, 0x42, 0x58, 0x00, 0x80, 0x61, 0xbd, + 0x65, 0x40, 0xff, 0x8c, 0x82, 0x9e, 0x80, 0xbb, + 0x85, 0x8b, 0x81, 0x8d, 0x01, 0x89, 0x91, 0xb8, + 0x9a, 0x8e, 0x89, 0x80, 0x93, 0x01, 0x88, 0x03, + 0x88, 0x41, 0xb1, 0x84, 0x41, 0x3d, 0x87, 0x41, + 0x09, 0xaf, 0xff, 0xf3, 0x8b, 0xd4, 0xaa, 0x8b, + 0x83, 0xb7, 0x87, 0x89, 0x85, 0xa7, 0x87, 0x9d, + 0xd1, 0x8b, 0xae, 0x80, 0x89, 0x80, 0x41, 0xb8, + 0x40, 0xff, 0x43, 0xfd, +}; + +static const uint8_t unicode_prop_Default_Ignorable_Code_Point_table[51] = { + 0x40, 0xac, 0x80, 0x42, 0xa0, 0x80, 0x42, 0xcb, + 0x80, 0x4b, 0x41, 0x81, 0x46, 0x52, 0x81, 0xd4, + 0x84, 0x47, 0xfa, 0x84, 0x99, 0x84, 0xb0, 0x8f, + 0x50, 0xf3, 0x80, 0x60, 0xcc, 0x9a, 0x8f, 0x40, + 0xee, 0x80, 0x40, 0x9f, 0x80, 0xce, 0x88, 0x60, + 0xbc, 0xa6, 0x83, 0x54, 0xce, 0x87, 0x6c, 0x2e, + 0x84, 0x4f, 0xff, +}; + +typedef enum { + UNICODE_PROP_Hyphen, + UNICODE_PROP_Other_Math, + UNICODE_PROP_Other_Alphabetic, + UNICODE_PROP_Other_Lowercase, + UNICODE_PROP_Other_Uppercase, + UNICODE_PROP_Other_Grapheme_Extend, + UNICODE_PROP_Other_Default_Ignorable_Code_Point, + UNICODE_PROP_Other_ID_Start, + UNICODE_PROP_Other_ID_Continue, + UNICODE_PROP_Prepended_Concatenation_Mark, + UNICODE_PROP_ID_Continue1, + UNICODE_PROP_XID_Start1, + UNICODE_PROP_XID_Continue1, + UNICODE_PROP_Changes_When_Titlecased1, + UNICODE_PROP_Changes_When_Casefolded1, + UNICODE_PROP_Changes_When_NFKC_Casefolded1, + UNICODE_PROP_ASCII_Hex_Digit, + UNICODE_PROP_Bidi_Control, + UNICODE_PROP_Dash, + UNICODE_PROP_Deprecated, + UNICODE_PROP_Diacritic, + UNICODE_PROP_Extender, + UNICODE_PROP_Hex_Digit, + UNICODE_PROP_IDS_Unary_Operator, + UNICODE_PROP_IDS_Binary_Operator, + UNICODE_PROP_IDS_Trinary_Operator, + UNICODE_PROP_Ideographic, + UNICODE_PROP_Join_Control, + UNICODE_PROP_Logical_Order_Exception, + UNICODE_PROP_Modifier_Combining_Mark, + UNICODE_PROP_Noncharacter_Code_Point, + UNICODE_PROP_Pattern_Syntax, + UNICODE_PROP_Pattern_White_Space, + UNICODE_PROP_Quotation_Mark, + UNICODE_PROP_Radical, + UNICODE_PROP_Regional_Indicator, + UNICODE_PROP_Sentence_Terminal, + UNICODE_PROP_Soft_Dotted, + UNICODE_PROP_Terminal_Punctuation, + UNICODE_PROP_Unified_Ideograph, + UNICODE_PROP_Variation_Selector, + UNICODE_PROP_White_Space, + UNICODE_PROP_Bidi_Mirrored, + UNICODE_PROP_Emoji, + UNICODE_PROP_Emoji_Component, + UNICODE_PROP_Emoji_Modifier, + UNICODE_PROP_Emoji_Modifier_Base, + UNICODE_PROP_Emoji_Presentation, + UNICODE_PROP_Extended_Pictographic, + UNICODE_PROP_Default_Ignorable_Code_Point, + UNICODE_PROP_ID_Start, + UNICODE_PROP_Case_Ignorable, + UNICODE_PROP_ASCII, + UNICODE_PROP_Alphabetic, + UNICODE_PROP_Any, + UNICODE_PROP_Assigned, + UNICODE_PROP_Cased, + UNICODE_PROP_Changes_When_Casefolded, + UNICODE_PROP_Changes_When_Casemapped, + UNICODE_PROP_Changes_When_Lowercased, + UNICODE_PROP_Changes_When_NFKC_Casefolded, + UNICODE_PROP_Changes_When_Titlecased, + UNICODE_PROP_Changes_When_Uppercased, + UNICODE_PROP_Grapheme_Base, + UNICODE_PROP_Grapheme_Extend, + UNICODE_PROP_ID_Continue, + UNICODE_PROP_ID_Compat_Math_Start, + UNICODE_PROP_ID_Compat_Math_Continue, + UNICODE_PROP_Lowercase, + UNICODE_PROP_Math, + UNICODE_PROP_Uppercase, + UNICODE_PROP_XID_Continue, + UNICODE_PROP_XID_Start, + UNICODE_PROP_Cased1, + UNICODE_PROP_InCB, + UNICODE_PROP_COUNT, +} UnicodePropertyEnum; + +static const char unicode_prop_name_table[] = + "ASCII_Hex_Digit,AHex" "\0" + "Bidi_Control,Bidi_C" "\0" + "Dash" "\0" + "Deprecated,Dep" "\0" + "Diacritic,Dia" "\0" + "Extender,Ext" "\0" + "Hex_Digit,Hex" "\0" + "IDS_Unary_Operator,IDSU" "\0" + "IDS_Binary_Operator,IDSB" "\0" + "IDS_Trinary_Operator,IDST" "\0" + "Ideographic,Ideo" "\0" + "Join_Control,Join_C" "\0" + "Logical_Order_Exception,LOE" "\0" + "Modifier_Combining_Mark,MCM" "\0" + "Noncharacter_Code_Point,NChar" "\0" + "Pattern_Syntax,Pat_Syn" "\0" + "Pattern_White_Space,Pat_WS" "\0" + "Quotation_Mark,QMark" "\0" + "Radical" "\0" + "Regional_Indicator,RI" "\0" + "Sentence_Terminal,STerm" "\0" + "Soft_Dotted,SD" "\0" + "Terminal_Punctuation,Term" "\0" + "Unified_Ideograph,UIdeo" "\0" + "Variation_Selector,VS" "\0" + "White_Space,space" "\0" + "Bidi_Mirrored,Bidi_M" "\0" + "Emoji" "\0" + "Emoji_Component,EComp" "\0" + "Emoji_Modifier,EMod" "\0" + "Emoji_Modifier_Base,EBase" "\0" + "Emoji_Presentation,EPres" "\0" + "Extended_Pictographic,ExtPict" "\0" + "Default_Ignorable_Code_Point,DI" "\0" + "ID_Start,IDS" "\0" + "Case_Ignorable,CI" "\0" + "ASCII" "\0" + "Alphabetic,Alpha" "\0" + "Any" "\0" + "Assigned" "\0" + "Cased" "\0" + "Changes_When_Casefolded,CWCF" "\0" + "Changes_When_Casemapped,CWCM" "\0" + "Changes_When_Lowercased,CWL" "\0" + "Changes_When_NFKC_Casefolded,CWKCF" "\0" + "Changes_When_Titlecased,CWT" "\0" + "Changes_When_Uppercased,CWU" "\0" + "Grapheme_Base,Gr_Base" "\0" + "Grapheme_Extend,Gr_Ext" "\0" + "ID_Continue,IDC" "\0" + "ID_Compat_Math_Start" "\0" + "ID_Compat_Math_Continue" "\0" + "Lowercase,Lower" "\0" + "Math" "\0" + "Uppercase,Upper" "\0" + "XID_Continue,XIDC" "\0" + "XID_Start,XIDS" "\0" +; + +static const uint8_t * const unicode_prop_table[] = { + unicode_prop_Hyphen_table, + unicode_prop_Other_Math_table, + unicode_prop_Other_Alphabetic_table, + unicode_prop_Other_Lowercase_table, + unicode_prop_Other_Uppercase_table, + unicode_prop_Other_Grapheme_Extend_table, + unicode_prop_Other_Default_Ignorable_Code_Point_table, + unicode_prop_Other_ID_Start_table, + unicode_prop_Other_ID_Continue_table, + unicode_prop_Prepended_Concatenation_Mark_table, + unicode_prop_ID_Continue1_table, + unicode_prop_XID_Start1_table, + unicode_prop_XID_Continue1_table, + unicode_prop_Changes_When_Titlecased1_table, + unicode_prop_Changes_When_Casefolded1_table, + unicode_prop_Changes_When_NFKC_Casefolded1_table, + unicode_prop_ASCII_Hex_Digit_table, + unicode_prop_Bidi_Control_table, + unicode_prop_Dash_table, + unicode_prop_Deprecated_table, + unicode_prop_Diacritic_table, + unicode_prop_Extender_table, + unicode_prop_Hex_Digit_table, + unicode_prop_IDS_Unary_Operator_table, + unicode_prop_IDS_Binary_Operator_table, + unicode_prop_IDS_Trinary_Operator_table, + unicode_prop_Ideographic_table, + unicode_prop_Join_Control_table, + unicode_prop_Logical_Order_Exception_table, + unicode_prop_Modifier_Combining_Mark_table, + unicode_prop_Noncharacter_Code_Point_table, + unicode_prop_Pattern_Syntax_table, + unicode_prop_Pattern_White_Space_table, + unicode_prop_Quotation_Mark_table, + unicode_prop_Radical_table, + unicode_prop_Regional_Indicator_table, + unicode_prop_Sentence_Terminal_table, + unicode_prop_Soft_Dotted_table, + unicode_prop_Terminal_Punctuation_table, + unicode_prop_Unified_Ideograph_table, + unicode_prop_Variation_Selector_table, + unicode_prop_White_Space_table, + unicode_prop_Bidi_Mirrored_table, + unicode_prop_Emoji_table, + unicode_prop_Emoji_Component_table, + unicode_prop_Emoji_Modifier_table, + unicode_prop_Emoji_Modifier_Base_table, + unicode_prop_Emoji_Presentation_table, + unicode_prop_Extended_Pictographic_table, + unicode_prop_Default_Ignorable_Code_Point_table, + unicode_prop_ID_Start_table, + unicode_prop_Case_Ignorable_table, +}; + +static const uint16_t unicode_prop_len_table[] = { + countof(unicode_prop_Hyphen_table), + countof(unicode_prop_Other_Math_table), + countof(unicode_prop_Other_Alphabetic_table), + countof(unicode_prop_Other_Lowercase_table), + countof(unicode_prop_Other_Uppercase_table), + countof(unicode_prop_Other_Grapheme_Extend_table), + countof(unicode_prop_Other_Default_Ignorable_Code_Point_table), + countof(unicode_prop_Other_ID_Start_table), + countof(unicode_prop_Other_ID_Continue_table), + countof(unicode_prop_Prepended_Concatenation_Mark_table), + countof(unicode_prop_ID_Continue1_table), + countof(unicode_prop_XID_Start1_table), + countof(unicode_prop_XID_Continue1_table), + countof(unicode_prop_Changes_When_Titlecased1_table), + countof(unicode_prop_Changes_When_Casefolded1_table), + countof(unicode_prop_Changes_When_NFKC_Casefolded1_table), + countof(unicode_prop_ASCII_Hex_Digit_table), + countof(unicode_prop_Bidi_Control_table), + countof(unicode_prop_Dash_table), + countof(unicode_prop_Deprecated_table), + countof(unicode_prop_Diacritic_table), + countof(unicode_prop_Extender_table), + countof(unicode_prop_Hex_Digit_table), + countof(unicode_prop_IDS_Unary_Operator_table), + countof(unicode_prop_IDS_Binary_Operator_table), + countof(unicode_prop_IDS_Trinary_Operator_table), + countof(unicode_prop_Ideographic_table), + countof(unicode_prop_Join_Control_table), + countof(unicode_prop_Logical_Order_Exception_table), + countof(unicode_prop_Modifier_Combining_Mark_table), + countof(unicode_prop_Noncharacter_Code_Point_table), + countof(unicode_prop_Pattern_Syntax_table), + countof(unicode_prop_Pattern_White_Space_table), + countof(unicode_prop_Quotation_Mark_table), + countof(unicode_prop_Radical_table), + countof(unicode_prop_Regional_Indicator_table), + countof(unicode_prop_Sentence_Terminal_table), + countof(unicode_prop_Soft_Dotted_table), + countof(unicode_prop_Terminal_Punctuation_table), + countof(unicode_prop_Unified_Ideograph_table), + countof(unicode_prop_Variation_Selector_table), + countof(unicode_prop_White_Space_table), + countof(unicode_prop_Bidi_Mirrored_table), + countof(unicode_prop_Emoji_table), + countof(unicode_prop_Emoji_Component_table), + countof(unicode_prop_Emoji_Modifier_table), + countof(unicode_prop_Emoji_Modifier_Base_table), + countof(unicode_prop_Emoji_Presentation_table), + countof(unicode_prop_Extended_Pictographic_table), + countof(unicode_prop_Default_Ignorable_Code_Point_table), + countof(unicode_prop_ID_Start_table), + countof(unicode_prop_Case_Ignorable_table), +}; + diff --git a/third_party/quickjs/libunicode.c b/third_party/quickjs/libunicode.c new file mode 100644 index 0000000000..e68b0cadd3 --- /dev/null +++ b/third_party/quickjs/libunicode.c @@ -0,0 +1,1744 @@ +/* + * Unicode utilities + * + * Copyright (c) 2017-2018 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include <stdlib.h> +#include <stdio.h> +#include <stdarg.h> +#include <string.h> +#include <assert.h> + +#include "cutils.h" +#include "libunicode.h" +#include "libunicode-table.h" + +// note: stored as 4 bit tag, not much room left +enum { + RUN_TYPE_U, + RUN_TYPE_L, + RUN_TYPE_UF, + RUN_TYPE_LF, + RUN_TYPE_UL, + RUN_TYPE_LSU, + RUN_TYPE_U2L_399_EXT2, + RUN_TYPE_UF_D20, + RUN_TYPE_UF_D1_EXT, + RUN_TYPE_U_EXT, + RUN_TYPE_LF_EXT, + RUN_TYPE_UF_EXT2, + RUN_TYPE_LF_EXT2, + RUN_TYPE_UF_EXT3, +}; + +static int lre_case_conv1(uint32_t c, int conv_type) +{ + uint32_t res[LRE_CC_RES_LEN_MAX]; + lre_case_conv(res, c, conv_type); + return res[0]; +} + +/* case conversion using the table entry 'idx' with value 'v' */ +static int lre_case_conv_entry(uint32_t *res, uint32_t c, int conv_type, uint32_t idx, uint32_t v) +{ + uint32_t code, data, type, a, is_lower; + is_lower = (conv_type != 0); + type = (v >> (32 - 17 - 7 - 4)) & 0xf; + data = ((v & 0xf) << 8) | case_conv_table2[idx]; + code = v >> (32 - 17); + switch(type) { + case RUN_TYPE_U: + case RUN_TYPE_L: + case RUN_TYPE_UF: + case RUN_TYPE_LF: + if (conv_type == (type & 1) || + (type >= RUN_TYPE_UF && conv_type == 2)) { + c = c - code + (case_conv_table1[data] >> (32 - 17)); + } + break; + case RUN_TYPE_UL: + a = c - code; + if ((a & 1) != (1 - is_lower)) + break; + c = (a ^ 1) + code; + break; + case RUN_TYPE_LSU: + a = c - code; + if (a == 1) { + c += 2 * is_lower - 1; + } else if (a == (1 - is_lower) * 2) { + c += (2 * is_lower - 1) * 2; + } + break; + case RUN_TYPE_U2L_399_EXT2: + if (!is_lower) { + res[0] = c - code + case_conv_ext[data >> 6]; + res[1] = 0x399; + return 2; + } else { + c = c - code + case_conv_ext[data & 0x3f]; + } + break; + case RUN_TYPE_UF_D20: + if (conv_type == 1) + break; + c = data + (conv_type == 2) * 0x20; + break; + case RUN_TYPE_UF_D1_EXT: + if (conv_type == 1) + break; + c = case_conv_ext[data] + (conv_type == 2); + break; + case RUN_TYPE_U_EXT: + case RUN_TYPE_LF_EXT: + if (is_lower != (type - RUN_TYPE_U_EXT)) + break; + c = case_conv_ext[data]; + break; + case RUN_TYPE_LF_EXT2: + if (!is_lower) + break; + res[0] = c - code + case_conv_ext[data >> 6]; + res[1] = case_conv_ext[data & 0x3f]; + return 2; + case RUN_TYPE_UF_EXT2: + if (conv_type == 1) + break; + res[0] = c - code + case_conv_ext[data >> 6]; + res[1] = case_conv_ext[data & 0x3f]; + if (conv_type == 2) { + /* convert to lower */ + res[0] = lre_case_conv1(res[0], 1); + res[1] = lre_case_conv1(res[1], 1); + } + return 2; + default: + case RUN_TYPE_UF_EXT3: + if (conv_type == 1) + break; + res[0] = case_conv_ext[data >> 8]; + res[1] = case_conv_ext[(data >> 4) & 0xf]; + res[2] = case_conv_ext[data & 0xf]; + if (conv_type == 2) { + /* convert to lower */ + res[0] = lre_case_conv1(res[0], 1); + res[1] = lre_case_conv1(res[1], 1); + res[2] = lre_case_conv1(res[2], 1); + } + return 3; + } + res[0] = c; + return 1; +} + +/* conv_type: + 0 = to upper + 1 = to lower + 2 = case folding (= to lower with modifications) +*/ +int lre_case_conv(uint32_t *res, uint32_t c, int conv_type) +{ + if (c < 128) { + if (conv_type) { + if (c >= 'A' && c <= 'Z') { + c = c - 'A' + 'a'; + } + } else { + if (c >= 'a' && c <= 'z') { + c = c - 'a' + 'A'; + } + } + } else { + uint32_t v, code, len; + int idx, idx_min, idx_max; + + idx_min = 0; + idx_max = countof(case_conv_table1) - 1; + while (idx_min <= idx_max) { + idx = (unsigned)(idx_max + idx_min) / 2; + v = case_conv_table1[idx]; + code = v >> (32 - 17); + len = (v >> (32 - 17 - 7)) & 0x7f; + if (c < code) { + idx_max = idx - 1; + } else if (c >= code + len) { + idx_min = idx + 1; + } else { + return lre_case_conv_entry(res, c, conv_type, idx, v); + } + } + } + res[0] = c; + return 1; +} + +static int lre_case_folding_entry(uint32_t c, uint32_t idx, uint32_t v, BOOL is_unicode) +{ + uint32_t res[LRE_CC_RES_LEN_MAX]; + int len; + + if (is_unicode) { + len = lre_case_conv_entry(res, c, 2, idx, v); + if (len == 1) { + c = res[0]; + } else { + /* handle the few specific multi-character cases (see + unicode_gen.c:dump_case_folding_special_cases()) */ + if (c == 0xfb06) { + c = 0xfb05; + } else if (c == 0x01fd3) { + c = 0x390; + } else if (c == 0x01fe3) { + c = 0x3b0; + } + } + } else { + if (likely(c < 128)) { + if (c >= 'a' && c <= 'z') + c = c - 'a' + 'A'; + } else { + /* legacy regexp: to upper case if single char >= 128 */ + len = lre_case_conv_entry(res, c, FALSE, idx, v); + if (len == 1 && res[0] >= 128) + c = res[0]; + } + } + return c; +} + +/* JS regexp specific rules for case folding */ +int lre_canonicalize(uint32_t c, BOOL is_unicode) +{ + if (c < 128) { + /* fast case */ + if (is_unicode) { + if (c >= 'A' && c <= 'Z') { + c = c - 'A' + 'a'; + } + } else { + if (c >= 'a' && c <= 'z') { + c = c - 'a' + 'A'; + } + } + } else { + uint32_t v, code, len; + int idx, idx_min, idx_max; + + idx_min = 0; + idx_max = countof(case_conv_table1) - 1; + while (idx_min <= idx_max) { + idx = (unsigned)(idx_max + idx_min) / 2; + v = case_conv_table1[idx]; + code = v >> (32 - 17); + len = (v >> (32 - 17 - 7)) & 0x7f; + if (c < code) { + idx_max = idx - 1; + } else if (c >= code + len) { + idx_min = idx + 1; + } else { + return lre_case_folding_entry(c, idx, v, is_unicode); + } + } + } + return c; +} + +static uint32_t get_le24(const uint8_t *ptr) +{ + return ptr[0] | (ptr[1] << 8) | (ptr[2] << 16); +} + +#define UNICODE_INDEX_BLOCK_LEN 32 + +/* return -1 if not in table, otherwise the offset in the block */ +static int get_index_pos(uint32_t *pcode, uint32_t c, + const uint8_t *index_table, int index_table_len) +{ + uint32_t code, v; + int idx_min, idx_max, idx; + + idx_min = 0; + v = get_le24(index_table); + code = v & ((1 << 21) - 1); + if (c < code) { + *pcode = 0; + return 0; + } + idx_max = index_table_len - 1; + code = get_le24(index_table + idx_max * 3); + if (c >= code) + return -1; + /* invariant: tab[idx_min] <= c < tab2[idx_max] */ + while ((idx_max - idx_min) > 1) { + idx = (idx_max + idx_min) / 2; + v = get_le24(index_table + idx * 3); + code = v & ((1 << 21) - 1); + if (c < code) { + idx_max = idx; + } else { + idx_min = idx; + } + } + v = get_le24(index_table + idx_min * 3); + *pcode = v & ((1 << 21) - 1); + return (idx_min + 1) * UNICODE_INDEX_BLOCK_LEN + (v >> 21); +} + +static BOOL lre_is_in_table(uint32_t c, const uint8_t *table, + const uint8_t *index_table, int index_table_len) +{ + uint32_t code, b, bit; + int pos; + const uint8_t *p; + + pos = get_index_pos(&code, c, index_table, index_table_len); + if (pos < 0) + return FALSE; /* outside the table */ + p = table + pos; + bit = 0; + for(;;) { + b = *p++; + if (b < 64) { + code += (b >> 3) + 1; + if (c < code) + return bit; + bit ^= 1; + code += (b & 7) + 1; + } else if (b >= 0x80) { + code += b - 0x80 + 1; + } else if (b < 0x60) { + code += (((b - 0x40) << 8) | p[0]) + 1; + p++; + } else { + code += (((b - 0x60) << 16) | (p[0] << 8) | p[1]) + 1; + p += 2; + } + if (c < code) + return bit; + bit ^= 1; + } +} + +BOOL lre_is_cased(uint32_t c) +{ + uint32_t v, code, len; + int idx, idx_min, idx_max; + + idx_min = 0; + idx_max = countof(case_conv_table1) - 1; + while (idx_min <= idx_max) { + idx = (unsigned)(idx_max + idx_min) / 2; + v = case_conv_table1[idx]; + code = v >> (32 - 17); + len = (v >> (32 - 17 - 7)) & 0x7f; + if (c < code) { + idx_max = idx - 1; + } else if (c >= code + len) { + idx_min = idx + 1; + } else { + return TRUE; + } + } + return lre_is_in_table(c, unicode_prop_Cased1_table, + unicode_prop_Cased1_index, + sizeof(unicode_prop_Cased1_index) / 3); +} + +BOOL lre_is_case_ignorable(uint32_t c) +{ + return lre_is_in_table(c, unicode_prop_Case_Ignorable_table, + unicode_prop_Case_Ignorable_index, + sizeof(unicode_prop_Case_Ignorable_index) / 3); +} + +/* character range */ + +static __maybe_unused void cr_dump(CharRange *cr) +{ + int i; + for(i = 0; i < cr->len; i++) + printf("%d: 0x%04x\n", i, cr->points[i]); +} + +static void *cr_default_realloc(void *opaque, void *ptr, size_t size) +{ + return realloc(ptr, size); +} + +void cr_init(CharRange *cr, void *mem_opaque, DynBufReallocFunc *realloc_func) +{ + cr->len = cr->size = 0; + cr->points = NULL; + cr->mem_opaque = mem_opaque; + cr->realloc_func = realloc_func ? realloc_func : cr_default_realloc; +} + +void cr_free(CharRange *cr) +{ + cr->realloc_func(cr->mem_opaque, cr->points, 0); +} + +int cr_realloc(CharRange *cr, int size) +{ + int new_size; + uint32_t *new_buf; + + if (size > cr->size) { + new_size = max_int(size, cr->size * 3 / 2); + new_buf = cr->realloc_func(cr->mem_opaque, cr->points, + new_size * sizeof(cr->points[0])); + if (!new_buf) + return -1; + cr->points = new_buf; + cr->size = new_size; + } + return 0; +} + +int cr_copy(CharRange *cr, const CharRange *cr1) +{ + if (cr_realloc(cr, cr1->len)) + return -1; + memcpy(cr->points, cr1->points, sizeof(cr->points[0]) * cr1->len); + cr->len = cr1->len; + return 0; +} + +/* merge consecutive intervals and remove empty intervals */ +static void cr_compress(CharRange *cr) +{ + int i, j, k, len; + uint32_t *pt; + + pt = cr->points; + len = cr->len; + i = 0; + j = 0; + k = 0; + while ((i + 1) < len) { + if (pt[i] == pt[i + 1]) { + /* empty interval */ + i += 2; + } else { + j = i; + while ((j + 3) < len && pt[j + 1] == pt[j + 2]) + j += 2; + /* just copy */ + pt[k] = pt[i]; + pt[k + 1] = pt[j + 1]; + k += 2; + i = j + 2; + } + } + cr->len = k; +} + +/* union or intersection */ +int cr_op(CharRange *cr, const uint32_t *a_pt, int a_len, + const uint32_t *b_pt, int b_len, int op) +{ + int a_idx, b_idx, is_in; + uint32_t v; + + a_idx = 0; + b_idx = 0; + for(;;) { + /* get one more point from a or b in increasing order */ + if (a_idx < a_len && b_idx < b_len) { + if (a_pt[a_idx] < b_pt[b_idx]) { + goto a_add; + } else if (a_pt[a_idx] == b_pt[b_idx]) { + v = a_pt[a_idx]; + a_idx++; + b_idx++; + } else { + goto b_add; + } + } else if (a_idx < a_len) { + a_add: + v = a_pt[a_idx++]; + } else if (b_idx < b_len) { + b_add: + v = b_pt[b_idx++]; + } else { + break; + } + /* add the point if the in/out status changes */ + switch(op) { + case CR_OP_UNION: + is_in = (a_idx & 1) | (b_idx & 1); + break; + case CR_OP_INTER: + is_in = (a_idx & 1) & (b_idx & 1); + break; + case CR_OP_XOR: + is_in = (a_idx & 1) ^ (b_idx & 1); + break; + default: + abort(); + } + if (is_in != (cr->len & 1)) { + if (cr_add_point(cr, v)) + return -1; + } + } + cr_compress(cr); + return 0; +} + +int cr_union1(CharRange *cr, const uint32_t *b_pt, int b_len) +{ + CharRange a = *cr; + int ret; + cr->len = 0; + cr->size = 0; + cr->points = NULL; + ret = cr_op(cr, a.points, a.len, b_pt, b_len, CR_OP_UNION); + cr_free(&a); + return ret; +} + +int cr_invert(CharRange *cr) +{ + int len; + len = cr->len; + if (cr_realloc(cr, len + 2)) + return -1; + memmove(cr->points + 1, cr->points, len * sizeof(cr->points[0])); + cr->points[0] = 0; + cr->points[len + 1] = UINT32_MAX; + cr->len = len + 2; + cr_compress(cr); + return 0; +} + +BOOL lre_is_id_start(uint32_t c) +{ + return lre_is_in_table(c, unicode_prop_ID_Start_table, + unicode_prop_ID_Start_index, + sizeof(unicode_prop_ID_Start_index) / 3); +} + +BOOL lre_is_id_continue(uint32_t c) +{ + return lre_is_id_start(c) || + lre_is_in_table(c, unicode_prop_ID_Continue1_table, + unicode_prop_ID_Continue1_index, + sizeof(unicode_prop_ID_Continue1_index) / 3); +} + +BOOL lre_is_white_space(uint32_t c) +{ + return lre_is_in_table(c, unicode_prop_White_Space_table, + unicode_prop_White_Space_index, + sizeof(unicode_prop_White_Space_index) / 3); +} + +#define UNICODE_DECOMP_LEN_MAX 18 + +typedef enum { + DECOMP_TYPE_C1, /* 16 bit char */ + DECOMP_TYPE_L1, /* 16 bit char table */ + DECOMP_TYPE_L2, + DECOMP_TYPE_L3, + DECOMP_TYPE_L4, + DECOMP_TYPE_L5, /* XXX: not used */ + DECOMP_TYPE_L6, /* XXX: could remove */ + DECOMP_TYPE_L7, /* XXX: could remove */ + DECOMP_TYPE_LL1, /* 18 bit char table */ + DECOMP_TYPE_LL2, + DECOMP_TYPE_S1, /* 8 bit char table */ + DECOMP_TYPE_S2, + DECOMP_TYPE_S3, + DECOMP_TYPE_S4, + DECOMP_TYPE_S5, + DECOMP_TYPE_I1, /* increment 16 bit char value */ + DECOMP_TYPE_I2_0, + DECOMP_TYPE_I2_1, + DECOMP_TYPE_I3_1, + DECOMP_TYPE_I3_2, + DECOMP_TYPE_I4_1, + DECOMP_TYPE_I4_2, + DECOMP_TYPE_B1, /* 16 bit base + 8 bit offset */ + DECOMP_TYPE_B2, + DECOMP_TYPE_B3, + DECOMP_TYPE_B4, + DECOMP_TYPE_B5, + DECOMP_TYPE_B6, + DECOMP_TYPE_B7, + DECOMP_TYPE_B8, + DECOMP_TYPE_B18, + DECOMP_TYPE_LS2, + DECOMP_TYPE_PAT3, + DECOMP_TYPE_S2_UL, + DECOMP_TYPE_LS2_UL, +} DecompTypeEnum; + +static uint32_t unicode_get_short_code(uint32_t c) +{ + static const uint16_t unicode_short_table[2] = { 0x2044, 0x2215 }; + + if (c < 0x80) + return c; + else if (c < 0x80 + 0x50) + return c - 0x80 + 0x300; + else + return unicode_short_table[c - 0x80 - 0x50]; +} + +static uint32_t unicode_get_lower_simple(uint32_t c) +{ + if (c < 0x100 || (c >= 0x410 && c <= 0x42f)) + c += 0x20; + else + c++; + return c; +} + +static uint16_t unicode_get16(const uint8_t *p) +{ + return p[0] | (p[1] << 8); +} + +static int unicode_decomp_entry(uint32_t *res, uint32_t c, + int idx, uint32_t code, uint32_t len, + uint32_t type) +{ + uint32_t c1; + int l, i, p; + const uint8_t *d; + + if (type == DECOMP_TYPE_C1) { + res[0] = unicode_decomp_table2[idx]; + return 1; + } else { + d = unicode_decomp_data + unicode_decomp_table2[idx]; + switch(type) { + case DECOMP_TYPE_L1: + case DECOMP_TYPE_L2: + case DECOMP_TYPE_L3: + case DECOMP_TYPE_L4: + case DECOMP_TYPE_L5: + case DECOMP_TYPE_L6: + case DECOMP_TYPE_L7: + l = type - DECOMP_TYPE_L1 + 1; + d += (c - code) * l * 2; + for(i = 0; i < l; i++) { + if ((res[i] = unicode_get16(d + 2 * i)) == 0) + return 0; + } + return l; + case DECOMP_TYPE_LL1: + case DECOMP_TYPE_LL2: + { + uint32_t k, p; + l = type - DECOMP_TYPE_LL1 + 1; + k = (c - code) * l; + p = len * l * 2; + for(i = 0; i < l; i++) { + c1 = unicode_get16(d + 2 * k) | + (((d[p + (k / 4)] >> ((k % 4) * 2)) & 3) << 16); + if (!c1) + return 0; + res[i] = c1; + k++; + } + } + return l; + case DECOMP_TYPE_S1: + case DECOMP_TYPE_S2: + case DECOMP_TYPE_S3: + case DECOMP_TYPE_S4: + case DECOMP_TYPE_S5: + l = type - DECOMP_TYPE_S1 + 1; + d += (c - code) * l; + for(i = 0; i < l; i++) { + if ((res[i] = unicode_get_short_code(d[i])) == 0) + return 0; + } + return l; + case DECOMP_TYPE_I1: + l = 1; + p = 0; + goto decomp_type_i; + case DECOMP_TYPE_I2_0: + case DECOMP_TYPE_I2_1: + case DECOMP_TYPE_I3_1: + case DECOMP_TYPE_I3_2: + case DECOMP_TYPE_I4_1: + case DECOMP_TYPE_I4_2: + l = 2 + ((type - DECOMP_TYPE_I2_0) >> 1); + p = ((type - DECOMP_TYPE_I2_0) & 1) + (l > 2); + decomp_type_i: + for(i = 0; i < l; i++) { + c1 = unicode_get16(d + 2 * i); + if (i == p) + c1 += c - code; + res[i] = c1; + } + return l; + case DECOMP_TYPE_B18: + l = 18; + goto decomp_type_b; + case DECOMP_TYPE_B1: + case DECOMP_TYPE_B2: + case DECOMP_TYPE_B3: + case DECOMP_TYPE_B4: + case DECOMP_TYPE_B5: + case DECOMP_TYPE_B6: + case DECOMP_TYPE_B7: + case DECOMP_TYPE_B8: + l = type - DECOMP_TYPE_B1 + 1; + decomp_type_b: + { + uint32_t c_min; + c_min = unicode_get16(d); + d += 2 + (c - code) * l; + for(i = 0; i < l; i++) { + c1 = d[i]; + if (c1 == 0xff) + c1 = 0x20; + else + c1 += c_min; + res[i] = c1; + } + } + return l; + case DECOMP_TYPE_LS2: + d += (c - code) * 3; + if (!(res[0] = unicode_get16(d))) + return 0; + res[1] = unicode_get_short_code(d[2]); + return 2; + case DECOMP_TYPE_PAT3: + res[0] = unicode_get16(d); + res[2] = unicode_get16(d + 2); + d += 4 + (c - code) * 2; + res[1] = unicode_get16(d); + return 3; + case DECOMP_TYPE_S2_UL: + case DECOMP_TYPE_LS2_UL: + c1 = c - code; + if (type == DECOMP_TYPE_S2_UL) { + d += c1 & ~1; + c = unicode_get_short_code(*d); + d++; + } else { + d += (c1 >> 1) * 3; + c = unicode_get16(d); + d += 2; + } + if (c1 & 1) + c = unicode_get_lower_simple(c); + res[0] = c; + res[1] = unicode_get_short_code(*d); + return 2; + } + } + return 0; +} + + +/* return the length of the decomposition (length <= + UNICODE_DECOMP_LEN_MAX) or 0 if no decomposition */ +static int unicode_decomp_char(uint32_t *res, uint32_t c, BOOL is_compat1) +{ + uint32_t v, type, is_compat, code, len; + int idx_min, idx_max, idx; + + idx_min = 0; + idx_max = countof(unicode_decomp_table1) - 1; + while (idx_min <= idx_max) { + idx = (idx_max + idx_min) / 2; + v = unicode_decomp_table1[idx]; + code = v >> (32 - 18); + len = (v >> (32 - 18 - 7)) & 0x7f; + // printf("idx=%d code=%05x len=%d\n", idx, code, len); + if (c < code) { + idx_max = idx - 1; + } else if (c >= code + len) { + idx_min = idx + 1; + } else { + is_compat = v & 1; + if (is_compat1 < is_compat) + break; + type = (v >> (32 - 18 - 7 - 6)) & 0x3f; + return unicode_decomp_entry(res, c, idx, code, len, type); + } + } + return 0; +} + +/* return 0 if no pair found */ +static int unicode_compose_pair(uint32_t c0, uint32_t c1) +{ + uint32_t code, len, type, v, idx1, d_idx, d_offset, ch; + int idx_min, idx_max, idx, d; + uint32_t pair[2]; + + idx_min = 0; + idx_max = countof(unicode_comp_table) - 1; + while (idx_min <= idx_max) { + idx = (idx_max + idx_min) / 2; + idx1 = unicode_comp_table[idx]; + + /* idx1 represent an entry of the decomposition table */ + d_idx = idx1 >> 6; + d_offset = idx1 & 0x3f; + v = unicode_decomp_table1[d_idx]; + code = v >> (32 - 18); + len = (v >> (32 - 18 - 7)) & 0x7f; + type = (v >> (32 - 18 - 7 - 6)) & 0x3f; + ch = code + d_offset; + unicode_decomp_entry(pair, ch, d_idx, code, len, type); + d = c0 - pair[0]; + if (d == 0) + d = c1 - pair[1]; + if (d < 0) { + idx_max = idx - 1; + } else if (d > 0) { + idx_min = idx + 1; + } else { + return ch; + } + } + return 0; +} + +/* return the combining class of character c (between 0 and 255) */ +static int unicode_get_cc(uint32_t c) +{ + uint32_t code, n, type, cc, c1, b; + int pos; + const uint8_t *p; + + pos = get_index_pos(&code, c, + unicode_cc_index, sizeof(unicode_cc_index) / 3); + if (pos < 0) + return 0; + p = unicode_cc_table + pos; + for(;;) { + b = *p++; + type = b >> 6; + n = b & 0x3f; + if (n < 48) { + } else if (n < 56) { + n = (n - 48) << 8; + n |= *p++; + n += 48; + } else { + n = (n - 56) << 8; + n |= *p++ << 8; + n |= *p++; + n += 48 + (1 << 11); + } + if (type <= 1) + p++; + c1 = code + n + 1; + if (c < c1) { + switch(type) { + case 0: + cc = p[-1]; + break; + case 1: + cc = p[-1] + c - code; + break; + case 2: + cc = 0; + break; + default: + case 3: + cc = 230; + break; + } + return cc; + } + code = c1; + } +} + +static void sort_cc(int *buf, int len) +{ + int i, j, k, cc, cc1, start, ch1; + + for(i = 0; i < len; i++) { + cc = unicode_get_cc(buf[i]); + if (cc != 0) { + start = i; + j = i + 1; + while (j < len) { + ch1 = buf[j]; + cc1 = unicode_get_cc(ch1); + if (cc1 == 0) + break; + k = j - 1; + while (k >= start) { + if (unicode_get_cc(buf[k]) <= cc1) + break; + buf[k + 1] = buf[k]; + k--; + } + buf[k + 1] = ch1; + j++; + } + i = j; + } + } +} + +static void to_nfd_rec(DynBuf *dbuf, + const int *src, int src_len, int is_compat) +{ + uint32_t c, v; + int i, l; + uint32_t res[UNICODE_DECOMP_LEN_MAX]; + + for(i = 0; i < src_len; i++) { + c = src[i]; + if (c >= 0xac00 && c < 0xd7a4) { + /* Hangul decomposition */ + c -= 0xac00; + dbuf_put_u32(dbuf, 0x1100 + c / 588); + dbuf_put_u32(dbuf, 0x1161 + (c % 588) / 28); + v = c % 28; + if (v != 0) + dbuf_put_u32(dbuf, 0x11a7 + v); + } else { + l = unicode_decomp_char(res, c, is_compat); + if (l) { + to_nfd_rec(dbuf, (int *)res, l, is_compat); + } else { + dbuf_put_u32(dbuf, c); + } + } + } +} + +/* return 0 if not found */ +static int compose_pair(uint32_t c0, uint32_t c1) +{ + /* Hangul composition */ + if (c0 >= 0x1100 && c0 < 0x1100 + 19 && + c1 >= 0x1161 && c1 < 0x1161 + 21) { + return 0xac00 + (c0 - 0x1100) * 588 + (c1 - 0x1161) * 28; + } else if (c0 >= 0xac00 && c0 < 0xac00 + 11172 && + (c0 - 0xac00) % 28 == 0 && + c1 >= 0x11a7 && c1 < 0x11a7 + 28) { + return c0 + c1 - 0x11a7; + } else { + return unicode_compose_pair(c0, c1); + } +} + +int unicode_normalize(uint32_t **pdst, const uint32_t *src, int src_len, + UnicodeNormalizationEnum n_type, + void *opaque, DynBufReallocFunc *realloc_func) +{ + int *buf, buf_len, i, p, starter_pos, cc, last_cc, out_len; + BOOL is_compat; + DynBuf dbuf_s, *dbuf = &dbuf_s; + + is_compat = n_type >> 1; + + dbuf_init2(dbuf, opaque, realloc_func); + if (dbuf_realloc(dbuf, sizeof(int) * src_len)) + goto fail; + + /* common case: latin1 is unaffected by NFC */ + if (n_type == UNICODE_NFC) { + for(i = 0; i < src_len; i++) { + if (src[i] >= 0x100) + goto not_latin1; + } + buf = (int *)dbuf->buf; + memcpy(buf, src, src_len * sizeof(int)); + *pdst = (uint32_t *)buf; + return src_len; + not_latin1: ; + } + + to_nfd_rec(dbuf, (const int *)src, src_len, is_compat); + if (dbuf_error(dbuf)) { + fail: + *pdst = NULL; + return -1; + } + buf = (int *)dbuf->buf; + buf_len = dbuf->size / sizeof(int); + + sort_cc(buf, buf_len); + + if (buf_len <= 1 || (n_type & 1) != 0) { + /* NFD / NFKD */ + *pdst = (uint32_t *)buf; + return buf_len; + } + + i = 1; + out_len = 1; + while (i < buf_len) { + /* find the starter character and test if it is blocked from + the character at 'i' */ + last_cc = unicode_get_cc(buf[i]); + starter_pos = out_len - 1; + while (starter_pos >= 0) { + cc = unicode_get_cc(buf[starter_pos]); + if (cc == 0) + break; + if (cc >= last_cc) + goto next; + last_cc = 256; + starter_pos--; + } + if (starter_pos >= 0 && + (p = compose_pair(buf[starter_pos], buf[i])) != 0) { + buf[starter_pos] = p; + i++; + } else { + next: + buf[out_len++] = buf[i++]; + } + } + *pdst = (uint32_t *)buf; + return out_len; +} + +/* char ranges for various unicode properties */ + +static int unicode_find_name(const char *name_table, const char *name) +{ + const char *p, *r; + int pos; + size_t name_len, len; + + p = name_table; + pos = 0; + name_len = strlen(name); + while (*p) { + for(;;) { + r = strchr(p, ','); + if (!r) + len = strlen(p); + else + len = r - p; + if (len == name_len && !memcmp(p, name, name_len)) + return pos; + p += len + 1; + if (!r) + break; + } + pos++; + } + return -1; +} + +/* 'cr' must be initialized and empty. Return 0 if OK, -1 if error, -2 + if not found */ +int unicode_script(CharRange *cr, + const char *script_name, BOOL is_ext) +{ + int script_idx; + const uint8_t *p, *p_end; + uint32_t c, c1, b, n, v, v_len, i, type; + CharRange cr1_s = { 0 }, *cr1 = NULL; + CharRange cr2_s = { 0 }, *cr2 = &cr2_s; + BOOL is_common; + + script_idx = unicode_find_name(unicode_script_name_table, script_name); + if (script_idx < 0) + return -2; + /* Note: we remove the "Unknown" Script */ + script_idx += UNICODE_SCRIPT_Unknown + 1; + + is_common = (script_idx == UNICODE_SCRIPT_Common || + script_idx == UNICODE_SCRIPT_Inherited); + if (is_ext) { + cr1 = &cr1_s; + cr_init(cr1, cr->mem_opaque, cr->realloc_func); + cr_init(cr2, cr->mem_opaque, cr->realloc_func); + } else { + cr1 = cr; + } + + p = unicode_script_table; + p_end = unicode_script_table + countof(unicode_script_table); + c = 0; + while (p < p_end) { + b = *p++; + type = b >> 7; + n = b & 0x7f; + if (n < 96) { + } else if (n < 112) { + n = (n - 96) << 8; + n |= *p++; + n += 96; + } else { + n = (n - 112) << 16; + n |= *p++ << 8; + n |= *p++; + n += 96 + (1 << 12); + } + if (type == 0) + v = 0; + else + v = *p++; + c1 = c + n + 1; + if (v == script_idx) { + if (cr_add_interval(cr1, c, c1)) + goto fail; + } + c = c1; + } + + if (is_ext) { + /* add the script extensions */ + p = unicode_script_ext_table; + p_end = unicode_script_ext_table + countof(unicode_script_ext_table); + c = 0; + while (p < p_end) { + b = *p++; + if (b < 128) { + n = b; + } else if (b < 128 + 64) { + n = (b - 128) << 8; + n |= *p++; + n += 128; + } else { + n = (b - 128 - 64) << 16; + n |= *p++ << 8; + n |= *p++; + n += 128 + (1 << 14); + } + c1 = c + n + 1; + v_len = *p++; + if (is_common) { + if (v_len != 0) { + if (cr_add_interval(cr2, c, c1)) + goto fail; + } + } else { + for(i = 0; i < v_len; i++) { + if (p[i] == script_idx) { + if (cr_add_interval(cr2, c, c1)) + goto fail; + break; + } + } + } + p += v_len; + c = c1; + } + if (is_common) { + /* remove all the characters with script extensions */ + if (cr_invert(cr2)) + goto fail; + if (cr_op(cr, cr1->points, cr1->len, cr2->points, cr2->len, + CR_OP_INTER)) + goto fail; + } else { + if (cr_op(cr, cr1->points, cr1->len, cr2->points, cr2->len, + CR_OP_UNION)) + goto fail; + } + cr_free(cr1); + cr_free(cr2); + } + return 0; + fail: + if (is_ext) { + cr_free(cr1); + cr_free(cr2); + } + goto fail; +} + +#define M(id) (1U << UNICODE_GC_ ## id) + +static int unicode_general_category1(CharRange *cr, uint32_t gc_mask) +{ + const uint8_t *p, *p_end; + uint32_t c, c0, b, n, v; + + p = unicode_gc_table; + p_end = unicode_gc_table + countof(unicode_gc_table); + c = 0; + while (p < p_end) { + b = *p++; + n = b >> 5; + v = b & 0x1f; + if (n == 7) { + n = *p++; + if (n < 128) { + n += 7; + } else if (n < 128 + 64) { + n = (n - 128) << 8; + n |= *p++; + n += 7 + 128; + } else { + n = (n - 128 - 64) << 16; + n |= *p++ << 8; + n |= *p++; + n += 7 + 128 + (1 << 14); + } + } + c0 = c; + c += n + 1; + if (v == 31) { + /* run of Lu / Ll */ + b = gc_mask & (M(Lu) | M(Ll)); + if (b != 0) { + if (b == (M(Lu) | M(Ll))) { + goto add_range; + } else { + c0 += ((gc_mask & M(Ll)) != 0); + for(; c0 < c; c0 += 2) { + if (cr_add_interval(cr, c0, c0 + 1)) + return -1; + } + } + } + } else if ((gc_mask >> v) & 1) { + add_range: + if (cr_add_interval(cr, c0, c)) + return -1; + } + } + return 0; +} + +static int unicode_prop1(CharRange *cr, int prop_idx) +{ + const uint8_t *p, *p_end; + uint32_t c, c0, b, bit; + + p = unicode_prop_table[prop_idx]; + p_end = p + unicode_prop_len_table[prop_idx]; + c = 0; + bit = 0; + while (p < p_end) { + c0 = c; + b = *p++; + if (b < 64) { + c += (b >> 3) + 1; + if (bit) { + if (cr_add_interval(cr, c0, c)) + return -1; + } + bit ^= 1; + c0 = c; + c += (b & 7) + 1; + } else if (b >= 0x80) { + c += b - 0x80 + 1; + } else if (b < 0x60) { + c += (((b - 0x40) << 8) | p[0]) + 1; + p++; + } else { + c += (((b - 0x60) << 16) | (p[0] << 8) | p[1]) + 1; + p += 2; + } + if (bit) { + if (cr_add_interval(cr, c0, c)) + return -1; + } + bit ^= 1; + } + return 0; +} + +#define CASE_U (1 << 0) +#define CASE_L (1 << 1) +#define CASE_F (1 << 2) + +/* use the case conversion table to generate range of characters. + CASE_U: set char if modified by uppercasing, + CASE_L: set char if modified by lowercasing, + CASE_F: set char if modified by case folding, + */ +static int unicode_case1(CharRange *cr, int case_mask) +{ +#define MR(x) (1 << RUN_TYPE_ ## x) + const uint32_t tab_run_mask[3] = { + MR(U) | MR(UF) | MR(UL) | MR(LSU) | MR(U2L_399_EXT2) | MR(UF_D20) | + MR(UF_D1_EXT) | MR(U_EXT) | MR(UF_EXT2) | MR(UF_EXT3), + + MR(L) | MR(LF) | MR(UL) | MR(LSU) | MR(U2L_399_EXT2) | MR(LF_EXT) | MR(LF_EXT2), + + MR(UF) | MR(LF) | MR(UL) | MR(LSU) | MR(U2L_399_EXT2) | MR(LF_EXT) | MR(LF_EXT2) | MR(UF_D20) | MR(UF_D1_EXT) | MR(LF_EXT) | MR(UF_EXT2) | MR(UF_EXT3), + }; +#undef MR + uint32_t mask, v, code, type, len, i, idx; + + if (case_mask == 0) + return 0; + mask = 0; + for(i = 0; i < 3; i++) { + if ((case_mask >> i) & 1) + mask |= tab_run_mask[i]; + } + for(idx = 0; idx < countof(case_conv_table1); idx++) { + v = case_conv_table1[idx]; + type = (v >> (32 - 17 - 7 - 4)) & 0xf; + code = v >> (32 - 17); + len = (v >> (32 - 17 - 7)) & 0x7f; + if ((mask >> type) & 1) { + // printf("%d: type=%d %04x %04x\n", idx, type, code, code + len - 1); + switch(type) { + case RUN_TYPE_UL: + if ((case_mask & CASE_U) && (case_mask & (CASE_L | CASE_F))) + goto def_case; + code += ((case_mask & CASE_U) != 0); + for(i = 0; i < len; i += 2) { + if (cr_add_interval(cr, code + i, code + i + 1)) + return -1; + } + break; + case RUN_TYPE_LSU: + if ((case_mask & CASE_U) && (case_mask & (CASE_L | CASE_F))) + goto def_case; + if (!(case_mask & CASE_U)) { + if (cr_add_interval(cr, code, code + 1)) + return -1; + } + if (cr_add_interval(cr, code + 1, code + 2)) + return -1; + if (case_mask & CASE_U) { + if (cr_add_interval(cr, code + 2, code + 3)) + return -1; + } + break; + default: + def_case: + if (cr_add_interval(cr, code, code + len)) + return -1; + break; + } + } + } + return 0; +} + +static int point_cmp(const void *p1, const void *p2, void *arg) +{ + uint32_t v1 = *(uint32_t *)p1; + uint32_t v2 = *(uint32_t *)p2; + return (v1 > v2) - (v1 < v2); +} + +static void cr_sort_and_remove_overlap(CharRange *cr) +{ + uint32_t start, end, start1, end1, i, j; + + /* the resulting ranges are not necessarily sorted and may overlap */ + rqsort(cr->points, cr->len / 2, sizeof(cr->points[0]) * 2, point_cmp, NULL); + j = 0; + for(i = 0; i < cr->len; ) { + start = cr->points[i]; + end = cr->points[i + 1]; + i += 2; + while (i < cr->len) { + start1 = cr->points[i]; + end1 = cr->points[i + 1]; + if (start1 > end) { + /* |------| + * |-------| */ + break; + } else if (end1 <= end) { + /* |------| + * |--| */ + i += 2; + } else { + /* |------| + * |-------| */ + end = end1; + i += 2; + } + } + cr->points[j] = start; + cr->points[j + 1] = end; + j += 2; + } + cr->len = j; +} + +/* canonicalize a character set using the JS regex case folding rules + (see lre_canonicalize()) */ +int cr_regexp_canonicalize(CharRange *cr, BOOL is_unicode) +{ + CharRange cr_inter, cr_mask, cr_result, cr_sub; + uint32_t v, code, len, i, idx, start, end, c, d_start, d_end, d; + + cr_init(&cr_mask, cr->mem_opaque, cr->realloc_func); + cr_init(&cr_inter, cr->mem_opaque, cr->realloc_func); + cr_init(&cr_result, cr->mem_opaque, cr->realloc_func); + cr_init(&cr_sub, cr->mem_opaque, cr->realloc_func); + + if (unicode_case1(&cr_mask, is_unicode ? CASE_F : CASE_U)) + goto fail; + if (cr_op(&cr_inter, cr_mask.points, cr_mask.len, cr->points, cr->len, CR_OP_INTER)) + goto fail; + + if (cr_invert(&cr_mask)) + goto fail; + if (cr_op(&cr_sub, cr_mask.points, cr_mask.len, cr->points, cr->len, CR_OP_INTER)) + goto fail; + + /* cr_inter = cr & cr_mask */ + /* cr_sub = cr & ~cr_mask */ + + /* use the case conversion table to compute the result */ + d_start = -1; + d_end = -1; + idx = 0; + v = case_conv_table1[idx]; + code = v >> (32 - 17); + len = (v >> (32 - 17 - 7)) & 0x7f; + for(i = 0; i < cr_inter.len; i += 2) { + start = cr_inter.points[i]; + end = cr_inter.points[i + 1]; + + for(c = start; c < end; c++) { + for(;;) { + if (c >= code && c < code + len) + break; + idx++; + assert(idx < countof(case_conv_table1)); + v = case_conv_table1[idx]; + code = v >> (32 - 17); + len = (v >> (32 - 17 - 7)) & 0x7f; + } + d = lre_case_folding_entry(c, idx, v, is_unicode); + /* try to merge with the current interval */ + if (d_start == -1) { + d_start = d; + d_end = d + 1; + } else if (d_end == d) { + d_end++; + } else { + cr_add_interval(&cr_result, d_start, d_end); + d_start = d; + d_end = d + 1; + } + } + } + if (d_start != -1) { + if (cr_add_interval(&cr_result, d_start, d_end)) + goto fail; + } + + /* the resulting ranges are not necessarily sorted and may overlap */ + cr_sort_and_remove_overlap(&cr_result); + + /* or with the character not affected by the case folding */ + cr->len = 0; + if (cr_op(cr, cr_result.points, cr_result.len, cr_sub.points, cr_sub.len, CR_OP_UNION)) + goto fail; + + cr_free(&cr_inter); + cr_free(&cr_mask); + cr_free(&cr_result); + cr_free(&cr_sub); + return 0; + fail: + cr_free(&cr_inter); + cr_free(&cr_mask); + cr_free(&cr_result); + cr_free(&cr_sub); + return -1; +} + +typedef enum { + POP_GC, + POP_PROP, + POP_CASE, + POP_UNION, + POP_INTER, + POP_XOR, + POP_INVERT, + POP_END, +} PropOPEnum; + +#define POP_STACK_LEN_MAX 4 + +static int unicode_prop_ops(CharRange *cr, ...) +{ + va_list ap; + CharRange stack[POP_STACK_LEN_MAX]; + int stack_len, op, ret, i; + uint32_t a; + + va_start(ap, cr); + stack_len = 0; + for(;;) { + op = va_arg(ap, int); + switch(op) { + case POP_GC: + assert(stack_len < POP_STACK_LEN_MAX); + a = va_arg(ap, int); + cr_init(&stack[stack_len++], cr->mem_opaque, cr->realloc_func); + if (unicode_general_category1(&stack[stack_len - 1], a)) + goto fail; + break; + case POP_PROP: + assert(stack_len < POP_STACK_LEN_MAX); + a = va_arg(ap, int); + cr_init(&stack[stack_len++], cr->mem_opaque, cr->realloc_func); + if (unicode_prop1(&stack[stack_len - 1], a)) + goto fail; + break; + case POP_CASE: + assert(stack_len < POP_STACK_LEN_MAX); + a = va_arg(ap, int); + cr_init(&stack[stack_len++], cr->mem_opaque, cr->realloc_func); + if (unicode_case1(&stack[stack_len - 1], a)) + goto fail; + break; + case POP_UNION: + case POP_INTER: + case POP_XOR: + { + CharRange *cr1, *cr2, *cr3; + assert(stack_len >= 2); + assert(stack_len < POP_STACK_LEN_MAX); + cr1 = &stack[stack_len - 2]; + cr2 = &stack[stack_len - 1]; + cr3 = &stack[stack_len++]; + cr_init(cr3, cr->mem_opaque, cr->realloc_func); + if (cr_op(cr3, cr1->points, cr1->len, + cr2->points, cr2->len, op - POP_UNION + CR_OP_UNION)) + goto fail; + cr_free(cr1); + cr_free(cr2); + *cr1 = *cr3; + stack_len -= 2; + } + break; + case POP_INVERT: + assert(stack_len >= 1); + if (cr_invert(&stack[stack_len - 1])) + goto fail; + break; + case POP_END: + goto done; + default: + abort(); + } + } + done: + assert(stack_len == 1); + ret = cr_copy(cr, &stack[0]); + cr_free(&stack[0]); + return ret; + fail: + for(i = 0; i < stack_len; i++) + cr_free(&stack[i]); + return -1; +} + +static const uint32_t unicode_gc_mask_table[] = { + M(Lu) | M(Ll) | M(Lt), /* LC */ + M(Lu) | M(Ll) | M(Lt) | M(Lm) | M(Lo), /* L */ + M(Mn) | M(Mc) | M(Me), /* M */ + M(Nd) | M(Nl) | M(No), /* N */ + M(Sm) | M(Sc) | M(Sk) | M(So), /* S */ + M(Pc) | M(Pd) | M(Ps) | M(Pe) | M(Pi) | M(Pf) | M(Po), /* P */ + M(Zs) | M(Zl) | M(Zp), /* Z */ + M(Cc) | M(Cf) | M(Cs) | M(Co) | M(Cn), /* C */ +}; + +/* 'cr' must be initialized and empty. Return 0 if OK, -1 if error, -2 + if not found */ +int unicode_general_category(CharRange *cr, const char *gc_name) +{ + int gc_idx; + uint32_t gc_mask; + + gc_idx = unicode_find_name(unicode_gc_name_table, gc_name); + if (gc_idx < 0) + return -2; + if (gc_idx <= UNICODE_GC_Co) { + gc_mask = (uint64_t)1 << gc_idx; + } else { + gc_mask = unicode_gc_mask_table[gc_idx - UNICODE_GC_LC]; + } + return unicode_general_category1(cr, gc_mask); +} + + +/* 'cr' must be initialized and empty. Return 0 if OK, -1 if error, -2 + if not found */ +int unicode_prop(CharRange *cr, const char *prop_name) +{ + int prop_idx, ret; + + prop_idx = unicode_find_name(unicode_prop_name_table, prop_name); + if (prop_idx < 0) + return -2; + prop_idx += UNICODE_PROP_ASCII_Hex_Digit; + + ret = 0; + switch(prop_idx) { + case UNICODE_PROP_ASCII: + if (cr_add_interval(cr, 0x00, 0x7f + 1)) + return -1; + break; + case UNICODE_PROP_Any: + if (cr_add_interval(cr, 0x00000, 0x10ffff + 1)) + return -1; + break; + case UNICODE_PROP_Assigned: + ret = unicode_prop_ops(cr, + POP_GC, M(Cn), + POP_INVERT, + POP_END); + break; + case UNICODE_PROP_Math: + ret = unicode_prop_ops(cr, + POP_GC, M(Sm), + POP_PROP, UNICODE_PROP_Other_Math, + POP_UNION, + POP_END); + break; + case UNICODE_PROP_Lowercase: + ret = unicode_prop_ops(cr, + POP_GC, M(Ll), + POP_PROP, UNICODE_PROP_Other_Lowercase, + POP_UNION, + POP_END); + break; + case UNICODE_PROP_Uppercase: + ret = unicode_prop_ops(cr, + POP_GC, M(Lu), + POP_PROP, UNICODE_PROP_Other_Uppercase, + POP_UNION, + POP_END); + break; + case UNICODE_PROP_Cased: + ret = unicode_prop_ops(cr, + POP_GC, M(Lu) | M(Ll) | M(Lt), + POP_PROP, UNICODE_PROP_Other_Uppercase, + POP_UNION, + POP_PROP, UNICODE_PROP_Other_Lowercase, + POP_UNION, + POP_END); + break; + case UNICODE_PROP_Alphabetic: + ret = unicode_prop_ops(cr, + POP_GC, M(Lu) | M(Ll) | M(Lt) | M(Lm) | M(Lo) | M(Nl), + POP_PROP, UNICODE_PROP_Other_Uppercase, + POP_UNION, + POP_PROP, UNICODE_PROP_Other_Lowercase, + POP_UNION, + POP_PROP, UNICODE_PROP_Other_Alphabetic, + POP_UNION, + POP_END); + break; + case UNICODE_PROP_Grapheme_Base: + ret = unicode_prop_ops(cr, + POP_GC, M(Cc) | M(Cf) | M(Cs) | M(Co) | M(Cn) | M(Zl) | M(Zp) | M(Me) | M(Mn), + POP_PROP, UNICODE_PROP_Other_Grapheme_Extend, + POP_UNION, + POP_INVERT, + POP_END); + break; + case UNICODE_PROP_Grapheme_Extend: + ret = unicode_prop_ops(cr, + POP_GC, M(Me) | M(Mn), + POP_PROP, UNICODE_PROP_Other_Grapheme_Extend, + POP_UNION, + POP_END); + break; + case UNICODE_PROP_XID_Start: + ret = unicode_prop_ops(cr, + POP_GC, M(Lu) | M(Ll) | M(Lt) | M(Lm) | M(Lo) | M(Nl), + POP_PROP, UNICODE_PROP_Other_ID_Start, + POP_UNION, + POP_PROP, UNICODE_PROP_Pattern_Syntax, + POP_PROP, UNICODE_PROP_Pattern_White_Space, + POP_UNION, + POP_PROP, UNICODE_PROP_XID_Start1, + POP_UNION, + POP_INVERT, + POP_INTER, + POP_END); + break; + case UNICODE_PROP_XID_Continue: + ret = unicode_prop_ops(cr, + POP_GC, M(Lu) | M(Ll) | M(Lt) | M(Lm) | M(Lo) | M(Nl) | + M(Mn) | M(Mc) | M(Nd) | M(Pc), + POP_PROP, UNICODE_PROP_Other_ID_Start, + POP_UNION, + POP_PROP, UNICODE_PROP_Other_ID_Continue, + POP_UNION, + POP_PROP, UNICODE_PROP_Pattern_Syntax, + POP_PROP, UNICODE_PROP_Pattern_White_Space, + POP_UNION, + POP_PROP, UNICODE_PROP_XID_Continue1, + POP_UNION, + POP_INVERT, + POP_INTER, + POP_END); + break; + case UNICODE_PROP_Changes_When_Uppercased: + ret = unicode_case1(cr, CASE_U); + break; + case UNICODE_PROP_Changes_When_Lowercased: + ret = unicode_case1(cr, CASE_L); + break; + case UNICODE_PROP_Changes_When_Casemapped: + ret = unicode_case1(cr, CASE_U | CASE_L | CASE_F); + break; + case UNICODE_PROP_Changes_When_Titlecased: + ret = unicode_prop_ops(cr, + POP_CASE, CASE_U, + POP_PROP, UNICODE_PROP_Changes_When_Titlecased1, + POP_XOR, + POP_END); + break; + case UNICODE_PROP_Changes_When_Casefolded: + ret = unicode_prop_ops(cr, + POP_CASE, CASE_F, + POP_PROP, UNICODE_PROP_Changes_When_Casefolded1, + POP_XOR, + POP_END); + break; + case UNICODE_PROP_Changes_When_NFKC_Casefolded: + ret = unicode_prop_ops(cr, + POP_CASE, CASE_F, + POP_PROP, UNICODE_PROP_Changes_When_NFKC_Casefolded1, + POP_XOR, + POP_END); + break; + /* we use the existing tables */ + case UNICODE_PROP_ID_Continue: + ret = unicode_prop_ops(cr, + POP_PROP, UNICODE_PROP_ID_Start, + POP_PROP, UNICODE_PROP_ID_Continue1, + POP_XOR, + POP_END); + break; + default: + if (prop_idx >= countof(unicode_prop_table)) + return -2; + ret = unicode_prop1(cr, prop_idx); + break; + } + return ret; +} diff --git a/third_party/quickjs/libunicode.h b/third_party/quickjs/libunicode.h new file mode 100644 index 0000000000..ae20a4983c --- /dev/null +++ b/third_party/quickjs/libunicode.h @@ -0,0 +1,129 @@ +/* + * Unicode utilities + * + * Copyright (c) 2017-2018 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#ifndef LIBUNICODE_H +#define LIBUNICODE_H + +#include <stddef.h> +#include <inttypes.h> + +#ifdef __cplusplus +extern "C" { +#endif + +#define LRE_BOOL int /* for documentation purposes */ + +#define LRE_CC_RES_LEN_MAX 3 + +typedef enum { + UNICODE_NFC, + UNICODE_NFD, + UNICODE_NFKC, + UNICODE_NFKD, +} UnicodeNormalizationEnum; + +int lre_case_conv(uint32_t *res, uint32_t c, int conv_type); +int lre_canonicalize(uint32_t c, BOOL is_unicode); +LRE_BOOL lre_is_cased(uint32_t c); +LRE_BOOL lre_is_case_ignorable(uint32_t c); + +/* char ranges */ + +typedef struct { + int len; /* in points, always even */ + int size; + uint32_t *points; /* points sorted by increasing value */ + void *mem_opaque; + void *(*realloc_func)(void *opaque, void *ptr, size_t size); +} CharRange; + +typedef enum { + CR_OP_UNION, + CR_OP_INTER, + CR_OP_XOR, +} CharRangeOpEnum; + +void cr_init(CharRange *cr, void *mem_opaque, void *(*realloc_func)(void *opaque, void *ptr, size_t size)); +void cr_free(CharRange *cr); +int cr_realloc(CharRange *cr, int size); +int cr_copy(CharRange *cr, const CharRange *cr1); + +static inline int cr_add_point(CharRange *cr, uint32_t v) +{ + if (cr->len >= cr->size) { + if (cr_realloc(cr, cr->len + 1)) + return -1; + } + cr->points[cr->len++] = v; + return 0; +} + +static inline int cr_add_interval(CharRange *cr, uint32_t c1, uint32_t c2) +{ + if ((cr->len + 2) > cr->size) { + if (cr_realloc(cr, cr->len + 2)) + return -1; + } + cr->points[cr->len++] = c1; + cr->points[cr->len++] = c2; + return 0; +} + +int cr_union1(CharRange *cr, const uint32_t *b_pt, int b_len); + +static inline int cr_union_interval(CharRange *cr, uint32_t c1, uint32_t c2) +{ + uint32_t b_pt[2]; + b_pt[0] = c1; + b_pt[1] = c2 + 1; + return cr_union1(cr, b_pt, 2); +} + +int cr_op(CharRange *cr, const uint32_t *a_pt, int a_len, + const uint32_t *b_pt, int b_len, int op); + +int cr_invert(CharRange *cr); +int cr_regexp_canonicalize(CharRange *cr, BOOL is_unicode); + +LRE_BOOL lre_is_id_start(uint32_t c); +LRE_BOOL lre_is_id_continue(uint32_t c); +LRE_BOOL lre_is_white_space(uint32_t c); + +int unicode_normalize(uint32_t **pdst, const uint32_t *src, int src_len, + UnicodeNormalizationEnum n_type, + void *opaque, void *(*realloc_func)(void *opaque, void *ptr, size_t size)); + +/* Unicode character range functions */ + +int unicode_script(CharRange *cr, + const char *script_name, LRE_BOOL is_ext); +int unicode_general_category(CharRange *cr, const char *gc_name); +int unicode_prop(CharRange *cr, const char *prop_name); + +#undef LRE_BOOL + +#ifdef __cplusplus +} /* extern "C" { */ +#endif + +#endif /* LIBUNICODE_H */ diff --git a/third_party/quickjs/list.h b/third_party/quickjs/list.h new file mode 100644 index 0000000000..b8dd716814 --- /dev/null +++ b/third_party/quickjs/list.h @@ -0,0 +1,107 @@ +/* + * Linux klist like system + * + * Copyright (c) 2016-2017 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#ifndef LIST_H +#define LIST_H + +#ifndef NULL +#include <stddef.h> +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +struct list_head { + struct list_head *prev; + struct list_head *next; +}; + +#define LIST_HEAD_INIT(el) { &(el), &(el) } + +/* return the pointer of type 'type *' containing 'el' as field 'member' */ +#define list_entry(el, type, member) container_of(el, type, member) + +static inline void init_list_head(struct list_head *head) +{ + head->prev = head; + head->next = head; +} + +/* insert 'el' between 'prev' and 'next' */ +static inline void __list_add(struct list_head *el, + struct list_head *prev, struct list_head *next) +{ + prev->next = el; + el->prev = prev; + el->next = next; + next->prev = el; +} + +/* add 'el' at the head of the list 'head' (= after element head) */ +static inline void list_add(struct list_head *el, struct list_head *head) +{ + __list_add(el, head, head->next); +} + +/* add 'el' at the end of the list 'head' (= before element head) */ +static inline void list_add_tail(struct list_head *el, struct list_head *head) +{ + __list_add(el, head->prev, head); +} + +static inline void list_del(struct list_head *el) +{ + struct list_head *prev, *next; + prev = el->prev; + next = el->next; + prev->next = next; + next->prev = prev; + el->prev = NULL; /* fail safe */ + el->next = NULL; /* fail safe */ +} + +static inline int list_empty(struct list_head *el) +{ + return el->next == el; +} + +#define list_for_each(el, head) \ + for(el = (head)->next; el != (head); el = el->next) + +#define list_for_each_safe(el, el1, head) \ + for(el = (head)->next, el1 = el->next; el != (head); \ + el = el1, el1 = el->next) + +#define list_for_each_prev(el, head) \ + for(el = (head)->prev; el != (head); el = el->prev) + +#define list_for_each_prev_safe(el, el1, head) \ + for(el = (head)->prev, el1 = el->prev; el != (head); \ + el = el1, el1 = el->prev) + +#ifdef __cplusplus +} /* extern "C" { */ +#endif + +#endif /* LIST_H */ diff --git a/third_party/quickjs/qjs.c b/third_party/quickjs/qjs.c new file mode 100644 index 0000000000..ef1b67beb4 --- /dev/null +++ b/third_party/quickjs/qjs.c @@ -0,0 +1,602 @@ +/* + * QuickJS stand alone interpreter + * + * Copyright (c) 2017-2021 Fabrice Bellard + * Copyright (c) 2017-2021 Charlie Gordon + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include <stdlib.h> +#include <stdio.h> +#include <stdarg.h> +#include <inttypes.h> +#include <string.h> +#include <assert.h> +#if !defined(_MSC_VER) +#include <unistd.h> +#endif +#include <errno.h> +#include <fcntl.h> +#include <time.h> + +#include "cutils.h" +#include "quickjs-libc.h" + +#ifdef QJS_USE_MIMALLOC +#include <mimalloc.h> +#endif + +extern const uint8_t qjsc_repl[]; +extern const uint32_t qjsc_repl_size; + +static JSCFunctionListEntry argv0; + +static int eval_buf(JSContext *ctx, const void *buf, int buf_len, + const char *filename, int eval_flags) +{ + JSValue val; + int ret; + + if ((eval_flags & JS_EVAL_TYPE_MASK) == JS_EVAL_TYPE_MODULE) { + /* for the modules, we compile then run to be able to set + import.meta */ + val = JS_Eval(ctx, buf, buf_len, filename, + eval_flags | JS_EVAL_FLAG_COMPILE_ONLY); + if (!JS_IsException(val)) { + js_module_set_import_meta(ctx, val, TRUE, TRUE); + val = JS_EvalFunction(ctx, val); + } + val = js_std_await(ctx, val); + } else { + val = JS_Eval(ctx, buf, buf_len, filename, eval_flags); + } + if (JS_IsException(val)) { + js_std_dump_error(ctx); + ret = -1; + } else { + ret = 0; + } + JS_FreeValue(ctx, val); + return ret; +} + +static int eval_file(JSContext *ctx, const char *filename, int module) +{ + uint8_t *buf; + int ret, eval_flags; + size_t buf_len; + + buf = js_load_file(ctx, &buf_len, filename); + if (!buf) { + perror(filename); + exit(1); + } + + if (module < 0) { + module = (has_suffix(filename, ".mjs") || + JS_DetectModule((const char *)buf, buf_len)); + } + if (module) + eval_flags = JS_EVAL_TYPE_MODULE; + else + eval_flags = JS_EVAL_TYPE_GLOBAL; + ret = eval_buf(ctx, buf, buf_len, filename, eval_flags); + js_free(ctx, buf); + return ret; +} + +static int64_t parse_limit(const char *arg) { + char *p; + unsigned long unit = 1024; /* default to traditional KB */ + double d = strtod(arg, &p); + + if (p == arg) { + fprintf(stderr, "Invalid limit: %s\n", arg); + return -1; + } + + if (*p) { + switch (*p++) { + case 'b': case 'B': unit = 1UL << 0; break; + case 'k': case 'K': unit = 1UL << 10; break; /* IEC kibibytes */ + case 'm': case 'M': unit = 1UL << 20; break; /* IEC mebibytes */ + case 'g': case 'G': unit = 1UL << 30; break; /* IEC gigibytes */ + default: + fprintf(stderr, "Invalid limit: %s, unrecognized suffix, only k,m,g are allowed\n", arg); + return -1; + } + if (*p) { + fprintf(stderr, "Invalid limit: %s, only one suffix allowed\n", arg); + return -1; + } + } + + return (int64_t)(d * unit); +} + +static JSValue js_gc(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + JS_RunGC(JS_GetRuntime(ctx)); + return JS_UNDEFINED; +} + +static JSValue js_navigator_get_userAgent(JSContext *ctx, JSValue this_val) +{ + char version[32]; + snprintf(version, sizeof(version), "quickjs-ng/%s", JS_GetVersion()); + return JS_NewString(ctx, version); +} + +static const JSCFunctionListEntry navigator_proto_funcs[] = { + JS_CGETSET_DEF2("userAgent", js_navigator_get_userAgent, NULL, JS_PROP_CONFIGURABLE | JS_PROP_ENUMERABLE), + JS_PROP_STRING_DEF("[Symbol.toStringTag]", "Navigator", JS_PROP_CONFIGURABLE), +}; + +static const JSCFunctionListEntry global_obj[] = { + JS_CFUNC_DEF("gc", 0, js_gc), +#if defined(__ASAN__) || defined(__UBSAN__) + JS_PROP_INT32_DEF("__running_with_sanitizer__", 1, JS_PROP_C_W_E ), +#endif +}; + +/* also used to initialize the worker context */ +static JSContext *JS_NewCustomContext(JSRuntime *rt) +{ + JSContext *ctx; + ctx = JS_NewContext(rt); + if (!ctx) + return NULL; + /* system modules */ + js_init_module_std(ctx, "qjs:std"); + js_init_module_os(ctx, "qjs:os"); + js_init_module_bjson(ctx, "qjs:bjson"); + + JSValue global = JS_GetGlobalObject(ctx); + JS_SetPropertyFunctionList(ctx, global, global_obj, countof(global_obj)); + JS_SetPropertyFunctionList(ctx, global, &argv0, 1); + JSValue navigator_proto = JS_NewObject(ctx); + JS_SetPropertyFunctionList(ctx, navigator_proto, navigator_proto_funcs, countof(navigator_proto_funcs)); + JSValue navigator = JS_NewObjectProto(ctx, navigator_proto); + JS_DefinePropertyValueStr(ctx, global, "navigator", navigator, JS_PROP_CONFIGURABLE | JS_PROP_ENUMERABLE); + JS_FreeValue(ctx, global); + JS_FreeValue(ctx, navigator_proto); + + return ctx; +} + +struct trace_malloc_data { + uint8_t *base; +}; + +static inline unsigned long long js_trace_malloc_ptr_offset(uint8_t *ptr, + struct trace_malloc_data *dp) +{ + return ptr - dp->base; +} + +static void +#if defined(_WIN32) && !defined(__clang__) +/* mingw printf is used */ +__attribute__((format(gnu_printf, 2, 3))) +#else +__attribute__((format(printf, 2, 3))) +#endif + js_trace_malloc_printf(void *opaque, const char *fmt, ...) +{ + va_list ap; + int c; + + va_start(ap, fmt); + while ((c = *fmt++) != '\0') { + if (c == '%') { + /* only handle %p and %zd */ + if (*fmt == 'p') { + uint8_t *ptr = va_arg(ap, void *); + if (ptr == NULL) { + printf("NULL"); + } else { + printf("H%+06lld.%zd", + js_trace_malloc_ptr_offset(ptr, opaque), + js__malloc_usable_size(ptr)); + } + fmt++; + continue; + } + if (fmt[0] == 'z' && fmt[1] == 'd') { + size_t sz = va_arg(ap, size_t); + printf("%zd", sz); + fmt += 2; + continue; + } + } + putc(c, stdout); + } + va_end(ap); +} + +static void js_trace_malloc_init(struct trace_malloc_data *s) +{ + free(s->base = malloc(8)); +} + +static void *js_trace_calloc(void *opaque, size_t count, size_t size) +{ + void *ptr; + ptr = calloc(count, size); + js_trace_malloc_printf(opaque, "C %zd %zd -> %p\n", count, size, ptr); + return ptr; +} + +static void *js_trace_malloc(void *opaque, size_t size) +{ + void *ptr; + ptr = malloc(size); + js_trace_malloc_printf(opaque, "A %zd -> %p\n", size, ptr); + return ptr; +} + +static void js_trace_free(void *opaque, void *ptr) +{ + if (!ptr) + return; + js_trace_malloc_printf(opaque, "F %p\n", ptr); + free(ptr); +} + +static void *js_trace_realloc(void *opaque, void *ptr, size_t size) +{ + js_trace_malloc_printf(opaque, "R %zd %p", size, ptr); + ptr = realloc(ptr, size); + js_trace_malloc_printf(opaque, " -> %p\n", ptr); + return ptr; +} + +static const JSMallocFunctions trace_mf = { + js_trace_calloc, + js_trace_malloc, + js_trace_free, + js_trace_realloc, + js__malloc_usable_size +}; + +#ifdef QJS_USE_MIMALLOC +static void *js_mi_calloc(void *opaque, size_t count, size_t size) +{ + return mi_calloc(count, size); +} + +static void *js_mi_malloc(void *opaque, size_t size) +{ + return mi_malloc(size); +} + +static void js_mi_free(void *opaque, void *ptr) +{ + if (!ptr) + return; + mi_free(ptr); +} + +static void *js_mi_realloc(void *opaque, void *ptr, size_t size) +{ + return mi_realloc(ptr, size); +} + +static const JSMallocFunctions mi_mf = { + js_mi_calloc, + js_mi_malloc, + js_mi_free, + js_mi_realloc, + mi_malloc_usable_size +}; +#endif + +#define PROG_NAME "qjs" + +void help(void) +{ + printf("QuickJS-ng version %s\n" + "usage: " PROG_NAME " [options] [file [args]]\n" + "-h --help list options\n" + "-e --eval EXPR evaluate EXPR\n" + "-i --interactive go to interactive mode\n" + "-m --module load as ES6 module (default=autodetect)\n" + " --script load as ES6 script (default=autodetect)\n" + "-I --include file include an additional file\n" + " --std make 'std', 'os' and 'bjson' available to script\n" + "-T --trace trace memory allocation\n" + "-d --dump dump the memory usage stats\n" + "-D --dump-flags flags for dumping debug data (see DUMP_* defines)\n" + " --memory-limit n limit the memory usage to 'n' Kbytes\n" + " --stack-size n limit the stack size to 'n' Kbytes\n" + " --unhandled-rejection dump unhandled promise rejections\n" + "-q --quit just instantiate the interpreter and quit\n", JS_GetVersion()); + exit(1); +} + +int qjs_main(int argc, char **argv) +{ + JSRuntime *rt; + JSContext *ctx; + JSValue ret; + struct trace_malloc_data trace_data = { NULL }; + int optind; + char *expr = NULL; + char *dump_flags_str = NULL; + int interactive = 0; + int dump_memory = 0; + int dump_flags = 0; + int trace_memory = 0; + int empty_run = 0; + int module = -1; + int load_std = 0; + int dump_unhandled_promise_rejection = 0; + char *include_list[32]; + int i, include_count = 0; + int64_t memory_limit = -1; + int64_t stack_size = -1; + + argv0 = (JSCFunctionListEntry)JS_PROP_STRING_DEF("argv0", argv[0], + JS_PROP_C_W_E); + + dump_flags_str = getenv("QJS_DUMP_FLAGS"); + dump_flags = dump_flags_str ? strtol(dump_flags_str, NULL, 16) : 0; + + /* cannot use getopt because we want to pass the command line to + the script */ + optind = 1; + while (optind < argc && *argv[optind] == '-') { + char *arg = argv[optind] + 1; + const char *longopt = ""; + char *opt_arg = NULL; + /* a single - is not an option, it also stops argument scanning */ + if (!*arg) + break; + optind++; + if (*arg == '-') { + longopt = arg + 1; + opt_arg = strchr(longopt, '='); + if (opt_arg) + *opt_arg++ = '\0'; + arg += strlen(arg); + /* -- stops argument scanning */ + if (!*longopt) + break; + } + for (; *arg || *longopt; longopt = "") { + char opt = *arg; + if (opt) { + arg++; + if (!opt_arg && *arg) + opt_arg = arg; + } + if (opt == 'h' || opt == '?' || !strcmp(longopt, "help")) { + help(); + continue; + } + if (opt == 'e' || !strcmp(longopt, "eval")) { + if (!opt_arg) { + if (optind >= argc) { + fprintf(stderr, "qjs: missing expression for -e\n"); + exit(2); + } + opt_arg = argv[optind++]; + } + expr = opt_arg; + break; + } + if (opt == 'I' || !strcmp(longopt, "include")) { + if (optind >= argc) { + fprintf(stderr, "expecting filename"); + exit(1); + } + if (include_count >= countof(include_list)) { + fprintf(stderr, "too many included files"); + exit(1); + } + include_list[include_count++] = argv[optind++]; + continue; + } + if (opt == 'i' || !strcmp(longopt, "interactive")) { + interactive++; + continue; + } + if (opt == 'm' || !strcmp(longopt, "module")) { + module = 1; + continue; + } + if (!strcmp(longopt, "script")) { + module = 0; + continue; + } + if (opt == 'd' || !strcmp(longopt, "dump")) { + dump_memory++; + continue; + } + if (opt == 'D' || !strcmp(longopt, "dump-flags")) { + dump_flags = opt_arg ? strtol(opt_arg, NULL, 16) : 0; + break; + } + if (opt == 'T' || !strcmp(longopt, "trace")) { + trace_memory++; + continue; + } + if (!strcmp(longopt, "std")) { + load_std = 1; + continue; + } + if (!strcmp(longopt, "unhandled-rejection")) { + dump_unhandled_promise_rejection = 1; + continue; + } + if (opt == 'q' || !strcmp(longopt, "quit")) { + empty_run++; + continue; + } + if (!strcmp(longopt, "memory-limit")) { + if (!opt_arg) { + if (optind >= argc) { + fprintf(stderr, "expecting memory limit"); + exit(1); + } + opt_arg = argv[optind++]; + } + memory_limit = parse_limit(opt_arg); + break; + } + if (!strcmp(longopt, "stack-size")) { + if (!opt_arg) { + if (optind >= argc) { + fprintf(stderr, "expecting stack size"); + exit(1); + } + opt_arg = argv[optind++]; + } + stack_size = parse_limit(opt_arg); + break; + } + if (opt) { + fprintf(stderr, "qjs: unknown option '-%c'\n", opt); + } else { + fprintf(stderr, "qjs: unknown option '--%s'\n", longopt); + } + help(); + } + } + + if (trace_memory) { + js_trace_malloc_init(&trace_data); + rt = JS_NewRuntime2(&trace_mf, &trace_data); + } else { +#ifdef QJS_USE_MIMALLOC + rt = JS_NewRuntime2(&mi_mf, NULL); +#else + rt = JS_NewRuntime(); +#endif + } + if (!rt) { + fprintf(stderr, "qjs: cannot allocate JS runtime\n"); + exit(2); + } + if (memory_limit >= 0) + JS_SetMemoryLimit(rt, (size_t)memory_limit); + if (stack_size >= 0) + JS_SetMaxStackSize(rt, (size_t)stack_size); + if (dump_flags != 0) + JS_SetDumpFlags(rt, dump_flags); + js_std_set_worker_new_context_func(JS_NewCustomContext); + js_std_init_handlers(rt); + ctx = JS_NewCustomContext(rt); + if (!ctx) { + fprintf(stderr, "qjs: cannot allocate JS context\n"); + exit(2); + } + + /* loader for ES6 modules */ + JS_SetModuleLoaderFunc(rt, NULL, js_module_loader, NULL); + + if (dump_unhandled_promise_rejection) { + JS_SetHostPromiseRejectionTracker(rt, js_std_promise_rejection_tracker, + NULL); + } + + if (!empty_run) { + js_std_add_helpers(ctx, argc - optind, argv + optind); + + /* make 'std' and 'os' visible to non module code */ + if (load_std) { + const char *str = + "import * as bjson from 'qjs:bjson';\n" + "import * as std from 'qjs:std';\n" + "import * as os from 'qjs:os';\n" + "globalThis.bjson = bjson;\n" + "globalThis.std = std;\n" + "globalThis.os = os;\n"; + eval_buf(ctx, str, strlen(str), "<input>", JS_EVAL_TYPE_MODULE); + } + + for(i = 0; i < include_count; i++) { + if (eval_file(ctx, include_list[i], 0)) + goto fail; + } + + if (expr) { + if (eval_buf(ctx, expr, strlen(expr), "<cmdline>", 0)) + goto fail; + } else + if (optind >= argc) { + /* interactive mode */ + interactive = 1; + } else { + const char *filename; + filename = argv[optind]; + if (eval_file(ctx, filename, module)) + goto fail; + } + if (interactive) { + js_std_eval_binary(ctx, qjsc_repl, qjsc_repl_size, 0); + } + ret = js_std_loop(ctx); + if (!JS_IsUndefined(ret)) { + js_std_dump_error1(ctx, ret); + goto fail; + } + } + + if (dump_memory) { + JSMemoryUsage stats; + JS_ComputeMemoryUsage(rt, &stats); + JS_DumpMemoryUsage(stdout, &stats, rt); + } + js_std_free_handlers(rt); + JS_FreeContext(ctx); + JS_FreeRuntime(rt); + + if (empty_run && dump_memory) { + clock_t t[5]; + double best[5] = {0}; + int i, j; + for (i = 0; i < 100; i++) { + t[0] = clock(); + rt = JS_NewRuntime(); + t[1] = clock(); + ctx = JS_NewContext(rt); + t[2] = clock(); + JS_FreeContext(ctx); + t[3] = clock(); + JS_FreeRuntime(rt); + t[4] = clock(); + for (j = 4; j > 0; j--) { + double ms = 1000.0 * (t[j] - t[j - 1]) / CLOCKS_PER_SEC; + if (i == 0 || best[j] > ms) + best[j] = ms; + } + } + printf("\nInstantiation times (ms): %.3f = %.3f+%.3f+%.3f+%.3f\n", + best[1] + best[2] + best[3] + best[4], + best[1], best[2], best[3], best[4]); + } + return 0; + fail: + js_std_free_handlers(rt); + JS_FreeContext(ctx); + JS_FreeRuntime(rt); + return 1; +} diff --git a/third_party/quickjs/qjs.h b/third_party/quickjs/qjs.h new file mode 100644 index 0000000000..7fbb4b5923 --- /dev/null +++ b/third_party/quickjs/qjs.h @@ -0,0 +1,14 @@ +#ifndef QJS_H +#define QJS_H + +#ifdef __cplusplus +extern "C" { +#endif + +int qjs_main(int argc, char * argv[]); + +#ifdef __cplusplus +} /* extern "C" { */ +#endif + +#endif /* QJS_H */ diff --git a/third_party/quickjs/qjsc.c b/third_party/quickjs/qjsc.c new file mode 100644 index 0000000000..8e0b58bc7c --- /dev/null +++ b/third_party/quickjs/qjsc.c @@ -0,0 +1,567 @@ +/* + * QuickJS command line compiler + * + * Copyright (c) 2018-2021 Fabrice Bellard + * Copyright (c) 2023 Ben Noordhuis + * Copyright (c) 2023 Saúl Ibarra Corretgé + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include <stdlib.h> +#include <stdio.h> +#include <stdarg.h> +#include <inttypes.h> +#include <string.h> +#include <assert.h> +#if defined(_MSC_VER) +#include "getopt_compat.h" +#else +#include <unistd.h> +#endif +#include <errno.h> + +#include "cutils.h" +#include "quickjs-libc.h" + +typedef enum { + OUTPUT_C, + OUTPUT_C_MAIN, + OUTPUT_RAW, +} OutputTypeEnum; + +typedef struct { + char *name; + char *short_name; + int flags; +} namelist_entry_t; + +typedef struct namelist_t { + namelist_entry_t *array; + int count; + int size; +} namelist_t; + +static namelist_t cname_list; +static namelist_t cmodule_list; +static namelist_t init_module_list; +static OutputTypeEnum output_type; +static FILE *outfile; +static const char *c_ident_prefix = "qjsc_"; +static int strip; + +void namelist_add(namelist_t *lp, const char *name, const char *short_name, + int flags) +{ + namelist_entry_t *e; + if (lp->count == lp->size) { + size_t newsize = lp->size + (lp->size >> 1) + 4; + namelist_entry_t *a = + realloc(lp->array, sizeof(lp->array[0]) * newsize); + /* XXX: check for realloc failure */ + lp->array = a; + lp->size = newsize; + } + e = &lp->array[lp->count++]; + e->name = strdup(name); + if (short_name) + e->short_name = strdup(short_name); + else + e->short_name = NULL; + e->flags = flags; +} + +void namelist_free(namelist_t *lp) +{ + while (lp->count > 0) { + namelist_entry_t *e = &lp->array[--lp->count]; + free(e->name); + free(e->short_name); + } + free(lp->array); + lp->array = NULL; + lp->size = 0; +} + +namelist_entry_t *namelist_find(namelist_t *lp, const char *name) +{ + int i; + for(i = 0; i < lp->count; i++) { + namelist_entry_t *e = &lp->array[i]; + if (!strcmp(e->name, name)) + return e; + } + return NULL; +} + +static void get_c_name(char *buf, size_t buf_size, const char *file) +{ + const char *p, *r; + size_t len, i; + int c; + char *q; + + p = strrchr(file, '/'); + if (!p) + p = file; + else + p++; + r = strrchr(p, '.'); + if (!r) + len = strlen(p); + else + len = r - p; + pstrcpy(buf, buf_size, c_ident_prefix); + q = buf + strlen(buf); + for(i = 0; i < len; i++) { + c = p[i]; + if (!((c >= '0' && c <= '9') || + (c >= 'A' && c <= 'Z') || + (c >= 'a' && c <= 'z'))) { + c = '_'; + } + if ((q - buf) < buf_size - 1) + *q++ = c; + } + *q = '\0'; +} + +static void dump_hex(FILE *f, const uint8_t *buf, size_t len) +{ + size_t i, col; + col = 0; + for(i = 0; i < len; i++) { + fprintf(f, " 0x%02x,", buf[i]); + if (++col == 8) { + fprintf(f, "\n"); + col = 0; + } + } + if (col != 0) + fprintf(f, "\n"); +} + +static void output_object_code(JSContext *ctx, + FILE *fo, JSValue obj, const char *c_name, + BOOL load_only) +{ + uint8_t *out_buf; + size_t out_buf_len; + int flags = JS_WRITE_OBJ_BYTECODE; + + if (strip) { + flags |= JS_WRITE_OBJ_STRIP_SOURCE; + if (strip > 1) + flags |= JS_WRITE_OBJ_STRIP_DEBUG; + } + + out_buf = JS_WriteObject(ctx, &out_buf_len, obj, flags); + if (!out_buf) { + js_std_dump_error(ctx); + exit(1); + } + + namelist_add(&cname_list, c_name, NULL, load_only); + + if (output_type == OUTPUT_RAW) { + fwrite(out_buf, 1, out_buf_len, fo); + } else { + fprintf(fo, "const uint32_t %s_size = %u;\n\n", + c_name, (unsigned int)out_buf_len); + fprintf(fo, "const uint8_t %s[%u] = {\n", + c_name, (unsigned int)out_buf_len); + dump_hex(fo, out_buf, out_buf_len); + fprintf(fo, "};\n\n"); + } + + js_free(ctx, out_buf); +} + +static int js_module_dummy_init(JSContext *ctx, JSModuleDef *m) +{ + /* should never be called when compiling JS code */ + abort(); + return -1; // pacify compiler +} + +static void find_unique_cname(char *cname, size_t cname_size) +{ + char cname1[1024]; + int suffix_num; + size_t len, max_len; + assert(cname_size >= 32); + /* find a C name not matching an existing module C name by + adding a numeric suffix */ + len = strlen(cname); + max_len = cname_size - 16; + if (len > max_len) + cname[max_len] = '\0'; + suffix_num = 1; + for(;;) { + snprintf(cname1, sizeof(cname1), "%s_%d", cname, suffix_num); + if (!namelist_find(&cname_list, cname1)) + break; + suffix_num++; + } + pstrcpy(cname, cname_size, cname1); +} + +JSModuleDef *jsc_module_loader(JSContext *ctx, + const char *module_name, void *opaque) +{ + JSModuleDef *m; + namelist_entry_t *e; + + /* check if it is a declared C or system module */ + e = namelist_find(&cmodule_list, module_name); + if (e) { + /* add in the static init module list */ + namelist_add(&init_module_list, e->name, e->short_name, 0); + /* create a dummy module */ + m = JS_NewCModule(ctx, module_name, js_module_dummy_init); + } else if (has_suffix(module_name, ".so")) { + JS_ThrowReferenceError(ctx, "%s: dynamically linking to shared libraries not supported", + module_name); + return NULL; + } else { + size_t buf_len; + uint8_t *buf; + JSValue func_val; + char cname[1024]; + + buf = js_load_file(ctx, &buf_len, module_name); + if (!buf) { + JS_ThrowReferenceError(ctx, "could not load module filename '%s'", + module_name); + return NULL; + } + + /* compile the module */ + func_val = JS_Eval(ctx, (char *)buf, buf_len, module_name, + JS_EVAL_TYPE_MODULE | JS_EVAL_FLAG_COMPILE_ONLY); + js_free(ctx, buf); + if (JS_IsException(func_val)) + return NULL; + get_c_name(cname, sizeof(cname), module_name); + if (namelist_find(&cname_list, cname)) { + find_unique_cname(cname, sizeof(cname)); + } + output_object_code(ctx, outfile, func_val, cname, TRUE); + + /* the module is already referenced, so we must free it */ + m = JS_VALUE_GET_PTR(func_val); + JS_FreeValue(ctx, func_val); + } + return m; +} + +static void compile_file(JSContext *ctx, FILE *fo, + const char *filename, + const char *script_name, + const char *c_name1, + int module) +{ + uint8_t *buf; + char c_name[1024]; + int eval_flags; + JSValue obj; + size_t buf_len; + + buf = js_load_file(ctx, &buf_len, filename); + if (!buf) { + fprintf(stderr, "Could not load '%s'\n", filename); + exit(1); + } + eval_flags = JS_EVAL_FLAG_COMPILE_ONLY; + if (module < 0) { + module = (has_suffix(filename, ".mjs") || + JS_DetectModule((const char *)buf, buf_len)); + } + if (module) + eval_flags |= JS_EVAL_TYPE_MODULE; + else + eval_flags |= JS_EVAL_TYPE_GLOBAL; + obj = JS_Eval(ctx, (const char *)buf, buf_len, script_name ? script_name : filename, eval_flags); + if (JS_IsException(obj)) { + js_std_dump_error(ctx); + exit(1); + } + js_free(ctx, buf); + if (c_name1) { + pstrcpy(c_name, sizeof(c_name), c_name1); + } else { + get_c_name(c_name, sizeof(c_name), filename); + } + output_object_code(ctx, fo, obj, c_name, FALSE); + JS_FreeValue(ctx, obj); +} + +static const char main_c_template1[] = + "int main(int argc, char **argv)\n" + "{\n" + " JSRuntime *rt;\n" + " JSContext *ctx;\n" + " rt = JS_NewRuntime();\n" + " js_std_set_worker_new_context_func(JS_NewCustomContext);\n" + " js_std_init_handlers(rt);\n" + ; + +static const char main_c_template2[] = + " js_std_loop(ctx);\n" + " JS_FreeContext(ctx);\n" + " js_std_free_handlers(rt);\n" + " JS_FreeRuntime(rt);\n" + " return 0;\n" + "}\n"; + +#define PROG_NAME "qjsc" + +void help(void) +{ + printf("QuickJS-ng Compiler version %s\n" + "usage: " PROG_NAME " [options] [files]\n" + "\n" + "options are:\n" + "-b output raw bytecode instead of C code\n" + "-e output main() and bytecode in a C file\n" + "-o output set the output filename\n" + "-n script_name set the script name (as used in stack traces)\n" + "-N cname set the C name of the generated data\n" + "-m compile as Javascript module (default=autodetect)\n" + "-D module_name compile a dynamically loaded module or worker\n" + "-M module_name[,cname] add initialization code for an external C module\n" + "-p prefix set the prefix of the generated C names\n" + "-s strip the source code, specify twice to also strip debug info\n" + "-S n set the maximum stack size to 'n' bytes (default=%d)\n", + JS_GetVersion(), + JS_DEFAULT_STACK_SIZE); + exit(1); +} + +int main(int argc, char **argv) +{ + int c, i, verbose; + const char *out_filename, *cname, *script_name; + char cfilename[1024]; + FILE *fo; + JSRuntime *rt; + JSContext *ctx; + int module; + size_t stack_size; + namelist_t dynamic_module_list; + + out_filename = NULL; + script_name = NULL; + output_type = OUTPUT_C; + cname = NULL; + module = -1; + verbose = 0; + strip = 0; + stack_size = 0; + memset(&dynamic_module_list, 0, sizeof(dynamic_module_list)); + + + /* add system modules */ + namelist_add(&cmodule_list, "qjs:std", "std", 0); + namelist_add(&cmodule_list, "qjs:os", "os", 0); + namelist_add(&cmodule_list, "qjs:bjson", "bjson", 0); + namelist_add(&cmodule_list, "std", "std", 0); + namelist_add(&cmodule_list, "os", "os", 0); + namelist_add(&cmodule_list, "bjson", "bjson", 0); + + for(;;) { + c = getopt(argc, argv, "ho:N:mn:bxesvM:p:S:D:"); + if (c == -1) + break; + switch(c) { + case 'h': + help(); + case 'b': + output_type = OUTPUT_RAW; + break; + case 'o': + out_filename = optarg; + break; + case 'e': + output_type = OUTPUT_C_MAIN; + break; + case 'n': + script_name = optarg; + break; + case 'N': + cname = optarg; + break; + case 'm': + module = 1; + break; + case 'M': + { + char *p; + char path[1024]; + char cname[1024]; + pstrcpy(path, sizeof(path), optarg); + p = strchr(path, ','); + if (p) { + *p = '\0'; + pstrcpy(cname, sizeof(cname), p + 1); + } else { + get_c_name(cname, sizeof(cname), path); + } + namelist_add(&cmodule_list, path, cname, 0); + } + break; + case 'D': + namelist_add(&dynamic_module_list, optarg, NULL, 0); + break; + case 's': + strip++; + break; + case 'v': + verbose++; + break; + case 'p': + c_ident_prefix = optarg; + break; + case 'S': + stack_size = (size_t)strtod(optarg, NULL); + break; + default: + break; + } + } + + if (optind >= argc) + help(); + + if (!out_filename) + out_filename = "out.c"; + + pstrcpy(cfilename, sizeof(cfilename), out_filename); + + if (output_type == OUTPUT_RAW) + fo = fopen(cfilename, "wb"); + else + fo = fopen(cfilename, "w"); + + if (!fo) { + perror(cfilename); + exit(1); + } + outfile = fo; + + rt = JS_NewRuntime(); + ctx = JS_NewContext(rt); + + /* loader for ES6 modules */ + JS_SetModuleLoaderFunc(rt, NULL, jsc_module_loader, NULL); + + if (output_type != OUTPUT_RAW) { + fprintf(fo, "/* File generated automatically by the QuickJS-ng compiler. */\n" + "\n" + ); + } + + if (output_type == OUTPUT_C_MAIN) { + fprintf(fo, "#include \"quickjs-libc.h\"\n" + "\n" + ); + } else if (output_type == OUTPUT_C) { + fprintf(fo, "#include <inttypes.h>\n" + "\n" + ); + } + + for(i = optind; i < argc; i++) { + const char *filename = argv[i]; + compile_file(ctx, fo, filename, script_name, cname, module); + cname = NULL; + } + + for(i = 0; i < dynamic_module_list.count; i++) { + if (!jsc_module_loader(ctx, dynamic_module_list.array[i].name, NULL)) { + fprintf(stderr, "Could not load dynamic module '%s'\n", + dynamic_module_list.array[i].name); + exit(1); + } + } + + if (output_type == OUTPUT_C_MAIN) { + fprintf(fo, + "static JSContext *JS_NewCustomContext(JSRuntime *rt)\n" + "{\n" + " JSContext *ctx = JS_NewContext(rt);\n" + " if (!ctx)\n" + " return NULL;\n"); + /* add the precompiled modules (XXX: could modify the module + loader instead) */ + for(i = 0; i < init_module_list.count; i++) { + namelist_entry_t *e = &init_module_list.array[i]; + /* initialize the static C modules */ + + fprintf(fo, + " {\n" + " extern JSModuleDef *js_init_module_%s(JSContext *ctx, const char *name);\n" + " js_init_module_%s(ctx, \"%s\");\n" + " }\n", + e->short_name, e->short_name, e->name); + } + for(i = 0; i < cname_list.count; i++) { + namelist_entry_t *e = &cname_list.array[i]; + if (e->flags) { + fprintf(fo, " js_std_eval_binary(ctx, %s, %s_size, 1);\n", + e->name, e->name); + } + } + fprintf(fo, + " return ctx;\n" + "}\n\n"); + + fputs(main_c_template1, fo); + + if (stack_size != 0) { + fprintf(fo, " JS_SetMaxStackSize(rt, %u);\n", + (unsigned int)stack_size); + } + + /* add the module loader */ + fprintf(fo, " JS_SetModuleLoaderFunc(rt, NULL, js_module_loader, NULL);\n"); + + fprintf(fo, + " ctx = JS_NewCustomContext(rt);\n" + " js_std_add_helpers(ctx, argc, argv);\n"); + + for(i = 0; i < cname_list.count; i++) { + namelist_entry_t *e = &cname_list.array[i]; + if (!e->flags) { + fprintf(fo, " js_std_eval_binary(ctx, %s, %s_size, 0);\n", + e->name, e->name); + } + } + fputs(main_c_template2, fo); + } + + JS_FreeContext(ctx); + JS_FreeRuntime(rt); + + fclose(fo); + + namelist_free(&cname_list); + namelist_free(&cmodule_list); + namelist_free(&init_module_list); + return 0; +} diff --git a/third_party/quickjs/quickjs-atom.h b/third_party/quickjs/quickjs-atom.h new file mode 100644 index 0000000000..358fe23062 --- /dev/null +++ b/third_party/quickjs/quickjs-atom.h @@ -0,0 +1,262 @@ +/* + * QuickJS atom definitions + * + * Copyright (c) 2017-2018 Fabrice Bellard + * Copyright (c) 2017-2018 Charlie Gordon + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifdef DEF + +/* Note: first atoms are considered as keywords in the parser */ +DEF(null, "null") /* must be first */ +DEF(false, "false") +DEF(true, "true") +DEF(if, "if") +DEF(else, "else") +DEF(return, "return") +DEF(var, "var") +DEF(this, "this") +DEF(delete, "delete") +DEF(void, "void") +DEF(typeof, "typeof") +DEF(new, "new") +DEF(in, "in") +DEF(instanceof, "instanceof") +DEF(do, "do") +DEF(while, "while") +DEF(for, "for") +DEF(break, "break") +DEF(continue, "continue") +DEF(switch, "switch") +DEF(case, "case") +DEF(default, "default") +DEF(throw, "throw") +DEF(try, "try") +DEF(catch, "catch") +DEF(finally, "finally") +DEF(function, "function") +DEF(debugger, "debugger") +DEF(with, "with") +/* FutureReservedWord */ +DEF(class, "class") +DEF(const, "const") +DEF(enum, "enum") +DEF(export, "export") +DEF(extends, "extends") +DEF(import, "import") +DEF(super, "super") +/* FutureReservedWords when parsing strict mode code */ +DEF(implements, "implements") +DEF(interface, "interface") +DEF(let, "let") +DEF(package, "package") +DEF(private, "private") +DEF(protected, "protected") +DEF(public, "public") +DEF(static, "static") +DEF(yield, "yield") +DEF(await, "await") + +/* empty string */ +DEF(empty_string, "") +/* identifiers */ +DEF(keys, "keys") +DEF(size, "size") +DEF(length, "length") +DEF(message, "message") +DEF(cause, "cause") +DEF(errors, "errors") +DEF(stack, "stack") +DEF(name, "name") +DEF(toString, "toString") +DEF(toLocaleString, "toLocaleString") +DEF(valueOf, "valueOf") +DEF(eval, "eval") +DEF(prototype, "prototype") +DEF(constructor, "constructor") +DEF(configurable, "configurable") +DEF(writable, "writable") +DEF(enumerable, "enumerable") +DEF(value, "value") +DEF(get, "get") +DEF(set, "set") +DEF(of, "of") +DEF(__proto__, "__proto__") +DEF(undefined, "undefined") +DEF(number, "number") +DEF(boolean, "boolean") +DEF(string, "string") +DEF(object, "object") +DEF(symbol, "symbol") +DEF(integer, "integer") +DEF(unknown, "unknown") +DEF(arguments, "arguments") +DEF(callee, "callee") +DEF(caller, "caller") +DEF(_eval_, "<eval>") +DEF(_ret_, "<ret>") +DEF(_var_, "<var>") +DEF(_arg_var_, "<arg_var>") +DEF(_with_, "<with>") +DEF(lastIndex, "lastIndex") +DEF(target, "target") +DEF(index, "index") +DEF(input, "input") +DEF(defineProperties, "defineProperties") +DEF(apply, "apply") +DEF(join, "join") +DEF(concat, "concat") +DEF(split, "split") +DEF(construct, "construct") +DEF(getPrototypeOf, "getPrototypeOf") +DEF(setPrototypeOf, "setPrototypeOf") +DEF(isExtensible, "isExtensible") +DEF(preventExtensions, "preventExtensions") +DEF(has, "has") +DEF(deleteProperty, "deleteProperty") +DEF(defineProperty, "defineProperty") +DEF(getOwnPropertyDescriptor, "getOwnPropertyDescriptor") +DEF(ownKeys, "ownKeys") +DEF(add, "add") +DEF(done, "done") +DEF(next, "next") +DEF(values, "values") +DEF(source, "source") +DEF(flags, "flags") +DEF(global, "global") +DEF(unicode, "unicode") +DEF(raw, "raw") +DEF(new_target, "new.target") +DEF(this_active_func, "this.active_func") +DEF(home_object, "<home_object>") +DEF(computed_field, "<computed_field>") +DEF(static_computed_field, "<static_computed_field>") /* must come after computed_fields */ +DEF(class_fields_init, "<class_fields_init>") +DEF(brand, "<brand>") +DEF(hash_constructor, "#constructor") +DEF(as, "as") +DEF(from, "from") +DEF(meta, "meta") +DEF(_default_, "*default*") +DEF(_star_, "*") +DEF(Module, "Module") +DEF(then, "then") +DEF(resolve, "resolve") +DEF(reject, "reject") +DEF(promise, "promise") +DEF(proxy, "proxy") +DEF(revoke, "revoke") +DEF(async, "async") +DEF(exec, "exec") +DEF(groups, "groups") +DEF(indices, "indices") +DEF(status, "status") +DEF(reason, "reason") +DEF(globalThis, "globalThis") +DEF(bigint, "bigint") +DEF(not_equal, "not-equal") +DEF(timed_out, "timed-out") +DEF(ok, "ok") +DEF(toJSON, "toJSON") +DEF(maxByteLength, "maxByteLength") +/* class names */ +DEF(Object, "Object") +DEF(Array, "Array") +DEF(Error, "Error") +DEF(Number, "Number") +DEF(String, "String") +DEF(Boolean, "Boolean") +DEF(Symbol, "Symbol") +DEF(Arguments, "Arguments") +DEF(Math, "Math") +DEF(JSON, "JSON") +DEF(Date, "Date") +DEF(Function, "Function") +DEF(GeneratorFunction, "GeneratorFunction") +DEF(ForInIterator, "ForInIterator") +DEF(RegExp, "RegExp") +DEF(ArrayBuffer, "ArrayBuffer") +DEF(SharedArrayBuffer, "SharedArrayBuffer") +/* must keep same order as class IDs for typed arrays */ +DEF(Uint8ClampedArray, "Uint8ClampedArray") +DEF(Int8Array, "Int8Array") +DEF(Uint8Array, "Uint8Array") +DEF(Int16Array, "Int16Array") +DEF(Uint16Array, "Uint16Array") +DEF(Int32Array, "Int32Array") +DEF(Uint32Array, "Uint32Array") +DEF(BigInt64Array, "BigInt64Array") +DEF(BigUint64Array, "BigUint64Array") +DEF(Float16Array, "Float16Array") +DEF(Float32Array, "Float32Array") +DEF(Float64Array, "Float64Array") +DEF(DataView, "DataView") +DEF(BigInt, "BigInt") +DEF(WeakRef, "WeakRef") +DEF(FinalizationRegistry, "FinalizationRegistry") +DEF(Map, "Map") +DEF(Set, "Set") /* Map + 1 */ +DEF(WeakMap, "WeakMap") /* Map + 2 */ +DEF(WeakSet, "WeakSet") /* Map + 3 */ +DEF(Iterator, "Iterator") +DEF(IteratorHelper, "Iterator Helper") +DEF(IteratorWrap, "Iterator Wrap") +DEF(Map_Iterator, "Map Iterator") +DEF(Set_Iterator, "Set Iterator") +DEF(Array_Iterator, "Array Iterator") +DEF(String_Iterator, "String Iterator") +DEF(RegExp_String_Iterator, "RegExp String Iterator") +DEF(Generator, "Generator") +DEF(Proxy, "Proxy") +DEF(Promise, "Promise") +DEF(PromiseResolveFunction, "PromiseResolveFunction") +DEF(PromiseRejectFunction, "PromiseRejectFunction") +DEF(AsyncFunction, "AsyncFunction") +DEF(AsyncFunctionResolve, "AsyncFunctionResolve") +DEF(AsyncFunctionReject, "AsyncFunctionReject") +DEF(AsyncGeneratorFunction, "AsyncGeneratorFunction") +DEF(AsyncGenerator, "AsyncGenerator") +DEF(EvalError, "EvalError") +DEF(RangeError, "RangeError") +DEF(ReferenceError, "ReferenceError") +DEF(SyntaxError, "SyntaxError") +DEF(TypeError, "TypeError") +DEF(URIError, "URIError") +DEF(InternalError, "InternalError") +DEF(CallSite, "CallSite") +/* private symbols */ +DEF(Private_brand, "<brand>") +/* symbols */ +DEF(Symbol_toPrimitive, "Symbol.toPrimitive") +DEF(Symbol_iterator, "Symbol.iterator") +DEF(Symbol_match, "Symbol.match") +DEF(Symbol_matchAll, "Symbol.matchAll") +DEF(Symbol_replace, "Symbol.replace") +DEF(Symbol_search, "Symbol.search") +DEF(Symbol_split, "Symbol.split") +DEF(Symbol_toStringTag, "Symbol.toStringTag") +DEF(Symbol_isConcatSpreadable, "Symbol.isConcatSpreadable") +DEF(Symbol_hasInstance, "Symbol.hasInstance") +DEF(Symbol_species, "Symbol.species") +DEF(Symbol_unscopables, "Symbol.unscopables") +DEF(Symbol_asyncIterator, "Symbol.asyncIterator") + +#endif /* DEF */ diff --git a/third_party/quickjs/quickjs-c-atomics.h b/third_party/quickjs/quickjs-c-atomics.h new file mode 100644 index 0000000000..8fc6b72030 --- /dev/null +++ b/third_party/quickjs/quickjs-c-atomics.h @@ -0,0 +1,54 @@ +/* + * QuickJS C atomics definitions + * + * Copyright (c) 2023 Marcin Kolny + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#if (defined(__GNUC__) || defined(__GNUG__)) && !defined(__clang__) + // Use GCC builtins for version < 4.9 +# if((__GNUC__ << 16) + __GNUC_MINOR__ < ((4) << 16) + 9) +# define GCC_BUILTIN_ATOMICS +# endif +#endif + +#ifdef GCC_BUILTIN_ATOMICS +#define atomic_fetch_add(obj, arg) \ + __atomic_fetch_add(obj, arg, __ATOMIC_SEQ_CST) +#define atomic_compare_exchange_strong(obj, expected, desired) \ + __atomic_compare_exchange_n(obj, expected, desired, 0, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST) +#define atomic_exchange(obj, desired) \ + __atomic_exchange_n (obj, desired, __ATOMIC_SEQ_CST) +#define atomic_load(obj) \ + __atomic_load_n(obj, __ATOMIC_SEQ_CST) +#define atomic_store(obj, desired) \ + __atomic_store_n(obj, desired, __ATOMIC_SEQ_CST) +#define atomic_fetch_or(obj, arg) \ + __atomic_fetch_or(obj, arg, __ATOMIC_SEQ_CST) +#define atomic_fetch_xor(obj, arg) \ + __atomic_fetch_xor(obj, arg, __ATOMIC_SEQ_CST) +#define atomic_fetch_and(obj, arg) \ + __atomic_fetch_and(obj, arg, __ATOMIC_SEQ_CST) +#define atomic_fetch_sub(obj, arg) \ + __atomic_fetch_sub(obj, arg, __ATOMIC_SEQ_CST) +#define _Atomic +#else +#include <stdatomic.h> +#endif diff --git a/third_party/quickjs/quickjs-libc.c b/third_party/quickjs/quickjs-libc.c new file mode 100644 index 0000000000..15556cf209 --- /dev/null +++ b/third_party/quickjs/quickjs-libc.c @@ -0,0 +1,4287 @@ +/* + * QuickJS C library + * + * Copyright (c) 2017-2021 Fabrice Bellard + * Copyright (c) 2017-2021 Charlie Gordon + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include <stdlib.h> +#include <stdio.h> +#include <stdarg.h> +#include <inttypes.h> +#include <string.h> +#include <assert.h> +#if !defined(_MSC_VER) +#include <unistd.h> +#endif +#include <errno.h> +#include <fcntl.h> +#if !defined(_MSC_VER) +#include <sys/time.h> +#endif +#include <time.h> +#include <signal.h> +#include <limits.h> +#include <sys/stat.h> +#if defined(_MSC_VER) +#include "dirent_compat.h" +#else +#include <dirent.h> +#endif +#if defined(_WIN32) +#include <windows.h> +#include <direct.h> +#include <io.h> +#include <conio.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <sys/utime.h> +#define popen _popen +#define pclose _pclose +#define rmdir _rmdir +#define getcwd _getcwd +#define chdir _chdir +#else +#include <sys/ioctl.h> +#if !defined(__wasi__) +#include <dlfcn.h> +#include <termios.h> +#include <sys/resource.h> +#include <sys/wait.h> +#endif + +#if defined(__APPLE__) +typedef sig_t sighandler_t; +#include <crt_externs.h> +#define environ (*_NSGetEnviron()) +#endif + +#if defined(__OpenBSD__) || defined(__FreeBSD__) || defined(__NetBSD__) +typedef sig_t sighandler_t; +extern char **environ; +#endif + +#endif /* _WIN32 */ + +#if !defined(_WIN32) && !defined(__wasi__) +/* enable the os.Worker API. IT relies on POSIX threads */ +#define USE_WORKER +#endif + +#ifdef USE_WORKER +#include <pthread.h> +#include "quickjs-c-atomics.h" +#endif + +#include "cutils.h" +#include "list.h" +#include "quickjs-libc.h" + +#define MAX_SAFE_INTEGER (((int64_t) 1 << 53) - 1) + +#ifndef QJS_NATIVE_MODULE_SUFFIX +#ifdef _WIN32 +#define QJS_NATIVE_MODULE_SUFFIX ".dll" +#else +#define QJS_NATIVE_MODULE_SUFFIX ".so" +#endif +#endif + +/* TODO: + - add socket calls +*/ + +typedef struct { + struct list_head link; + int fd; + JSValue rw_func[2]; +} JSOSRWHandler; + +typedef struct { + struct list_head link; + int sig_num; + JSValue func; +} JSOSSignalHandler; + +typedef struct { + struct list_head link; + int64_t timer_id; + uint8_t repeats:1; + int64_t timeout; + int64_t delay; + JSValue func; +} JSOSTimer; + +typedef struct { + struct list_head link; + uint8_t *data; + size_t data_len; + /* list of SharedArrayBuffers, necessary to free the message */ + uint8_t **sab_tab; + size_t sab_tab_len; +} JSWorkerMessage; + +typedef struct { + int ref_count; +#ifdef USE_WORKER + pthread_mutex_t mutex; +#endif + struct list_head msg_queue; /* list of JSWorkerMessage.link */ + int read_fd; + int write_fd; +} JSWorkerMessagePipe; + +typedef struct { + struct list_head link; + JSWorkerMessagePipe *recv_pipe; + JSValue on_message_func; +} JSWorkerMessageHandler; + +typedef struct JSThreadState { + struct list_head os_rw_handlers; /* list of JSOSRWHandler.link */ + struct list_head os_signal_handlers; /* list JSOSSignalHandler.link */ + struct list_head os_timers; /* list of JSOSTimer.link */ + struct list_head port_list; /* list of JSWorkerMessageHandler.link */ + int eval_script_recurse; /* only used in the main thread */ + int64_t next_timer_id; /* for setTimeout / setInterval */ + JSValue exc; /* current exception from one of our handlers */ + BOOL can_js_os_poll; + /* not used in the main thread */ + JSWorkerMessagePipe *recv_pipe, *send_pipe; + JSClassID std_file_class_id; + JSClassID worker_class_id; +} JSThreadState; + +static uint64_t os_pending_signals; + +static void js_std_dbuf_init(JSContext *ctx, DynBuf *s) +{ + dbuf_init2(s, JS_GetRuntime(ctx), (DynBufReallocFunc *)js_realloc_rt); +} + +static BOOL my_isdigit(int c) +{ + return (c >= '0' && c <= '9'); +} + +static JSThreadState *js_get_thread_state(JSRuntime *rt) +{ + return (JSThreadState *)js_std_cmd(/*GetOpaque*/0, rt); +} + +static void js_set_thread_state(JSRuntime *rt, JSThreadState *ts) +{ + js_std_cmd(/*SetOpaque*/1, rt, ts); +} + +static JSValue js_printf_internal(JSContext *ctx, + int argc, JSValue *argv, FILE *fp) +{ + char fmtbuf[32]; + uint8_t cbuf[UTF8_CHAR_LEN_MAX+1]; + JSValue res; + DynBuf dbuf; + const char *fmt_str = NULL; + const uint8_t *fmt, *fmt_end; + const uint8_t *p; + char *q; + int i, c, len, mod; + size_t fmt_len; + int32_t int32_arg; + int64_t int64_arg; + double double_arg; + const char *string_arg; + int (*dbuf_printf_fun)(DynBuf *s, const char *fmt, ...) = dbuf_printf; + + js_std_dbuf_init(ctx, &dbuf); + + if (argc > 0) { + fmt_str = JS_ToCStringLen(ctx, &fmt_len, argv[0]); + if (!fmt_str) + goto fail; + + i = 1; + fmt = (const uint8_t *)fmt_str; + fmt_end = fmt + fmt_len; + while (fmt < fmt_end) { + for (p = fmt; fmt < fmt_end && *fmt != '%'; fmt++) + continue; + dbuf_put(&dbuf, p, fmt - p); + if (fmt >= fmt_end) + break; + q = fmtbuf; + *q++ = *fmt++; /* copy '%' */ + + /* flags */ + for(;;) { + c = *fmt; + if (c == '0' || c == '#' || c == '+' || c == '-' || c == ' ' || + c == '\'') { + if (q >= fmtbuf + sizeof(fmtbuf) - 1) + goto invalid; + *q++ = c; + fmt++; + } else { + break; + } + } + /* width */ + if (*fmt == '*') { + if (i >= argc) + goto missing; + if (JS_ToInt32(ctx, &int32_arg, argv[i++])) + goto fail; + q += snprintf(q, fmtbuf + sizeof(fmtbuf) - q, "%d", int32_arg); + fmt++; + } else { + while (my_isdigit(*fmt)) { + if (q >= fmtbuf + sizeof(fmtbuf) - 1) + goto invalid; + *q++ = *fmt++; + } + } + if (*fmt == '.') { + if (q >= fmtbuf + sizeof(fmtbuf) - 1) + goto invalid; + *q++ = *fmt++; + if (*fmt == '*') { + if (i >= argc) + goto missing; + if (JS_ToInt32(ctx, &int32_arg, argv[i++])) + goto fail; + q += snprintf(q, fmtbuf + sizeof(fmtbuf) - q, "%d", int32_arg); + fmt++; + } else { + while (my_isdigit(*fmt)) { + if (q >= fmtbuf + sizeof(fmtbuf) - 1) + goto invalid; + *q++ = *fmt++; + } + } + } + + /* we only support the "l" modifier for 64 bit numbers */ + mod = ' '; + if (*fmt == 'l') { + mod = *fmt++; + } + + /* type */ + c = *fmt++; + if (q >= fmtbuf + sizeof(fmtbuf) - 1) + goto invalid; + *q++ = c; + *q = '\0'; + + switch (c) { + case 'c': + if (i >= argc) + goto missing; + if (JS_IsString(argv[i])) { + // TODO(chqrlie) need an API to wrap charCodeAt and codePointAt */ + string_arg = JS_ToCString(ctx, argv[i++]); + if (!string_arg) + goto fail; + int32_arg = utf8_decode((const uint8_t *)string_arg, &p); + JS_FreeCString(ctx, string_arg); + } else { + if (JS_ToInt32(ctx, &int32_arg, argv[i++])) + goto fail; + } + // XXX: throw an exception? + if ((unsigned)int32_arg > 0x10FFFF) + int32_arg = 0xFFFD; + /* ignore conversion flags, width and precision */ + len = utf8_encode(cbuf, int32_arg); + dbuf_put(&dbuf, cbuf, len); + break; + + case 'd': + case 'i': + case 'o': + case 'u': + case 'x': + case 'X': + if (i >= argc) + goto missing; + if (JS_ToInt64Ext(ctx, &int64_arg, argv[i++])) + goto fail; + if (mod == 'l') { + /* 64 bit number */ +#if defined(_WIN32) + if (q >= fmtbuf + sizeof(fmtbuf) - 3) + goto invalid; + q[2] = q[-1]; + q[-1] = 'I'; + q[0] = '6'; + q[1] = '4'; + q[3] = '\0'; + dbuf_printf_fun(&dbuf, fmtbuf, (int64_t)int64_arg); +#else + if (q >= fmtbuf + sizeof(fmtbuf) - 2) + goto invalid; + q[1] = q[-1]; + q[-1] = q[0] = 'l'; + q[2] = '\0'; + dbuf_printf_fun(&dbuf, fmtbuf, (long long)int64_arg); +#endif + } else { + dbuf_printf_fun(&dbuf, fmtbuf, (int)int64_arg); + } + break; + + case 's': + if (i >= argc) + goto missing; + /* XXX: handle strings containing null characters */ + string_arg = JS_ToCString(ctx, argv[i++]); + if (!string_arg) + goto fail; + dbuf_printf_fun(&dbuf, fmtbuf, string_arg); + JS_FreeCString(ctx, string_arg); + break; + + case 'e': + case 'f': + case 'g': + case 'a': + case 'E': + case 'F': + case 'G': + case 'A': + if (i >= argc) + goto missing; + if (JS_ToFloat64(ctx, &double_arg, argv[i++])) + goto fail; + dbuf_printf_fun(&dbuf, fmtbuf, double_arg); + break; + + case '%': + dbuf_putc(&dbuf, '%'); + break; + + default: + /* XXX: should support an extension mechanism */ + invalid: + JS_ThrowTypeError(ctx, "invalid conversion specifier in format string"); + goto fail; + missing: + JS_ThrowReferenceError(ctx, "missing argument for conversion specifier"); + goto fail; + } + } + JS_FreeCString(ctx, fmt_str); + } + if (dbuf.error) { + res = JS_ThrowOutOfMemory(ctx); + } else { + if (fp) { + len = fwrite(dbuf.buf, 1, dbuf.size, fp); + res = JS_NewInt32(ctx, len); + } else { + res = JS_NewStringLen(ctx, (char *)dbuf.buf, dbuf.size); + } + } + dbuf_free(&dbuf); + return res; + +fail: + JS_FreeCString(ctx, fmt_str); + dbuf_free(&dbuf); + return JS_EXCEPTION; +} + +uint8_t *js_load_file(JSContext *ctx, size_t *pbuf_len, const char *filename) +{ + FILE *f; + uint8_t *buf; + size_t buf_len; + long lret; + + f = fopen(filename, "rb"); + if (!f) + return NULL; + if (fseek(f, 0, SEEK_END) < 0) + goto fail; + lret = ftell(f); + if (lret < 0) + goto fail; + /* XXX: on Linux, ftell() return LONG_MAX for directories */ + if (lret == LONG_MAX) { + errno = EISDIR; + goto fail; + } + buf_len = lret; + if (fseek(f, 0, SEEK_SET) < 0) + goto fail; + if (ctx) + buf = js_malloc(ctx, buf_len + 1); + else + buf = malloc(buf_len + 1); + if (!buf) + goto fail; + if (fread(buf, 1, buf_len, f) != buf_len) { + errno = EIO; + if (ctx) + js_free(ctx, buf); + else + free(buf); + fail: + fclose(f); + return NULL; + } + buf[buf_len] = '\0'; + fclose(f); + *pbuf_len = buf_len; + return buf; +} + +/* load and evaluate a file */ +static JSValue js_loadScript(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + uint8_t *buf; + const char *filename; + JSValue ret; + size_t buf_len; + + filename = JS_ToCString(ctx, argv[0]); + if (!filename) + return JS_EXCEPTION; + buf = js_load_file(ctx, &buf_len, filename); + if (!buf) { + JS_ThrowReferenceError(ctx, "could not load '%s'", filename); + JS_FreeCString(ctx, filename); + return JS_EXCEPTION; + } + ret = JS_Eval(ctx, (char *)buf, buf_len, filename, + JS_EVAL_TYPE_GLOBAL); + js_free(ctx, buf); + JS_FreeCString(ctx, filename); + return ret; +} + +/* load a file as a UTF-8 encoded string */ +static JSValue js_std_loadFile(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + uint8_t *buf; + const char *filename; + JSValue ret; + size_t buf_len; + + filename = JS_ToCString(ctx, argv[0]); + if (!filename) + return JS_EXCEPTION; + buf = js_load_file(ctx, &buf_len, filename); + JS_FreeCString(ctx, filename); + if (!buf) + return JS_NULL; + ret = JS_NewStringLen(ctx, (char *)buf, buf_len); + js_free(ctx, buf); + return ret; +} + +typedef JSModuleDef *(JSInitModuleFunc)(JSContext *ctx, + const char *module_name); + + +#if defined(_WIN32) +static JSModuleDef *js_module_loader_so(JSContext *ctx, + const char *module_name) +{ + JSModuleDef *m; + HINSTANCE hd; + JSInitModuleFunc *init; + char *filename = NULL; + size_t len = strlen(module_name); + JS_BOOL is_absolute = len > 2 && ((module_name[0] >= 'A' && module_name[0] <= 'Z') || + (module_name[0] >= 'a' && module_name[0] <= 'z')) && module_name[1] == ':'; + JS_BOOL is_relative = len > 2 && module_name[0] == '.' && (module_name[1] == '/' || module_name[1] == '\\'); + if (is_absolute || is_relative) { + filename = (char *)module_name; + } else { + filename = js_malloc(ctx, len + 2 + 1); + if (!filename) + return NULL; + strcpy(filename, "./"); + strcpy(filename + 2, module_name); + } + hd = LoadLibraryA(filename); + if (filename != module_name) + js_free(ctx, filename); + if (hd == NULL) { + JS_ThrowReferenceError(ctx, "js_load_module '%s' error: %lu", + module_name, GetLastError()); + goto fail; + } + init = (JSInitModuleFunc *)(uintptr_t)GetProcAddress(hd, "js_init_module"); + if (!init) { + JS_ThrowReferenceError(ctx, "js_init_module '%s' not found: %lu", + module_name, GetLastError()); + goto fail; + } + m = init(ctx, module_name); + if (!m) { + JS_ThrowReferenceError(ctx, "js_call_module '%s' initialization error", + module_name); + fail: + if (hd != NULL) + FreeLibrary(hd); + return NULL; + } + return m; +} +#elif defined(__wasi__) +static JSModuleDef *js_module_loader_so(JSContext *ctx, + const char *module_name) +{ + JS_ThrowReferenceError(ctx, "shared library modules are not supported yet"); + return NULL; +} +#else +static JSModuleDef *js_module_loader_so(JSContext *ctx, + const char *module_name) +{ + JSModuleDef *m; + void *hd; + JSInitModuleFunc *init; + char *filename; + + if (!strchr(module_name, '/')) { + /* must add a '/' so that the DLL is not searched in the + system library paths */ + filename = js_malloc(ctx, strlen(module_name) + 2 + 1); + if (!filename) + return NULL; + strcpy(filename, "./"); + strcpy(filename + 2, module_name); + } else { + filename = (char *)module_name; + } + + /* C module */ + hd = dlopen(filename, RTLD_NOW | RTLD_LOCAL); + if (filename != module_name) + js_free(ctx, filename); + if (!hd) { + JS_ThrowReferenceError(ctx, "could not load module filename '%s' as shared library: %s", + module_name, dlerror()); + goto fail; + } + + *(void **) (&init) = dlsym(hd, "js_init_module"); + if (!init) { + JS_ThrowReferenceError(ctx, "could not load module filename '%s': js_init_module not found", + module_name); + goto fail; + } + + m = init(ctx, module_name); + if (!m) { + JS_ThrowReferenceError(ctx, "could not load module filename '%s': initialization error", + module_name); + fail: + if (hd) + dlclose(hd); + return NULL; + } + return m; +} +#endif /* !_WIN32 */ + +int js_module_set_import_meta(JSContext *ctx, JSValue func_val, + JS_BOOL use_realpath, JS_BOOL is_main) +{ + JSModuleDef *m; + char buf[PATH_MAX + 16]; + JSValue meta_obj; + JSAtom module_name_atom; + const char *module_name; + + assert(JS_VALUE_GET_TAG(func_val) == JS_TAG_MODULE); + m = JS_VALUE_GET_PTR(func_val); + + module_name_atom = JS_GetModuleName(ctx, m); + module_name = JS_AtomToCString(ctx, module_name_atom); + JS_FreeAtom(ctx, module_name_atom); + if (!module_name) + return -1; + if (!strchr(module_name, ':')) { + strcpy(buf, "file://"); +#if !defined(_WIN32) && !defined(__wasi__) + /* realpath() cannot be used with modules compiled with qjsc + because the corresponding module source code is not + necessarily present */ + if (use_realpath) { + char *res = realpath(module_name, buf + strlen(buf)); + if (!res) { + JS_ThrowTypeError(ctx, "realpath failure"); + JS_FreeCString(ctx, module_name); + return -1; + } + } else +#endif + { + pstrcat(buf, sizeof(buf), module_name); + } + } else { + pstrcpy(buf, sizeof(buf), module_name); + } + JS_FreeCString(ctx, module_name); + + meta_obj = JS_GetImportMeta(ctx, m); + if (JS_IsException(meta_obj)) + return -1; + JS_DefinePropertyValueStr(ctx, meta_obj, "url", + JS_NewString(ctx, buf), + JS_PROP_C_W_E); + JS_DefinePropertyValueStr(ctx, meta_obj, "main", + JS_NewBool(ctx, is_main), + JS_PROP_C_W_E); + JS_FreeValue(ctx, meta_obj); + return 0; +} + +JSModuleDef *js_module_loader(JSContext *ctx, + const char *module_name, void *opaque) +{ + JSModuleDef *m; + + if (has_suffix(module_name, QJS_NATIVE_MODULE_SUFFIX)) { + m = js_module_loader_so(ctx, module_name); + } else { + size_t buf_len; + uint8_t *buf; + JSValue func_val; + + buf = js_load_file(ctx, &buf_len, module_name); + if (!buf) { + JS_ThrowReferenceError(ctx, "could not load module filename '%s'", + module_name); + return NULL; + } + + /* compile the module */ + func_val = JS_Eval(ctx, (char *)buf, buf_len, module_name, + JS_EVAL_TYPE_MODULE | JS_EVAL_FLAG_COMPILE_ONLY); + js_free(ctx, buf); + if (JS_IsException(func_val)) + return NULL; + /* XXX: could propagate the exception */ + js_module_set_import_meta(ctx, func_val, TRUE, FALSE); + /* the module is already referenced, so we must free it */ + m = JS_VALUE_GET_PTR(func_val); + JS_FreeValue(ctx, func_val); + } + return m; +} + +static JSValue js_std_exit(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + int status; + if (JS_ToInt32(ctx, &status, argv[0])) + status = -1; + exit(status); + return JS_UNDEFINED; +} + +static JSValue js_std_getenv(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + const char *name, *str; + name = JS_ToCString(ctx, argv[0]); + if (!name) + return JS_EXCEPTION; + str = getenv(name); + JS_FreeCString(ctx, name); + if (!str) + return JS_UNDEFINED; + else + return JS_NewString(ctx, str); +} + +#if defined(_WIN32) +static void setenv(const char *name, const char *value, int overwrite) +{ + char *str; + size_t name_len, value_len; + name_len = strlen(name); + value_len = strlen(value); + str = malloc(name_len + 1 + value_len + 1); + memcpy(str, name, name_len); + str[name_len] = '='; + memcpy(str + name_len + 1, value, value_len); + str[name_len + 1 + value_len] = '\0'; + _putenv(str); + free(str); +} + +static void unsetenv(const char *name) +{ + setenv(name, "", TRUE); +} +#endif /* _WIN32 */ + +static JSValue js_std_setenv(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + const char *name, *value; + name = JS_ToCString(ctx, argv[0]); + if (!name) + return JS_EXCEPTION; + value = JS_ToCString(ctx, argv[1]); + if (!value) { + JS_FreeCString(ctx, name); + return JS_EXCEPTION; + } + setenv(name, value, TRUE); + JS_FreeCString(ctx, name); + JS_FreeCString(ctx, value); + return JS_UNDEFINED; +} + +static JSValue js_std_unsetenv(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + const char *name; + name = JS_ToCString(ctx, argv[0]); + if (!name) + return JS_EXCEPTION; + unsetenv(name); + JS_FreeCString(ctx, name); + return JS_UNDEFINED; +} + +/* return an object containing the list of the available environment + variables. */ +static JSValue js_std_getenviron(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + char **envp; + const char *name, *p, *value; + JSValue obj; + uint32_t idx; + size_t name_len; + JSAtom atom; + int ret; + + obj = JS_NewObject(ctx); + if (JS_IsException(obj)) + return JS_EXCEPTION; + envp = environ; + for(idx = 0; envp[idx] != NULL; idx++) { + name = envp[idx]; + p = strchr(name, '='); + name_len = p - name; + if (!p) + continue; + value = p + 1; + atom = JS_NewAtomLen(ctx, name, name_len); + if (atom == JS_ATOM_NULL) + goto fail; + ret = JS_DefinePropertyValue(ctx, obj, atom, JS_NewString(ctx, value), + JS_PROP_C_W_E); + JS_FreeAtom(ctx, atom); + if (ret < 0) + goto fail; + } + return obj; + fail: + JS_FreeValue(ctx, obj); + return JS_EXCEPTION; +} + +static JSValue js_std_gc(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + JS_RunGC(JS_GetRuntime(ctx)); + return JS_UNDEFINED; +} + +static int interrupt_handler(JSRuntime *rt, void *opaque) +{ + return (os_pending_signals >> SIGINT) & 1; +} + +static int get_bool_option(JSContext *ctx, BOOL *pbool, + JSValue obj, + const char *option) +{ + JSValue val; + val = JS_GetPropertyStr(ctx, obj, option); + if (JS_IsException(val)) + return -1; + if (!JS_IsUndefined(val)) { + *pbool = JS_ToBool(ctx, val); + } + JS_FreeValue(ctx, val); + return 0; +} + +static JSValue js_evalScript(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + JSRuntime *rt = JS_GetRuntime(ctx); + JSThreadState *ts = js_get_thread_state(rt); + const char *str = NULL; + size_t len; + JSValue ret, obj; + JSValue options_obj; + BOOL backtrace_barrier = FALSE; + BOOL eval_function = FALSE; + BOOL compile_only = FALSE; + BOOL is_async = FALSE; + int flags; + + if (argc >= 2) { + options_obj = argv[1]; + if (get_bool_option(ctx, &backtrace_barrier, options_obj, + "backtrace_barrier")) + return JS_EXCEPTION; + if (get_bool_option(ctx, &eval_function, options_obj, + "eval_function")) + return JS_EXCEPTION; + if (get_bool_option(ctx, &compile_only, options_obj, + "compile_only")) + return JS_EXCEPTION; + if (get_bool_option(ctx, &is_async, options_obj, + "async")) + return JS_EXCEPTION; + } + + if (!eval_function) { + str = JS_ToCStringLen(ctx, &len, argv[0]); + if (!str) + return JS_EXCEPTION; + } + if (!ts->recv_pipe && ++ts->eval_script_recurse == 1) { + /* install the interrupt handler */ + JS_SetInterruptHandler(JS_GetRuntime(ctx), interrupt_handler, NULL); + } + flags = JS_EVAL_TYPE_GLOBAL; + if (backtrace_barrier) + flags |= JS_EVAL_FLAG_BACKTRACE_BARRIER; + if (compile_only) + flags |= JS_EVAL_FLAG_COMPILE_ONLY; + if (is_async) + flags |= JS_EVAL_FLAG_ASYNC; + if (eval_function) { + obj = JS_DupValue(ctx, argv[0]); + ret = JS_EvalFunction(ctx, obj); // takes ownership of |obj| + } else { + ret = JS_Eval(ctx, str, len, "<evalScript>", flags); + } + JS_FreeCString(ctx, str); + if (!ts->recv_pipe && --ts->eval_script_recurse == 0) { + /* remove the interrupt handler */ + JS_SetInterruptHandler(JS_GetRuntime(ctx), NULL, NULL); + os_pending_signals &= ~((uint64_t)1 << SIGINT); + /* convert the uncatchable "interrupted" error into a normal error + so that it can be caught by the REPL */ + if (JS_IsException(ret)) + JS_ResetUncatchableError(ctx); + } + return ret; +} + +typedef struct { + FILE *f; + BOOL is_popen; +} JSSTDFile; + +static BOOL is_stdio(FILE *f) +{ + return f == stdin || f == stdout || f == stderr; +} + +static void js_std_file_finalizer(JSRuntime *rt, JSValue val) +{ + JSThreadState *ts = js_get_thread_state(rt); + JSSTDFile *s = JS_GetOpaque(val, ts->std_file_class_id); + if (s) { + if (s->f && !is_stdio(s->f)) { +#if !defined(__wasi__) + if (s->is_popen) + pclose(s->f); + else +#endif + fclose(s->f); + } + js_free_rt(rt, s); + } +} + +static ssize_t js_get_errno(ssize_t ret) +{ + if (ret == -1) + ret = -errno; + return ret; +} + +static JSValue js_std_strerror(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + int err; + if (JS_ToInt32(ctx, &err, argv[0])) + return JS_EXCEPTION; + return JS_NewString(ctx, strerror(err)); +} + +static JSValue js_new_std_file(JSContext *ctx, FILE *f, BOOL is_popen) +{ + JSRuntime *rt = JS_GetRuntime(ctx); + JSThreadState *ts = js_get_thread_state(rt); + JSSTDFile *s; + JSValue obj; + obj = JS_NewObjectClass(ctx, ts->std_file_class_id); + if (JS_IsException(obj)) + return obj; + s = js_mallocz(ctx, sizeof(*s)); + if (!s) { + JS_FreeValue(ctx, obj); + return JS_EXCEPTION; + } + s->is_popen = is_popen; + s->f = f; + JS_SetOpaque(obj, s); + return obj; +} + +static void js_set_error_object(JSContext *ctx, JSValue obj, int err) +{ + if (!JS_IsUndefined(obj)) { + JS_SetPropertyStr(ctx, obj, "errno", JS_NewInt32(ctx, err)); + } +} + +static JSValue js_std_open(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + const char *filename, *mode = NULL; + FILE *f; + int err; + + filename = JS_ToCString(ctx, argv[0]); + if (!filename) + goto fail; + mode = JS_ToCString(ctx, argv[1]); + if (!mode) + goto fail; + if (mode[strspn(mode, "rwa+b")] != '\0') { + JS_ThrowTypeError(ctx, "invalid file mode"); + goto fail; + } + + f = fopen(filename, mode); + if (!f) + err = errno; + else + err = 0; + if (argc >= 3) + js_set_error_object(ctx, argv[2], err); + JS_FreeCString(ctx, filename); + JS_FreeCString(ctx, mode); + if (!f) + return JS_NULL; + return js_new_std_file(ctx, f, FALSE); + fail: + JS_FreeCString(ctx, filename); + JS_FreeCString(ctx, mode); + return JS_EXCEPTION; +} + +#if !defined(__wasi__) +static JSValue js_std_popen(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + const char *filename, *mode = NULL; + FILE *f; + int err; + + filename = JS_ToCString(ctx, argv[0]); + if (!filename) + goto fail; + mode = JS_ToCString(ctx, argv[1]); + if (!mode) + goto fail; + if (mode[strspn(mode, "rw")] != '\0') { + JS_ThrowTypeError(ctx, "invalid file mode"); + goto fail; + } + + f = popen(filename, mode); + if (!f) + err = errno; + else + err = 0; + if (argc >= 3) + js_set_error_object(ctx, argv[2], err); + JS_FreeCString(ctx, filename); + JS_FreeCString(ctx, mode); + if (!f) + return JS_NULL; + return js_new_std_file(ctx, f, TRUE); + fail: + JS_FreeCString(ctx, filename); + JS_FreeCString(ctx, mode); + return JS_EXCEPTION; +} +#endif // !defined(__wasi__) + +static JSValue js_std_fdopen(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + const char *mode; + FILE *f; + int fd, err; + + if (JS_ToInt32(ctx, &fd, argv[0])) + return JS_EXCEPTION; + mode = JS_ToCString(ctx, argv[1]); + if (!mode) + goto fail; + if (mode[strspn(mode, "rwa+")] != '\0') { + JS_ThrowTypeError(ctx, "invalid file mode"); + goto fail; + } + + f = fdopen(fd, mode); + if (!f) + err = errno; + else + err = 0; + if (argc >= 3) + js_set_error_object(ctx, argv[2], err); + JS_FreeCString(ctx, mode); + if (!f) + return JS_NULL; + return js_new_std_file(ctx, f, FALSE); + fail: + JS_FreeCString(ctx, mode); + return JS_EXCEPTION; +} + +#if !defined(__wasi__) +static JSValue js_std_tmpfile(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + FILE *f; + f = tmpfile(); + if (argc >= 1) + js_set_error_object(ctx, argv[0], f ? 0 : errno); + if (!f) + return JS_NULL; + return js_new_std_file(ctx, f, FALSE); +} +#endif + +static JSValue js_std_sprintf(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + return js_printf_internal(ctx, argc, argv, NULL); +} + +static JSValue js_std_printf(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + return js_printf_internal(ctx, argc, argv, stdout); +} + +static FILE *js_std_file_get(JSContext *ctx, JSValue obj) +{ + JSRuntime *rt = JS_GetRuntime(ctx); + JSThreadState *ts = js_get_thread_state(rt); + JSSTDFile *s = JS_GetOpaque2(ctx, obj, ts->std_file_class_id); + if (!s) + return NULL; + if (!s->f) { + JS_ThrowTypeError(ctx, "invalid file handle"); + return NULL; + } + return s->f; +} + +static JSValue js_std_file_puts(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv, int magic) +{ + FILE *f; + int i; + const char *str; + size_t len; + + if (magic == 0) { + f = stdout; + } else { + f = js_std_file_get(ctx, this_val); + if (!f) + return JS_EXCEPTION; + } + + for(i = 0; i < argc; i++) { + str = JS_ToCStringLen(ctx, &len, argv[i]); + if (!str) + return JS_EXCEPTION; + fwrite(str, 1, len, f); + JS_FreeCString(ctx, str); + } + return JS_UNDEFINED; +} + +static JSValue js_std_file_close(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + JSRuntime *rt = JS_GetRuntime(ctx); + JSThreadState *ts = js_get_thread_state(rt); + JSSTDFile *s = JS_GetOpaque2(ctx, this_val, ts->std_file_class_id); + int err; + if (!s) + return JS_EXCEPTION; + if (!s->f) + return JS_ThrowTypeError(ctx, "invalid file handle"); + if (is_stdio(s->f)) + return JS_ThrowTypeError(ctx, "cannot close stdio"); +#if !defined(__wasi__) + if (s->is_popen) + err = js_get_errno(pclose(s->f)); + else +#endif + err = js_get_errno(fclose(s->f)); + s->f = NULL; + return JS_NewInt32(ctx, err); +} + +static JSValue js_std_file_printf(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + FILE *f = js_std_file_get(ctx, this_val); + if (!f) + return JS_EXCEPTION; + return js_printf_internal(ctx, argc, argv, f); +} + +static JSValue js_std_file_flush(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + FILE *f = js_std_file_get(ctx, this_val); + if (!f) + return JS_EXCEPTION; + fflush(f); + return JS_UNDEFINED; +} + +static JSValue js_std_file_tell(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv, int is_bigint) +{ + FILE *f = js_std_file_get(ctx, this_val); + int64_t pos; + if (!f) + return JS_EXCEPTION; +#if defined(__linux__) + pos = ftello(f); +#else + pos = ftell(f); +#endif + if (is_bigint) + return JS_NewBigInt64(ctx, pos); + else + return JS_NewInt64(ctx, pos); +} + +static JSValue js_std_file_seek(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + FILE *f = js_std_file_get(ctx, this_val); + int64_t pos; + int whence, ret; + if (!f) + return JS_EXCEPTION; + if (JS_ToInt64Ext(ctx, &pos, argv[0])) + return JS_EXCEPTION; + if (JS_ToInt32(ctx, &whence, argv[1])) + return JS_EXCEPTION; +#if defined(__linux__) + ret = fseeko(f, pos, whence); +#else + ret = fseek(f, pos, whence); +#endif + if (ret < 0) + ret = -errno; + return JS_NewInt32(ctx, ret); +} + +static JSValue js_std_file_eof(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + FILE *f = js_std_file_get(ctx, this_val); + if (!f) + return JS_EXCEPTION; + return JS_NewBool(ctx, feof(f)); +} + +static JSValue js_std_file_error(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + FILE *f = js_std_file_get(ctx, this_val); + if (!f) + return JS_EXCEPTION; + return JS_NewBool(ctx, ferror(f)); +} + +static JSValue js_std_file_clearerr(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + FILE *f = js_std_file_get(ctx, this_val); + if (!f) + return JS_EXCEPTION; + clearerr(f); + return JS_UNDEFINED; +} + +static JSValue js_std_file_fileno(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + FILE *f = js_std_file_get(ctx, this_val); + if (!f) + return JS_EXCEPTION; + return JS_NewInt32(ctx, fileno(f)); +} + +static JSValue js_std_file_read_write(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv, int magic) +{ + FILE *f = js_std_file_get(ctx, this_val); + uint64_t pos, len; + size_t size, ret; + uint8_t *buf; + + if (!f) + return JS_EXCEPTION; + if (JS_ToIndex(ctx, &pos, argv[1])) + return JS_EXCEPTION; + if (JS_ToIndex(ctx, &len, argv[2])) + return JS_EXCEPTION; + buf = JS_GetArrayBuffer(ctx, &size, argv[0]); + if (!buf) + return JS_EXCEPTION; + if (pos + len > size) + return JS_ThrowRangeError(ctx, "read/write array buffer overflow"); + if (magic) + ret = fwrite(buf + pos, 1, len, f); + else + ret = fread(buf + pos, 1, len, f); + return JS_NewInt64(ctx, ret); +} + +/* XXX: could use less memory and go faster */ +static JSValue js_std_file_getline(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + FILE *f = js_std_file_get(ctx, this_val); + int c; + DynBuf dbuf; + JSValue obj; + + if (!f) + return JS_EXCEPTION; + + js_std_dbuf_init(ctx, &dbuf); + for(;;) { + c = fgetc(f); + if (c == EOF) { + if (dbuf.size == 0) { + /* EOF */ + dbuf_free(&dbuf); + return JS_NULL; + } else { + break; + } + } + if (c == '\n') + break; + if (dbuf_putc(&dbuf, c)) { + dbuf_free(&dbuf); + return JS_ThrowOutOfMemory(ctx); + } + } + obj = JS_NewStringLen(ctx, (const char *)dbuf.buf, dbuf.size); + dbuf_free(&dbuf); + return obj; +} + +/* XXX: could use less memory and go faster */ +static JSValue js_std_file_readAsString(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + FILE *f = js_std_file_get(ctx, this_val); + int c; + DynBuf dbuf; + JSValue obj; + uint64_t max_size64; + size_t max_size; + JSValue max_size_val; + + if (!f) + return JS_EXCEPTION; + + if (argc >= 1) + max_size_val = argv[0]; + else + max_size_val = JS_UNDEFINED; + max_size = (size_t)-1; + if (!JS_IsUndefined(max_size_val)) { + if (JS_ToIndex(ctx, &max_size64, max_size_val)) + return JS_EXCEPTION; + if (max_size64 < max_size) + max_size = max_size64; + } + + js_std_dbuf_init(ctx, &dbuf); + while (max_size != 0) { + c = fgetc(f); + if (c == EOF) + break; + if (dbuf_putc(&dbuf, c)) { + dbuf_free(&dbuf); + return JS_EXCEPTION; + } + max_size--; + } + obj = JS_NewStringLen(ctx, (const char *)dbuf.buf, dbuf.size); + dbuf_free(&dbuf); + return obj; +} + +static JSValue js_std_file_getByte(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + FILE *f = js_std_file_get(ctx, this_val); + if (!f) + return JS_EXCEPTION; + return JS_NewInt32(ctx, fgetc(f)); +} + +static JSValue js_std_file_putByte(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + FILE *f = js_std_file_get(ctx, this_val); + int c; + if (!f) + return JS_EXCEPTION; + if (JS_ToInt32(ctx, &c, argv[0])) + return JS_EXCEPTION; + c = fputc(c, f); + return JS_NewInt32(ctx, c); +} + +/* urlGet */ +#if !defined(__wasi__) + +#define URL_GET_PROGRAM "curl -s -i --" +#define URL_GET_BUF_SIZE 4096 + +static int http_get_header_line(FILE *f, char *buf, size_t buf_size, + DynBuf *dbuf) +{ + int c; + char *p; + + p = buf; + for(;;) { + c = fgetc(f); + if (c < 0) + return -1; + if ((p - buf) < buf_size - 1) + *p++ = c; + if (dbuf) + dbuf_putc(dbuf, c); + if (c == '\n') + break; + } + *p = '\0'; + return 0; +} + +static int http_get_status(const char *buf) +{ + const char *p = buf; + while (*p != ' ' && *p != '\0') + p++; + if (*p != ' ') + return 0; + while (*p == ' ') + p++; + return atoi(p); +} + +static JSValue js_std_urlGet(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + const char *url; + DynBuf cmd_buf; + DynBuf data_buf_s, *data_buf = &data_buf_s; + DynBuf header_buf_s, *header_buf = &header_buf_s; + char *buf; + size_t i, len; + int status; + JSValue response = JS_UNDEFINED, ret_obj; + JSValue options_obj; + FILE *f; + BOOL binary_flag, full_flag; + + url = JS_ToCString(ctx, argv[0]); + if (!url) + return JS_EXCEPTION; + + binary_flag = FALSE; + full_flag = FALSE; + + if (argc >= 2) { + options_obj = argv[1]; + + if (get_bool_option(ctx, &binary_flag, options_obj, "binary")) + goto fail_obj; + + if (get_bool_option(ctx, &full_flag, options_obj, "full")) { + fail_obj: + JS_FreeCString(ctx, url); + return JS_EXCEPTION; + } + } + + js_std_dbuf_init(ctx, &cmd_buf); + dbuf_printf(&cmd_buf, "%s '", URL_GET_PROGRAM); + for(i = 0; url[i] != '\0'; i++) { + unsigned char c = url[i]; + switch (c) { + case '\'': + /* shell single quoted string does not support \' */ + dbuf_putstr(&cmd_buf, "'\\''"); + break; + case '[': case ']': case '{': case '}': case '\\': + /* prevent interpretation by curl as range or set specification */ + dbuf_putc(&cmd_buf, '\\'); + /* FALLTHROUGH */ + default: + dbuf_putc(&cmd_buf, c); + break; + } + } + JS_FreeCString(ctx, url); + dbuf_putstr(&cmd_buf, "'"); + dbuf_putc(&cmd_buf, '\0'); + if (dbuf_error(&cmd_buf)) { + dbuf_free(&cmd_buf); + return JS_EXCEPTION; + } + // printf("%s\n", (char *)cmd_buf.buf); + f = popen((char *)cmd_buf.buf, "r"); + dbuf_free(&cmd_buf); + if (!f) { + return JS_ThrowTypeError(ctx, "could not start curl"); + } + + js_std_dbuf_init(ctx, data_buf); + js_std_dbuf_init(ctx, header_buf); + + buf = js_malloc(ctx, URL_GET_BUF_SIZE); + if (!buf) + goto fail; + + /* get the HTTP status */ + if (http_get_header_line(f, buf, URL_GET_BUF_SIZE, NULL) < 0) { + status = 0; + goto bad_header; + } + status = http_get_status(buf); + if (!full_flag && !(status >= 200 && status <= 299)) { + goto bad_header; + } + + /* wait until there is an empty line */ + for(;;) { + if (http_get_header_line(f, buf, URL_GET_BUF_SIZE, header_buf) < 0) { + bad_header: + response = JS_NULL; + goto done; + } + if (!strcmp(buf, "\r\n")) + break; + } + if (dbuf_error(header_buf)) + goto fail; + header_buf->size -= 2; /* remove the trailing CRLF */ + + /* download the data */ + for(;;) { + len = fread(buf, 1, URL_GET_BUF_SIZE, f); + if (len == 0) + break; + dbuf_put(data_buf, (uint8_t *)buf, len); + } + if (dbuf_error(data_buf)) + goto fail; + if (binary_flag) { + response = JS_NewArrayBufferCopy(ctx, + data_buf->buf, data_buf->size); + } else { + response = JS_NewStringLen(ctx, (char *)data_buf->buf, data_buf->size); + } + if (JS_IsException(response)) + goto fail; + done: + js_free(ctx, buf); + buf = NULL; + pclose(f); + f = NULL; + dbuf_free(data_buf); + data_buf = NULL; + + if (full_flag) { + ret_obj = JS_NewObject(ctx); + if (JS_IsException(ret_obj)) + goto fail; + JS_DefinePropertyValueStr(ctx, ret_obj, "response", + response, + JS_PROP_C_W_E); + if (!JS_IsNull(response)) { + JS_DefinePropertyValueStr(ctx, ret_obj, "responseHeaders", + JS_NewStringLen(ctx, (char *)header_buf->buf, + header_buf->size), + JS_PROP_C_W_E); + JS_DefinePropertyValueStr(ctx, ret_obj, "status", + JS_NewInt32(ctx, status), + JS_PROP_C_W_E); + } + } else { + ret_obj = response; + } + dbuf_free(header_buf); + return ret_obj; + fail: + if (f) + pclose(f); + js_free(ctx, buf); + if (data_buf) + dbuf_free(data_buf); + if (header_buf) + dbuf_free(header_buf); + JS_FreeValue(ctx, response); + return JS_EXCEPTION; +} +#endif // !defined(__wasi__) + +static JSClassDef js_std_file_class = { + "FILE", + .finalizer = js_std_file_finalizer, +}; + +static const JSCFunctionListEntry js_std_error_props[] = { + /* various errno values */ +#define DEF(x) JS_PROP_INT32_DEF(#x, x, JS_PROP_CONFIGURABLE ) + DEF(EINVAL), + DEF(EIO), + DEF(EACCES), + DEF(EEXIST), + DEF(ENOSPC), + DEF(ENOSYS), + DEF(EBUSY), + DEF(ENOENT), + DEF(EPERM), + DEF(EPIPE), + DEF(EBADF), +#undef DEF +}; + +static const JSCFunctionListEntry js_std_funcs[] = { + JS_CFUNC_DEF("exit", 1, js_std_exit ), + JS_CFUNC_DEF("gc", 0, js_std_gc ), + JS_CFUNC_DEF("evalScript", 1, js_evalScript ), + JS_CFUNC_DEF("loadScript", 1, js_loadScript ), + JS_CFUNC_DEF("getenv", 1, js_std_getenv ), + JS_CFUNC_DEF("setenv", 1, js_std_setenv ), + JS_CFUNC_DEF("unsetenv", 1, js_std_unsetenv ), + JS_CFUNC_DEF("getenviron", 1, js_std_getenviron ), +#if !defined(__wasi__) + JS_CFUNC_DEF("urlGet", 1, js_std_urlGet ), +#endif + JS_CFUNC_DEF("loadFile", 1, js_std_loadFile ), + JS_CFUNC_DEF("strerror", 1, js_std_strerror ), + + /* FILE I/O */ + JS_CFUNC_DEF("open", 2, js_std_open ), +#if !defined(__wasi__) + JS_CFUNC_DEF("popen", 2, js_std_popen ), + JS_CFUNC_DEF("tmpfile", 0, js_std_tmpfile ), +#endif + JS_CFUNC_DEF("fdopen", 2, js_std_fdopen ), + JS_CFUNC_MAGIC_DEF("puts", 1, js_std_file_puts, 0 ), + JS_CFUNC_DEF("printf", 1, js_std_printf ), + JS_CFUNC_DEF("sprintf", 1, js_std_sprintf ), + JS_PROP_INT32_DEF("SEEK_SET", SEEK_SET, JS_PROP_CONFIGURABLE ), + JS_PROP_INT32_DEF("SEEK_CUR", SEEK_CUR, JS_PROP_CONFIGURABLE ), + JS_PROP_INT32_DEF("SEEK_END", SEEK_END, JS_PROP_CONFIGURABLE ), + JS_OBJECT_DEF("Error", js_std_error_props, countof(js_std_error_props), JS_PROP_CONFIGURABLE), +}; + +static const JSCFunctionListEntry js_std_file_proto_funcs[] = { + JS_CFUNC_DEF("close", 0, js_std_file_close ), + JS_CFUNC_MAGIC_DEF("puts", 1, js_std_file_puts, 1 ), + JS_CFUNC_DEF("printf", 1, js_std_file_printf ), + JS_CFUNC_DEF("flush", 0, js_std_file_flush ), + JS_CFUNC_MAGIC_DEF("tell", 0, js_std_file_tell, 0 ), + JS_CFUNC_MAGIC_DEF("tello", 0, js_std_file_tell, 1 ), + JS_CFUNC_DEF("seek", 2, js_std_file_seek ), + JS_CFUNC_DEF("eof", 0, js_std_file_eof ), + JS_CFUNC_DEF("fileno", 0, js_std_file_fileno ), + JS_CFUNC_DEF("error", 0, js_std_file_error ), + JS_CFUNC_DEF("clearerr", 0, js_std_file_clearerr ), + JS_CFUNC_MAGIC_DEF("read", 3, js_std_file_read_write, 0 ), + JS_CFUNC_MAGIC_DEF("write", 3, js_std_file_read_write, 1 ), + JS_CFUNC_DEF("getline", 0, js_std_file_getline ), + JS_CFUNC_DEF("readAsString", 0, js_std_file_readAsString ), + JS_CFUNC_DEF("getByte", 0, js_std_file_getByte ), + JS_CFUNC_DEF("putByte", 1, js_std_file_putByte ), + /* setvbuf, ... */ +}; + +static int js_std_init(JSContext *ctx, JSModuleDef *m) +{ + JSValue proto; + JSRuntime *rt = JS_GetRuntime(ctx); + JSThreadState *ts = js_get_thread_state(rt); + + /* FILE class */ + /* the class ID is created once */ + JS_NewClassID(rt, &ts->std_file_class_id); + /* the class is created once per runtime */ + JS_NewClass(rt, ts->std_file_class_id, &js_std_file_class); + proto = JS_NewObject(ctx); + JS_SetPropertyFunctionList(ctx, proto, js_std_file_proto_funcs, + countof(js_std_file_proto_funcs)); + JS_SetClassProto(ctx, ts->std_file_class_id, proto); + + JS_SetModuleExportList(ctx, m, js_std_funcs, + countof(js_std_funcs)); + JS_SetModuleExport(ctx, m, "in", js_new_std_file(ctx, stdin, FALSE)); + JS_SetModuleExport(ctx, m, "out", js_new_std_file(ctx, stdout, FALSE)); + JS_SetModuleExport(ctx, m, "err", js_new_std_file(ctx, stderr, FALSE)); + return 0; +} + +JSModuleDef *js_init_module_std(JSContext *ctx, const char *module_name) +{ + JSModuleDef *m; + m = JS_NewCModule(ctx, module_name, js_std_init); + if (!m) + return NULL; + JS_AddModuleExportList(ctx, m, js_std_funcs, countof(js_std_funcs)); + JS_AddModuleExport(ctx, m, "in"); + JS_AddModuleExport(ctx, m, "out"); + JS_AddModuleExport(ctx, m, "err"); + return m; +} + +/**********************************************************/ +/* 'os' object */ + +static JSValue js_os_open(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + const char *filename; + int flags, mode, ret; + + filename = JS_ToCString(ctx, argv[0]); + if (!filename) + return JS_EXCEPTION; + if (JS_ToInt32(ctx, &flags, argv[1])) + goto fail; + if (argc >= 3 && !JS_IsUndefined(argv[2])) { + if (JS_ToInt32(ctx, &mode, argv[2])) { + fail: + JS_FreeCString(ctx, filename); + return JS_EXCEPTION; + } + } else { + mode = 0666; + } +#if defined(_WIN32) + /* force binary mode by default */ + if (!(flags & O_TEXT)) + flags |= O_BINARY; +#endif + ret = js_get_errno(open(filename, flags, mode)); + JS_FreeCString(ctx, filename); + return JS_NewInt32(ctx, ret); +} + +static JSValue js_os_close(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + int fd, ret; + if (JS_ToInt32(ctx, &fd, argv[0])) + return JS_EXCEPTION; + ret = js_get_errno(close(fd)); + return JS_NewInt32(ctx, ret); +} + +static JSValue js_os_seek(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + int fd, whence; + int64_t pos, ret; + BOOL is_bigint; + + if (JS_ToInt32(ctx, &fd, argv[0])) + return JS_EXCEPTION; + is_bigint = JS_IsBigInt(ctx, argv[1]); + if (JS_ToInt64Ext(ctx, &pos, argv[1])) + return JS_EXCEPTION; + if (JS_ToInt32(ctx, &whence, argv[2])) + return JS_EXCEPTION; + ret = lseek(fd, pos, whence); + if (ret == -1) + ret = -errno; + if (is_bigint) + return JS_NewBigInt64(ctx, ret); + else + return JS_NewInt64(ctx, ret); +} + +static JSValue js_os_read_write(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv, int magic) +{ + int fd; + uint64_t pos, len; + size_t size; + ssize_t ret; + uint8_t *buf; + + if (JS_ToInt32(ctx, &fd, argv[0])) + return JS_EXCEPTION; + if (JS_ToIndex(ctx, &pos, argv[2])) + return JS_EXCEPTION; + if (JS_ToIndex(ctx, &len, argv[3])) + return JS_EXCEPTION; + buf = JS_GetArrayBuffer(ctx, &size, argv[1]); + if (!buf) + return JS_EXCEPTION; + if (pos + len > size) + return JS_ThrowRangeError(ctx, "read/write array buffer overflow"); + if (magic) + ret = js_get_errno(write(fd, buf + pos, len)); + else + ret = js_get_errno(read(fd, buf + pos, len)); + return JS_NewInt64(ctx, ret); +} + +static JSValue js_os_isatty(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + int fd; + if (JS_ToInt32(ctx, &fd, argv[0])) + return JS_EXCEPTION; + return JS_NewBool(ctx, (isatty(fd) != 0)); +} + +#if defined(_WIN32) +static JSValue js_os_ttyGetWinSize(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + int fd; + HANDLE handle; + CONSOLE_SCREEN_BUFFER_INFO info; + JSValue obj; + + if (JS_ToInt32(ctx, &fd, argv[0])) + return JS_EXCEPTION; + handle = (HANDLE)_get_osfhandle(fd); + + if (!GetConsoleScreenBufferInfo(handle, &info)) + return JS_NULL; + obj = JS_NewArray(ctx); + if (JS_IsException(obj)) + return obj; + JS_DefinePropertyValueUint32(ctx, obj, 0, JS_NewInt32(ctx, info.dwSize.X), JS_PROP_C_W_E); + JS_DefinePropertyValueUint32(ctx, obj, 1, JS_NewInt32(ctx, info.dwSize.Y), JS_PROP_C_W_E); + return obj; +} + +/* Windows 10 built-in VT100 emulation */ +#define __ENABLE_VIRTUAL_TERMINAL_PROCESSING 0x0004 +#define __ENABLE_VIRTUAL_TERMINAL_INPUT 0x0200 + +static JSValue js_os_ttySetRaw(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + int fd; + HANDLE handle; + + if (JS_ToInt32(ctx, &fd, argv[0])) + return JS_EXCEPTION; + handle = (HANDLE)_get_osfhandle(fd); + SetConsoleMode(handle, ENABLE_WINDOW_INPUT | __ENABLE_VIRTUAL_TERMINAL_INPUT); + _setmode(fd, _O_BINARY); + if (fd == 0) { + handle = (HANDLE)_get_osfhandle(1); /* corresponding output */ + SetConsoleMode(handle, ENABLE_PROCESSED_OUTPUT | ENABLE_WRAP_AT_EOL_OUTPUT | __ENABLE_VIRTUAL_TERMINAL_PROCESSING); + } + return JS_UNDEFINED; +} +#elif !defined(__wasi__) +static JSValue js_os_ttyGetWinSize(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + int fd; + struct winsize ws; + JSValue obj; + + if (JS_ToInt32(ctx, &fd, argv[0])) + return JS_EXCEPTION; + if (ioctl(fd, TIOCGWINSZ, &ws) == 0 && + ws.ws_col >= 4 && ws.ws_row >= 4) { + obj = JS_NewArray(ctx); + if (JS_IsException(obj)) + return obj; + JS_DefinePropertyValueUint32(ctx, obj, 0, JS_NewInt32(ctx, ws.ws_col), JS_PROP_C_W_E); + JS_DefinePropertyValueUint32(ctx, obj, 1, JS_NewInt32(ctx, ws.ws_row), JS_PROP_C_W_E); + return obj; + } else { + return JS_NULL; + } +} + +static struct termios oldtty; + +static void term_exit(void) +{ + tcsetattr(0, TCSANOW, &oldtty); +} + +/* XXX: should add a way to go back to normal mode */ +static JSValue js_os_ttySetRaw(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + struct termios tty; + int fd; + + if (JS_ToInt32(ctx, &fd, argv[0])) + return JS_EXCEPTION; + + memset(&tty, 0, sizeof(tty)); + tcgetattr(fd, &tty); + oldtty = tty; + + tty.c_iflag &= ~(IGNBRK|BRKINT|PARMRK|ISTRIP + |INLCR|IGNCR|ICRNL|IXON); + tty.c_oflag |= OPOST; + tty.c_lflag &= ~(ECHO|ECHONL|ICANON|IEXTEN); + tty.c_cflag &= ~(CSIZE|PARENB); + tty.c_cflag |= CS8; + tty.c_cc[VMIN] = 1; + tty.c_cc[VTIME] = 0; + + tcsetattr(fd, TCSANOW, &tty); + + atexit(term_exit); + return JS_UNDEFINED; +} + +#endif /* !_WIN32 && !__wasi__ */ + +static JSValue js_os_remove(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + const char *filename; + int ret; + + filename = JS_ToCString(ctx, argv[0]); + if (!filename) + return JS_EXCEPTION; +#if defined(_WIN32) + { + struct stat st; + if (stat(filename, &st) == 0 && S_ISDIR(st.st_mode)) { + ret = rmdir(filename); + } else { + ret = unlink(filename); + } + } +#else + ret = remove(filename); +#endif + ret = js_get_errno(ret); + JS_FreeCString(ctx, filename); + return JS_NewInt32(ctx, ret); +} + +static JSValue js_os_rename(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + const char *oldpath, *newpath; + int ret; + + oldpath = JS_ToCString(ctx, argv[0]); + if (!oldpath) + return JS_EXCEPTION; + newpath = JS_ToCString(ctx, argv[1]); + if (!newpath) { + JS_FreeCString(ctx, oldpath); + return JS_EXCEPTION; + } + ret = js_get_errno(rename(oldpath, newpath)); + JS_FreeCString(ctx, oldpath); + JS_FreeCString(ctx, newpath); + return JS_NewInt32(ctx, ret); +} + +static BOOL is_main_thread(JSRuntime *rt) +{ + JSThreadState *ts = js_get_thread_state(rt); + return !ts->recv_pipe; +} + +static JSOSRWHandler *find_rh(JSThreadState *ts, int fd) +{ + JSOSRWHandler *rh; + struct list_head *el; + + list_for_each(el, &ts->os_rw_handlers) { + rh = list_entry(el, JSOSRWHandler, link); + if (rh->fd == fd) + return rh; + } + return NULL; +} + +static void free_rw_handler(JSRuntime *rt, JSOSRWHandler *rh) +{ + int i; + list_del(&rh->link); + for(i = 0; i < 2; i++) { + JS_FreeValueRT(rt, rh->rw_func[i]); + } + js_free_rt(rt, rh); +} + +static JSValue js_os_setReadHandler(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv, int magic) +{ + JSRuntime *rt = JS_GetRuntime(ctx); + JSThreadState *ts = js_get_thread_state(rt); + JSOSRWHandler *rh; + int fd; + JSValue func; + + if (JS_ToInt32(ctx, &fd, argv[0])) + return JS_EXCEPTION; + func = argv[1]; + if (JS_IsNull(func)) { + rh = find_rh(ts, fd); + if (rh) { + JS_FreeValue(ctx, rh->rw_func[magic]); + rh->rw_func[magic] = JS_NULL; + if (JS_IsNull(rh->rw_func[0]) && + JS_IsNull(rh->rw_func[1])) { + /* remove the entry */ + free_rw_handler(JS_GetRuntime(ctx), rh); + } + } + } else { + if (!JS_IsFunction(ctx, func)) + return JS_ThrowTypeError(ctx, "not a function"); + rh = find_rh(ts, fd); + if (!rh) { + rh = js_mallocz(ctx, sizeof(*rh)); + if (!rh) + return JS_EXCEPTION; + rh->fd = fd; + rh->rw_func[0] = JS_NULL; + rh->rw_func[1] = JS_NULL; + list_add_tail(&rh->link, &ts->os_rw_handlers); + } + JS_FreeValue(ctx, rh->rw_func[magic]); + rh->rw_func[magic] = JS_DupValue(ctx, func); + } + return JS_UNDEFINED; +} + +static JSOSSignalHandler *find_sh(JSThreadState *ts, int sig_num) +{ + JSOSSignalHandler *sh; + struct list_head *el; + list_for_each(el, &ts->os_signal_handlers) { + sh = list_entry(el, JSOSSignalHandler, link); + if (sh->sig_num == sig_num) + return sh; + } + return NULL; +} + +static void free_sh(JSRuntime *rt, JSOSSignalHandler *sh) +{ + list_del(&sh->link); + JS_FreeValueRT(rt, sh->func); + js_free_rt(rt, sh); +} + +static void os_signal_handler(int sig_num) +{ + os_pending_signals |= ((uint64_t)1 << sig_num); +} + +#if defined(_WIN32) +typedef void (*sighandler_t)(int sig_num); +#endif + +static JSValue js_os_signal(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + JSRuntime *rt = JS_GetRuntime(ctx); + JSThreadState *ts = js_get_thread_state(rt); + JSOSSignalHandler *sh; + uint32_t sig_num; + JSValue func; + sighandler_t handler; + + if (!is_main_thread(rt)) + return JS_ThrowTypeError(ctx, "signal handler can only be set in the main thread"); + + if (JS_ToUint32(ctx, &sig_num, argv[0])) + return JS_EXCEPTION; + if (sig_num >= 64) + return JS_ThrowRangeError(ctx, "invalid signal number"); + func = argv[1]; + /* func = null: SIG_DFL, func = undefined, SIG_IGN */ + if (JS_IsNull(func) || JS_IsUndefined(func)) { + sh = find_sh(ts, sig_num); + if (sh) { + free_sh(JS_GetRuntime(ctx), sh); + } + if (JS_IsNull(func)) + handler = SIG_DFL; + else + handler = SIG_IGN; + signal(sig_num, handler); + } else { + if (!JS_IsFunction(ctx, func)) + return JS_ThrowTypeError(ctx, "not a function"); + sh = find_sh(ts, sig_num); + if (!sh) { + sh = js_mallocz(ctx, sizeof(*sh)); + if (!sh) + return JS_EXCEPTION; + sh->sig_num = sig_num; + list_add_tail(&sh->link, &ts->os_signal_handlers); + } + JS_FreeValue(ctx, sh->func); + sh->func = JS_DupValue(ctx, func); + signal(sig_num, os_signal_handler); + } + return JS_UNDEFINED; +} + +#if !defined(_WIN32) && !defined(__wasi__) +static JSValue js_os_cputime(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + struct rusage ru; + int64_t cputime; + + cputime = 0; + if (!getrusage(RUSAGE_SELF, &ru)) + cputime = (int64_t)ru.ru_utime.tv_sec * 1000000 + ru.ru_utime.tv_usec; + + return JS_NewInt64(ctx, cputime); +} +#endif + +static JSValue js_os_now(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + return JS_NewInt64(ctx, js__hrtime_ns() / 1000); +} + +static uint64_t js__hrtime_ms(void) +{ + return js__hrtime_ns() / (1000 * 1000); +} + +static void free_timer(JSRuntime *rt, JSOSTimer *th) +{ + list_del(&th->link); + JS_FreeValueRT(rt, th->func); + js_free_rt(rt, th); +} + +// TODO(bnoordhuis) accept string as first arg and eval at timer expiry +// TODO(bnoordhuis) retain argv[2..] as args for callback if argc > 2 +static JSValue js_os_setTimeout(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv, int magic) +{ + JSRuntime *rt = JS_GetRuntime(ctx); + JSThreadState *ts = js_get_thread_state(rt); + int64_t delay; + JSValue func; + JSOSTimer *th; + + func = argv[0]; + if (!JS_IsFunction(ctx, func)) + return JS_ThrowTypeError(ctx, "not a function"); + if (JS_ToInt64(ctx, &delay, argv[1])) + return JS_EXCEPTION; + if (delay < 1) + delay = 1; + th = js_mallocz(ctx, sizeof(*th)); + if (!th) + return JS_EXCEPTION; + th->timer_id = ts->next_timer_id++; + if (ts->next_timer_id > MAX_SAFE_INTEGER) + ts->next_timer_id = 1; + th->repeats = (magic > 0); + th->timeout = js__hrtime_ms() + delay; + th->delay = delay; + th->func = JS_DupValue(ctx, func); + list_add_tail(&th->link, &ts->os_timers); + return JS_NewInt64(ctx, th->timer_id); +} + +static JSOSTimer *find_timer_by_id(JSThreadState *ts, int timer_id) +{ + struct list_head *el; + if (timer_id <= 0) + return NULL; + list_for_each(el, &ts->os_timers) { + JSOSTimer *th = list_entry(el, JSOSTimer, link); + if (th->timer_id == timer_id) + return th; + } + return NULL; +} + +static JSValue js_os_clearTimeout(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + JSRuntime *rt = JS_GetRuntime(ctx); + JSThreadState *ts = js_get_thread_state(rt); + JSOSTimer *th; + int64_t timer_id; + + if (JS_ToInt64(ctx, &timer_id, argv[0])) + return JS_EXCEPTION; + th = find_timer_by_id(ts, timer_id); + if (!th) + return JS_UNDEFINED; + free_timer(rt, th); + return JS_UNDEFINED; +} + +/* return a promise */ +static JSValue js_os_sleepAsync(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + JSRuntime *rt = JS_GetRuntime(ctx); + JSThreadState *ts = js_get_thread_state(rt); + int64_t delay; + JSOSTimer *th; + JSValue promise, resolving_funcs[2]; + + if (JS_ToInt64(ctx, &delay, argv[0])) + return JS_EXCEPTION; + promise = JS_NewPromiseCapability(ctx, resolving_funcs); + if (JS_IsException(promise)) + return JS_EXCEPTION; + + th = js_mallocz(ctx, sizeof(*th)); + if (!th) { + JS_FreeValue(ctx, promise); + JS_FreeValue(ctx, resolving_funcs[0]); + JS_FreeValue(ctx, resolving_funcs[1]); + return JS_EXCEPTION; + } + th->timer_id = -1; + th->timeout = js__hrtime_ms() + delay; + th->func = JS_DupValue(ctx, resolving_funcs[0]); + list_add_tail(&th->link, &ts->os_timers); + JS_FreeValue(ctx, resolving_funcs[0]); + JS_FreeValue(ctx, resolving_funcs[1]); + return promise; +} + +static int call_handler(JSContext *ctx, JSValue func) +{ + int r; + JSValue ret, func1; + /* 'func' might be destroyed when calling itself (if it frees the + handler), so must take extra care */ + func1 = JS_DupValue(ctx, func); + ret = JS_Call(ctx, func1, JS_UNDEFINED, 0, NULL); + JS_FreeValue(ctx, func1); + r = 0; + if (JS_IsException(ret)) { + JSRuntime *rt = JS_GetRuntime(ctx); + JSThreadState *ts = js_get_thread_state(rt); + ts->exc = JS_GetException(ctx); + r = -1; + } + JS_FreeValue(ctx, ret); + return r; +} + +static int js_os_run_timers(JSRuntime *rt, JSContext *ctx, JSThreadState *ts, int *min_delay) +{ + JSValue func; + JSOSTimer *th; + int64_t cur_time, delay; + struct list_head *el; + int r; + + if (list_empty(&ts->os_timers)) { + *min_delay = -1; + return 0; + } + + cur_time = js__hrtime_ms(); + *min_delay = INT32_MAX; + + list_for_each(el, &ts->os_timers) { + th = list_entry(el, JSOSTimer, link); + delay = th->timeout - cur_time; + if (delay > 0) { + *min_delay = min_int(*min_delay, delay); + } else { + *min_delay = 0; + func = JS_DupValueRT(rt, th->func); + if (th->repeats) + th->timeout = cur_time + th->delay; + else + free_timer(rt, th); + r = call_handler(ctx, func); + JS_FreeValueRT(rt, func); + return r; + } + } + + return 0; +} + +#if defined(_WIN32) + +static int js_os_poll(JSContext *ctx) +{ + JSRuntime *rt = JS_GetRuntime(ctx); + JSThreadState *ts = js_get_thread_state(rt); + int min_delay, console_fd; + JSOSRWHandler *rh; + struct list_head *el; + + /* XXX: handle signals if useful */ + + if (js_os_run_timers(rt, ctx, ts, &min_delay)) + return -1; + if (min_delay == 0) + return 0; // expired timer + if (min_delay < 0) + if (list_empty(&ts->os_rw_handlers)) + return -1; /* no more events */ + + console_fd = -1; + list_for_each(el, &ts->os_rw_handlers) { + rh = list_entry(el, JSOSRWHandler, link); + if (rh->fd == 0 && !JS_IsNull(rh->rw_func[0])) { + console_fd = rh->fd; + break; + } + } + + if (console_fd >= 0) { + DWORD ti, ret; + HANDLE handle; + if (min_delay == -1) + ti = INFINITE; + else + ti = min_delay; + handle = (HANDLE)_get_osfhandle(console_fd); + ret = WaitForSingleObject(handle, ti); + if (ret == WAIT_OBJECT_0) { + list_for_each(el, &ts->os_rw_handlers) { + rh = list_entry(el, JSOSRWHandler, link); + if (rh->fd == console_fd && !JS_IsNull(rh->rw_func[0])) { + return call_handler(ctx, rh->rw_func[0]); + /* must stop because the list may have been modified */ + } + } + } + } else { + Sleep(min_delay); + } + return 0; +} +#else + +#ifdef USE_WORKER + +static void js_free_message(JSWorkerMessage *msg); + +/* return 1 if a message was handled, 0 if no message */ +static int handle_posted_message(JSRuntime *rt, JSContext *ctx, + JSWorkerMessageHandler *port) +{ + JSWorkerMessagePipe *ps = port->recv_pipe; + int ret; + struct list_head *el; + JSWorkerMessage *msg; + JSValue obj, data_obj, func, retval; + + pthread_mutex_lock(&ps->mutex); + if (!list_empty(&ps->msg_queue)) { + el = ps->msg_queue.next; + msg = list_entry(el, JSWorkerMessage, link); + + /* remove the message from the queue */ + list_del(&msg->link); + + if (list_empty(&ps->msg_queue)) { + uint8_t buf[16]; + int ret; + for(;;) { + ret = read(ps->read_fd, buf, sizeof(buf)); + if (ret >= 0) + break; + if (errno != EAGAIN && errno != EINTR) + break; + } + } + + pthread_mutex_unlock(&ps->mutex); + + data_obj = JS_ReadObject(ctx, msg->data, msg->data_len, + JS_READ_OBJ_SAB | JS_READ_OBJ_REFERENCE); + + js_free_message(msg); + + if (JS_IsException(data_obj)) + goto fail; + obj = JS_NewObject(ctx); + if (JS_IsException(obj)) { + JS_FreeValue(ctx, data_obj); + goto fail; + } + JS_DefinePropertyValueStr(ctx, obj, "data", data_obj, JS_PROP_C_W_E); + + /* 'func' might be destroyed when calling itself (if it frees the + handler), so must take extra care */ + func = JS_DupValue(ctx, port->on_message_func); + retval = JS_Call(ctx, func, JS_UNDEFINED, 1, &obj); + JS_FreeValue(ctx, obj); + JS_FreeValue(ctx, func); + if (JS_IsException(retval)) { + fail: + js_std_dump_error(ctx); + } else { + JS_FreeValue(ctx, retval); + } + ret = 1; + } else { + pthread_mutex_unlock(&ps->mutex); + ret = 0; + } + return ret; +} +#else +static int handle_posted_message(JSRuntime *rt, JSContext *ctx, + JSWorkerMessageHandler *port) +{ + return 0; +} +#endif + +static int js_os_poll(JSContext *ctx) +{ + JSRuntime *rt = JS_GetRuntime(ctx); + JSThreadState *ts = js_get_thread_state(rt); + int ret, fd_max, min_delay; + fd_set rfds, wfds; + JSOSRWHandler *rh; + struct list_head *el; + struct timeval tv, *tvp; + + /* only check signals in the main thread */ + if (!ts->recv_pipe && + unlikely(os_pending_signals != 0)) { + JSOSSignalHandler *sh; + uint64_t mask; + + list_for_each(el, &ts->os_signal_handlers) { + sh = list_entry(el, JSOSSignalHandler, link); + mask = (uint64_t)1 << sh->sig_num; + if (os_pending_signals & mask) { + os_pending_signals &= ~mask; + return call_handler(ctx, sh->func); + } + } + } + + if (js_os_run_timers(rt, ctx, ts, &min_delay)) + return -1; + if (min_delay == 0) + return 0; // expired timer + if (min_delay < 0) + if (list_empty(&ts->os_rw_handlers) && list_empty(&ts->port_list)) + return -1; /* no more events */ + + tvp = NULL; + if (min_delay >= 0) { + tv.tv_sec = min_delay / 1000; + tv.tv_usec = (min_delay % 1000) * 1000; + tvp = &tv; + } + + FD_ZERO(&rfds); + FD_ZERO(&wfds); + fd_max = -1; + list_for_each(el, &ts->os_rw_handlers) { + rh = list_entry(el, JSOSRWHandler, link); + fd_max = max_int(fd_max, rh->fd); + if (!JS_IsNull(rh->rw_func[0])) + FD_SET(rh->fd, &rfds); + if (!JS_IsNull(rh->rw_func[1])) + FD_SET(rh->fd, &wfds); + } + + list_for_each(el, &ts->port_list) { + JSWorkerMessageHandler *port = list_entry(el, JSWorkerMessageHandler, link); + if (!JS_IsNull(port->on_message_func)) { + JSWorkerMessagePipe *ps = port->recv_pipe; + fd_max = max_int(fd_max, ps->read_fd); + FD_SET(ps->read_fd, &rfds); + } + } + + ret = select(fd_max + 1, &rfds, &wfds, NULL, tvp); + if (ret > 0) { + list_for_each(el, &ts->os_rw_handlers) { + rh = list_entry(el, JSOSRWHandler, link); + if (!JS_IsNull(rh->rw_func[0]) && + FD_ISSET(rh->fd, &rfds)) { + return call_handler(ctx, rh->rw_func[0]); + /* must stop because the list may have been modified */ + } + if (!JS_IsNull(rh->rw_func[1]) && + FD_ISSET(rh->fd, &wfds)) { + return call_handler(ctx, rh->rw_func[1]); + /* must stop because the list may have been modified */ + } + } + + list_for_each(el, &ts->port_list) { + JSWorkerMessageHandler *port = list_entry(el, JSWorkerMessageHandler, link); + if (!JS_IsNull(port->on_message_func)) { + JSWorkerMessagePipe *ps = port->recv_pipe; + if (FD_ISSET(ps->read_fd, &rfds)) { + if (handle_posted_message(rt, ctx, port)) + goto done; + } + } + } + } + done: + return 0; +} +#endif /* !_WIN32 */ + +static JSValue make_obj_error(JSContext *ctx, + JSValue obj, + int err) +{ + JSValue arr; + if (JS_IsException(obj)) + return obj; + arr = JS_NewArray(ctx); + if (JS_IsException(arr)) + return JS_EXCEPTION; + JS_DefinePropertyValueUint32(ctx, arr, 0, obj, + JS_PROP_C_W_E); + JS_DefinePropertyValueUint32(ctx, arr, 1, JS_NewInt32(ctx, err), + JS_PROP_C_W_E); + return arr; +} + +static JSValue make_string_error(JSContext *ctx, + const char *buf, + int err) +{ + return make_obj_error(ctx, JS_NewString(ctx, buf), err); +} + +/* return [cwd, errorcode] */ +static JSValue js_os_getcwd(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + char buf[PATH_MAX]; + int err; + + if (!getcwd(buf, sizeof(buf))) { + buf[0] = '\0'; + err = errno; + } else { + err = 0; + } + return make_string_error(ctx, buf, err); +} + +static JSValue js_os_chdir(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + const char *target; + int err; + + target = JS_ToCString(ctx, argv[0]); + if (!target) + return JS_EXCEPTION; + err = js_get_errno(chdir(target)); + JS_FreeCString(ctx, target); + return JS_NewInt32(ctx, err); +} + +static JSValue js_os_mkdir(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + int mode, ret; + const char *path; + + if (argc >= 2) { + if (JS_ToInt32(ctx, &mode, argv[1])) + return JS_EXCEPTION; + } else { + mode = 0777; + } + path = JS_ToCString(ctx, argv[0]); + if (!path) + return JS_EXCEPTION; +#if defined(_WIN32) + (void)mode; + ret = js_get_errno(mkdir(path)); +#else + ret = js_get_errno(mkdir(path, mode)); +#endif + JS_FreeCString(ctx, path); + return JS_NewInt32(ctx, ret); +} + +/* return [array, errorcode] */ +static JSValue js_os_readdir(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + const char *path; + DIR *f; + struct dirent *d; + JSValue obj; + int err; + uint32_t len; + + path = JS_ToCString(ctx, argv[0]); + if (!path) + return JS_EXCEPTION; + obj = JS_NewArray(ctx); + if (JS_IsException(obj)) { + JS_FreeCString(ctx, path); + return JS_EXCEPTION; + } + f = opendir(path); + if (!f) + err = errno; + else + err = 0; + JS_FreeCString(ctx, path); + if (!f) + goto done; + len = 0; + for(;;) { + errno = 0; + d = readdir(f); + if (!d) { + err = errno; + break; + } + JS_DefinePropertyValueUint32(ctx, obj, len++, + JS_NewString(ctx, d->d_name), + JS_PROP_C_W_E); + } + closedir(f); + done: + return make_obj_error(ctx, obj, err); +} + +#if !defined(_WIN32) +static int64_t timespec_to_ms(const struct timespec *tv) +{ + return (int64_t)tv->tv_sec * 1000 + (tv->tv_nsec / 1000000); +} +#endif + +/* return [obj, errcode] */ +static JSValue js_os_stat(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv, int is_lstat) +{ + const char *path; + int err, res; + struct stat st; + JSValue obj; + + path = JS_ToCString(ctx, argv[0]); + if (!path) + return JS_EXCEPTION; +#if defined(_WIN32) + res = stat(path, &st); +#else + if (is_lstat) + res = lstat(path, &st); + else + res = stat(path, &st); +#endif + err = (res < 0) ? errno : 0; + JS_FreeCString(ctx, path); + if (res < 0) { + obj = JS_NULL; + } else { + obj = JS_NewObject(ctx); + if (JS_IsException(obj)) + return JS_EXCEPTION; + JS_DefinePropertyValueStr(ctx, obj, "dev", + JS_NewInt64(ctx, st.st_dev), + JS_PROP_C_W_E); + JS_DefinePropertyValueStr(ctx, obj, "ino", + JS_NewInt64(ctx, st.st_ino), + JS_PROP_C_W_E); + JS_DefinePropertyValueStr(ctx, obj, "mode", + JS_NewInt32(ctx, st.st_mode), + JS_PROP_C_W_E); + JS_DefinePropertyValueStr(ctx, obj, "nlink", + JS_NewInt64(ctx, st.st_nlink), + JS_PROP_C_W_E); + JS_DefinePropertyValueStr(ctx, obj, "uid", + JS_NewInt64(ctx, st.st_uid), + JS_PROP_C_W_E); + JS_DefinePropertyValueStr(ctx, obj, "gid", + JS_NewInt64(ctx, st.st_gid), + JS_PROP_C_W_E); + JS_DefinePropertyValueStr(ctx, obj, "rdev", + JS_NewInt64(ctx, st.st_rdev), + JS_PROP_C_W_E); + JS_DefinePropertyValueStr(ctx, obj, "size", + JS_NewInt64(ctx, st.st_size), + JS_PROP_C_W_E); +#if !defined(_WIN32) + JS_DefinePropertyValueStr(ctx, obj, "blocks", + JS_NewInt64(ctx, st.st_blocks), + JS_PROP_C_W_E); +#endif +#if defined(_WIN32) + JS_DefinePropertyValueStr(ctx, obj, "atime", + JS_NewInt64(ctx, (int64_t)st.st_atime * 1000), + JS_PROP_C_W_E); + JS_DefinePropertyValueStr(ctx, obj, "mtime", + JS_NewInt64(ctx, (int64_t)st.st_mtime * 1000), + JS_PROP_C_W_E); + JS_DefinePropertyValueStr(ctx, obj, "ctime", + JS_NewInt64(ctx, (int64_t)st.st_ctime * 1000), + JS_PROP_C_W_E); +#elif defined(__APPLE__) + JS_DefinePropertyValueStr(ctx, obj, "atime", + JS_NewInt64(ctx, timespec_to_ms(&st.st_atimespec)), + JS_PROP_C_W_E); + JS_DefinePropertyValueStr(ctx, obj, "mtime", + JS_NewInt64(ctx, timespec_to_ms(&st.st_mtimespec)), + JS_PROP_C_W_E); + JS_DefinePropertyValueStr(ctx, obj, "ctime", + JS_NewInt64(ctx, timespec_to_ms(&st.st_ctimespec)), + JS_PROP_C_W_E); +#else + JS_DefinePropertyValueStr(ctx, obj, "atime", + JS_NewInt64(ctx, timespec_to_ms(&st.st_atim)), + JS_PROP_C_W_E); + JS_DefinePropertyValueStr(ctx, obj, "mtime", + JS_NewInt64(ctx, timespec_to_ms(&st.st_mtim)), + JS_PROP_C_W_E); + JS_DefinePropertyValueStr(ctx, obj, "ctime", + JS_NewInt64(ctx, timespec_to_ms(&st.st_ctim)), + JS_PROP_C_W_E); +#endif + } + return make_obj_error(ctx, obj, err); +} + +#if !defined(_WIN32) +static void ms_to_timeval(struct timeval *tv, uint64_t v) +{ + tv->tv_sec = v / 1000; + tv->tv_usec = (v % 1000) * 1000; +} +#endif + +static JSValue js_os_utimes(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + const char *path; + int64_t atime, mtime; + int ret; + + if (JS_ToInt64(ctx, &atime, argv[1])) + return JS_EXCEPTION; + if (JS_ToInt64(ctx, &mtime, argv[2])) + return JS_EXCEPTION; + path = JS_ToCString(ctx, argv[0]); + if (!path) + return JS_EXCEPTION; +#if defined(_WIN32) + { + struct _utimbuf times; + times.actime = atime / 1000; + times.modtime = mtime / 1000; + ret = js_get_errno(_utime(path, ×)); + } +#else + { + struct timeval times[2]; + ms_to_timeval(×[0], atime); + ms_to_timeval(×[1], mtime); + ret = js_get_errno(utimes(path, times)); + } +#endif + JS_FreeCString(ctx, path); + return JS_NewInt32(ctx, ret); +} + +/* sleep(delay_ms) */ +static JSValue js_os_sleep(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + int64_t delay; + int ret; + + if (JS_ToInt64(ctx, &delay, argv[0])) + return JS_EXCEPTION; + if (delay < 0) + delay = 0; +#if defined(_WIN32) + { + if (delay > INT32_MAX) + delay = INT32_MAX; + Sleep(delay); + ret = 0; + } +#else + { + struct timespec ts; + + ts.tv_sec = delay / 1000; + ts.tv_nsec = (delay % 1000) * 1000000; + ret = js_get_errno(nanosleep(&ts, NULL)); + } +#endif + return JS_NewInt32(ctx, ret); +} + +#if defined(_WIN32) +static char *realpath(const char *path, char *buf) +{ + if (!_fullpath(buf, path, PATH_MAX)) { + errno = ENOENT; + return NULL; + } else { + return buf; + } +} +#endif + +#if !defined(__wasi__) +/* return [path, errorcode] */ +static JSValue js_os_realpath(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + const char *path; + char buf[PATH_MAX], *res; + int err; + + path = JS_ToCString(ctx, argv[0]); + if (!path) + return JS_EXCEPTION; + res = realpath(path, buf); + JS_FreeCString(ctx, path); + if (!res) { + buf[0] = '\0'; + err = errno; + } else { + err = 0; + } + return make_string_error(ctx, buf, err); +} +#endif + +#if !defined(_WIN32) && !defined(__wasi__) +static JSValue js_os_symlink(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + const char *target, *linkpath; + int err; + + target = JS_ToCString(ctx, argv[0]); + if (!target) + return JS_EXCEPTION; + linkpath = JS_ToCString(ctx, argv[1]); + if (!linkpath) { + JS_FreeCString(ctx, target); + return JS_EXCEPTION; + } + err = js_get_errno(symlink(target, linkpath)); + JS_FreeCString(ctx, target); + JS_FreeCString(ctx, linkpath); + return JS_NewInt32(ctx, err); +} + +/* return [path, errorcode] */ +static JSValue js_os_readlink(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + const char *path; + char buf[PATH_MAX]; + int err; + ssize_t res; + + path = JS_ToCString(ctx, argv[0]); + if (!path) + return JS_EXCEPTION; + res = readlink(path, buf, sizeof(buf) - 1); + if (res < 0) { + buf[0] = '\0'; + err = errno; + } else { + buf[res] = '\0'; + err = 0; + } + JS_FreeCString(ctx, path); + return make_string_error(ctx, buf, err); +} + +static char **build_envp(JSContext *ctx, JSValue obj) +{ + uint32_t len, i; + JSPropertyEnum *tab; + char **envp, *pair; + const char *key, *str; + JSValue val; + size_t key_len, str_len; + + if (JS_GetOwnPropertyNames(ctx, &tab, &len, obj, + JS_GPN_STRING_MASK | JS_GPN_ENUM_ONLY) < 0) + return NULL; + envp = js_mallocz(ctx, sizeof(envp[0]) * ((size_t)len + 1)); + if (!envp) + goto fail; + for(i = 0; i < len; i++) { + val = JS_GetProperty(ctx, obj, tab[i].atom); + if (JS_IsException(val)) + goto fail; + str = JS_ToCString(ctx, val); + JS_FreeValue(ctx, val); + if (!str) + goto fail; + key = JS_AtomToCString(ctx, tab[i].atom); + if (!key) { + JS_FreeCString(ctx, str); + goto fail; + } + key_len = strlen(key); + str_len = strlen(str); + pair = js_malloc(ctx, key_len + str_len + 2); + if (!pair) { + JS_FreeCString(ctx, key); + JS_FreeCString(ctx, str); + goto fail; + } + memcpy(pair, key, key_len); + pair[key_len] = '='; + memcpy(pair + key_len + 1, str, str_len); + pair[key_len + 1 + str_len] = '\0'; + envp[i] = pair; + JS_FreeCString(ctx, key); + JS_FreeCString(ctx, str); + } + done: + for(i = 0; i < len; i++) + JS_FreeAtom(ctx, tab[i].atom); + js_free(ctx, tab); + return envp; + fail: + if (envp) { + for(i = 0; i < len; i++) + js_free(ctx, envp[i]); + js_free(ctx, envp); + envp = NULL; + } + goto done; +} + +/* execvpe is not available on non GNU systems */ +static int my_execvpe(const char *filename, char **argv, char **envp) +{ + char *path, *p, *p_next, *p1; + char buf[PATH_MAX]; + size_t filename_len, path_len; + BOOL eacces_error; + + filename_len = strlen(filename); + if (filename_len == 0) { + errno = ENOENT; + return -1; + } + if (strchr(filename, '/')) + return execve(filename, argv, envp); + + path = getenv("PATH"); + if (!path) + path = (char *)"/bin:/usr/bin"; + eacces_error = FALSE; + p = path; + for(p = path; p != NULL; p = p_next) { + p1 = strchr(p, ':'); + if (!p1) { + p_next = NULL; + path_len = strlen(p); + } else { + p_next = p1 + 1; + path_len = p1 - p; + } + /* path too long */ + if ((path_len + 1 + filename_len + 1) > PATH_MAX) + continue; + memcpy(buf, p, path_len); + buf[path_len] = '/'; + memcpy(buf + path_len + 1, filename, filename_len); + buf[path_len + 1 + filename_len] = '\0'; + + execve(buf, argv, envp); + + switch(errno) { + case EACCES: + eacces_error = TRUE; + break; + case ENOENT: + case ENOTDIR: + break; + default: + return -1; + } + } + if (eacces_error) + errno = EACCES; + return -1; +} + +/* exec(args[, options]) -> exitcode */ +static JSValue js_os_exec(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + JSValue options, args = argv[0]; + JSValue val, ret_val; + const char **exec_argv, *file = NULL, *str, *cwd = NULL; + char **envp = environ; + uint32_t exec_argc, i; + int ret, pid, status; + BOOL block_flag = TRUE, use_path = TRUE; + static const char *std_name[3] = { "stdin", "stdout", "stderr" }; + int std_fds[3]; + uint32_t uid = -1, gid = -1; + + val = JS_GetPropertyStr(ctx, args, "length"); + if (JS_IsException(val)) + return JS_EXCEPTION; + ret = JS_ToUint32(ctx, &exec_argc, val); + JS_FreeValue(ctx, val); + if (ret) + return JS_EXCEPTION; + /* arbitrary limit to avoid overflow */ + if (exec_argc < 1 || exec_argc > 65535) { + return JS_ThrowTypeError(ctx, "invalid number of arguments"); + } + exec_argv = js_mallocz(ctx, sizeof(exec_argv[0]) * (exec_argc + 1)); + if (!exec_argv) + return JS_EXCEPTION; + for(i = 0; i < exec_argc; i++) { + val = JS_GetPropertyUint32(ctx, args, i); + if (JS_IsException(val)) + goto exception; + str = JS_ToCString(ctx, val); + JS_FreeValue(ctx, val); + if (!str) + goto exception; + exec_argv[i] = str; + } + exec_argv[exec_argc] = NULL; + + for(i = 0; i < 3; i++) + std_fds[i] = i; + + /* get the options, if any */ + if (argc >= 2) { + options = argv[1]; + + if (get_bool_option(ctx, &block_flag, options, "block")) + goto exception; + if (get_bool_option(ctx, &use_path, options, "usePath")) + goto exception; + + val = JS_GetPropertyStr(ctx, options, "file"); + if (JS_IsException(val)) + goto exception; + if (!JS_IsUndefined(val)) { + file = JS_ToCString(ctx, val); + JS_FreeValue(ctx, val); + if (!file) + goto exception; + } + + val = JS_GetPropertyStr(ctx, options, "cwd"); + if (JS_IsException(val)) + goto exception; + if (!JS_IsUndefined(val)) { + cwd = JS_ToCString(ctx, val); + JS_FreeValue(ctx, val); + if (!cwd) + goto exception; + } + + /* stdin/stdout/stderr handles */ + for(i = 0; i < 3; i++) { + val = JS_GetPropertyStr(ctx, options, std_name[i]); + if (JS_IsException(val)) + goto exception; + if (!JS_IsUndefined(val)) { + int fd; + ret = JS_ToInt32(ctx, &fd, val); + JS_FreeValue(ctx, val); + if (ret) + goto exception; + std_fds[i] = fd; + } + } + + val = JS_GetPropertyStr(ctx, options, "env"); + if (JS_IsException(val)) + goto exception; + if (!JS_IsUndefined(val)) { + envp = build_envp(ctx, val); + JS_FreeValue(ctx, val); + if (!envp) + goto exception; + } + + val = JS_GetPropertyStr(ctx, options, "uid"); + if (JS_IsException(val)) + goto exception; + if (!JS_IsUndefined(val)) { + ret = JS_ToUint32(ctx, &uid, val); + JS_FreeValue(ctx, val); + if (ret) + goto exception; + } + + val = JS_GetPropertyStr(ctx, options, "gid"); + if (JS_IsException(val)) + goto exception; + if (!JS_IsUndefined(val)) { + ret = JS_ToUint32(ctx, &gid, val); + JS_FreeValue(ctx, val); + if (ret) + goto exception; + } + } + + pid = fork(); + if (pid < 0) { + JS_ThrowTypeError(ctx, "fork error"); + goto exception; + } + if (pid == 0) { + /* child */ + int fd_max = sysconf(_SC_OPEN_MAX); + + /* remap the stdin/stdout/stderr handles if necessary */ + for(i = 0; i < 3; i++) { + if (std_fds[i] != i) { + if (dup2(std_fds[i], i) < 0) + _exit(127); + } + } + + for(i = 3; i < fd_max; i++) + close(i); + if (cwd) { + if (chdir(cwd) < 0) + _exit(127); + } + if (uid != -1) { + if (setuid(uid) < 0) + _exit(127); + } + if (gid != -1) { + if (setgid(gid) < 0) + _exit(127); + } + + if (!file) + file = exec_argv[0]; + if (use_path) + ret = my_execvpe(file, (char **)exec_argv, envp); + else + ret = execve(file, (char **)exec_argv, envp); + _exit(127); + } + /* parent */ + if (block_flag) { + for(;;) { + ret = waitpid(pid, &status, 0); + if (ret == pid) { + if (WIFEXITED(status)) { + ret = WEXITSTATUS(status); + break; + } else if (WIFSIGNALED(status)) { + ret = -WTERMSIG(status); + break; + } + } + } + } else { + ret = pid; + } + ret_val = JS_NewInt32(ctx, ret); + done: + JS_FreeCString(ctx, file); + JS_FreeCString(ctx, cwd); + for(i = 0; i < exec_argc; i++) + JS_FreeCString(ctx, exec_argv[i]); + js_free(ctx, exec_argv); + if (envp != environ) { + char **p; + p = envp; + while (*p != NULL) { + js_free(ctx, *p); + p++; + } + js_free(ctx, envp); + } + return ret_val; + exception: + ret_val = JS_EXCEPTION; + goto done; +} + +/* getpid() -> pid */ +static JSValue js_os_getpid(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + return JS_NewInt32(ctx, getpid()); +} + +/* waitpid(pid, block) -> [pid, status] */ +static JSValue js_os_waitpid(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + int pid, status, options, ret; + JSValue obj; + + if (JS_ToInt32(ctx, &pid, argv[0])) + return JS_EXCEPTION; + if (JS_ToInt32(ctx, &options, argv[1])) + return JS_EXCEPTION; + + ret = waitpid(pid, &status, options); + if (ret < 0) { + ret = -errno; + status = 0; + } + + obj = JS_NewArray(ctx); + if (JS_IsException(obj)) + return obj; + JS_DefinePropertyValueUint32(ctx, obj, 0, JS_NewInt32(ctx, ret), + JS_PROP_C_W_E); + JS_DefinePropertyValueUint32(ctx, obj, 1, JS_NewInt32(ctx, status), + JS_PROP_C_W_E); + return obj; +} + +/* pipe() -> [read_fd, write_fd] or null if error */ +static JSValue js_os_pipe(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + int pipe_fds[2], ret; + JSValue obj; + + ret = pipe(pipe_fds); + if (ret < 0) + return JS_NULL; + obj = JS_NewArray(ctx); + if (JS_IsException(obj)) + return obj; + JS_DefinePropertyValueUint32(ctx, obj, 0, JS_NewInt32(ctx, pipe_fds[0]), + JS_PROP_C_W_E); + JS_DefinePropertyValueUint32(ctx, obj, 1, JS_NewInt32(ctx, pipe_fds[1]), + JS_PROP_C_W_E); + return obj; +} + +/* kill(pid, sig) */ +static JSValue js_os_kill(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + int pid, sig, ret; + + if (JS_ToInt32(ctx, &pid, argv[0])) + return JS_EXCEPTION; + if (JS_ToInt32(ctx, &sig, argv[1])) + return JS_EXCEPTION; + ret = js_get_errno(kill(pid, sig)); + return JS_NewInt32(ctx, ret); +} + +/* dup(fd) */ +static JSValue js_os_dup(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + int fd, ret; + + if (JS_ToInt32(ctx, &fd, argv[0])) + return JS_EXCEPTION; + ret = js_get_errno(dup(fd)); + return JS_NewInt32(ctx, ret); +} + +/* dup2(fd) */ +static JSValue js_os_dup2(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + int fd, fd2, ret; + + if (JS_ToInt32(ctx, &fd, argv[0])) + return JS_EXCEPTION; + if (JS_ToInt32(ctx, &fd2, argv[1])) + return JS_EXCEPTION; + ret = js_get_errno(dup2(fd, fd2)); + return JS_NewInt32(ctx, ret); +} + +#endif /* !_WIN32 */ + +#ifdef USE_WORKER + +/* Worker */ + +typedef struct { + JSWorkerMessagePipe *recv_pipe; + JSWorkerMessagePipe *send_pipe; + JSWorkerMessageHandler *msg_handler; +} JSWorkerData; + +typedef struct { + char *filename; /* module filename */ + char *basename; /* module base name */ + JSWorkerMessagePipe *recv_pipe, *send_pipe; +} WorkerFuncArgs; + +typedef struct { + int ref_count; + uint64_t buf[]; +} JSSABHeader; + +static JSContext *(*js_worker_new_context_func)(JSRuntime *rt); + +static int atomic_add_int(int *ptr, int v) +{ + return atomic_fetch_add((_Atomic uint32_t*)ptr, v) + v; +} + +/* shared array buffer allocator */ +static void *js_sab_alloc(void *opaque, size_t size) +{ + JSSABHeader *sab; + sab = malloc(sizeof(JSSABHeader) + size); + if (!sab) + return NULL; + sab->ref_count = 1; + return sab->buf; +} + +static void js_sab_free(void *opaque, void *ptr) +{ + JSSABHeader *sab; + int ref_count; + sab = (JSSABHeader *)((uint8_t *)ptr - sizeof(JSSABHeader)); + ref_count = atomic_add_int(&sab->ref_count, -1); + assert(ref_count >= 0); + if (ref_count == 0) { + free(sab); + } +} + +static void js_sab_dup(void *opaque, void *ptr) +{ + JSSABHeader *sab; + sab = (JSSABHeader *)((uint8_t *)ptr - sizeof(JSSABHeader)); + atomic_add_int(&sab->ref_count, 1); +} + +static JSWorkerMessagePipe *js_new_message_pipe(void) +{ + JSWorkerMessagePipe *ps; + int pipe_fds[2]; + + if (pipe(pipe_fds) < 0) + return NULL; + + ps = malloc(sizeof(*ps)); + if (!ps) { + close(pipe_fds[0]); + close(pipe_fds[1]); + return NULL; + } + ps->ref_count = 1; + init_list_head(&ps->msg_queue); + pthread_mutex_init(&ps->mutex, NULL); + ps->read_fd = pipe_fds[0]; + ps->write_fd = pipe_fds[1]; + return ps; +} + +static JSWorkerMessagePipe *js_dup_message_pipe(JSWorkerMessagePipe *ps) +{ + atomic_add_int(&ps->ref_count, 1); + return ps; +} + +static void js_free_message(JSWorkerMessage *msg) +{ + size_t i; + /* free the SAB */ + for(i = 0; i < msg->sab_tab_len; i++) { + js_sab_free(NULL, msg->sab_tab[i]); + } + free(msg->sab_tab); + free(msg->data); + free(msg); +} + +static void js_free_message_pipe(JSWorkerMessagePipe *ps) +{ + struct list_head *el, *el1; + JSWorkerMessage *msg; + int ref_count; + + if (!ps) + return; + + ref_count = atomic_add_int(&ps->ref_count, -1); + assert(ref_count >= 0); + if (ref_count == 0) { + list_for_each_safe(el, el1, &ps->msg_queue) { + msg = list_entry(el, JSWorkerMessage, link); + js_free_message(msg); + } + pthread_mutex_destroy(&ps->mutex); + close(ps->read_fd); + close(ps->write_fd); + free(ps); + } +} + +static void js_free_port(JSRuntime *rt, JSWorkerMessageHandler *port) +{ + if (port) { + js_free_message_pipe(port->recv_pipe); + JS_FreeValueRT(rt, port->on_message_func); + list_del(&port->link); + js_free_rt(rt, port); + } +} + +static void js_worker_finalizer(JSRuntime *rt, JSValue val) +{ + JSThreadState *ts = js_get_thread_state(rt); + JSWorkerData *worker = JS_GetOpaque(val, ts->worker_class_id); + if (worker) { + js_free_message_pipe(worker->recv_pipe); + js_free_message_pipe(worker->send_pipe); + js_free_port(rt, worker->msg_handler); + js_free_rt(rt, worker); + } +} + +static JSClassDef js_worker_class = { + "Worker", + .finalizer = js_worker_finalizer, +}; + +static void *worker_func(void *opaque) +{ + WorkerFuncArgs *args = opaque; + JSRuntime *rt; + JSThreadState *ts; + JSContext *ctx; + JSValue val; + + rt = JS_NewRuntime(); + if (rt == NULL) { + fprintf(stderr, "JS_NewRuntime failure"); + exit(1); + } + js_std_init_handlers(rt); + + JS_SetModuleLoaderFunc(rt, NULL, js_module_loader, NULL); + + /* set the pipe to communicate with the parent */ + ts = js_get_thread_state(rt); + ts->recv_pipe = args->recv_pipe; + ts->send_pipe = args->send_pipe; + + /* function pointer to avoid linking the whole JS_NewContext() if + not needed */ + ctx = js_worker_new_context_func(rt); + if (ctx == NULL) { + fprintf(stderr, "JS_NewContext failure"); + } + + JS_SetCanBlock(rt, TRUE); + + js_std_add_helpers(ctx, -1, NULL); + + val = JS_LoadModule(ctx, args->basename, args->filename); + free(args->filename); + free(args->basename); + free(args); + val = js_std_await(ctx, val); + if (JS_IsException(val)) + js_std_dump_error(ctx); + JS_FreeValue(ctx, val); + + js_std_loop(ctx); + + JS_FreeContext(ctx); + js_std_free_handlers(rt); + JS_FreeRuntime(rt); + return NULL; +} + +static JSValue js_worker_ctor_internal(JSContext *ctx, JSValue new_target, + JSWorkerMessagePipe *recv_pipe, + JSWorkerMessagePipe *send_pipe) +{ + JSRuntime *rt = JS_GetRuntime(ctx); + JSThreadState *ts = js_get_thread_state(rt); + JSValue obj = JS_UNDEFINED, proto; + JSWorkerData *s; + + /* create the object */ + if (JS_IsUndefined(new_target)) { + proto = JS_GetClassProto(ctx, ts->worker_class_id); + } else { + proto = JS_GetPropertyStr(ctx, new_target, "prototype"); + if (JS_IsException(proto)) + goto fail; + } + obj = JS_NewObjectProtoClass(ctx, proto, ts->worker_class_id); + JS_FreeValue(ctx, proto); + if (JS_IsException(obj)) + goto fail; + s = js_mallocz(ctx, sizeof(*s)); + if (!s) + goto fail; + s->recv_pipe = js_dup_message_pipe(recv_pipe); + s->send_pipe = js_dup_message_pipe(send_pipe); + + JS_SetOpaque(obj, s); + return obj; + fail: + JS_FreeValue(ctx, obj); + return JS_EXCEPTION; +} + +static JSValue js_worker_ctor(JSContext *ctx, JSValue new_target, + int argc, JSValue *argv) +{ + JSRuntime *rt = JS_GetRuntime(ctx); + WorkerFuncArgs *args = NULL; + pthread_t tid; + pthread_attr_t attr; + JSValue obj = JS_UNDEFINED; + int ret; + const char *filename = NULL, *basename; + JSAtom basename_atom; + + /* XXX: in order to avoid problems with resource liberation, we + don't support creating workers inside workers */ + if (!is_main_thread(rt)) + return JS_ThrowTypeError(ctx, "cannot create a worker inside a worker"); + + /* base name, assuming the calling function is a normal JS + function */ + basename_atom = JS_GetScriptOrModuleName(ctx, 1); + if (basename_atom == JS_ATOM_NULL) { + return JS_ThrowTypeError(ctx, "could not determine calling script or module name"); + } + basename = JS_AtomToCString(ctx, basename_atom); + JS_FreeAtom(ctx, basename_atom); + if (!basename) + goto fail; + + /* module name */ + filename = JS_ToCString(ctx, argv[0]); + if (!filename) + goto fail; + + args = malloc(sizeof(*args)); + if (!args) + goto oom_fail; + memset(args, 0, sizeof(*args)); + args->filename = strdup(filename); + args->basename = strdup(basename); + + /* ports */ + args->recv_pipe = js_new_message_pipe(); + if (!args->recv_pipe) + goto oom_fail; + args->send_pipe = js_new_message_pipe(); + if (!args->send_pipe) + goto oom_fail; + + obj = js_worker_ctor_internal(ctx, new_target, + args->send_pipe, args->recv_pipe); + if (JS_IsException(obj)) + goto fail; + + pthread_attr_init(&attr); + /* no join at the end */ + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); + // musl libc gives threads 80 kb stacks, much smaller than + // JS_DEFAULT_STACK_SIZE (256 kb) + pthread_attr_setstacksize(&attr, 2 << 20); // 2 MB, glibc default + ret = pthread_create(&tid, &attr, worker_func, args); + pthread_attr_destroy(&attr); + if (ret != 0) { + JS_ThrowTypeError(ctx, "could not create worker"); + goto fail; + } + JS_FreeCString(ctx, basename); + JS_FreeCString(ctx, filename); + return obj; + oom_fail: + JS_ThrowOutOfMemory(ctx); + fail: + JS_FreeCString(ctx, basename); + JS_FreeCString(ctx, filename); + if (args) { + free(args->filename); + free(args->basename); + js_free_message_pipe(args->recv_pipe); + js_free_message_pipe(args->send_pipe); + free(args); + } + JS_FreeValue(ctx, obj); + return JS_EXCEPTION; +} + +static JSValue js_worker_postMessage(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + JSRuntime *rt = JS_GetRuntime(ctx); + JSThreadState *ts = js_get_thread_state(rt); + JSWorkerData *worker = JS_GetOpaque2(ctx, this_val, ts->worker_class_id); + JSWorkerMessagePipe *ps; + size_t data_len, i; + uint8_t *data; + JSWorkerMessage *msg; + JSSABTab sab_tab; + + if (!worker) + return JS_EXCEPTION; + + data = JS_WriteObject2(ctx, &data_len, argv[0], + JS_WRITE_OBJ_SAB | JS_WRITE_OBJ_REFERENCE, + &sab_tab); + if (!data) + return JS_EXCEPTION; + + msg = malloc(sizeof(*msg)); + if (!msg) + goto fail; + msg->data = NULL; + msg->sab_tab = NULL; + + /* must reallocate because the allocator may be different */ + msg->data = malloc(data_len); + if (!msg->data) + goto fail; + memcpy(msg->data, data, data_len); + msg->data_len = data_len; + + if (sab_tab.len > 0) { + msg->sab_tab = malloc(sizeof(msg->sab_tab[0]) * sab_tab.len); + if (!msg->sab_tab) + goto fail; + memcpy(msg->sab_tab, sab_tab.tab, sizeof(msg->sab_tab[0]) * sab_tab.len); + } + msg->sab_tab_len = sab_tab.len; + + js_free(ctx, data); + js_free(ctx, sab_tab.tab); + + /* increment the SAB reference counts */ + for(i = 0; i < msg->sab_tab_len; i++) { + js_sab_dup(NULL, msg->sab_tab[i]); + } + + ps = worker->send_pipe; + pthread_mutex_lock(&ps->mutex); + /* indicate that data is present */ + if (list_empty(&ps->msg_queue)) { + uint8_t ch = '\0'; + int ret; + for(;;) { + ret = write(ps->write_fd, &ch, 1); + if (ret == 1) + break; + if (ret < 0 && (errno != EAGAIN || errno != EINTR)) + break; + } + } + list_add_tail(&msg->link, &ps->msg_queue); + pthread_mutex_unlock(&ps->mutex); + return JS_UNDEFINED; + fail: + if (msg) { + free(msg->data); + free(msg->sab_tab); + free(msg); + } + js_free(ctx, data); + js_free(ctx, sab_tab.tab); + return JS_EXCEPTION; + +} + +static JSValue js_worker_set_onmessage(JSContext *ctx, JSValue this_val, + JSValue func) +{ + JSRuntime *rt = JS_GetRuntime(ctx); + JSThreadState *ts = js_get_thread_state(rt); + JSWorkerData *worker = JS_GetOpaque2(ctx, this_val, ts->worker_class_id); + JSWorkerMessageHandler *port; + + if (!worker) + return JS_EXCEPTION; + + port = worker->msg_handler; + if (JS_IsNull(func)) { + if (port) { + js_free_port(rt, port); + worker->msg_handler = NULL; + } + } else { + if (!JS_IsFunction(ctx, func)) + return JS_ThrowTypeError(ctx, "not a function"); + if (!port) { + port = js_mallocz(ctx, sizeof(*port)); + if (!port) + return JS_EXCEPTION; + port->recv_pipe = js_dup_message_pipe(worker->recv_pipe); + port->on_message_func = JS_NULL; + list_add_tail(&port->link, &ts->port_list); + worker->msg_handler = port; + } + JS_FreeValue(ctx, port->on_message_func); + port->on_message_func = JS_DupValue(ctx, func); + } + return JS_UNDEFINED; +} + +static JSValue js_worker_get_onmessage(JSContext *ctx, JSValue this_val) +{ + JSRuntime *rt = JS_GetRuntime(ctx); + JSThreadState *ts = js_get_thread_state(rt); + JSWorkerData *worker = JS_GetOpaque2(ctx, this_val, ts->worker_class_id); + JSWorkerMessageHandler *port; + if (!worker) + return JS_EXCEPTION; + port = worker->msg_handler; + if (port) { + return JS_DupValue(ctx, port->on_message_func); + } else { + return JS_NULL; + } +} + +static const JSCFunctionListEntry js_worker_proto_funcs[] = { + JS_CFUNC_DEF("postMessage", 1, js_worker_postMessage ), + JS_CGETSET_DEF("onmessage", js_worker_get_onmessage, js_worker_set_onmessage ), +}; + +#endif /* USE_WORKER */ + +void js_std_set_worker_new_context_func(JSContext *(*func)(JSRuntime *rt)) +{ +#ifdef USE_WORKER + js_worker_new_context_func = func; +#endif +} + +#if defined(_WIN32) +#define OS_PLATFORM "win32" +#elif defined(__APPLE__) +#define OS_PLATFORM "darwin" +#elif defined(EMSCRIPTEN) +#define OS_PLATFORM "js" +#elif defined(__CYGWIN__) +#define OS_PLATFORM "cygwin" +#elif defined(__linux__) +#define OS_PLATFORM "linux" +#elif defined(__OpenBSD__) +#define OS_PLATFORM "openbsd" +#elif defined(__NetBSD__) +#define OS_PLATFORM "netbsd" +#elif defined(__FreeBSD__) +#define OS_PLATFORM "freebsd" +#elif defined(__wasi__) +#define OS_PLATFORM "wasi" +#else +#define OS_PLATFORM "unknown" +#endif + +#define OS_FLAG(x) JS_PROP_INT32_DEF(#x, x, JS_PROP_CONFIGURABLE ) + +static const JSCFunctionListEntry js_os_funcs[] = { + JS_CFUNC_DEF("open", 2, js_os_open ), + OS_FLAG(O_RDONLY), + OS_FLAG(O_WRONLY), + OS_FLAG(O_RDWR), + OS_FLAG(O_APPEND), + OS_FLAG(O_CREAT), + OS_FLAG(O_EXCL), + OS_FLAG(O_TRUNC), +#if defined(_WIN32) + OS_FLAG(O_BINARY), + OS_FLAG(O_TEXT), +#endif + JS_CFUNC_DEF("close", 1, js_os_close ), + JS_CFUNC_DEF("seek", 3, js_os_seek ), + JS_CFUNC_MAGIC_DEF("read", 4, js_os_read_write, 0 ), + JS_CFUNC_MAGIC_DEF("write", 4, js_os_read_write, 1 ), + JS_CFUNC_DEF("isatty", 1, js_os_isatty ), +#if !defined(__wasi__) + JS_CFUNC_DEF("ttyGetWinSize", 1, js_os_ttyGetWinSize ), + JS_CFUNC_DEF("ttySetRaw", 1, js_os_ttySetRaw ), +#endif + JS_CFUNC_DEF("remove", 1, js_os_remove ), + JS_CFUNC_DEF("rename", 2, js_os_rename ), + JS_CFUNC_MAGIC_DEF("setReadHandler", 2, js_os_setReadHandler, 0 ), + JS_CFUNC_MAGIC_DEF("setWriteHandler", 2, js_os_setReadHandler, 1 ), + JS_CFUNC_DEF("signal", 2, js_os_signal ), + OS_FLAG(SIGINT), + OS_FLAG(SIGABRT), + OS_FLAG(SIGFPE), + OS_FLAG(SIGILL), + OS_FLAG(SIGSEGV), + OS_FLAG(SIGTERM), +#if !defined(_WIN32) && !defined(__wasi__) + OS_FLAG(SIGQUIT), + OS_FLAG(SIGPIPE), + OS_FLAG(SIGALRM), + OS_FLAG(SIGUSR1), + OS_FLAG(SIGUSR2), + OS_FLAG(SIGCHLD), + OS_FLAG(SIGCONT), + OS_FLAG(SIGSTOP), + OS_FLAG(SIGTSTP), + OS_FLAG(SIGTTIN), + OS_FLAG(SIGTTOU), + JS_CFUNC_DEF("cputime", 0, js_os_cputime ), +#endif + JS_CFUNC_DEF("now", 0, js_os_now ), + JS_CFUNC_MAGIC_DEF("setTimeout", 2, js_os_setTimeout, 0 ), + JS_CFUNC_MAGIC_DEF("setInterval", 2, js_os_setTimeout, 1 ), + // per spec: both functions can cancel timeouts and intervals + JS_CFUNC_DEF("clearTimeout", 1, js_os_clearTimeout ), + JS_CFUNC_DEF("clearInterval", 1, js_os_clearTimeout ), + JS_CFUNC_DEF("sleepAsync", 1, js_os_sleepAsync ), + JS_PROP_STRING_DEF("platform", OS_PLATFORM, 0 ), + JS_CFUNC_DEF("getcwd", 0, js_os_getcwd ), + JS_CFUNC_DEF("chdir", 0, js_os_chdir ), + JS_CFUNC_DEF("mkdir", 1, js_os_mkdir ), + JS_CFUNC_DEF("readdir", 1, js_os_readdir ), + /* st_mode constants */ + OS_FLAG(S_IFMT), + OS_FLAG(S_IFIFO), + OS_FLAG(S_IFCHR), + OS_FLAG(S_IFDIR), + OS_FLAG(S_IFBLK), + OS_FLAG(S_IFREG), +#if !defined(_WIN32) + OS_FLAG(S_IFSOCK), + OS_FLAG(S_IFLNK), + OS_FLAG(S_ISGID), + OS_FLAG(S_ISUID), +#endif + JS_CFUNC_MAGIC_DEF("stat", 1, js_os_stat, 0 ), + JS_CFUNC_DEF("utimes", 3, js_os_utimes ), + JS_CFUNC_DEF("sleep", 1, js_os_sleep ), +#if !defined(__wasi__) + JS_CFUNC_DEF("realpath", 1, js_os_realpath ), +#endif +#if !defined(_WIN32) && !defined(__wasi__) + JS_CFUNC_MAGIC_DEF("lstat", 1, js_os_stat, 1 ), + JS_CFUNC_DEF("symlink", 2, js_os_symlink ), + JS_CFUNC_DEF("readlink", 1, js_os_readlink ), + JS_CFUNC_DEF("exec", 1, js_os_exec ), + JS_CFUNC_DEF("getpid", 0, js_os_getpid ), + JS_CFUNC_DEF("waitpid", 2, js_os_waitpid ), + OS_FLAG(WNOHANG), + JS_CFUNC_DEF("pipe", 0, js_os_pipe ), + JS_CFUNC_DEF("kill", 2, js_os_kill ), + JS_CFUNC_DEF("dup", 1, js_os_dup ), + JS_CFUNC_DEF("dup2", 2, js_os_dup2 ), +#endif +}; + +static int js_os_init(JSContext *ctx, JSModuleDef *m) +{ + JSRuntime *rt = JS_GetRuntime(ctx); + JSThreadState *ts = js_get_thread_state(rt); + + ts->can_js_os_poll = TRUE; + +#ifdef USE_WORKER + { + JSValue proto, obj; + /* Worker class */ + JS_NewClassID(rt, &ts->worker_class_id); + JS_NewClass(rt, ts->worker_class_id, &js_worker_class); + proto = JS_NewObject(ctx); + JS_SetPropertyFunctionList(ctx, proto, js_worker_proto_funcs, countof(js_worker_proto_funcs)); + + obj = JS_NewCFunction2(ctx, js_worker_ctor, "Worker", 1, + JS_CFUNC_constructor, 0); + JS_SetConstructor(ctx, obj, proto); + + JS_SetClassProto(ctx, ts->worker_class_id, proto); + + /* set 'Worker.parent' if necessary */ + if (ts->recv_pipe && ts->send_pipe) { + JS_DefinePropertyValueStr(ctx, obj, "parent", + js_worker_ctor_internal(ctx, JS_UNDEFINED, ts->recv_pipe, ts->send_pipe), + JS_PROP_C_W_E); + } + + JS_SetModuleExport(ctx, m, "Worker", obj); + } +#endif /* USE_WORKER */ + + return JS_SetModuleExportList(ctx, m, js_os_funcs, countof(js_os_funcs)); +} + +JSModuleDef *js_init_module_os(JSContext *ctx, const char *module_name) +{ + JSModuleDef *m; + m = JS_NewCModule(ctx, module_name, js_os_init); + if (!m) + return NULL; + JS_AddModuleExportList(ctx, m, js_os_funcs, countof(js_os_funcs)); +#ifdef USE_WORKER + JS_AddModuleExport(ctx, m, "Worker"); +#endif + return m; +} + +/**********************************************************/ + +JSValue js_print(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ +#ifdef _WIN32 + HANDLE handle; + DWORD mode; +#endif + const char *s; + DynBuf b; + int i; + + dbuf_init(&b); + for(i = 0; i < argc; i++) { + s = JS_ToCString(ctx, argv[i]); + if (s) { + dbuf_printf(&b, "%s%s", &" "[!i], s); + JS_FreeCString(ctx, s); + } else { + dbuf_printf(&b, "%s<exception>", &" "[!i]); + JS_FreeValue(ctx, JS_GetException(ctx)); + } + } + dbuf_putc(&b, '\n'); +#ifdef _WIN32 + // use WriteConsoleA with CP_UTF8 for better Unicode handling vis-a-vis + // the mangling that happens when going through msvcrt's stdio layer, + // *except* when stdout is redirected to something that is not a console + handle = (HANDLE)_get_osfhandle(/*STDOUT_FILENO*/1); // don't CloseHandle + if (GetFileType(handle) != FILE_TYPE_CHAR) + goto fallback; + if (!GetConsoleMode(handle, &mode)) + goto fallback; + handle = GetStdHandle(STD_OUTPUT_HANDLE); + if (handle == INVALID_HANDLE_VALUE) + goto fallback; + mode = GetConsoleOutputCP(); + SetConsoleOutputCP(CP_UTF8); + WriteConsoleA(handle, b.buf, b.size, NULL, NULL); + SetConsoleOutputCP(mode); + FlushFileBuffers(handle); + goto done; +fallback: +#endif + fwrite(b.buf, 1, b.size, stdout); + fflush(stdout); + goto done; // avoid unused label warning +done: + dbuf_free(&b); + return JS_UNDEFINED; +} + +void js_std_add_helpers(JSContext *ctx, int argc, char **argv) +{ + JSValue global_obj, console, args; + int i; + + /* XXX: should these global definitions be enumerable? */ + global_obj = JS_GetGlobalObject(ctx); + + console = JS_NewObject(ctx); + JS_SetPropertyStr(ctx, console, "log", + JS_NewCFunction(ctx, js_print, "log", 1)); + JS_SetPropertyStr(ctx, global_obj, "console", console); + + /* same methods as the mozilla JS shell */ + if (argc >= 0) { + args = JS_NewArray(ctx); + for(i = 0; i < argc; i++) { + JS_SetPropertyUint32(ctx, args, i, JS_NewString(ctx, argv[i])); + } + JS_SetPropertyStr(ctx, global_obj, "scriptArgs", args); + } + + JS_SetPropertyStr(ctx, global_obj, "print", + JS_NewCFunction(ctx, js_print, "print", 1)); + + JS_FreeValue(ctx, global_obj); +} + +static void js_std_finalize(JSRuntime *rt, void *arg) +{ + JSThreadState *ts = arg; + js_set_thread_state(rt, NULL); + js_free_rt(rt, ts); +} + +void js_std_init_handlers(JSRuntime *rt) +{ + JSThreadState *ts; + + ts = js_mallocz_rt(rt, sizeof(*ts)); + if (!ts) { + fprintf(stderr, "Could not allocate memory for the worker"); + exit(1); + } + init_list_head(&ts->os_rw_handlers); + init_list_head(&ts->os_signal_handlers); + init_list_head(&ts->os_timers); + init_list_head(&ts->port_list); + + ts->next_timer_id = 1; + ts->exc = JS_UNDEFINED; + + js_set_thread_state(rt, ts); + JS_AddRuntimeFinalizer(rt, js_std_finalize, ts); + +#ifdef USE_WORKER + /* set the SharedArrayBuffer memory handlers */ + { + JSSharedArrayBufferFunctions sf; + memset(&sf, 0, sizeof(sf)); + sf.sab_alloc = js_sab_alloc; + sf.sab_free = js_sab_free; + sf.sab_dup = js_sab_dup; + JS_SetSharedArrayBufferFunctions(rt, &sf); + } +#endif +} + +void js_std_free_handlers(JSRuntime *rt) +{ + JSThreadState *ts = js_get_thread_state(rt); + struct list_head *el, *el1; + + list_for_each_safe(el, el1, &ts->os_rw_handlers) { + JSOSRWHandler *rh = list_entry(el, JSOSRWHandler, link); + free_rw_handler(rt, rh); + } + + list_for_each_safe(el, el1, &ts->os_signal_handlers) { + JSOSSignalHandler *sh = list_entry(el, JSOSSignalHandler, link); + free_sh(rt, sh); + } + + list_for_each_safe(el, el1, &ts->os_timers) { + JSOSTimer *th = list_entry(el, JSOSTimer, link); + free_timer(rt, th); + } + +#ifdef USE_WORKER + /* XXX: free port_list ? */ + js_free_message_pipe(ts->recv_pipe); + js_free_message_pipe(ts->send_pipe); +#endif +} + +static void js_dump_obj(JSContext *ctx, FILE *f, JSValue val) +{ + const char *str; + + str = JS_ToCString(ctx, val); + if (str) { + fprintf(f, "%s\n", str); + JS_FreeCString(ctx, str); + } else { + fprintf(f, "[exception]\n"); + } +} + +void js_std_dump_error1(JSContext *ctx, JSValue exception_val) +{ + JSValue val; + BOOL is_error; + + is_error = JS_IsError(ctx, exception_val); + js_dump_obj(ctx, stderr, exception_val); + if (is_error) { + val = JS_GetPropertyStr(ctx, exception_val, "stack"); + if (!JS_IsUndefined(val)) { + js_dump_obj(ctx, stderr, val); + } + JS_FreeValue(ctx, val); + } +} + +void js_std_dump_error(JSContext *ctx) +{ + JSValue exception_val; + + exception_val = JS_GetException(ctx); + js_std_dump_error1(ctx, exception_val); + JS_FreeValue(ctx, exception_val); +} + +void js_std_promise_rejection_tracker(JSContext *ctx, JSValue promise, + JSValue reason, + BOOL is_handled, void *opaque) +{ + if (!is_handled) { + fprintf(stderr, "Possibly unhandled promise rejection: "); + js_std_dump_error1(ctx, reason); + } +} + +/* main loop which calls the user JS callbacks */ +JSValue js_std_loop(JSContext *ctx) +{ + JSRuntime *rt = JS_GetRuntime(ctx); + JSThreadState *ts = js_get_thread_state(rt); + JSContext *ctx1; + int err; + + for(;;) { + /* execute the pending jobs */ + for(;;) { + err = JS_ExecutePendingJob(JS_GetRuntime(ctx), &ctx1); + if (err <= 0) { + if (err < 0) { + ts->exc = JS_GetException(ctx1); + goto done; + } + break; + } + } + + if (!ts->can_js_os_poll || js_os_poll(ctx)) + break; + } +done: + return ts->exc; +} + +/* Wait for a promise and execute pending jobs while waiting for + it. Return the promise result or JS_EXCEPTION in case of promise + rejection. */ +JSValue js_std_await(JSContext *ctx, JSValue obj) +{ + JSRuntime *rt = JS_GetRuntime(ctx); + JSThreadState *ts = js_get_thread_state(rt); + JSValue ret; + int state; + + for(;;) { + state = JS_PromiseState(ctx, obj); + if (state == JS_PROMISE_FULFILLED) { + ret = JS_PromiseResult(ctx, obj); + JS_FreeValue(ctx, obj); + break; + } else if (state == JS_PROMISE_REJECTED) { + ret = JS_Throw(ctx, JS_PromiseResult(ctx, obj)); + JS_FreeValue(ctx, obj); + break; + } else if (state == JS_PROMISE_PENDING) { + JSContext *ctx1; + int err; + err = JS_ExecutePendingJob(JS_GetRuntime(ctx), &ctx1); + if (err < 0) { + js_std_dump_error(ctx1); + } + if (ts->can_js_os_poll) + js_os_poll(ctx); + } else { + /* not a promise */ + ret = obj; + break; + } + } + return ret; +} + +void js_std_eval_binary(JSContext *ctx, const uint8_t *buf, size_t buf_len, + int load_only) +{ + JSValue obj, val; + obj = JS_ReadObject(ctx, buf, buf_len, JS_READ_OBJ_BYTECODE); + if (JS_IsException(obj)) + goto exception; + if (load_only) { + if (JS_VALUE_GET_TAG(obj) == JS_TAG_MODULE) { + js_module_set_import_meta(ctx, obj, FALSE, FALSE); + } + } else { + if (JS_VALUE_GET_TAG(obj) == JS_TAG_MODULE) { + if (JS_ResolveModule(ctx, obj) < 0) { + JS_FreeValue(ctx, obj); + goto exception; + } + js_module_set_import_meta(ctx, obj, FALSE, TRUE); + val = JS_EvalFunction(ctx, obj); + val = js_std_await(ctx, val); + } else { + val = JS_EvalFunction(ctx, obj); + } + if (JS_IsException(val)) { + exception: + js_std_dump_error(ctx); + exit(1); + } + JS_FreeValue(ctx, val); + } +} + +static JSValue js_bjson_read(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + uint8_t *buf; + uint64_t pos, len; + JSValue obj; + size_t size; + int flags; + + if (JS_ToIndex(ctx, &pos, argv[1])) + return JS_EXCEPTION; + if (JS_ToIndex(ctx, &len, argv[2])) + return JS_EXCEPTION; + if (JS_ToInt32(ctx, &flags, argv[3])) + return JS_EXCEPTION; + buf = JS_GetArrayBuffer(ctx, &size, argv[0]); + if (!buf) + return JS_EXCEPTION; + if (pos + len > size) + return JS_ThrowRangeError(ctx, "array buffer overflow"); + obj = JS_ReadObject(ctx, buf + pos, len, flags); + return obj; +} + +static JSValue js_bjson_write(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + size_t len; + uint8_t *buf; + JSValue array; + int flags; + + if (JS_ToInt32(ctx, &flags, argv[1])) + return JS_EXCEPTION; + buf = JS_WriteObject(ctx, &len, argv[0], flags); + if (!buf) + return JS_EXCEPTION; + array = JS_NewArrayBufferCopy(ctx, buf, len); + js_free(ctx, buf); + return array; +} + + +static const JSCFunctionListEntry js_bjson_funcs[] = { + JS_CFUNC_DEF("read", 4, js_bjson_read ), + JS_CFUNC_DEF("write", 2, js_bjson_write ), +#define DEF(x) JS_PROP_INT32_DEF(#x, JS_##x, JS_PROP_CONFIGURABLE) + DEF(READ_OBJ_BYTECODE), + DEF(READ_OBJ_REFERENCE), + DEF(READ_OBJ_SAB), + DEF(WRITE_OBJ_BYTECODE), + DEF(WRITE_OBJ_REFERENCE), + DEF(WRITE_OBJ_SAB), + DEF(WRITE_OBJ_STRIP_DEBUG), + DEF(WRITE_OBJ_STRIP_SOURCE), +#undef DEF +}; + +static int js_bjson_init(JSContext *ctx, JSModuleDef *m) +{ + return JS_SetModuleExportList(ctx, m, js_bjson_funcs, + countof(js_bjson_funcs)); +} + +JSModuleDef *js_init_module_bjson(JSContext *ctx, const char *module_name) +{ + JSModuleDef *m; + m = JS_NewCModule(ctx, module_name, js_bjson_init); + if (!m) + return NULL; + JS_AddModuleExportList(ctx, m, js_bjson_funcs, countof(js_bjson_funcs)); + return m; +} diff --git a/third_party/quickjs/quickjs-libc.h b/third_party/quickjs/quickjs-libc.h new file mode 100644 index 0000000000..a0ea389df5 --- /dev/null +++ b/third_party/quickjs/quickjs-libc.h @@ -0,0 +1,65 @@ +/* + * QuickJS C library + * + * Copyright (c) 2017-2018 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#ifndef QUICKJS_LIBC_H +#define QUICKJS_LIBC_H + +#include <stdio.h> +#include <stdlib.h> + +#include "quickjs.h" + +#ifdef __cplusplus +extern "C" { +#endif + +JSModuleDef *js_init_module_std(JSContext *ctx, const char *module_name); +JSModuleDef *js_init_module_os(JSContext *ctx, const char *module_name); +JSModuleDef *js_init_module_bjson(JSContext *ctx, const char *module_name); +void js_std_add_helpers(JSContext *ctx, int argc, char **argv); +JSValue js_std_loop(JSContext *ctx); +JSValue js_std_await(JSContext *ctx, JSValue obj); +void js_std_init_handlers(JSRuntime *rt); +void js_std_free_handlers(JSRuntime *rt); +void js_std_dump_error(JSContext *ctx); +void js_std_dump_error1(JSContext *ctx, JSValue exception_val); +uint8_t *js_load_file(JSContext *ctx, size_t *pbuf_len, const char *filename); +int js_module_set_import_meta(JSContext *ctx, JSValue func_val, + JS_BOOL use_realpath, JS_BOOL is_main); +JSModuleDef *js_module_loader(JSContext *ctx, + const char *module_name, void *opaque); +void js_std_eval_binary(JSContext *ctx, const uint8_t *buf, size_t buf_len, + int flags); +void js_std_promise_rejection_tracker(JSContext *ctx, JSValue promise, + JSValue reason, + JS_BOOL is_handled, void *opaque); +void js_std_set_worker_new_context_func(JSContext *(*func)(JSRuntime *rt)); + +// Added for llamafile project +JSValue js_print(JSContext *ctx, JSValue this_val,int argc, JSValue *argv); + +#ifdef __cplusplus +} /* extern "C" { */ +#endif + +#endif /* QUICKJS_LIBC_H */ diff --git a/third_party/quickjs/quickjs-opcode.h b/third_party/quickjs/quickjs-opcode.h new file mode 100644 index 0000000000..09dacb3e32 --- /dev/null +++ b/third_party/quickjs/quickjs-opcode.h @@ -0,0 +1,374 @@ +/* + * QuickJS opcode definitions + * + * Copyright (c) 2017-2018 Fabrice Bellard + * Copyright (c) 2017-2018 Charlie Gordon + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifdef FMT +FMT(none) +FMT(none_int) +FMT(none_loc) +FMT(none_arg) +FMT(none_var_ref) +FMT(u8) +FMT(i8) +FMT(loc8) +FMT(const8) +FMT(label8) +FMT(u16) +FMT(i16) +FMT(label16) +FMT(npop) +FMT(npopx) +FMT(npop_u16) +FMT(loc) +FMT(arg) +FMT(var_ref) +FMT(u32) +FMT(u32x2) +FMT(i32) +FMT(const) +FMT(label) +FMT(atom) +FMT(atom_u8) +FMT(atom_u16) +FMT(atom_label_u8) +FMT(atom_label_u16) +FMT(label_u16) +#undef FMT +#endif /* FMT */ + +#ifdef DEF + +#ifndef def +#define def(id, size, n_pop, n_push, f) DEF(id, size, n_pop, n_push, f) +#endif + +DEF(invalid, 1, 0, 0, none) /* never emitted */ + +/* push values */ +DEF( push_i32, 5, 0, 1, i32) +DEF( push_const, 5, 0, 1, const) +DEF( fclosure, 5, 0, 1, const) /* must follow push_const */ +DEF(push_atom_value, 5, 0, 1, atom) +DEF( private_symbol, 5, 0, 1, atom) +DEF( undefined, 1, 0, 1, none) +DEF( null, 1, 0, 1, none) +DEF( push_this, 1, 0, 1, none) /* only used at the start of a function */ +DEF( push_false, 1, 0, 1, none) +DEF( push_true, 1, 0, 1, none) +DEF( object, 1, 0, 1, none) +DEF( special_object, 2, 0, 1, u8) /* only used at the start of a function */ +DEF( rest, 3, 0, 1, u16) /* only used at the start of a function */ + +DEF( drop, 1, 1, 0, none) /* a -> */ +DEF( nip, 1, 2, 1, none) /* a b -> b */ +DEF( nip1, 1, 3, 2, none) /* a b c -> b c */ +DEF( dup, 1, 1, 2, none) /* a -> a a */ +DEF( dup1, 1, 2, 3, none) /* a b -> a a b */ +DEF( dup2, 1, 2, 4, none) /* a b -> a b a b */ +DEF( dup3, 1, 3, 6, none) /* a b c -> a b c a b c */ +DEF( insert2, 1, 2, 3, none) /* obj a -> a obj a (dup_x1) */ +DEF( insert3, 1, 3, 4, none) /* obj prop a -> a obj prop a (dup_x2) */ +DEF( insert4, 1, 4, 5, none) /* this obj prop a -> a this obj prop a */ +DEF( perm3, 1, 3, 3, none) /* obj a b -> a obj b */ +DEF( perm4, 1, 4, 4, none) /* obj prop a b -> a obj prop b */ +DEF( perm5, 1, 5, 5, none) /* this obj prop a b -> a this obj prop b */ +DEF( swap, 1, 2, 2, none) /* a b -> b a */ +DEF( swap2, 1, 4, 4, none) /* a b c d -> c d a b */ +DEF( rot3l, 1, 3, 3, none) /* x a b -> a b x */ +DEF( rot3r, 1, 3, 3, none) /* a b x -> x a b */ +DEF( rot4l, 1, 4, 4, none) /* x a b c -> a b c x */ +DEF( rot5l, 1, 5, 5, none) /* x a b c d -> a b c d x */ + +DEF(call_constructor, 3, 2, 1, npop) /* func new.target args -> ret. arguments are not counted in n_pop */ +DEF( call, 3, 1, 1, npop) /* arguments are not counted in n_pop */ +DEF( tail_call, 3, 1, 0, npop) /* arguments are not counted in n_pop */ +DEF( call_method, 3, 2, 1, npop) /* arguments are not counted in n_pop */ +DEF(tail_call_method, 3, 2, 0, npop) /* arguments are not counted in n_pop */ +DEF( array_from, 3, 0, 1, npop) /* arguments are not counted in n_pop */ +DEF( apply, 3, 3, 1, u16) +DEF( return, 1, 1, 0, none) +DEF( return_undef, 1, 0, 0, none) +DEF(check_ctor_return, 1, 1, 2, none) +DEF( check_ctor, 1, 0, 0, none) +DEF( check_brand, 1, 2, 2, none) /* this_obj func -> this_obj func */ +DEF( add_brand, 1, 2, 0, none) /* this_obj home_obj -> */ +DEF( return_async, 1, 1, 0, none) +DEF( throw, 1, 1, 0, none) +DEF( throw_error, 6, 0, 0, atom_u8) +DEF( eval, 5, 1, 1, npop_u16) /* func args... -> ret_val */ +DEF( apply_eval, 3, 2, 1, u16) /* func array -> ret_eval */ +DEF( regexp, 1, 2, 1, none) /* create a RegExp object from the pattern and a + bytecode string */ +DEF( get_super, 1, 1, 1, none) +DEF( import, 1, 1, 1, none) /* dynamic module import */ + +DEF( check_var, 5, 0, 1, atom) /* check if a variable exists */ +DEF( get_var_undef, 5, 0, 1, atom) /* push undefined if the variable does not exist */ +DEF( get_var, 5, 0, 1, atom) /* throw an exception if the variable does not exist */ +DEF( put_var, 5, 1, 0, atom) /* must come after get_var */ +DEF( put_var_init, 5, 1, 0, atom) /* must come after put_var. Used to initialize a global lexical variable */ +DEF( put_var_strict, 5, 2, 0, atom) /* for strict mode variable write */ + +DEF( get_ref_value, 1, 2, 3, none) +DEF( put_ref_value, 1, 3, 0, none) + +DEF( define_var, 6, 0, 0, atom_u8) +DEF(check_define_var, 6, 0, 0, atom_u8) +DEF( define_func, 6, 1, 0, atom_u8) + +// order matters, see IC counterparts +DEF( get_field, 5, 1, 1, atom) +DEF( get_field2, 5, 1, 2, atom) +DEF( put_field, 5, 2, 0, atom) + +DEF( get_private_field, 1, 2, 1, none) /* obj prop -> value */ +DEF( put_private_field, 1, 3, 0, none) /* obj value prop -> */ +DEF(define_private_field, 1, 3, 1, none) /* obj prop value -> obj */ +DEF( get_array_el, 1, 2, 1, none) +DEF( get_array_el2, 1, 2, 2, none) /* obj prop -> obj value */ +DEF( put_array_el, 1, 3, 0, none) +DEF(get_super_value, 1, 3, 1, none) /* this obj prop -> value */ +DEF(put_super_value, 1, 4, 0, none) /* this obj prop value -> */ +DEF( define_field, 5, 2, 1, atom) +DEF( set_name, 5, 1, 1, atom) +DEF(set_name_computed, 1, 2, 2, none) +DEF( set_proto, 1, 2, 1, none) +DEF(set_home_object, 1, 2, 2, none) +DEF(define_array_el, 1, 3, 2, none) +DEF( append, 1, 3, 2, none) /* append enumerated object, update length */ +DEF(copy_data_properties, 2, 3, 3, u8) +DEF( define_method, 6, 2, 1, atom_u8) +DEF(define_method_computed, 2, 3, 1, u8) /* must come after define_method */ +DEF( define_class, 6, 2, 2, atom_u8) /* parent ctor -> ctor proto */ +DEF( define_class_computed, 6, 3, 3, atom_u8) /* field_name parent ctor -> field_name ctor proto (class with computed name) */ + +DEF( get_loc, 3, 0, 1, loc) +DEF( put_loc, 3, 1, 0, loc) /* must come after get_loc */ +DEF( set_loc, 3, 1, 1, loc) /* must come after put_loc */ +DEF( get_arg, 3, 0, 1, arg) +DEF( put_arg, 3, 1, 0, arg) /* must come after get_arg */ +DEF( set_arg, 3, 1, 1, arg) /* must come after put_arg */ +DEF( get_var_ref, 3, 0, 1, var_ref) +DEF( put_var_ref, 3, 1, 0, var_ref) /* must come after get_var_ref */ +DEF( set_var_ref, 3, 1, 1, var_ref) /* must come after put_var_ref */ +DEF(set_loc_uninitialized, 3, 0, 0, loc) +DEF( get_loc_check, 3, 0, 1, loc) +DEF( put_loc_check, 3, 1, 0, loc) /* must come after get_loc_check */ +DEF( put_loc_check_init, 3, 1, 0, loc) +DEF(get_var_ref_check, 3, 0, 1, var_ref) +DEF(put_var_ref_check, 3, 1, 0, var_ref) /* must come after get_var_ref_check */ +DEF(put_var_ref_check_init, 3, 1, 0, var_ref) +DEF( close_loc, 3, 0, 0, loc) +DEF( if_false, 5, 1, 0, label) +DEF( if_true, 5, 1, 0, label) /* must come after if_false */ +DEF( goto, 5, 0, 0, label) /* must come after if_true */ +DEF( catch, 5, 0, 1, label) +DEF( gosub, 5, 0, 0, label) /* used to execute the finally block */ +DEF( ret, 1, 1, 0, none) /* used to return from the finally block */ +DEF( nip_catch, 1, 2, 1, none) /* catch ... a -> a */ + +DEF( to_object, 1, 1, 1, none) +//DEF( to_string, 1, 1, 1, none) +DEF( to_propkey, 1, 1, 1, none) +DEF( to_propkey2, 1, 2, 2, none) + +DEF( with_get_var, 10, 1, 0, atom_label_u8) /* must be in the same order as scope_xxx */ +DEF( with_put_var, 10, 2, 1, atom_label_u8) /* must be in the same order as scope_xxx */ +DEF(with_delete_var, 10, 1, 0, atom_label_u8) /* must be in the same order as scope_xxx */ +DEF( with_make_ref, 10, 1, 0, atom_label_u8) /* must be in the same order as scope_xxx */ +DEF( with_get_ref, 10, 1, 0, atom_label_u8) /* must be in the same order as scope_xxx */ +DEF(with_get_ref_undef, 10, 1, 0, atom_label_u8) + +DEF( make_loc_ref, 7, 0, 2, atom_u16) +DEF( make_arg_ref, 7, 0, 2, atom_u16) +DEF(make_var_ref_ref, 7, 0, 2, atom_u16) +DEF( make_var_ref, 5, 0, 2, atom) + +DEF( for_in_start, 1, 1, 1, none) +DEF( for_of_start, 1, 1, 3, none) +DEF(for_await_of_start, 1, 1, 3, none) +DEF( for_in_next, 1, 1, 3, none) +DEF( for_of_next, 2, 3, 5, u8) +DEF(iterator_check_object, 1, 1, 1, none) +DEF(iterator_get_value_done, 1, 1, 2, none) +DEF( iterator_close, 1, 3, 0, none) +DEF( iterator_next, 1, 4, 4, none) +DEF( iterator_call, 2, 4, 5, u8) +DEF( initial_yield, 1, 0, 0, none) +DEF( yield, 1, 1, 2, none) +DEF( yield_star, 1, 1, 2, none) +DEF(async_yield_star, 1, 1, 2, none) +DEF( await, 1, 1, 1, none) + +/* arithmetic/logic operations */ +DEF( neg, 1, 1, 1, none) +DEF( plus, 1, 1, 1, none) +DEF( dec, 1, 1, 1, none) +DEF( inc, 1, 1, 1, none) +DEF( post_dec, 1, 1, 2, none) +DEF( post_inc, 1, 1, 2, none) +DEF( dec_loc, 2, 0, 0, loc8) +DEF( inc_loc, 2, 0, 0, loc8) +DEF( add_loc, 2, 1, 0, loc8) +DEF( not, 1, 1, 1, none) +DEF( lnot, 1, 1, 1, none) +DEF( typeof, 1, 1, 1, none) +DEF( delete, 1, 2, 1, none) +DEF( delete_var, 5, 0, 1, atom) + +/* warning: order matters (see js_parse_assign_expr) */ +DEF( mul, 1, 2, 1, none) +DEF( div, 1, 2, 1, none) +DEF( mod, 1, 2, 1, none) +DEF( add, 1, 2, 1, none) +DEF( sub, 1, 2, 1, none) +DEF( shl, 1, 2, 1, none) +DEF( sar, 1, 2, 1, none) +DEF( shr, 1, 2, 1, none) +DEF( and, 1, 2, 1, none) +DEF( xor, 1, 2, 1, none) +DEF( or, 1, 2, 1, none) +DEF( pow, 1, 2, 1, none) + +DEF( lt, 1, 2, 1, none) +DEF( lte, 1, 2, 1, none) +DEF( gt, 1, 2, 1, none) +DEF( gte, 1, 2, 1, none) +DEF( instanceof, 1, 2, 1, none) +DEF( in, 1, 2, 1, none) +DEF( eq, 1, 2, 1, none) +DEF( neq, 1, 2, 1, none) +DEF( strict_eq, 1, 2, 1, none) +DEF( strict_neq, 1, 2, 1, none) +DEF(is_undefined_or_null, 1, 1, 1, none) +DEF( private_in, 1, 2, 1, none) +/* must be the last non short and non temporary opcode */ +DEF( nop, 1, 0, 0, none) + +/* temporary opcodes: never emitted in the final bytecode */ + +def( enter_scope, 3, 0, 0, u16) /* emitted in phase 1, removed in phase 2 */ +def( leave_scope, 3, 0, 0, u16) /* emitted in phase 1, removed in phase 2 */ + +def( label, 5, 0, 0, label) /* emitted in phase 1, removed in phase 3 */ + +def(scope_get_var_undef, 7, 0, 1, atom_u16) /* emitted in phase 1, removed in phase 2 */ +def( scope_get_var, 7, 0, 1, atom_u16) /* emitted in phase 1, removed in phase 2 */ +def( scope_put_var, 7, 1, 0, atom_u16) /* emitted in phase 1, removed in phase 2 */ +def(scope_delete_var, 7, 0, 1, atom_u16) /* emitted in phase 1, removed in phase 2 */ +def( scope_make_ref, 11, 0, 2, atom_label_u16) /* emitted in phase 1, removed in phase 2 */ +def( scope_get_ref, 7, 0, 2, atom_u16) /* emitted in phase 1, removed in phase 2 */ +def(scope_put_var_init, 7, 0, 2, atom_u16) /* emitted in phase 1, removed in phase 2 */ +def(scope_get_private_field, 7, 1, 1, atom_u16) /* obj -> value, emitted in phase 1, removed in phase 2 */ +def(scope_get_private_field2, 7, 1, 2, atom_u16) /* obj -> obj value, emitted in phase 1, removed in phase 2 */ +def(scope_put_private_field, 7, 2, 0, atom_u16) /* obj value ->, emitted in phase 1, removed in phase 2 */ +def(scope_in_private_field, 7, 1, 1, atom_u16) /* obj -> res emitted in phase 1, removed in phase 2 */ +def(get_field_opt_chain, 5, 1, 1, atom) /* emitted in phase 1, removed in phase 2 */ +def(get_array_el_opt_chain, 1, 2, 1, none) /* emitted in phase 1, removed in phase 2 */ +def( set_class_name, 5, 1, 1, u32) /* emitted in phase 1, removed in phase 2 */ + +def( source_loc, 9, 0, 0, u32x2) /* emitted in phase 1, removed in phase 3 */ + +DEF( push_minus1, 1, 0, 1, none_int) +DEF( push_0, 1, 0, 1, none_int) +DEF( push_1, 1, 0, 1, none_int) +DEF( push_2, 1, 0, 1, none_int) +DEF( push_3, 1, 0, 1, none_int) +DEF( push_4, 1, 0, 1, none_int) +DEF( push_5, 1, 0, 1, none_int) +DEF( push_6, 1, 0, 1, none_int) +DEF( push_7, 1, 0, 1, none_int) +DEF( push_i8, 2, 0, 1, i8) +DEF( push_i16, 3, 0, 1, i16) +DEF( push_const8, 2, 0, 1, const8) +DEF( fclosure8, 2, 0, 1, const8) /* must follow push_const8 */ +DEF(push_empty_string, 1, 0, 1, none) + +DEF( get_loc8, 2, 0, 1, loc8) +DEF( put_loc8, 2, 1, 0, loc8) +DEF( set_loc8, 2, 1, 1, loc8) + +DEF( get_loc0_loc1, 1, 0, 2, none_loc) +DEF( get_loc0, 1, 0, 1, none_loc) +DEF( get_loc1, 1, 0, 1, none_loc) +DEF( get_loc2, 1, 0, 1, none_loc) +DEF( get_loc3, 1, 0, 1, none_loc) +DEF( put_loc0, 1, 1, 0, none_loc) +DEF( put_loc1, 1, 1, 0, none_loc) +DEF( put_loc2, 1, 1, 0, none_loc) +DEF( put_loc3, 1, 1, 0, none_loc) +DEF( set_loc0, 1, 1, 1, none_loc) +DEF( set_loc1, 1, 1, 1, none_loc) +DEF( set_loc2, 1, 1, 1, none_loc) +DEF( set_loc3, 1, 1, 1, none_loc) +DEF( get_arg0, 1, 0, 1, none_arg) +DEF( get_arg1, 1, 0, 1, none_arg) +DEF( get_arg2, 1, 0, 1, none_arg) +DEF( get_arg3, 1, 0, 1, none_arg) +DEF( put_arg0, 1, 1, 0, none_arg) +DEF( put_arg1, 1, 1, 0, none_arg) +DEF( put_arg2, 1, 1, 0, none_arg) +DEF( put_arg3, 1, 1, 0, none_arg) +DEF( set_arg0, 1, 1, 1, none_arg) +DEF( set_arg1, 1, 1, 1, none_arg) +DEF( set_arg2, 1, 1, 1, none_arg) +DEF( set_arg3, 1, 1, 1, none_arg) +DEF( get_var_ref0, 1, 0, 1, none_var_ref) +DEF( get_var_ref1, 1, 0, 1, none_var_ref) +DEF( get_var_ref2, 1, 0, 1, none_var_ref) +DEF( get_var_ref3, 1, 0, 1, none_var_ref) +DEF( put_var_ref0, 1, 1, 0, none_var_ref) +DEF( put_var_ref1, 1, 1, 0, none_var_ref) +DEF( put_var_ref2, 1, 1, 0, none_var_ref) +DEF( put_var_ref3, 1, 1, 0, none_var_ref) +DEF( set_var_ref0, 1, 1, 1, none_var_ref) +DEF( set_var_ref1, 1, 1, 1, none_var_ref) +DEF( set_var_ref2, 1, 1, 1, none_var_ref) +DEF( set_var_ref3, 1, 1, 1, none_var_ref) + +DEF( get_length, 1, 1, 1, none) + +DEF( if_false8, 2, 1, 0, label8) +DEF( if_true8, 2, 1, 0, label8) /* must come after if_false8 */ +DEF( goto8, 2, 0, 0, label8) /* must come after if_true8 */ +DEF( goto16, 3, 0, 0, label16) + +DEF( call0, 1, 1, 1, npopx) +DEF( call1, 1, 1, 1, npopx) +DEF( call2, 1, 1, 1, npopx) +DEF( call3, 1, 1, 1, npopx) + +DEF( is_undefined, 1, 1, 1, none) +DEF( is_null, 1, 1, 1, none) +DEF(typeof_is_undefined, 1, 1, 1, none) +DEF( typeof_is_function, 1, 1, 1, none) + +// order matters, see non-IC counterparts +DEF( get_field_ic, 5, 1, 1, none) +DEF( get_field2_ic, 5, 1, 2, none) +DEF( put_field_ic, 5, 2, 0, none) + +#undef DEF +#undef def +#endif /* DEF */ diff --git a/third_party/quickjs/quickjs.c b/third_party/quickjs/quickjs.c new file mode 100644 index 0000000000..9cac6de40b --- /dev/null +++ b/third_party/quickjs/quickjs.c @@ -0,0 +1,56103 @@ +/* + * QuickJS Javascript Engine + * + * Copyright (c) 2017-2021 Fabrice Bellard + * Copyright (c) 2017-2021 Charlie Gordon + * Copyright (c) 2023 Ben Noordhuis + * Copyright (c) 2023 Saúl Ibarra Corretgé + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include <stdlib.h> +#include <stdio.h> +#include <stdarg.h> +#include <inttypes.h> +#include <string.h> +#include <assert.h> +#if !defined(_MSC_VER) +#include <sys/time.h> +#if defined(_WIN32) +#include <timezoneapi.h> +#endif +#endif +#include <time.h> +#include <fenv.h> +#include <math.h> + +#include "cutils.h" +#include "list.h" +#include "quickjs.h" +#include "libregexp.h" +#include "libbf.h" + +#if defined(EMSCRIPTEN) || defined(_MSC_VER) +#define DIRECT_DISPATCH 0 +#else +#define DIRECT_DISPATCH 1 +#endif + +#if defined(__APPLE__) +#define MALLOC_OVERHEAD 0 +#else +#define MALLOC_OVERHEAD 8 +#endif + +#if defined(__NEWLIB__) +#define NO_TM_GMTOFF +#endif + +// atomic_store etc. are completely busted in recent versions of tcc; +// somehow the compiler forgets to load |ptr| into %rdi when calling +// the __atomic_*() helpers in its lib/stdatomic.c and lib/atomic.S +#if !defined(__TINYC__) && !defined(EMSCRIPTEN) && !defined(__wasi__) && !__STDC_NO_ATOMICS__ +#include "quickjs-c-atomics.h" +#define CONFIG_ATOMICS +#endif + +#ifndef __GNUC__ +#define __extension__ +#endif + +// Debug trace system: the debug output will be produced to the dump stream (currently +// stdout) if qjs is invoked with -D<bitmask> with the corresponding bit set. + +#ifndef NDEBUG +#define DUMP_BYTECODE_FINAL 0x01 /* dump pass 3 final byte code */ +#define DUMP_BYTECODE_PASS2 0x02 /* dump pass 2 code */ +#define DUMP_BYTECODE_PASS1 0x04 /* dump pass 1 code */ +#define DUMP_BYTECODE_HEX 0x10 /* dump bytecode in hex */ +#define DUMP_BYTECODE_PC2LINE 0x20 /* dump line number table */ +#define DUMP_BYTECODE_STACK 0x40 /* dump compute_stack_size */ +#define DUMP_BYTECODE_STEP 0x80 /* dump executed bytecode */ +#define DUMP_READ_OBJECT 0x100 /* dump the marshalled objects at load time */ +#define DUMP_FREE 0x200 /* dump every object free */ +#define DUMP_GC 0x400 /* dump the occurrence of the automatic GC */ +#define DUMP_GC_FREE 0x800 /* dump objects freed by the GC */ +#define DUMP_MODULE_RESOLVE 0x1000 /* dump module resolution steps */ +#define DUMP_PROMISE 0x2000 /* dump promise steps */ +#define DUMP_LEAKS 0x4000 /* dump leaked objects and strings in JS_FreeRuntime */ +#define DUMP_ATOM_LEAKS 0x8000 /* dump leaked atoms in JS_FreeRuntime */ +#define DUMP_MEM 0x10000 /* dump memory usage in JS_FreeRuntime */ +#define DUMP_OBJECTS 0x20000 /* dump objects in JS_FreeRuntime */ +#define DUMP_ATOMS 0x40000 /* dump atoms in JS_FreeRuntime */ +#define DUMP_SHAPES 0x80000 /* dump shapes in JS_FreeRuntime */ +#endif + +//#define FORCE_GC_AT_MALLOC /* test the GC by forcing it before each object allocation */ + +#define check_dump_flag(rt, flag) ((rt->dump_flags & (flag +0)) == (flag +0)) + +#define STRINGIFY_(x) #x +#define STRINGIFY(x) STRINGIFY_(x) + +#define QJS_VERSION_STRING \ + STRINGIFY(QJS_VERSION_MAJOR) "." STRINGIFY(QJS_VERSION_MINOR) "." STRINGIFY(QJS_VERSION_PATCH) QJS_VERSION_SUFFIX + +const char* JS_GetVersion(void) { + return QJS_VERSION_STRING; +} + +#undef STRINFIGY_ +#undef STRINGIFY + +enum { + /* classid tag */ /* union usage | properties */ + JS_CLASS_OBJECT = 1, /* must be first */ + JS_CLASS_ARRAY, /* u.array | length */ + JS_CLASS_ERROR, + JS_CLASS_NUMBER, /* u.object_data */ + JS_CLASS_STRING, /* u.object_data */ + JS_CLASS_BOOLEAN, /* u.object_data */ + JS_CLASS_SYMBOL, /* u.object_data */ + JS_CLASS_ARGUMENTS, /* u.array | length */ + JS_CLASS_MAPPED_ARGUMENTS, /* | length */ + JS_CLASS_DATE, /* u.object_data */ + JS_CLASS_MODULE_NS, + JS_CLASS_C_FUNCTION, /* u.cfunc */ + JS_CLASS_BYTECODE_FUNCTION, /* u.func */ + JS_CLASS_BOUND_FUNCTION, /* u.bound_function */ + JS_CLASS_C_FUNCTION_DATA, /* u.c_function_data_record */ + JS_CLASS_GENERATOR_FUNCTION, /* u.func */ + JS_CLASS_FOR_IN_ITERATOR, /* u.for_in_iterator */ + JS_CLASS_REGEXP, /* u.regexp */ + JS_CLASS_ARRAY_BUFFER, /* u.array_buffer */ + JS_CLASS_SHARED_ARRAY_BUFFER, /* u.array_buffer */ + JS_CLASS_UINT8C_ARRAY, /* u.array (typed_array) */ + JS_CLASS_INT8_ARRAY, /* u.array (typed_array) */ + JS_CLASS_UINT8_ARRAY, /* u.array (typed_array) */ + JS_CLASS_INT16_ARRAY, /* u.array (typed_array) */ + JS_CLASS_UINT16_ARRAY, /* u.array (typed_array) */ + JS_CLASS_INT32_ARRAY, /* u.array (typed_array) */ + JS_CLASS_UINT32_ARRAY, /* u.array (typed_array) */ + JS_CLASS_BIG_INT64_ARRAY, /* u.array (typed_array) */ + JS_CLASS_BIG_UINT64_ARRAY, /* u.array (typed_array) */ + JS_CLASS_FLOAT16_ARRAY, /* u.array (typed_array) */ + JS_CLASS_FLOAT32_ARRAY, /* u.array (typed_array) */ + JS_CLASS_FLOAT64_ARRAY, /* u.array (typed_array) */ + JS_CLASS_DATAVIEW, /* u.typed_array */ + JS_CLASS_BIG_INT, /* u.object_data */ + JS_CLASS_MAP, /* u.map_state */ + JS_CLASS_SET, /* u.map_state */ + JS_CLASS_WEAKMAP, /* u.map_state */ + JS_CLASS_WEAKSET, /* u.map_state */ + JS_CLASS_ITERATOR, + JS_CLASS_ITERATOR_HELPER, /* u.iterator_helper_data */ + JS_CLASS_ITERATOR_WRAP, /* u.iterator_wrap_data */ + JS_CLASS_MAP_ITERATOR, /* u.map_iterator_data */ + JS_CLASS_SET_ITERATOR, /* u.map_iterator_data */ + JS_CLASS_ARRAY_ITERATOR, /* u.array_iterator_data */ + JS_CLASS_STRING_ITERATOR, /* u.array_iterator_data */ + JS_CLASS_REGEXP_STRING_ITERATOR, /* u.regexp_string_iterator_data */ + JS_CLASS_GENERATOR, /* u.generator_data */ + JS_CLASS_PROXY, /* u.proxy_data */ + JS_CLASS_PROMISE, /* u.promise_data */ + JS_CLASS_PROMISE_RESOLVE_FUNCTION, /* u.promise_function_data */ + JS_CLASS_PROMISE_REJECT_FUNCTION, /* u.promise_function_data */ + JS_CLASS_ASYNC_FUNCTION, /* u.func */ + JS_CLASS_ASYNC_FUNCTION_RESOLVE, /* u.async_function_data */ + JS_CLASS_ASYNC_FUNCTION_REJECT, /* u.async_function_data */ + JS_CLASS_ASYNC_FROM_SYNC_ITERATOR, /* u.async_from_sync_iterator_data */ + JS_CLASS_ASYNC_GENERATOR_FUNCTION, /* u.func */ + JS_CLASS_ASYNC_GENERATOR, /* u.async_generator_data */ + JS_CLASS_WEAK_REF, + JS_CLASS_FINALIZATION_REGISTRY, + JS_CLASS_CALL_SITE, + + JS_CLASS_INIT_COUNT, /* last entry for predefined classes */ +}; + +/* number of typed array types */ +#define JS_TYPED_ARRAY_COUNT (JS_CLASS_FLOAT64_ARRAY - JS_CLASS_UINT8C_ARRAY + 1) +static uint8_t const typed_array_size_log2[JS_TYPED_ARRAY_COUNT]; +#define typed_array_size_log2(classid) (typed_array_size_log2[(classid)- JS_CLASS_UINT8C_ARRAY]) + +typedef enum JSErrorEnum { + JS_EVAL_ERROR, + JS_RANGE_ERROR, + JS_REFERENCE_ERROR, + JS_SYNTAX_ERROR, + JS_TYPE_ERROR, + JS_URI_ERROR, + JS_INTERNAL_ERROR, + JS_AGGREGATE_ERROR, + + JS_NATIVE_ERROR_COUNT, /* number of different NativeError objects */ + JS_PLAIN_ERROR = JS_NATIVE_ERROR_COUNT +} JSErrorEnum; + +#define JS_MAX_LOCAL_VARS 65535 +#define JS_STACK_SIZE_MAX 65534 +#define JS_STRING_LEN_MAX ((1 << 30) - 1) + +#define __exception __attribute__((warn_unused_result)) + +typedef struct JSShape JSShape; +typedef struct JSString JSString; +typedef struct JSString JSAtomStruct; + +#define JS_VALUE_GET_STRING(v) ((JSString *)JS_VALUE_GET_PTR(v)) + +typedef enum { + JS_GC_PHASE_NONE, + JS_GC_PHASE_DECREF, + JS_GC_PHASE_REMOVE_CYCLES, +} JSGCPhaseEnum; + +typedef struct JSMallocState { + size_t malloc_count; + size_t malloc_size; + size_t malloc_limit; + void *opaque; /* user opaque */ +} JSMallocState; + +typedef struct JSRuntimeFinalizerState { + struct JSRuntimeFinalizerState *next; + JSRuntimeFinalizer *finalizer; + void *arg; +} JSRuntimeFinalizerState; + +struct JSRuntime { + JSMallocFunctions mf; + JSMallocState malloc_state; + const char *rt_info; + + int atom_hash_size; /* power of two */ + int atom_count; + int atom_size; + int atom_count_resize; /* resize hash table at this count */ + uint32_t *atom_hash; + JSAtomStruct **atom_array; + int atom_free_index; /* 0 = none */ + + JSClassID js_class_id_alloc; /* counter for user defined classes */ + int class_count; /* size of class_array */ + JSClass *class_array; + + struct list_head context_list; /* list of JSContext.link */ + /* list of JSGCObjectHeader.link. List of allocated GC objects (used + by the garbage collector) */ + struct list_head gc_obj_list; + /* list of JSGCObjectHeader.link. Used during JS_FreeValueRT() */ + struct list_head gc_zero_ref_count_list; + struct list_head tmp_obj_list; /* used during GC */ + JSGCPhaseEnum gc_phase : 8; + size_t malloc_gc_threshold; +#ifdef DUMP_LEAKS + struct list_head string_list; /* list of JSString.link */ +#endif + /* stack limitation */ + uintptr_t stack_size; /* in bytes, 0 if no limit */ + uintptr_t stack_top; + uintptr_t stack_limit; /* lower stack limit */ + + JSValue current_exception; + /* true if inside an out of memory error, to avoid recursing */ + BOOL in_out_of_memory : 8; + /* and likewise if inside Error.prepareStackTrace() */ + BOOL in_prepare_stack_trace : 8; + /* true if inside JS_FreeRuntime */ + BOOL in_free : 8; + + struct JSStackFrame *current_stack_frame; + + JSInterruptHandler *interrupt_handler; + void *interrupt_opaque; + + JSHostPromiseRejectionTracker *host_promise_rejection_tracker; + void *host_promise_rejection_tracker_opaque; + + struct list_head job_list; /* list of JSJobEntry.link */ + + JSModuleNormalizeFunc *module_normalize_func; + JSModuleLoaderFunc *module_loader_func; + void *module_loader_opaque; + /* timestamp for internal use in module evaluation */ + int64_t module_async_evaluation_next_timestamp; + + /* used to allocate, free and clone SharedArrayBuffers */ + JSSharedArrayBufferFunctions sab_funcs; + + BOOL can_block : 8; /* TRUE if Atomics.wait can block */ + uint32_t dump_flags : 24; + + /* Shape hash table */ + int shape_hash_bits; + int shape_hash_size; + int shape_hash_count; /* number of hashed shapes */ + JSShape **shape_hash; + bf_context_t bf_ctx; + void *user_opaque; + void *libc_opaque; + JSRuntimeFinalizerState *finalizers; +}; + +struct JSClass { + uint32_t class_id; /* 0 means free entry */ + JSAtom class_name; + JSClassFinalizer *finalizer; + JSClassGCMark *gc_mark; + JSClassCall *call; + /* pointers for exotic behavior, can be NULL if none are present */ + const JSClassExoticMethods *exotic; +}; + +typedef struct JSStackFrame { + struct JSStackFrame *prev_frame; /* NULL if first stack frame */ + JSValue cur_func; /* current function, JS_UNDEFINED if the frame is detached */ + JSValue *arg_buf; /* arguments */ + JSValue *var_buf; /* variables */ + struct list_head var_ref_list; /* list of JSVarRef.link */ + uint8_t *cur_pc; /* only used in bytecode functions : PC of the + instruction after the call */ + uint32_t arg_count : 31; + uint32_t is_strict_mode : 1; + /* only used in generators. Current stack pointer value. NULL if + the function is running. */ + JSValue *cur_sp; +} JSStackFrame; + +typedef enum { + JS_GC_OBJ_TYPE_JS_OBJECT, + JS_GC_OBJ_TYPE_FUNCTION_BYTECODE, + JS_GC_OBJ_TYPE_SHAPE, + JS_GC_OBJ_TYPE_VAR_REF, + JS_GC_OBJ_TYPE_ASYNC_FUNCTION, + JS_GC_OBJ_TYPE_JS_CONTEXT, +} JSGCObjectTypeEnum; + +/* header for GC objects. GC objects are C data structures with a + reference count that can reference other GC objects. JS Objects are + a particular type of GC object. */ +struct JSGCObjectHeader { + int ref_count; /* must come first, 32-bit */ + JSGCObjectTypeEnum gc_obj_type : 4; + uint8_t mark : 4; /* used by the GC */ + uint8_t dummy1; /* not used by the GC */ + uint16_t dummy2; /* not used by the GC */ + struct list_head link; +}; + +typedef struct JSVarRef { + union { + JSGCObjectHeader header; /* must come first */ + struct { + int __gc_ref_count; /* corresponds to header.ref_count */ + uint8_t __gc_mark; /* corresponds to header.mark/gc_obj_type */ + + /* 0 : the JSVarRef is on the stack. header.link is an element + of JSStackFrame.var_ref_list. + 1 : the JSVarRef is detached. header.link has the normal meanning + */ + uint8_t is_detached : 1; + uint8_t is_arg : 1; + uint16_t var_idx; /* index of the corresponding function variable on + the stack */ + }; + }; + JSValue *pvalue; /* pointer to the value, either on the stack or + to 'value' */ + JSValue value; /* used when the variable is no longer on the stack */ +} JSVarRef; + +typedef struct JSRefCountHeader { + int ref_count; +} JSRefCountHeader; + +/* the same structure is used for big integers. + Big integers are never infinite or NaNs */ +typedef struct JSBigInt { + JSRefCountHeader header; /* must come first, 32-bit */ + bf_t num; +} JSBigInt; + +typedef enum { + JS_AUTOINIT_ID_PROTOTYPE, + JS_AUTOINIT_ID_MODULE_NS, + JS_AUTOINIT_ID_PROP, +} JSAutoInitIDEnum; + +/* must be large enough to have a negligible runtime cost and small + enough to call the interrupt callback often. */ +#define JS_INTERRUPT_COUNTER_INIT 10000 + +struct JSContext { + JSGCObjectHeader header; /* must come first */ + JSRuntime *rt; + struct list_head link; + + uint16_t binary_object_count; + int binary_object_size; + + JSShape *array_shape; /* initial shape for Array objects */ + + JSValue *class_proto; + JSValue function_proto; + JSValue function_ctor; + JSValue array_ctor; + JSValue regexp_ctor; + JSValue promise_ctor; + JSValue native_error_proto[JS_NATIVE_ERROR_COUNT]; + JSValue error_ctor; + JSValue error_prepare_stack; + int error_stack_trace_limit; + JSValue iterator_ctor; + JSValue iterator_proto; + JSValue async_iterator_proto; + JSValue array_proto_values; + JSValue throw_type_error; + JSValue eval_obj; + + JSValue global_obj; /* global object */ + JSValue global_var_obj; /* contains the global let/const definitions */ + + double time_origin; + + uint64_t random_state; + bf_context_t *bf_ctx; /* points to rt->bf_ctx, shared by all contexts */ + /* when the counter reaches zero, JSRutime.interrupt_handler is called */ + int interrupt_counter; + + struct list_head loaded_modules; /* list of JSModuleDef.link */ + + /* if NULL, RegExp compilation is not supported */ + JSValue (*compile_regexp)(JSContext *ctx, JSValue pattern, + JSValue flags); + /* if NULL, eval is not supported */ + JSValue (*eval_internal)(JSContext *ctx, JSValue this_obj, + const char *input, size_t input_len, + const char *filename, int flags, int scope_idx); + void *user_opaque; +}; + +typedef union JSFloat64Union { + double d; + uint64_t u64; + uint32_t u32[2]; +} JSFloat64Union; + +typedef enum { + JS_WEAK_REF_KIND_MAP, + JS_WEAK_REF_KIND_WEAK_REF, + JS_WEAK_REF_KIND_FINALIZATION_REGISTRY_ENTRY, +} JSWeakRefKindEnum; + +typedef struct JSWeakRefRecord { + JSWeakRefKindEnum kind; + struct JSWeakRefRecord *next_weak_ref; + union { + struct JSMapRecord *map_record; + struct JSWeakRefData *weak_ref_data; + struct JSFinRecEntry *fin_rec_entry; + } u; +} JSWeakRefRecord; + +enum { + JS_ATOM_TYPE_STRING = 1, + JS_ATOM_TYPE_GLOBAL_SYMBOL, + JS_ATOM_TYPE_SYMBOL, + JS_ATOM_TYPE_PRIVATE, +}; + +enum { + JS_ATOM_HASH_SYMBOL, + JS_ATOM_HASH_PRIVATE, +}; + +typedef enum { + JS_ATOM_KIND_STRING, + JS_ATOM_KIND_SYMBOL, + JS_ATOM_KIND_PRIVATE, +} JSAtomKindEnum; + +#define JS_ATOM_HASH_MASK ((1 << 30) - 1) + +struct JSString { + JSRefCountHeader header; /* must come first, 32-bit */ + uint32_t len : 31; + uint8_t is_wide_char : 1; /* 0 = 8 bits, 1 = 16 bits characters */ + /* for JS_ATOM_TYPE_SYMBOL: hash = 0, atom_type = 3, + for JS_ATOM_TYPE_PRIVATE: hash = 1, atom_type = 3 + XXX: could change encoding to have one more bit in hash */ + uint32_t hash : 30; + uint8_t atom_type : 2; /* != 0 if atom, JS_ATOM_TYPE_x */ + uint32_t hash_next; /* atom_index for JS_ATOM_TYPE_SYMBOL */ + JSWeakRefRecord *first_weak_ref; +#ifdef DUMP_LEAKS + struct list_head link; /* string list */ +#endif + union { + __extension__ uint8_t str8[0]; /* 8 bit strings will get an extra null terminator */ + __extension__ uint16_t str16[0]; + } u; +}; + +typedef struct JSClosureVar { + uint8_t is_local : 1; + uint8_t is_arg : 1; + uint8_t is_const : 1; + uint8_t is_lexical : 1; + uint8_t var_kind : 4; /* see JSVarKindEnum */ + /* 8 bits available */ + uint16_t var_idx; /* is_local = TRUE: index to a normal variable of the + parent function. otherwise: index to a closure + variable of the parent function */ + JSAtom var_name; +} JSClosureVar; + +#define ARG_SCOPE_INDEX 1 +#define ARG_SCOPE_END (-2) + +typedef struct JSVarScope { + int parent; /* index into fd->scopes of the enclosing scope */ + int first; /* index into fd->vars of the last variable in this scope */ +} JSVarScope; + +typedef enum { + /* XXX: add more variable kinds here instead of using bit fields */ + JS_VAR_NORMAL, + JS_VAR_FUNCTION_DECL, /* lexical var with function declaration */ + JS_VAR_NEW_FUNCTION_DECL, /* lexical var with async/generator + function declaration */ + JS_VAR_CATCH, + JS_VAR_FUNCTION_NAME, /* function expression name */ + JS_VAR_PRIVATE_FIELD, + JS_VAR_PRIVATE_METHOD, + JS_VAR_PRIVATE_GETTER, + JS_VAR_PRIVATE_SETTER, /* must come after JS_VAR_PRIVATE_GETTER */ + JS_VAR_PRIVATE_GETTER_SETTER, /* must come after JS_VAR_PRIVATE_SETTER */ +} JSVarKindEnum; + +/* XXX: could use a different structure in bytecode functions to save + memory */ +typedef struct JSVarDef { + JSAtom var_name; + /* index into fd->scopes of this variable lexical scope */ + int scope_level; + /* during compilation: + - if scope_level = 0: scope in which the variable is defined + - if scope_level != 0: index into fd->vars of the next + variable in the same or enclosing lexical scope + in a bytecode function: + index into fd->vars of the next + variable in the same or enclosing lexical scope + */ + int scope_next; + uint8_t is_const : 1; + uint8_t is_lexical : 1; + uint8_t is_captured : 1; + uint8_t is_static_private : 1; /* only used during private class field parsing */ + uint8_t var_kind : 4; /* see JSVarKindEnum */ + /* only used during compilation: function pool index for lexical + variables with var_kind = + JS_VAR_FUNCTION_DECL/JS_VAR_NEW_FUNCTION_DECL or scope level of + the definition of the 'var' variables (they have scope_level = + 0) */ + int func_pool_idx : 24; /* only used during compilation : index in + the constant pool for hoisted function + definition */ +} JSVarDef; + +/* for the encoding of the pc2line table */ +#define PC2LINE_BASE (-1) +#define PC2LINE_RANGE 5 +#define PC2LINE_OP_FIRST 1 +#define PC2LINE_DIFF_PC_MAX ((255 - PC2LINE_OP_FIRST) / PC2LINE_RANGE) + +typedef enum JSFunctionKindEnum { + JS_FUNC_NORMAL = 0, + JS_FUNC_GENERATOR = (1 << 0), + JS_FUNC_ASYNC = (1 << 1), + JS_FUNC_ASYNC_GENERATOR = (JS_FUNC_GENERATOR | JS_FUNC_ASYNC), +} JSFunctionKindEnum; + +#define IC_CACHE_ITEM_CAPACITY 4 + +typedef struct JSInlineCacheRingSlot { + /* SoA for space optimization: 56 bytes */ + JSShape* shape[IC_CACHE_ITEM_CAPACITY]; + uint32_t prop_offset[IC_CACHE_ITEM_CAPACITY]; + JSAtom atom; + uint8_t index; +} JSInlineCacheRingSlot; + +typedef struct JSInlineCacheHashSlot { + JSAtom atom; + uint32_t index; + struct JSInlineCacheHashSlot *next; +} JSInlineCacheHashSlot; + +typedef struct JSInlineCache { + uint32_t count; + uint32_t capacity; + uint32_t hash_bits; + JSInlineCacheHashSlot **hash; + JSInlineCacheRingSlot *cache; +} JSInlineCache; + +#define INLINE_CACHE_MISS ((uint32_t)-1) // sentinel + +// This is a struct so we don't tie up two argument registers in calls to +// JS_GetPropertyInternal2 and JS_SetPropertyInternal2 in the common case +// where there is no IC and therefore no offset to update. +typedef struct JSInlineCacheUpdate { + JSInlineCache *ic; + uint32_t offset; +} JSInlineCacheUpdate; + +static JSInlineCache *init_ic(JSContext *ctx); +static int rebuild_ic(JSContext *ctx, JSInlineCache *ic); +static int resize_ic_hash(JSContext *ctx, JSInlineCache *ic); +static int free_ic(JSRuntime *rt, JSInlineCache *ic); +static void add_ic_slot(JSContext *ctx, JSInlineCacheUpdate *icu, + JSAtom atom, JSObject *object, uint32_t prop_offset); + +static uint32_t get_ic_prop_offset(const JSInlineCacheUpdate *icu, + JSShape *shape) +{ + uint32_t i, cache_offset = icu->offset; + JSInlineCache *ic = icu->ic; + JSInlineCacheRingSlot *cr; + JSShape *shape_slot; + assert(cache_offset < ic->capacity); + cr = ic->cache + cache_offset; + i = cr->index; + for (;;) { + shape_slot = *(cr->shape + i); + if (likely(shape_slot == shape)) { + cr->index = i; + return cr->prop_offset[i]; + } + + i = (i + 1) % countof(cr->shape); + if (unlikely(i == cr->index)) { + break; + } + } + + return INLINE_CACHE_MISS; +} + +static force_inline JSAtom get_ic_atom(JSInlineCache *ic, uint32_t cache_offset) +{ + assert(cache_offset < ic->capacity); + return ic->cache[cache_offset].atom; +} + +typedef struct JSFunctionBytecode { + JSGCObjectHeader header; /* must come first */ + uint8_t is_strict_mode : 1; + uint8_t has_prototype : 1; /* true if a prototype field is necessary */ + uint8_t has_simple_parameter_list : 1; + uint8_t is_derived_class_constructor : 1; + /* true if home_object needs to be initialized */ + uint8_t need_home_object : 1; + uint8_t func_kind : 2; + uint8_t new_target_allowed : 1; + uint8_t super_call_allowed : 1; + uint8_t super_allowed : 1; + uint8_t arguments_allowed : 1; + uint8_t backtrace_barrier : 1; /* stop backtrace on this function */ + /* XXX: 5 bits available */ + uint8_t *byte_code_buf; /* (self pointer) */ + int byte_code_len; + JSAtom func_name; + JSVarDef *vardefs; /* arguments + local variables (arg_count + var_count) (self pointer) */ + JSClosureVar *closure_var; /* list of variables in the closure (self pointer) */ + uint16_t arg_count; + uint16_t var_count; + uint16_t defined_arg_count; /* for length function property */ + uint16_t stack_size; /* maximum stack size */ + JSContext *realm; /* function realm */ + JSValue *cpool; /* constant pool (self pointer) */ + int cpool_count; + int closure_var_count; + JSInlineCache *ic; + JSAtom filename; + int line_num; + int col_num; + int source_len; + int pc2line_len; + uint8_t *pc2line_buf; + char *source; +} JSFunctionBytecode; + +typedef struct JSBoundFunction { + JSValue func_obj; + JSValue this_val; + int argc; + JSValue argv[]; +} JSBoundFunction; + +typedef enum JSIteratorKindEnum { + JS_ITERATOR_KIND_KEY, + JS_ITERATOR_KIND_VALUE, + JS_ITERATOR_KIND_KEY_AND_VALUE, +} JSIteratorKindEnum; + +typedef enum JSIteratorHelperKindEnum { + JS_ITERATOR_HELPER_KIND_DROP, + JS_ITERATOR_HELPER_KIND_FILTER, + JS_ITERATOR_HELPER_KIND_FLAT_MAP, + JS_ITERATOR_HELPER_KIND_MAP, + JS_ITERATOR_HELPER_KIND_TAKE, +} JSIteratorHelperKindEnum; + +typedef struct JSForInIterator { + JSValue obj; + BOOL is_array; + uint32_t array_length; + uint32_t idx; +} JSForInIterator; + +typedef struct JSRegExp { + JSString *pattern; + JSString *bytecode; /* also contains the flags */ +} JSRegExp; + +typedef struct JSProxyData { + JSValue target; + JSValue handler; + uint8_t is_func; + uint8_t is_revoked; +} JSProxyData; + +typedef struct JSArrayBuffer { + int byte_length; /* 0 if detached */ + int max_byte_length; /* -1 if not resizable; >= byte_length otherwise */ + uint8_t detached; + uint8_t shared; /* if shared, the array buffer cannot be detached */ + uint8_t *data; /* NULL if detached */ + struct list_head array_list; + void *opaque; + JSFreeArrayBufferDataFunc *free_func; +} JSArrayBuffer; + +typedef struct JSTypedArray { + struct list_head link; /* link to arraybuffer */ + JSObject *obj; /* back pointer to the TypedArray/DataView object */ + JSObject *buffer; /* based array buffer */ + uint32_t offset; /* byte offset in the array buffer */ + uint32_t length; /* byte length in the array buffer */ + BOOL track_rab; /* auto-track length of backing array buffer */ +} JSTypedArray; + +typedef struct JSAsyncFunctionState { + JSValue this_val; /* 'this' generator argument */ + int argc; /* number of function arguments */ + BOOL throw_flag; /* used to throw an exception in JS_CallInternal() */ + JSStackFrame frame; +} JSAsyncFunctionState; + +/* XXX: could use an object instead to avoid the + JS_TAG_ASYNC_FUNCTION tag for the GC */ +typedef struct JSAsyncFunctionData { + JSGCObjectHeader header; /* must come first */ + JSValue resolving_funcs[2]; + BOOL is_active; /* true if the async function state is valid */ + JSAsyncFunctionState func_state; +} JSAsyncFunctionData; + +typedef struct JSReqModuleEntry { + JSAtom module_name; + JSModuleDef *module; /* used using resolution */ +} JSReqModuleEntry; + +typedef enum JSExportTypeEnum { + JS_EXPORT_TYPE_LOCAL, + JS_EXPORT_TYPE_INDIRECT, +} JSExportTypeEnum; + +typedef struct JSExportEntry { + union { + struct { + int var_idx; /* closure variable index */ + JSVarRef *var_ref; /* if != NULL, reference to the variable */ + } local; /* for local export */ + int req_module_idx; /* module for indirect export */ + } u; + JSExportTypeEnum export_type; + JSAtom local_name; /* '*' if export ns from. not used for local + export after compilation */ + JSAtom export_name; /* exported variable name */ +} JSExportEntry; + +typedef struct JSStarExportEntry { + int req_module_idx; /* in req_module_entries */ +} JSStarExportEntry; + +typedef struct JSImportEntry { + int var_idx; /* closure variable index */ + JSAtom import_name; + int req_module_idx; /* in req_module_entries */ +} JSImportEntry; + +typedef enum { + JS_MODULE_STATUS_UNLINKED, + JS_MODULE_STATUS_LINKING, + JS_MODULE_STATUS_LINKED, + JS_MODULE_STATUS_EVALUATING, + JS_MODULE_STATUS_EVALUATING_ASYNC, + JS_MODULE_STATUS_EVALUATED, +} JSModuleStatus; + +struct JSModuleDef { + JSRefCountHeader header; /* must come first, 32-bit */ + JSAtom module_name; + struct list_head link; + + JSReqModuleEntry *req_module_entries; + int req_module_entries_count; + int req_module_entries_size; + + JSExportEntry *export_entries; + int export_entries_count; + int export_entries_size; + + JSStarExportEntry *star_export_entries; + int star_export_entries_count; + int star_export_entries_size; + + JSImportEntry *import_entries; + int import_entries_count; + int import_entries_size; + + JSValue module_ns; + JSValue func_obj; /* only used for JS modules */ + JSModuleInitFunc *init_func; /* only used for C modules */ + BOOL has_tla : 8; /* true if func_obj contains await */ + BOOL resolved : 8; + BOOL func_created : 8; + JSModuleStatus status : 8; + /* temp use during js_module_link() & js_module_evaluate() */ + int dfs_index, dfs_ancestor_index; + JSModuleDef *stack_prev; + /* temp use during js_module_evaluate() */ + JSModuleDef **async_parent_modules; + int async_parent_modules_count; + int async_parent_modules_size; + int pending_async_dependencies; + BOOL async_evaluation; + int64_t async_evaluation_timestamp; + JSModuleDef *cycle_root; + JSValue promise; /* corresponds to spec field: capability */ + JSValue resolving_funcs[2]; /* corresponds to spec field: capability */ + /* true if evaluation yielded an exception. It is saved in + eval_exception */ + BOOL eval_has_exception : 8; + JSValue eval_exception; + JSValue meta_obj; /* for import.meta */ +}; + +typedef struct JSJobEntry { + struct list_head link; + JSContext *ctx; + JSJobFunc *job_func; + int argc; + JSValue argv[]; +} JSJobEntry; + +typedef struct JSProperty { + union { + JSValue value; /* JS_PROP_NORMAL */ + struct { /* JS_PROP_GETSET */ + JSObject *getter; /* NULL if undefined */ + JSObject *setter; /* NULL if undefined */ + } getset; + JSVarRef *var_ref; /* JS_PROP_VARREF */ + struct { /* JS_PROP_AUTOINIT */ + /* in order to use only 2 pointers, we compress the realm + and the init function pointer */ + uintptr_t realm_and_id; /* realm and init_id (JS_AUTOINIT_ID_x) + in the 2 low bits */ + void *opaque; + } init; + } u; +} JSProperty; + +#define JS_PROP_INITIAL_SIZE 2 +#define JS_PROP_INITIAL_HASH_SIZE 4 /* must be a power of two */ +#define JS_ARRAY_INITIAL_SIZE 2 + +typedef struct JSShapeProperty { + uint32_t hash_next : 26; /* 0 if last in list */ + uint32_t flags : 6; /* JS_PROP_XXX */ + JSAtom atom; /* JS_ATOM_NULL = free property entry */ +} JSShapeProperty; + +struct JSShape { + /* hash table of size hash_mask + 1 before the start of the + structure (see prop_hash_end()). */ + JSGCObjectHeader header; + /* true if the shape is inserted in the shape hash table. If not, + JSShape.hash is not valid */ + uint8_t is_hashed; + /* If true, the shape may have small array index properties 'n' with 0 + <= n <= 2^31-1. If false, the shape is guaranteed not to have + small array index properties */ + uint8_t has_small_array_index; + uint32_t hash; /* current hash value */ + uint32_t prop_hash_mask; + int prop_size; /* allocated properties */ + int prop_count; /* include deleted properties */ + int deleted_prop_count; + JSShape *shape_hash_next; /* in JSRuntime.shape_hash[h] list */ + JSObject *proto; + JSShapeProperty prop[]; /* prop_size elements */ +}; + +struct JSObject { + union { + JSGCObjectHeader header; + struct { + int __gc_ref_count; /* corresponds to header.ref_count */ + uint8_t __gc_mark; /* corresponds to header.mark/gc_obj_type */ + + uint8_t extensible : 1; + uint8_t free_mark : 1; /* only used when freeing objects with cycles */ + uint8_t is_exotic : 1; /* TRUE if object has exotic property handlers */ + uint8_t fast_array : 1; /* TRUE if u.array is used for get/put (for JS_CLASS_ARRAY, JS_CLASS_ARGUMENTS and typed arrays) */ + uint8_t is_constructor : 1; /* TRUE if object is a constructor function */ + uint8_t is_uncatchable_error : 1; /* if TRUE, error is not catchable */ + uint8_t tmp_mark : 1; /* used in JS_WriteObjectRec() */ + uint8_t is_HTMLDDA : 1; /* specific annex B IsHtmlDDA behavior */ + uint16_t class_id; /* see JS_CLASS_x */ + }; + }; + /* byte offsets: 16/24 */ + JSShape *shape; /* prototype and property names + flag */ + JSProperty *prop; /* array of properties */ + /* byte offsets: 24/40 */ + JSWeakRefRecord *first_weak_ref; + /* byte offsets: 28/48 */ + union { + void *opaque; + struct JSBoundFunction *bound_function; /* JS_CLASS_BOUND_FUNCTION */ + struct JSCFunctionDataRecord *c_function_data_record; /* JS_CLASS_C_FUNCTION_DATA */ + struct JSForInIterator *for_in_iterator; /* JS_CLASS_FOR_IN_ITERATOR */ + struct JSArrayBuffer *array_buffer; /* JS_CLASS_ARRAY_BUFFER, JS_CLASS_SHARED_ARRAY_BUFFER */ + struct JSTypedArray *typed_array; /* JS_CLASS_UINT8C_ARRAY..JS_CLASS_DATAVIEW */ + struct JSMapState *map_state; /* JS_CLASS_MAP..JS_CLASS_WEAKSET */ + struct JSMapIteratorData *map_iterator_data; /* JS_CLASS_MAP_ITERATOR, JS_CLASS_SET_ITERATOR */ + struct JSArrayIteratorData *array_iterator_data; /* JS_CLASS_ARRAY_ITERATOR, JS_CLASS_STRING_ITERATOR */ + struct JSRegExpStringIteratorData *regexp_string_iterator_data; /* JS_CLASS_REGEXP_STRING_ITERATOR */ + struct JSGeneratorData *generator_data; /* JS_CLASS_GENERATOR */ + struct JSIteratorHelperData *iterator_helper_data; /* JS_CLASS_ITERATOR_HELPER */ + struct JSIteratorWrapData *iterator_wrap_data; /* JS_CLASS_ITERATOR_WRAP */ + struct JSProxyData *proxy_data; /* JS_CLASS_PROXY */ + struct JSPromiseData *promise_data; /* JS_CLASS_PROMISE */ + struct JSPromiseFunctionData *promise_function_data; /* JS_CLASS_PROMISE_RESOLVE_FUNCTION, JS_CLASS_PROMISE_REJECT_FUNCTION */ + struct JSAsyncFunctionData *async_function_data; /* JS_CLASS_ASYNC_FUNCTION_RESOLVE, JS_CLASS_ASYNC_FUNCTION_REJECT */ + struct JSAsyncFromSyncIteratorData *async_from_sync_iterator_data; /* JS_CLASS_ASYNC_FROM_SYNC_ITERATOR */ + struct JSAsyncGeneratorData *async_generator_data; /* JS_CLASS_ASYNC_GENERATOR */ + struct { /* JS_CLASS_BYTECODE_FUNCTION: 12/24 bytes */ + /* also used by JS_CLASS_GENERATOR_FUNCTION, JS_CLASS_ASYNC_FUNCTION and JS_CLASS_ASYNC_GENERATOR_FUNCTION */ + struct JSFunctionBytecode *function_bytecode; + JSVarRef **var_refs; + JSObject *home_object; /* for 'super' access */ + } func; + struct { /* JS_CLASS_C_FUNCTION: 12/20 bytes */ + JSContext *realm; + JSCFunctionType c_function; + uint8_t length; + uint8_t cproto; + int16_t magic; + } cfunc; + /* array part for fast arrays and typed arrays */ + struct { /* JS_CLASS_ARRAY, JS_CLASS_ARGUMENTS, JS_CLASS_UINT8C_ARRAY..JS_CLASS_FLOAT64_ARRAY */ + union { + uint32_t size; /* JS_CLASS_ARRAY, JS_CLASS_ARGUMENTS */ + struct JSTypedArray *typed_array; /* JS_CLASS_UINT8C_ARRAY..JS_CLASS_FLOAT64_ARRAY */ + } u1; + union { + JSValue *values; /* JS_CLASS_ARRAY, JS_CLASS_ARGUMENTS */ + void *ptr; /* JS_CLASS_UINT8C_ARRAY..JS_CLASS_FLOAT64_ARRAY */ + int8_t *int8_ptr; /* JS_CLASS_INT8_ARRAY */ + uint8_t *uint8_ptr; /* JS_CLASS_UINT8_ARRAY, JS_CLASS_UINT8C_ARRAY */ + int16_t *int16_ptr; /* JS_CLASS_INT16_ARRAY */ + uint16_t *uint16_ptr; /* JS_CLASS_UINT16_ARRAY */ + int32_t *int32_ptr; /* JS_CLASS_INT32_ARRAY */ + uint32_t *uint32_ptr; /* JS_CLASS_UINT32_ARRAY */ + int64_t *int64_ptr; /* JS_CLASS_INT64_ARRAY */ + uint64_t *uint64_ptr; /* JS_CLASS_UINT64_ARRAY */ + uint16_t *fp16_ptr; /* JS_CLASS_FLOAT16_ARRAY */ + float *float_ptr; /* JS_CLASS_FLOAT32_ARRAY */ + double *double_ptr; /* JS_CLASS_FLOAT64_ARRAY */ + } u; + uint32_t count; /* <= 2^31-1. 0 for a detached typed array */ + } array; /* 12/20 bytes */ + JSRegExp regexp; /* JS_CLASS_REGEXP: 8/16 bytes */ + JSValue object_data; /* for JS_SetObjectData(): 8/16/16 bytes */ + } u; + /* byte sizes: 40/48/72 */ +}; + +typedef struct JSCallSiteData { + JSValue filename; + JSValue func; + JSValue func_name; + BOOL native; + int line_num; + int col_num; +} JSCallSiteData; + +enum { + __JS_ATOM_NULL = JS_ATOM_NULL, +#define DEF(name, str) JS_ATOM_ ## name, +#include "quickjs-atom.h" +#undef DEF + JS_ATOM_END, +}; +#define JS_ATOM_LAST_KEYWORD JS_ATOM_super +#define JS_ATOM_LAST_STRICT_KEYWORD JS_ATOM_yield + +static const char js_atom_init[] = +#define DEF(name, str) str "\0" +#include "quickjs-atom.h" +#undef DEF +; + +typedef enum OPCodeFormat { +#define FMT(f) OP_FMT_ ## f, +#define DEF(id, size, n_pop, n_push, f) +#include "quickjs-opcode.h" +#undef DEF +#undef FMT +} OPCodeFormat; + +typedef enum OPCodeEnum { +#define FMT(f) +#define DEF(id, size, n_pop, n_push, f) OP_ ## id, +#define def(id, size, n_pop, n_push, f) +#include "quickjs-opcode.h" +#undef def +#undef DEF +#undef FMT + OP_COUNT, /* excluding temporary opcodes */ + /* temporary opcodes : overlap with the short opcodes */ + OP_TEMP_START = OP_nop + 1, + OP___dummy = OP_TEMP_START - 1, +#define FMT(f) +#define DEF(id, size, n_pop, n_push, f) +#define def(id, size, n_pop, n_push, f) OP_ ## id, +#include "quickjs-opcode.h" +#undef def +#undef DEF +#undef FMT + OP_TEMP_END, +} OPCodeEnum; + +static int JS_InitAtoms(JSRuntime *rt); +static JSAtom __JS_NewAtomInit(JSRuntime *rt, const char *str, int len, + int atom_type); +static void JS_FreeAtomStruct(JSRuntime *rt, JSAtomStruct *p); +static void free_function_bytecode(JSRuntime *rt, JSFunctionBytecode *b); +static JSValue js_call_c_function(JSContext *ctx, JSValue func_obj, + JSValue this_obj, + int argc, JSValue *argv, int flags); +static JSValue js_call_bound_function(JSContext *ctx, JSValue func_obj, + JSValue this_obj, + int argc, JSValue *argv, int flags); +static JSValue JS_CallInternal(JSContext *ctx, JSValue func_obj, + JSValue this_obj, JSValue new_target, + int argc, JSValue *argv, int flags); +static JSValue JS_CallConstructorInternal(JSContext *ctx, + JSValue func_obj, + JSValue new_target, + int argc, JSValue *argv, int flags); +static JSValue JS_CallFree(JSContext *ctx, JSValue func_obj, JSValue this_obj, + int argc, JSValue *argv); +static JSValue JS_InvokeFree(JSContext *ctx, JSValue this_val, JSAtom atom, + int argc, JSValue *argv); +static __exception int JS_ToArrayLengthFree(JSContext *ctx, uint32_t *plen, + JSValue val, BOOL is_array_ctor); +static JSValue JS_EvalObject(JSContext *ctx, JSValue this_obj, + JSValue val, int flags, int scope_idx); +JSValue __attribute__((format(printf, 2, 3))) JS_ThrowInternalError(JSContext *ctx, const char *fmt, ...); + +static __maybe_unused void JS_DumpString(JSRuntime *rt, const JSString *p); +static __maybe_unused void JS_DumpObjectHeader(JSRuntime *rt); +static __maybe_unused void JS_DumpObject(JSRuntime *rt, JSObject *p); +static __maybe_unused void JS_DumpGCObject(JSRuntime *rt, JSGCObjectHeader *p); +static __maybe_unused void JS_DumpValue(JSRuntime *rt, JSValue val); +static __maybe_unused void JS_DumpAtoms(JSRuntime *rt); +static __maybe_unused void JS_DumpShapes(JSRuntime *rt); + +static JSValue js_function_apply(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv, int magic); +static void js_array_finalizer(JSRuntime *rt, JSValue val); +static void js_array_mark(JSRuntime *rt, JSValue val, + JS_MarkFunc *mark_func); +static void js_object_data_finalizer(JSRuntime *rt, JSValue val); +static void js_object_data_mark(JSRuntime *rt, JSValue val, + JS_MarkFunc *mark_func); +static void js_c_function_finalizer(JSRuntime *rt, JSValue val); +static void js_c_function_mark(JSRuntime *rt, JSValue val, + JS_MarkFunc *mark_func); +static void js_bytecode_function_finalizer(JSRuntime *rt, JSValue val); +static void js_bytecode_function_mark(JSRuntime *rt, JSValue val, + JS_MarkFunc *mark_func); +static void js_bound_function_finalizer(JSRuntime *rt, JSValue val); +static void js_bound_function_mark(JSRuntime *rt, JSValue val, + JS_MarkFunc *mark_func); +static void js_for_in_iterator_finalizer(JSRuntime *rt, JSValue val); +static void js_for_in_iterator_mark(JSRuntime *rt, JSValue val, + JS_MarkFunc *mark_func); +static void js_regexp_finalizer(JSRuntime *rt, JSValue val); +static void js_array_buffer_finalizer(JSRuntime *rt, JSValue val); +static void js_typed_array_finalizer(JSRuntime *rt, JSValue val); +static void js_typed_array_mark(JSRuntime *rt, JSValue val, + JS_MarkFunc *mark_func); +static void js_proxy_finalizer(JSRuntime *rt, JSValue val); +static void js_proxy_mark(JSRuntime *rt, JSValue val, + JS_MarkFunc *mark_func); +static void js_map_finalizer(JSRuntime *rt, JSValue val); +static void js_map_mark(JSRuntime *rt, JSValue val, + JS_MarkFunc *mark_func); +static void js_map_iterator_finalizer(JSRuntime *rt, JSValue val); +static void js_map_iterator_mark(JSRuntime *rt, JSValue val, + JS_MarkFunc *mark_func); +static void js_array_iterator_finalizer(JSRuntime *rt, JSValue val); +static void js_array_iterator_mark(JSRuntime *rt, JSValue val, + JS_MarkFunc *mark_func); +static void js_iterator_helper_finalizer(JSRuntime *rt, JSValue val); +static void js_iterator_helper_mark(JSRuntime *rt, JSValue val, + JS_MarkFunc *mark_func); +static void js_iterator_wrap_finalizer(JSRuntime *rt, JSValue val); +static void js_iterator_wrap_mark(JSRuntime *rt, JSValue val, + JS_MarkFunc *mark_func); +static void js_regexp_string_iterator_finalizer(JSRuntime *rt, JSValue val); +static void js_regexp_string_iterator_mark(JSRuntime *rt, JSValue val, + JS_MarkFunc *mark_func); +static void js_generator_finalizer(JSRuntime *rt, JSValue obj); +static void js_generator_mark(JSRuntime *rt, JSValue val, + JS_MarkFunc *mark_func); +static void js_promise_finalizer(JSRuntime *rt, JSValue val); +static void js_promise_mark(JSRuntime *rt, JSValue val, + JS_MarkFunc *mark_func); +static void js_promise_resolve_function_finalizer(JSRuntime *rt, JSValue val); +static void js_promise_resolve_function_mark(JSRuntime *rt, JSValue val, + JS_MarkFunc *mark_func); + +#define HINT_STRING 0 +#define HINT_NUMBER 1 +#define HINT_NONE 2 +#define HINT_FORCE_ORDINARY (1 << 4) // don't try Symbol.toPrimitive +static JSValue JS_ToPrimitiveFree(JSContext *ctx, JSValue val, int hint); +static JSValue JS_ToStringFree(JSContext *ctx, JSValue val); +static int JS_ToBoolFree(JSContext *ctx, JSValue val); +static int JS_ToInt32Free(JSContext *ctx, int32_t *pres, JSValue val); +static int JS_ToFloat64Free(JSContext *ctx, double *pres, JSValue val); +static int JS_ToUint8ClampFree(JSContext *ctx, int32_t *pres, JSValue val); +static JSValue js_new_string8_len(JSContext *ctx, const char *buf, int len); +static JSValue js_compile_regexp(JSContext *ctx, JSValue pattern, + JSValue flags); +static JSValue js_regexp_constructor_internal(JSContext *ctx, JSValue ctor, + JSValue pattern, JSValue bc); +static void gc_decref(JSRuntime *rt); +static int JS_NewClass1(JSRuntime *rt, JSClassID class_id, + const JSClassDef *class_def, JSAtom name); +static JSValue js_array_push(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv, int unshift); + +typedef enum JSStrictEqModeEnum { + JS_EQ_STRICT, + JS_EQ_SAME_VALUE, + JS_EQ_SAME_VALUE_ZERO, +} JSStrictEqModeEnum; + +static BOOL js_strict_eq2(JSContext *ctx, JSValue op1, JSValue op2, + JSStrictEqModeEnum eq_mode); +static BOOL js_strict_eq(JSContext *ctx, JSValue op1, JSValue op2); +static BOOL js_same_value(JSContext *ctx, JSValue op1, JSValue op2); +static BOOL js_same_value_zero(JSContext *ctx, JSValue op1, JSValue op2); +static JSValue JS_ToObject(JSContext *ctx, JSValue val); +static JSValue JS_ToObjectFree(JSContext *ctx, JSValue val); +static JSProperty *add_property(JSContext *ctx, + JSObject *p, JSAtom prop, int prop_flags); +static JSValue JS_NewBigInt(JSContext *ctx); +static inline bf_t *JS_GetBigInt(JSValue val) +{ + JSBigInt *p = JS_VALUE_GET_PTR(val); + return &p->num; +} +static JSValue JS_CompactBigInt1(JSContext *ctx, JSValue val); +static JSValue JS_CompactBigInt(JSContext *ctx, JSValue val); +static int JS_ToBigInt64Free(JSContext *ctx, int64_t *pres, JSValue val); +static bf_t *JS_ToBigInt(JSContext *ctx, bf_t *buf, JSValue val); +static bf_t *JS_ToBigInt1(JSContext *ctx, bf_t *buf, JSValue val); +static void JS_FreeBigInt(JSContext *ctx, bf_t *a, bf_t *buf); +JSValue JS_ThrowOutOfMemory(JSContext *ctx); +static JSValue JS_ThrowTypeErrorRevokedProxy(JSContext *ctx); +static JSValue js_proxy_getPrototypeOf(JSContext *ctx, JSValue obj); +static int js_proxy_setPrototypeOf(JSContext *ctx, JSValue obj, + JSValue proto_val, BOOL throw_flag); +static int js_proxy_isExtensible(JSContext *ctx, JSValue obj); +static int js_proxy_preventExtensions(JSContext *ctx, JSValue obj); +static int js_proxy_isArray(JSContext *ctx, JSValue obj); +static int JS_CreateProperty(JSContext *ctx, JSObject *p, + JSAtom prop, JSValue val, + JSValue getter, JSValue setter, + int flags); +static int js_string_memcmp(const JSString *p1, const JSString *p2, int len); +static void reset_weak_ref(JSRuntime *rt, JSWeakRefRecord **first_weak_ref); +static BOOL is_valid_weakref_target(JSValue val); +static void insert_weakref_record(JSValue target, struct JSWeakRefRecord *wr); +static JSValue js_array_buffer_constructor3(JSContext *ctx, + JSValue new_target, + uint64_t len, uint64_t *max_len, + JSClassID class_id, + uint8_t *buf, + JSFreeArrayBufferDataFunc *free_func, + void *opaque, BOOL alloc_flag); +static void js_array_buffer_free(JSRuntime *rt, void *opaque, void *ptr); +static JSArrayBuffer *js_get_array_buffer(JSContext *ctx, JSValue obj); +static BOOL array_buffer_is_resizable(const JSArrayBuffer *abuf); +static JSValue js_typed_array_constructor(JSContext *ctx, + JSValue this_val, + int argc, JSValue *argv, + int classid); +static JSValue js_typed_array_constructor_ta(JSContext *ctx, + JSValue new_target, + JSValue src_obj, + int classid, uint32_t len); +static BOOL typed_array_is_oob(JSObject *p); +static uint32_t typed_array_get_length(JSContext *ctx, JSObject *p); +static JSValue JS_ThrowTypeErrorDetachedArrayBuffer(JSContext *ctx); +static JSValue JS_ThrowTypeErrorArrayBufferOOB(JSContext *ctx); +static JSVarRef *get_var_ref(JSContext *ctx, JSStackFrame *sf, int var_idx, + BOOL is_arg); +static JSValue js_generator_function_call(JSContext *ctx, JSValue func_obj, + JSValue this_obj, + int argc, JSValue *argv, + int flags); +static void js_async_function_resolve_finalizer(JSRuntime *rt, JSValue val); +static void js_async_function_resolve_mark(JSRuntime *rt, JSValue val, + JS_MarkFunc *mark_func); +static JSValue JS_EvalInternal(JSContext *ctx, JSValue this_obj, + const char *input, size_t input_len, + const char *filename, int flags, int scope_idx); +static void js_free_module_def(JSContext *ctx, JSModuleDef *m); +static void js_mark_module_def(JSRuntime *rt, JSModuleDef *m, + JS_MarkFunc *mark_func); +static JSValue js_import_meta(JSContext *ctx); +static JSValue js_dynamic_import(JSContext *ctx, JSValue specifier); +static void free_var_ref(JSRuntime *rt, JSVarRef *var_ref); +static JSValue js_new_promise_capability(JSContext *ctx, + JSValue *resolving_funcs, + JSValue ctor); +static __exception int perform_promise_then(JSContext *ctx, + JSValue promise, + JSValue *resolve_reject, + JSValue *cap_resolving_funcs); +static JSValue js_promise_resolve(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv, int magic); +static JSValue js_promise_then(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv); +static int js_string_compare(JSContext *ctx, + const JSString *p1, const JSString *p2); +static JSValue JS_ToNumber(JSContext *ctx, JSValue val); +static int JS_SetPropertyValue(JSContext *ctx, JSValue this_obj, + JSValue prop, JSValue val, int flags); +static int JS_NumberIsInteger(JSContext *ctx, JSValue val); +static BOOL JS_NumberIsNegativeOrMinusZero(JSContext *ctx, JSValue val); +static JSValue JS_ToNumberFree(JSContext *ctx, JSValue val); +static int JS_GetOwnPropertyInternal(JSContext *ctx, JSPropertyDescriptor *desc, + JSObject *p, JSAtom prop); +static void js_free_desc(JSContext *ctx, JSPropertyDescriptor *desc); +static void async_func_mark(JSRuntime *rt, JSAsyncFunctionState *s, + JS_MarkFunc *mark_func); +static void JS_AddIntrinsicBasicObjects(JSContext *ctx); +static void js_free_shape(JSRuntime *rt, JSShape *sh); +static void js_free_shape_null(JSRuntime *rt, JSShape *sh); +static int js_shape_prepare_update(JSContext *ctx, JSObject *p, + JSShapeProperty **pprs); +static int init_shape_hash(JSRuntime *rt); +static __exception int js_get_length32(JSContext *ctx, uint32_t *pres, + JSValue obj); +static __exception int js_get_length64(JSContext *ctx, int64_t *pres, + JSValue obj); +static __exception int js_set_length64(JSContext *ctx, JSValue obj, + int64_t len); +static void free_arg_list(JSContext *ctx, JSValue *tab, uint32_t len); +static JSValue *build_arg_list(JSContext *ctx, uint32_t *plen, + JSValue array_arg); +static BOOL js_get_fast_array(JSContext *ctx, JSValue obj, + JSValue **arrpp, uint32_t *countp); +static JSValue JS_CreateAsyncFromSyncIterator(JSContext *ctx, + JSValue sync_iter); +static void js_c_function_data_finalizer(JSRuntime *rt, JSValue val); +static void js_c_function_data_mark(JSRuntime *rt, JSValue val, + JS_MarkFunc *mark_func); +static JSValue js_c_function_data_call(JSContext *ctx, JSValue func_obj, + JSValue this_val, + int argc, JSValue *argv, int flags); +static JSAtom js_symbol_to_atom(JSContext *ctx, JSValue val); +static void add_gc_object(JSRuntime *rt, JSGCObjectHeader *h, + JSGCObjectTypeEnum type); +static void remove_gc_object(JSGCObjectHeader *h); +static void js_async_function_free0(JSRuntime *rt, JSAsyncFunctionData *s); +static JSValue js_instantiate_prototype(JSContext *ctx, JSObject *p, JSAtom atom, void *opaque); +static JSValue js_module_ns_autoinit(JSContext *ctx, JSObject *p, JSAtom atom, + void *opaque); +static JSValue JS_InstantiateFunctionListItem2(JSContext *ctx, JSObject *p, + JSAtom atom, void *opaque); +void JS_SetUncatchableError(JSContext *ctx, JSValue val, BOOL flag); + +static JSValue js_new_callsite(JSContext *ctx, JSCallSiteData *csd); +static void js_new_callsite_data(JSContext *ctx, JSCallSiteData *csd, JSStackFrame *sf); +static void js_new_callsite_data2(JSContext *ctx, JSCallSiteData *csd, const char *filename, int line_num, int col_num); +static void _JS_AddIntrinsicCallSite(JSContext *ctx); + +static void JS_SetOpaqueInternal(JSValue obj, void *opaque); + +static const JSClassExoticMethods js_arguments_exotic_methods; +static const JSClassExoticMethods js_string_exotic_methods; +static const JSClassExoticMethods js_proxy_exotic_methods; +static const JSClassExoticMethods js_module_ns_exotic_methods; + +static inline BOOL double_is_int32(double d) +{ + uint64_t u, e; + JSFloat64Union t; + + t.d = d; + u = t.u64; + + e = ((u >> 52) & 0x7FF) - 1023; + if (e > 30) { + // accept 0, INT32_MIN, reject too large, too small, nan, inf, -0 + return !u || (u == 0xc1e0000000000000); + } else { + // shift out sign, exponent and whole part bits + // value is fractional if remaining low bits are non-zero + return !(u << 12 << e); + } +} + +static JSValue js_float64(double d) +{ + return __JS_NewFloat64(d); +} + +static int compare_u32(uint32_t a, uint32_t b) +{ + return -(a < b) + (b < a); // -1, 0 or 1 +} + +static JSValue js_int32(int32_t v) +{ + return JS_MKVAL(JS_TAG_INT, v); +} + +static JSValue js_uint32(uint32_t v) +{ + if (v <= INT32_MAX) + return js_int32(v); + else + return js_float64(v); +} + +static JSValue js_int64(int64_t v) +{ + if (v >= INT32_MIN && v <= INT32_MAX) + return js_int32(v); + else + return js_float64(v); +} + +#define JS_NewInt64(ctx, val) js_int64(val) + +static JSValue js_number(double d) +{ + if (double_is_int32(d)) + return js_int32((int32_t)d); + else + return js_float64(d); +} + +JSValue JS_NewNumber(JSContext *ctx, double d) +{ + return js_number(d); +} + +static JSValue js_bool(JS_BOOL v) +{ + return JS_MKVAL(JS_TAG_BOOL, (v != 0)); +} + +static JSValue js_dup(JSValue v) +{ + if (JS_VALUE_HAS_REF_COUNT(v)) { + JSRefCountHeader *p = (JSRefCountHeader *)JS_VALUE_GET_PTR(v); + p->ref_count++; + } + return v; +} + +JSValue JS_DupValue(JSContext *ctx, JSValue v) +{ + return js_dup(v); +} + +JSValue JS_DupValueRT(JSRuntime *rt, JSValue v) +{ + return js_dup(v); +} + +static void js_trigger_gc(JSRuntime *rt, size_t size) +{ + BOOL force_gc; +#ifdef FORCE_GC_AT_MALLOC + force_gc = TRUE; +#else + force_gc = ((rt->malloc_state.malloc_size + size) > + rt->malloc_gc_threshold); +#endif + if (force_gc) { +#ifdef DUMP_GC + if (check_dump_flag(rt, DUMP_GC)) { + printf("GC: size=%" PRIu64 "\n", + (uint64_t)rt->malloc_state.malloc_size); + } +#endif + JS_RunGC(rt); + rt->malloc_gc_threshold = rt->malloc_state.malloc_size + + (rt->malloc_state.malloc_size >> 1); + } +} + +static size_t js_malloc_usable_size_unknown(const void *ptr) +{ + return 0; +} + +void *js_calloc_rt(JSRuntime *rt, size_t count, size_t size) +{ + void *ptr; + JSMallocState *s; + + /* Do not allocate zero bytes: behavior is platform dependent */ + assert(count != 0 && size != 0); + + if (size > 0) + if (unlikely(count != (count * size) / size)) + return NULL; + + s = &rt->malloc_state; + /* When malloc_limit is 0 (unlimited), malloc_limit - 1 will be SIZE_MAX. */ + if (unlikely(s->malloc_size + (count * size) > s->malloc_limit - 1)) + return NULL; + + ptr = rt->mf.js_calloc(s->opaque, count, size); + if (!ptr) + return NULL; + + s->malloc_count++; + s->malloc_size += rt->mf.js_malloc_usable_size(ptr) + MALLOC_OVERHEAD; + return ptr; +} + +void *js_malloc_rt(JSRuntime *rt, size_t size) +{ + void *ptr; + JSMallocState *s; + + /* Do not allocate zero bytes: behavior is platform dependent */ + assert(size != 0); + + s = &rt->malloc_state; + /* When malloc_limit is 0 (unlimited), malloc_limit - 1 will be SIZE_MAX. */ + if (unlikely(s->malloc_size + size > s->malloc_limit - 1)) + return NULL; + + ptr = rt->mf.js_malloc(s->opaque, size); + if (!ptr) + return NULL; + + s->malloc_count++; + s->malloc_size += rt->mf.js_malloc_usable_size(ptr) + MALLOC_OVERHEAD; + return ptr; +} + +void js_free_rt(JSRuntime *rt, void *ptr) +{ + JSMallocState *s; + + if (!ptr) + return; + + s = &rt->malloc_state; + s->malloc_count--; + s->malloc_size -= rt->mf.js_malloc_usable_size(ptr) + MALLOC_OVERHEAD; + rt->mf.js_free(s->opaque, ptr); +} + +void *js_realloc_rt(JSRuntime *rt, void *ptr, size_t size) +{ + size_t old_size; + JSMallocState *s; + + if (!ptr) { + if (size == 0) + return NULL; + return js_malloc_rt(rt, size); + } + if (unlikely(size == 0)) { + js_free_rt(rt, ptr); + return NULL; + } + old_size = rt->mf.js_malloc_usable_size(ptr); + s = &rt->malloc_state; + /* When malloc_limit is 0 (unlimited), malloc_limit - 1 will be SIZE_MAX. */ + if (s->malloc_size + size - old_size > s->malloc_limit - 1) + return NULL; + + ptr = rt->mf.js_realloc(s->opaque, ptr, size); + if (!ptr) + return NULL; + + s->malloc_size += rt->mf.js_malloc_usable_size(ptr) - old_size; + return ptr; +} + +static void *js_dbuf_realloc(void *opaque, void *ptr, size_t size) +{ + JSRuntime *rt = opaque; + return js_realloc_rt(rt, ptr, size); +} + +size_t js_malloc_usable_size_rt(JSRuntime *rt, const void *ptr) +{ + return rt->mf.js_malloc_usable_size(ptr); +} + +/** + * This used to be implemented as malloc + memset, but using calloc + * yields better performance in initial, bursty allocations, something useful + * for QuickJS. + * + * More information: https://github.com/quickjs-ng/quickjs/pull/519 + */ +void *js_mallocz_rt(JSRuntime *rt, size_t size) +{ + return js_calloc_rt(rt, 1, size); +} + +/* called by libbf */ +static void *js_bf_realloc(void *opaque, void *ptr, size_t size) +{ + JSRuntime *rt = opaque; + return js_realloc_rt(rt, ptr, size); +} + +/* Throw out of memory in case of error */ +void *js_calloc(JSContext *ctx, size_t count, size_t size) +{ + void *ptr; + ptr = js_calloc_rt(ctx->rt, count, size); + if (unlikely(!ptr)) { + JS_ThrowOutOfMemory(ctx); + return NULL; + } + return ptr; +} + +/* Throw out of memory in case of error */ +void *js_malloc(JSContext *ctx, size_t size) +{ + void *ptr; + ptr = js_malloc_rt(ctx->rt, size); + if (unlikely(!ptr)) { + JS_ThrowOutOfMemory(ctx); + return NULL; + } + return ptr; +} + +/* Throw out of memory in case of error */ +void *js_mallocz(JSContext *ctx, size_t size) +{ + void *ptr; + ptr = js_mallocz_rt(ctx->rt, size); + if (unlikely(!ptr)) { + JS_ThrowOutOfMemory(ctx); + return NULL; + } + return ptr; +} + +void js_free(JSContext *ctx, void *ptr) +{ + js_free_rt(ctx->rt, ptr); +} + +/* Throw out of memory in case of error */ +void *js_realloc(JSContext *ctx, void *ptr, size_t size) +{ + void *ret; + ret = js_realloc_rt(ctx->rt, ptr, size); + if (unlikely(!ret && size != 0)) { + JS_ThrowOutOfMemory(ctx); + return NULL; + } + return ret; +} + +/* store extra allocated size in *pslack if successful */ +void *js_realloc2(JSContext *ctx, void *ptr, size_t size, size_t *pslack) +{ + void *ret; + ret = js_realloc_rt(ctx->rt, ptr, size); + if (unlikely(!ret && size != 0)) { + JS_ThrowOutOfMemory(ctx); + return NULL; + } + if (pslack) { + size_t new_size = js_malloc_usable_size_rt(ctx->rt, ret); + *pslack = (new_size > size) ? new_size - size : 0; + } + return ret; +} + +size_t js_malloc_usable_size(JSContext *ctx, const void *ptr) +{ + return js_malloc_usable_size_rt(ctx->rt, ptr); +} + +/* Throw out of memory exception in case of error */ +char *js_strndup(JSContext *ctx, const char *s, size_t n) +{ + char *ptr; + ptr = js_malloc(ctx, n + 1); + if (ptr) { + memcpy(ptr, s, n); + ptr[n] = '\0'; + } + return ptr; +} + +char *js_strdup(JSContext *ctx, const char *str) +{ + return js_strndup(ctx, str, strlen(str)); +} + +static no_inline int js_realloc_array(JSContext *ctx, void **parray, + int elem_size, int *psize, int req_size) +{ + int new_size; + size_t slack; + void *new_array; + /* XXX: potential arithmetic overflow */ + new_size = max_int(req_size, *psize * 3 / 2); + new_array = js_realloc2(ctx, *parray, new_size * elem_size, &slack); + if (!new_array) + return -1; + new_size += slack / elem_size; + *psize = new_size; + *parray = new_array; + return 0; +} + +/* resize the array and update its size if req_size > *psize */ +static inline int js_resize_array(JSContext *ctx, void **parray, int elem_size, + int *psize, int req_size) +{ + if (unlikely(req_size > *psize)) + return js_realloc_array(ctx, parray, elem_size, psize, req_size); + else + return 0; +} + +static inline void js_dbuf_init(JSContext *ctx, DynBuf *s) +{ + dbuf_init2(s, ctx->rt, js_dbuf_realloc); +} + +static inline int is_digit(int c) { + return c >= '0' && c <= '9'; +} + +static inline int string_get(const JSString *p, int idx) { + return p->is_wide_char ? p->u.str16[idx] : p->u.str8[idx]; +} + +typedef struct JSClassShortDef { + JSAtom class_name; + JSClassFinalizer *finalizer; + JSClassGCMark *gc_mark; +} JSClassShortDef; + +static JSClassShortDef const js_std_class_def[] = { + { JS_ATOM_Object, NULL, NULL }, /* JS_CLASS_OBJECT */ + { JS_ATOM_Array, js_array_finalizer, js_array_mark }, /* JS_CLASS_ARRAY */ + { JS_ATOM_Error, NULL, NULL }, /* JS_CLASS_ERROR */ + { JS_ATOM_Number, js_object_data_finalizer, js_object_data_mark }, /* JS_CLASS_NUMBER */ + { JS_ATOM_String, js_object_data_finalizer, js_object_data_mark }, /* JS_CLASS_STRING */ + { JS_ATOM_Boolean, js_object_data_finalizer, js_object_data_mark }, /* JS_CLASS_BOOLEAN */ + { JS_ATOM_Symbol, js_object_data_finalizer, js_object_data_mark }, /* JS_CLASS_SYMBOL */ + { JS_ATOM_Arguments, js_array_finalizer, js_array_mark }, /* JS_CLASS_ARGUMENTS */ + { JS_ATOM_Arguments, NULL, NULL }, /* JS_CLASS_MAPPED_ARGUMENTS */ + { JS_ATOM_Date, js_object_data_finalizer, js_object_data_mark }, /* JS_CLASS_DATE */ + { JS_ATOM_Object, NULL, NULL }, /* JS_CLASS_MODULE_NS */ + { JS_ATOM_Function, js_c_function_finalizer, js_c_function_mark }, /* JS_CLASS_C_FUNCTION */ + { JS_ATOM_Function, js_bytecode_function_finalizer, js_bytecode_function_mark }, /* JS_CLASS_BYTECODE_FUNCTION */ + { JS_ATOM_Function, js_bound_function_finalizer, js_bound_function_mark }, /* JS_CLASS_BOUND_FUNCTION */ + { JS_ATOM_Function, js_c_function_data_finalizer, js_c_function_data_mark }, /* JS_CLASS_C_FUNCTION_DATA */ + { JS_ATOM_GeneratorFunction, js_bytecode_function_finalizer, js_bytecode_function_mark }, /* JS_CLASS_GENERATOR_FUNCTION */ + { JS_ATOM_ForInIterator, js_for_in_iterator_finalizer, js_for_in_iterator_mark }, /* JS_CLASS_FOR_IN_ITERATOR */ + { JS_ATOM_RegExp, js_regexp_finalizer, NULL }, /* JS_CLASS_REGEXP */ + { JS_ATOM_ArrayBuffer, js_array_buffer_finalizer, NULL }, /* JS_CLASS_ARRAY_BUFFER */ + { JS_ATOM_SharedArrayBuffer, js_array_buffer_finalizer, NULL }, /* JS_CLASS_SHARED_ARRAY_BUFFER */ + { JS_ATOM_Uint8ClampedArray, js_typed_array_finalizer, js_typed_array_mark }, /* JS_CLASS_UINT8C_ARRAY */ + { JS_ATOM_Int8Array, js_typed_array_finalizer, js_typed_array_mark }, /* JS_CLASS_INT8_ARRAY */ + { JS_ATOM_Uint8Array, js_typed_array_finalizer, js_typed_array_mark }, /* JS_CLASS_UINT8_ARRAY */ + { JS_ATOM_Int16Array, js_typed_array_finalizer, js_typed_array_mark }, /* JS_CLASS_INT16_ARRAY */ + { JS_ATOM_Uint16Array, js_typed_array_finalizer, js_typed_array_mark }, /* JS_CLASS_UINT16_ARRAY */ + { JS_ATOM_Int32Array, js_typed_array_finalizer, js_typed_array_mark }, /* JS_CLASS_INT32_ARRAY */ + { JS_ATOM_Uint32Array, js_typed_array_finalizer, js_typed_array_mark }, /* JS_CLASS_UINT32_ARRAY */ + { JS_ATOM_BigInt64Array, js_typed_array_finalizer, js_typed_array_mark }, /* JS_CLASS_BIG_INT64_ARRAY */ + { JS_ATOM_BigUint64Array, js_typed_array_finalizer, js_typed_array_mark }, /* JS_CLASS_BIG_UINT64_ARRAY */ + { JS_ATOM_Float16Array, js_typed_array_finalizer, js_typed_array_mark }, /* JS_CLASS_FLOAT16_ARRAY */ + { JS_ATOM_Float32Array, js_typed_array_finalizer, js_typed_array_mark }, /* JS_CLASS_FLOAT32_ARRAY */ + { JS_ATOM_Float64Array, js_typed_array_finalizer, js_typed_array_mark }, /* JS_CLASS_FLOAT64_ARRAY */ + { JS_ATOM_DataView, js_typed_array_finalizer, js_typed_array_mark }, /* JS_CLASS_DATAVIEW */ + { JS_ATOM_BigInt, js_object_data_finalizer, js_object_data_mark }, /* JS_CLASS_BIG_INT */ + { JS_ATOM_Map, js_map_finalizer, js_map_mark }, /* JS_CLASS_MAP */ + { JS_ATOM_Set, js_map_finalizer, js_map_mark }, /* JS_CLASS_SET */ + { JS_ATOM_WeakMap, js_map_finalizer, js_map_mark }, /* JS_CLASS_WEAKMAP */ + { JS_ATOM_WeakSet, js_map_finalizer, js_map_mark }, /* JS_CLASS_WEAKSET */ + { JS_ATOM_Iterator, NULL, NULL }, /* JS_CLASS_ITERATOR */ + { JS_ATOM_IteratorHelper, js_iterator_helper_finalizer, js_iterator_helper_mark }, /* JS_CLASS_ITERATOR_HELPER */ + { JS_ATOM_IteratorWrap, js_iterator_wrap_finalizer, js_iterator_wrap_mark }, /* JS_CLASS_ITERATOR_WRAP */ + { JS_ATOM_Map_Iterator, js_map_iterator_finalizer, js_map_iterator_mark }, /* JS_CLASS_MAP_ITERATOR */ + { JS_ATOM_Set_Iterator, js_map_iterator_finalizer, js_map_iterator_mark }, /* JS_CLASS_SET_ITERATOR */ + { JS_ATOM_Array_Iterator, js_array_iterator_finalizer, js_array_iterator_mark }, /* JS_CLASS_ARRAY_ITERATOR */ + { JS_ATOM_String_Iterator, js_array_iterator_finalizer, js_array_iterator_mark }, /* JS_CLASS_STRING_ITERATOR */ + { JS_ATOM_RegExp_String_Iterator, js_regexp_string_iterator_finalizer, js_regexp_string_iterator_mark }, /* JS_CLASS_REGEXP_STRING_ITERATOR */ + { JS_ATOM_Generator, js_generator_finalizer, js_generator_mark }, /* JS_CLASS_GENERATOR */ +}; + +static int init_class_range(JSRuntime *rt, JSClassShortDef const *tab, + int start, int count) +{ + JSClassDef cm_s, *cm = &cm_s; + int i, class_id; + + for(i = 0; i < count; i++) { + class_id = i + start; + memset(cm, 0, sizeof(*cm)); + cm->finalizer = tab[i].finalizer; + cm->gc_mark = tab[i].gc_mark; + if (JS_NewClass1(rt, class_id, cm, tab[i].class_name) < 0) + return -1; + } + return 0; +} + +/* Note: OS and CPU dependent */ +static inline uintptr_t js_get_stack_pointer(void) +{ + return (uintptr_t)__builtin_frame_address(0); +} + +static inline BOOL js_check_stack_overflow(JSRuntime *rt, size_t alloca_size) +{ + uintptr_t sp; + sp = js_get_stack_pointer() - alloca_size; + return unlikely(sp < rt->stack_limit); +} + +JSRuntime *JS_NewRuntime2(const JSMallocFunctions *mf, void *opaque) +{ + JSRuntime *rt; + JSMallocState ms; + + memset(&ms, 0, sizeof(ms)); + ms.opaque = opaque; + ms.malloc_limit = 0; + + rt = mf->js_calloc(opaque, 1, sizeof(JSRuntime)); + if (!rt) + return NULL; + rt->mf = *mf; + if (!rt->mf.js_malloc_usable_size) { + /* use dummy function if none provided */ + rt->mf.js_malloc_usable_size = js_malloc_usable_size_unknown; + } + /* Inline what js_malloc_rt does since we cannot use it here. */ + ms.malloc_count++; + ms.malloc_size += rt->mf.js_malloc_usable_size(rt) + MALLOC_OVERHEAD; + rt->malloc_state = ms; + rt->malloc_gc_threshold = 256 * 1024; + + bf_context_init(&rt->bf_ctx, js_bf_realloc, rt); + + init_list_head(&rt->context_list); + init_list_head(&rt->gc_obj_list); + init_list_head(&rt->gc_zero_ref_count_list); + rt->gc_phase = JS_GC_PHASE_NONE; + +#ifdef DUMP_LEAKS + init_list_head(&rt->string_list); +#endif + init_list_head(&rt->job_list); + + if (JS_InitAtoms(rt)) + goto fail; + + /* create the object, array and function classes */ + if (init_class_range(rt, js_std_class_def, JS_CLASS_OBJECT, + countof(js_std_class_def)) < 0) + goto fail; + rt->class_array[JS_CLASS_ARGUMENTS].exotic = &js_arguments_exotic_methods; + rt->class_array[JS_CLASS_STRING].exotic = &js_string_exotic_methods; + rt->class_array[JS_CLASS_MODULE_NS].exotic = &js_module_ns_exotic_methods; + + rt->class_array[JS_CLASS_C_FUNCTION].call = js_call_c_function; + rt->class_array[JS_CLASS_C_FUNCTION_DATA].call = js_c_function_data_call; + rt->class_array[JS_CLASS_BOUND_FUNCTION].call = js_call_bound_function; + rt->class_array[JS_CLASS_GENERATOR_FUNCTION].call = js_generator_function_call; + if (init_shape_hash(rt)) + goto fail; + + rt->js_class_id_alloc = JS_CLASS_INIT_COUNT; + + rt->stack_size = JS_DEFAULT_STACK_SIZE; +#ifdef __ASAN__ + rt->stack_size *= 2; // stack frames are bigger under AddressSanitizer +#endif + JS_UpdateStackTop(rt); + + rt->current_exception = JS_UNINITIALIZED; + + return rt; + fail: + JS_FreeRuntime(rt); + return NULL; +} + +void *JS_GetRuntimeOpaque(JSRuntime *rt) +{ + return rt->user_opaque; +} + +void JS_SetRuntimeOpaque(JSRuntime *rt, void *opaque) +{ + rt->user_opaque = opaque; +} + +int JS_AddRuntimeFinalizer(JSRuntime *rt, JSRuntimeFinalizer *finalizer, + void *arg) +{ + JSRuntimeFinalizerState *fs = js_malloc_rt(rt, sizeof(*fs)); + if (!fs) + return -1; + fs->next = rt->finalizers; + fs->finalizer = finalizer; + fs->arg = arg; + rt->finalizers = fs; + return 0; +} + +static void *js_def_calloc(void *opaque, size_t count, size_t size) +{ + return calloc(count, size); +} + +static void *js_def_malloc(void *opaque, size_t size) +{ + return malloc(size); +} + +static void js_def_free(void *opaque, void *ptr) +{ + free(ptr); +} + +static void *js_def_realloc(void *opaque, void *ptr, size_t size) +{ + return realloc(ptr, size); +} + +static const JSMallocFunctions def_malloc_funcs = { + js_def_calloc, + js_def_malloc, + js_def_free, + js_def_realloc, + js__malloc_usable_size +}; + +JSRuntime *JS_NewRuntime(void) +{ + return JS_NewRuntime2(&def_malloc_funcs, NULL); +} + +void JS_SetMemoryLimit(JSRuntime *rt, size_t limit) +{ + rt->malloc_state.malloc_limit = limit; +} + +void JS_SetDumpFlags(JSRuntime *rt, uint64_t flags) +{ + rt->dump_flags = flags; +} + +size_t JS_GetGCThreshold(JSRuntime *rt) { + return rt->malloc_gc_threshold; +} + +/* use -1 to disable automatic GC */ +void JS_SetGCThreshold(JSRuntime *rt, size_t gc_threshold) +{ + rt->malloc_gc_threshold = gc_threshold; +} + +#define malloc(s) malloc_is_forbidden(s) +#define free(p) free_is_forbidden(p) +#define realloc(p,s) realloc_is_forbidden(p,s) + +void JS_SetInterruptHandler(JSRuntime *rt, JSInterruptHandler *cb, void *opaque) +{ + rt->interrupt_handler = cb; + rt->interrupt_opaque = opaque; +} + +void JS_SetCanBlock(JSRuntime *rt, BOOL can_block) +{ + rt->can_block = can_block; +} + +void JS_SetSharedArrayBufferFunctions(JSRuntime *rt, + const JSSharedArrayBufferFunctions *sf) +{ + rt->sab_funcs = *sf; +} + +/* return 0 if OK, < 0 if exception */ +int JS_EnqueueJob(JSContext *ctx, JSJobFunc *job_func, + int argc, JSValue *argv) +{ + JSRuntime *rt = ctx->rt; + JSJobEntry *e; + int i; + + assert(!rt->in_free); + + e = js_malloc(ctx, sizeof(*e) + argc * sizeof(JSValue)); + if (!e) + return -1; + e->ctx = ctx; + e->job_func = job_func; + e->argc = argc; + for(i = 0; i < argc; i++) { + e->argv[i] = js_dup(argv[i]); + } + list_add_tail(&e->link, &rt->job_list); + return 0; +} + +BOOL JS_IsJobPending(JSRuntime *rt) +{ + return !list_empty(&rt->job_list); +} + +/* return < 0 if exception, 0 if no job pending, 1 if a job was + executed successfully. the context of the job is stored in '*pctx' */ +int JS_ExecutePendingJob(JSRuntime *rt, JSContext **pctx) +{ + JSContext *ctx; + JSJobEntry *e; + JSValue res; + int i, ret; + + if (list_empty(&rt->job_list)) { + *pctx = NULL; + return 0; + } + + /* get the first pending job and execute it */ + e = list_entry(rt->job_list.next, JSJobEntry, link); + list_del(&e->link); + ctx = e->ctx; + res = e->job_func(e->ctx, e->argc, e->argv); + for(i = 0; i < e->argc; i++) + JS_FreeValue(ctx, e->argv[i]); + if (JS_IsException(res)) + ret = -1; + else + ret = 1; + JS_FreeValue(ctx, res); + js_free(ctx, e); + *pctx = ctx; + return ret; +} + +static inline uint32_t atom_get_free(const JSAtomStruct *p) +{ + return (uintptr_t)p >> 1; +} + +static inline BOOL atom_is_free(const JSAtomStruct *p) +{ + return (uintptr_t)p & 1; +} + +static inline JSAtomStruct *atom_set_free(uint32_t v) +{ + return (JSAtomStruct *)(((uintptr_t)v << 1) | 1); +} + +/* Note: the string contents are uninitialized */ +static JSString *js_alloc_string_rt(JSRuntime *rt, int max_len, int is_wide_char) +{ + JSString *str; + str = js_malloc_rt(rt, sizeof(JSString) + (max_len << is_wide_char) + 1 - is_wide_char); + if (unlikely(!str)) + return NULL; + str->header.ref_count = 1; + str->is_wide_char = is_wide_char; + str->len = max_len; + str->atom_type = 0; + str->hash = 0; /* optional but costless */ + str->hash_next = 0; /* optional */ +#ifdef DUMP_LEAKS + list_add_tail(&str->link, &rt->string_list); +#endif + return str; +} + +static JSString *js_alloc_string(JSContext *ctx, int max_len, int is_wide_char) +{ + JSString *p; + p = js_alloc_string_rt(ctx->rt, max_len, is_wide_char); + if (unlikely(!p)) { + JS_ThrowOutOfMemory(ctx); + return NULL; + } + return p; +} + +/* same as JS_FreeValueRT() but faster */ +static inline void js_free_string(JSRuntime *rt, JSString *str) +{ + if (--str->header.ref_count <= 0) { + if (str->atom_type) { + JS_FreeAtomStruct(rt, str); + } else { +#ifdef DUMP_LEAKS + list_del(&str->link); +#endif + js_free_rt(rt, str); + } + } +} + +void JS_SetRuntimeInfo(JSRuntime *rt, const char *s) +{ + if (rt) + rt->rt_info = s; +} + +void JS_FreeRuntime(JSRuntime *rt) +{ + struct list_head *el, *el1; + int i; + + rt->in_free = TRUE; + JS_FreeValueRT(rt, rt->current_exception); + + list_for_each_safe(el, el1, &rt->job_list) { + JSJobEntry *e = list_entry(el, JSJobEntry, link); + for(i = 0; i < e->argc; i++) + JS_FreeValueRT(rt, e->argv[i]); + js_free_rt(rt, e); + } + init_list_head(&rt->job_list); + + JS_RunGC(rt); + +#ifdef DUMP_LEAKS + /* leaking objects */ + if (check_dump_flag(rt, DUMP_LEAKS)) { + BOOL header_done; + JSGCObjectHeader *p; + int count; + + /* remove the internal refcounts to display only the object + referenced externally */ + list_for_each(el, &rt->gc_obj_list) { + p = list_entry(el, JSGCObjectHeader, link); + p->mark = 0; + } + gc_decref(rt); + + header_done = FALSE; + list_for_each(el, &rt->gc_obj_list) { + p = list_entry(el, JSGCObjectHeader, link); + if (p->ref_count != 0) { + if (!header_done) { + printf("Object leaks:\n"); + JS_DumpObjectHeader(rt); + header_done = TRUE; + } + JS_DumpGCObject(rt, p); + } + } + + count = 0; + list_for_each(el, &rt->gc_obj_list) { + p = list_entry(el, JSGCObjectHeader, link); + if (p->ref_count == 0) { + count++; + } + } + if (count != 0) + printf("Secondary object leaks: %d\n", count); + } +#endif + + assert(list_empty(&rt->gc_obj_list)); + + /* free the classes */ + for(i = 0; i < rt->class_count; i++) { + JSClass *cl = &rt->class_array[i]; + if (cl->class_id != 0) { + JS_FreeAtomRT(rt, cl->class_name); + } + } + js_free_rt(rt, rt->class_array); + + bf_context_end(&rt->bf_ctx); + +#ifdef DUMP_ATOM_LEAKS + /* only the atoms defined in JS_InitAtoms() should be left */ + if (check_dump_flag(rt, DUMP_ATOM_LEAKS)) { + BOOL header_done = FALSE; + + for(i = 0; i < rt->atom_size; i++) { + JSAtomStruct *p = rt->atom_array[i]; + if (!atom_is_free(p) /* && p->str*/) { + if (i >= JS_ATOM_END || p->header.ref_count != 1) { + if (!header_done) { + header_done = TRUE; + if (rt->rt_info) { + printf("%s:1: atom leakage:", rt->rt_info); + } else { + printf("Atom leaks:\n" + " %6s %6s %s\n", + "ID", "REFCNT", "NAME"); + } + } + if (rt->rt_info) { + printf(" "); + } else { + printf(" %6u %6u ", i, p->header.ref_count); + } + switch (p->atom_type) { + case JS_ATOM_TYPE_STRING: + JS_DumpString(rt, p); + break; + case JS_ATOM_TYPE_GLOBAL_SYMBOL: + printf("Symbol.for("); + JS_DumpString(rt, p); + printf(")"); + break; + case JS_ATOM_TYPE_SYMBOL: + if (p->hash == JS_ATOM_HASH_SYMBOL) { + printf("Symbol("); + JS_DumpString(rt, p); + printf(")"); + } else { + printf("Private("); + JS_DumpString(rt, p); + printf(")"); + } + break; + } + if (rt->rt_info) { + printf(":%u", p->header.ref_count); + } else { + printf("\n"); + } + } + } + } + if (rt->rt_info && header_done) + printf("\n"); + } +#endif + + /* free the atoms */ + for(i = 0; i < rt->atom_size; i++) { + JSAtomStruct *p = rt->atom_array[i]; + if (!atom_is_free(p)) { +#ifdef DUMP_LEAKS + list_del(&p->link); +#endif + js_free_rt(rt, p); + } + } + js_free_rt(rt, rt->atom_array); + js_free_rt(rt, rt->atom_hash); + js_free_rt(rt, rt->shape_hash); +#ifdef DUMP_LEAKS + if (check_dump_flag(rt, DUMP_LEAKS) && !list_empty(&rt->string_list)) { + if (rt->rt_info) { + printf("%s:1: string leakage:", rt->rt_info); + } else { + printf("String leaks:\n" + " %6s %s\n", + "REFCNT", "VALUE"); + } + list_for_each_safe(el, el1, &rt->string_list) { + JSString *str = list_entry(el, JSString, link); + if (rt->rt_info) { + printf(" "); + } else { + printf(" %6u ", str->header.ref_count); + } + JS_DumpString(rt, str); + if (rt->rt_info) { + printf(":%u", str->header.ref_count); + } else { + printf("\n"); + } + list_del(&str->link); + js_free_rt(rt, str); + } + if (rt->rt_info) + printf("\n"); + } +#endif + + while (rt->finalizers) { + JSRuntimeFinalizerState *fs = rt->finalizers; + rt->finalizers = fs->next; + fs->finalizer(rt, fs->arg); + js_free_rt(rt, fs); + } + +#ifdef DUMP_LEAKS + if (check_dump_flag(rt, DUMP_LEAKS)) { + JSMallocState *s = &rt->malloc_state; + if (s->malloc_count > 1) { + if (rt->rt_info) + printf("%s:1: ", rt->rt_info); + printf("Memory leak: %"PRIu64" bytes lost in %"PRIu64" block%s\n", + (uint64_t)(s->malloc_size - sizeof(JSRuntime)), + (uint64_t)(s->malloc_count - 1), &"s"[s->malloc_count == 2]); + } + } +#endif + + { + JSMallocState *ms = &rt->malloc_state; + rt->mf.js_free(ms->opaque, rt); + } +} + +JSContext *JS_NewContextRaw(JSRuntime *rt) +{ + JSContext *ctx; + int i; + + ctx = js_mallocz_rt(rt, sizeof(JSContext)); + if (!ctx) + return NULL; + ctx->header.ref_count = 1; + add_gc_object(rt, &ctx->header, JS_GC_OBJ_TYPE_JS_CONTEXT); + + ctx->class_proto = js_malloc_rt(rt, sizeof(ctx->class_proto[0]) * + rt->class_count); + if (!ctx->class_proto) { + js_free_rt(rt, ctx); + return NULL; + } + ctx->rt = rt; + list_add_tail(&ctx->link, &rt->context_list); + ctx->bf_ctx = &rt->bf_ctx; + for(i = 0; i < rt->class_count; i++) + ctx->class_proto[i] = JS_NULL; + ctx->array_ctor = JS_NULL; + ctx->iterator_ctor = JS_NULL; + ctx->regexp_ctor = JS_NULL; + ctx->promise_ctor = JS_NULL; + ctx->error_ctor = JS_NULL; + ctx->error_prepare_stack = JS_UNDEFINED; + ctx->error_stack_trace_limit = 10; + init_list_head(&ctx->loaded_modules); + + JS_AddIntrinsicBasicObjects(ctx); + return ctx; +} + +JSContext *JS_NewContext(JSRuntime *rt) +{ + JSContext *ctx; + + ctx = JS_NewContextRaw(rt); + if (!ctx) + return NULL; + + JS_AddIntrinsicBaseObjects(ctx); + JS_AddIntrinsicDate(ctx); + JS_AddIntrinsicEval(ctx); + JS_AddIntrinsicRegExp(ctx); + JS_AddIntrinsicJSON(ctx); + JS_AddIntrinsicProxy(ctx); + JS_AddIntrinsicMapSet(ctx); + JS_AddIntrinsicTypedArrays(ctx); + JS_AddIntrinsicPromise(ctx); + JS_AddIntrinsicBigInt(ctx); + JS_AddIntrinsicWeakRef(ctx); + + JS_AddPerformance(ctx); + + return ctx; +} + +void *JS_GetContextOpaque(JSContext *ctx) +{ + return ctx->user_opaque; +} + +void JS_SetContextOpaque(JSContext *ctx, void *opaque) +{ + ctx->user_opaque = opaque; +} + +/* set the new value and free the old value after (freeing the value + can reallocate the object data) */ +static inline void set_value(JSContext *ctx, JSValue *pval, JSValue new_val) +{ + JSValue old_val; + old_val = *pval; + *pval = new_val; + JS_FreeValue(ctx, old_val); +} + +void JS_SetClassProto(JSContext *ctx, JSClassID class_id, JSValue obj) +{ + assert(class_id < ctx->rt->class_count); + set_value(ctx, &ctx->class_proto[class_id], obj); +} + +JSValue JS_GetClassProto(JSContext *ctx, JSClassID class_id) +{ + assert(class_id < ctx->rt->class_count); + return js_dup(ctx->class_proto[class_id]); +} + +JSValue JS_GetFunctionProto(JSContext *ctx) +{ + return js_dup(ctx->function_proto); +} + +typedef enum JSFreeModuleEnum { + JS_FREE_MODULE_ALL, + JS_FREE_MODULE_NOT_RESOLVED, +} JSFreeModuleEnum; + +/* XXX: would be more efficient with separate module lists */ +static void js_free_modules(JSContext *ctx, JSFreeModuleEnum flag) +{ + struct list_head *el, *el1; + list_for_each_safe(el, el1, &ctx->loaded_modules) { + JSModuleDef *m = list_entry(el, JSModuleDef, link); + if (flag == JS_FREE_MODULE_ALL || + (flag == JS_FREE_MODULE_NOT_RESOLVED && !m->resolved)) { + js_free_module_def(ctx, m); + } + } +} + +JSContext *JS_DupContext(JSContext *ctx) +{ + ctx->header.ref_count++; + return ctx; +} + +/* used by the GC */ +static void JS_MarkContext(JSRuntime *rt, JSContext *ctx, + JS_MarkFunc *mark_func) +{ + int i; + struct list_head *el; + + /* modules are not seen by the GC, so we directly mark the objects + referenced by each module */ + list_for_each(el, &ctx->loaded_modules) { + JSModuleDef *m = list_entry(el, JSModuleDef, link); + js_mark_module_def(rt, m, mark_func); + } + + JS_MarkValue(rt, ctx->global_obj, mark_func); + JS_MarkValue(rt, ctx->global_var_obj, mark_func); + + JS_MarkValue(rt, ctx->throw_type_error, mark_func); + JS_MarkValue(rt, ctx->eval_obj, mark_func); + + JS_MarkValue(rt, ctx->array_proto_values, mark_func); + for(i = 0; i < JS_NATIVE_ERROR_COUNT; i++) { + JS_MarkValue(rt, ctx->native_error_proto[i], mark_func); + } + JS_MarkValue(rt, ctx->error_ctor, mark_func); + JS_MarkValue(rt, ctx->error_prepare_stack, mark_func); + for(i = 0; i < rt->class_count; i++) { + JS_MarkValue(rt, ctx->class_proto[i], mark_func); + } + JS_MarkValue(rt, ctx->iterator_ctor, mark_func); + JS_MarkValue(rt, ctx->async_iterator_proto, mark_func); + JS_MarkValue(rt, ctx->promise_ctor, mark_func); + JS_MarkValue(rt, ctx->array_ctor, mark_func); + JS_MarkValue(rt, ctx->regexp_ctor, mark_func); + JS_MarkValue(rt, ctx->function_ctor, mark_func); + JS_MarkValue(rt, ctx->function_proto, mark_func); + + if (ctx->array_shape) + mark_func(rt, &ctx->array_shape->header); +} + +void JS_FreeContext(JSContext *ctx) +{ + JSRuntime *rt = ctx->rt; + int i; + + if (--ctx->header.ref_count > 0) + return; + assert(ctx->header.ref_count == 0); + +#ifdef DUMP_ATOMS + if (check_dump_flag(rt, DUMP_ATOMS)) + JS_DumpAtoms(ctx->rt); +#endif +#ifdef DUMP_SHAPES + if (check_dump_flag(rt, DUMP_SHAPES)) + JS_DumpShapes(ctx->rt); +#endif +#ifdef DUMP_OBJECTS + if (check_dump_flag(rt, DUMP_OBJECTS)) { + struct list_head *el; + JSGCObjectHeader *p; + printf("JSObjects: {\n"); + JS_DumpObjectHeader(ctx->rt); + list_for_each(el, &rt->gc_obj_list) { + p = list_entry(el, JSGCObjectHeader, link); + JS_DumpGCObject(rt, p); + } + printf("}\n"); + } +#endif +#ifdef DUMP_MEM + if (check_dump_flag(rt, DUMP_MEM)) { + JSMemoryUsage stats; + JS_ComputeMemoryUsage(rt, &stats); + JS_DumpMemoryUsage(stdout, &stats, rt); + } +#endif + + js_free_modules(ctx, JS_FREE_MODULE_ALL); + + JS_FreeValue(ctx, ctx->global_obj); + JS_FreeValue(ctx, ctx->global_var_obj); + + JS_FreeValue(ctx, ctx->throw_type_error); + JS_FreeValue(ctx, ctx->eval_obj); + + JS_FreeValue(ctx, ctx->array_proto_values); + for(i = 0; i < JS_NATIVE_ERROR_COUNT; i++) { + JS_FreeValue(ctx, ctx->native_error_proto[i]); + } + JS_FreeValue(ctx, ctx->error_ctor); + JS_FreeValue(ctx, ctx->error_prepare_stack); + for(i = 0; i < rt->class_count; i++) { + JS_FreeValue(ctx, ctx->class_proto[i]); + } + js_free_rt(rt, ctx->class_proto); + JS_FreeValue(ctx, ctx->iterator_ctor); + JS_FreeValue(ctx, ctx->async_iterator_proto); + JS_FreeValue(ctx, ctx->promise_ctor); + JS_FreeValue(ctx, ctx->array_ctor); + JS_FreeValue(ctx, ctx->regexp_ctor); + JS_FreeValue(ctx, ctx->function_ctor); + JS_FreeValue(ctx, ctx->function_proto); + + js_free_shape_null(ctx->rt, ctx->array_shape); + + list_del(&ctx->link); + remove_gc_object(&ctx->header); + js_free_rt(ctx->rt, ctx); +} + +JSRuntime *JS_GetRuntime(JSContext *ctx) +{ + return ctx->rt; +} + +static void update_stack_limit(JSRuntime *rt) +{ +#if defined(__wasi__) || (defined(__ASAN__) && !defined(NDEBUG)) + rt->stack_limit = 0; /* no limit */ +#else + if (rt->stack_size == 0) { + rt->stack_limit = 0; /* no limit */ + } else { + rt->stack_limit = rt->stack_top - rt->stack_size; + } +#endif +} + +void JS_SetMaxStackSize(JSRuntime *rt, size_t stack_size) +{ + rt->stack_size = stack_size; + update_stack_limit(rt); +} + +void JS_UpdateStackTop(JSRuntime *rt) +{ + rt->stack_top = js_get_stack_pointer(); + update_stack_limit(rt); +} + +static inline BOOL is_strict_mode(JSContext *ctx) +{ + JSStackFrame *sf = ctx->rt->current_stack_frame; + return sf && sf->is_strict_mode; +} + +/* JSAtom support */ + +#define JS_ATOM_TAG_INT (1U << 31) +#define JS_ATOM_MAX_INT (JS_ATOM_TAG_INT - 1) +#define JS_ATOM_MAX ((1U << 30) - 1) + +/* return the max count from the hash size */ +#define JS_ATOM_COUNT_RESIZE(n) ((n) * 2) + +static inline BOOL __JS_AtomIsConst(JSAtom v) +{ + return (int32_t)v < JS_ATOM_END; +} + +static inline BOOL __JS_AtomIsTaggedInt(JSAtom v) +{ + return (v & JS_ATOM_TAG_INT) != 0; +} + +static inline JSAtom __JS_AtomFromUInt32(uint32_t v) +{ + return v | JS_ATOM_TAG_INT; +} + +static inline uint32_t __JS_AtomToUInt32(JSAtom atom) +{ + return atom & ~JS_ATOM_TAG_INT; +} + +static inline int is_num(int c) +{ + return c >= '0' && c <= '9'; +} + +/* return TRUE if the string is a number n with 0 <= n <= 2^32-1 */ +static inline BOOL is_num_string(uint32_t *pval, const JSString *p) +{ + uint32_t n; + uint64_t n64; + int c, i, len; + + len = p->len; + if (len == 0 || len > 10) + return FALSE; + c = string_get(p, 0); + if (is_num(c)) { + if (c == '0') { + if (len != 1) + return FALSE; + n = 0; + } else { + n = c - '0'; + for(i = 1; i < len; i++) { + c = string_get(p, i); + if (!is_num(c)) + return FALSE; + n64 = (uint64_t)n * 10 + (c - '0'); + if ((n64 >> 32) != 0) + return FALSE; + n = n64; + } + } + *pval = n; + return TRUE; + } else { + return FALSE; + } +} + +/* XXX: could use faster version ? */ +static inline uint32_t hash_string8(const uint8_t *str, size_t len, uint32_t h) +{ + size_t i; + + for(i = 0; i < len; i++) + h = h * 263 + str[i]; + return h; +} + +static inline uint32_t hash_string16(const uint16_t *str, + size_t len, uint32_t h) +{ + size_t i; + + for(i = 0; i < len; i++) + h = h * 263 + str[i]; + return h; +} + +static uint32_t hash_string(const JSString *str, uint32_t h) +{ + if (str->is_wide_char) + h = hash_string16(str->u.str16, str->len, h); + else + h = hash_string8(str->u.str8, str->len, h); + return h; +} + +static __maybe_unused void JS_DumpString(JSRuntime *rt, + const JSString *p) +{ + int i, c, sep; + + if (p == NULL) { + printf("<null>"); + return; + } + if (p->header.ref_count != 1) + printf("%d", p->header.ref_count); + if (p->is_wide_char) + putchar('L'); + sep = '\"'; + putchar(sep); + for(i = 0; i < p->len; i++) { + c = string_get(p, i); + if (c == sep || c == '\\') { + putchar('\\'); + putchar(c); + } else if (c >= ' ' && c <= 126) { + putchar(c); + } else if (c == '\n') { + putchar('\\'); + putchar('n'); + } else { + printf("\\u%04x", c); + } + } + putchar(sep); +} + +static __maybe_unused void JS_DumpAtoms(JSRuntime *rt) +{ + JSAtomStruct *p; + int h, i; + /* This only dumps hashed atoms, not JS_ATOM_TYPE_SYMBOL atoms */ + printf("JSAtom count=%d size=%d hash_size=%d:\n", + rt->atom_count, rt->atom_size, rt->atom_hash_size); + printf("JSAtom hash table: {\n"); + for(i = 0; i < rt->atom_hash_size; i++) { + h = rt->atom_hash[i]; + if (h) { + printf(" %d:", i); + while (h) { + p = rt->atom_array[h]; + printf(" "); + JS_DumpString(rt, p); + h = p->hash_next; + } + printf("\n"); + } + } + printf("}\n"); + printf("JSAtom table: {\n"); + for(i = 0; i < rt->atom_size; i++) { + p = rt->atom_array[i]; + if (!atom_is_free(p)) { + printf(" %d: { %d %08x ", i, p->atom_type, p->hash); + if (!(p->len == 0 && p->is_wide_char != 0)) + JS_DumpString(rt, p); + printf(" %d }\n", p->hash_next); + } + } + printf("}\n"); +} + +static int JS_ResizeAtomHash(JSRuntime *rt, int new_hash_size) +{ + JSAtomStruct *p; + uint32_t new_hash_mask, h, i, hash_next1, j, *new_hash; + + assert((new_hash_size & (new_hash_size - 1)) == 0); /* power of two */ + new_hash_mask = new_hash_size - 1; + new_hash = js_mallocz_rt(rt, sizeof(rt->atom_hash[0]) * new_hash_size); + if (!new_hash) + return -1; + for(i = 0; i < rt->atom_hash_size; i++) { + h = rt->atom_hash[i]; + while (h != 0) { + p = rt->atom_array[h]; + hash_next1 = p->hash_next; + /* add in new hash table */ + j = p->hash & new_hash_mask; + p->hash_next = new_hash[j]; + new_hash[j] = h; + h = hash_next1; + } + } + js_free_rt(rt, rt->atom_hash); + rt->atom_hash = new_hash; + rt->atom_hash_size = new_hash_size; + rt->atom_count_resize = JS_ATOM_COUNT_RESIZE(new_hash_size); + // JS_DumpAtoms(rt); + return 0; +} + +static int JS_InitAtoms(JSRuntime *rt) +{ + int i, len, atom_type; + const char *p; + + rt->atom_hash_size = 0; + rt->atom_hash = NULL; + rt->atom_count = 0; + rt->atom_size = 0; + rt->atom_free_index = 0; + if (JS_ResizeAtomHash(rt, 256)) /* there are at least 195 predefined atoms */ + return -1; + + p = js_atom_init; + for(i = 1; i < JS_ATOM_END; i++) { + if (i == JS_ATOM_Private_brand) + atom_type = JS_ATOM_TYPE_PRIVATE; + else if (i >= JS_ATOM_Symbol_toPrimitive) + atom_type = JS_ATOM_TYPE_SYMBOL; + else + atom_type = JS_ATOM_TYPE_STRING; + len = strlen(p); + if (__JS_NewAtomInit(rt, p, len, atom_type) == JS_ATOM_NULL) + return -1; + p = p + len + 1; + } + return 0; +} + +static JSAtom JS_DupAtomRT(JSRuntime *rt, JSAtom v) +{ + JSAtomStruct *p; + + if (!__JS_AtomIsConst(v)) { + p = rt->atom_array[v]; + p->header.ref_count++; + } + return v; +} + +JSAtom JS_DupAtom(JSContext *ctx, JSAtom v) +{ + JSRuntime *rt; + JSAtomStruct *p; + + if (!__JS_AtomIsConst(v)) { + rt = ctx->rt; + p = rt->atom_array[v]; + p->header.ref_count++; + } + return v; +} + +static JSAtomKindEnum JS_AtomGetKind(JSContext *ctx, JSAtom v) +{ + JSRuntime *rt; + JSAtomStruct *p; + + rt = ctx->rt; + if (__JS_AtomIsTaggedInt(v)) + return JS_ATOM_KIND_STRING; + p = rt->atom_array[v]; + switch(p->atom_type) { + case JS_ATOM_TYPE_STRING: + return JS_ATOM_KIND_STRING; + case JS_ATOM_TYPE_GLOBAL_SYMBOL: + return JS_ATOM_KIND_SYMBOL; + case JS_ATOM_TYPE_SYMBOL: + switch(p->hash) { + case JS_ATOM_HASH_SYMBOL: + return JS_ATOM_KIND_SYMBOL; + case JS_ATOM_HASH_PRIVATE: + return JS_ATOM_KIND_PRIVATE; + default: + abort(); + } + default: + abort(); + } + return (JSAtomKindEnum){-1}; // pacify compiler +} + +static JSAtom js_get_atom_index(JSRuntime *rt, JSAtomStruct *p) +{ + uint32_t i = p->hash_next; /* atom_index */ + if (p->atom_type != JS_ATOM_TYPE_SYMBOL) { + JSAtomStruct *p1; + + i = rt->atom_hash[p->hash & (rt->atom_hash_size - 1)]; + p1 = rt->atom_array[i]; + while (p1 != p) { + assert(i != 0); + i = p1->hash_next; + p1 = rt->atom_array[i]; + } + } + return i; +} + +/* string case (internal). Return JS_ATOM_NULL if error. 'str' is + freed. */ +static JSAtom __JS_NewAtom(JSRuntime *rt, JSString *str, int atom_type) +{ + uint32_t h, h1, i; + JSAtomStruct *p; + int len; + + if (atom_type < JS_ATOM_TYPE_SYMBOL) { + /* str is not NULL */ + if (str->atom_type == atom_type) { + /* str is the atom, return its index */ + i = js_get_atom_index(rt, str); + /* reduce string refcount and increase atom's unless constant */ + if (__JS_AtomIsConst(i)) + str->header.ref_count--; + return i; + } + /* try and locate an already registered atom */ + len = str->len; + h = hash_string(str, atom_type); + h &= JS_ATOM_HASH_MASK; + h1 = h & (rt->atom_hash_size - 1); + i = rt->atom_hash[h1]; + while (i != 0) { + p = rt->atom_array[i]; + if (p->hash == h && + p->atom_type == atom_type && + p->len == len && + js_string_memcmp(p, str, len) == 0) { + if (!__JS_AtomIsConst(i)) + p->header.ref_count++; + goto done; + } + i = p->hash_next; + } + } else { + h1 = 0; /* avoid warning */ + if (atom_type == JS_ATOM_TYPE_SYMBOL) { + h = JS_ATOM_HASH_SYMBOL; + } else { + h = JS_ATOM_HASH_PRIVATE; + atom_type = JS_ATOM_TYPE_SYMBOL; + } + } + + if (rt->atom_free_index == 0) { + /* allow new atom entries */ + uint32_t new_size, start; + JSAtomStruct **new_array; + + /* alloc new with size progression 3/2: + 4 6 9 13 19 28 42 63 94 141 211 316 474 711 1066 1599 2398 3597 5395 8092 + preallocating space for predefined atoms (at least 195). + */ + new_size = max_int(211, rt->atom_size * 3 / 2); + if (new_size > JS_ATOM_MAX) + goto fail; + /* XXX: should use realloc2 to use slack space */ + new_array = js_realloc_rt(rt, rt->atom_array, sizeof(*new_array) * new_size); + if (!new_array) + goto fail; + /* Note: the atom 0 is not used */ + start = rt->atom_size; + if (start == 0) { + /* JS_ATOM_NULL entry */ + p = js_mallocz_rt(rt, sizeof(JSAtomStruct)); + if (!p) { + js_free_rt(rt, new_array); + goto fail; + } + p->header.ref_count = 1; /* not refcounted */ + p->atom_type = JS_ATOM_TYPE_SYMBOL; +#ifdef DUMP_LEAKS + list_add_tail(&p->link, &rt->string_list); +#endif + new_array[0] = p; + rt->atom_count++; + start = 1; + } + rt->atom_size = new_size; + rt->atom_array = new_array; + rt->atom_free_index = start; + for(i = start; i < new_size; i++) { + uint32_t next; + if (i == (new_size - 1)) + next = 0; + else + next = i + 1; + rt->atom_array[i] = atom_set_free(next); + } + } + + if (str) { + if (str->atom_type == 0) { + p = str; + p->atom_type = atom_type; + } else { + p = js_malloc_rt(rt, sizeof(JSString) + + (str->len << str->is_wide_char) + + 1 - str->is_wide_char); + if (unlikely(!p)) + goto fail; + p->header.ref_count = 1; + p->is_wide_char = str->is_wide_char; + p->len = str->len; +#ifdef DUMP_LEAKS + list_add_tail(&p->link, &rt->string_list); +#endif + memcpy(p->u.str8, str->u.str8, (str->len << str->is_wide_char) + + 1 - str->is_wide_char); + js_free_string(rt, str); + } + } else { + p = js_malloc_rt(rt, sizeof(JSAtomStruct)); /* empty wide string */ + if (!p) + return JS_ATOM_NULL; + p->header.ref_count = 1; + p->is_wide_char = 1; /* Hack to represent NULL as a JSString */ + p->len = 0; +#ifdef DUMP_LEAKS + list_add_tail(&p->link, &rt->string_list); +#endif + } + + /* use an already free entry */ + i = rt->atom_free_index; + rt->atom_free_index = atom_get_free(rt->atom_array[i]); + rt->atom_array[i] = p; + + p->hash = h; + p->hash_next = i; /* atom_index */ + p->atom_type = atom_type; + p->first_weak_ref = NULL; + + rt->atom_count++; + + if (atom_type != JS_ATOM_TYPE_SYMBOL) { + p->hash_next = rt->atom_hash[h1]; + rt->atom_hash[h1] = i; + if (unlikely(rt->atom_count >= rt->atom_count_resize)) + JS_ResizeAtomHash(rt, rt->atom_hash_size * 2); + } + + // JS_DumpAtoms(rt); + return i; + + fail: + i = JS_ATOM_NULL; + done: + if (str) + js_free_string(rt, str); + return i; +} + +// XXX: `str` must be pure ASCII. No UTF-8 encoded strings +// XXX: `str` must not be the string representation of a small integer +static JSAtom __JS_NewAtomInit(JSRuntime *rt, const char *str, int len, + int atom_type) +{ + JSString *p; + p = js_alloc_string_rt(rt, len, 0); + if (!p) + return JS_ATOM_NULL; + memcpy(p->u.str8, str, len); + p->u.str8[len] = '\0'; + return __JS_NewAtom(rt, p, atom_type); +} + +// XXX: `str` must be raw 8-bit contents. No UTF-8 encoded strings +static JSAtom __JS_FindAtom(JSRuntime *rt, const char *str, size_t len, + int atom_type) +{ + uint32_t h, h1, i; + JSAtomStruct *p; + + h = hash_string8((const uint8_t *)str, len, JS_ATOM_TYPE_STRING); + h &= JS_ATOM_HASH_MASK; + h1 = h & (rt->atom_hash_size - 1); + i = rt->atom_hash[h1]; + while (i != 0) { + p = rt->atom_array[i]; + if (p->hash == h && + p->atom_type == JS_ATOM_TYPE_STRING && + p->len == len && + p->is_wide_char == 0 && + memcmp(p->u.str8, str, len) == 0) { + if (!__JS_AtomIsConst(i)) + p->header.ref_count++; + return i; + } + i = p->hash_next; + } + return JS_ATOM_NULL; +} + +static void JS_FreeAtomStruct(JSRuntime *rt, JSAtomStruct *p) +{ + uint32_t i = p->hash_next; /* atom_index */ + if (p->atom_type != JS_ATOM_TYPE_SYMBOL) { + JSAtomStruct *p0, *p1; + uint32_t h0; + + h0 = p->hash & (rt->atom_hash_size - 1); + i = rt->atom_hash[h0]; + p1 = rt->atom_array[i]; + if (p1 == p) { + rt->atom_hash[h0] = p1->hash_next; + } else { + for(;;) { + assert(i != 0); + p0 = p1; + i = p1->hash_next; + p1 = rt->atom_array[i]; + if (p1 == p) { + p0->hash_next = p1->hash_next; + break; + } + } + } + } + /* insert in free atom list */ + rt->atom_array[i] = atom_set_free(rt->atom_free_index); + rt->atom_free_index = i; + if (unlikely(p->first_weak_ref)) { + reset_weak_ref(rt, &p->first_weak_ref); + } + /* free the string structure */ +#ifdef DUMP_LEAKS + list_del(&p->link); +#endif + js_free_rt(rt, p); + rt->atom_count--; + assert(rt->atom_count >= 0); +} + +static void __JS_FreeAtom(JSRuntime *rt, uint32_t i) +{ + JSAtomStruct *p; + + p = rt->atom_array[i]; + if (--p->header.ref_count > 0) + return; + JS_FreeAtomStruct(rt, p); +} + +/* Warning: 'p' is freed */ +static JSAtom JS_NewAtomStr(JSContext *ctx, JSString *p) +{ + JSRuntime *rt = ctx->rt; + uint32_t n; + if (is_num_string(&n, p)) { + if (n <= JS_ATOM_MAX_INT) { + js_free_string(rt, p); + return __JS_AtomFromUInt32(n); + } + } + /* XXX: should generate an exception */ + return __JS_NewAtom(rt, p, JS_ATOM_TYPE_STRING); +} + +/* `str` may be pure ASCII or UTF-8 encoded */ +JSAtom JS_NewAtomLen(JSContext *ctx, const char *str, size_t len) +{ + JSValue val; + + if (len == 0 || !is_digit(*str)) { + // TODO(chqrlie): this does not work if `str` has UTF-8 encoded contents + // bug example: `({ "\u00c3\u00a9": 1 }).\u00e9` evaluates to `1`. + JSAtom atom = __JS_FindAtom(ctx->rt, str, len, JS_ATOM_TYPE_STRING); + if (atom) + return atom; + } + val = JS_NewStringLen(ctx, str, len); + if (JS_IsException(val)) + return JS_ATOM_NULL; + return JS_NewAtomStr(ctx, JS_VALUE_GET_STRING(val)); +} + +/* `str` may be pure ASCII or UTF-8 encoded */ +JSAtom JS_NewAtom(JSContext *ctx, const char *str) +{ + return JS_NewAtomLen(ctx, str, strlen(str)); +} + +JSAtom JS_NewAtomUInt32(JSContext *ctx, uint32_t n) +{ + if (n <= JS_ATOM_MAX_INT) { + return __JS_AtomFromUInt32(n); + } else { + char buf[16]; + size_t len = u32toa(buf, n); + JSValue val = js_new_string8_len(ctx, buf, len); + if (JS_IsException(val)) + return JS_ATOM_NULL; + return __JS_NewAtom(ctx->rt, JS_VALUE_GET_STRING(val), + JS_ATOM_TYPE_STRING); + } +} + +static JSAtom JS_NewAtomInt64(JSContext *ctx, int64_t n) +{ + if ((uint64_t)n <= JS_ATOM_MAX_INT) { + return __JS_AtomFromUInt32((uint32_t)n); + } else { + char buf[24]; + size_t len = i64toa(buf, n); + JSValue val = js_new_string8_len(ctx, buf, len); + if (JS_IsException(val)) + return JS_ATOM_NULL; + return __JS_NewAtom(ctx->rt, JS_VALUE_GET_STRING(val), + JS_ATOM_TYPE_STRING); + } +} + +/* 'p' is freed */ +static JSValue JS_NewSymbolInternal(JSContext *ctx, JSString *p, int atom_type) +{ + JSRuntime *rt = ctx->rt; + JSAtom atom; + atom = __JS_NewAtom(rt, p, atom_type); + if (atom == JS_ATOM_NULL) + return JS_ThrowOutOfMemory(ctx); + return JS_MKPTR(JS_TAG_SYMBOL, rt->atom_array[atom]); +} + +/* descr must be a non-numeric string atom */ +static JSValue JS_NewSymbolFromAtom(JSContext *ctx, JSAtom descr, + int atom_type) +{ + JSRuntime *rt = ctx->rt; + JSString *p; + + assert(!__JS_AtomIsTaggedInt(descr)); + assert(descr < rt->atom_size); + p = rt->atom_array[descr]; + js_dup(JS_MKPTR(JS_TAG_STRING, p)); + return JS_NewSymbolInternal(ctx, p, atom_type); +} + +/* `description` may be pure ASCII or UTF-8 encoded */ +JSValue JS_NewSymbol(JSContext *ctx, const char *description, JS_BOOL is_global) +{ + JSAtom atom = JS_NewAtom(ctx, description); + if (atom == JS_ATOM_NULL) + return JS_EXCEPTION; + return JS_NewSymbolFromAtom(ctx, atom, is_global ? JS_ATOM_TYPE_GLOBAL_SYMBOL : JS_ATOM_TYPE_SYMBOL); +} + +#define ATOM_GET_STR_BUF_SIZE 64 + +/* Should only be used for debug. */ +static const char *JS_AtomGetStrRT(JSRuntime *rt, char *buf, int buf_size, + JSAtom atom) +{ + if (__JS_AtomIsTaggedInt(atom)) { + snprintf(buf, buf_size, "%u", __JS_AtomToUInt32(atom)); + } else if (atom == JS_ATOM_NULL) { + snprintf(buf, buf_size, "<null>"); + } else if (atom >= rt->atom_size) { + assert(atom < rt->atom_size); + snprintf(buf, buf_size, "<invalid %x>", atom); + } else { + JSAtomStruct *p = rt->atom_array[atom]; + *buf = '\0'; + if (atom_is_free(p)) { + assert(!atom_is_free(p)); + snprintf(buf, buf_size, "<free %x>", atom); + } else if (p != NULL) { + JSString *str = p; + if (str->is_wide_char) { + /* encode surrogates correctly */ + utf8_encode_buf16(buf, buf_size, str->u.str16, str->len); + } else { + /* special case ASCII strings */ + int i, c = 0; + for(i = 0; i < str->len; i++) { + c |= str->u.str8[i]; + } + if (c < 0x80) + return (const char *)str->u.str8; + utf8_encode_buf8(buf, buf_size, str->u.str8, str->len); + } + } + } + return buf; +} + +static const char *JS_AtomGetStr(JSContext *ctx, char *buf, int buf_size, JSAtom atom) +{ + return JS_AtomGetStrRT(ctx->rt, buf, buf_size, atom); +} + +static JSValue __JS_AtomToValue(JSContext *ctx, JSAtom atom, BOOL force_string) +{ + char buf[ATOM_GET_STR_BUF_SIZE]; + + if (__JS_AtomIsTaggedInt(atom)) { + size_t len = u32toa(buf, __JS_AtomToUInt32(atom)); + return js_new_string8_len(ctx, buf, len); + } else { + JSRuntime *rt = ctx->rt; + JSAtomStruct *p; + assert(atom < rt->atom_size); + p = rt->atom_array[atom]; + if (p->atom_type == JS_ATOM_TYPE_STRING) { + goto ret_string; + } else if (force_string) { + if (p->len == 0 && p->is_wide_char != 0) { + /* no description string */ + p = rt->atom_array[JS_ATOM_empty_string]; + } + ret_string: + return js_dup(JS_MKPTR(JS_TAG_STRING, p)); + } else { + return js_dup(JS_MKPTR(JS_TAG_SYMBOL, p)); + } + } +} + +JSValue JS_AtomToValue(JSContext *ctx, JSAtom atom) +{ + return __JS_AtomToValue(ctx, atom, FALSE); +} + +JSValue JS_AtomToString(JSContext *ctx, JSAtom atom) +{ + return __JS_AtomToValue(ctx, atom, TRUE); +} + +/* return TRUE if the atom is an array index (i.e. 0 <= index <= + 2^32-2 and return its value */ +static BOOL JS_AtomIsArrayIndex(JSContext *ctx, uint32_t *pval, JSAtom atom) +{ + if (__JS_AtomIsTaggedInt(atom)) { + *pval = __JS_AtomToUInt32(atom); + return TRUE; + } else { + JSRuntime *rt = ctx->rt; + JSAtomStruct *p; + uint32_t val; + + assert(atom < rt->atom_size); + p = rt->atom_array[atom]; + if (p->atom_type == JS_ATOM_TYPE_STRING && + is_num_string(&val, p) && val != -1) { + *pval = val; + return TRUE; + } else { + *pval = 0; + return FALSE; + } + } +} + +/* This test must be fast if atom is not a numeric index (e.g. a + method name). Return JS_UNDEFINED if not a numeric + index. JS_EXCEPTION can also be returned. */ +static JSValue JS_AtomIsNumericIndex1(JSContext *ctx, JSAtom atom) +{ + JSRuntime *rt = ctx->rt; + JSAtomStruct *p1; + JSString *p; + int c, len, ret; + JSValue num, str; + + if (__JS_AtomIsTaggedInt(atom)) + return js_int32(__JS_AtomToUInt32(atom)); + assert(atom < rt->atom_size); + p1 = rt->atom_array[atom]; + if (p1->atom_type != JS_ATOM_TYPE_STRING) + return JS_UNDEFINED; + p = p1; + len = p->len; + if (p->is_wide_char) { + const uint16_t *r = p->u.str16, *r_end = p->u.str16 + len; + if (r >= r_end) + return JS_UNDEFINED; + c = *r; + if (c == '-') { + if (r >= r_end) + return JS_UNDEFINED; + r++; + c = *r; + /* -0 case is specific */ + if (c == '0' && len == 2) + goto minus_zero; + } + /* XXX: should test NaN, but the tests do not check it */ + if (!is_num(c)) { + /* XXX: String should be normalized, therefore 8-bit only */ + const uint16_t nfinity16[7] = { 'n', 'f', 'i', 'n', 'i', 't', 'y' }; + if (!(c =='I' && (r_end - r) == 8 && + !memcmp(r + 1, nfinity16, sizeof(nfinity16)))) + return JS_UNDEFINED; + } + } else { + const uint8_t *r = p->u.str8, *r_end = p->u.str8 + len; + if (r >= r_end) + return JS_UNDEFINED; + c = *r; + if (c == '-') { + if (r >= r_end) + return JS_UNDEFINED; + r++; + c = *r; + /* -0 case is specific */ + if (c == '0' && len == 2) { + minus_zero: + return js_float64(-0.0); + } + } + if (!is_num(c)) { + if (!(c =='I' && (r_end - r) == 8 && + !memcmp(r + 1, "nfinity", 7))) + return JS_UNDEFINED; + } + } + /* this is ECMA CanonicalNumericIndexString primitive */ + num = JS_ToNumber(ctx, JS_MKPTR(JS_TAG_STRING, p)); + if (JS_IsException(num)) + return num; + str = JS_ToString(ctx, num); + if (JS_IsException(str)) { + JS_FreeValue(ctx, num); + return str; + } + ret = js_string_compare(ctx, p, JS_VALUE_GET_STRING(str)); + JS_FreeValue(ctx, str); + if (ret == 0) { + return num; + } else { + JS_FreeValue(ctx, num); + return JS_UNDEFINED; + } +} + +/* return -1 if exception or TRUE/FALSE */ +static int JS_AtomIsNumericIndex(JSContext *ctx, JSAtom atom) +{ + JSValue num; + num = JS_AtomIsNumericIndex1(ctx, atom); + if (likely(JS_IsUndefined(num))) + return FALSE; + if (JS_IsException(num)) + return -1; + JS_FreeValue(ctx, num); + return TRUE; +} + +void JS_FreeAtom(JSContext *ctx, JSAtom v) +{ + if (!__JS_AtomIsConst(v)) + __JS_FreeAtom(ctx->rt, v); +} + +void JS_FreeAtomRT(JSRuntime *rt, JSAtom v) +{ + if (!__JS_AtomIsConst(v)) + __JS_FreeAtom(rt, v); +} + +/* return TRUE if 'v' is a symbol with a string description */ +static BOOL JS_AtomSymbolHasDescription(JSContext *ctx, JSAtom v) +{ + JSRuntime *rt; + JSAtomStruct *p; + + rt = ctx->rt; + if (__JS_AtomIsTaggedInt(v)) + return FALSE; + p = rt->atom_array[v]; + return (((p->atom_type == JS_ATOM_TYPE_SYMBOL && + p->hash == JS_ATOM_HASH_SYMBOL) || + p->atom_type == JS_ATOM_TYPE_GLOBAL_SYMBOL) && + !(p->len == 0 && p->is_wide_char != 0)); +} + +static __maybe_unused void print_atom(JSContext *ctx, JSAtom atom) +{ + char buf[ATOM_GET_STR_BUF_SIZE]; + const char *p; + int i; + + /* XXX: should handle embedded null characters */ + /* XXX: should move encoding code to JS_AtomGetStr */ + p = JS_AtomGetStr(ctx, buf, sizeof(buf), atom); + for (i = 0; p[i]; i++) { + int c = (unsigned char)p[i]; + if (!((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || + (c == '_' || c == '$') || (c >= '0' && c <= '9' && i > 0))) + break; + } + if (i > 0 && p[i] == '\0') { + printf("%s", p); + } else { + putchar('"'); + printf("%.*s", i, p); + for (; p[i]; i++) { + int c = (unsigned char)p[i]; + if (c == '\"' || c == '\\') { + putchar('\\'); + putchar(c); + } else if (c >= ' ' && c <= 126) { + putchar(c); + } else if (c == '\n') { + putchar('\\'); + putchar('n'); + } else { + printf("\\u%04x", c); + } + } + putchar('\"'); + } +} + +/* free with JS_FreeCString() */ +const char *JS_AtomToCString(JSContext *ctx, JSAtom atom) +{ + JSValue str; + const char *cstr; + + str = JS_AtomToString(ctx, atom); + if (JS_IsException(str)) + return NULL; + cstr = JS_ToCString(ctx, str); + JS_FreeValue(ctx, str); + return cstr; +} + +/* return a string atom containing name concatenated with str1 */ +/* `str1` may be pure ASCII or UTF-8 encoded */ +// TODO(chqrlie): use string concatenation instead of UTF-8 conversion +static JSAtom js_atom_concat_str(JSContext *ctx, JSAtom name, const char *str1) +{ + JSValue str; + JSAtom atom; + const char *cstr; + char *cstr2; + size_t len, len1; + + str = JS_AtomToString(ctx, name); + if (JS_IsException(str)) + return JS_ATOM_NULL; + cstr = JS_ToCStringLen(ctx, &len, str); + if (!cstr) + goto fail; + len1 = strlen(str1); + cstr2 = js_malloc(ctx, len + len1 + 1); + if (!cstr2) + goto fail; + memcpy(cstr2, cstr, len); + memcpy(cstr2 + len, str1, len1); + cstr2[len + len1] = '\0'; + atom = JS_NewAtomLen(ctx, cstr2, len + len1); + js_free(ctx, cstr2); + JS_FreeCString(ctx, cstr); + JS_FreeValue(ctx, str); + return atom; + fail: + JS_FreeCString(ctx, cstr); + JS_FreeValue(ctx, str); + return JS_ATOM_NULL; +} + +static JSAtom js_atom_concat_num(JSContext *ctx, JSAtom name, uint32_t n) +{ + char buf[16]; + u32toa(buf, n); + return js_atom_concat_str(ctx, name, buf); +} + +static inline BOOL JS_IsEmptyString(JSValue v) +{ + return JS_VALUE_GET_TAG(v) == JS_TAG_STRING && JS_VALUE_GET_STRING(v)->len == 0; +} + +/* JSClass support */ + +/* a new class ID is allocated if *pclass_id != 0 */ +JSClassID JS_NewClassID(JSRuntime *rt, JSClassID *pclass_id) +{ + JSClassID class_id = *pclass_id; + if (class_id == 0) { + class_id = rt->js_class_id_alloc++; + *pclass_id = class_id; + } + return class_id; +} + +JSClassID JS_GetClassID(JSValue v) +{ + JSObject *p; + if (JS_VALUE_GET_TAG(v) != JS_TAG_OBJECT) + return JS_INVALID_CLASS_ID; + p = JS_VALUE_GET_OBJ(v); + return p->class_id; +} + +BOOL JS_IsRegisteredClass(JSRuntime *rt, JSClassID class_id) +{ + return (class_id < rt->class_count && + rt->class_array[class_id].class_id != 0); +} + +/* create a new object internal class. Return -1 if error, 0 if + OK. The finalizer can be NULL if none is needed. */ +static int JS_NewClass1(JSRuntime *rt, JSClassID class_id, + const JSClassDef *class_def, JSAtom name) +{ + int new_size, i; + JSClass *cl, *new_class_array; + struct list_head *el; + + if (class_id >= (1 << 16)) + return -1; + if (class_id < rt->class_count && + rt->class_array[class_id].class_id != 0) + return -1; + + if (class_id >= rt->class_count) { + new_size = max_int(JS_CLASS_INIT_COUNT, + max_int(class_id + 1, rt->class_count * 3 / 2)); + + /* reallocate the context class prototype array, if any */ + list_for_each(el, &rt->context_list) { + JSContext *ctx = list_entry(el, JSContext, link); + JSValue *new_tab; + new_tab = js_realloc_rt(rt, ctx->class_proto, + sizeof(ctx->class_proto[0]) * new_size); + if (!new_tab) + return -1; + for(i = rt->class_count; i < new_size; i++) + new_tab[i] = JS_NULL; + ctx->class_proto = new_tab; + } + /* reallocate the class array */ + new_class_array = js_realloc_rt(rt, rt->class_array, + sizeof(JSClass) * new_size); + if (!new_class_array) + return -1; + memset(new_class_array + rt->class_count, 0, + (new_size - rt->class_count) * sizeof(JSClass)); + rt->class_array = new_class_array; + rt->class_count = new_size; + } + cl = &rt->class_array[class_id]; + cl->class_id = class_id; + cl->class_name = JS_DupAtomRT(rt, name); + cl->finalizer = class_def->finalizer; + cl->gc_mark = class_def->gc_mark; + cl->call = class_def->call; + cl->exotic = class_def->exotic; + return 0; +} + +int JS_NewClass(JSRuntime *rt, JSClassID class_id, const JSClassDef *class_def) +{ + int ret, len; + JSAtom name; + + // XXX: class_def->class_name must be raw 8-bit contents. No UTF-8 encoded strings + len = strlen(class_def->class_name); + name = __JS_FindAtom(rt, class_def->class_name, len, JS_ATOM_TYPE_STRING); + if (name == JS_ATOM_NULL) { + name = __JS_NewAtomInit(rt, class_def->class_name, len, JS_ATOM_TYPE_STRING); + if (name == JS_ATOM_NULL) + return -1; + } + ret = JS_NewClass1(rt, class_id, class_def, name); + JS_FreeAtomRT(rt, name); + return ret; +} + +// XXX: `buf` contains raw 8-bit data, no UTF-8 decoding is performed +// XXX: no special case for len == 0 +static JSValue js_new_string8_len(JSContext *ctx, const char *buf, int len) +{ + JSString *str; + str = js_alloc_string(ctx, len, 0); + if (!str) + return JS_EXCEPTION; + memcpy(str->u.str8, buf, len); + str->u.str8[len] = '\0'; + return JS_MKPTR(JS_TAG_STRING, str); +} + +// XXX: `buf` contains raw 8-bit data, no UTF-8 decoding is performed +// XXX: no special case for the empty string +static inline JSValue js_new_string8(JSContext *ctx, const char *str) +{ + return js_new_string8_len(ctx, str, strlen(str)); +} + +static JSValue js_new_string16_len(JSContext *ctx, const uint16_t *buf, int len) +{ + JSString *str; + str = js_alloc_string(ctx, len, 1); + if (!str) + return JS_EXCEPTION; + memcpy(str->u.str16, buf, len * 2); + return JS_MKPTR(JS_TAG_STRING, str); +} + +static JSValue js_new_string_char(JSContext *ctx, uint16_t c) +{ + if (c < 0x100) { + char ch8 = c; + return js_new_string8_len(ctx, &ch8, 1); + } else { + uint16_t ch16 = c; + return js_new_string16_len(ctx, &ch16, 1); + } +} + +static JSValue js_sub_string(JSContext *ctx, JSString *p, int start, int end) +{ + int len = end - start; + if (start == 0 && end == p->len) { + return js_dup(JS_MKPTR(JS_TAG_STRING, p)); + } + if (len <= 0) { + return JS_AtomToString(ctx, JS_ATOM_empty_string); + } + if (p->is_wide_char) { + JSString *str; + int i; + uint16_t c = 0; + for (i = start; i < end; i++) { + c |= p->u.str16[i]; + } + if (c > 0xFF) + return js_new_string16_len(ctx, p->u.str16 + start, len); + + str = js_alloc_string(ctx, len, 0); + if (!str) + return JS_EXCEPTION; + for (i = 0; i < len; i++) { + str->u.str8[i] = p->u.str16[start + i]; + } + str->u.str8[len] = '\0'; + return JS_MKPTR(JS_TAG_STRING, str); + } else { + return js_new_string8_len(ctx, (const char *)(p->u.str8 + start), len); + } +} + +typedef struct StringBuffer { + JSContext *ctx; + JSString *str; + int len; + int size; + int is_wide_char; + int error_status; +} StringBuffer; + +/* It is valid to call string_buffer_end() and all string_buffer functions even + if string_buffer_init() or another string_buffer function returns an error. + If the error_status is set, string_buffer_end() returns JS_EXCEPTION. + */ +static int string_buffer_init2(JSContext *ctx, StringBuffer *s, int size, + int is_wide) +{ + s->ctx = ctx; + s->size = size; + s->len = 0; + s->is_wide_char = is_wide; + s->error_status = 0; + s->str = js_alloc_string(ctx, size, is_wide); + if (unlikely(!s->str)) { + s->size = 0; + return s->error_status = -1; + } +#ifdef DUMP_LEAKS + /* the StringBuffer may reallocate the JSString, only link it at the end */ + list_del(&s->str->link); +#endif + return 0; +} + +static inline int string_buffer_init(JSContext *ctx, StringBuffer *s, int size) +{ + return string_buffer_init2(ctx, s, size, 0); +} + +static void string_buffer_free(StringBuffer *s) +{ + js_free(s->ctx, s->str); + s->str = NULL; +} + +static int string_buffer_set_error(StringBuffer *s) +{ + js_free(s->ctx, s->str); + s->str = NULL; + s->size = 0; + s->len = 0; + return s->error_status = -1; +} + +static no_inline int string_buffer_widen(StringBuffer *s, int size) +{ + JSString *str; + size_t slack; + int i; + + if (s->error_status) + return -1; + + str = js_realloc2(s->ctx, s->str, sizeof(JSString) + (size << 1), &slack); + if (!str) + return string_buffer_set_error(s); + size += slack >> 1; + for(i = s->len; i-- > 0;) { + str->u.str16[i] = str->u.str8[i]; + } + s->is_wide_char = 1; + s->size = size; + s->str = str; + return 0; +} + +static no_inline int string_buffer_realloc(StringBuffer *s, int new_len, int c) +{ + JSString *new_str; + int new_size; + size_t new_size_bytes, slack; + + if (s->error_status) + return -1; + + if (new_len > JS_STRING_LEN_MAX) { + JS_ThrowRangeError(s->ctx, "invalid string length"); + return string_buffer_set_error(s); + } + new_size = min_int(max_int(new_len, s->size * 3 / 2), JS_STRING_LEN_MAX); + if (!s->is_wide_char && c >= 0x100) { + return string_buffer_widen(s, new_size); + } + new_size_bytes = sizeof(JSString) + (new_size << s->is_wide_char) + 1 - s->is_wide_char; + new_str = js_realloc2(s->ctx, s->str, new_size_bytes, &slack); + if (!new_str) + return string_buffer_set_error(s); + new_size = min_int(new_size + (slack >> s->is_wide_char), JS_STRING_LEN_MAX); + s->size = new_size; + s->str = new_str; + return 0; +} + +static no_inline int string_buffer_putc_slow(StringBuffer *s, uint32_t c) +{ + if (unlikely(s->len >= s->size)) { + if (string_buffer_realloc(s, s->len + 1, c)) + return -1; + } + if (s->is_wide_char) { + s->str->u.str16[s->len++] = c; + } else if (c < 0x100) { + s->str->u.str8[s->len++] = c; + } else { + if (string_buffer_widen(s, s->size)) + return -1; + s->str->u.str16[s->len++] = c; + } + return 0; +} + +/* 0 <= c <= 0xff */ +static int string_buffer_putc8(StringBuffer *s, uint32_t c) +{ + if (unlikely(s->len >= s->size)) { + if (string_buffer_realloc(s, s->len + 1, c)) + return -1; + } + if (s->is_wide_char) { + s->str->u.str16[s->len++] = c; + } else { + s->str->u.str8[s->len++] = c; + } + return 0; +} + +/* 0 <= c <= 0xffff */ +static int string_buffer_putc16(StringBuffer *s, uint32_t c) +{ + if (likely(s->len < s->size)) { + if (s->is_wide_char) { + s->str->u.str16[s->len++] = c; + return 0; + } else if (c < 0x100) { + s->str->u.str8[s->len++] = c; + return 0; + } + } + return string_buffer_putc_slow(s, c); +} + +/* 0 <= c <= 0x10ffff */ +static int string_buffer_putc(StringBuffer *s, uint32_t c) +{ + if (unlikely(c >= 0x10000)) { + /* surrogate pair */ + if (string_buffer_putc16(s, get_hi_surrogate(c))) + return -1; + c = get_lo_surrogate(c); + } + return string_buffer_putc16(s, c); +} + +static int string_getc(const JSString *p, int *pidx) +{ + int idx, c, c1; + idx = *pidx; + if (p->is_wide_char) { + c = p->u.str16[idx++]; + if (is_hi_surrogate(c) && idx < p->len) { + c1 = p->u.str16[idx]; + if (is_lo_surrogate(c1)) { + c = from_surrogate(c, c1); + idx++; + } + } + } else { + c = p->u.str8[idx++]; + } + *pidx = idx; + return c; +} + +static int string_buffer_write8(StringBuffer *s, const uint8_t *p, int len) +{ + int i; + + if (s->len + len > s->size) { + if (string_buffer_realloc(s, s->len + len, 0)) + return -1; + } + if (s->is_wide_char) { + for (i = 0; i < len; i++) { + s->str->u.str16[s->len + i] = p[i]; + } + s->len += len; + } else { + memcpy(&s->str->u.str8[s->len], p, len); + s->len += len; + } + return 0; +} + +static int string_buffer_write16(StringBuffer *s, const uint16_t *p, int len) +{ + int c = 0, i; + + for (i = 0; i < len; i++) { + c |= p[i]; + } + if (s->len + len > s->size) { + if (string_buffer_realloc(s, s->len + len, c)) + return -1; + } else if (!s->is_wide_char && c >= 0x100) { + if (string_buffer_widen(s, s->size)) + return -1; + } + if (s->is_wide_char) { + memcpy(&s->str->u.str16[s->len], p, len << 1); + s->len += len; + } else { + for (i = 0; i < len; i++) { + s->str->u.str8[s->len + i] = p[i]; + } + s->len += len; + } + return 0; +} + +/* appending an ASCII string */ +static int string_buffer_puts8(StringBuffer *s, const char *str) +{ + return string_buffer_write8(s, (const uint8_t *)str, strlen(str)); +} + +static int string_buffer_concat(StringBuffer *s, const JSString *p, + uint32_t from, uint32_t to) +{ + if (to <= from) + return 0; + if (p->is_wide_char) + return string_buffer_write16(s, p->u.str16 + from, to - from); + else + return string_buffer_write8(s, p->u.str8 + from, to - from); +} + +static int string_buffer_concat_value(StringBuffer *s, JSValue v) +{ + JSString *p; + JSValue v1; + int res; + + if (s->error_status) { + /* prevent exception overload */ + return -1; + } + if (unlikely(JS_VALUE_GET_TAG(v) != JS_TAG_STRING)) { + v1 = JS_ToString(s->ctx, v); + if (JS_IsException(v1)) + return string_buffer_set_error(s); + p = JS_VALUE_GET_STRING(v1); + res = string_buffer_concat(s, p, 0, p->len); + JS_FreeValue(s->ctx, v1); + return res; + } + p = JS_VALUE_GET_STRING(v); + return string_buffer_concat(s, p, 0, p->len); +} + +static int string_buffer_concat_value_free(StringBuffer *s, JSValue v) +{ + JSString *p; + int res; + + if (s->error_status) { + /* prevent exception overload */ + JS_FreeValue(s->ctx, v); + return -1; + } + if (unlikely(JS_VALUE_GET_TAG(v) != JS_TAG_STRING)) { + v = JS_ToStringFree(s->ctx, v); + if (JS_IsException(v)) + return string_buffer_set_error(s); + } + p = JS_VALUE_GET_STRING(v); + res = string_buffer_concat(s, p, 0, p->len); + JS_FreeValue(s->ctx, v); + return res; +} + +static int string_buffer_fill(StringBuffer *s, int c, int count) +{ + /* XXX: optimize */ + if (s->len + count > s->size) { + if (string_buffer_realloc(s, s->len + count, c)) + return -1; + } + while (count-- > 0) { + if (string_buffer_putc16(s, c)) + return -1; + } + return 0; +} + +static JSValue string_buffer_end(StringBuffer *s) +{ + JSString *str; + str = s->str; + if (s->error_status) + return JS_EXCEPTION; + if (s->len == 0) { + js_free(s->ctx, str); + s->str = NULL; + return JS_AtomToString(s->ctx, JS_ATOM_empty_string); + } + if (s->len < s->size) { + /* smaller size so js_realloc should not fail, but OK if it does */ + /* XXX: should add some slack to avoid unnecessary calls */ + /* XXX: might need to use malloc+free to ensure smaller size */ + str = js_realloc_rt(s->ctx->rt, str, sizeof(JSString) + + (s->len << s->is_wide_char) + 1 - s->is_wide_char); + if (str == NULL) + str = s->str; + s->str = str; + } + if (!s->is_wide_char) + str->u.str8[s->len] = 0; +#ifdef DUMP_LEAKS + list_add_tail(&str->link, &s->ctx->rt->string_list); +#endif + str->is_wide_char = s->is_wide_char; + str->len = s->len; + s->str = NULL; + return JS_MKPTR(JS_TAG_STRING, str); +} + +/* create a string from a UTF-8 buffer */ +JSValue JS_NewStringLen(JSContext *ctx, const char *buf, size_t buf_len) +{ + JSString *str; + size_t len; + int kind; + + if (buf_len <= 0) { + return JS_AtomToString(ctx, JS_ATOM_empty_string); + } + /* Compute string kind and length: 7-bit, 8-bit, 16-bit, 16-bit UTF-16 */ + kind = utf8_scan(buf, buf_len, &len); + if (len > JS_STRING_LEN_MAX) + return JS_ThrowRangeError(ctx, "invalid string length"); + + switch (kind) { + case UTF8_PLAIN_ASCII: + str = js_alloc_string(ctx, len, 0); + if (!str) + return JS_EXCEPTION; + memcpy(str->u.str8, buf, len); + str->u.str8[len] = '\0'; + break; + case UTF8_NON_ASCII: + /* buf contains non-ASCII code-points, but limited to 8-bit values */ + str = js_alloc_string(ctx, len, 0); + if (!str) + return JS_EXCEPTION; + utf8_decode_buf8(str->u.str8, len + 1, buf, buf_len); + break; + default: + // This causes a potential problem in JS_ThrowError if message is invalid + //if (kind & UTF8_HAS_ERRORS) + // return JS_ThrowRangeError(ctx, "invalid UTF-8 sequence"); + str = js_alloc_string(ctx, len, 1); + if (!str) + return JS_EXCEPTION; + utf8_decode_buf16(str->u.str16, len, buf, buf_len); + break; + } + return JS_MKPTR(JS_TAG_STRING, str); +} + +static JSValue JS_ConcatString3(JSContext *ctx, const char *str1, + JSValue str2, const char *str3) +{ + StringBuffer b_s, *b = &b_s; + int len1, len3; + JSString *p; + + if (unlikely(JS_VALUE_GET_TAG(str2) != JS_TAG_STRING)) { + str2 = JS_ToStringFree(ctx, str2); + if (JS_IsException(str2)) + goto fail; + } + p = JS_VALUE_GET_STRING(str2); + len1 = strlen(str1); + len3 = strlen(str3); + + if (string_buffer_init2(ctx, b, len1 + p->len + len3, p->is_wide_char)) + goto fail; + + string_buffer_write8(b, (const uint8_t *)str1, len1); + string_buffer_concat(b, p, 0, p->len); + string_buffer_write8(b, (const uint8_t *)str3, len3); + + JS_FreeValue(ctx, str2); + return string_buffer_end(b); + + fail: + JS_FreeValue(ctx, str2); + return JS_EXCEPTION; +} + +/* `str` may be pure ASCII or UTF-8 encoded */ +JSValue JS_NewAtomString(JSContext *ctx, const char *str) +{ + JSAtom atom = JS_NewAtom(ctx, str); + if (atom == JS_ATOM_NULL) + return JS_EXCEPTION; + JSValue val = JS_AtomToString(ctx, atom); + JS_FreeAtom(ctx, atom); + return val; +} + +/* return (NULL, 0) if exception. */ +/* return pointer into a JSString with a live ref_count */ +/* cesu8 determines if non-BMP1 codepoints are encoded as 1 or 2 utf-8 sequences */ +const char *JS_ToCStringLen2(JSContext *ctx, size_t *plen, JSValue val1, BOOL cesu8) +{ + JSValue val; + JSString *str, *str_new; + int pos, len, c, c1; + JSObject *p; + uint8_t *q; + + if (JS_VALUE_GET_TAG(val1) == JS_TAG_STRING) { + val = js_dup(val1); + goto go; + } + + val = JS_ToString(ctx, val1); + if (!JS_IsException(val)) + goto go; + + // Stringification can fail when there is an exception pending, + // e.g. a stack overflow InternalError. Special-case exception + // objects to make debugging easier, look up the .message property + // and stringify that. + if (JS_VALUE_GET_TAG(val1) != JS_TAG_OBJECT) + goto fail; + + p = JS_VALUE_GET_OBJ(val1); + if (p->class_id != JS_CLASS_ERROR) + goto fail; + + val = JS_GetProperty(ctx, val1, JS_ATOM_message); + if (JS_VALUE_GET_TAG(val) != JS_TAG_STRING) { + JS_FreeValue(ctx, val); + goto fail; + } + +go: + str = JS_VALUE_GET_STRING(val); + len = str->len; + if (!str->is_wide_char) { + const uint8_t *src = str->u.str8; + int count; + + /* count the number of non-ASCII characters */ + /* Scanning the whole string is required for ASCII strings, + and computing the number of non-ASCII bytes is less expensive + than testing each byte, hence this method is faster for ASCII + strings, which is the most common case. + */ + count = 0; + for (pos = 0; pos < len; pos++) { + count += src[pos] >> 7; + } + if (count == 0) { + if (plen) + *plen = len; + return (const char *)src; + } + str_new = js_alloc_string(ctx, len + count, 0); + if (!str_new) + goto fail; + q = str_new->u.str8; + for (pos = 0; pos < len; pos++) { + c = src[pos]; + if (c < 0x80) { + *q++ = c; + } else { + *q++ = (c >> 6) | 0xc0; + *q++ = (c & 0x3f) | 0x80; + } + } + } else { + const uint16_t *src = str->u.str16; + /* Allocate 3 bytes per 16 bit code point. Surrogate pairs may + produce 4 bytes but use 2 code points. + */ + str_new = js_alloc_string(ctx, len * 3, 0); + if (!str_new) + goto fail; + q = str_new->u.str8; + pos = 0; + while (pos < len) { + c = src[pos++]; + if (c < 0x80) { + *q++ = c; + } else { + if (is_hi_surrogate(c)) { + if (pos < len && !cesu8) { + c1 = src[pos]; + if (is_lo_surrogate(c1)) { + pos++; + c = from_surrogate(c, c1); + } else { + /* Keep unmatched surrogate code points */ + /* c = 0xfffd; */ /* error */ + } + } else { + /* Keep unmatched surrogate code points */ + /* c = 0xfffd; */ /* error */ + } + } + q += utf8_encode(q, c); + } + } + } + + *q = '\0'; + str_new->len = q - str_new->u.str8; + JS_FreeValue(ctx, val); + if (plen) + *plen = str_new->len; + return (const char *)str_new->u.str8; + fail: + if (plen) + *plen = 0; + return NULL; +} + +void JS_FreeCString(JSContext *ctx, const char *ptr) +{ + JSString *p; + if (!ptr) + return; + /* purposely removing constness */ + p = container_of(ptr, JSString, u); + JS_FreeValue(ctx, JS_MKPTR(JS_TAG_STRING, p)); +} + +static int memcmp16_8(const uint16_t *src1, const uint8_t *src2, int len) +{ + int c, i; + for(i = 0; i < len; i++) { + c = src1[i] - src2[i]; + if (c != 0) + return c; + } + return 0; +} + +static int memcmp16(const uint16_t *src1, const uint16_t *src2, int len) +{ + int c, i; + for(i = 0; i < len; i++) { + c = src1[i] - src2[i]; + if (c != 0) + return c; + } + return 0; +} + +static int js_string_memcmp(const JSString *p1, const JSString *p2, int len) +{ + int res; + + if (likely(!p1->is_wide_char)) { + if (likely(!p2->is_wide_char)) + res = memcmp(p1->u.str8, p2->u.str8, len); + else + res = -memcmp16_8(p2->u.str16, p1->u.str8, len); + } else { + if (!p2->is_wide_char) + res = memcmp16_8(p1->u.str16, p2->u.str8, len); + else + res = memcmp16(p1->u.str16, p2->u.str16, len); + } + return res; +} + +/* return < 0, 0 or > 0 */ +static int js_string_compare(JSContext *ctx, + const JSString *p1, const JSString *p2) +{ + int res, len; + len = min_int(p1->len, p2->len); + res = js_string_memcmp(p1, p2, len); + if (res == 0) + res = compare_u32(p1->len, p2->len); + return res; +} + +static void copy_str16(uint16_t *dst, const JSString *p, int offset, int len) +{ + if (p->is_wide_char) { + memcpy(dst, p->u.str16 + offset, len * 2); + } else { + const uint8_t *src1 = p->u.str8 + offset; + int i; + + for(i = 0; i < len; i++) + dst[i] = src1[i]; + } +} + +static JSValue JS_ConcatString1(JSContext *ctx, + const JSString *p1, const JSString *p2) +{ + JSString *p; + uint32_t len; + int is_wide_char; + + len = p1->len + p2->len; + if (len > JS_STRING_LEN_MAX) + return JS_ThrowRangeError(ctx, "invalid string length"); + is_wide_char = p1->is_wide_char | p2->is_wide_char; + p = js_alloc_string(ctx, len, is_wide_char); + if (!p) + return JS_EXCEPTION; + if (!is_wide_char) { + memcpy(p->u.str8, p1->u.str8, p1->len); + memcpy(p->u.str8 + p1->len, p2->u.str8, p2->len); + p->u.str8[len] = '\0'; + } else { + copy_str16(p->u.str16, p1, 0, p1->len); + copy_str16(p->u.str16 + p1->len, p2, 0, p2->len); + } + return JS_MKPTR(JS_TAG_STRING, p); +} + +/* op1 and op2 are converted to strings. For convience, op1 or op2 = + JS_EXCEPTION are accepted and return JS_EXCEPTION. */ +static JSValue JS_ConcatString(JSContext *ctx, JSValue op1, JSValue op2) +{ + JSValue ret; + JSString *p1, *p2; + + if (unlikely(JS_VALUE_GET_TAG(op1) != JS_TAG_STRING)) { + op1 = JS_ToStringFree(ctx, op1); + if (JS_IsException(op1)) { + JS_FreeValue(ctx, op2); + return JS_EXCEPTION; + } + } + if (unlikely(JS_VALUE_GET_TAG(op2) != JS_TAG_STRING)) { + op2 = JS_ToStringFree(ctx, op2); + if (JS_IsException(op2)) { + JS_FreeValue(ctx, op1); + return JS_EXCEPTION; + } + } + p1 = JS_VALUE_GET_STRING(op1); + p2 = JS_VALUE_GET_STRING(op2); + + /* XXX: could also check if p1 is empty */ + if (p2->len == 0) { + goto ret_op1; + } + if (p1->header.ref_count == 1 && p1->is_wide_char == p2->is_wide_char + && js_malloc_usable_size(ctx, p1) >= sizeof(*p1) + ((p1->len + p2->len) << p2->is_wide_char) + 1 - p1->is_wide_char) { + /* Concatenate in place in available space at the end of p1 */ + if (p1->is_wide_char) { + memcpy(p1->u.str16 + p1->len, p2->u.str16, p2->len << 1); + p1->len += p2->len; + } else { + memcpy(p1->u.str8 + p1->len, p2->u.str8, p2->len); + p1->len += p2->len; + p1->u.str8[p1->len] = '\0'; + } + ret_op1: + JS_FreeValue(ctx, op2); + return op1; + } + ret = JS_ConcatString1(ctx, p1, p2); + JS_FreeValue(ctx, op1); + JS_FreeValue(ctx, op2); + return ret; +} + +/* Shape support */ + +static inline size_t get_shape_size(size_t hash_size, size_t prop_size) +{ + return hash_size * sizeof(uint32_t) + sizeof(JSShape) + + prop_size * sizeof(JSShapeProperty); +} + +static inline JSShape *get_shape_from_alloc(void *sh_alloc, size_t hash_size) +{ + return (JSShape *)(void *)((uint32_t *)sh_alloc + hash_size); +} + +static inline uint32_t *prop_hash_end(JSShape *sh) +{ + return (uint32_t *)sh; +} + +static inline void *get_alloc_from_shape(JSShape *sh) +{ + return prop_hash_end(sh) - ((intptr_t)sh->prop_hash_mask + 1); +} + +static inline JSShapeProperty *get_shape_prop(JSShape *sh) +{ + return sh->prop; +} + +static int init_shape_hash(JSRuntime *rt) +{ + rt->shape_hash_bits = 4; /* 16 shapes */ + rt->shape_hash_size = 1 << rt->shape_hash_bits; + rt->shape_hash_count = 0; + rt->shape_hash = js_mallocz_rt(rt, sizeof(rt->shape_hash[0]) * + rt->shape_hash_size); + if (!rt->shape_hash) + return -1; + return 0; +} + +/* same magic hash multiplier as the Linux kernel */ +static uint32_t shape_hash(uint32_t h, uint32_t val) +{ + return (h + val) * 0x9e370001; +} + +/* truncate the shape hash to 'hash_bits' bits */ +static uint32_t get_shape_hash(uint32_t h, int hash_bits) +{ + return h >> (32 - hash_bits); +} + +static uint32_t shape_initial_hash(JSObject *proto) +{ + uint32_t h; + h = shape_hash(1, (uintptr_t)proto); + if (sizeof(proto) > 4) + h = shape_hash(h, (uint64_t)(uintptr_t)proto >> 32); + return h; +} + +static int resize_shape_hash(JSRuntime *rt, int new_shape_hash_bits) +{ + int new_shape_hash_size, i; + uint32_t h; + JSShape **new_shape_hash, *sh, *sh_next; + + new_shape_hash_size = 1 << new_shape_hash_bits; + new_shape_hash = js_mallocz_rt(rt, sizeof(rt->shape_hash[0]) * + new_shape_hash_size); + if (!new_shape_hash) + return -1; + for(i = 0; i < rt->shape_hash_size; i++) { + for(sh = rt->shape_hash[i]; sh != NULL; sh = sh_next) { + sh_next = sh->shape_hash_next; + h = get_shape_hash(sh->hash, new_shape_hash_bits); + sh->shape_hash_next = new_shape_hash[h]; + new_shape_hash[h] = sh; + } + } + js_free_rt(rt, rt->shape_hash); + rt->shape_hash_bits = new_shape_hash_bits; + rt->shape_hash_size = new_shape_hash_size; + rt->shape_hash = new_shape_hash; + return 0; +} + +static void js_shape_hash_link(JSRuntime *rt, JSShape *sh) +{ + uint32_t h; + h = get_shape_hash(sh->hash, rt->shape_hash_bits); + sh->shape_hash_next = rt->shape_hash[h]; + rt->shape_hash[h] = sh; + rt->shape_hash_count++; +} + +static void js_shape_hash_unlink(JSRuntime *rt, JSShape *sh) +{ + uint32_t h; + JSShape **psh; + + h = get_shape_hash(sh->hash, rt->shape_hash_bits); + psh = &rt->shape_hash[h]; + while (*psh != sh) + psh = &(*psh)->shape_hash_next; + *psh = sh->shape_hash_next; + rt->shape_hash_count--; +} + +/* create a new empty shape with prototype 'proto' */ +static no_inline JSShape *js_new_shape2(JSContext *ctx, JSObject *proto, + int hash_size, int prop_size) +{ + JSRuntime *rt = ctx->rt; + void *sh_alloc; + JSShape *sh; + + /* resize the shape hash table if necessary */ + if (2 * (rt->shape_hash_count + 1) > rt->shape_hash_size) { + resize_shape_hash(rt, rt->shape_hash_bits + 1); + } + + sh_alloc = js_malloc(ctx, get_shape_size(hash_size, prop_size)); + if (!sh_alloc) + return NULL; + sh = get_shape_from_alloc(sh_alloc, hash_size); + sh->header.ref_count = 1; + add_gc_object(rt, &sh->header, JS_GC_OBJ_TYPE_SHAPE); + if (proto) + js_dup(JS_MKPTR(JS_TAG_OBJECT, proto)); + sh->proto = proto; + memset(prop_hash_end(sh) - hash_size, 0, sizeof(prop_hash_end(sh)[0]) * + hash_size); + sh->prop_hash_mask = hash_size - 1; + sh->prop_size = prop_size; + sh->prop_count = 0; + sh->deleted_prop_count = 0; + + /* insert in the hash table */ + sh->hash = shape_initial_hash(proto); + sh->is_hashed = TRUE; + sh->has_small_array_index = FALSE; + js_shape_hash_link(ctx->rt, sh); + return sh; +} + +static JSShape *js_new_shape(JSContext *ctx, JSObject *proto) +{ + return js_new_shape2(ctx, proto, JS_PROP_INITIAL_HASH_SIZE, + JS_PROP_INITIAL_SIZE); +} + +/* The shape is cloned. The new shape is not inserted in the shape + hash table */ +static JSShape *js_clone_shape(JSContext *ctx, JSShape *sh1) +{ + JSShape *sh; + void *sh_alloc, *sh_alloc1; + size_t size; + JSShapeProperty *pr; + uint32_t i, hash_size; + + hash_size = sh1->prop_hash_mask + 1; + size = get_shape_size(hash_size, sh1->prop_size); + sh_alloc = js_malloc(ctx, size); + if (!sh_alloc) + return NULL; + sh_alloc1 = get_alloc_from_shape(sh1); + memcpy(sh_alloc, sh_alloc1, size); + sh = get_shape_from_alloc(sh_alloc, hash_size); + sh->header.ref_count = 1; + add_gc_object(ctx->rt, &sh->header, JS_GC_OBJ_TYPE_SHAPE); + sh->is_hashed = FALSE; + if (sh->proto) { + js_dup(JS_MKPTR(JS_TAG_OBJECT, sh->proto)); + } + for(i = 0, pr = get_shape_prop(sh); i < sh->prop_count; i++, pr++) { + JS_DupAtom(ctx, pr->atom); + } + return sh; +} + +static JSShape *js_dup_shape(JSShape *sh) +{ + sh->header.ref_count++; + return sh; +} + +static void js_free_shape0(JSRuntime *rt, JSShape *sh) +{ + uint32_t i; + JSShapeProperty *pr; + + assert(sh->header.ref_count == 0); + if (sh->is_hashed) + js_shape_hash_unlink(rt, sh); + if (sh->proto != NULL) { + JS_FreeValueRT(rt, JS_MKPTR(JS_TAG_OBJECT, sh->proto)); + } + pr = get_shape_prop(sh); + for(i = 0; i < sh->prop_count; i++) { + JS_FreeAtomRT(rt, pr->atom); + pr++; + } + remove_gc_object(&sh->header); + js_free_rt(rt, get_alloc_from_shape(sh)); +} + +static void js_free_shape(JSRuntime *rt, JSShape *sh) +{ + if (unlikely(--sh->header.ref_count <= 0)) { + js_free_shape0(rt, sh); + } +} + +static void js_free_shape_null(JSRuntime *rt, JSShape *sh) +{ + if (sh) + js_free_shape(rt, sh); +} + +/* make space to hold at least 'count' properties */ +static no_inline int resize_properties(JSContext *ctx, JSShape **psh, + JSObject *p, uint32_t count) +{ + JSShape *sh; + uint32_t new_size, new_hash_size, new_hash_mask, i; + JSShapeProperty *pr; + void *sh_alloc; + intptr_t h; + + sh = *psh; + new_size = max_int(count, sh->prop_size * 3 / 2); + /* Reallocate prop array first to avoid crash or size inconsistency + in case of memory allocation failure */ + if (p) { + JSProperty *new_prop; + new_prop = js_realloc(ctx, p->prop, sizeof(new_prop[0]) * new_size); + if (unlikely(!new_prop)) + return -1; + p->prop = new_prop; + } + new_hash_size = sh->prop_hash_mask + 1; + while (new_hash_size < new_size) + new_hash_size = 2 * new_hash_size; + if (new_hash_size != (sh->prop_hash_mask + 1)) { + JSShape *old_sh; + /* resize the hash table and the properties */ + old_sh = sh; + sh_alloc = js_malloc(ctx, get_shape_size(new_hash_size, new_size)); + if (!sh_alloc) + return -1; + sh = get_shape_from_alloc(sh_alloc, new_hash_size); + list_del(&old_sh->header.link); + /* copy all the fields and the properties */ + memcpy(sh, old_sh, + sizeof(JSShape) + sizeof(sh->prop[0]) * old_sh->prop_count); + list_add_tail(&sh->header.link, &ctx->rt->gc_obj_list); + new_hash_mask = new_hash_size - 1; + sh->prop_hash_mask = new_hash_mask; + memset(prop_hash_end(sh) - new_hash_size, 0, + sizeof(prop_hash_end(sh)[0]) * new_hash_size); + for(i = 0, pr = sh->prop; i < sh->prop_count; i++, pr++) { + if (pr->atom != JS_ATOM_NULL) { + h = ((uintptr_t)pr->atom & new_hash_mask); + pr->hash_next = prop_hash_end(sh)[-h - 1]; + prop_hash_end(sh)[-h - 1] = i + 1; + } + } + js_free(ctx, get_alloc_from_shape(old_sh)); + } else { + /* only resize the properties */ + list_del(&sh->header.link); + sh_alloc = js_realloc(ctx, get_alloc_from_shape(sh), + get_shape_size(new_hash_size, new_size)); + if (unlikely(!sh_alloc)) { + /* insert again in the GC list */ + list_add_tail(&sh->header.link, &ctx->rt->gc_obj_list); + return -1; + } + sh = get_shape_from_alloc(sh_alloc, new_hash_size); + list_add_tail(&sh->header.link, &ctx->rt->gc_obj_list); + } + *psh = sh; + sh->prop_size = new_size; + return 0; +} + +/* remove the deleted properties. */ +static int compact_properties(JSContext *ctx, JSObject *p) +{ + JSShape *sh, *old_sh; + void *sh_alloc; + intptr_t h; + uint32_t new_hash_size, i, j, new_hash_mask, new_size; + JSShapeProperty *old_pr, *pr; + JSProperty *prop, *new_prop; + + sh = p->shape; + assert(!sh->is_hashed); + + new_size = max_int(JS_PROP_INITIAL_SIZE, + sh->prop_count - sh->deleted_prop_count); + assert(new_size <= sh->prop_size); + + new_hash_size = sh->prop_hash_mask + 1; + while ((new_hash_size / 2) >= new_size) + new_hash_size = new_hash_size / 2; + new_hash_mask = new_hash_size - 1; + + /* resize the hash table and the properties */ + old_sh = sh; + sh_alloc = js_malloc(ctx, get_shape_size(new_hash_size, new_size)); + if (!sh_alloc) + return -1; + sh = get_shape_from_alloc(sh_alloc, new_hash_size); + list_del(&old_sh->header.link); + memcpy(sh, old_sh, sizeof(JSShape)); + list_add_tail(&sh->header.link, &ctx->rt->gc_obj_list); + + memset(prop_hash_end(sh) - new_hash_size, 0, + sizeof(prop_hash_end(sh)[0]) * new_hash_size); + + j = 0; + old_pr = old_sh->prop; + pr = sh->prop; + prop = p->prop; + for(i = 0; i < sh->prop_count; i++) { + if (old_pr->atom != JS_ATOM_NULL) { + pr->atom = old_pr->atom; + pr->flags = old_pr->flags; + h = ((uintptr_t)old_pr->atom & new_hash_mask); + pr->hash_next = prop_hash_end(sh)[-h - 1]; + prop_hash_end(sh)[-h - 1] = j + 1; + prop[j] = prop[i]; + j++; + pr++; + } + old_pr++; + } + assert(j == (sh->prop_count - sh->deleted_prop_count)); + sh->prop_hash_mask = new_hash_mask; + sh->prop_size = new_size; + sh->deleted_prop_count = 0; + sh->prop_count = j; + + p->shape = sh; + js_free(ctx, get_alloc_from_shape(old_sh)); + + /* reduce the size of the object properties */ + new_prop = js_realloc(ctx, p->prop, sizeof(new_prop[0]) * new_size); + if (new_prop) + p->prop = new_prop; + return 0; +} + +static int add_shape_property(JSContext *ctx, JSShape **psh, + JSObject *p, JSAtom atom, int prop_flags) +{ + JSRuntime *rt = ctx->rt; + JSShape *sh = *psh; + JSShapeProperty *pr, *prop; + uint32_t hash_mask, new_shape_hash = 0; + intptr_t h; + + /* update the shape hash */ + if (sh->is_hashed) { + js_shape_hash_unlink(rt, sh); + new_shape_hash = shape_hash(shape_hash(sh->hash, atom), prop_flags); + } + + if (unlikely(sh->prop_count >= sh->prop_size)) { + if (resize_properties(ctx, psh, p, sh->prop_count + 1)) { + /* in case of error, reinsert in the hash table. + sh is still valid if resize_properties() failed */ + if (sh->is_hashed) + js_shape_hash_link(rt, sh); + return -1; + } + sh = *psh; + } + if (sh->is_hashed) { + sh->hash = new_shape_hash; + js_shape_hash_link(rt, sh); + } + /* Initialize the new shape property. + The object property at p->prop[sh->prop_count] is uninitialized */ + prop = get_shape_prop(sh); + pr = &prop[sh->prop_count++]; + pr->atom = JS_DupAtom(ctx, atom); + pr->flags = prop_flags; + sh->has_small_array_index |= __JS_AtomIsTaggedInt(atom); + /* add in hash table */ + hash_mask = sh->prop_hash_mask; + h = atom & hash_mask; + pr->hash_next = prop_hash_end(sh)[-h - 1]; + prop_hash_end(sh)[-h - 1] = sh->prop_count; + return 0; +} + +/* find a hashed empty shape matching the prototype. Return NULL if + not found */ +static JSShape *find_hashed_shape_proto(JSRuntime *rt, JSObject *proto) +{ + JSShape *sh1; + uint32_t h, h1; + + h = shape_initial_hash(proto); + h1 = get_shape_hash(h, rt->shape_hash_bits); + for(sh1 = rt->shape_hash[h1]; sh1 != NULL; sh1 = sh1->shape_hash_next) { + if (sh1->hash == h && + sh1->proto == proto && + sh1->prop_count == 0) { + return sh1; + } + } + return NULL; +} + +/* find a hashed shape matching sh + (prop, prop_flags). Return NULL if + not found */ +static JSShape *find_hashed_shape_prop(JSRuntime *rt, JSShape *sh, + JSAtom atom, int prop_flags) +{ + JSShape *sh1; + uint32_t h, h1, i, n; + + h = sh->hash; + h = shape_hash(h, atom); + h = shape_hash(h, prop_flags); + h1 = get_shape_hash(h, rt->shape_hash_bits); + for(sh1 = rt->shape_hash[h1]; sh1 != NULL; sh1 = sh1->shape_hash_next) { + /* we test the hash first so that the rest is done only if the + shapes really match */ + if (sh1->hash == h && + sh1->proto == sh->proto && + sh1->prop_count == ((n = sh->prop_count) + 1)) { + for(i = 0; i < n; i++) { + if (unlikely(sh1->prop[i].atom != sh->prop[i].atom) || + unlikely(sh1->prop[i].flags != sh->prop[i].flags)) + goto next; + } + if (unlikely(sh1->prop[n].atom != atom) || + unlikely(sh1->prop[n].flags != prop_flags)) + goto next; + return sh1; + } + next: ; + } + return NULL; +} + +static __maybe_unused void JS_DumpShape(JSRuntime *rt, int i, JSShape *sh) +{ + char atom_buf[ATOM_GET_STR_BUF_SIZE]; + int j; + + /* XXX: should output readable class prototype */ + printf("%5d %3d%c %14p %5d %5d", i, + sh->header.ref_count, " *"[sh->is_hashed], + (void *)sh->proto, sh->prop_size, sh->prop_count); + for(j = 0; j < sh->prop_count; j++) { + printf(" %s", JS_AtomGetStrRT(rt, atom_buf, sizeof(atom_buf), + sh->prop[j].atom)); + } + printf("\n"); +} + +static __maybe_unused void JS_DumpShapes(JSRuntime *rt) +{ + int i; + JSShape *sh; + struct list_head *el; + JSObject *p; + JSGCObjectHeader *gp; + + printf("JSShapes: {\n"); + printf("%5s %4s %14s %5s %5s %s\n", "SLOT", "REFS", "PROTO", "SIZE", "COUNT", "PROPS"); + for(i = 0; i < rt->shape_hash_size; i++) { + for(sh = rt->shape_hash[i]; sh != NULL; sh = sh->shape_hash_next) { + JS_DumpShape(rt, i, sh); + assert(sh->is_hashed); + } + } + /* dump non-hashed shapes */ + list_for_each(el, &rt->gc_obj_list) { + gp = list_entry(el, JSGCObjectHeader, link); + if (gp->gc_obj_type == JS_GC_OBJ_TYPE_JS_OBJECT) { + p = (JSObject *)gp; + if (!p->shape->is_hashed) { + JS_DumpShape(rt, -1, p->shape); + } + } + } + printf("}\n"); +} + +static JSValue JS_NewObjectFromShape(JSContext *ctx, JSShape *sh, JSClassID class_id) +{ + JSObject *p; + + js_trigger_gc(ctx->rt, sizeof(JSObject)); + p = js_malloc(ctx, sizeof(JSObject)); + if (unlikely(!p)) + goto fail; + p->class_id = class_id; + p->extensible = TRUE; + p->free_mark = 0; + p->is_exotic = 0; + p->fast_array = 0; + p->is_constructor = 0; + p->is_uncatchable_error = 0; + p->tmp_mark = 0; + p->is_HTMLDDA = 0; + p->first_weak_ref = NULL; + p->u.opaque = NULL; + p->shape = sh; + p->prop = js_malloc(ctx, sizeof(JSProperty) * sh->prop_size); + if (unlikely(!p->prop)) { + js_free(ctx, p); + fail: + js_free_shape(ctx->rt, sh); + return JS_EXCEPTION; + } + + switch(class_id) { + case JS_CLASS_OBJECT: + break; + case JS_CLASS_ARRAY: + { + JSProperty *pr; + p->is_exotic = 1; + p->fast_array = 1; + p->u.array.u.values = NULL; + p->u.array.count = 0; + p->u.array.u1.size = 0; + /* the length property is always the first one */ + if (likely(sh == ctx->array_shape)) { + pr = &p->prop[0]; + } else { + /* only used for the first array */ + /* cannot fail */ + pr = add_property(ctx, p, JS_ATOM_length, + JS_PROP_WRITABLE | JS_PROP_LENGTH); + } + pr->u.value = js_int32(0); + } + break; + case JS_CLASS_C_FUNCTION: + p->prop[0].u.value = JS_UNDEFINED; + break; + case JS_CLASS_ARGUMENTS: + case JS_CLASS_UINT8C_ARRAY: + case JS_CLASS_INT8_ARRAY: + case JS_CLASS_UINT8_ARRAY: + case JS_CLASS_INT16_ARRAY: + case JS_CLASS_UINT16_ARRAY: + case JS_CLASS_INT32_ARRAY: + case JS_CLASS_UINT32_ARRAY: + case JS_CLASS_BIG_INT64_ARRAY: + case JS_CLASS_BIG_UINT64_ARRAY: + case JS_CLASS_FLOAT16_ARRAY: + case JS_CLASS_FLOAT32_ARRAY: + case JS_CLASS_FLOAT64_ARRAY: + p->is_exotic = 1; + p->fast_array = 1; + p->u.array.u.ptr = NULL; + p->u.array.count = 0; + break; + case JS_CLASS_DATAVIEW: + p->u.array.u.ptr = NULL; + p->u.array.count = 0; + break; + case JS_CLASS_NUMBER: + case JS_CLASS_STRING: + case JS_CLASS_BOOLEAN: + case JS_CLASS_SYMBOL: + case JS_CLASS_DATE: + case JS_CLASS_BIG_INT: + p->u.object_data = JS_UNDEFINED; + goto set_exotic; + case JS_CLASS_REGEXP: + p->u.regexp.pattern = NULL; + p->u.regexp.bytecode = NULL; + goto set_exotic; + default: + set_exotic: + if (ctx->rt->class_array[class_id].exotic) { + p->is_exotic = 1; + } + break; + } + p->header.ref_count = 1; + add_gc_object(ctx->rt, &p->header, JS_GC_OBJ_TYPE_JS_OBJECT); + return JS_MKPTR(JS_TAG_OBJECT, p); +} + +static JSObject *get_proto_obj(JSValue proto_val) +{ + if (JS_VALUE_GET_TAG(proto_val) != JS_TAG_OBJECT) + return NULL; + else + return JS_VALUE_GET_OBJ(proto_val); +} + +/* WARNING: proto must be an object or JS_NULL */ +JSValue JS_NewObjectProtoClass(JSContext *ctx, JSValue proto_val, + JSClassID class_id) +{ + JSShape *sh; + JSObject *proto; + + proto = get_proto_obj(proto_val); + sh = find_hashed_shape_proto(ctx->rt, proto); + if (likely(sh)) { + sh = js_dup_shape(sh); + } else { + sh = js_new_shape(ctx, proto); + if (!sh) + return JS_EXCEPTION; + } + return JS_NewObjectFromShape(ctx, sh, class_id); +} + +static int JS_SetObjectData(JSContext *ctx, JSValue obj, JSValue val) +{ + JSObject *p; + + if (JS_VALUE_GET_TAG(obj) == JS_TAG_OBJECT) { + p = JS_VALUE_GET_OBJ(obj); + switch(p->class_id) { + case JS_CLASS_NUMBER: + case JS_CLASS_STRING: + case JS_CLASS_BOOLEAN: + case JS_CLASS_SYMBOL: + case JS_CLASS_DATE: + case JS_CLASS_BIG_INT: + JS_FreeValue(ctx, p->u.object_data); + p->u.object_data = val; + return 0; + } + } + JS_FreeValue(ctx, val); + if (!JS_IsException(obj)) + JS_ThrowTypeError(ctx, "invalid object type"); + return -1; +} + +JSValue JS_NewObjectClass(JSContext *ctx, int class_id) +{ + return JS_NewObjectProtoClass(ctx, ctx->class_proto[class_id], class_id); +} + +JSValue JS_NewObjectProto(JSContext *ctx, JSValue proto) +{ + return JS_NewObjectProtoClass(ctx, proto, JS_CLASS_OBJECT); +} + +JSValue JS_NewArray(JSContext *ctx) +{ + return JS_NewObjectFromShape(ctx, js_dup_shape(ctx->array_shape), + JS_CLASS_ARRAY); +} + +JSValue JS_NewObject(JSContext *ctx) +{ + /* inline JS_NewObjectClass(ctx, JS_CLASS_OBJECT); */ + return JS_NewObjectProtoClass(ctx, ctx->class_proto[JS_CLASS_OBJECT], JS_CLASS_OBJECT); +} + +static void js_function_set_properties(JSContext *ctx, JSValue func_obj, + JSAtom name, int len) +{ + /* ES6 feature non compatible with ES5.1: length is configurable */ + JS_DefinePropertyValue(ctx, func_obj, JS_ATOM_length, js_int32(len), + JS_PROP_CONFIGURABLE); + JS_DefinePropertyValue(ctx, func_obj, JS_ATOM_name, + JS_AtomToString(ctx, name), JS_PROP_CONFIGURABLE); +} + +static BOOL js_class_has_bytecode(JSClassID class_id) +{ + return (class_id == JS_CLASS_BYTECODE_FUNCTION || + class_id == JS_CLASS_GENERATOR_FUNCTION || + class_id == JS_CLASS_ASYNC_FUNCTION || + class_id == JS_CLASS_ASYNC_GENERATOR_FUNCTION); +} + +/* return NULL without exception if not a function or no bytecode */ +static JSFunctionBytecode *JS_GetFunctionBytecode(JSValue val) +{ + JSObject *p; + if (JS_VALUE_GET_TAG(val) != JS_TAG_OBJECT) + return NULL; + p = JS_VALUE_GET_OBJ(val); + if (!js_class_has_bytecode(p->class_id)) + return NULL; + return p->u.func.function_bytecode; +} + +static void js_method_set_home_object(JSContext *ctx, JSValue func_obj, + JSValue home_obj) +{ + JSObject *p, *p1; + JSFunctionBytecode *b; + + if (JS_VALUE_GET_TAG(func_obj) != JS_TAG_OBJECT) + return; + p = JS_VALUE_GET_OBJ(func_obj); + if (!js_class_has_bytecode(p->class_id)) + return; + b = p->u.func.function_bytecode; + if (b->need_home_object) { + p1 = p->u.func.home_object; + if (p1) { + JS_FreeValue(ctx, JS_MKPTR(JS_TAG_OBJECT, p1)); + } + if (JS_VALUE_GET_TAG(home_obj) == JS_TAG_OBJECT) + p1 = JS_VALUE_GET_OBJ(js_dup(home_obj)); + else + p1 = NULL; + p->u.func.home_object = p1; + } +} + +static JSValue js_get_function_name(JSContext *ctx, JSAtom name) +{ + JSValue name_str; + + name_str = JS_AtomToString(ctx, name); + if (JS_AtomSymbolHasDescription(ctx, name)) { + name_str = JS_ConcatString3(ctx, "[", name_str, "]"); + } + return name_str; +} + +/* Modify the name of a method according to the atom and + 'flags'. 'flags' is a bitmask of JS_PROP_HAS_GET and + JS_PROP_HAS_SET. Also set the home object of the method. + Return < 0 if exception. */ +static int js_method_set_properties(JSContext *ctx, JSValue func_obj, + JSAtom name, int flags, JSValue home_obj) +{ + JSValue name_str; + + name_str = js_get_function_name(ctx, name); + if (flags & JS_PROP_HAS_GET) { + name_str = JS_ConcatString3(ctx, "get ", name_str, ""); + } else if (flags & JS_PROP_HAS_SET) { + name_str = JS_ConcatString3(ctx, "set ", name_str, ""); + } + if (JS_IsException(name_str)) + return -1; + if (JS_DefinePropertyValue(ctx, func_obj, JS_ATOM_name, name_str, + JS_PROP_CONFIGURABLE) < 0) + return -1; + js_method_set_home_object(ctx, func_obj, home_obj); + return 0; +} + +/* Note: at least 'length' arguments will be readable in 'argv' */ +/* `name` may be NULL, pure ASCII or UTF-8 encoded */ +static JSValue JS_NewCFunction3(JSContext *ctx, JSCFunction *func, + const char *name, + int length, JSCFunctionEnum cproto, int magic, + JSValue proto_val) +{ + JSValue func_obj; + JSObject *p; + JSAtom name_atom; + + func_obj = JS_NewObjectProtoClass(ctx, proto_val, JS_CLASS_C_FUNCTION); + if (JS_IsException(func_obj)) + return func_obj; + p = JS_VALUE_GET_OBJ(func_obj); + p->u.cfunc.realm = JS_DupContext(ctx); + p->u.cfunc.c_function.generic = func; + p->u.cfunc.length = length; + p->u.cfunc.cproto = cproto; + p->u.cfunc.magic = magic; + p->is_constructor = (cproto == JS_CFUNC_constructor || + cproto == JS_CFUNC_constructor_magic || + cproto == JS_CFUNC_constructor_or_func || + cproto == JS_CFUNC_constructor_or_func_magic); + if (!name) + name = ""; + name_atom = JS_NewAtom(ctx, name); + js_function_set_properties(ctx, func_obj, name_atom, length); + JS_FreeAtom(ctx, name_atom); + return func_obj; +} + +/* Note: at least 'length' arguments will be readable in 'argv' */ +JSValue JS_NewCFunction2(JSContext *ctx, JSCFunction *func, + const char *name, + int length, JSCFunctionEnum cproto, int magic) +{ + return JS_NewCFunction3(ctx, func, name, length, cproto, magic, + ctx->function_proto); +} + +typedef struct JSCFunctionDataRecord { + JSCFunctionData *func; + uint8_t length; + uint8_t data_len; + uint16_t magic; + JSValue data[]; +} JSCFunctionDataRecord; + +static void js_c_function_data_finalizer(JSRuntime *rt, JSValue val) +{ + JSCFunctionDataRecord *s = JS_GetOpaque(val, JS_CLASS_C_FUNCTION_DATA); + int i; + + if (s) { + for(i = 0; i < s->data_len; i++) { + JS_FreeValueRT(rt, s->data[i]); + } + js_free_rt(rt, s); + } +} + +static void js_c_function_data_mark(JSRuntime *rt, JSValue val, + JS_MarkFunc *mark_func) +{ + JSCFunctionDataRecord *s = JS_GetOpaque(val, JS_CLASS_C_FUNCTION_DATA); + int i; + + if (s) { + for(i = 0; i < s->data_len; i++) { + JS_MarkValue(rt, s->data[i], mark_func); + } + } +} + +static JSValue js_c_function_data_call(JSContext *ctx, JSValue func_obj, + JSValue this_val, + int argc, JSValue *argv, int flags) +{ + JSCFunctionDataRecord *s = JS_GetOpaque(func_obj, JS_CLASS_C_FUNCTION_DATA); + JSValue *arg_buf; + int i; + + /* XXX: could add the function on the stack for debug */ + if (unlikely(argc < s->length)) { + arg_buf = alloca(sizeof(arg_buf[0]) * s->length); + for(i = 0; i < argc; i++) + arg_buf[i] = argv[i]; + for(i = argc; i < s->length; i++) + arg_buf[i] = JS_UNDEFINED; + } else { + arg_buf = argv; + } + + return s->func(ctx, this_val, argc, arg_buf, s->magic, s->data); +} + +JSValue JS_NewCFunctionData(JSContext *ctx, JSCFunctionData *func, + int length, int magic, int data_len, + JSValue *data) +{ + JSCFunctionDataRecord *s; + JSValue func_obj; + int i; + + func_obj = JS_NewObjectProtoClass(ctx, ctx->function_proto, + JS_CLASS_C_FUNCTION_DATA); + if (JS_IsException(func_obj)) + return func_obj; + s = js_malloc(ctx, sizeof(*s) + data_len * sizeof(JSValue)); + if (!s) { + JS_FreeValue(ctx, func_obj); + return JS_EXCEPTION; + } + s->func = func; + s->length = length; + s->data_len = data_len; + s->magic = magic; + for(i = 0; i < data_len; i++) + s->data[i] = js_dup(data[i]); + JS_SetOpaqueInternal(func_obj, s); + js_function_set_properties(ctx, func_obj, + JS_ATOM_empty_string, length); + return func_obj; +} + +static JSContext *js_autoinit_get_realm(JSProperty *pr) +{ + return (JSContext *)(pr->u.init.realm_and_id & ~3); +} + +static JSAutoInitIDEnum js_autoinit_get_id(JSProperty *pr) +{ + return pr->u.init.realm_and_id & 3; +} + +static void js_autoinit_free(JSRuntime *rt, JSProperty *pr) +{ + JS_FreeContext(js_autoinit_get_realm(pr)); +} + +static void js_autoinit_mark(JSRuntime *rt, JSProperty *pr, + JS_MarkFunc *mark_func) +{ + mark_func(rt, &js_autoinit_get_realm(pr)->header); +} + +static void free_property(JSRuntime *rt, JSProperty *pr, int prop_flags) +{ + if (unlikely(prop_flags & JS_PROP_TMASK)) { + if ((prop_flags & JS_PROP_TMASK) == JS_PROP_GETSET) { + if (pr->u.getset.getter) + JS_FreeValueRT(rt, JS_MKPTR(JS_TAG_OBJECT, pr->u.getset.getter)); + if (pr->u.getset.setter) + JS_FreeValueRT(rt, JS_MKPTR(JS_TAG_OBJECT, pr->u.getset.setter)); + } else if ((prop_flags & JS_PROP_TMASK) == JS_PROP_VARREF) { + free_var_ref(rt, pr->u.var_ref); + } else if ((prop_flags & JS_PROP_TMASK) == JS_PROP_AUTOINIT) { + js_autoinit_free(rt, pr); + } + } else { + JS_FreeValueRT(rt, pr->u.value); + } +} + +static force_inline JSShapeProperty *find_own_property1(JSObject *p, + JSAtom atom) +{ + JSShape *sh; + JSShapeProperty *pr, *prop; + intptr_t h; + sh = p->shape; + h = (uintptr_t)atom & sh->prop_hash_mask; + h = prop_hash_end(sh)[-h - 1]; + prop = get_shape_prop(sh); + while (h) { + pr = &prop[h - 1]; + if (likely(pr->atom == atom)) { + return pr; + } + h = pr->hash_next; + } + return NULL; +} + +static force_inline JSShapeProperty *find_own_property(JSProperty **ppr, + JSObject *p, + JSAtom atom) +{ + JSShape *sh; + JSShapeProperty *pr, *prop; + intptr_t h; + sh = p->shape; + h = (uintptr_t)atom & sh->prop_hash_mask; + h = prop_hash_end(sh)[-h - 1]; + prop = get_shape_prop(sh); + while (h) { + pr = &prop[h - 1]; + if (likely(pr->atom == atom)) { + *ppr = &p->prop[h - 1]; + /* the compiler should be able to assume that pr != NULL here */ + return pr; + } + h = pr->hash_next; + } + *ppr = NULL; + return NULL; +} + +static force_inline JSShapeProperty* find_own_property_ic(JSProperty** ppr, JSObject* p, + JSAtom atom, uint32_t* offset) +{ + JSShape* sh; + JSShapeProperty *pr, *prop; + intptr_t h, i; + sh = p->shape; + h = (uintptr_t)atom & sh->prop_hash_mask; + h = prop_hash_end(sh)[-h - 1]; + prop = get_shape_prop(sh); + while (h) { + i = h - 1; + pr = &prop[i]; + if (likely(pr->atom == atom)) { + *ppr = &p->prop[i]; + *offset = i; + /* the compiler should be able to assume that pr != NULL here */ + return pr; + } + h = pr->hash_next; + } + *ppr = NULL; + return NULL; +} + +/* indicate that the object may be part of a function prototype cycle */ +static void set_cycle_flag(JSContext *ctx, JSValue obj) +{ +} + +static void free_var_ref(JSRuntime *rt, JSVarRef *var_ref) +{ + if (var_ref) { + assert(var_ref->header.ref_count > 0); + if (--var_ref->header.ref_count == 0) { + if (var_ref->is_detached) { + JS_FreeValueRT(rt, var_ref->value); + remove_gc_object(&var_ref->header); + } else { + list_del(&var_ref->header.link); /* still on the stack */ + } + js_free_rt(rt, var_ref); + } + } +} + +static void js_array_finalizer(JSRuntime *rt, JSValue val) +{ + JSObject *p = JS_VALUE_GET_OBJ(val); + int i; + + for(i = 0; i < p->u.array.count; i++) { + JS_FreeValueRT(rt, p->u.array.u.values[i]); + } + js_free_rt(rt, p->u.array.u.values); +} + +static void js_array_mark(JSRuntime *rt, JSValue val, + JS_MarkFunc *mark_func) +{ + JSObject *p = JS_VALUE_GET_OBJ(val); + int i; + + for(i = 0; i < p->u.array.count; i++) { + JS_MarkValue(rt, p->u.array.u.values[i], mark_func); + } +} + +static void js_object_data_finalizer(JSRuntime *rt, JSValue val) +{ + JSObject *p = JS_VALUE_GET_OBJ(val); + JS_FreeValueRT(rt, p->u.object_data); + p->u.object_data = JS_UNDEFINED; +} + +static void js_object_data_mark(JSRuntime *rt, JSValue val, + JS_MarkFunc *mark_func) +{ + JSObject *p = JS_VALUE_GET_OBJ(val); + JS_MarkValue(rt, p->u.object_data, mark_func); +} + +static void js_c_function_finalizer(JSRuntime *rt, JSValue val) +{ + JSObject *p = JS_VALUE_GET_OBJ(val); + + if (p->u.cfunc.realm) + JS_FreeContext(p->u.cfunc.realm); +} + +static void js_c_function_mark(JSRuntime *rt, JSValue val, + JS_MarkFunc *mark_func) +{ + JSObject *p = JS_VALUE_GET_OBJ(val); + + if (p->u.cfunc.realm) + mark_func(rt, &p->u.cfunc.realm->header); +} + +static void js_bytecode_function_finalizer(JSRuntime *rt, JSValue val) +{ + JSObject *p1, *p = JS_VALUE_GET_OBJ(val); + JSFunctionBytecode *b; + JSVarRef **var_refs; + int i; + + p1 = p->u.func.home_object; + if (p1) { + JS_FreeValueRT(rt, JS_MKPTR(JS_TAG_OBJECT, p1)); + } + b = p->u.func.function_bytecode; + if (b) { + var_refs = p->u.func.var_refs; + if (var_refs) { + for(i = 0; i < b->closure_var_count; i++) + free_var_ref(rt, var_refs[i]); + js_free_rt(rt, var_refs); + } + JS_FreeValueRT(rt, JS_MKPTR(JS_TAG_FUNCTION_BYTECODE, b)); + } +} + +static void js_bytecode_function_mark(JSRuntime *rt, JSValue val, + JS_MarkFunc *mark_func) +{ + JSObject *p = JS_VALUE_GET_OBJ(val); + JSVarRef **var_refs = p->u.func.var_refs; + JSFunctionBytecode *b = p->u.func.function_bytecode; + int i; + + if (p->u.func.home_object) { + JS_MarkValue(rt, JS_MKPTR(JS_TAG_OBJECT, p->u.func.home_object), + mark_func); + } + if (b) { + if (var_refs) { + for(i = 0; i < b->closure_var_count; i++) { + JSVarRef *var_ref = var_refs[i]; + if (var_ref && var_ref->is_detached) { + mark_func(rt, &var_ref->header); + } + } + } + /* must mark the function bytecode because template objects may be + part of a cycle */ + JS_MarkValue(rt, JS_MKPTR(JS_TAG_FUNCTION_BYTECODE, b), mark_func); + } +} + +static void js_bound_function_finalizer(JSRuntime *rt, JSValue val) +{ + JSObject *p = JS_VALUE_GET_OBJ(val); + JSBoundFunction *bf = p->u.bound_function; + int i; + + JS_FreeValueRT(rt, bf->func_obj); + JS_FreeValueRT(rt, bf->this_val); + for(i = 0; i < bf->argc; i++) { + JS_FreeValueRT(rt, bf->argv[i]); + } + js_free_rt(rt, bf); +} + +static void js_bound_function_mark(JSRuntime *rt, JSValue val, + JS_MarkFunc *mark_func) +{ + JSObject *p = JS_VALUE_GET_OBJ(val); + JSBoundFunction *bf = p->u.bound_function; + int i; + + JS_MarkValue(rt, bf->func_obj, mark_func); + JS_MarkValue(rt, bf->this_val, mark_func); + for(i = 0; i < bf->argc; i++) + JS_MarkValue(rt, bf->argv[i], mark_func); +} + +static void js_for_in_iterator_finalizer(JSRuntime *rt, JSValue val) +{ + JSObject *p = JS_VALUE_GET_OBJ(val); + JSForInIterator *it = p->u.for_in_iterator; + JS_FreeValueRT(rt, it->obj); + js_free_rt(rt, it); +} + +static void js_for_in_iterator_mark(JSRuntime *rt, JSValue val, + JS_MarkFunc *mark_func) +{ + JSObject *p = JS_VALUE_GET_OBJ(val); + JSForInIterator *it = p->u.for_in_iterator; + JS_MarkValue(rt, it->obj, mark_func); +} + +static void free_object(JSRuntime *rt, JSObject *p) +{ + int i; + JSClassFinalizer *finalizer; + JSShape *sh; + JSShapeProperty *pr; + + p->free_mark = 1; /* used to tell the object is invalid when + freeing cycles */ + /* free all the fields */ + sh = p->shape; + pr = get_shape_prop(sh); + for(i = 0; i < sh->prop_count; i++) { + free_property(rt, &p->prop[i], pr->flags); + pr++; + } + js_free_rt(rt, p->prop); + /* as an optimization we destroy the shape immediately without + putting it in gc_zero_ref_count_list */ + js_free_shape(rt, sh); + + /* fail safe */ + p->shape = NULL; + p->prop = NULL; + + if (unlikely(p->first_weak_ref)) { + reset_weak_ref(rt, &p->first_weak_ref); + } + + finalizer = rt->class_array[p->class_id].finalizer; + if (finalizer) + (*finalizer)(rt, JS_MKPTR(JS_TAG_OBJECT, p)); + + /* fail safe */ + p->class_id = 0; + p->u.opaque = NULL; + p->u.func.var_refs = NULL; + p->u.func.home_object = NULL; + + remove_gc_object(&p->header); + if (rt->gc_phase == JS_GC_PHASE_REMOVE_CYCLES && p->header.ref_count != 0) { + list_add_tail(&p->header.link, &rt->gc_zero_ref_count_list); + } else { + js_free_rt(rt, p); + } +} + +static void free_gc_object(JSRuntime *rt, JSGCObjectHeader *gp) +{ + switch(gp->gc_obj_type) { + case JS_GC_OBJ_TYPE_JS_OBJECT: + free_object(rt, (JSObject *)gp); + break; + case JS_GC_OBJ_TYPE_FUNCTION_BYTECODE: + free_function_bytecode(rt, (JSFunctionBytecode *)gp); + break; + default: + abort(); + } +} + +static void free_zero_refcount(JSRuntime *rt) +{ + struct list_head *el; + JSGCObjectHeader *p; + + rt->gc_phase = JS_GC_PHASE_DECREF; + for(;;) { + el = rt->gc_zero_ref_count_list.next; + if (el == &rt->gc_zero_ref_count_list) + break; + p = list_entry(el, JSGCObjectHeader, link); + assert(p->ref_count == 0); + free_gc_object(rt, p); + } + rt->gc_phase = JS_GC_PHASE_NONE; +} + +/* called with the ref_count of 'v' reaches zero. */ +static void js_free_value_rt(JSRuntime *rt, JSValue v) +{ + uint32_t tag = JS_VALUE_GET_TAG(v); + +#ifdef DUMP_FREE + if (check_dump_flag(rt, DUMP_FREE)) { + /* Prevent invalid object access during GC */ + if ((rt->gc_phase != JS_GC_PHASE_REMOVE_CYCLES) + || (tag != JS_TAG_OBJECT && tag != JS_TAG_FUNCTION_BYTECODE)) { + printf("Freeing "); + if (tag == JS_TAG_OBJECT) { + JS_DumpObject(rt, JS_VALUE_GET_OBJ(v)); + } else { + JS_DumpValue(rt, v); + printf("\n"); + } + } + } +#endif + + switch(tag) { + case JS_TAG_STRING: + { + JSString *p = JS_VALUE_GET_STRING(v); + if (p->atom_type) { + JS_FreeAtomStruct(rt, p); + } else { +#ifdef DUMP_LEAKS + list_del(&p->link); +#endif + js_free_rt(rt, p); + } + } + break; + case JS_TAG_OBJECT: + case JS_TAG_FUNCTION_BYTECODE: + { + JSGCObjectHeader *p = JS_VALUE_GET_PTR(v); + if (rt->gc_phase != JS_GC_PHASE_REMOVE_CYCLES) { + list_del(&p->link); + list_add(&p->link, &rt->gc_zero_ref_count_list); + if (rt->gc_phase == JS_GC_PHASE_NONE) { + free_zero_refcount(rt); + } + } + } + break; + case JS_TAG_MODULE: + abort(); /* never freed here */ + break; + case JS_TAG_BIG_INT: + { + JSBigInt *bf = JS_VALUE_GET_PTR(v); + bf_delete(&bf->num); + js_free_rt(rt, bf); + } + break; + case JS_TAG_SYMBOL: + { + JSAtomStruct *p = JS_VALUE_GET_PTR(v); + JS_FreeAtomStruct(rt, p); + } + break; + default: + printf("js_free_value_rt: unknown tag=%d\n", tag); + abort(); + } +} + +void JS_FreeValueRT(JSRuntime *rt, JSValue v) +{ + if (JS_VALUE_HAS_REF_COUNT(v)) { + JSRefCountHeader *p = (JSRefCountHeader *)JS_VALUE_GET_PTR(v); + if (--p->ref_count <= 0) { + js_free_value_rt(rt, v); + } + } +} + +void JS_FreeValue(JSContext *ctx, JSValue v) +{ + JS_FreeValueRT(ctx->rt, v); +} + +/* garbage collection */ + +static void add_gc_object(JSRuntime *rt, JSGCObjectHeader *h, + JSGCObjectTypeEnum type) +{ + h->mark = 0; + h->gc_obj_type = type; + list_add_tail(&h->link, &rt->gc_obj_list); +} + +static void remove_gc_object(JSGCObjectHeader *h) +{ + list_del(&h->link); +} + +void JS_MarkValue(JSRuntime *rt, JSValue val, JS_MarkFunc *mark_func) +{ + if (JS_VALUE_HAS_REF_COUNT(val)) { + switch(JS_VALUE_GET_TAG(val)) { + case JS_TAG_OBJECT: + case JS_TAG_FUNCTION_BYTECODE: + mark_func(rt, JS_VALUE_GET_PTR(val)); + break; + default: + break; + } + } +} + +static void mark_children(JSRuntime *rt, JSGCObjectHeader *gp, + JS_MarkFunc *mark_func) +{ + switch(gp->gc_obj_type) { + case JS_GC_OBJ_TYPE_JS_OBJECT: + { + JSObject *p = (JSObject *)gp; + JSShapeProperty *prs; + JSShape *sh; + int i; + sh = p->shape; + mark_func(rt, &sh->header); + /* mark all the fields */ + prs = get_shape_prop(sh); + for(i = 0; i < sh->prop_count; i++) { + JSProperty *pr = &p->prop[i]; + if (prs->atom != JS_ATOM_NULL) { + if (prs->flags & JS_PROP_TMASK) { + if ((prs->flags & JS_PROP_TMASK) == JS_PROP_GETSET) { + if (pr->u.getset.getter) + mark_func(rt, &pr->u.getset.getter->header); + if (pr->u.getset.setter) + mark_func(rt, &pr->u.getset.setter->header); + } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF) { + if (pr->u.var_ref->is_detached) { + /* Note: the tag does not matter + provided it is a GC object */ + mark_func(rt, &pr->u.var_ref->header); + } + } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_AUTOINIT) { + js_autoinit_mark(rt, pr, mark_func); + } + } else { + JS_MarkValue(rt, pr->u.value, mark_func); + } + } + prs++; + } + + if (p->class_id != JS_CLASS_OBJECT) { + JSClassGCMark *gc_mark; + gc_mark = rt->class_array[p->class_id].gc_mark; + if (gc_mark) + gc_mark(rt, JS_MKPTR(JS_TAG_OBJECT, p), mark_func); + } + } + break; + case JS_GC_OBJ_TYPE_FUNCTION_BYTECODE: + /* the template objects can be part of a cycle */ + { + JSShape **shape, *(*shapes)[IC_CACHE_ITEM_CAPACITY]; + JSFunctionBytecode *b = (JSFunctionBytecode *)gp; + int i; + for(i = 0; i < b->cpool_count; i++) { + JS_MarkValue(rt, b->cpool[i], mark_func); + } + if (b->realm) + mark_func(rt, &b->realm->header); + if (b->ic) { + for (i = 0; i < b->ic->count; i++) { + shapes = &b->ic->cache[i].shape; + for (shape = *shapes; shape != endof(*shapes); shape++) + if (*shape) + mark_func(rt, &(*shape)->header); + } + } + } + break; + case JS_GC_OBJ_TYPE_VAR_REF: + { + JSVarRef *var_ref = (JSVarRef *)gp; + /* only detached variable referenced are taken into account */ + assert(var_ref->is_detached); + JS_MarkValue(rt, *var_ref->pvalue, mark_func); + } + break; + case JS_GC_OBJ_TYPE_ASYNC_FUNCTION: + { + JSAsyncFunctionData *s = (JSAsyncFunctionData *)gp; + if (s->is_active) + async_func_mark(rt, &s->func_state, mark_func); + JS_MarkValue(rt, s->resolving_funcs[0], mark_func); + JS_MarkValue(rt, s->resolving_funcs[1], mark_func); + } + break; + case JS_GC_OBJ_TYPE_SHAPE: + { + JSShape *sh = (JSShape *)gp; + if (sh->proto != NULL) { + mark_func(rt, &sh->proto->header); + } + } + break; + case JS_GC_OBJ_TYPE_JS_CONTEXT: + { + JSContext *ctx = (JSContext *)gp; + JS_MarkContext(rt, ctx, mark_func); + } + break; + default: + abort(); + } +} + +static void gc_decref_child(JSRuntime *rt, JSGCObjectHeader *p) +{ + assert(p->ref_count > 0); + p->ref_count--; + if (p->ref_count == 0 && p->mark == 1) { + list_del(&p->link); + list_add_tail(&p->link, &rt->tmp_obj_list); + } +} + +static void gc_decref(JSRuntime *rt) +{ + struct list_head *el, *el1; + JSGCObjectHeader *p; + + init_list_head(&rt->tmp_obj_list); + + /* decrement the refcount of all the children of all the GC + objects and move the GC objects with zero refcount to + tmp_obj_list */ + list_for_each_safe(el, el1, &rt->gc_obj_list) { + p = list_entry(el, JSGCObjectHeader, link); + assert(p->mark == 0); + mark_children(rt, p, gc_decref_child); + p->mark = 1; + if (p->ref_count == 0) { + list_del(&p->link); + list_add_tail(&p->link, &rt->tmp_obj_list); + } + } +} + +static void gc_scan_incref_child(JSRuntime *rt, JSGCObjectHeader *p) +{ + p->ref_count++; + if (p->ref_count == 1) { + /* ref_count was 0: remove from tmp_obj_list and add at the + end of gc_obj_list */ + list_del(&p->link); + list_add_tail(&p->link, &rt->gc_obj_list); + p->mark = 0; /* reset the mark for the next GC call */ + } +} + +static void gc_scan_incref_child2(JSRuntime *rt, JSGCObjectHeader *p) +{ + p->ref_count++; +} + +static void gc_scan(JSRuntime *rt) +{ + struct list_head *el; + JSGCObjectHeader *p; + + /* keep the objects with a refcount > 0 and their children. */ + list_for_each(el, &rt->gc_obj_list) { + p = list_entry(el, JSGCObjectHeader, link); + assert(p->ref_count > 0); + p->mark = 0; /* reset the mark for the next GC call */ + mark_children(rt, p, gc_scan_incref_child); + } + + /* restore the refcount of the objects to be deleted. */ + list_for_each(el, &rt->tmp_obj_list) { + p = list_entry(el, JSGCObjectHeader, link); + mark_children(rt, p, gc_scan_incref_child2); + } +} + +static void gc_free_cycles(JSRuntime *rt) +{ + struct list_head *el, *el1; + JSGCObjectHeader *p; +#ifdef DUMP_GC_FREE + BOOL header_done = FALSE; +#endif + + rt->gc_phase = JS_GC_PHASE_REMOVE_CYCLES; + + for(;;) { + el = rt->tmp_obj_list.next; + if (el == &rt->tmp_obj_list) + break; + p = list_entry(el, JSGCObjectHeader, link); + /* Only need to free the GC object associated with JS + values. The rest will be automatically removed because they + must be referenced by them. */ + switch(p->gc_obj_type) { + case JS_GC_OBJ_TYPE_JS_OBJECT: + case JS_GC_OBJ_TYPE_FUNCTION_BYTECODE: +#ifdef DUMP_GC_FREE + if (check_dump_flag(rt, DUMP_GC_FREE)) { + if (!header_done) { + printf("Freeing cycles:\n"); + JS_DumpObjectHeader(rt); + header_done = TRUE; + } + JS_DumpGCObject(rt, p); + } +#endif + free_gc_object(rt, p); + break; + default: + list_del(&p->link); + list_add_tail(&p->link, &rt->gc_zero_ref_count_list); + break; + } + } + rt->gc_phase = JS_GC_PHASE_NONE; + + list_for_each_safe(el, el1, &rt->gc_zero_ref_count_list) { + p = list_entry(el, JSGCObjectHeader, link); + assert(p->gc_obj_type == JS_GC_OBJ_TYPE_JS_OBJECT || + p->gc_obj_type == JS_GC_OBJ_TYPE_FUNCTION_BYTECODE); + js_free_rt(rt, p); + } + + init_list_head(&rt->gc_zero_ref_count_list); +} + +void JS_RunGC(JSRuntime *rt) +{ + /* decrement the reference of the children of each object. mark = + 1 after this pass. */ + gc_decref(rt); + + /* keep the GC objects with a non zero refcount and their childs */ + gc_scan(rt); + + /* free the GC objects in a cycle */ + gc_free_cycles(rt); +} + +/* Return false if not an object or if the object has already been + freed (zombie objects are visible in finalizers when freeing + cycles). */ +BOOL JS_IsLiveObject(JSRuntime *rt, JSValue obj) +{ + JSObject *p; + if (!JS_IsObject(obj)) + return FALSE; + p = JS_VALUE_GET_OBJ(obj); + return !p->free_mark; +} + +/* Compute memory used by various object types */ +/* XXX: poor man's approach to handling multiply referenced objects */ +typedef struct JSMemoryUsage_helper { + double memory_used_count; + double str_count; + double str_size; + int64_t js_func_count; + double js_func_size; + int64_t js_func_code_size; + int64_t js_func_pc2line_count; + int64_t js_func_pc2line_size; +} JSMemoryUsage_helper; + +static void compute_value_size(JSValue val, JSMemoryUsage_helper *hp); + +static void compute_jsstring_size(JSString *str, JSMemoryUsage_helper *hp) +{ + if (!str->atom_type) { /* atoms are handled separately */ + double s_ref_count = str->header.ref_count; + hp->str_count += 1 / s_ref_count; + hp->str_size += ((sizeof(*str) + (str->len << str->is_wide_char) + + 1 - str->is_wide_char) / s_ref_count); + } +} + +static void compute_bytecode_size(JSFunctionBytecode *b, JSMemoryUsage_helper *hp) +{ + int memory_used_count, js_func_size, i; + + memory_used_count = 0; + js_func_size = sizeof(*b); + if (b->vardefs) { + js_func_size += (b->arg_count + b->var_count) * sizeof(*b->vardefs); + } + if (b->cpool) { + js_func_size += b->cpool_count * sizeof(*b->cpool); + for (i = 0; i < b->cpool_count; i++) { + JSValue val = b->cpool[i]; + compute_value_size(val, hp); + } + } + if (b->closure_var) { + js_func_size += b->closure_var_count * sizeof(*b->closure_var); + } + if (b->byte_code_buf) { + hp->js_func_code_size += b->byte_code_len; + } + memory_used_count++; + js_func_size += b->source_len + 1; + if (b->pc2line_len) { + memory_used_count++; + hp->js_func_pc2line_count += 1; + hp->js_func_pc2line_size += b->pc2line_len; + } + hp->js_func_size += js_func_size; + hp->js_func_count += 1; + hp->memory_used_count += memory_used_count; +} + +static void compute_value_size(JSValue val, JSMemoryUsage_helper *hp) +{ + switch(JS_VALUE_GET_TAG(val)) { + case JS_TAG_STRING: + compute_jsstring_size(JS_VALUE_GET_STRING(val), hp); + break; + case JS_TAG_BIG_INT: + /* should track JSBigInt usage */ + break; + } +} + +void JS_ComputeMemoryUsage(JSRuntime *rt, JSMemoryUsage *s) +{ + struct list_head *el, *el1; + int i; + JSMemoryUsage_helper mem = { 0 }, *hp = &mem; + + memset(s, 0, sizeof(*s)); + s->malloc_count = rt->malloc_state.malloc_count; + s->malloc_size = rt->malloc_state.malloc_size; + s->malloc_limit = rt->malloc_state.malloc_limit; + + s->memory_used_count = 2; /* rt + rt->class_array */ + s->memory_used_size = sizeof(JSRuntime) + sizeof(JSClass) * rt->class_count; + + list_for_each(el, &rt->context_list) { + JSContext *ctx = list_entry(el, JSContext, link); + JSShape *sh = ctx->array_shape; + s->memory_used_count += 2; /* ctx + ctx->class_proto */ + s->memory_used_size += sizeof(JSContext) + + sizeof(JSValue) * rt->class_count; + s->binary_object_count += ctx->binary_object_count; + s->binary_object_size += ctx->binary_object_size; + + /* the hashed shapes are counted separately */ + if (sh && !sh->is_hashed) { + int hash_size = sh->prop_hash_mask + 1; + s->shape_count++; + s->shape_size += get_shape_size(hash_size, sh->prop_size); + } + list_for_each(el1, &ctx->loaded_modules) { + JSModuleDef *m = list_entry(el1, JSModuleDef, link); + s->memory_used_count += 1; + s->memory_used_size += sizeof(*m); + if (m->req_module_entries) { + s->memory_used_count += 1; + s->memory_used_size += m->req_module_entries_count * sizeof(*m->req_module_entries); + } + if (m->export_entries) { + s->memory_used_count += 1; + s->memory_used_size += m->export_entries_count * sizeof(*m->export_entries); + for (i = 0; i < m->export_entries_count; i++) { + JSExportEntry *me = &m->export_entries[i]; + if (me->export_type == JS_EXPORT_TYPE_LOCAL && me->u.local.var_ref) { + /* potential multiple count */ + s->memory_used_count += 1; + compute_value_size(me->u.local.var_ref->value, hp); + } + } + } + if (m->star_export_entries) { + s->memory_used_count += 1; + s->memory_used_size += m->star_export_entries_count * sizeof(*m->star_export_entries); + } + if (m->import_entries) { + s->memory_used_count += 1; + s->memory_used_size += m->import_entries_count * sizeof(*m->import_entries); + } + compute_value_size(m->module_ns, hp); + compute_value_size(m->func_obj, hp); + } + } + + list_for_each(el, &rt->gc_obj_list) { + JSGCObjectHeader *gp = list_entry(el, JSGCObjectHeader, link); + JSObject *p; + JSShape *sh; + JSShapeProperty *prs; + + /* XXX: could count the other GC object types too */ + if (gp->gc_obj_type == JS_GC_OBJ_TYPE_FUNCTION_BYTECODE) { + compute_bytecode_size((JSFunctionBytecode *)gp, hp); + continue; + } else if (gp->gc_obj_type != JS_GC_OBJ_TYPE_JS_OBJECT) { + continue; + } + p = (JSObject *)gp; + sh = p->shape; + s->obj_count++; + if (p->prop) { + s->memory_used_count++; + s->prop_size += sh->prop_size * sizeof(*p->prop); + s->prop_count += sh->prop_count; + prs = get_shape_prop(sh); + for(i = 0; i < sh->prop_count; i++) { + JSProperty *pr = &p->prop[i]; + if (prs->atom != JS_ATOM_NULL && !(prs->flags & JS_PROP_TMASK)) { + compute_value_size(pr->u.value, hp); + } + prs++; + } + } + /* the hashed shapes are counted separately */ + if (!sh->is_hashed) { + int hash_size = sh->prop_hash_mask + 1; + s->shape_count++; + s->shape_size += get_shape_size(hash_size, sh->prop_size); + } + + switch(p->class_id) { + case JS_CLASS_ARRAY: /* u.array | length */ + case JS_CLASS_ARGUMENTS: /* u.array | length */ + s->array_count++; + if (p->fast_array) { + s->fast_array_count++; + if (p->u.array.u.values) { + s->memory_used_count++; + s->memory_used_size += p->u.array.count * + sizeof(*p->u.array.u.values); + s->fast_array_elements += p->u.array.count; + for (i = 0; i < p->u.array.count; i++) { + compute_value_size(p->u.array.u.values[i], hp); + } + } + } + break; + case JS_CLASS_NUMBER: /* u.object_data */ + case JS_CLASS_STRING: /* u.object_data */ + case JS_CLASS_BOOLEAN: /* u.object_data */ + case JS_CLASS_SYMBOL: /* u.object_data */ + case JS_CLASS_DATE: /* u.object_data */ + case JS_CLASS_BIG_INT: /* u.object_data */ + compute_value_size(p->u.object_data, hp); + break; + case JS_CLASS_C_FUNCTION: /* u.cfunc */ + s->c_func_count++; + break; + case JS_CLASS_BYTECODE_FUNCTION: /* u.func */ + { + JSFunctionBytecode *b = p->u.func.function_bytecode; + JSVarRef **var_refs = p->u.func.var_refs; + /* home_object: object will be accounted for in list scan */ + if (var_refs) { + s->memory_used_count++; + s->js_func_size += b->closure_var_count * sizeof(*var_refs); + for (i = 0; i < b->closure_var_count; i++) { + if (var_refs[i]) { + double ref_count = var_refs[i]->header.ref_count; + s->memory_used_count += 1 / ref_count; + s->js_func_size += sizeof(*var_refs[i]) / ref_count; + /* handle non object closed values */ + if (var_refs[i]->pvalue == &var_refs[i]->value) { + /* potential multiple count */ + compute_value_size(var_refs[i]->value, hp); + } + } + } + } + } + break; + case JS_CLASS_BOUND_FUNCTION: /* u.bound_function */ + { + JSBoundFunction *bf = p->u.bound_function; + /* func_obj and this_val are objects */ + for (i = 0; i < bf->argc; i++) { + compute_value_size(bf->argv[i], hp); + } + s->memory_used_count += 1; + s->memory_used_size += sizeof(*bf) + bf->argc * sizeof(*bf->argv); + } + break; + case JS_CLASS_C_FUNCTION_DATA: /* u.c_function_data_record */ + { + JSCFunctionDataRecord *fd = p->u.c_function_data_record; + if (fd) { + for (i = 0; i < fd->data_len; i++) { + compute_value_size(fd->data[i], hp); + } + s->memory_used_count += 1; + s->memory_used_size += sizeof(*fd) + fd->data_len * sizeof(*fd->data); + } + } + break; + case JS_CLASS_REGEXP: /* u.regexp */ + compute_jsstring_size(p->u.regexp.pattern, hp); + compute_jsstring_size(p->u.regexp.bytecode, hp); + break; + + case JS_CLASS_FOR_IN_ITERATOR: /* u.for_in_iterator */ + { + JSForInIterator *it = p->u.for_in_iterator; + if (it) { + compute_value_size(it->obj, hp); + s->memory_used_count += 1; + s->memory_used_size += sizeof(*it); + } + } + break; + case JS_CLASS_ARRAY_BUFFER: /* u.array_buffer */ + case JS_CLASS_SHARED_ARRAY_BUFFER: /* u.array_buffer */ + { + JSArrayBuffer *abuf = p->u.array_buffer; + if (abuf) { + s->memory_used_count += 1; + s->memory_used_size += sizeof(*abuf); + if (abuf->data) { + s->memory_used_count += 1; + s->memory_used_size += abuf->byte_length; + } + } + } + break; + case JS_CLASS_GENERATOR: /* u.generator_data */ + case JS_CLASS_UINT8C_ARRAY: /* u.typed_array / u.array */ + case JS_CLASS_INT8_ARRAY: /* u.typed_array / u.array */ + case JS_CLASS_UINT8_ARRAY: /* u.typed_array / u.array */ + case JS_CLASS_INT16_ARRAY: /* u.typed_array / u.array */ + case JS_CLASS_UINT16_ARRAY: /* u.typed_array / u.array */ + case JS_CLASS_INT32_ARRAY: /* u.typed_array / u.array */ + case JS_CLASS_UINT32_ARRAY: /* u.typed_array / u.array */ + case JS_CLASS_BIG_INT64_ARRAY: /* u.typed_array / u.array */ + case JS_CLASS_BIG_UINT64_ARRAY: /* u.typed_array / u.array */ + case JS_CLASS_FLOAT16_ARRAY: /* u.typed_array / u.array */ + case JS_CLASS_FLOAT32_ARRAY: /* u.typed_array / u.array */ + case JS_CLASS_FLOAT64_ARRAY: /* u.typed_array / u.array */ + case JS_CLASS_DATAVIEW: /* u.typed_array */ + case JS_CLASS_MAP: /* u.map_state */ + case JS_CLASS_SET: /* u.map_state */ + case JS_CLASS_WEAKMAP: /* u.map_state */ + case JS_CLASS_WEAKSET: /* u.map_state */ + case JS_CLASS_MAP_ITERATOR: /* u.map_iterator_data */ + case JS_CLASS_SET_ITERATOR: /* u.map_iterator_data */ + case JS_CLASS_ARRAY_ITERATOR: /* u.array_iterator_data */ + case JS_CLASS_STRING_ITERATOR: /* u.array_iterator_data */ + case JS_CLASS_PROXY: /* u.proxy_data */ + case JS_CLASS_PROMISE: /* u.promise_data */ + case JS_CLASS_PROMISE_RESOLVE_FUNCTION: /* u.promise_function_data */ + case JS_CLASS_PROMISE_REJECT_FUNCTION: /* u.promise_function_data */ + case JS_CLASS_ASYNC_FUNCTION_RESOLVE: /* u.async_function_data */ + case JS_CLASS_ASYNC_FUNCTION_REJECT: /* u.async_function_data */ + case JS_CLASS_ASYNC_FROM_SYNC_ITERATOR: /* u.async_from_sync_iterator_data */ + case JS_CLASS_ASYNC_GENERATOR: /* u.async_generator_data */ + /* TODO */ + default: + /* XXX: class definition should have an opaque block size */ + if (p->u.opaque) { + s->memory_used_count += 1; + } + break; + } + } + s->obj_size += s->obj_count * sizeof(JSObject); + + /* hashed shapes */ + s->memory_used_count++; /* rt->shape_hash */ + s->memory_used_size += sizeof(rt->shape_hash[0]) * rt->shape_hash_size; + for(i = 0; i < rt->shape_hash_size; i++) { + JSShape *sh; + for(sh = rt->shape_hash[i]; sh != NULL; sh = sh->shape_hash_next) { + int hash_size = sh->prop_hash_mask + 1; + s->shape_count++; + s->shape_size += get_shape_size(hash_size, sh->prop_size); + } + } + + /* atoms */ + s->memory_used_count += 2; /* rt->atom_array, rt->atom_hash */ + s->atom_count = rt->atom_count; + s->atom_size = sizeof(rt->atom_array[0]) * rt->atom_size + + sizeof(rt->atom_hash[0]) * rt->atom_hash_size; + for(i = 0; i < rt->atom_size; i++) { + JSAtomStruct *p = rt->atom_array[i]; + if (!atom_is_free(p)) { + s->atom_size += (sizeof(*p) + (p->len << p->is_wide_char) + + 1 - p->is_wide_char); + } + } + s->str_count = round(mem.str_count); + s->str_size = round(mem.str_size); + s->js_func_count = mem.js_func_count; + s->js_func_size = round(mem.js_func_size); + s->js_func_code_size = mem.js_func_code_size; + s->js_func_pc2line_count = mem.js_func_pc2line_count; + s->js_func_pc2line_size = mem.js_func_pc2line_size; + s->memory_used_count += round(mem.memory_used_count) + + s->atom_count + s->str_count + + s->obj_count + s->shape_count + + s->js_func_count + s->js_func_pc2line_count; + s->memory_used_size += s->atom_size + s->str_size + + s->obj_size + s->prop_size + s->shape_size + + s->js_func_size + s->js_func_code_size + s->js_func_pc2line_size; +} + +void JS_DumpMemoryUsage(FILE *fp, const JSMemoryUsage *s, JSRuntime *rt) +{ + fprintf(fp, "QuickJS-ng memory usage -- %s version, %d-bit, %s Endian, malloc limit: %"PRId64"\n\n", + JS_GetVersion(), (int)sizeof(void *) * 8, is_be() ? "Big" : "Little", s->malloc_limit); + if (rt) { + static const struct { + const char *name; + size_t size; + } object_types[] = { + { "JSRuntime", sizeof(JSRuntime) }, + { "JSContext", sizeof(JSContext) }, + { "JSObject", sizeof(JSObject) }, + { "JSString", sizeof(JSString) }, + { "JSFunctionBytecode", sizeof(JSFunctionBytecode) }, + }; + int i, usage_size_ok = 0; + for(i = 0; i < countof(object_types); i++) { + unsigned int size = object_types[i].size; + void *p = js_malloc_rt(rt, size); + if (p) { + unsigned int size1 = js_malloc_usable_size_rt(rt, p); + if (size1 >= size) { + usage_size_ok = 1; + fprintf(fp, " %3u + %-2u %s\n", + size, size1 - size, object_types[i].name); + } + js_free_rt(rt, p); + } + } + if (!usage_size_ok) { + fprintf(fp, " malloc_usable_size unavailable\n"); + } + { + int obj_classes[JS_CLASS_INIT_COUNT + 1] = { 0 }; + int class_id; + struct list_head *el; + list_for_each(el, &rt->gc_obj_list) { + JSGCObjectHeader *gp = list_entry(el, JSGCObjectHeader, link); + JSObject *p; + if (gp->gc_obj_type == JS_GC_OBJ_TYPE_JS_OBJECT) { + p = (JSObject *)gp; + obj_classes[min_uint32(p->class_id, JS_CLASS_INIT_COUNT)]++; + } + } + fprintf(fp, "\n" "JSObject classes\n"); + if (obj_classes[0]) + fprintf(fp, " %5d %2.0d %s\n", obj_classes[0], 0, "none"); + for (class_id = 1; class_id < JS_CLASS_INIT_COUNT; class_id++) { + if (obj_classes[class_id]) { + char buf[ATOM_GET_STR_BUF_SIZE]; + fprintf(fp, " %5d %2.0d %s\n", obj_classes[class_id], class_id, + JS_AtomGetStrRT(rt, buf, sizeof(buf), js_std_class_def[class_id - 1].class_name)); + } + } + if (obj_classes[JS_CLASS_INIT_COUNT]) + fprintf(fp, " %5d %2.0d %s\n", obj_classes[JS_CLASS_INIT_COUNT], 0, "other"); + } + fprintf(fp, "\n"); + } + fprintf(fp, "%-20s %8s %8s\n", "NAME", "COUNT", "SIZE"); + + if (s->malloc_count) { + fprintf(fp, "%-20s %8"PRId64" %8"PRId64" (%0.1f per block)\n", + "memory allocated", s->malloc_count, s->malloc_size, + (double)s->malloc_size / s->malloc_count); + fprintf(fp, "%-20s %8"PRId64" %8"PRId64" (%d overhead, %0.1f average slack)\n", + "memory used", s->memory_used_count, s->memory_used_size, + MALLOC_OVERHEAD, ((double)(s->malloc_size - s->memory_used_size) / + s->memory_used_count)); + } + if (s->atom_count) { + fprintf(fp, "%-20s %8"PRId64" %8"PRId64" (%0.1f per atom)\n", + "atoms", s->atom_count, s->atom_size, + (double)s->atom_size / s->atom_count); + } + if (s->str_count) { + fprintf(fp, "%-20s %8"PRId64" %8"PRId64" (%0.1f per string)\n", + "strings", s->str_count, s->str_size, + (double)s->str_size / s->str_count); + } + if (s->obj_count) { + fprintf(fp, "%-20s %8"PRId64" %8"PRId64" (%0.1f per object)\n", + "objects", s->obj_count, s->obj_size, + (double)s->obj_size / s->obj_count); + fprintf(fp, "%-20s %8"PRId64" %8"PRId64" (%0.1f per object)\n", + " properties", s->prop_count, s->prop_size, + (double)s->prop_count / s->obj_count); + fprintf(fp, "%-20s %8"PRId64" %8"PRId64" (%0.1f per shape)\n", + " shapes", s->shape_count, s->shape_size, + (double)s->shape_size / s->shape_count); + } + if (s->js_func_count) { + fprintf(fp, "%-20s %8"PRId64" %8"PRId64"\n", + "bytecode functions", s->js_func_count, s->js_func_size); + fprintf(fp, "%-20s %8"PRId64" %8"PRId64" (%0.1f per function)\n", + " bytecode", s->js_func_count, s->js_func_code_size, + (double)s->js_func_code_size / s->js_func_count); + if (s->js_func_pc2line_count) { + fprintf(fp, "%-20s %8"PRId64" %8"PRId64" (%0.1f per function)\n", + " pc2line", s->js_func_pc2line_count, + s->js_func_pc2line_size, + (double)s->js_func_pc2line_size / s->js_func_pc2line_count); + } + } + if (s->c_func_count) { + fprintf(fp, "%-20s %8"PRId64"\n", "C functions", s->c_func_count); + } + if (s->array_count) { + fprintf(fp, "%-20s %8"PRId64"\n", "arrays", s->array_count); + if (s->fast_array_count) { + fprintf(fp, "%-20s %8"PRId64"\n", " fast arrays", s->fast_array_count); + fprintf(fp, "%-20s %8"PRId64" %8"PRId64" (%0.1f per fast array)\n", + " elements", s->fast_array_elements, + s->fast_array_elements * (int)sizeof(JSValue), + (double)s->fast_array_elements / s->fast_array_count); + } + } + if (s->binary_object_count) { + fprintf(fp, "%-20s %8"PRId64" %8"PRId64"\n", + "binary objects", s->binary_object_count, s->binary_object_size); + } +} + +JSValue JS_GetGlobalObject(JSContext *ctx) +{ + return js_dup(ctx->global_obj); +} + +/* WARNING: obj is freed */ +JSValue JS_Throw(JSContext *ctx, JSValue obj) +{ + JSRuntime *rt = ctx->rt; + JS_FreeValue(ctx, rt->current_exception); + rt->current_exception = obj; + return JS_EXCEPTION; +} + +/* return the pending exception (cannot be called twice). */ +JSValue JS_GetException(JSContext *ctx) +{ + JSValue val; + JSRuntime *rt = ctx->rt; + val = rt->current_exception; + rt->current_exception = JS_UNINITIALIZED; + return val; +} + +JS_BOOL JS_HasException(JSContext *ctx) +{ + return !JS_IsUninitialized(ctx->rt->current_exception); +} + +static void dbuf_put_leb128(DynBuf *s, uint32_t v) +{ + uint32_t a; + for(;;) { + a = v & 0x7f; + v >>= 7; + if (v != 0) { + dbuf_putc(s, a | 0x80); + } else { + dbuf_putc(s, a); + break; + } + } +} + +static void dbuf_put_sleb128(DynBuf *s, int32_t v1) +{ + uint32_t v = v1; + dbuf_put_leb128(s, (2 * v) ^ -(v >> 31)); +} + +static int get_leb128(uint32_t *pval, const uint8_t *buf, + const uint8_t *buf_end) +{ + const uint8_t *ptr = buf; + uint32_t v, a, i; + v = 0; + for(i = 0; i < 5; i++) { + if (unlikely(ptr >= buf_end)) + break; + a = *ptr++; + v |= (a & 0x7f) << (i * 7); + if (!(a & 0x80)) { + *pval = v; + return ptr - buf; + } + } + *pval = 0; + return -1; +} + +static int get_sleb128(int32_t *pval, const uint8_t *buf, + const uint8_t *buf_end) +{ + int ret; + uint32_t val; + ret = get_leb128(&val, buf, buf_end); + if (ret < 0) { + *pval = 0; + return -1; + } + *pval = (val >> 1) ^ -(val & 1); + return ret; +} + +static int find_line_num(JSContext *ctx, JSFunctionBytecode *b, + uint32_t pc_value, int *col) +{ + const uint8_t *p_end, *p; + int new_line_num, new_col_num, line_num, col_num, pc, v, ret; + unsigned int op; + + *col = 1; + p = b->pc2line_buf; + if (!p) + goto fail; + p_end = p + b->pc2line_len; + pc = 0; + line_num = b->line_num; + col_num = b->col_num; + while (p < p_end) { + op = *p++; + if (op == 0) { + uint32_t val; + ret = get_leb128(&val, p, p_end); + if (ret < 0) + goto fail; + pc += val; + p += ret; + ret = get_sleb128(&v, p, p_end); + if (ret < 0) + goto fail; + p += ret; + new_line_num = line_num + v; + } else { + op -= PC2LINE_OP_FIRST; + pc += (op / PC2LINE_RANGE); + new_line_num = line_num + (op % PC2LINE_RANGE) + PC2LINE_BASE; + } + ret = get_sleb128(&v, p, p_end); + if (ret < 0) + goto fail; + p += ret; + new_col_num = col_num + v; + if (pc_value < pc) + break; + line_num = new_line_num; + col_num = new_col_num; + } + *col = col_num; + return line_num; +fail: + /* should never happen */ + return b->line_num; +} + +/* in order to avoid executing arbitrary code during the stack trace + generation, we only look at simple 'name' properties containing a + string. */ +static const char *get_func_name(JSContext *ctx, JSValue func) +{ + JSProperty *pr; + JSShapeProperty *prs; + JSValue val; + + if (JS_VALUE_GET_TAG(func) != JS_TAG_OBJECT) + return NULL; + prs = find_own_property(&pr, JS_VALUE_GET_OBJ(func), JS_ATOM_name); + if (!prs) + return NULL; + if ((prs->flags & JS_PROP_TMASK) != JS_PROP_NORMAL) + return NULL; + val = pr->u.value; + if (JS_VALUE_GET_TAG(val) != JS_TAG_STRING) + return NULL; + return JS_ToCString(ctx, val); +} + +#define JS_BACKTRACE_FLAG_SKIP_FIRST_LEVEL (1 << 0) +/* only taken into account if filename is provided */ +#define JS_BACKTRACE_FLAG_SINGLE_LEVEL (1 << 1) + +/* if filename != NULL, an additional level is added with the filename + and line number information (used for parse error). */ +static void build_backtrace(JSContext *ctx, JSValue error_obj, + const char *filename, int line_num, int col_num, + int backtrace_flags) +{ + JSStackFrame *sf; + JSValue stack, prepare, saved_exception; + DynBuf dbuf; + const char *func_name_str; + const char *str1; + JSObject *p; + JSFunctionBytecode *b; + BOOL backtrace_barrier, has_prepare; + JSRuntime *rt; + JSCallSiteData csd[64]; + uint32_t i; + int stack_trace_limit; + + stack_trace_limit = ctx->error_stack_trace_limit; + stack_trace_limit = min_int(stack_trace_limit, countof(csd)); + stack_trace_limit = max_int(stack_trace_limit, 0); + rt = ctx->rt; + has_prepare = FALSE; + i = 0; + + if (!rt->in_prepare_stack_trace && !JS_IsNull(ctx->error_ctor)) { + prepare = js_dup(ctx->error_prepare_stack); + has_prepare = JS_IsFunction(ctx, prepare); + rt->in_prepare_stack_trace = TRUE; + } + + if (has_prepare) { + saved_exception = rt->current_exception; + rt->current_exception = JS_NULL; + if (stack_trace_limit == 0) + goto done; + if (filename) + js_new_callsite_data2(ctx, &csd[i++], filename, line_num, col_num); + } else { + js_dbuf_init(ctx, &dbuf); + if (stack_trace_limit == 0) + goto done; + if (filename) { + i++; + dbuf_printf(&dbuf, " at %s", filename); + if (line_num != -1) + dbuf_printf(&dbuf, ":%d:%d", line_num, col_num); + dbuf_putc(&dbuf, '\n'); + } + } + + if (filename && (backtrace_flags & JS_BACKTRACE_FLAG_SINGLE_LEVEL)) + goto done; + + for (sf = rt->current_stack_frame; sf != NULL && i < stack_trace_limit; sf = sf->prev_frame) { + if (backtrace_flags & JS_BACKTRACE_FLAG_SKIP_FIRST_LEVEL) { + backtrace_flags &= ~JS_BACKTRACE_FLAG_SKIP_FIRST_LEVEL; + continue; + } + + p = JS_VALUE_GET_OBJ(sf->cur_func); + b = NULL; + backtrace_barrier = FALSE; + + if (js_class_has_bytecode(p->class_id)) { + b = p->u.func.function_bytecode; + backtrace_barrier = b->backtrace_barrier; + } + + if (has_prepare) { + js_new_callsite_data(ctx, &csd[i], sf); + } else { + /* func_name_str is UTF-8 encoded if needed */ + func_name_str = get_func_name(ctx, sf->cur_func); + if (!func_name_str || func_name_str[0] == '\0') + str1 = "<anonymous>"; + else + str1 = func_name_str; + dbuf_printf(&dbuf, " at %s", str1); + JS_FreeCString(ctx, func_name_str); + + if (b) { + const char *atom_str; + int line_num1, col_num1; + + /* Bytecode functions must have cur_pc set in the stack frame. */ + if (sf->cur_pc == NULL) + abort(); + + line_num1 = find_line_num(ctx, b, + sf->cur_pc - b->byte_code_buf - 1, + &col_num1); + atom_str = b->filename ? JS_AtomToCString(ctx, b->filename) : NULL; + dbuf_printf(&dbuf, " (%s", atom_str ? atom_str : "<null>"); + JS_FreeCString(ctx, atom_str); + if (line_num1 != -1) + dbuf_printf(&dbuf, ":%d:%d", line_num1, col_num1); + dbuf_putc(&dbuf, ')'); + } else { + dbuf_printf(&dbuf, " (native)"); + } + dbuf_putc(&dbuf, '\n'); + } + i++; + + /* stop backtrace if JS_EVAL_FLAG_BACKTRACE_BARRIER was used */ + if (backtrace_barrier) + break; + } + done: + if (has_prepare) { + int j = 0, k; + stack = JS_NewArray(ctx); + if (JS_IsException(stack)) { + stack = JS_NULL; + } else { + for (; j < i; j++) { + JSValue v = js_new_callsite(ctx, &csd[j]); + if (JS_IsException(v)) + break; + if (JS_DefinePropertyValueUint32(ctx, stack, j, v, JS_PROP_C_W_E) < 0) { + JS_FreeValue(ctx, v); + break; + } + } + } + // Clear the csd's we didn't use in case of error. + for (k = j; k < i; k++) { + JS_FreeValue(ctx, csd[k].filename); + JS_FreeValue(ctx, csd[k].func); + JS_FreeValue(ctx, csd[k].func_name); + } + JSValue args[] = { + error_obj, + stack, + }; + JSValue stack2 = JS_Call(ctx, prepare, ctx->error_ctor, countof(args), args); + JS_FreeValue(ctx, stack); + if (JS_IsException(stack2)) + stack = JS_NULL; + else + stack = stack2; + JS_FreeValue(ctx, prepare); + JS_FreeValue(ctx, rt->current_exception); + rt->current_exception = saved_exception; + } else { + if (dbuf_error(&dbuf)) + stack = JS_NULL; + else + stack = JS_NewStringLen(ctx, (char *)dbuf.buf, dbuf.size); + dbuf_free(&dbuf); + } + + rt->in_prepare_stack_trace = FALSE; + JS_DefinePropertyValue(ctx, error_obj, JS_ATOM_stack, stack, JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE); +} + +/* Note: it is important that no exception is returned by this function */ +static BOOL is_backtrace_needed(JSContext *ctx, JSValue obj) +{ + JSObject *p; + if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT) + return FALSE; + p = JS_VALUE_GET_OBJ(obj); + if (p->class_id != JS_CLASS_ERROR) + return FALSE; + if (find_own_property1(p, JS_ATOM_stack)) + return FALSE; + return TRUE; +} + +JSValue JS_NewError(JSContext *ctx) +{ + return JS_NewObjectClass(ctx, JS_CLASS_ERROR); +} + +static JSValue JS_MakeError(JSContext *ctx, JSErrorEnum error_num, + const char *message, BOOL add_backtrace) +{ + JSValue obj, msg; + + if (error_num == JS_PLAIN_ERROR) { + obj = JS_NewError(ctx); + } else { + obj = JS_NewObjectProtoClass(ctx, ctx->native_error_proto[error_num], + JS_CLASS_ERROR); + } + if (JS_IsException(obj)) + return JS_EXCEPTION; + msg = JS_NewString(ctx, message); + if (JS_IsException(msg)) + msg = JS_NewString(ctx, "Invalid error message"); + if (!JS_IsException(msg)) { + JS_DefinePropertyValue(ctx, obj, JS_ATOM_message, msg, + JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE); + } + if (add_backtrace) + build_backtrace(ctx, obj, NULL, 0, 0, 0); + return obj; +} + +/* fmt and arguments may be pure ASCII or UTF-8 encoded contents */ +static JSValue JS_ThrowError2(JSContext *ctx, JSErrorEnum error_num, + const char *fmt, va_list ap, BOOL add_backtrace) +{ + char buf[256]; + JSValue obj; + + vsnprintf(buf, sizeof(buf), fmt, ap); + obj = JS_MakeError(ctx, error_num, buf, add_backtrace); + if (unlikely(JS_IsException(obj))) { + /* out of memory: throw JS_NULL to avoid recursing */ + obj = JS_NULL; + } + return JS_Throw(ctx, obj); +} + +static JSValue JS_ThrowError(JSContext *ctx, JSErrorEnum error_num, + const char *fmt, va_list ap) +{ + JSRuntime *rt = ctx->rt; + JSStackFrame *sf; + BOOL add_backtrace; + + /* the backtrace is added later if called from a bytecode function */ + sf = rt->current_stack_frame; + add_backtrace = !rt->in_out_of_memory && + (!sf || (JS_GetFunctionBytecode(sf->cur_func) == NULL)); + return JS_ThrowError2(ctx, error_num, fmt, ap, add_backtrace); +} + +JSValue __attribute__((format(printf, 2, 3))) JS_ThrowPlainError(JSContext *ctx, const char *fmt, ...) +{ + JSValue val; + va_list ap; + + va_start(ap, fmt); + val = JS_ThrowError(ctx, JS_PLAIN_ERROR, fmt, ap); + va_end(ap); + return val; +} + +JSValue __attribute__((format(printf, 2, 3))) JS_ThrowSyntaxError(JSContext *ctx, const char *fmt, ...) +{ + JSValue val; + va_list ap; + + va_start(ap, fmt); + val = JS_ThrowError(ctx, JS_SYNTAX_ERROR, fmt, ap); + va_end(ap); + return val; +} + +JSValue __attribute__((format(printf, 2, 3))) JS_ThrowTypeError(JSContext *ctx, const char *fmt, ...) +{ + JSValue val; + va_list ap; + + va_start(ap, fmt); + val = JS_ThrowError(ctx, JS_TYPE_ERROR, fmt, ap); + va_end(ap); + return val; +} + +static int __attribute__((format(printf, 3, 4))) JS_ThrowTypeErrorOrFalse(JSContext *ctx, int flags, const char *fmt, ...) +{ + va_list ap; + + if ((flags & JS_PROP_THROW) || + ((flags & JS_PROP_THROW_STRICT) && is_strict_mode(ctx))) { + va_start(ap, fmt); + JS_ThrowError(ctx, JS_TYPE_ERROR, fmt, ap); + va_end(ap); + return -1; + } else { + return FALSE; + } +} + +/* never use it directly */ +static JSValue __attribute__((format(printf, 3, 4))) __JS_ThrowTypeErrorAtom(JSContext *ctx, JSAtom atom, const char *fmt, ...) +{ + char buf[ATOM_GET_STR_BUF_SIZE]; + return JS_ThrowTypeError(ctx, fmt, + JS_AtomGetStr(ctx, buf, sizeof(buf), atom)); +} + +/* never use it directly */ +static JSValue __attribute__((format(printf, 3, 4))) __JS_ThrowSyntaxErrorAtom(JSContext *ctx, JSAtom atom, const char *fmt, ...) +{ + char buf[ATOM_GET_STR_BUF_SIZE]; + return JS_ThrowSyntaxError(ctx, fmt, + JS_AtomGetStr(ctx, buf, sizeof(buf), atom)); +} + +/* %s is replaced by 'atom'. The macro is used so that gcc can check + the format string. */ +#define JS_ThrowTypeErrorAtom(ctx, fmt, atom) __JS_ThrowTypeErrorAtom(ctx, atom, fmt, "") +#define JS_ThrowSyntaxErrorAtom(ctx, fmt, atom) __JS_ThrowSyntaxErrorAtom(ctx, atom, fmt, "") + +static int JS_ThrowTypeErrorReadOnly(JSContext *ctx, int flags, JSAtom atom) +{ + if ((flags & JS_PROP_THROW) || + ((flags & JS_PROP_THROW_STRICT) && is_strict_mode(ctx))) { + JS_ThrowTypeErrorAtom(ctx, "'%s' is read-only", atom); + return -1; + } else { + return FALSE; + } +} + +JSValue __attribute__((format(printf, 2, 3))) JS_ThrowReferenceError(JSContext *ctx, const char *fmt, ...) +{ + JSValue val; + va_list ap; + + va_start(ap, fmt); + val = JS_ThrowError(ctx, JS_REFERENCE_ERROR, fmt, ap); + va_end(ap); + return val; +} + +JSValue __attribute__((format(printf, 2, 3))) JS_ThrowRangeError(JSContext *ctx, const char *fmt, ...) +{ + JSValue val; + va_list ap; + + va_start(ap, fmt); + val = JS_ThrowError(ctx, JS_RANGE_ERROR, fmt, ap); + va_end(ap); + return val; +} + +JSValue __attribute__((format(printf, 2, 3))) JS_ThrowInternalError(JSContext *ctx, const char *fmt, ...) +{ + JSValue val; + va_list ap; + + va_start(ap, fmt); + val = JS_ThrowError(ctx, JS_INTERNAL_ERROR, fmt, ap); + va_end(ap); + return val; +} + +JSValue JS_ThrowOutOfMemory(JSContext *ctx) +{ + JSRuntime *rt = ctx->rt; + if (!rt->in_out_of_memory) { + rt->in_out_of_memory = TRUE; + JS_ThrowInternalError(ctx, "out of memory"); + rt->in_out_of_memory = FALSE; + } + return JS_EXCEPTION; +} + +static JSValue JS_ThrowStackOverflow(JSContext *ctx) +{ + return JS_ThrowRangeError(ctx, "Maximum call stack size exceeded"); +} + +static JSValue JS_ThrowTypeErrorNotAnObject(JSContext *ctx) +{ + return JS_ThrowTypeError(ctx, "not an object"); +} + +static JSValue JS_ThrowTypeErrorNotASymbol(JSContext *ctx) +{ + return JS_ThrowTypeError(ctx, "not a symbol"); +} + +static JSValue JS_ThrowReferenceErrorNotDefined(JSContext *ctx, JSAtom name) +{ + char buf[ATOM_GET_STR_BUF_SIZE]; + return JS_ThrowReferenceError(ctx, "%s is not defined", + JS_AtomGetStr(ctx, buf, sizeof(buf), name)); +} + +static JSValue JS_ThrowReferenceErrorUninitialized(JSContext *ctx, JSAtom name) +{ + char buf[ATOM_GET_STR_BUF_SIZE]; + return JS_ThrowReferenceError(ctx, "%s is not initialized", + name == JS_ATOM_NULL ? "lexical variable" : + JS_AtomGetStr(ctx, buf, sizeof(buf), name)); +} + +static JSValue JS_ThrowReferenceErrorUninitialized2(JSContext *ctx, + JSFunctionBytecode *b, + int idx, BOOL is_ref) +{ + JSAtom atom = JS_ATOM_NULL; + if (is_ref) { + atom = b->closure_var[idx].var_name; + } else { + /* not present if the function is stripped and contains no eval() */ + if (b->vardefs) + atom = b->vardefs[b->arg_count + idx].var_name; + } + return JS_ThrowReferenceErrorUninitialized(ctx, atom); +} + +static JSValue JS_ThrowTypeErrorInvalidClass(JSContext *ctx, int class_id) +{ + JSRuntime *rt = ctx->rt; + JSAtom name; + name = rt->class_array[class_id].class_name; + return JS_ThrowTypeErrorAtom(ctx, "%s object expected", name); +} + +static no_inline __exception int __js_poll_interrupts(JSContext *ctx) +{ + JSRuntime *rt = ctx->rt; + ctx->interrupt_counter = JS_INTERRUPT_COUNTER_INIT; + if (rt->interrupt_handler) { + if (rt->interrupt_handler(rt, rt->interrupt_opaque)) { + /* XXX: should set a specific flag to avoid catching */ + JS_ThrowInternalError(ctx, "interrupted"); + JS_SetUncatchableError(ctx, ctx->rt->current_exception, TRUE); + return -1; + } + } + return 0; +} + +static inline __exception int js_poll_interrupts(JSContext *ctx) +{ + if (unlikely(--ctx->interrupt_counter <= 0)) { + return __js_poll_interrupts(ctx); + } else { + return 0; + } +} + +/* return -1 (exception) or TRUE/FALSE */ +static int JS_SetPrototypeInternal(JSContext *ctx, JSValue obj, + JSValue proto_val, + BOOL throw_flag) +{ + JSObject *proto, *p, *p1; + JSShape *sh; + + if (throw_flag) { + if (JS_VALUE_GET_TAG(obj) == JS_TAG_NULL || + JS_VALUE_GET_TAG(obj) == JS_TAG_UNDEFINED) + goto not_obj; + } else { + if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT) + goto not_obj; + } + p = JS_VALUE_GET_OBJ(obj); + if (JS_VALUE_GET_TAG(proto_val) != JS_TAG_OBJECT) { + if (JS_VALUE_GET_TAG(proto_val) != JS_TAG_NULL) { + not_obj: + JS_ThrowTypeErrorNotAnObject(ctx); + return -1; + } + proto = NULL; + } else { + proto = JS_VALUE_GET_OBJ(proto_val); + } + + if (throw_flag && JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT) + return TRUE; + + if (unlikely(p->class_id == JS_CLASS_PROXY)) + return js_proxy_setPrototypeOf(ctx, obj, proto_val, throw_flag); + sh = p->shape; + if (sh->proto == proto) + return TRUE; + if (p == JS_VALUE_GET_OBJ(ctx->class_proto[JS_CLASS_OBJECT])) { + if (throw_flag) { + JS_ThrowTypeError(ctx, "'Immutable prototype object \'Object.prototype\' cannot have their prototype set'"); + return -1; + } + return FALSE; + } + if (!p->extensible) { + if (throw_flag) { + JS_ThrowTypeError(ctx, "object is not extensible"); + return -1; + } else { + return FALSE; + } + } + if (proto) { + /* check if there is a cycle */ + p1 = proto; + do { + if (p1 == p) { + if (throw_flag) { + JS_ThrowTypeError(ctx, "circular prototype chain"); + return -1; + } else { + return FALSE; + } + } + /* Note: for Proxy objects, proto is NULL */ + p1 = p1->shape->proto; + } while (p1 != NULL); + js_dup(proto_val); + } + + if (js_shape_prepare_update(ctx, p, NULL)) + return -1; + sh = p->shape; + if (sh->proto) + JS_FreeValue(ctx, JS_MKPTR(JS_TAG_OBJECT, sh->proto)); + sh->proto = proto; + return TRUE; +} + +/* return -1 (exception) or TRUE/FALSE */ +int JS_SetPrototype(JSContext *ctx, JSValue obj, JSValue proto_val) +{ + return JS_SetPrototypeInternal(ctx, obj, proto_val, TRUE); +} + +/* Only works for primitive types, otherwise return JS_NULL. */ +static JSValue JS_GetPrototypePrimitive(JSContext *ctx, JSValue val) +{ + switch(JS_VALUE_GET_NORM_TAG(val)) { + case JS_TAG_BIG_INT: + val = ctx->class_proto[JS_CLASS_BIG_INT]; + break; + case JS_TAG_INT: + case JS_TAG_FLOAT64: + val = ctx->class_proto[JS_CLASS_NUMBER]; + break; + case JS_TAG_BOOL: + val = ctx->class_proto[JS_CLASS_BOOLEAN]; + break; + case JS_TAG_STRING: + val = ctx->class_proto[JS_CLASS_STRING]; + break; + case JS_TAG_SYMBOL: + val = ctx->class_proto[JS_CLASS_SYMBOL]; + break; + case JS_TAG_OBJECT: + case JS_TAG_NULL: + case JS_TAG_UNDEFINED: + default: + val = JS_NULL; + break; + } + return val; +} + +/* Return an Object, JS_NULL or JS_EXCEPTION in case of Proxy object. */ +JSValue JS_GetPrototype(JSContext *ctx, JSValue obj) +{ + JSValue val; + if (JS_VALUE_GET_TAG(obj) == JS_TAG_OBJECT) { + JSObject *p; + p = JS_VALUE_GET_OBJ(obj); + if (unlikely(p->class_id == JS_CLASS_PROXY)) { + val = js_proxy_getPrototypeOf(ctx, obj); + } else { + p = p->shape->proto; + if (!p) + val = JS_NULL; + else + val = js_dup(JS_MKPTR(JS_TAG_OBJECT, p)); + } + } else { + val = js_dup(JS_GetPrototypePrimitive(ctx, obj)); + } + return val; +} + +static JSValue JS_GetPrototypeFree(JSContext *ctx, JSValue obj) +{ + JSValue obj1; + obj1 = JS_GetPrototype(ctx, obj); + JS_FreeValue(ctx, obj); + return obj1; +} + +int JS_GetLength(JSContext *ctx, JSValue obj, int64_t *pres) { + return js_get_length64(ctx, pres, obj); +} + +int JS_SetLength(JSContext *ctx, JSValue obj, int64_t len) { + return js_set_length64(ctx, obj, len); +} + +/* return TRUE, FALSE or (-1) in case of exception */ +static int JS_OrdinaryIsInstanceOf(JSContext *ctx, JSValue val, + JSValue obj) +{ + JSValue obj_proto; + JSObject *proto; + const JSObject *p, *proto1; + BOOL ret; + + if (!JS_IsFunction(ctx, obj)) + return FALSE; + p = JS_VALUE_GET_OBJ(obj); + if (p->class_id == JS_CLASS_BOUND_FUNCTION) { + JSBoundFunction *s = p->u.bound_function; + return JS_IsInstanceOf(ctx, val, s->func_obj); + } + + /* Only explicitly boxed values are instances of constructors */ + if (JS_VALUE_GET_TAG(val) != JS_TAG_OBJECT) + return FALSE; + obj_proto = JS_GetProperty(ctx, obj, JS_ATOM_prototype); + if (JS_VALUE_GET_TAG(obj_proto) != JS_TAG_OBJECT) { + if (!JS_IsException(obj_proto)) + JS_ThrowTypeError(ctx, "operand 'prototype' property is not an object"); + ret = -1; + goto done; + } + proto = JS_VALUE_GET_OBJ(obj_proto); + p = JS_VALUE_GET_OBJ(val); + for(;;) { + proto1 = p->shape->proto; + if (!proto1) { + /* slow case if proxy in the prototype chain */ + if (unlikely(p->class_id == JS_CLASS_PROXY)) { + JSValue obj1; + obj1 = js_dup(JS_MKPTR(JS_TAG_OBJECT, (JSObject *)p)); + for(;;) { + obj1 = JS_GetPrototypeFree(ctx, obj1); + if (JS_IsException(obj1)) { + ret = -1; + break; + } + if (JS_IsNull(obj1)) { + ret = FALSE; + break; + } + if (proto == JS_VALUE_GET_OBJ(obj1)) { + JS_FreeValue(ctx, obj1); + ret = TRUE; + break; + } + /* must check for timeout to avoid infinite loop */ + if (js_poll_interrupts(ctx)) { + JS_FreeValue(ctx, obj1); + ret = -1; + break; + } + } + } else { + ret = FALSE; + } + break; + } + p = proto1; + if (proto == p) { + ret = TRUE; + break; + } + } +done: + JS_FreeValue(ctx, obj_proto); + return ret; +} + +/* return TRUE, FALSE or (-1) in case of exception */ +int JS_IsInstanceOf(JSContext *ctx, JSValue val, JSValue obj) +{ + JSValue method; + + if (!JS_IsObject(obj)) + goto fail; + method = JS_GetProperty(ctx, obj, JS_ATOM_Symbol_hasInstance); + if (JS_IsException(method)) + return -1; + if (!JS_IsNull(method) && !JS_IsUndefined(method)) { + JSValue ret; + ret = JS_CallFree(ctx, method, obj, 1, &val); + return JS_ToBoolFree(ctx, ret); + } + + /* legacy case */ + if (!JS_IsFunction(ctx, obj)) { + fail: + JS_ThrowTypeError(ctx, "invalid 'instanceof' right operand"); + return -1; + } + return JS_OrdinaryIsInstanceOf(ctx, val, obj); +} + +/* return the value associated to the autoinit property or an exception */ +typedef JSValue JSAutoInitFunc(JSContext *ctx, JSObject *p, JSAtom atom, void *opaque); + +static JSAutoInitFunc *js_autoinit_func_table[] = { + js_instantiate_prototype, /* JS_AUTOINIT_ID_PROTOTYPE */ + js_module_ns_autoinit, /* JS_AUTOINIT_ID_MODULE_NS */ + JS_InstantiateFunctionListItem2, /* JS_AUTOINIT_ID_PROP */ +}; + +/* warning: 'prs' is reallocated after it */ +static int JS_AutoInitProperty(JSContext *ctx, JSObject *p, JSAtom prop, + JSProperty *pr, JSShapeProperty *prs) +{ + JSValue val; + JSContext *realm; + JSAutoInitFunc *func; + + if (js_shape_prepare_update(ctx, p, &prs)) + return -1; + + realm = js_autoinit_get_realm(pr); + func = js_autoinit_func_table[js_autoinit_get_id(pr)]; + /* 'func' shall not modify the object properties 'pr' */ + val = func(realm, p, prop, pr->u.init.opaque); + js_autoinit_free(ctx->rt, pr); + prs->flags &= ~JS_PROP_TMASK; + pr->u.value = JS_UNDEFINED; + if (JS_IsException(val)) + return -1; + pr->u.value = val; + return 0; +} + +static JSValue JS_GetPropertyInternal2(JSContext *ctx, JSValue obj, + JSAtom prop, JSValue this_obj, + JSInlineCacheUpdate *icu, + BOOL throw_ref_error) +{ + JSObject *p; + JSProperty *pr; + JSShapeProperty *prs; + uint32_t tag, offset, proto_depth; + + offset = proto_depth = 0; + tag = JS_VALUE_GET_TAG(obj); + if (unlikely(tag != JS_TAG_OBJECT)) { + switch(tag) { + case JS_TAG_NULL: + return JS_ThrowTypeErrorAtom(ctx, "cannot read property '%s' of null", prop); + case JS_TAG_UNDEFINED: + return JS_ThrowTypeErrorAtom(ctx, "cannot read property '%s' of undefined", prop); + case JS_TAG_EXCEPTION: + return JS_EXCEPTION; + case JS_TAG_STRING: + { + JSString *p1 = JS_VALUE_GET_STRING(obj); + if (__JS_AtomIsTaggedInt(prop)) { + uint32_t idx, ch; + idx = __JS_AtomToUInt32(prop); + if (idx < p1->len) { + ch = string_get(p1, idx); + return js_new_string_char(ctx, ch); + } + } else if (prop == JS_ATOM_length) { + return js_int32(p1->len); + } + } + break; + default: + break; + } + /* cannot raise an exception */ + p = JS_VALUE_GET_OBJ(JS_GetPrototypePrimitive(ctx, obj)); + if (!p) + return JS_UNDEFINED; + } else { + p = JS_VALUE_GET_OBJ(obj); + } + + for(;;) { + prs = find_own_property_ic(&pr, p, prop, &offset); + if (prs) { + /* found */ + if (unlikely(prs->flags & JS_PROP_TMASK)) { + if ((prs->flags & JS_PROP_TMASK) == JS_PROP_GETSET) { + if (unlikely(!pr->u.getset.getter)) { + return JS_UNDEFINED; + } else { + JSValue func = JS_MKPTR(JS_TAG_OBJECT, pr->u.getset.getter); + /* Note: the field could be removed in the getter */ + func = js_dup(func); + return JS_CallFree(ctx, func, this_obj, 0, NULL); + } + } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF) { + JSValue val = *pr->u.var_ref->pvalue; + if (unlikely(JS_IsUninitialized(val))) + return JS_ThrowReferenceErrorUninitialized(ctx, prs->atom); + return js_dup(val); + } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_AUTOINIT) { + /* Instantiate property and retry */ + if (JS_AutoInitProperty(ctx, p, prop, pr, prs)) + return JS_EXCEPTION; + continue; + } + } else { + if (proto_depth == 0) + add_ic_slot(ctx, icu, prop, p, offset); + return js_dup(pr->u.value); + } + } + if (unlikely(p->is_exotic)) { + /* exotic behaviors */ + if (p->fast_array) { + if (__JS_AtomIsTaggedInt(prop)) { + uint32_t idx = __JS_AtomToUInt32(prop); + if (idx < p->u.array.count) { + /* we avoid duplicating the code */ + return JS_GetPropertyUint32(ctx, JS_MKPTR(JS_TAG_OBJECT, p), idx); + } else if (p->class_id >= JS_CLASS_UINT8C_ARRAY && + p->class_id <= JS_CLASS_FLOAT64_ARRAY) { + return JS_UNDEFINED; + } + } else if (p->class_id >= JS_CLASS_UINT8C_ARRAY && + p->class_id <= JS_CLASS_FLOAT64_ARRAY) { + int ret; + ret = JS_AtomIsNumericIndex(ctx, prop); + if (ret != 0) { + if (ret < 0) + return JS_EXCEPTION; + return JS_UNDEFINED; + } + } + } else { + const JSClassExoticMethods *em = ctx->rt->class_array[p->class_id].exotic; + if (em) { + if (em->get_property) { + JSValue obj1, retval; + /* XXX: should pass throw_ref_error */ + /* Note: if 'p' is a prototype, it can be + freed in the called function */ + obj1 = js_dup(JS_MKPTR(JS_TAG_OBJECT, p)); + retval = em->get_property(ctx, obj1, prop, this_obj); + JS_FreeValue(ctx, obj1); + return retval; + } + if (em->get_own_property) { + JSPropertyDescriptor desc; + int ret; + JSValue obj1; + + /* Note: if 'p' is a prototype, it can be + freed in the called function */ + obj1 = js_dup(JS_MKPTR(JS_TAG_OBJECT, p)); + ret = em->get_own_property(ctx, &desc, obj1, prop); + JS_FreeValue(ctx, obj1); + if (ret < 0) + return JS_EXCEPTION; + if (ret) { + if (desc.flags & JS_PROP_GETSET) { + JS_FreeValue(ctx, desc.setter); + return JS_CallFree(ctx, desc.getter, this_obj, 0, NULL); + } else { + return desc.value; + } + } + } + } + } + } + proto_depth++; + p = p->shape->proto; + if (!p) + break; + } + if (unlikely(throw_ref_error)) { + return JS_ThrowReferenceErrorNotDefined(ctx, prop); + } else { + return JS_UNDEFINED; + } +} + +static JSValue JS_GetPropertyInternal(JSContext *ctx, JSValue obj, + JSAtom prop, JSValue this_obj, + BOOL throw_ref_error) +{ + return JS_GetPropertyInternal2(ctx, obj, prop, this_obj, NULL, throw_ref_error); +} + +JSValue JS_GetProperty(JSContext *ctx, JSValue this_obj, JSAtom prop) +{ + return JS_GetPropertyInternal2(ctx, this_obj, prop, this_obj, NULL, FALSE); +} + +static JSValue JS_GetPropertyInternalWithIC(JSContext *ctx, JSValue obj, + JSAtom prop, JSValue this_obj, + JSInlineCacheUpdate *icu, + BOOL throw_ref_error) +{ + uint32_t tag, offset; + JSObject *p; + tag = JS_VALUE_GET_TAG(obj); + if (unlikely(tag != JS_TAG_OBJECT)) + goto slow_path; + p = JS_VALUE_GET_OBJ(obj); + offset = get_ic_prop_offset(icu, p->shape); + if (likely(offset != INLINE_CACHE_MISS)) + return js_dup(p->prop[offset].u.value); +slow_path: + return JS_GetPropertyInternal2(ctx, obj, prop, this_obj, icu, throw_ref_error); +} + +static JSValue JS_ThrowTypeErrorPrivateNotFound(JSContext *ctx, JSAtom atom) +{ + return JS_ThrowTypeErrorAtom(ctx, "private class field '%s' does not exist", + atom); +} + +/* Private fields can be added even on non extensible objects or + Proxies */ +static int JS_DefinePrivateField(JSContext *ctx, JSValue obj, + JSValue name, JSValue val) +{ + JSObject *p; + JSShapeProperty *prs; + JSProperty *pr; + JSAtom prop; + + if (unlikely(JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)) { + JS_ThrowTypeErrorNotAnObject(ctx); + goto fail; + } + /* safety check */ + if (unlikely(JS_VALUE_GET_TAG(name) != JS_TAG_SYMBOL)) { + JS_ThrowTypeErrorNotASymbol(ctx); + goto fail; + } + prop = js_symbol_to_atom(ctx, name); + p = JS_VALUE_GET_OBJ(obj); + prs = find_own_property(&pr, p, prop); + if (prs) { + JS_ThrowTypeErrorAtom(ctx, "private class field '%s' already exists", + prop); + goto fail; + } + pr = add_property(ctx, p, prop, JS_PROP_C_W_E); + if (unlikely(!pr)) { + fail: + JS_FreeValue(ctx, val); + return -1; + } + pr->u.value = val; + return 0; +} + +static JSValue JS_GetPrivateField(JSContext *ctx, JSValue obj, + JSValue name) +{ + JSObject *p; + JSShapeProperty *prs; + JSProperty *pr; + JSAtom prop; + + if (unlikely(JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)) + return JS_ThrowTypeErrorNotAnObject(ctx); + /* safety check */ + if (unlikely(JS_VALUE_GET_TAG(name) != JS_TAG_SYMBOL)) + return JS_ThrowTypeErrorNotASymbol(ctx); + prop = js_symbol_to_atom(ctx, name); + p = JS_VALUE_GET_OBJ(obj); + prs = find_own_property(&pr, p, prop); + if (!prs) { + JS_ThrowTypeErrorPrivateNotFound(ctx, prop); + return JS_EXCEPTION; + } + return js_dup(pr->u.value); +} + +static int JS_SetPrivateField(JSContext *ctx, JSValue obj, + JSValue name, JSValue val) +{ + JSObject *p; + JSShapeProperty *prs; + JSProperty *pr; + JSAtom prop; + + if (unlikely(JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)) { + JS_ThrowTypeErrorNotAnObject(ctx); + goto fail; + } + /* safety check */ + if (unlikely(JS_VALUE_GET_TAG(name) != JS_TAG_SYMBOL)) { + JS_ThrowTypeErrorNotASymbol(ctx); + goto fail; + } + prop = js_symbol_to_atom(ctx, name); + p = JS_VALUE_GET_OBJ(obj); + prs = find_own_property(&pr, p, prop); + if (!prs) { + JS_ThrowTypeErrorPrivateNotFound(ctx, prop); + fail: + JS_FreeValue(ctx, val); + return -1; + } + set_value(ctx, &pr->u.value, val); + return 0; +} + +/* add a private brand field to 'home_obj' if not already present and + if obj is != null add a private brand to it */ +static int JS_AddBrand(JSContext *ctx, JSValue obj, JSValue home_obj) +{ + JSObject *p, *p1; + JSShapeProperty *prs; + JSProperty *pr; + JSValue brand; + JSAtom brand_atom; + + if (unlikely(JS_VALUE_GET_TAG(home_obj) != JS_TAG_OBJECT)) { + JS_ThrowTypeErrorNotAnObject(ctx); + return -1; + } + p = JS_VALUE_GET_OBJ(home_obj); + prs = find_own_property(&pr, p, JS_ATOM_Private_brand); + if (!prs) { + /* if the brand is not present, add it */ + brand = JS_NewSymbolFromAtom(ctx, JS_ATOM_brand, JS_ATOM_TYPE_PRIVATE); + if (JS_IsException(brand)) + return -1; + pr = add_property(ctx, p, JS_ATOM_Private_brand, JS_PROP_C_W_E); + if (!pr) { + JS_FreeValue(ctx, brand); + return -1; + } + pr->u.value = js_dup(brand); + } else { + brand = js_dup(pr->u.value); + } + brand_atom = js_symbol_to_atom(ctx, brand); + + if (JS_IsObject(obj)) { + p1 = JS_VALUE_GET_OBJ(obj); + prs = find_own_property(&pr, p1, brand_atom); + if (unlikely(prs)) { + JS_FreeAtom(ctx, brand_atom); + JS_ThrowTypeError(ctx, "private method is already present"); + return -1; + } + pr = add_property(ctx, p1, brand_atom, JS_PROP_C_W_E); + JS_FreeAtom(ctx, brand_atom); + if (!pr) + return -1; + pr->u.value = JS_UNDEFINED; + } else { + JS_FreeAtom(ctx, brand_atom); + } + + return 0; +} + +/* return a boolean telling if the brand of the home object of 'func' + is present on 'obj' or -1 in case of exception */ +static int JS_CheckBrand(JSContext *ctx, JSValue obj, JSValue func) +{ + JSObject *p, *p1, *home_obj; + JSShapeProperty *prs; + JSProperty *pr; + JSValue brand; + + /* get the home object of 'func' */ + if (unlikely(JS_VALUE_GET_TAG(func) != JS_TAG_OBJECT)) + goto not_obj; + p1 = JS_VALUE_GET_OBJ(func); + if (!js_class_has_bytecode(p1->class_id)) + goto not_obj; + home_obj = p1->u.func.home_object; + if (!home_obj) + goto not_obj; + prs = find_own_property(&pr, home_obj, JS_ATOM_Private_brand); + if (!prs) { + JS_ThrowTypeError(ctx, "expecting <brand> private field"); + return -1; + } + brand = pr->u.value; + /* safety check */ + if (unlikely(JS_VALUE_GET_TAG(brand) != JS_TAG_SYMBOL)) + goto not_obj; + + /* get the brand array of 'obj' */ + if (unlikely(JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)) { + not_obj: + JS_ThrowTypeErrorNotAnObject(ctx); + return -1; + } + p = JS_VALUE_GET_OBJ(obj); + prs = find_own_property(&pr, p, js_symbol_to_atom(ctx, brand)); + return (prs != NULL); +} + +static uint32_t js_string_obj_get_length(JSContext *ctx, + JSValue obj) +{ + JSObject *p; + JSString *p1; + uint32_t len = 0; + + /* This is a class exotic method: obj class_id is JS_CLASS_STRING */ + p = JS_VALUE_GET_OBJ(obj); + if (JS_VALUE_GET_TAG(p->u.object_data) == JS_TAG_STRING) { + p1 = JS_VALUE_GET_STRING(p->u.object_data); + len = p1->len; + } + return len; +} + +static int num_keys_cmp(const void *p1, const void *p2, void *opaque) +{ + JSContext *ctx = opaque; + JSAtom atom1 = ((const JSPropertyEnum *)p1)->atom; + JSAtom atom2 = ((const JSPropertyEnum *)p2)->atom; + uint32_t v1, v2; + BOOL atom1_is_integer, atom2_is_integer; + + atom1_is_integer = JS_AtomIsArrayIndex(ctx, &v1, atom1); + atom2_is_integer = JS_AtomIsArrayIndex(ctx, &v2, atom2); + assert(atom1_is_integer && atom2_is_integer); + if (v1 < v2) + return -1; + else if (v1 == v2) + return 0; + else + return 1; +} + +static void js_free_prop_enum(JSContext *ctx, JSPropertyEnum *tab, uint32_t len) +{ + uint32_t i; + if (tab) { + for(i = 0; i < len; i++) + JS_FreeAtom(ctx, tab[i].atom); + js_free(ctx, tab); + } +} + +/* return < 0 in case if exception, 0 if OK. ptab and its atoms must + be freed by the user. */ +static int __exception JS_GetOwnPropertyNamesInternal(JSContext *ctx, + JSPropertyEnum **ptab, + uint32_t *plen, + JSObject *p, int flags) +{ + int i, j; + JSShape *sh; + JSShapeProperty *prs; + JSPropertyEnum *tab_atom, *tab_exotic; + JSAtom atom; + uint32_t num_keys_count, str_keys_count, sym_keys_count, atom_count; + uint32_t num_index, str_index, sym_index, exotic_count, exotic_keys_count; + BOOL is_enumerable, num_sorted; + uint32_t num_key; + JSAtomKindEnum kind; + + /* clear pointer for consistency in case of failure */ + *ptab = NULL; + *plen = 0; + + /* compute the number of returned properties */ + num_keys_count = 0; + str_keys_count = 0; + sym_keys_count = 0; + exotic_keys_count = 0; + exotic_count = 0; + tab_exotic = NULL; + sh = p->shape; + for(i = 0, prs = get_shape_prop(sh); i < sh->prop_count; i++, prs++) { + atom = prs->atom; + if (atom != JS_ATOM_NULL) { + is_enumerable = ((prs->flags & JS_PROP_ENUMERABLE) != 0); + kind = JS_AtomGetKind(ctx, atom); + if ((!(flags & JS_GPN_ENUM_ONLY) || is_enumerable) && + ((flags >> kind) & 1) != 0) { + /* need to raise an exception in case of the module + name space (implicit GetOwnProperty) */ + if (unlikely((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF) && + (flags & (JS_GPN_SET_ENUM | JS_GPN_ENUM_ONLY))) { + JSVarRef *var_ref = p->prop[i].u.var_ref; + if (unlikely(JS_IsUninitialized(*var_ref->pvalue))) { + JS_ThrowReferenceErrorUninitialized(ctx, prs->atom); + return -1; + } + } + if (JS_AtomIsArrayIndex(ctx, &num_key, atom)) { + num_keys_count++; + } else if (kind == JS_ATOM_KIND_STRING) { + str_keys_count++; + } else { + sym_keys_count++; + } + } + } + } + + if (p->is_exotic) { + if (p->fast_array) { + if (flags & JS_GPN_STRING_MASK) { + num_keys_count += p->u.array.count; + } + } else if (p->class_id == JS_CLASS_STRING) { + if (flags & JS_GPN_STRING_MASK) { + num_keys_count += js_string_obj_get_length(ctx, JS_MKPTR(JS_TAG_OBJECT, p)); + } + } else { + const JSClassExoticMethods *em = ctx->rt->class_array[p->class_id].exotic; + if (em && em->get_own_property_names) { + if (em->get_own_property_names(ctx, &tab_exotic, &exotic_count, + JS_MKPTR(JS_TAG_OBJECT, p))) + return -1; + for(i = 0; i < exotic_count; i++) { + atom = tab_exotic[i].atom; + kind = JS_AtomGetKind(ctx, atom); + if (((flags >> kind) & 1) != 0) { + is_enumerable = FALSE; + if (flags & (JS_GPN_SET_ENUM | JS_GPN_ENUM_ONLY)) { + JSPropertyDescriptor desc; + int res; + /* set the "is_enumerable" field if necessary */ + res = JS_GetOwnPropertyInternal(ctx, &desc, p, atom); + if (res < 0) { + js_free_prop_enum(ctx, tab_exotic, exotic_count); + return -1; + } + if (res) { + is_enumerable = + ((desc.flags & JS_PROP_ENUMERABLE) != 0); + js_free_desc(ctx, &desc); + } + tab_exotic[i].is_enumerable = is_enumerable; + } + if (!(flags & JS_GPN_ENUM_ONLY) || is_enumerable) { + exotic_keys_count++; + } + } + } + } + } + } + + /* fill them */ + + atom_count = num_keys_count + str_keys_count + sym_keys_count + exotic_keys_count; + /* avoid allocating 0 bytes */ + tab_atom = js_malloc(ctx, sizeof(tab_atom[0]) * max_int(atom_count, 1)); + if (!tab_atom) { + js_free_prop_enum(ctx, tab_exotic, exotic_count); + return -1; + } + + num_index = 0; + str_index = num_keys_count; + sym_index = str_index + str_keys_count; + + num_sorted = TRUE; + sh = p->shape; + for(i = 0, prs = get_shape_prop(sh); i < sh->prop_count; i++, prs++) { + atom = prs->atom; + if (atom != JS_ATOM_NULL) { + is_enumerable = ((prs->flags & JS_PROP_ENUMERABLE) != 0); + kind = JS_AtomGetKind(ctx, atom); + if ((!(flags & JS_GPN_ENUM_ONLY) || is_enumerable) && + ((flags >> kind) & 1) != 0) { + if (JS_AtomIsArrayIndex(ctx, &num_key, atom)) { + j = num_index++; + num_sorted = FALSE; + } else if (kind == JS_ATOM_KIND_STRING) { + j = str_index++; + } else { + j = sym_index++; + } + tab_atom[j].atom = JS_DupAtom(ctx, atom); + tab_atom[j].is_enumerable = is_enumerable; + } + } + } + + if (p->is_exotic) { + int len; + if (p->fast_array) { + if (flags & JS_GPN_STRING_MASK) { + len = p->u.array.count; + goto add_array_keys; + } + } else if (p->class_id == JS_CLASS_STRING) { + if (flags & JS_GPN_STRING_MASK) { + len = js_string_obj_get_length(ctx, JS_MKPTR(JS_TAG_OBJECT, p)); + add_array_keys: + for(i = 0; i < len; i++) { + tab_atom[num_index].atom = __JS_AtomFromUInt32(i); + if (tab_atom[num_index].atom == JS_ATOM_NULL) { + js_free_prop_enum(ctx, tab_atom, num_index); + return -1; + } + tab_atom[num_index].is_enumerable = TRUE; + num_index++; + } + } + } else { + /* Note: exotic keys are not reordered and comes after the object own properties. */ + for(i = 0; i < exotic_count; i++) { + atom = tab_exotic[i].atom; + is_enumerable = tab_exotic[i].is_enumerable; + kind = JS_AtomGetKind(ctx, atom); + if ((!(flags & JS_GPN_ENUM_ONLY) || is_enumerable) && + ((flags >> kind) & 1) != 0) { + tab_atom[sym_index].atom = atom; + tab_atom[sym_index].is_enumerable = is_enumerable; + sym_index++; + } else { + JS_FreeAtom(ctx, atom); + } + } + js_free(ctx, tab_exotic); + } + } + + assert(num_index == num_keys_count); + assert(str_index == num_keys_count + str_keys_count); + assert(sym_index == atom_count); + + if (num_keys_count != 0 && !num_sorted) { + rqsort(tab_atom, num_keys_count, sizeof(tab_atom[0]), num_keys_cmp, + ctx); + } + *ptab = tab_atom; + *plen = atom_count; + return 0; +} + +int JS_GetOwnPropertyNames(JSContext *ctx, JSPropertyEnum **ptab, + uint32_t *plen, JSValue obj, int flags) +{ + if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT) { + JS_ThrowTypeErrorNotAnObject(ctx); + return -1; + } + return JS_GetOwnPropertyNamesInternal(ctx, ptab, plen, + JS_VALUE_GET_OBJ(obj), flags); +} + +/* Return -1 if exception, + FALSE if the property does not exist, TRUE if it exists. If TRUE is + returned, the property descriptor 'desc' is filled present. */ +static int JS_GetOwnPropertyInternal(JSContext *ctx, JSPropertyDescriptor *desc, + JSObject *p, JSAtom prop) +{ + JSShapeProperty *prs; + JSProperty *pr; + +retry: + prs = find_own_property(&pr, p, prop); + if (prs) { + if (desc) { + desc->flags = prs->flags & JS_PROP_C_W_E; + desc->getter = JS_UNDEFINED; + desc->setter = JS_UNDEFINED; + desc->value = JS_UNDEFINED; + if (unlikely(prs->flags & JS_PROP_TMASK)) { + if ((prs->flags & JS_PROP_TMASK) == JS_PROP_GETSET) { + desc->flags |= JS_PROP_GETSET; + if (pr->u.getset.getter) + desc->getter = js_dup(JS_MKPTR(JS_TAG_OBJECT, pr->u.getset.getter)); + if (pr->u.getset.setter) + desc->setter = js_dup(JS_MKPTR(JS_TAG_OBJECT, pr->u.getset.setter)); + } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF) { + JSValue val = *pr->u.var_ref->pvalue; + if (unlikely(JS_IsUninitialized(val))) { + JS_ThrowReferenceErrorUninitialized(ctx, prs->atom); + return -1; + } + desc->value = js_dup(val); + } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_AUTOINIT) { + /* Instantiate property and retry */ + if (JS_AutoInitProperty(ctx, p, prop, pr, prs)) + return -1; + goto retry; + } + } else { + desc->value = js_dup(pr->u.value); + } + } else { + /* for consistency, send the exception even if desc is NULL */ + if (unlikely((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF)) { + if (unlikely(JS_IsUninitialized(*pr->u.var_ref->pvalue))) { + JS_ThrowReferenceErrorUninitialized(ctx, prs->atom); + return -1; + } + } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_AUTOINIT) { + /* nothing to do: delay instantiation until actual value and/or attributes are read */ + } + } + return TRUE; + } + if (p->is_exotic) { + if (p->fast_array) { + /* specific case for fast arrays */ + if (__JS_AtomIsTaggedInt(prop)) { + uint32_t idx; + idx = __JS_AtomToUInt32(prop); + if (idx < p->u.array.count) { + if (desc) { + desc->flags = JS_PROP_WRITABLE | JS_PROP_ENUMERABLE | + JS_PROP_CONFIGURABLE; + desc->getter = JS_UNDEFINED; + desc->setter = JS_UNDEFINED; + desc->value = JS_GetPropertyUint32(ctx, JS_MKPTR(JS_TAG_OBJECT, p), idx); + } + return TRUE; + } + } + } else { + const JSClassExoticMethods *em = ctx->rt->class_array[p->class_id].exotic; + if (em && em->get_own_property) { + return em->get_own_property(ctx, desc, + JS_MKPTR(JS_TAG_OBJECT, p), prop); + } + } + } + return FALSE; +} + +int JS_GetOwnProperty(JSContext *ctx, JSPropertyDescriptor *desc, + JSValue obj, JSAtom prop) +{ + if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT) { + JS_ThrowTypeErrorNotAnObject(ctx); + return -1; + } + return JS_GetOwnPropertyInternal(ctx, desc, JS_VALUE_GET_OBJ(obj), prop); +} + +void JS_FreePropertyEnum(JSContext *ctx, JSPropertyEnum *tab, + uint32_t len) +{ + js_free_prop_enum(ctx, tab, len); +} + +/* return -1 if exception (Proxy object only) or TRUE/FALSE */ +int JS_IsExtensible(JSContext *ctx, JSValue obj) +{ + JSObject *p; + + if (unlikely(JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)) + return FALSE; + p = JS_VALUE_GET_OBJ(obj); + if (unlikely(p->class_id == JS_CLASS_PROXY)) + return js_proxy_isExtensible(ctx, obj); + else + return p->extensible; +} + +/* return -1 if exception (Proxy object only) or TRUE/FALSE */ +int JS_PreventExtensions(JSContext *ctx, JSValue obj) +{ + JSObject *p; + + if (unlikely(JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)) + return FALSE; + p = JS_VALUE_GET_OBJ(obj); + if (unlikely(p->class_id == JS_CLASS_PROXY)) + return js_proxy_preventExtensions(ctx, obj); + p->extensible = FALSE; + return TRUE; +} + +/* return -1 if exception otherwise TRUE or FALSE */ +int JS_HasProperty(JSContext *ctx, JSValue obj, JSAtom prop) +{ + JSObject *p; + int ret; + JSValue obj1; + + if (unlikely(JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)) + return FALSE; + p = JS_VALUE_GET_OBJ(obj); + for(;;) { + if (p->is_exotic) { + const JSClassExoticMethods *em = ctx->rt->class_array[p->class_id].exotic; + if (em && em->has_property) { + /* has_property can free the prototype */ + obj1 = js_dup(JS_MKPTR(JS_TAG_OBJECT, p)); + ret = em->has_property(ctx, obj1, prop); + JS_FreeValue(ctx, obj1); + return ret; + } + } + /* JS_GetOwnPropertyInternal can free the prototype */ + js_dup(JS_MKPTR(JS_TAG_OBJECT, p)); + ret = JS_GetOwnPropertyInternal(ctx, NULL, p, prop); + JS_FreeValue(ctx, JS_MKPTR(JS_TAG_OBJECT, p)); + if (ret != 0) + return ret; + if (p->class_id >= JS_CLASS_UINT8C_ARRAY && + p->class_id <= JS_CLASS_FLOAT64_ARRAY) { + ret = JS_AtomIsNumericIndex(ctx, prop); + if (ret != 0) { + if (ret < 0) + return -1; + return FALSE; + } + } + p = p->shape->proto; + if (!p) + break; + } + return FALSE; +} + +/* val must be a symbol */ +static JSAtom js_symbol_to_atom(JSContext *ctx, JSValue val) +{ + JSAtomStruct *p = JS_VALUE_GET_PTR(val); + return js_get_atom_index(ctx->rt, p); +} + +/* return JS_ATOM_NULL in case of exception */ +JSAtom JS_ValueToAtom(JSContext *ctx, JSValue val) +{ + JSAtom atom; + uint32_t tag; + tag = JS_VALUE_GET_TAG(val); + if (tag == JS_TAG_INT && + (uint32_t)JS_VALUE_GET_INT(val) <= JS_ATOM_MAX_INT) { + /* fast path for integer values */ + atom = __JS_AtomFromUInt32(JS_VALUE_GET_INT(val)); + } else if (tag == JS_TAG_SYMBOL) { + JSAtomStruct *p = JS_VALUE_GET_PTR(val); + atom = JS_DupAtom(ctx, js_get_atom_index(ctx->rt, p)); + } else { + JSValue str; + str = JS_ToPropertyKey(ctx, val); + if (JS_IsException(str)) + return JS_ATOM_NULL; + if (JS_VALUE_GET_TAG(str) == JS_TAG_SYMBOL) { + atom = js_symbol_to_atom(ctx, str); + } else { + atom = JS_NewAtomStr(ctx, JS_VALUE_GET_STRING(str)); + } + } + return atom; +} + +static BOOL js_get_fast_array_element(JSContext *ctx, JSObject *p, + uint32_t idx, JSValue *pval) +{ + switch(p->class_id) { + case JS_CLASS_ARRAY: + case JS_CLASS_ARGUMENTS: + if (unlikely(idx >= p->u.array.count)) return FALSE; + *pval = js_dup(p->u.array.u.values[idx]); + return TRUE; + case JS_CLASS_INT8_ARRAY: + if (unlikely(idx >= p->u.array.count)) return FALSE; + *pval = js_int32(p->u.array.u.int8_ptr[idx]); + return TRUE; + case JS_CLASS_UINT8C_ARRAY: + case JS_CLASS_UINT8_ARRAY: + if (unlikely(idx >= p->u.array.count)) return FALSE; + *pval = js_int32(p->u.array.u.uint8_ptr[idx]); + return TRUE; + case JS_CLASS_INT16_ARRAY: + if (unlikely(idx >= p->u.array.count)) return FALSE; + *pval = js_int32(p->u.array.u.int16_ptr[idx]); + return TRUE; + case JS_CLASS_UINT16_ARRAY: + if (unlikely(idx >= p->u.array.count)) return FALSE; + *pval = js_int32(p->u.array.u.uint16_ptr[idx]); + return TRUE; + case JS_CLASS_INT32_ARRAY: + if (unlikely(idx >= p->u.array.count)) return FALSE; + *pval = js_int32(p->u.array.u.int32_ptr[idx]); + return TRUE; + case JS_CLASS_UINT32_ARRAY: + if (unlikely(idx >= p->u.array.count)) return FALSE; + *pval = js_uint32(p->u.array.u.uint32_ptr[idx]); + return TRUE; + case JS_CLASS_BIG_INT64_ARRAY: + if (unlikely(idx >= p->u.array.count)) return FALSE; + *pval = JS_NewBigInt64(ctx, p->u.array.u.int64_ptr[idx]); + return TRUE; + case JS_CLASS_BIG_UINT64_ARRAY: + if (unlikely(idx >= p->u.array.count)) return FALSE; + *pval = JS_NewBigUint64(ctx, p->u.array.u.uint64_ptr[idx]); + return TRUE; + case JS_CLASS_FLOAT16_ARRAY: + if (unlikely(idx >= p->u.array.count)) return FALSE; + *pval = js_float64(fromfp16(p->u.array.u.fp16_ptr[idx])); + return TRUE; + case JS_CLASS_FLOAT32_ARRAY: + if (unlikely(idx >= p->u.array.count)) return FALSE; + *pval = js_float64(p->u.array.u.float_ptr[idx]); + return TRUE; + case JS_CLASS_FLOAT64_ARRAY: + if (unlikely(idx >= p->u.array.count)) return FALSE; + *pval = js_float64(p->u.array.u.double_ptr[idx]); + return TRUE; + default: + return FALSE; + } +} + +static JSValue JS_GetPropertyValue(JSContext *ctx, JSValue this_obj, + JSValue prop) +{ + JSAtom atom; + JSValue ret; + uint32_t tag; + + tag = JS_VALUE_GET_TAG(this_obj); + if (likely(tag == JS_TAG_OBJECT)) { + if (JS_VALUE_GET_TAG(prop) == JS_TAG_INT) { + JSObject *p = JS_VALUE_GET_OBJ(this_obj); + uint32_t idx = JS_VALUE_GET_INT(prop); + JSValue val; + /* fast path for array and typed array access */ + if (js_get_fast_array_element(ctx, p, idx, &val)) + return val; + } + } else { + switch(tag) { + case JS_TAG_NULL: + JS_FreeValue(ctx, prop); + return JS_ThrowTypeError(ctx, "cannot read property of null"); + case JS_TAG_UNDEFINED: + JS_FreeValue(ctx, prop); + return JS_ThrowTypeError(ctx, "cannot read property of undefined"); + } + } + atom = JS_ValueToAtom(ctx, prop); + JS_FreeValue(ctx, prop); + if (unlikely(atom == JS_ATOM_NULL)) + return JS_EXCEPTION; + ret = JS_GetProperty(ctx, this_obj, atom); + JS_FreeAtom(ctx, atom); + return ret; +} + +JSValue JS_GetPropertyUint32(JSContext *ctx, JSValue this_obj, + uint32_t idx) +{ + return JS_GetPropertyInt64(ctx, this_obj, idx); +} + +/* Check if an object has a generalized numeric property. Return value: + -1 for exception, *pval set to JS_EXCEPTION + TRUE if property exists, stored into *pval, + FALSE if property does not exist. *pval set to JS_UNDEFINED. + */ +static int JS_TryGetPropertyInt64(JSContext *ctx, JSValue obj, int64_t idx, JSValue *pval) +{ + JSValue val; + JSAtom prop; + int present; + + if (likely(JS_VALUE_GET_TAG(obj) == JS_TAG_OBJECT && + (uint64_t)idx <= INT32_MAX)) { + /* fast path for array and typed array access */ + JSObject *p = JS_VALUE_GET_OBJ(obj); + if (js_get_fast_array_element(ctx, p, idx, pval)) + return TRUE; + } + val = JS_EXCEPTION; + present = -1; + prop = JS_NewAtomInt64(ctx, idx); + if (likely(prop != JS_ATOM_NULL)) { + present = JS_HasProperty(ctx, obj, prop); + if (present > 0) { + val = JS_GetProperty(ctx, obj, prop); + if (unlikely(JS_IsException(val))) + present = -1; + } else if (present == FALSE) { + val = JS_UNDEFINED; + } + JS_FreeAtom(ctx, prop); + } + *pval = val; + return present; +} + +JSValue JS_GetPropertyInt64(JSContext *ctx, JSValue obj, int64_t idx) +{ + JSAtom prop; + JSValue val; + + if (likely(JS_VALUE_GET_TAG(obj) == JS_TAG_OBJECT && + (uint64_t)idx <= INT32_MAX)) { + /* fast path for array and typed array access */ + JSObject *p = JS_VALUE_GET_OBJ(obj); + if (js_get_fast_array_element(ctx, p, idx, &val)) + return val; + } + prop = JS_NewAtomInt64(ctx, idx); + if (prop == JS_ATOM_NULL) + return JS_EXCEPTION; + + val = JS_GetProperty(ctx, obj, prop); + JS_FreeAtom(ctx, prop); + return val; +} + +/* `prop` may be pure ASCII or UTF-8 encoded */ +JSValue JS_GetPropertyStr(JSContext *ctx, JSValue this_obj, + const char *prop) +{ + JSAtom atom; + JSValue ret; + atom = JS_NewAtom(ctx, prop); + ret = JS_GetProperty(ctx, this_obj, atom); + JS_FreeAtom(ctx, atom); + return ret; +} + +/* Note: the property value is not initialized. Return NULL if memory + error. */ +static JSProperty *add_property(JSContext *ctx, + JSObject *p, JSAtom prop, int prop_flags) +{ + JSShape *sh, *new_sh; + + sh = p->shape; + if (sh->is_hashed) { + /* try to find an existing shape */ + new_sh = find_hashed_shape_prop(ctx->rt, sh, prop, prop_flags); + if (new_sh) { + /* matching shape found: use it */ + /* the property array may need to be resized */ + if (new_sh->prop_size != sh->prop_size) { + JSProperty *new_prop; + new_prop = js_realloc(ctx, p->prop, sizeof(p->prop[0]) * + new_sh->prop_size); + if (!new_prop) + return NULL; + p->prop = new_prop; + } + p->shape = js_dup_shape(new_sh); + js_free_shape(ctx->rt, sh); + return &p->prop[new_sh->prop_count - 1]; + } else if (sh->header.ref_count != 1) { + /* if the shape is shared, clone it */ + new_sh = js_clone_shape(ctx, sh); + if (!new_sh) + return NULL; + /* hash the cloned shape */ + new_sh->is_hashed = TRUE; + js_shape_hash_link(ctx->rt, new_sh); + js_free_shape(ctx->rt, p->shape); + p->shape = new_sh; + } + } + assert(p->shape->header.ref_count == 1); + if (add_shape_property(ctx, &p->shape, p, prop, prop_flags)) + return NULL; + return &p->prop[p->shape->prop_count - 1]; +} + +/* can be called on Array or Arguments objects. return < 0 if + memory alloc error. */ +static no_inline __exception int convert_fast_array_to_array(JSContext *ctx, + JSObject *p) +{ + JSProperty *pr; + JSShape *sh; + JSValue *tab; + uint32_t i, len, new_count; + + if (js_shape_prepare_update(ctx, p, NULL)) + return -1; + len = p->u.array.count; + /* resize the properties once to simplify the error handling */ + sh = p->shape; + new_count = sh->prop_count + len; + if (new_count > sh->prop_size) { + if (resize_properties(ctx, &p->shape, p, new_count)) + return -1; + } + + tab = p->u.array.u.values; + for(i = 0; i < len; i++) { + /* add_property cannot fail here but + __JS_AtomFromUInt32(i) fails for i > INT32_MAX */ + pr = add_property(ctx, p, __JS_AtomFromUInt32(i), JS_PROP_C_W_E); + pr->u.value = *tab++; + } + js_free(ctx, p->u.array.u.values); + p->u.array.count = 0; + p->u.array.u.values = NULL; /* fail safe */ + p->u.array.u1.size = 0; + p->fast_array = 0; + return 0; +} + +static int delete_property(JSContext *ctx, JSObject *p, JSAtom atom) +{ + JSShape *sh; + JSShapeProperty *pr, *lpr, *prop; + JSProperty *pr1; + uint32_t lpr_idx; + intptr_t h, h1; + + redo: + sh = p->shape; + h1 = atom & sh->prop_hash_mask; + h = prop_hash_end(sh)[-h1 - 1]; + prop = get_shape_prop(sh); + lpr = NULL; + lpr_idx = 0; /* prevent warning */ + while (h != 0) { + pr = &prop[h - 1]; + if (likely(pr->atom == atom)) { + /* found ! */ + if (!(pr->flags & JS_PROP_CONFIGURABLE)) + return FALSE; + /* realloc the shape if needed */ + if (lpr) + lpr_idx = lpr - get_shape_prop(sh); + if (js_shape_prepare_update(ctx, p, &pr)) + return -1; + sh = p->shape; + /* remove property */ + if (lpr) { + lpr = get_shape_prop(sh) + lpr_idx; + lpr->hash_next = pr->hash_next; + } else { + prop_hash_end(sh)[-h1 - 1] = pr->hash_next; + } + sh->deleted_prop_count++; + /* free the entry */ + pr1 = &p->prop[h - 1]; + free_property(ctx->rt, pr1, pr->flags); + JS_FreeAtom(ctx, pr->atom); + /* put default values */ + pr->flags = 0; + pr->atom = JS_ATOM_NULL; + pr1->u.value = JS_UNDEFINED; + + /* compact the properties if too many deleted properties */ + if (sh->deleted_prop_count >= 8 && + sh->deleted_prop_count >= ((unsigned)sh->prop_count / 2)) { + compact_properties(ctx, p); + } + return TRUE; + } + lpr = pr; + h = pr->hash_next; + } + + if (p->is_exotic) { + if (p->fast_array) { + uint32_t idx; + if (JS_AtomIsArrayIndex(ctx, &idx, atom) && + idx < p->u.array.count) { + if (p->class_id == JS_CLASS_ARRAY || + p->class_id == JS_CLASS_ARGUMENTS) { + /* Special case deleting the last element of a fast Array */ + if (idx == p->u.array.count - 1) { + JS_FreeValue(ctx, p->u.array.u.values[idx]); + p->u.array.count = idx; + return TRUE; + } + if (convert_fast_array_to_array(ctx, p)) + return -1; + goto redo; + } else { + return FALSE; + } + } + } else { + const JSClassExoticMethods *em = ctx->rt->class_array[p->class_id].exotic; + if (em && em->delete_property) { + return em->delete_property(ctx, JS_MKPTR(JS_TAG_OBJECT, p), atom); + } + } + } + /* not found */ + return TRUE; +} + +static int call_setter(JSContext *ctx, JSObject *setter, + JSValue this_obj, JSValue val, int flags) +{ + JSValue ret, func; + if (likely(setter)) { + func = JS_MKPTR(JS_TAG_OBJECT, setter); + /* Note: the field could be removed in the setter */ + func = js_dup(func); + ret = JS_CallFree(ctx, func, this_obj, 1, &val); + JS_FreeValue(ctx, val); + if (JS_IsException(ret)) + return -1; + JS_FreeValue(ctx, ret); + return TRUE; + } else { + JS_FreeValue(ctx, val); + if ((flags & JS_PROP_THROW) || + ((flags & JS_PROP_THROW_STRICT) && is_strict_mode(ctx))) { + JS_ThrowTypeError(ctx, "no setter for property"); + return -1; + } + return FALSE; + } +} + +/* set the array length and remove the array elements if necessary. */ +static int set_array_length(JSContext *ctx, JSObject *p, JSValue val, + int flags) +{ + uint32_t len, idx, cur_len; + int i, ret; + + /* Note: this call can reallocate the properties of 'p' */ + ret = JS_ToArrayLengthFree(ctx, &len, val, FALSE); + if (ret) + return -1; + /* JS_ToArrayLengthFree() must be done before the read-only test */ + if (unlikely(!(p->shape->prop[0].flags & JS_PROP_WRITABLE))) + return JS_ThrowTypeErrorReadOnly(ctx, flags, JS_ATOM_length); + + if (likely(p->fast_array)) { + uint32_t old_len = p->u.array.count; + if (len < old_len) { + for(i = len; i < old_len; i++) { + JS_FreeValue(ctx, p->u.array.u.values[i]); + } + p->u.array.count = len; + } + p->prop[0].u.value = js_uint32(len); + } else { + /* Note: length is always a uint32 because the object is an + array */ + JS_ToUint32(ctx, &cur_len, p->prop[0].u.value); + if (len < cur_len) { + uint32_t d; + JSShape *sh; + JSShapeProperty *pr; + + d = cur_len - len; + sh = p->shape; + if (d <= sh->prop_count) { + JSAtom atom; + + /* faster to iterate */ + while (cur_len > len) { + atom = JS_NewAtomUInt32(ctx, cur_len - 1); + ret = delete_property(ctx, p, atom); + JS_FreeAtom(ctx, atom); + if (unlikely(!ret)) { + /* unlikely case: property is not + configurable */ + break; + } + cur_len--; + } + } else { + /* faster to iterate thru all the properties. Need two + passes in case one of the property is not + configurable */ + cur_len = len; + for(i = 0, pr = get_shape_prop(sh); i < sh->prop_count; + i++, pr++) { + if (pr->atom != JS_ATOM_NULL && + JS_AtomIsArrayIndex(ctx, &idx, pr->atom)) { + if (idx >= cur_len && + !(pr->flags & JS_PROP_CONFIGURABLE)) { + cur_len = idx + 1; + } + } + } + + for(i = 0, pr = get_shape_prop(sh); i < sh->prop_count; + i++, pr++) { + if (pr->atom != JS_ATOM_NULL && + JS_AtomIsArrayIndex(ctx, &idx, pr->atom)) { + if (idx >= cur_len) { + /* remove the property */ + delete_property(ctx, p, pr->atom); + /* WARNING: the shape may have been modified */ + sh = p->shape; + pr = get_shape_prop(sh) + i; + } + } + } + } + } else { + cur_len = len; + } + set_value(ctx, &p->prop[0].u.value, js_uint32(cur_len)); + if (unlikely(cur_len > len)) { + return JS_ThrowTypeErrorOrFalse(ctx, flags, "not configurable"); + } + } + return TRUE; +} + +/* return -1 if exception */ +static int expand_fast_array(JSContext *ctx, JSObject *p, uint32_t new_len) +{ + uint32_t new_size; + size_t slack; + JSValue *new_array_prop; + /* XXX: potential arithmetic overflow */ + new_size = max_int(new_len, p->u.array.u1.size * 3 / 2); + new_array_prop = js_realloc2(ctx, p->u.array.u.values, sizeof(JSValue) * new_size, &slack); + if (!new_array_prop) + return -1; + new_size += slack / sizeof(*new_array_prop); + p->u.array.u.values = new_array_prop; + p->u.array.u1.size = new_size; + return 0; +} + +/* Preconditions: 'p' must be of class JS_CLASS_ARRAY, p->fast_array = + TRUE and p->extensible = TRUE */ +static int add_fast_array_element(JSContext *ctx, JSObject *p, + JSValue val, int flags) +{ + uint32_t new_len, array_len; + /* extend the array by one */ + /* XXX: convert to slow array if new_len > 2^31-1 elements */ + new_len = p->u.array.count + 1; + /* update the length if necessary. We assume that if the length is + not an integer, then if it >= 2^31. */ + if (likely(JS_VALUE_GET_TAG(p->prop[0].u.value) == JS_TAG_INT)) { + array_len = JS_VALUE_GET_INT(p->prop[0].u.value); + if (new_len > array_len) { + if (unlikely(!(get_shape_prop(p->shape)->flags & JS_PROP_WRITABLE))) { + JS_FreeValue(ctx, val); + return JS_ThrowTypeErrorReadOnly(ctx, flags, JS_ATOM_length); + } + p->prop[0].u.value = js_int32(new_len); + } + } + if (unlikely(new_len > p->u.array.u1.size)) { + if (expand_fast_array(ctx, p, new_len)) { + JS_FreeValue(ctx, val); + return -1; + } + } + p->u.array.u.values[new_len - 1] = val; + p->u.array.count = new_len; + return TRUE; +} + +static void js_free_desc(JSContext *ctx, JSPropertyDescriptor *desc) +{ + JS_FreeValue(ctx, desc->getter); + JS_FreeValue(ctx, desc->setter); + JS_FreeValue(ctx, desc->value); +} + +/* return -1 in case of exception or TRUE or FALSE. Warning: 'val' is + freed by the function. 'flags' is a bitmask of JS_PROP_NO_ADD, + JS_PROP_THROW or JS_PROP_THROW_STRICT. If JS_PROP_NO_ADD is set, + the new property is not added and an error is raised. + 'obj' must be an object when obj != this_obj. + */ +static int JS_SetPropertyInternal2(JSContext *ctx, JSValue obj, JSAtom prop, + JSValue val, JSValue this_obj, int flags, + JSInlineCacheUpdate *icu) +{ + JSObject *p, *p1; + JSShapeProperty *prs; + JSProperty *pr; + JSPropertyDescriptor desc; + int ret; + uint32_t offset = 0; + + switch(JS_VALUE_GET_TAG(this_obj)) { + case JS_TAG_NULL: + JS_ThrowTypeErrorAtom(ctx, "cannot set property '%s' of null", prop); + goto fail; + case JS_TAG_UNDEFINED: + JS_ThrowTypeErrorAtom(ctx, "cannot set property '%s' of undefined", prop); + goto fail; + case JS_TAG_OBJECT: + p = JS_VALUE_GET_OBJ(this_obj); + p1 = JS_VALUE_GET_OBJ(obj); + if (p == p1) + break; + goto retry2; + default: + if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT) + obj = JS_GetPrototypePrimitive(ctx, obj); + p = NULL; + p1 = JS_VALUE_GET_OBJ(obj); + goto prototype_lookup; + } + +retry: + prs = find_own_property_ic(&pr, p1, prop, &offset); + if (prs) { + if (likely((prs->flags & (JS_PROP_TMASK | JS_PROP_WRITABLE | + JS_PROP_LENGTH)) == JS_PROP_WRITABLE)) { + /* fast case */ + add_ic_slot(ctx, icu, prop, p, offset); + set_value(ctx, &pr->u.value, val); + return TRUE; + } else if (prs->flags & JS_PROP_LENGTH) { + assert(p->class_id == JS_CLASS_ARRAY); + assert(prop == JS_ATOM_length); + return set_array_length(ctx, p, val, flags); + } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_GETSET) { + return call_setter(ctx, pr->u.getset.setter, this_obj, val, flags); + } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF) { + /* JS_PROP_WRITABLE is always true for variable + references, but they are write protected in module name + spaces. */ + if (p->class_id == JS_CLASS_MODULE_NS) + goto read_only_prop; + set_value(ctx, pr->u.var_ref->pvalue, val); + return TRUE; + } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_AUTOINIT) { + /* Instantiate property and retry (potentially useless) */ + if (JS_AutoInitProperty(ctx, p, prop, pr, prs)) + goto fail; + goto retry; + } else { + goto read_only_prop; + } + } + + for(;;) { + if (p1->is_exotic) { + if (p1->fast_array) { + if (__JS_AtomIsTaggedInt(prop)) { + uint32_t idx = __JS_AtomToUInt32(prop); + if (idx < p1->u.array.count) { + if (unlikely(p == p1)) + return JS_SetPropertyValue(ctx, this_obj, js_int32(idx), val, flags); + else + break; + } else if (p1->class_id >= JS_CLASS_UINT8C_ARRAY && + p1->class_id <= JS_CLASS_FLOAT64_ARRAY) { + goto typed_array_oob; + } + } else if (p1->class_id >= JS_CLASS_UINT8C_ARRAY && + p1->class_id <= JS_CLASS_FLOAT64_ARRAY) { + ret = JS_AtomIsNumericIndex(ctx, prop); + if (ret != 0) { + if (ret < 0) + goto fail; + typed_array_oob: + // per spec: evaluate value for side effects + if (p1->class_id == JS_CLASS_BIG_INT64_ARRAY || + p1->class_id == JS_CLASS_BIG_UINT64_ARRAY) { + int64_t v; + if (JS_ToBigInt64Free(ctx, &v, val)) + return -1; + } else { + val = JS_ToNumberFree(ctx, val); + JS_FreeValue(ctx, val); + if (JS_IsException(val)) + return -1; + } + return TRUE; + } + } + } else { + const JSClassExoticMethods *em = ctx->rt->class_array[p1->class_id].exotic; + if (em) { + JSValue obj1; + if (em->set_property) { + /* set_property can free the prototype */ + obj1 = js_dup(JS_MKPTR(JS_TAG_OBJECT, p1)); + ret = em->set_property(ctx, obj1, prop, + val, this_obj, flags); + JS_FreeValue(ctx, obj1); + JS_FreeValue(ctx, val); + return ret; + } + if (em->get_own_property) { + /* get_own_property can free the prototype */ + obj1 = js_dup(JS_MKPTR(JS_TAG_OBJECT, p1)); + ret = em->get_own_property(ctx, &desc, + obj1, prop); + JS_FreeValue(ctx, obj1); + if (ret < 0) + goto fail; + if (ret) { + if (desc.flags & JS_PROP_GETSET) { + JSObject *setter; + if (JS_IsUndefined(desc.setter)) + setter = NULL; + else + setter = JS_VALUE_GET_OBJ(desc.setter); + ret = call_setter(ctx, setter, this_obj, val, flags); + JS_FreeValue(ctx, desc.getter); + JS_FreeValue(ctx, desc.setter); + return ret; + } else { + JS_FreeValue(ctx, desc.value); + if (!(desc.flags & JS_PROP_WRITABLE)) + goto read_only_prop; + if (likely(p == p1)) { + ret = JS_DefineProperty(ctx, this_obj, prop, val, + JS_UNDEFINED, JS_UNDEFINED, + JS_PROP_HAS_VALUE); + JS_FreeValue(ctx, val); + return ret; + } else { + break; + } + } + } + } + } + } + } + p1 = p1->shape->proto; + prototype_lookup: + if (!p1) + break; + + retry2: + prs = find_own_property(&pr, p1, prop); + if (prs) { + if ((prs->flags & JS_PROP_TMASK) == JS_PROP_GETSET) { + return call_setter(ctx, pr->u.getset.setter, this_obj, val, flags); + } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_AUTOINIT) { + /* Instantiate property and retry (potentially useless) */ + if (JS_AutoInitProperty(ctx, p1, prop, pr, prs)) + return -1; + goto retry2; + } else if (!(prs->flags & JS_PROP_WRITABLE)) { + goto read_only_prop; + } + } + } + + if (unlikely(flags & JS_PROP_NO_ADD)) { + JS_ThrowReferenceErrorNotDefined(ctx, prop); + goto fail; + } + + if (unlikely(!p)) { + ret = JS_ThrowTypeErrorOrFalse(ctx, flags, "not an object"); + goto done; + } + + if (unlikely(!p->extensible)) { + ret = JS_ThrowTypeErrorOrFalse(ctx, flags, "object is not extensible"); + goto done; + } + + if (p == JS_VALUE_GET_OBJ(obj)) { + if (p->is_exotic) { + if (p->class_id == JS_CLASS_ARRAY && p->fast_array && + __JS_AtomIsTaggedInt(prop)) { + uint32_t idx = __JS_AtomToUInt32(prop); + if (idx == p->u.array.count) { + /* fast case */ + return add_fast_array_element(ctx, p, val, flags); + } + } + goto generic_create_prop; + } else { + pr = add_property(ctx, p, prop, JS_PROP_C_W_E); + if (!pr) + goto fail; + pr->u.value = val; + return TRUE; + } + } + + // TODO(bnoordhuis) return JSProperty slot and update in place + // when plain property (not is_exotic/setter/etc.) to avoid + // calling find_own_property() thrice? + ret = JS_GetOwnPropertyInternal(ctx, &desc, p, prop); + if (ret < 0) + goto fail; + + if (ret) { + JS_FreeValue(ctx, desc.value); + if (desc.flags & JS_PROP_GETSET) { + JS_FreeValue(ctx, desc.getter); + JS_FreeValue(ctx, desc.setter); + ret = JS_ThrowTypeErrorOrFalse(ctx, flags, "setter is forbidden"); + goto done; + } else if (!(desc.flags & JS_PROP_WRITABLE) || + p->class_id == JS_CLASS_MODULE_NS) { + read_only_prop: + ret = JS_ThrowTypeErrorReadOnly(ctx, flags, prop); + goto done; + } + ret = JS_DefineProperty(ctx, this_obj, prop, val, + JS_UNDEFINED, JS_UNDEFINED, + JS_PROP_HAS_VALUE); + } else { + generic_create_prop: + ret = JS_CreateProperty(ctx, p, prop, val, JS_UNDEFINED, JS_UNDEFINED, + flags | + JS_PROP_HAS_VALUE | + JS_PROP_HAS_ENUMERABLE | + JS_PROP_HAS_WRITABLE | + JS_PROP_HAS_CONFIGURABLE | + JS_PROP_C_W_E); + } + +done: + JS_FreeValue(ctx, val); + return ret; +fail: + JS_FreeValue(ctx, val); + return -1; +} + +static int JS_SetPropertyInternal(JSContext *ctx, JSValue this_obj, + JSAtom prop, JSValue val, int flags) +{ + return JS_SetPropertyInternal2(ctx, this_obj, prop, val, this_obj, + flags, NULL); +} + +int JS_SetProperty(JSContext *ctx, JSValue this_obj, JSAtom prop, JSValue val) +{ + return JS_SetPropertyInternal2(ctx, this_obj, prop, val, this_obj, JS_PROP_THROW, NULL); +} + +// XXX(bnoordhuis) only used by OP_put_field_ic, maybe inline at call site +static int JS_SetPropertyInternalWithIC(JSContext *ctx, JSValue this_obj, + JSAtom prop, JSValue val, int flags, + JSInlineCacheUpdate *icu) { + uint32_t tag, offset; + JSObject *p; + tag = JS_VALUE_GET_TAG(this_obj); + if (unlikely(tag != JS_TAG_OBJECT)) + goto slow_path; + p = JS_VALUE_GET_OBJ(this_obj); + offset = get_ic_prop_offset(icu, p->shape); + if (likely(offset != INLINE_CACHE_MISS)) { + set_value(ctx, &p->prop[offset].u.value, val); + return TRUE; + } +slow_path: + return JS_SetPropertyInternal2(ctx, this_obj, prop, val, this_obj, + flags, icu); +} + +/* flags can be JS_PROP_THROW or JS_PROP_THROW_STRICT */ +static int JS_SetPropertyValue(JSContext *ctx, JSValue this_obj, + JSValue prop, JSValue val, int flags) +{ + if (likely(JS_VALUE_GET_TAG(this_obj) == JS_TAG_OBJECT && + JS_VALUE_GET_TAG(prop) == JS_TAG_INT)) { + JSObject *p; + uint32_t idx; + double d; + int32_t v; + + /* fast path for array access */ + p = JS_VALUE_GET_OBJ(this_obj); + idx = JS_VALUE_GET_INT(prop); + switch(p->class_id) { + case JS_CLASS_ARRAY: + if (unlikely(idx >= (uint32_t)p->u.array.count)) { + JSObject *p1; + JSShape *sh1; + + /* fast path to add an element to the array */ + if (idx != (uint32_t)p->u.array.count || + !p->fast_array || !p->extensible) + goto slow_path; + /* check if prototype chain has a numeric property */ + p1 = p->shape->proto; + while (p1 != NULL) { + sh1 = p1->shape; + if (p1->class_id == JS_CLASS_ARRAY) { + if (unlikely(!p1->fast_array)) + goto slow_path; + } else if (p1->class_id == JS_CLASS_OBJECT) { + if (unlikely(sh1->has_small_array_index)) + goto slow_path; + } else { + goto slow_path; + } + p1 = sh1->proto; + } + /* add element */ + return add_fast_array_element(ctx, p, val, flags); + } + set_value(ctx, &p->u.array.u.values[idx], val); + break; + case JS_CLASS_ARGUMENTS: + if (unlikely(idx >= (uint32_t)p->u.array.count)) + goto slow_path; + set_value(ctx, &p->u.array.u.values[idx], val); + break; + case JS_CLASS_UINT8C_ARRAY: + if (JS_ToUint8ClampFree(ctx, &v, val)) + goto ta_cvt_fail; + /* Note: the conversion can detach the typed array, so the + array bound check must be done after */ + if (unlikely(idx >= (uint32_t)p->u.array.count)) + goto ta_out_of_bound; + p->u.array.u.uint8_ptr[idx] = v; + break; + case JS_CLASS_INT8_ARRAY: + case JS_CLASS_UINT8_ARRAY: + if (JS_ToInt32Free(ctx, &v, val)) + goto ta_cvt_fail; + if (unlikely(idx >= (uint32_t)p->u.array.count)) + goto ta_out_of_bound; + p->u.array.u.uint8_ptr[idx] = v; + break; + case JS_CLASS_INT16_ARRAY: + case JS_CLASS_UINT16_ARRAY: + if (JS_ToInt32Free(ctx, &v, val)) + goto ta_cvt_fail; + if (unlikely(idx >= (uint32_t)p->u.array.count)) + goto ta_out_of_bound; + p->u.array.u.uint16_ptr[idx] = v; + break; + case JS_CLASS_INT32_ARRAY: + case JS_CLASS_UINT32_ARRAY: + if (JS_ToInt32Free(ctx, &v, val)) + goto ta_cvt_fail; + if (unlikely(idx >= (uint32_t)p->u.array.count)) + goto ta_out_of_bound; + p->u.array.u.uint32_ptr[idx] = v; + break; + case JS_CLASS_BIG_INT64_ARRAY: + case JS_CLASS_BIG_UINT64_ARRAY: + /* XXX: need specific conversion function */ + { + int64_t v; + if (JS_ToBigInt64Free(ctx, &v, val)) + goto ta_cvt_fail; + if (unlikely(idx >= (uint32_t)p->u.array.count)) + goto ta_out_of_bound; + p->u.array.u.uint64_ptr[idx] = v; + } + break; + case JS_CLASS_FLOAT16_ARRAY: + if (JS_ToFloat64Free(ctx, &d, val)) + goto ta_cvt_fail; + if (unlikely(idx >= (uint32_t)p->u.array.count)) + goto ta_out_of_bound; + p->u.array.u.fp16_ptr[idx] = tofp16(d); + break; + case JS_CLASS_FLOAT32_ARRAY: + if (JS_ToFloat64Free(ctx, &d, val)) + goto ta_cvt_fail; + if (unlikely(idx >= (uint32_t)p->u.array.count)) + goto ta_out_of_bound; + p->u.array.u.float_ptr[idx] = d; + break; + case JS_CLASS_FLOAT64_ARRAY: + if (JS_ToFloat64Free(ctx, &d, val)) { + ta_cvt_fail: + if (flags & JS_PROP_REFLECT_DEFINE_PROPERTY) { + JS_FreeValue(ctx, JS_GetException(ctx)); + return FALSE; + } + return -1; + } + if (unlikely(idx >= (uint32_t)p->u.array.count)) { + ta_out_of_bound: + if (typed_array_is_oob(p)) + if (flags & JS_PROP_DEFINE_PROPERTY) + return JS_ThrowTypeErrorOrFalse(ctx, flags, "out-of-bound numeric index"); + return TRUE; // per spec: no OOB exception + } + p->u.array.u.double_ptr[idx] = d; + break; + default: + goto slow_path; + } + return TRUE; + } else { + JSAtom atom; + int ret; + slow_path: + atom = JS_ValueToAtom(ctx, prop); + JS_FreeValue(ctx, prop); + if (unlikely(atom == JS_ATOM_NULL)) { + JS_FreeValue(ctx, val); + return -1; + } + ret = JS_SetPropertyInternal(ctx, this_obj, atom, val, flags); + JS_FreeAtom(ctx, atom); + return ret; + } +} + +int JS_SetPropertyUint32(JSContext *ctx, JSValue this_obj, + uint32_t idx, JSValue val) +{ + return JS_SetPropertyValue(ctx, this_obj, js_uint32(idx), val, + JS_PROP_THROW); +} + +int JS_SetPropertyInt64(JSContext *ctx, JSValue this_obj, + int64_t idx, JSValue val) +{ + JSAtom prop; + int res; + + if ((uint64_t)idx <= INT32_MAX) { + /* fast path for fast arrays */ + return JS_SetPropertyValue(ctx, this_obj, js_int32(idx), val, + JS_PROP_THROW); + } + prop = JS_NewAtomInt64(ctx, idx); + if (prop == JS_ATOM_NULL) { + JS_FreeValue(ctx, val); + return -1; + } + res = JS_SetProperty(ctx, this_obj, prop, val); + JS_FreeAtom(ctx, prop); + return res; +} + +/* `prop` may be pure ASCII or UTF-8 encoded */ +int JS_SetPropertyStr(JSContext *ctx, JSValue this_obj, + const char *prop, JSValue val) +{ + JSAtom atom; + int ret; + atom = JS_NewAtom(ctx, prop); + ret = JS_SetPropertyInternal(ctx, this_obj, atom, val, JS_PROP_THROW); + JS_FreeAtom(ctx, atom); + return ret; +} + +/* compute the property flags. For each flag: (JS_PROP_HAS_x forces + it, otherwise def_flags is used) + Note: makes assumption about the bit pattern of the flags +*/ +static int get_prop_flags(int flags, int def_flags) +{ + int mask; + mask = (flags >> JS_PROP_HAS_SHIFT) & JS_PROP_C_W_E; + return (flags & mask) | (def_flags & ~mask); +} + +static int JS_CreateProperty(JSContext *ctx, JSObject *p, + JSAtom prop, JSValue val, + JSValue getter, JSValue setter, + int flags) +{ + JSProperty *pr; + int ret, prop_flags; + + /* add a new property or modify an existing exotic one */ + if (p->is_exotic) { + if (p->class_id == JS_CLASS_ARRAY) { + uint32_t idx, len; + + if (p->fast_array) { + if (__JS_AtomIsTaggedInt(prop)) { + idx = __JS_AtomToUInt32(prop); + if (idx == p->u.array.count) { + if (!p->extensible) + goto not_extensible; + if (flags & (JS_PROP_HAS_GET | JS_PROP_HAS_SET)) + goto convert_to_array; + prop_flags = get_prop_flags(flags, 0); + if (prop_flags != JS_PROP_C_W_E) + goto convert_to_array; + return add_fast_array_element(ctx, p, + js_dup(val), flags); + } else { + goto convert_to_array; + } + } else if (JS_AtomIsArrayIndex(ctx, &idx, prop)) { + /* convert the fast array to normal array */ + convert_to_array: + if (convert_fast_array_to_array(ctx, p)) + return -1; + goto generic_array; + } + } else if (JS_AtomIsArrayIndex(ctx, &idx, prop)) { + JSProperty *plen; + JSShapeProperty *pslen; + generic_array: + /* update the length field */ + plen = &p->prop[0]; + JS_ToUint32(ctx, &len, plen->u.value); + if ((idx + 1) > len) { + pslen = get_shape_prop(p->shape); + if (unlikely(!(pslen->flags & JS_PROP_WRITABLE))) + return JS_ThrowTypeErrorReadOnly(ctx, flags, JS_ATOM_length); + /* XXX: should update the length after defining + the property */ + len = idx + 1; + set_value(ctx, &plen->u.value, js_uint32(len)); + } + } + } else if (p->class_id >= JS_CLASS_UINT8C_ARRAY && + p->class_id <= JS_CLASS_FLOAT64_ARRAY) { + ret = JS_AtomIsNumericIndex(ctx, prop); + if (ret != 0) { + if (ret < 0) + return -1; + return JS_ThrowTypeErrorOrFalse(ctx, flags, "cannot create numeric index in typed array"); + } + } else if (!(flags & JS_PROP_NO_EXOTIC)) { + const JSClassExoticMethods *em = ctx->rt->class_array[p->class_id].exotic; + if (em) { + if (em->define_own_property) { + return em->define_own_property(ctx, JS_MKPTR(JS_TAG_OBJECT, p), + prop, val, getter, setter, flags); + } + ret = JS_IsExtensible(ctx, JS_MKPTR(JS_TAG_OBJECT, p)); + if (ret < 0) + return -1; + if (!ret) + goto not_extensible; + } + } + } + + if (!p->extensible) { + not_extensible: + return JS_ThrowTypeErrorOrFalse(ctx, flags, "object is not extensible"); + } + + if (flags & (JS_PROP_HAS_GET | JS_PROP_HAS_SET)) { + prop_flags = (flags & (JS_PROP_CONFIGURABLE | JS_PROP_ENUMERABLE)) | + JS_PROP_GETSET; + } else { + prop_flags = flags & JS_PROP_C_W_E; + } + pr = add_property(ctx, p, prop, prop_flags); + if (unlikely(!pr)) + return -1; + if (flags & (JS_PROP_HAS_GET | JS_PROP_HAS_SET)) { + pr->u.getset.getter = NULL; + if ((flags & JS_PROP_HAS_GET) && JS_IsFunction(ctx, getter)) { + pr->u.getset.getter = + JS_VALUE_GET_OBJ(js_dup(getter)); + } + pr->u.getset.setter = NULL; + if ((flags & JS_PROP_HAS_SET) && JS_IsFunction(ctx, setter)) { + pr->u.getset.setter = + JS_VALUE_GET_OBJ(js_dup(setter)); + } + } else { + if (flags & JS_PROP_HAS_VALUE) { + pr->u.value = js_dup(val); + } else { + pr->u.value = JS_UNDEFINED; + } + } + return TRUE; +} + +/* return FALSE if not OK */ +static BOOL check_define_prop_flags(int prop_flags, int flags) +{ + BOOL has_accessor, is_getset; + + if (!(prop_flags & JS_PROP_CONFIGURABLE)) { + if ((flags & (JS_PROP_HAS_CONFIGURABLE | JS_PROP_CONFIGURABLE)) == + (JS_PROP_HAS_CONFIGURABLE | JS_PROP_CONFIGURABLE)) { + return FALSE; + } + if ((flags & JS_PROP_HAS_ENUMERABLE) && + (flags & JS_PROP_ENUMERABLE) != (prop_flags & JS_PROP_ENUMERABLE)) + return FALSE; + } + if (flags & (JS_PROP_HAS_VALUE | JS_PROP_HAS_WRITABLE | + JS_PROP_HAS_GET | JS_PROP_HAS_SET)) { + if (!(prop_flags & JS_PROP_CONFIGURABLE)) { + has_accessor = ((flags & (JS_PROP_HAS_GET | JS_PROP_HAS_SET)) != 0); + is_getset = ((prop_flags & JS_PROP_TMASK) == JS_PROP_GETSET); + if (has_accessor != is_getset) + return FALSE; + if (!has_accessor && !is_getset && !(prop_flags & JS_PROP_WRITABLE)) { + /* not writable: cannot set the writable bit */ + if ((flags & (JS_PROP_HAS_WRITABLE | JS_PROP_WRITABLE)) == + (JS_PROP_HAS_WRITABLE | JS_PROP_WRITABLE)) + return FALSE; + } + } + } + return TRUE; +} + +/* ensure that the shape can be safely modified */ +static int js_shape_prepare_update(JSContext *ctx, JSObject *p, + JSShapeProperty **pprs) +{ + JSShape *sh; + uint32_t idx = 0; /* prevent warning */ + + sh = p->shape; + if (sh->is_hashed) { + if (sh->header.ref_count != 1) { + if (pprs) + idx = *pprs - get_shape_prop(sh); + /* clone the shape (the resulting one is no longer hashed) */ + sh = js_clone_shape(ctx, sh); + if (!sh) + return -1; + js_free_shape(ctx->rt, p->shape); + p->shape = sh; + if (pprs) + *pprs = get_shape_prop(sh) + idx; + } else { + js_shape_hash_unlink(ctx->rt, sh); + sh->is_hashed = FALSE; + } + } + return 0; +} + +static int js_update_property_flags(JSContext *ctx, JSObject *p, + JSShapeProperty **pprs, int flags) +{ + if (flags != (*pprs)->flags) { + if (js_shape_prepare_update(ctx, p, pprs)) + return -1; + (*pprs)->flags = flags; + } + return 0; +} + +/* allowed flags: + JS_PROP_CONFIGURABLE, JS_PROP_WRITABLE, JS_PROP_ENUMERABLE + JS_PROP_HAS_GET, JS_PROP_HAS_SET, JS_PROP_HAS_VALUE, + JS_PROP_HAS_CONFIGURABLE, JS_PROP_HAS_WRITABLE, JS_PROP_HAS_ENUMERABLE, + JS_PROP_THROW, JS_PROP_NO_EXOTIC. + If JS_PROP_THROW is set, return an exception instead of FALSE. + if JS_PROP_NO_EXOTIC is set, do not call the exotic + define_own_property callback. + return -1 (exception), FALSE or TRUE. +*/ +int JS_DefineProperty(JSContext *ctx, JSValue this_obj, + JSAtom prop, JSValue val, + JSValue getter, JSValue setter, int flags) +{ + JSObject *p; + JSShapeProperty *prs; + JSProperty *pr; + int mask, res; + + if (JS_VALUE_GET_TAG(this_obj) != JS_TAG_OBJECT) { + JS_ThrowTypeErrorNotAnObject(ctx); + return -1; + } + p = JS_VALUE_GET_OBJ(this_obj); + + redo_prop_update: + prs = find_own_property(&pr, p, prop); + if (prs) { + /* the range of the Array length property is always tested before */ + if ((prs->flags & JS_PROP_LENGTH) && (flags & JS_PROP_HAS_VALUE)) { + uint32_t array_length; + if (JS_ToArrayLengthFree(ctx, &array_length, + js_dup(val), FALSE)) { + return -1; + } + /* this code relies on the fact that Uint32 are never allocated */ + val = js_uint32(array_length); + /* prs may have been modified */ + prs = find_own_property(&pr, p, prop); + assert(prs != NULL); + } + /* property already exists */ + if (!check_define_prop_flags(prs->flags, flags)) { + not_configurable: + return JS_ThrowTypeErrorOrFalse(ctx, flags, "property is not configurable"); + } + + if ((prs->flags & JS_PROP_TMASK) == JS_PROP_AUTOINIT) { + /* Instantiate property and retry */ + if (JS_AutoInitProperty(ctx, p, prop, pr, prs)) + return -1; + goto redo_prop_update; + } + + if (flags & (JS_PROP_HAS_VALUE | JS_PROP_HAS_WRITABLE | + JS_PROP_HAS_GET | JS_PROP_HAS_SET)) { + if (flags & (JS_PROP_HAS_GET | JS_PROP_HAS_SET)) { + JSObject *new_getter, *new_setter; + + if (JS_IsFunction(ctx, getter)) { + new_getter = JS_VALUE_GET_OBJ(getter); + } else { + new_getter = NULL; + } + if (JS_IsFunction(ctx, setter)) { + new_setter = JS_VALUE_GET_OBJ(setter); + } else { + new_setter = NULL; + } + + if ((prs->flags & JS_PROP_TMASK) != JS_PROP_GETSET) { + if (js_shape_prepare_update(ctx, p, &prs)) + return -1; + /* convert to getset */ + if ((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF) { + free_var_ref(ctx->rt, pr->u.var_ref); + } else { + JS_FreeValue(ctx, pr->u.value); + } + prs->flags = (prs->flags & + (JS_PROP_CONFIGURABLE | JS_PROP_ENUMERABLE)) | + JS_PROP_GETSET; + pr->u.getset.getter = NULL; + pr->u.getset.setter = NULL; + } else { + if (!(prs->flags & JS_PROP_CONFIGURABLE)) { + if ((flags & JS_PROP_HAS_GET) && + new_getter != pr->u.getset.getter) { + goto not_configurable; + } + if ((flags & JS_PROP_HAS_SET) && + new_setter != pr->u.getset.setter) { + goto not_configurable; + } + } + } + if (flags & JS_PROP_HAS_GET) { + if (pr->u.getset.getter) + JS_FreeValue(ctx, JS_MKPTR(JS_TAG_OBJECT, pr->u.getset.getter)); + if (new_getter) + js_dup(getter); + pr->u.getset.getter = new_getter; + } + if (flags & JS_PROP_HAS_SET) { + if (pr->u.getset.setter) + JS_FreeValue(ctx, JS_MKPTR(JS_TAG_OBJECT, pr->u.getset.setter)); + if (new_setter) + js_dup(setter); + pr->u.getset.setter = new_setter; + } + } else { + if ((prs->flags & JS_PROP_TMASK) == JS_PROP_GETSET) { + /* convert to data descriptor */ + if (js_shape_prepare_update(ctx, p, &prs)) + return -1; + if (pr->u.getset.getter) + JS_FreeValue(ctx, JS_MKPTR(JS_TAG_OBJECT, pr->u.getset.getter)); + if (pr->u.getset.setter) + JS_FreeValue(ctx, JS_MKPTR(JS_TAG_OBJECT, pr->u.getset.setter)); + prs->flags &= ~(JS_PROP_TMASK | JS_PROP_WRITABLE); + pr->u.value = JS_UNDEFINED; + } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF) { + /* Note: JS_PROP_VARREF is always writable */ + } else { + if ((prs->flags & (JS_PROP_CONFIGURABLE | JS_PROP_WRITABLE)) == 0 && + (flags & JS_PROP_HAS_VALUE)) { + if (!js_same_value(ctx, val, pr->u.value)) { + goto not_configurable; + } else { + return TRUE; + } + } + } + if ((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF) { + if (flags & JS_PROP_HAS_VALUE) { + if (p->class_id == JS_CLASS_MODULE_NS) { + /* JS_PROP_WRITABLE is always true for variable + references, but they are write protected in module name + spaces. */ + if (!js_same_value(ctx, val, *pr->u.var_ref->pvalue)) + goto not_configurable; + } + /* update the reference */ + set_value(ctx, pr->u.var_ref->pvalue, + js_dup(val)); + } + /* if writable is set to false, no longer a + reference (for mapped arguments) */ + if ((flags & (JS_PROP_HAS_WRITABLE | JS_PROP_WRITABLE)) == JS_PROP_HAS_WRITABLE) { + JSValue val1; + if (js_shape_prepare_update(ctx, p, &prs)) + return -1; + val1 = js_dup(*pr->u.var_ref->pvalue); + free_var_ref(ctx->rt, pr->u.var_ref); + pr->u.value = val1; + prs->flags &= ~(JS_PROP_TMASK | JS_PROP_WRITABLE); + } + } else if (prs->flags & JS_PROP_LENGTH) { + if (flags & JS_PROP_HAS_VALUE) { + /* Note: no JS code is executable because + 'val' is guaranted to be a Uint32 */ + res = set_array_length(ctx, p, js_dup(val), + flags); + } else { + res = TRUE; + } + /* still need to reset the writable flag if + needed. The JS_PROP_LENGTH is kept because the + Uint32 test is still done if the length + property is read-only. */ + if ((flags & (JS_PROP_HAS_WRITABLE | JS_PROP_WRITABLE)) == + JS_PROP_HAS_WRITABLE) { + prs = get_shape_prop(p->shape); + if (js_update_property_flags(ctx, p, &prs, + prs->flags & ~JS_PROP_WRITABLE)) + return -1; + } + return res; + } else { + if (flags & JS_PROP_HAS_VALUE) { + JS_FreeValue(ctx, pr->u.value); + pr->u.value = js_dup(val); + } + if (flags & JS_PROP_HAS_WRITABLE) { + if (js_update_property_flags(ctx, p, &prs, + (prs->flags & ~JS_PROP_WRITABLE) | + (flags & JS_PROP_WRITABLE))) + return -1; + } + } + } + } + mask = 0; + if (flags & JS_PROP_HAS_CONFIGURABLE) + mask |= JS_PROP_CONFIGURABLE; + if (flags & JS_PROP_HAS_ENUMERABLE) + mask |= JS_PROP_ENUMERABLE; + if (js_update_property_flags(ctx, p, &prs, + (prs->flags & ~mask) | (flags & mask))) + return -1; + return TRUE; + } + + /* handle modification of fast array elements */ + if (p->fast_array) { + uint32_t idx; + uint32_t prop_flags; + if (p->class_id == JS_CLASS_ARRAY) { + if (__JS_AtomIsTaggedInt(prop)) { + idx = __JS_AtomToUInt32(prop); + if (idx < p->u.array.count) { + prop_flags = get_prop_flags(flags, JS_PROP_C_W_E); + if (prop_flags != JS_PROP_C_W_E) + goto convert_to_slow_array; + if (flags & (JS_PROP_HAS_GET | JS_PROP_HAS_SET)) { + convert_to_slow_array: + if (convert_fast_array_to_array(ctx, p)) + return -1; + else + goto redo_prop_update; + } + if (flags & JS_PROP_HAS_VALUE) { + set_value(ctx, &p->u.array.u.values[idx], js_dup(val)); + } + return TRUE; + } + } + } else if (p->class_id >= JS_CLASS_UINT8C_ARRAY && + p->class_id <= JS_CLASS_FLOAT64_ARRAY) { + JSValue num; + int ret; + + if (!__JS_AtomIsTaggedInt(prop)) { + /* slow path with to handle all numeric indexes */ + num = JS_AtomIsNumericIndex1(ctx, prop); + if (JS_IsUndefined(num)) + goto typed_array_done; + if (JS_IsException(num)) + return -1; + ret = JS_NumberIsInteger(ctx, num); + if (ret < 0) { + JS_FreeValue(ctx, num); + return -1; + } + if (!ret) { + JS_FreeValue(ctx, num); + return JS_ThrowTypeErrorOrFalse(ctx, flags, "non integer index in typed array"); + } + ret = JS_NumberIsNegativeOrMinusZero(ctx, num); + JS_FreeValue(ctx, num); + if (ret) { + return JS_ThrowTypeErrorOrFalse(ctx, flags, "negative index in typed array"); + } + if (!__JS_AtomIsTaggedInt(prop)) + goto typed_array_oob; + } + idx = __JS_AtomToUInt32(prop); + /* if the typed array is detached, p->u.array.count = 0 */ + if (idx >= typed_array_get_length(ctx, p)) { + typed_array_oob: + return JS_ThrowTypeErrorOrFalse(ctx, flags, "out-of-bound index in typed array"); + } + prop_flags = get_prop_flags(flags, JS_PROP_ENUMERABLE | JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE); + if (flags & (JS_PROP_HAS_GET | JS_PROP_HAS_SET) || + prop_flags != (JS_PROP_ENUMERABLE | JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE)) { + return JS_ThrowTypeErrorOrFalse(ctx, flags, "invalid descriptor flags"); + } + if (flags & JS_PROP_HAS_VALUE) { + return JS_SetPropertyValue(ctx, this_obj, js_int32(idx), js_dup(val), flags); + } + return TRUE; + typed_array_done: ; + } + } + + return JS_CreateProperty(ctx, p, prop, val, getter, setter, flags); +} + +static int JS_DefineAutoInitProperty(JSContext *ctx, JSValue this_obj, + JSAtom prop, JSAutoInitIDEnum id, + void *opaque, int flags) +{ + JSObject *p; + JSProperty *pr; + + if (JS_VALUE_GET_TAG(this_obj) != JS_TAG_OBJECT) + return FALSE; + + p = JS_VALUE_GET_OBJ(this_obj); + + if (find_own_property(&pr, p, prop)) { + /* property already exists */ + abort(); + return FALSE; + } + + /* Specialized CreateProperty */ + pr = add_property(ctx, p, prop, (flags & JS_PROP_C_W_E) | JS_PROP_AUTOINIT); + if (unlikely(!pr)) + return -1; + pr->u.init.realm_and_id = (uintptr_t)JS_DupContext(ctx); + assert((pr->u.init.realm_and_id & 3) == 0); + assert(id <= 3); + pr->u.init.realm_and_id |= id; + pr->u.init.opaque = opaque; + return TRUE; +} + +/* shortcut to add or redefine a new property value */ +int JS_DefinePropertyValue(JSContext *ctx, JSValue this_obj, + JSAtom prop, JSValue val, int flags) +{ + int ret; + ret = JS_DefineProperty(ctx, this_obj, prop, val, JS_UNDEFINED, JS_UNDEFINED, + flags | JS_PROP_HAS_VALUE | JS_PROP_HAS_CONFIGURABLE | JS_PROP_HAS_WRITABLE | JS_PROP_HAS_ENUMERABLE); + JS_FreeValue(ctx, val); + return ret; +} + +int JS_DefinePropertyValueValue(JSContext *ctx, JSValue this_obj, + JSValue prop, JSValue val, int flags) +{ + JSAtom atom; + int ret; + atom = JS_ValueToAtom(ctx, prop); + JS_FreeValue(ctx, prop); + if (unlikely(atom == JS_ATOM_NULL)) { + JS_FreeValue(ctx, val); + return -1; + } + ret = JS_DefinePropertyValue(ctx, this_obj, atom, val, flags); + JS_FreeAtom(ctx, atom); + return ret; +} + +int JS_DefinePropertyValueUint32(JSContext *ctx, JSValue this_obj, + uint32_t idx, JSValue val, int flags) +{ + return JS_DefinePropertyValueValue(ctx, this_obj, js_uint32(idx), + val, flags); +} + +int JS_DefinePropertyValueInt64(JSContext *ctx, JSValue this_obj, + int64_t idx, JSValue val, int flags) +{ + return JS_DefinePropertyValueValue(ctx, this_obj, js_int64(idx), + val, flags); +} + +/* `prop` may be pure ASCII or UTF-8 encoded */ +int JS_DefinePropertyValueStr(JSContext *ctx, JSValue this_obj, + const char *prop, JSValue val, int flags) +{ + JSAtom atom; + int ret; + atom = JS_NewAtom(ctx, prop); + ret = JS_DefinePropertyValue(ctx, this_obj, atom, val, flags); + JS_FreeAtom(ctx, atom); + return ret; +} + +/* shortcut to add getter & setter */ +int JS_DefinePropertyGetSet(JSContext *ctx, JSValue this_obj, + JSAtom prop, JSValue getter, JSValue setter, + int flags) +{ + int ret; + ret = JS_DefineProperty(ctx, this_obj, prop, JS_UNDEFINED, getter, setter, + flags | JS_PROP_HAS_GET | JS_PROP_HAS_SET | + JS_PROP_HAS_CONFIGURABLE | JS_PROP_HAS_ENUMERABLE); + JS_FreeValue(ctx, getter); + JS_FreeValue(ctx, setter); + return ret; +} + +static int JS_CreateDataPropertyUint32(JSContext *ctx, JSValue this_obj, + int64_t idx, JSValue val, int flags) +{ + return JS_DefinePropertyValueValue(ctx, this_obj, js_int64(idx), + val, flags | JS_PROP_CONFIGURABLE | + JS_PROP_ENUMERABLE | JS_PROP_WRITABLE); +} + + +/* return TRUE if 'obj' has a non empty 'name' string */ +static BOOL js_object_has_name(JSContext *ctx, JSValue obj) +{ + JSProperty *pr; + JSShapeProperty *prs; + JSValue val; + JSString *p; + + prs = find_own_property(&pr, JS_VALUE_GET_OBJ(obj), JS_ATOM_name); + if (!prs) + return FALSE; + if ((prs->flags & JS_PROP_TMASK) != JS_PROP_NORMAL) + return TRUE; + val = pr->u.value; + if (JS_VALUE_GET_TAG(val) != JS_TAG_STRING) + return TRUE; + p = JS_VALUE_GET_STRING(val); + return (p->len != 0); +} + +static int JS_DefineObjectName(JSContext *ctx, JSValue obj, + JSAtom name, int flags) +{ + if (name != JS_ATOM_NULL + && JS_IsObject(obj) + && !js_object_has_name(ctx, obj) + && JS_DefinePropertyValue(ctx, obj, JS_ATOM_name, JS_AtomToString(ctx, name), flags) < 0) { + return -1; + } + return 0; +} + +static int JS_DefineObjectNameComputed(JSContext *ctx, JSValue obj, + JSValue str, int flags) +{ + if (JS_IsObject(obj) && + !js_object_has_name(ctx, obj)) { + JSAtom prop; + JSValue name_str; + prop = JS_ValueToAtom(ctx, str); + if (prop == JS_ATOM_NULL) + return -1; + name_str = js_get_function_name(ctx, prop); + JS_FreeAtom(ctx, prop); + if (JS_IsException(name_str)) + return -1; + if (JS_DefinePropertyValue(ctx, obj, JS_ATOM_name, name_str, flags) < 0) + return -1; + } + return 0; +} + +#define DEFINE_GLOBAL_LEX_VAR (1 << 7) +#define DEFINE_GLOBAL_FUNC_VAR (1 << 6) + +static JSValue JS_ThrowSyntaxErrorVarRedeclaration(JSContext *ctx, JSAtom prop) +{ + return JS_ThrowSyntaxErrorAtom(ctx, "redeclaration of '%s'", prop); +} + +/* flags is 0, DEFINE_GLOBAL_LEX_VAR or DEFINE_GLOBAL_FUNC_VAR */ +/* XXX: could support exotic global object. */ +static int JS_CheckDefineGlobalVar(JSContext *ctx, JSAtom prop, int flags) +{ + JSObject *p; + JSShapeProperty *prs; + + p = JS_VALUE_GET_OBJ(ctx->global_obj); + prs = find_own_property1(p, prop); + /* XXX: should handle JS_PROP_AUTOINIT */ + if (flags & DEFINE_GLOBAL_LEX_VAR) { + if (prs && !(prs->flags & JS_PROP_CONFIGURABLE)) + goto fail_redeclaration; + } else { + if (!prs && !p->extensible) + goto define_error; + if (flags & DEFINE_GLOBAL_FUNC_VAR) { + if (prs) { + if (!(prs->flags & JS_PROP_CONFIGURABLE) && + ((prs->flags & JS_PROP_TMASK) == JS_PROP_GETSET || + ((prs->flags & (JS_PROP_WRITABLE | JS_PROP_ENUMERABLE)) != + (JS_PROP_WRITABLE | JS_PROP_ENUMERABLE)))) { + define_error: + JS_ThrowTypeErrorAtom(ctx, "cannot define variable '%s'", + prop); + return -1; + } + } + } + } + /* check if there already is a lexical declaration */ + p = JS_VALUE_GET_OBJ(ctx->global_var_obj); + prs = find_own_property1(p, prop); + if (prs) { + fail_redeclaration: + JS_ThrowSyntaxErrorVarRedeclaration(ctx, prop); + return -1; + } + return 0; +} + +/* def_flags is (0, DEFINE_GLOBAL_LEX_VAR) | + JS_PROP_CONFIGURABLE | JS_PROP_WRITABLE */ +/* XXX: could support exotic global object. */ +static int JS_DefineGlobalVar(JSContext *ctx, JSAtom prop, int def_flags) +{ + JSObject *p; + JSShapeProperty *prs; + JSProperty *pr; + JSValue val; + int flags; + + if (def_flags & DEFINE_GLOBAL_LEX_VAR) { + p = JS_VALUE_GET_OBJ(ctx->global_var_obj); + flags = JS_PROP_ENUMERABLE | (def_flags & JS_PROP_WRITABLE) | + JS_PROP_CONFIGURABLE; + val = JS_UNINITIALIZED; + } else { + p = JS_VALUE_GET_OBJ(ctx->global_obj); + flags = JS_PROP_ENUMERABLE | JS_PROP_WRITABLE | + (def_flags & JS_PROP_CONFIGURABLE); + val = JS_UNDEFINED; + } + prs = find_own_property1(p, prop); + if (prs) + return 0; + if (!p->extensible) + return 0; + pr = add_property(ctx, p, prop, flags); + if (unlikely(!pr)) + return -1; + pr->u.value = val; + return 0; +} + +/* 'def_flags' is 0 or JS_PROP_CONFIGURABLE. */ +/* XXX: could support exotic global object. */ +static int JS_DefineGlobalFunction(JSContext *ctx, JSAtom prop, + JSValue func, int def_flags) +{ + + JSObject *p; + JSShapeProperty *prs; + int flags; + + p = JS_VALUE_GET_OBJ(ctx->global_obj); + prs = find_own_property1(p, prop); + flags = JS_PROP_HAS_VALUE | JS_PROP_THROW; + if (!prs || (prs->flags & JS_PROP_CONFIGURABLE)) { + flags |= JS_PROP_ENUMERABLE | JS_PROP_WRITABLE | def_flags | + JS_PROP_HAS_CONFIGURABLE | JS_PROP_HAS_WRITABLE | JS_PROP_HAS_ENUMERABLE; + } + if (JS_DefineProperty(ctx, ctx->global_obj, prop, func, + JS_UNDEFINED, JS_UNDEFINED, flags) < 0) + return -1; + return 0; +} + +static JSValue JS_GetGlobalVar(JSContext *ctx, JSAtom prop, + BOOL throw_ref_error) +{ + JSObject *p; + JSShapeProperty *prs; + JSProperty *pr; + + /* no exotic behavior is possible in global_var_obj */ + p = JS_VALUE_GET_OBJ(ctx->global_var_obj); + prs = find_own_property(&pr, p, prop); + if (prs) { + /* XXX: should handle JS_PROP_TMASK properties */ + if (unlikely(JS_IsUninitialized(pr->u.value))) + return JS_ThrowReferenceErrorUninitialized(ctx, prs->atom); + return js_dup(pr->u.value); + } + return JS_GetPropertyInternal(ctx, ctx->global_obj, prop, + ctx->global_obj, throw_ref_error); +} + +/* construct a reference to a global variable */ +static int JS_GetGlobalVarRef(JSContext *ctx, JSAtom prop, JSValue *sp) +{ + JSObject *p; + JSShapeProperty *prs; + JSProperty *pr; + + /* no exotic behavior is possible in global_var_obj */ + p = JS_VALUE_GET_OBJ(ctx->global_var_obj); + prs = find_own_property(&pr, p, prop); + if (prs) { + /* XXX: should handle JS_PROP_AUTOINIT properties? */ + /* XXX: conformance: do these tests in + OP_put_var_ref/OP_get_var_ref ? */ + if (unlikely(JS_IsUninitialized(pr->u.value))) { + JS_ThrowReferenceErrorUninitialized(ctx, prs->atom); + return -1; + } + if (unlikely(!(prs->flags & JS_PROP_WRITABLE))) { + return JS_ThrowTypeErrorReadOnly(ctx, JS_PROP_THROW, prop); + } + sp[0] = js_dup(ctx->global_var_obj); + } else { + int ret; + ret = JS_HasProperty(ctx, ctx->global_obj, prop); + if (ret < 0) + return -1; + if (ret) { + sp[0] = js_dup(ctx->global_obj); + } else { + sp[0] = JS_UNDEFINED; + } + } + sp[1] = JS_AtomToValue(ctx, prop); + return 0; +} + +/* use for strict variable access: test if the variable exists */ +static int JS_CheckGlobalVar(JSContext *ctx, JSAtom prop) +{ + JSObject *p; + JSShapeProperty *prs; + int ret; + + /* no exotic behavior is possible in global_var_obj */ + p = JS_VALUE_GET_OBJ(ctx->global_var_obj); + prs = find_own_property1(p, prop); + if (prs) { + ret = TRUE; + } else { + ret = JS_HasProperty(ctx, ctx->global_obj, prop); + if (ret < 0) + return -1; + } + return ret; +} + +/* flag = 0: normal variable write + flag = 1: initialize lexical variable + flag = 2: normal variable write, strict check was done before +*/ +static int JS_SetGlobalVar(JSContext *ctx, JSAtom prop, JSValue val, + int flag) +{ + JSObject *p; + JSShapeProperty *prs; + JSProperty *pr; + int flags; + + /* no exotic behavior is possible in global_var_obj */ + p = JS_VALUE_GET_OBJ(ctx->global_var_obj); + prs = find_own_property(&pr, p, prop); + if (prs) { + /* XXX: should handle JS_PROP_AUTOINIT properties? */ + if (flag != 1) { + if (unlikely(JS_IsUninitialized(pr->u.value))) { + JS_FreeValue(ctx, val); + JS_ThrowReferenceErrorUninitialized(ctx, prs->atom); + return -1; + } + if (unlikely(!(prs->flags & JS_PROP_WRITABLE))) { + JS_FreeValue(ctx, val); + return JS_ThrowTypeErrorReadOnly(ctx, JS_PROP_THROW, prop); + } + } + set_value(ctx, &pr->u.value, val); + return 0; + } + flags = JS_PROP_THROW_STRICT; + if (is_strict_mode(ctx)) + flags |= JS_PROP_NO_ADD; + return JS_SetPropertyInternal(ctx, ctx->global_obj, prop, val, flags); +} + +/* return -1, FALSE or TRUE. return FALSE if not configurable or + invalid object. return -1 in case of exception. + flags can be 0, JS_PROP_THROW or JS_PROP_THROW_STRICT */ +int JS_DeleteProperty(JSContext *ctx, JSValue obj, JSAtom prop, int flags) +{ + JSValue obj1; + JSObject *p; + int res; + + obj1 = JS_ToObject(ctx, obj); + if (JS_IsException(obj1)) + return -1; + p = JS_VALUE_GET_OBJ(obj1); + res = delete_property(ctx, p, prop); + JS_FreeValue(ctx, obj1); + if (res != FALSE) + return res; + if ((flags & JS_PROP_THROW) || + ((flags & JS_PROP_THROW_STRICT) && is_strict_mode(ctx))) { + JS_ThrowTypeError(ctx, "could not delete property"); + return -1; + } + return FALSE; +} + +int JS_DeletePropertyInt64(JSContext *ctx, JSValue obj, int64_t idx, int flags) +{ + JSAtom prop; + int res; + + if ((uint64_t)idx <= JS_ATOM_MAX_INT) { + /* fast path for fast arrays */ + return JS_DeleteProperty(ctx, obj, __JS_AtomFromUInt32(idx), flags); + } + prop = JS_NewAtomInt64(ctx, idx); + if (prop == JS_ATOM_NULL) + return -1; + res = JS_DeleteProperty(ctx, obj, prop, flags); + JS_FreeAtom(ctx, prop); + return res; +} + +BOOL JS_IsFunction(JSContext *ctx, JSValue val) +{ + JSObject *p; + if (JS_VALUE_GET_TAG(val) != JS_TAG_OBJECT) + return FALSE; + p = JS_VALUE_GET_OBJ(val); + switch(p->class_id) { + case JS_CLASS_BYTECODE_FUNCTION: + return TRUE; + case JS_CLASS_PROXY: + return p->u.proxy_data->is_func; + default: + return (ctx->rt->class_array[p->class_id].call != NULL); + } +} + +BOOL JS_IsCFunction(JSContext *ctx, JSValue val, JSCFunction *func, int magic) +{ + JSObject *p; + if (JS_VALUE_GET_TAG(val) != JS_TAG_OBJECT) + return FALSE; + p = JS_VALUE_GET_OBJ(val); + if (p->class_id == JS_CLASS_C_FUNCTION) + return (p->u.cfunc.c_function.generic == func && p->u.cfunc.magic == magic); + else + return FALSE; +} + +BOOL JS_IsConstructor(JSContext *ctx, JSValue val) +{ + JSObject *p; + if (JS_VALUE_GET_TAG(val) != JS_TAG_OBJECT) + return FALSE; + p = JS_VALUE_GET_OBJ(val); + return p->is_constructor; +} + +BOOL JS_SetConstructorBit(JSContext *ctx, JSValue func_obj, BOOL val) +{ + JSObject *p; + if (JS_VALUE_GET_TAG(func_obj) != JS_TAG_OBJECT) + return FALSE; + p = JS_VALUE_GET_OBJ(func_obj); + p->is_constructor = val; + return TRUE; +} + +BOOL JS_IsError(JSContext *ctx, JSValue val) +{ + JSObject *p; + if (JS_VALUE_GET_TAG(val) != JS_TAG_OBJECT) + return FALSE; + p = JS_VALUE_GET_OBJ(val); + return (p->class_id == JS_CLASS_ERROR); +} + +/* used to avoid catching interrupt exceptions */ +BOOL JS_IsUncatchableError(JSContext *ctx, JSValue val) +{ + JSObject *p; + if (JS_VALUE_GET_TAG(val) != JS_TAG_OBJECT) + return FALSE; + p = JS_VALUE_GET_OBJ(val); + return p->class_id == JS_CLASS_ERROR && p->is_uncatchable_error; +} + +void JS_SetUncatchableError(JSContext *ctx, JSValue val, BOOL flag) +{ + JSObject *p; + if (JS_VALUE_GET_TAG(val) != JS_TAG_OBJECT) + return; + p = JS_VALUE_GET_OBJ(val); + if (p->class_id == JS_CLASS_ERROR) + p->is_uncatchable_error = flag; +} + +void JS_ResetUncatchableError(JSContext *ctx) +{ + JS_SetUncatchableError(ctx, ctx->rt->current_exception, FALSE); +} + +JS_BOOL JS_SetOpaque(JSValue obj, void *opaque) +{ + JSObject *p; + if (JS_VALUE_GET_TAG(obj) == JS_TAG_OBJECT) { + p = JS_VALUE_GET_OBJ(obj); + // User code can't set the opaque of internal objects. + if (p->class_id >= JS_CLASS_INIT_COUNT) { + p->u.opaque = opaque; + return 0; + } + } + + return 1; +} + +/* |obj| must be a JSObject of an internal class. */ +static void JS_SetOpaqueInternal(JSValue obj, void *opaque) +{ + JSObject *p; + assert(JS_VALUE_GET_TAG(obj) == JS_TAG_OBJECT); + p = JS_VALUE_GET_OBJ(obj); + assert(p->class_id < JS_CLASS_INIT_COUNT); + p->u.opaque = opaque; +} + +/* return NULL if not an object of class class_id */ +void *JS_GetOpaque(JSValue obj, JSClassID class_id) +{ + JSObject *p; + if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT) + return NULL; + p = JS_VALUE_GET_OBJ(obj); + if (p->class_id != class_id) + return NULL; + return p->u.opaque; +} + +void *JS_GetOpaque2(JSContext *ctx, JSValue obj, JSClassID class_id) +{ + void *p = JS_GetOpaque(obj, class_id); + if (unlikely(!p)) { + JS_ThrowTypeErrorInvalidClass(ctx, class_id); + } + return p; +} + +void *JS_GetAnyOpaque(JSValue obj, JSClassID *class_id) +{ + JSObject *p; + if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT) { + *class_id = 0; + return NULL; + } + p = JS_VALUE_GET_OBJ(obj); + *class_id = p->class_id; + return p->u.opaque; +} + +static JSValue JS_ToPrimitiveFree(JSContext *ctx, JSValue val, int hint) +{ + int i; + BOOL force_ordinary; + + JSAtom method_name; + JSValue method, ret; + if (JS_VALUE_GET_TAG(val) != JS_TAG_OBJECT) + return val; + force_ordinary = hint & HINT_FORCE_ORDINARY; + hint &= ~HINT_FORCE_ORDINARY; + if (!force_ordinary) { + method = JS_GetProperty(ctx, val, JS_ATOM_Symbol_toPrimitive); + if (JS_IsException(method)) + goto exception; + /* ECMA says *If exoticToPrim is not undefined* but tests in + test262 use null as a non callable converter */ + if (!JS_IsUndefined(method) && !JS_IsNull(method)) { + JSAtom atom; + JSValue arg; + switch(hint) { + case HINT_STRING: + atom = JS_ATOM_string; + break; + case HINT_NUMBER: + atom = JS_ATOM_number; + break; + default: + case HINT_NONE: + atom = JS_ATOM_default; + break; + } + arg = JS_AtomToString(ctx, atom); + ret = JS_CallFree(ctx, method, val, 1, &arg); + JS_FreeValue(ctx, arg); + if (JS_IsException(ret)) + goto exception; + JS_FreeValue(ctx, val); + if (JS_VALUE_GET_TAG(ret) != JS_TAG_OBJECT) + return ret; + JS_FreeValue(ctx, ret); + return JS_ThrowTypeError(ctx, "toPrimitive"); + } + } + if (hint != HINT_STRING) + hint = HINT_NUMBER; + for(i = 0; i < 2; i++) { + if ((i ^ hint) == 0) { + method_name = JS_ATOM_toString; + } else { + method_name = JS_ATOM_valueOf; + } + method = JS_GetProperty(ctx, val, method_name); + if (JS_IsException(method)) + goto exception; + if (JS_IsFunction(ctx, method)) { + ret = JS_CallFree(ctx, method, val, 0, NULL); + if (JS_IsException(ret)) + goto exception; + if (JS_VALUE_GET_TAG(ret) != JS_TAG_OBJECT) { + JS_FreeValue(ctx, val); + return ret; + } + JS_FreeValue(ctx, ret); + } else { + JS_FreeValue(ctx, method); + } + } + JS_ThrowTypeError(ctx, "toPrimitive"); +exception: + JS_FreeValue(ctx, val); + return JS_EXCEPTION; +} + +static JSValue JS_ToPrimitive(JSContext *ctx, JSValue val, int hint) +{ + return JS_ToPrimitiveFree(ctx, js_dup(val), hint); +} + +void JS_SetIsHTMLDDA(JSContext *ctx, JSValue obj) +{ + JSObject *p; + if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT) + return; + p = JS_VALUE_GET_OBJ(obj); + p->is_HTMLDDA = TRUE; +} + +static inline BOOL JS_IsHTMLDDA(JSContext *ctx, JSValue obj) +{ + JSObject *p; + if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT) + return FALSE; + p = JS_VALUE_GET_OBJ(obj); + return p->is_HTMLDDA; +} + +static int JS_ToBoolFree(JSContext *ctx, JSValue val) +{ + uint32_t tag = JS_VALUE_GET_TAG(val); + switch(tag) { + case JS_TAG_INT: + return JS_VALUE_GET_INT(val) != 0; + case JS_TAG_BOOL: + case JS_TAG_NULL: + case JS_TAG_UNDEFINED: + return JS_VALUE_GET_INT(val); + case JS_TAG_EXCEPTION: + return -1; + case JS_TAG_STRING: + { + BOOL ret = JS_VALUE_GET_STRING(val)->len != 0; + JS_FreeValue(ctx, val); + return ret; + } + case JS_TAG_BIG_INT: + { + JSBigInt *p = JS_VALUE_GET_PTR(val); + BOOL ret; + ret = p->num.expn != BF_EXP_ZERO && p->num.expn != BF_EXP_NAN; + JS_FreeValue(ctx, val); + return ret; + } + case JS_TAG_OBJECT: + { + JSObject *p = JS_VALUE_GET_OBJ(val); + BOOL ret; + ret = !p->is_HTMLDDA; + JS_FreeValue(ctx, val); + return ret; + } + break; + default: + if (JS_TAG_IS_FLOAT64(tag)) { + double d = JS_VALUE_GET_FLOAT64(val); + return !isnan(d) && d != 0; + } else { + JS_FreeValue(ctx, val); + return TRUE; + } + } +} + +int JS_ToBool(JSContext *ctx, JSValue val) +{ + return JS_ToBoolFree(ctx, js_dup(val)); +} + +/* pc points to pure ASCII or UTF-8, null terminated contents */ +static int skip_spaces(const char *pc) +{ + const uint8_t *p, *p_next, *p_start; + uint32_t c; + + p = p_start = (const uint8_t *)pc; + for (;;) { + c = *p++; + if (c < 0x80) { + if (!((c >= 0x09 && c <= 0x0d) || (c == 0x20))) + break; + } else { + c = utf8_decode(p - 1, &p_next); + /* no need to test for invalid UTF-8, 0xFFFD is not a space */ + if (!lre_is_space(c)) + break; + p = p_next; + } + } + return p - 1 - p_start; +} + +static inline int to_digit(int c) +{ + if (c >= '0' && c <= '9') + return c - '0'; + else if (c >= 'A' && c <= 'Z') + return c - 'A' + 10; + else if (c >= 'a' && c <= 'z') + return c - 'a' + 10; + else + return 36; +} + +/* XXX: remove */ +static double js_strtod(const char *str, int radix, BOOL is_float) +{ + double d; + int c; + + if (!is_float || radix != 10) { + const char *p = str; + uint64_t n_max, n; + int int_exp, is_neg; + + is_neg = 0; + if (*p == '-') { + is_neg = 1; + p++; + } + + /* skip leading zeros */ + while (*p == '0') + p++; + n = 0; + if (radix == 10) + n_max = ((uint64_t)-1 - 9) / 10; /* most common case */ + else + n_max = ((uint64_t)-1 - (radix - 1)) / radix; + /* XXX: could be more precise */ + int_exp = 0; + while ((c = to_digit(*p)) < radix) { + if (n <= n_max) { + n = n * radix + c; + } else { + if (radix == 10) + goto strtod_case; + int_exp++; + } + p++; + } + d = n; + if (int_exp != 0) { + d *= pow(radix, int_exp); + } + if (is_neg) + d = -d; + } else { + strtod_case: + d = strtod(str, NULL); + } + return d; +} + +static JSValue js_string_to_bigint(JSContext *ctx, const char *buf, int radix) +{ + bf_t *a; + int ret; + JSValue val; + val = JS_NewBigInt(ctx); + if (JS_IsException(val)) + return val; + a = JS_GetBigInt(val); + ret = bf_atof(a, buf, NULL, radix, BF_PREC_INF, BF_RNDZ); + if (ret & BF_ST_MEM_ERROR) { + JS_FreeValue(ctx, val); + return JS_ThrowOutOfMemory(ctx); + } + return JS_CompactBigInt1(ctx, val); +} + +/* `js_atof(ctx, p, len, pp, radix, flags)` + Convert the string pointed to by `p` to a number value. + Return an exception in case of memory error. + Return `JS_NAN` if invalid syntax. + - `p` points to a null terminated UTF-8 encoded char array, + - `len` the length of the array, + - `pp` if not null receives a pointer to the next character, + - `radix` must be in range 2 to 36, else return `JS_NAN`. + - `flags` is a combination of the flags below. + There is a null byte at `p[len]`, but there might be embedded null + bytes between `p[0]` and `p[len]` which must produce `JS_NAN` if + the `ATOD_NO_TRAILING_CHARS` flag is present. + */ + +#define ATOD_TRIM_SPACES (1 << 0) /* trim white space */ +#define ATOD_ACCEPT_EMPTY (1 << 1) /* accept an empty string, value is 0 */ +#define ATOD_ACCEPT_FLOAT (1 << 2) /* parse decimal floating point syntax */ +#define ATOD_ACCEPT_INFINITY (1 << 3) /* parse Infinity as a float point number */ +#define ATOD_ACCEPT_BIN_OCT (1 << 4) /* accept 0o and 0b prefixes */ +#define ATOD_ACCEPT_HEX_PREFIX (1 << 5) /* accept 0x prefix for radix 16 */ +#define ATOD_ACCEPT_UNDERSCORES (1 << 6) /* accept _ between digits as a digit separator */ +#define ATOD_ACCEPT_SUFFIX (1 << 7) /* allow 'n' suffix to produce BigInt */ +#define ATOD_WANT_BIG_INT (1 << 8) /* return type must be BigInt */ +#define ATOD_DECIMAL_AFTER_SIGN (1 << 9) /* only accept decimal number after sign */ +#define ATOD_NO_TRAILING_CHARS (1 << 10) /* do not accept trailing characters */ + +static JSValue js_atof(JSContext *ctx, const char *p, size_t len, + const char **pp, int radix, int flags) +{ + const char *p_start; + const char *end = p + len; + int sep; + BOOL is_float; + char buf1[64], *buf = buf1; + size_t i, j; + JSValue val = JS_NAN; + double d; + char sign; + + if (radix < 2 || radix > 36) + goto done; + + /* optional separator between digits */ + sep = (flags & ATOD_ACCEPT_UNDERSCORES) ? '_' : 256; + sign = 0; + if (flags & ATOD_TRIM_SPACES) + p += skip_spaces(p); + if (p == end && (flags & ATOD_ACCEPT_EMPTY)) { + if (pp) *pp = p; + if (flags & ATOD_WANT_BIG_INT) + return JS_NewBigInt64(ctx, 0); + else + return js_int32(0); + } + if (*p == '+' || *p == '-') { + sign = *p; + p++; + if (flags & ATOD_DECIMAL_AFTER_SIGN) + flags &= ~(ATOD_ACCEPT_HEX_PREFIX | ATOD_ACCEPT_BIN_OCT); + } + if (p[0] == '0') { + if ((p[1] == 'x' || p[1] == 'X') && + ((flags & ATOD_ACCEPT_HEX_PREFIX) || radix == 16)) { + p += 2; + radix = 16; + } else if (flags & ATOD_ACCEPT_BIN_OCT) { + if (p[1] == 'o' || p[1] == 'O') { + p += 2; + radix = 8; + } else if (p[1] == 'b' || p[1] == 'B') { + p += 2; + radix = 2; + } + } + } else { + if (*p == 'I' && (flags & ATOD_ACCEPT_INFINITY) && strstart(p, "Infinity", &p)) { + d = INF; + if (sign == '-') + d = -d; + val = js_float64(d); + goto done; + } + } + is_float = FALSE; + p_start = p; + while (to_digit(*p) < radix) { + p++; + if (*p == sep && to_digit(p[1]) < radix) + p++; + } + if ((flags & ATOD_ACCEPT_FLOAT) && radix == 10) { + if (*p == '.' && (p > p_start || to_digit(p[1]) < radix)) { + is_float = TRUE; + p++; + while (to_digit(*p) < radix) { + p++; + if (*p == sep && to_digit(p[1]) < radix) + p++; + } + } + if (p > p_start && (*p == 'e' || *p == 'E')) { + i = 1; + if (p[1] == '+' || p[1] == '-') { + i++; + } + if (is_digit(p[i])) { + is_float = TRUE; + p += i + 1; + while (is_digit(*p) || (*p == sep && is_digit(p[1]))) + p++; + } + } + } + if (p == p_start) + goto done; + + len = p - p_start; + if (unlikely((len + 2) > sizeof(buf1))) { + buf = js_malloc_rt(ctx->rt, len + 2); /* no exception raised */ + if (!buf) { + if (pp) *pp = p; + return JS_ThrowOutOfMemory(ctx); + } + } + /* remove the separators and the radix prefix */ + j = 0; + if (sign == '-') + buf[j++] = '-'; + for (i = 0; i < len; i++) { + if (p_start[i] != '_') + buf[j++] = p_start[i]; + } + buf[j] = '\0'; + + if (flags & ATOD_ACCEPT_SUFFIX) { + if (*p == 'n') { + p++; + flags |= ATOD_WANT_BIG_INT; + } + } + + if (flags & ATOD_WANT_BIG_INT) { + if (!is_float) + val = js_string_to_bigint(ctx, buf, radix); + } else { + d = js_strtod(buf, radix, is_float); + val = js_number(d); /* return int or float64 */ + } + + done: + if (flags & ATOD_NO_TRAILING_CHARS) { + if (flags & ATOD_TRIM_SPACES) + p += skip_spaces(p); + if (p != end) { + JS_FreeValue(ctx, val); + val = JS_NAN; + } + } + if (buf != buf1) + js_free_rt(ctx->rt, buf); + if (pp) *pp = p; + return val; +} + +typedef enum JSToNumberHintEnum { + TON_FLAG_NUMBER, + TON_FLAG_NUMERIC, +} JSToNumberHintEnum; + +static JSValue JS_ToNumberHintFree(JSContext *ctx, JSValue val, + JSToNumberHintEnum flag) +{ + uint32_t tag; + JSValue ret; + + redo: + tag = JS_VALUE_GET_NORM_TAG(val); + switch(tag) { + case JS_TAG_BIG_INT: + if (flag != TON_FLAG_NUMERIC) { + JS_FreeValue(ctx, val); + return JS_ThrowTypeError(ctx, "cannot convert BigInt to number"); + } + ret = val; + break; + case JS_TAG_FLOAT64: + case JS_TAG_INT: + case JS_TAG_EXCEPTION: + ret = val; + break; + case JS_TAG_BOOL: + case JS_TAG_NULL: + ret = js_int32(JS_VALUE_GET_INT(val)); + break; + case JS_TAG_UNDEFINED: + ret = JS_NAN; + break; + case JS_TAG_OBJECT: + val = JS_ToPrimitiveFree(ctx, val, HINT_NUMBER); + if (JS_IsException(val)) + return JS_EXCEPTION; + goto redo; + case JS_TAG_STRING: + { + const char *str; + size_t len; + int flags; + + str = JS_ToCStringLen(ctx, &len, val); + JS_FreeValue(ctx, val); + if (!str) + return JS_EXCEPTION; + flags = ATOD_TRIM_SPACES | ATOD_ACCEPT_EMPTY | + ATOD_ACCEPT_FLOAT | ATOD_ACCEPT_INFINITY | + ATOD_ACCEPT_HEX_PREFIX | ATOD_ACCEPT_BIN_OCT | + ATOD_DECIMAL_AFTER_SIGN | ATOD_NO_TRAILING_CHARS; + ret = js_atof(ctx, str, len, NULL, 10, flags); + JS_FreeCString(ctx, str); + } + break; + case JS_TAG_SYMBOL: + JS_FreeValue(ctx, val); + return JS_ThrowTypeError(ctx, "cannot convert symbol to number"); + default: + JS_FreeValue(ctx, val); + ret = JS_NAN; + break; + } + return ret; +} + +static JSValue JS_ToNumberFree(JSContext *ctx, JSValue val) +{ + return JS_ToNumberHintFree(ctx, val, TON_FLAG_NUMBER); +} + +static JSValue JS_ToNumericFree(JSContext *ctx, JSValue val) +{ + return JS_ToNumberHintFree(ctx, val, TON_FLAG_NUMERIC); +} + +static JSValue JS_ToNumeric(JSContext *ctx, JSValue val) +{ + return JS_ToNumericFree(ctx, js_dup(val)); +} + +static __exception int __JS_ToFloat64Free(JSContext *ctx, double *pres, + JSValue val) +{ + double d; + uint32_t tag; + + val = JS_ToNumberFree(ctx, val); + if (JS_IsException(val)) { + *pres = JS_FLOAT64_NAN; + return -1; + } + tag = JS_VALUE_GET_NORM_TAG(val); + switch(tag) { + case JS_TAG_INT: + d = JS_VALUE_GET_INT(val); + break; + case JS_TAG_FLOAT64: + d = JS_VALUE_GET_FLOAT64(val); + break; + case JS_TAG_BIG_INT: + { + JSBigInt *p = JS_VALUE_GET_PTR(val); + /* XXX: there can be a double rounding issue with some + primitives (such as JS_ToUint8ClampFree()), but it is + not critical to fix it. */ + bf_get_float64(&p->num, &d, BF_RNDN); + JS_FreeValue(ctx, val); + } + break; + default: + abort(); + } + *pres = d; + return 0; +} + +static inline int JS_ToFloat64Free(JSContext *ctx, double *pres, JSValue val) +{ + uint32_t tag; + + tag = JS_VALUE_GET_TAG(val); + if (tag <= JS_TAG_NULL) { + *pres = JS_VALUE_GET_INT(val); + return 0; + } else if (JS_TAG_IS_FLOAT64(tag)) { + *pres = JS_VALUE_GET_FLOAT64(val); + return 0; + } else { + return __JS_ToFloat64Free(ctx, pres, val); + } +} + +int JS_ToFloat64(JSContext *ctx, double *pres, JSValue val) +{ + return JS_ToFloat64Free(ctx, pres, js_dup(val)); +} + +static JSValue JS_ToNumber(JSContext *ctx, JSValue val) +{ + return JS_ToNumberFree(ctx, js_dup(val)); +} + +/* same as JS_ToNumber() but return 0 in case of NaN/Undefined */ +static __maybe_unused JSValue JS_ToIntegerFree(JSContext *ctx, JSValue val) +{ + uint32_t tag; + JSValue ret; + + redo: + tag = JS_VALUE_GET_NORM_TAG(val); + switch(tag) { + case JS_TAG_INT: + case JS_TAG_BOOL: + case JS_TAG_NULL: + case JS_TAG_UNDEFINED: + ret = js_int32(JS_VALUE_GET_INT(val)); + break; + case JS_TAG_FLOAT64: + { + double d = JS_VALUE_GET_FLOAT64(val); + if (isnan(d)) { + ret = js_int32(0); + } else { + /* convert -0 to +0 */ + d = trunc(d) + 0.0; + ret = js_number(d); + } + } + break; + default: + val = JS_ToNumberFree(ctx, val); + if (JS_IsException(val)) + return val; + goto redo; + } + return ret; +} + +/* Note: the integer value is satured to 32 bits */ +static int JS_ToInt32SatFree(JSContext *ctx, int *pres, JSValue val) +{ + uint32_t tag; + int ret; + + redo: + tag = JS_VALUE_GET_NORM_TAG(val); + switch(tag) { + case JS_TAG_INT: + case JS_TAG_BOOL: + case JS_TAG_NULL: + case JS_TAG_UNDEFINED: + ret = JS_VALUE_GET_INT(val); + break; + case JS_TAG_EXCEPTION: + *pres = 0; + return -1; + case JS_TAG_FLOAT64: + { + double d = JS_VALUE_GET_FLOAT64(val); + if (isnan(d)) { + ret = 0; + } else { + if (d < INT32_MIN) + ret = INT32_MIN; + else if (d > INT32_MAX) + ret = INT32_MAX; + else + ret = (int)d; + } + } + break; + default: + val = JS_ToNumberFree(ctx, val); + if (JS_IsException(val)) { + *pres = 0; + return -1; + } + goto redo; + } + *pres = ret; + return 0; +} + +int JS_ToInt32Sat(JSContext *ctx, int *pres, JSValue val) +{ + return JS_ToInt32SatFree(ctx, pres, js_dup(val)); +} + +int JS_ToInt32Clamp(JSContext *ctx, int *pres, JSValue val, + int min, int max, int min_offset) +{ + int res = JS_ToInt32SatFree(ctx, pres, js_dup(val)); + if (res == 0) { + if (*pres < min) { + *pres += min_offset; + if (*pres < min) + *pres = min; + } else { + if (*pres > max) + *pres = max; + } + } + return res; +} + +static int JS_ToInt64SatFree(JSContext *ctx, int64_t *pres, JSValue val) +{ + uint32_t tag; + + redo: + tag = JS_VALUE_GET_NORM_TAG(val); + switch(tag) { + case JS_TAG_INT: + case JS_TAG_BOOL: + case JS_TAG_NULL: + case JS_TAG_UNDEFINED: + *pres = JS_VALUE_GET_INT(val); + return 0; + case JS_TAG_EXCEPTION: + *pres = 0; + return -1; + case JS_TAG_FLOAT64: + { + double d = JS_VALUE_GET_FLOAT64(val); + if (isnan(d)) { + *pres = 0; + } else { + if (d < INT64_MIN) + *pres = INT64_MIN; + else if (d >= 0x1p63) + *pres = INT64_MAX; + else + *pres = (int64_t)d; + } + } + return 0; + default: + val = JS_ToNumberFree(ctx, val); + if (JS_IsException(val)) { + *pres = 0; + return -1; + } + goto redo; + } +} + +int JS_ToInt64Sat(JSContext *ctx, int64_t *pres, JSValue val) +{ + return JS_ToInt64SatFree(ctx, pres, js_dup(val)); +} + +int JS_ToInt64Clamp(JSContext *ctx, int64_t *pres, JSValue val, + int64_t min, int64_t max, int64_t neg_offset) +{ + int res = JS_ToInt64SatFree(ctx, pres, js_dup(val)); + if (res == 0) { + if (*pres < 0) + *pres += neg_offset; + if (*pres < min) + *pres = min; + else if (*pres > max) + *pres = max; + } + return res; +} + +/* Same as JS_ToInt32Free() but with a 64 bit result. Return (<0, 0) + in case of exception */ +static int JS_ToInt64Free(JSContext *ctx, int64_t *pres, JSValue val) +{ + uint32_t tag; + int64_t ret; + + redo: + tag = JS_VALUE_GET_NORM_TAG(val); + switch(tag) { + case JS_TAG_INT: + case JS_TAG_BOOL: + case JS_TAG_NULL: + case JS_TAG_UNDEFINED: + ret = JS_VALUE_GET_INT(val); + break; + case JS_TAG_FLOAT64: + { + JSFloat64Union u; + double d; + int e; + d = JS_VALUE_GET_FLOAT64(val); + u.d = d; + /* we avoid doing fmod(x, 2^64) */ + e = (u.u64 >> 52) & 0x7ff; + if (likely(e <= (1023 + 62))) { + /* fast case */ + ret = (int64_t)d; + } else if (e <= (1023 + 62 + 53)) { + uint64_t v; + /* remainder modulo 2^64 */ + v = (u.u64 & (((uint64_t)1 << 52) - 1)) | ((uint64_t)1 << 52); + ret = v << ((e - 1023) - 52); + /* take the sign into account */ + if (u.u64 >> 63) + if (ret != INT64_MIN) + ret = -ret; + } else { + ret = 0; /* also handles NaN and +inf */ + } + } + break; + default: + val = JS_ToNumberFree(ctx, val); + if (JS_IsException(val)) { + *pres = 0; + return -1; + } + goto redo; + } + *pres = ret; + return 0; +} + +int JS_ToInt64(JSContext *ctx, int64_t *pres, JSValue val) +{ + return JS_ToInt64Free(ctx, pres, js_dup(val)); +} + +int JS_ToInt64Ext(JSContext *ctx, int64_t *pres, JSValue val) +{ + if (JS_IsBigInt(ctx, val)) + return JS_ToBigInt64(ctx, pres, val); + else + return JS_ToInt64(ctx, pres, val); +} + +/* return (<0, 0) in case of exception */ +static int JS_ToInt32Free(JSContext *ctx, int32_t *pres, JSValue val) +{ + uint32_t tag; + int32_t ret; + + redo: + tag = JS_VALUE_GET_NORM_TAG(val); + switch(tag) { + case JS_TAG_INT: + case JS_TAG_BOOL: + case JS_TAG_NULL: + case JS_TAG_UNDEFINED: + ret = JS_VALUE_GET_INT(val); + break; + case JS_TAG_FLOAT64: + { + JSFloat64Union u; + double d; + int e; + d = JS_VALUE_GET_FLOAT64(val); + u.d = d; + /* we avoid doing fmod(x, 2^32) */ + e = (u.u64 >> 52) & 0x7ff; + if (likely(e <= (1023 + 30))) { + /* fast case */ + ret = (int32_t)d; + } else if (e <= (1023 + 30 + 53)) { + uint64_t v; + /* remainder modulo 2^32 */ + v = (u.u64 & (((uint64_t)1 << 52) - 1)) | ((uint64_t)1 << 52); + v = v << ((e - 1023) - 52 + 32); + ret = v >> 32; + /* take the sign into account */ + if (u.u64 >> 63) + if (ret != INT32_MIN) + ret = -ret; + } else { + ret = 0; /* also handles NaN and +inf */ + } + } + break; + default: + val = JS_ToNumberFree(ctx, val); + if (JS_IsException(val)) { + *pres = 0; + return -1; + } + goto redo; + } + *pres = ret; + return 0; +} + +int JS_ToInt32(JSContext *ctx, int32_t *pres, JSValue val) +{ + return JS_ToInt32Free(ctx, pres, js_dup(val)); +} + +static inline int JS_ToUint32Free(JSContext *ctx, uint32_t *pres, JSValue val) +{ + return JS_ToInt32Free(ctx, (int32_t *)pres, val); +} + +static int JS_ToUint8ClampFree(JSContext *ctx, int32_t *pres, JSValue val) +{ + uint32_t tag; + int res; + + redo: + tag = JS_VALUE_GET_NORM_TAG(val); + switch(tag) { + case JS_TAG_INT: + case JS_TAG_BOOL: + case JS_TAG_NULL: + case JS_TAG_UNDEFINED: + res = JS_VALUE_GET_INT(val); + res = max_int(0, min_int(255, res)); + break; + case JS_TAG_FLOAT64: + { + double d = JS_VALUE_GET_FLOAT64(val); + if (isnan(d)) { + res = 0; + } else { + if (d < 0) + res = 0; + else if (d > 255) + res = 255; + else + res = lrint(d); + } + } + break; + default: + val = JS_ToNumberFree(ctx, val); + if (JS_IsException(val)) { + *pres = 0; + return -1; + } + goto redo; + } + *pres = res; + return 0; +} + +static __exception int JS_ToArrayLengthFree(JSContext *ctx, uint32_t *plen, + JSValue val, BOOL is_array_ctor) +{ + uint32_t tag, len; + + tag = JS_VALUE_GET_TAG(val); + switch(tag) { + case JS_TAG_INT: + case JS_TAG_BOOL: + case JS_TAG_NULL: + { + int v; + v = JS_VALUE_GET_INT(val); + if (v < 0) + goto fail; + len = v; + } + break; + case JS_TAG_BIG_INT: + { + JSBigInt *p = JS_VALUE_GET_PTR(val); + bf_t a; + BOOL res; + bf_get_int32((int32_t *)&len, &p->num, BF_GET_INT_MOD); + bf_init(ctx->bf_ctx, &a); + bf_set_ui(&a, len); + res = bf_cmp_eq(&a, &p->num); + bf_delete(&a); + JS_FreeValue(ctx, val); + if (!res) + goto fail; + } + break; + default: + if (JS_TAG_IS_FLOAT64(tag)) { + double d; + d = JS_VALUE_GET_FLOAT64(val); + if (!(d >= 0 && d <= UINT32_MAX)) + goto fail; + len = (uint32_t)d; + if (len != d) + goto fail; + } else { + uint32_t len1; + + if (is_array_ctor) { + val = JS_ToNumberFree(ctx, val); + if (JS_IsException(val)) + return -1; + /* cannot recurse because val is a number */ + if (JS_ToArrayLengthFree(ctx, &len, val, TRUE)) + return -1; + } else { + /* legacy behavior: must do the conversion twice and compare */ + if (JS_ToUint32(ctx, &len, val)) { + JS_FreeValue(ctx, val); + return -1; + } + val = JS_ToNumberFree(ctx, val); + if (JS_IsException(val)) + return -1; + /* cannot recurse because val is a number */ + if (JS_ToArrayLengthFree(ctx, &len1, val, FALSE)) + return -1; + if (len1 != len) { + fail: + JS_ThrowRangeError(ctx, "invalid array length"); + return -1; + } + } + } + break; + } + *plen = len; + return 0; +} + +#define MAX_SAFE_INTEGER (((int64_t)1 << 53) - 1) + +static BOOL is_safe_integer(double d) +{ + return isfinite(d) && floor(d) == d && + fabs(d) <= (double)MAX_SAFE_INTEGER; +} + +int JS_ToIndex(JSContext *ctx, uint64_t *plen, JSValue val) +{ + int64_t v; + if (JS_ToInt64Sat(ctx, &v, val)) + return -1; + if (v < 0 || v > MAX_SAFE_INTEGER) { + JS_ThrowRangeError(ctx, "invalid array index"); + *plen = 0; + return -1; + } + *plen = v; + return 0; +} + +/* convert a value to a length between 0 and MAX_SAFE_INTEGER. + return -1 for exception */ +static __exception int JS_ToLengthFree(JSContext *ctx, int64_t *plen, + JSValue val) +{ + int res = JS_ToInt64Clamp(ctx, plen, val, 0, MAX_SAFE_INTEGER, 0); + JS_FreeValue(ctx, val); + return res; +} + +/* Note: can return an exception */ +static int JS_NumberIsInteger(JSContext *ctx, JSValue val) +{ + double d; + if (!JS_IsNumber(val)) + return FALSE; + if (unlikely(JS_ToFloat64(ctx, &d, val))) + return -1; + return isfinite(d) && floor(d) == d; +} + +static BOOL JS_NumberIsNegativeOrMinusZero(JSContext *ctx, JSValue val) +{ + uint32_t tag; + + tag = JS_VALUE_GET_NORM_TAG(val); + switch(tag) { + case JS_TAG_INT: + { + int v; + v = JS_VALUE_GET_INT(val); + return (v < 0); + } + case JS_TAG_FLOAT64: + { + JSFloat64Union u; + u.d = JS_VALUE_GET_FLOAT64(val); + return (u.u64 >> 63); + } + case JS_TAG_BIG_INT: + { + JSBigInt *p = JS_VALUE_GET_PTR(val); + /* Note: integer zeros are not necessarily positive */ + return p->num.sign && !bf_is_zero(&p->num); + } + default: + return FALSE; + } +} + +static JSValue js_bigint_to_string1(JSContext *ctx, JSValue val, int radix) +{ + JSValue ret; + bf_t a_s, *a; + char *str; + int saved_sign; + size_t len; + + a = JS_ToBigInt(ctx, &a_s, val); + if (!a) + return JS_EXCEPTION; + saved_sign = a->sign; + if (a->expn == BF_EXP_ZERO) + a->sign = 0; + str = bf_ftoa(&len, a, radix, 0, BF_RNDZ | BF_FTOA_FORMAT_FRAC | + BF_FTOA_JS_QUIRKS); + a->sign = saved_sign; + JS_FreeBigInt(ctx, a, &a_s); + if (!str) + return JS_ThrowOutOfMemory(ctx); + ret = js_new_string8_len(ctx, str, len); + bf_free(ctx->bf_ctx, str); + return ret; +} + +static JSValue js_bigint_to_string(JSContext *ctx, JSValue val) +{ + return js_bigint_to_string1(ctx, val, 10); +} + +/*---- floating point number to string conversions ----*/ + +/* JavaScript rounding is specified as round to nearest tie away + from zero (RNDNA), but in `printf` the "ties" case is not + specified (in most cases it is RNDN, round to nearest, tie to even), + so we must round manually. We generate 2 extra places and make + an extra call to snprintf if these are exactly '50'. + We set the current rounding mode to FE_DOWNWARD to check if the + last 2 places become '49'. If not, we must round up, which is + performed in place using the string digits. + + Note that we cannot rely on snprintf for rounding up: + the code below fails on macOS for `0.5.toFixed(0)`: gives `0` expected `1` + fesetround(FE_UPWARD); + snprintf(dest, size, "%.*f", n_digits, d); + fesetround(FE_TONEAREST); + */ + +/* `js_fcvt` minimum buffer length: + - up to 21 digits in integral part + - 1 potential decimal point + - up to 102 decimals + - 1 null terminator + */ +#define JS_FCVT_BUF_SIZE (21+1+102+1) + +/* `js_ecvt` minimum buffer length: + - 1 leading digit + - 1 potential decimal point + - up to 102 decimals + - 5 exponent characters (from 'e-324' to 'e+308') + - 1 null terminator + */ +#define JS_ECVT_BUF_SIZE (1+1+102+5+1) + +/* `js_dtoa` minimum buffer length: + - 8 byte prefix + - either JS_FCVT_BUF_SIZE or JS_ECVT_BUF_SIZE + - JS_FCVT_BUF_SIZE is larger than JS_ECVT_BUF_SIZE + */ +#define JS_DTOA_BUF_SIZE (8+JS_FCVT_BUF_SIZE) + +/* `js_ecvt1`: compute the digits and decimal point spot for a double + - `d` is finite, positive or zero + - `n_digits` number of significant digits in range 1..103 + - `buf` receives the printf result + - `buf` has a fixed format: n_digits with a decimal point at offset 1 + and exponent 'e{+/-}xx[x]' at offset n_digits+1 + Return n_digits + Store the position of the decimal point into `*decpt` + */ +static int js_ecvt1(double d, int n_digits, + char dest[minimum_length(JS_ECVT_BUF_SIZE)], + size_t size, int *decpt) +{ + /* d is positive, ensure decimal point is always present */ + snprintf(dest, size, "%#.*e", n_digits - 1, d); + /* dest contents: + 0: first digit + 1: '.' decimal point (locale specific) + 2..n_digits: (n_digits-1) additional digits + n_digits+1: 'e' exponent mark + n_digits+2..: exponent sign, value and null terminator + */ + /* extract the exponent (actually the position of the decimal point) */ + *decpt = 1 + atoi(dest + n_digits + 2); + return n_digits; +} + +/* `js_ecvt`: compute the digits and decimal point spot for a double + with proper javascript rounding. We cannot use `ecvt` for multiple + resasons: portability, because of the number of digits is typically + limited to 17, finally because the default rounding is inadequate. + `d` is finite and positive or zero. + `n_digits` number of significant digits in range 1..101 + or 0 for automatic (only as many digits as necessary) + Return the number of digits produced in `dest`. + Store the position of the decimal point into `*decpt` + */ +static int js_ecvt(double d, int n_digits, + char dest[minimum_length(JS_ECVT_BUF_SIZE)], + size_t size, int *decpt) +{ + if (n_digits == 0) { + /* find the minimum number of digits (XXX: inefficient but simple) */ + // TODO(chqrlie) use direct method from quickjs-printf + unsigned int n_digits_min = 1; + unsigned int n_digits_max = 17; + for (;;) { + n_digits = (n_digits_min + n_digits_max) / 2; + js_ecvt1(d, n_digits, dest, size, decpt); + if (n_digits_min == n_digits_max) + return n_digits; + /* dest contents: + 0: first digit + 1: '.' decimal point (locale specific) + 2..n_digits: (n_digits-1) additional digits + n_digits+1: 'e' exponent mark + n_digits+2..: exponent sign, value and null terminator + */ + if (strtod(dest, NULL) == d) { + unsigned int n0 = n_digits; + /* enough digits */ + /* strip the trailing zeros */ + while (dest[n_digits] == '0') + n_digits--; + if (n_digits == n_digits_min) + return n_digits; + /* done if trailing zeros and not denormal or huge */ + if (n_digits < n0 && d > 3e-308 && d < 8e307) + return n_digits; + n_digits_max = n_digits; + } else { + /* need at least one more digit */ + n_digits_min = n_digits + 1; + } + } + } else { +#if defined(FE_DOWNWARD) && defined(FE_TONEAREST) + /* generate 2 extra digits: 99% chances to avoid 2 calls */ + js_ecvt1(d, n_digits + 2, dest, size, decpt); + if (dest[n_digits + 1] < '5') + return n_digits; /* truncate the 2 extra digits */ + if (dest[n_digits + 1] == '5' && dest[n_digits + 2] == '0') { + /* close to half-way: try rounding toward 0 */ + fesetround(FE_DOWNWARD); + js_ecvt1(d, n_digits + 2, dest, size, decpt); + fesetround(FE_TONEAREST); + if (dest[n_digits + 1] < '5') + return n_digits; /* truncate the 2 extra digits */ + } + /* round up in the string */ + for(int i = n_digits;; i--) { + /* ignore the locale specific decimal point */ + if (is_digit(dest[i])) { + if (dest[i]++ < '9') + break; + dest[i] = '0'; + if (i == 0) { + dest[0] = '1'; + (*decpt)++; + break; + } + } + } + return n_digits; /* truncate the 2 extra digits */ +#else + /* No disambiguation available, eg: __wasi__ targets */ + return js_ecvt1(d, n_digits, dest, size, decpt); +#endif + } +} + +/* `js_fcvt`: convert a floating point value to %f format using RNDNA + `d` is finite and positive or zero. + `n_digits` number of decimal places in range 0..100 + Return the number of characters produced in `dest`. + */ +static size_t js_fcvt(double d, int n_digits, + char dest[minimum_length(JS_FCVT_BUF_SIZE)], size_t size) +{ +#if defined(FE_DOWNWARD) && defined(FE_TONEAREST) + int i, n1; + /* generate 2 extra digits: 99% chances to avoid 2 calls */ + n1 = snprintf(dest, size, "%.*f", n_digits + 2, d) - 2; + if (dest[n1] >= '5') { + if (dest[n1] == '5' && dest[n1 + 1] == '0') { + /* close to half-way: try rounding toward 0 */ + fesetround(FE_DOWNWARD); + n1 = snprintf(dest, size, "%.*f", n_digits + 2, d) - 2; + fesetround(FE_TONEAREST); + } + if (dest[n1] >= '5') { /* number should be rounded up */ + /* d is either exactly half way or greater: round the string manually */ + for (i = n1 - 1;; i--) { + /* ignore the locale specific decimal point */ + if (is_digit(dest[i])) { + if (dest[i]++ < '9') + break; + dest[i] = '0'; + if (i == 0) { + dest[0] = '1'; + dest[n1] = '0'; + dest[n1 - n_digits - 1] = '0'; + dest[n1 - n_digits] = '.'; + n1++; + break; + } + } + } + } + } + /* truncate the extra 2 digits and the decimal point if !n_digits */ + n1 -= !n_digits; + //dest[n1] = '\0'; // optional + return n1; +#else + /* No disambiguation available, eg: __wasi__ targets */ + return snprintf(dest, size, "%.*f", n_digits, d); +#endif +} + +static JSValue js_dtoa_infinite(JSContext *ctx, double d) +{ + // TODO(chqrlie) use atoms for NaN and Infinite? + if (isnan(d)) + return js_new_string8(ctx, "NaN"); + if (d < 0) + return js_new_string8(ctx, "-Infinity"); + else + return js_new_string8(ctx, "Infinity"); +} + +#define JS_DTOA_TOSTRING 0 /* use as many digits as necessary */ +#define JS_DTOA_EXPONENTIAL 1 /* use exponential notation either fixed or variable digits */ +#define JS_DTOA_FIXED 2 /* force fixed number of fractional digits */ +#define JS_DTOA_PRECISION 3 /* use n_digits significant digits (1 <= n_digits <= 101) */ + +/* `js_dtoa`: convert a floating point number to a string + - `mode`: one of the 4 supported formats + - `n_digits`: digit number according to mode + - TOSTRING: 0 only. As many digits as necessary + - EXPONENTIAL: 0 as many decimals as necessary + - 1..101 number of significant digits + - FIXED: 0..100 number of decimal places + - PRECISION: 1..101 number of significant digits + */ +// XXX: should use libbf or quickjs-printf. +static JSValue js_dtoa(JSContext *ctx, double d, int n_digits, int mode) +{ + char buf[JS_DTOA_BUF_SIZE]; + size_t len; + char *start; + int sign, decpt, exp, i, k, n, n_max; + + if (!isfinite(d)) + return js_dtoa_infinite(ctx, d); + + sign = (d < 0); + start = buf + 8; + d = fabs(d); /* also converts -0 to 0 */ + + if (mode != JS_DTOA_EXPONENTIAL && n_digits == 0) { + /* fast path for exact integers in variable format: + clip to MAX_SAFE_INTEGER because to ensure insignificant + digits are generated as 0. + used for JS_DTOA_TOSTRING and JS_DTOA_FIXED without decimals. + */ + if (d <= (double)MAX_SAFE_INTEGER) { + uint64_t u64 = (uint64_t)d; + if (d == u64) { + len = u64toa(start, u64); + goto done; + } + } + } + if (mode == JS_DTOA_FIXED) { + len = js_fcvt(d, n_digits, start, sizeof(buf) - 8); + // TODO(chqrlie) patch the locale specific decimal point + goto done; + } + + n_max = (n_digits > 0) ? n_digits : 21; + /* the number has k digits (1 <= k <= n_max) */ + k = js_ecvt(d, n_digits, start, sizeof(buf) - 8, &decpt); + /* buffer contents: + 0: first digit + 1: '.' decimal point + 2..k: (k-1) additional digits + */ + n = decpt; /* d=10^(n-k)*(buf1) i.e. d= < x.yyyy 10^(n-1) */ + if (mode != JS_DTOA_EXPONENTIAL) { + /* mode is JS_DTOA_PRECISION or JS_DTOA_TOSTRING */ + if (n >= 1 && n <= n_max) { + /* between 1 and n_max digits before the decimal point */ + if (k <= n) { + /* all digits before the point, append zeros */ + start[1] = start[0]; + start++; + for(i = k; i < n; i++) + start[i] = '0'; + len = n; + } else { + /* k > n: move digits before the point */ + for(i = 1; i < n; i++) + start[i] = start[i + 1]; + start[i] = '.'; + len = 1 + k; + } + goto done; + } + if (n >= -5 && n <= 0) { + /* insert -n leading 0 decimals and a '0.' prefix */ + n = -n; + start[1] = start[0]; + start -= n + 1; + start[0] = '0'; + start[1] = '.'; + for(i = 0; i < n; i++) + start[2 + i] = '0'; + len = 2 + k + n; + goto done; + } + } + /* exponential notation */ + exp = n - 1; + /* count the digits and the decimal point if at least one decimal */ + len = k + (k > 1); + start[1] = '.'; /* patch the locale specific decimal point */ + start[len] = 'e'; + start[len + 1] = '+'; + if (exp < 0) { + start[len + 1] = '-'; + exp = -exp; + } + len += 2 + 1 + (exp > 9) + (exp > 99); + for (i = len - 1; exp > 9;) { + int quo = exp / 10; + start[i--] = (char)('0' + exp % 10); + exp = quo; + } + start[i] = (char)('0' + exp); + + done: + start[-1] = '-'; /* prepend the sign if negative */ + return js_new_string8_len(ctx, start - sign, len + sign); +} + +/* `js_dtoa_radix`: convert a floating point number using a specific base + - `d` must be finite + - `radix` must be in range 2..36 + */ +static JSValue js_dtoa_radix(JSContext *ctx, double d, int radix) +{ + char buf[2200], *ptr, *ptr2, *ptr3; + int sign, digit; + double frac, d0; + int64_t n0; + + if (!isfinite(d)) + return js_dtoa_infinite(ctx, d); + + sign = (d < 0); + d = fabs(d); + d0 = trunc(d); + n0 = 0; + frac = d - d0; + ptr2 = buf + 1100; /* ptr2 points to the end of the string */ + ptr = ptr2; /* ptr points to the beginning of the string */ + if (d0 <= MAX_SAFE_INTEGER) { + int64_t n = n0 = (int64_t)d0; + while (n >= radix) { + digit = n % radix; + n = n / radix; + *--ptr = digits36[digit]; + } + *--ptr = digits36[(size_t)n]; + } else { + /* no decimals */ + while (d0 >= radix) { + digit = fmod(d0, radix); + d0 = trunc(d0 / radix); + if (d0 >= MAX_SAFE_INTEGER) + digit = 0; + *--ptr = digits36[digit]; + } + *--ptr = digits36[(size_t)d0]; + goto done; + } + if (frac != 0) { + double log2_radix = log2(radix); + double prec = 1023 + 51; // handle subnormals + *ptr2++ = '.'; + while (frac != 0 && n0 <= MAX_SAFE_INTEGER/2 && prec > 0) { + frac *= radix; + digit = trunc(frac); + frac -= digit; + *ptr2++ = digits36[digit]; + n0 = n0 * radix + digit; + prec -= log2_radix; + } + if (frac * radix >= radix / 2) { + /* round up the string representation manually */ + char nine = digits36[radix - 1]; + while (ptr2[-1] == nine) { + /* strip trailing '9' or equivalent digits */ + ptr2--; + } + if (ptr2[-1] == '.') { + /* strip the 'decimal' point */ + ptr2--; + /* increment the integral part */ + for (ptr3 = ptr2;;) { + if (ptr3[-1] != nine) { + ptr3[-1] = (ptr3[-1] == '9') ? 'a' : ptr3[-1] + 1; + break; + } + *--ptr3 = '0'; + if (ptr3 <= ptr) { + /* prepend a '1' if number was all nines */ + *--ptr = '1'; + break; + } + } + } else { + /* increment the last fractional digit */ + ptr2[-1] = (ptr2[-1] == '9') ? 'a' : ptr2[-1] + 1; + } + } else { + /* strip trailing fractional zeros */ + while (ptr2[-1] == '0') + ptr2--; + /* strip the 'decimal' point if last */ + ptr2 -= (ptr2[-1] == '.'); + } + } +done: + ptr[-1] = '-'; + ptr -= sign; + return js_new_string8_len(ctx, ptr, ptr2 - ptr); +} + +JSValue JS_ToStringInternal(JSContext *ctx, JSValue val, BOOL is_ToPropertyKey) +{ + uint32_t tag; + char buf[32]; + size_t len; + + tag = JS_VALUE_GET_NORM_TAG(val); + switch(tag) { + case JS_TAG_STRING: + return js_dup(val); + case JS_TAG_INT: + len = i32toa(buf, JS_VALUE_GET_INT(val)); + return js_new_string8_len(ctx, buf, len); + case JS_TAG_BOOL: + return JS_AtomToString(ctx, JS_VALUE_GET_BOOL(val) ? + JS_ATOM_true : JS_ATOM_false); + case JS_TAG_NULL: + return JS_AtomToString(ctx, JS_ATOM_null); + case JS_TAG_UNDEFINED: + return JS_AtomToString(ctx, JS_ATOM_undefined); + case JS_TAG_EXCEPTION: + return JS_EXCEPTION; + case JS_TAG_OBJECT: + { + JSValue val1, ret; + val1 = JS_ToPrimitive(ctx, val, HINT_STRING); + if (JS_IsException(val1)) + return val1; + ret = JS_ToStringInternal(ctx, val1, is_ToPropertyKey); + JS_FreeValue(ctx, val1); + return ret; + } + break; + case JS_TAG_FUNCTION_BYTECODE: + return js_new_string8(ctx, "[function bytecode]"); + case JS_TAG_SYMBOL: + if (is_ToPropertyKey) { + return js_dup(val); + } else { + return JS_ThrowTypeError(ctx, "cannot convert symbol to string"); + } + case JS_TAG_FLOAT64: + return js_dtoa(ctx, JS_VALUE_GET_FLOAT64(val), 0, JS_DTOA_TOSTRING); + case JS_TAG_BIG_INT: + return js_bigint_to_string(ctx, val); + case JS_TAG_UNINITIALIZED: + return js_new_string8(ctx, "[uninitialized]"); + default: + return js_new_string8(ctx, "[unsupported type]"); + } +} + +JSValue JS_ToString(JSContext *ctx, JSValue val) +{ + return JS_ToStringInternal(ctx, val, FALSE); +} + +static JSValue JS_ToStringFree(JSContext *ctx, JSValue val) +{ + JSValue ret; + ret = JS_ToString(ctx, val); + JS_FreeValue(ctx, val); + return ret; +} + +static JSValue JS_ToLocaleStringFree(JSContext *ctx, JSValue val) +{ + if (JS_IsUndefined(val) || JS_IsNull(val)) + return JS_ToStringFree(ctx, val); + return JS_InvokeFree(ctx, val, JS_ATOM_toLocaleString, 0, NULL); +} + +JSValue JS_ToPropertyKey(JSContext *ctx, JSValue val) +{ + return JS_ToStringInternal(ctx, val, TRUE); +} + +static JSValue JS_ToStringCheckObject(JSContext *ctx, JSValue val) +{ + uint32_t tag = JS_VALUE_GET_TAG(val); + if (tag == JS_TAG_NULL || tag == JS_TAG_UNDEFINED) + return JS_ThrowTypeError(ctx, "null or undefined are forbidden"); + return JS_ToString(ctx, val); +} + +static JSValue JS_ToQuotedString(JSContext *ctx, JSValue val1) +{ + JSValue val; + JSString *p; + int i; + uint32_t c; + StringBuffer b_s, *b = &b_s; + char buf[16]; + + val = JS_ToStringCheckObject(ctx, val1); + if (JS_IsException(val)) + return val; + p = JS_VALUE_GET_STRING(val); + + if (string_buffer_init(ctx, b, p->len + 2)) + goto fail; + + if (string_buffer_putc8(b, '\"')) + goto fail; + for(i = 0; i < p->len; ) { + c = string_getc(p, &i); + switch(c) { + case '\t': + c = 't'; + goto quote; + case '\r': + c = 'r'; + goto quote; + case '\n': + c = 'n'; + goto quote; + case '\b': + c = 'b'; + goto quote; + case '\f': + c = 'f'; + goto quote; + case '\"': + case '\\': + quote: + if (string_buffer_putc8(b, '\\')) + goto fail; + if (string_buffer_putc8(b, c)) + goto fail; + break; + default: + if (c < 32 || is_surrogate(c)) { + snprintf(buf, sizeof(buf), "\\u%04x", c); + if (string_buffer_write8(b, (uint8_t*)buf, 6)) + goto fail; + } else { + if (string_buffer_putc(b, c)) + goto fail; + } + break; + } + } + if (string_buffer_putc8(b, '\"')) + goto fail; + JS_FreeValue(ctx, val); + return string_buffer_end(b); + fail: + JS_FreeValue(ctx, val); + string_buffer_free(b); + return JS_EXCEPTION; +} + +static __maybe_unused void JS_DumpObjectHeader(JSRuntime *rt) +{ + printf("%14s %4s %4s %14s %10s %s\n", + "ADDRESS", "REFS", "SHRF", "PROTO", "CLASS", "PROPS"); +} + +/* for debug only: dump an object without side effect */ +static __maybe_unused void JS_DumpObject(JSRuntime *rt, JSObject *p) +{ + uint32_t i; + char atom_buf[ATOM_GET_STR_BUF_SIZE]; + JSShape *sh; + JSShapeProperty *prs; + JSProperty *pr; + BOOL is_first = TRUE; + + /* XXX: should encode atoms with special characters */ + sh = p->shape; /* the shape can be NULL while freeing an object */ + printf("%14p %4d ", + (void *)p, + p->header.ref_count); + if (sh) { + printf("%3d%c %14p ", + sh->header.ref_count, + " *"[sh->is_hashed], + (void *)sh->proto); + } else { + printf("%3s %14s ", "-", "-"); + } + printf("%10s ", + JS_AtomGetStrRT(rt, atom_buf, sizeof(atom_buf), rt->class_array[p->class_id].class_name)); + if (p->is_exotic && p->fast_array) { + printf("[ "); + for(i = 0; i < p->u.array.count; i++) { + if (i != 0) + printf(", "); + switch (p->class_id) { + case JS_CLASS_ARRAY: + case JS_CLASS_ARGUMENTS: + JS_DumpValue(rt, p->u.array.u.values[i]); + break; + case JS_CLASS_UINT8C_ARRAY: + case JS_CLASS_INT8_ARRAY: + case JS_CLASS_UINT8_ARRAY: + case JS_CLASS_INT16_ARRAY: + case JS_CLASS_UINT16_ARRAY: + case JS_CLASS_INT32_ARRAY: + case JS_CLASS_UINT32_ARRAY: + case JS_CLASS_BIG_INT64_ARRAY: + case JS_CLASS_BIG_UINT64_ARRAY: + case JS_CLASS_FLOAT16_ARRAY: + case JS_CLASS_FLOAT32_ARRAY: + case JS_CLASS_FLOAT64_ARRAY: + { + int size = 1 << typed_array_size_log2(p->class_id); + const uint8_t *b = p->u.array.u.uint8_ptr + i * size; + while (size-- > 0) + printf("%02X", *b++); + } + break; + } + } + printf(" ] "); + } + + if (sh) { + printf("{ "); + for(i = 0, prs = get_shape_prop(sh); i < sh->prop_count; i++, prs++) { + if (prs->atom != JS_ATOM_NULL) { + pr = &p->prop[i]; + if (!is_first) + printf(", "); + printf("%s: ", + JS_AtomGetStrRT(rt, atom_buf, sizeof(atom_buf), prs->atom)); + if ((prs->flags & JS_PROP_TMASK) == JS_PROP_GETSET) { + printf("[getset %p %p]", (void *)pr->u.getset.getter, + (void *)pr->u.getset.setter); + } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF) { + printf("[varref %p]", (void *)pr->u.var_ref); + } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_AUTOINIT) { + printf("[autoinit %p %d %p]", + (void *)js_autoinit_get_realm(pr), + js_autoinit_get_id(pr), + (void *)pr->u.init.opaque); + } else { + JS_DumpValue(rt, pr->u.value); + } + is_first = FALSE; + } + } + printf(" }"); + } + + if (js_class_has_bytecode(p->class_id)) { + JSFunctionBytecode *b = p->u.func.function_bytecode; + JSVarRef **var_refs; + if (b->closure_var_count) { + var_refs = p->u.func.var_refs; + printf(" Closure:"); + for(i = 0; i < b->closure_var_count; i++) { + printf(" "); + JS_DumpValue(rt, var_refs[i]->value); + } + if (p->u.func.home_object) { + printf(" HomeObject: "); + JS_DumpValue(rt, JS_MKPTR(JS_TAG_OBJECT, p->u.func.home_object)); + } + } + } + printf("\n"); +} + +static __maybe_unused void JS_DumpGCObject(JSRuntime *rt, JSGCObjectHeader *p) +{ + if (p->gc_obj_type == JS_GC_OBJ_TYPE_JS_OBJECT) { + JS_DumpObject(rt, (JSObject *)p); + } else { + printf("%14p %4d ", + (void *)p, + p->ref_count); + switch(p->gc_obj_type) { + case JS_GC_OBJ_TYPE_FUNCTION_BYTECODE: + printf("[function bytecode]"); + break; + case JS_GC_OBJ_TYPE_SHAPE: + printf("[shape]"); + break; + case JS_GC_OBJ_TYPE_VAR_REF: + printf("[var_ref]"); + break; + case JS_GC_OBJ_TYPE_ASYNC_FUNCTION: + printf("[async_function]"); + break; + case JS_GC_OBJ_TYPE_JS_CONTEXT: + printf("[js_context]"); + break; + default: + printf("[unknown %d]", p->gc_obj_type); + break; + } + printf("\n"); + } +} + +static __maybe_unused void JS_DumpValue(JSRuntime *rt, JSValue val) +{ + uint32_t tag = JS_VALUE_GET_NORM_TAG(val); + const char *str; + + switch(tag) { + case JS_TAG_INT: + printf("%d", JS_VALUE_GET_INT(val)); + break; + case JS_TAG_BOOL: + if (JS_VALUE_GET_BOOL(val)) + str = "true"; + else + str = "false"; + goto print_str; + case JS_TAG_NULL: + str = "null"; + goto print_str; + case JS_TAG_EXCEPTION: + str = "exception"; + goto print_str; + case JS_TAG_UNINITIALIZED: + str = "uninitialized"; + goto print_str; + case JS_TAG_UNDEFINED: + str = "undefined"; + print_str: + printf("%s", str); + break; + case JS_TAG_FLOAT64: + printf("%.14g", JS_VALUE_GET_FLOAT64(val)); + break; + case JS_TAG_BIG_INT: + { + JSBigInt *p = JS_VALUE_GET_PTR(val); + char *str; + str = bf_ftoa(NULL, &p->num, 10, 0, + BF_RNDZ | BF_FTOA_FORMAT_FRAC); + printf("%sn", str); + bf_realloc(&rt->bf_ctx, str, 0); + } + break; + case JS_TAG_STRING: + { + JSString *p; + p = JS_VALUE_GET_STRING(val); + JS_DumpString(rt, p); + } + break; + case JS_TAG_FUNCTION_BYTECODE: + { + JSFunctionBytecode *b = JS_VALUE_GET_PTR(val); + char buf[ATOM_GET_STR_BUF_SIZE]; + if (b->func_name) { + printf("[bytecode %s]", JS_AtomGetStrRT(rt, buf, sizeof(buf), b->func_name)); + } else { + printf("[bytecode (anonymous)]"); + } + } + break; + case JS_TAG_OBJECT: + { + JSObject *p = JS_VALUE_GET_OBJ(val); + JSAtom atom = rt->class_array[p->class_id].class_name; + char atom_buf[ATOM_GET_STR_BUF_SIZE]; + printf("[%s %p]", + JS_AtomGetStrRT(rt, atom_buf, sizeof(atom_buf), atom), (void *)p); + } + break; + case JS_TAG_SYMBOL: + { + JSAtomStruct *p = JS_VALUE_GET_PTR(val); + char atom_buf[ATOM_GET_STR_BUF_SIZE]; + printf("Symbol(%s)", + JS_AtomGetStrRT(rt, atom_buf, sizeof(atom_buf), js_get_atom_index(rt, p))); + } + break; + case JS_TAG_MODULE: + printf("[module]"); + break; + default: + printf("[unknown tag %d]", tag); + break; + } +} + +/* return -1 if exception (proxy case) or TRUE/FALSE */ +int JS_IsArray(JSContext *ctx, JSValue val) +{ + JSObject *p; + if (JS_VALUE_GET_TAG(val) == JS_TAG_OBJECT) { + p = JS_VALUE_GET_OBJ(val); + if (unlikely(p->class_id == JS_CLASS_PROXY)) + return js_proxy_isArray(ctx, val); + else + return p->class_id == JS_CLASS_ARRAY; + } else { + return FALSE; + } +} + +static double js_math_pow(double a, double b) +{ + if (unlikely(!isfinite(b)) && fabs(a) == 1) { + /* not compatible with IEEE 754 */ + return JS_FLOAT64_NAN; + } else { + return pow(a, b); + } +} + +JSValue JS_NewBigInt64(JSContext *ctx, int64_t v) +{ + JSValue val; + bf_t *a; + val = JS_NewBigInt(ctx); + if (JS_IsException(val)) + return val; + a = JS_GetBigInt(val); + if (bf_set_si(a, v)) { + JS_FreeValue(ctx, val); + return JS_ThrowOutOfMemory(ctx); + } + return val; +} + +JSValue JS_NewBigUint64(JSContext *ctx, uint64_t v) +{ + JSValue val; + bf_t *a; + val = JS_NewBigInt(ctx); + if (JS_IsException(val)) + return val; + a = JS_GetBigInt(val); + if (bf_set_ui(a, v)) { + JS_FreeValue(ctx, val); + return JS_ThrowOutOfMemory(ctx); + } + + return val; +} + +/* if the returned bigint is allocated it is equal to + 'buf'. Otherwise it is a pointer to the bigint in 'val'. Return + NULL in case of error. */ +// TODO(bnoordhuis) Merge with JS_ToBigInt() +static bf_t *JS_ToBigInt1(JSContext *ctx, bf_t *buf, JSValue val) +{ + uint32_t tag; + bf_t *r; + JSBigInt *p; + + tag = JS_VALUE_GET_NORM_TAG(val); + switch(tag) { + case JS_TAG_INT: + case JS_TAG_BOOL: + case JS_TAG_NULL: + r = buf; + bf_init(ctx->bf_ctx, r); + if (bf_set_si(r, JS_VALUE_GET_INT(val))) + goto fail; + break; + case JS_TAG_FLOAT64: + r = buf; + bf_init(ctx->bf_ctx, r); + if (bf_set_float64(r, JS_VALUE_GET_FLOAT64(val))) { + fail: + bf_delete(r); + return NULL; + } + break; + case JS_TAG_BIG_INT: + p = JS_VALUE_GET_PTR(val); + r = &p->num; + break; + case JS_TAG_UNDEFINED: + default: + r = buf; + bf_init(ctx->bf_ctx, r); + bf_set_nan(r); + break; + } + return r; +} + +/* return NaN if bad bigint literal */ +static JSValue JS_StringToBigInt(JSContext *ctx, JSValue val) +{ + const char *str; + size_t len; + int flags; + + str = JS_ToCStringLen(ctx, &len, val); + JS_FreeValue(ctx, val); + if (!str) + return JS_EXCEPTION; + flags = ATOD_WANT_BIG_INT | + ATOD_TRIM_SPACES | ATOD_ACCEPT_EMPTY | + ATOD_ACCEPT_HEX_PREFIX | ATOD_ACCEPT_BIN_OCT | + ATOD_DECIMAL_AFTER_SIGN | ATOD_NO_TRAILING_CHARS; + val = js_atof(ctx, str, len, NULL, 10, flags); + JS_FreeCString(ctx, str); + return val; +} + +static JSValue JS_StringToBigIntErr(JSContext *ctx, JSValue val) +{ + val = JS_StringToBigInt(ctx, val); + if (JS_VALUE_IS_NAN(val)) + return JS_ThrowSyntaxError(ctx, "invalid BigInt literal"); + return val; +} + +/* if the returned bigint is allocated it is equal to + 'buf'. Otherwise it is a pointer to the bigint in 'val'. */ +static bf_t *JS_ToBigIntFree(JSContext *ctx, bf_t *buf, JSValue val) +{ + uint32_t tag; + bf_t *r; + JSBigInt *p; + + redo: + tag = JS_VALUE_GET_NORM_TAG(val); + switch(tag) { + case JS_TAG_INT: + case JS_TAG_NULL: + case JS_TAG_UNDEFINED: + case JS_TAG_FLOAT64: + goto fail; + case JS_TAG_BOOL: + r = buf; + bf_init(ctx->bf_ctx, r); + bf_set_si(r, JS_VALUE_GET_INT(val)); + break; + case JS_TAG_BIG_INT: + p = JS_VALUE_GET_PTR(val); + r = &p->num; + break; + case JS_TAG_STRING: + val = JS_StringToBigIntErr(ctx, val); + if (JS_IsException(val)) + return NULL; + goto redo; + case JS_TAG_OBJECT: + val = JS_ToPrimitiveFree(ctx, val, HINT_NUMBER); + if (JS_IsException(val)) + return NULL; + goto redo; + default: + fail: + JS_FreeValue(ctx, val); + JS_ThrowTypeError(ctx, "cannot convert to BigInt"); + return NULL; + } + return r; +} + +static bf_t *JS_ToBigInt(JSContext *ctx, bf_t *buf, JSValue val) +{ + return JS_ToBigIntFree(ctx, buf, js_dup(val)); +} + +static __maybe_unused JSValue JS_ToBigIntValueFree(JSContext *ctx, JSValue val) +{ + if (JS_VALUE_GET_TAG(val) == JS_TAG_BIG_INT) { + return val; + } else { + bf_t a_s, *a, *r; + int ret; + JSValue res; + + res = JS_NewBigInt(ctx); + if (JS_IsException(res)) + return JS_EXCEPTION; + a = JS_ToBigIntFree(ctx, &a_s, val); + if (!a) { + JS_FreeValue(ctx, res); + return JS_EXCEPTION; + } + r = JS_GetBigInt(res); + ret = bf_set(r, a); + JS_FreeBigInt(ctx, a, &a_s); + if (ret) { + JS_FreeValue(ctx, res); + return JS_ThrowOutOfMemory(ctx); + } + return JS_CompactBigInt(ctx, res); + } +} + +/* free the bf_t allocated by JS_ToBigInt */ +static void JS_FreeBigInt(JSContext *ctx, bf_t *a, bf_t *buf) +{ + if (a == buf) { + bf_delete(a); + } else { + JSBigInt *p = (JSBigInt *)((uint8_t *)a - offsetof(JSBigInt, num)); + JS_FreeValue(ctx, JS_MKPTR(JS_TAG_BIG_INT, p)); + } +} + +/* XXX: merge with JS_ToInt64Free with a specific flag */ +static int JS_ToBigInt64Free(JSContext *ctx, int64_t *pres, JSValue val) +{ + bf_t a_s, *a; + + a = JS_ToBigIntFree(ctx, &a_s, val); + if (!a) { + *pres = 0; + return -1; + } + bf_get_int64(pres, a, BF_GET_INT_MOD); + JS_FreeBigInt(ctx, a, &a_s); + return 0; +} + +int JS_ToBigInt64(JSContext *ctx, int64_t *pres, JSValue val) +{ + return JS_ToBigInt64Free(ctx, pres, js_dup(val)); +} + +int JS_ToBigUint64(JSContext *ctx, uint64_t *pres, JSValue val) +{ + return JS_ToBigInt64Free(ctx, (int64_t *)pres, js_dup(val)); +} + +static JSValue JS_NewBigInt(JSContext *ctx) +{ + JSBigInt *p; + p = js_malloc(ctx, sizeof(*p)); + if (!p) + return JS_EXCEPTION; + p->header.ref_count = 1; + bf_init(ctx->bf_ctx, &p->num); + return JS_MKPTR(JS_TAG_BIG_INT, p); +} + +static JSValue JS_CompactBigInt1(JSContext *ctx, JSValue val) +{ + if (JS_VALUE_GET_TAG(val) != JS_TAG_BIG_INT) + return val; /* fail safe */ + bf_t *a = JS_GetBigInt(val); + if (a->expn == BF_EXP_ZERO && a->sign) { + assert(((JSBigInt*)JS_VALUE_GET_PTR(val))->header.ref_count == 1); + a->sign = 0; + } + return val; +} + +/* Nnormalize the zero representation. Could also be used to convert the bigint + to a short bigint value. The reference count of the value must be + 1. Cannot fail */ +static JSValue JS_CompactBigInt(JSContext *ctx, JSValue val) +{ + return JS_CompactBigInt1(ctx, val); +} + +static JSValue throw_bf_exception(JSContext *ctx, int status) +{ + const char *str; + if (status & BF_ST_MEM_ERROR) + return JS_ThrowOutOfMemory(ctx); + if (status & BF_ST_DIVIDE_ZERO) { + str = "division by zero"; + } else if (status & BF_ST_INVALID_OP) { + str = "invalid operation"; + } else { + str = "integer overflow"; + } + return JS_ThrowRangeError(ctx, "%s", str); +} + +static int js_unary_arith_bigint(JSContext *ctx, + JSValue *pres, OPCodeEnum op, JSValue op1) +{ + bf_t a_s, *r, *a; + int ret, v; + JSValue res; + + if (op == OP_plus) { + JS_ThrowTypeError(ctx, "BigInt argument with unary +"); + JS_FreeValue(ctx, op1); + return -1; + } + res = JS_NewBigInt(ctx); + if (JS_IsException(res)) { + JS_FreeValue(ctx, op1); + return -1; + } + r = JS_GetBigInt(res); + a = JS_ToBigIntFree(ctx, &a_s, op1); // infallible, always a bigint + ret = 0; + switch(op) { + case OP_inc: + case OP_dec: + v = 2 * (op - OP_dec) - 1; + ret = bf_add_si(r, a, v, BF_PREC_INF, BF_RNDZ); + break; + case OP_plus: + ret = bf_set(r, a); + break; + case OP_neg: + ret = bf_set(r, a); + bf_neg(r); + break; + case OP_not: + ret = bf_add_si(r, a, 1, BF_PREC_INF, BF_RNDZ); + bf_neg(r); + break; + default: + abort(); + } + JS_FreeBigInt(ctx, a, &a_s); + if (unlikely(ret)) { + JS_FreeValue(ctx, res); + throw_bf_exception(ctx, ret); + return -1; + } + res = JS_CompactBigInt(ctx, res); + *pres = res; + return 0; +} + +static no_inline __exception int js_unary_arith_slow(JSContext *ctx, + JSValue *sp, + OPCodeEnum op) +{ + JSValue op1; + int v; + uint32_t tag; + + op1 = sp[-1]; + /* fast path for float64 */ + if (JS_TAG_IS_FLOAT64(JS_VALUE_GET_TAG(op1))) + goto handle_float64; + + op1 = JS_ToNumericFree(ctx, op1); + if (JS_IsException(op1)) + goto exception; + tag = JS_VALUE_GET_TAG(op1); + switch(tag) { + case JS_TAG_INT: + { + int64_t v64; + v64 = JS_VALUE_GET_INT(op1); + switch(op) { + case OP_inc: + case OP_dec: + v = 2 * (op - OP_dec) - 1; + v64 += v; + break; + case OP_plus: + break; + case OP_neg: + if (v64 == 0) { + sp[-1] = js_float64(-0.0); + return 0; + } else { + v64 = -v64; + } + break; + default: + abort(); + } + sp[-1] = js_int64(v64); + } + break; + case JS_TAG_BIG_INT: + if (js_unary_arith_bigint(ctx, sp - 1, op, op1)) + goto exception; + break; + default: + handle_float64: + { + double d = JS_VALUE_GET_FLOAT64(op1); + switch(op) { + case OP_inc: + case OP_dec: + v = 2 * (op - OP_dec) - 1; + d += v; + break; + case OP_plus: + break; + case OP_neg: + d = -d; + break; + default: + abort(); + } + sp[-1] = js_float64(d); + } + break; + } + return 0; + exception: + sp[-1] = JS_UNDEFINED; + return -1; +} + +static __exception int js_post_inc_slow(JSContext *ctx, + JSValue *sp, OPCodeEnum op) +{ + JSValue op1; + + /* XXX: allow custom operators */ + op1 = sp[-1]; + op1 = JS_ToNumericFree(ctx, op1); + if (JS_IsException(op1)) { + sp[-1] = JS_UNDEFINED; + return -1; + } + sp[-1] = op1; + sp[0] = js_dup(op1); + return js_unary_arith_slow(ctx, sp + 1, op - OP_post_dec + OP_dec); +} + +static no_inline int js_not_slow(JSContext *ctx, JSValue *sp) +{ + JSValue op1; + + op1 = JS_ToNumericFree(ctx, sp[-1]); + if (JS_IsException(op1)) + goto exception; + if (JS_VALUE_GET_TAG(op1) == JS_TAG_BIG_INT) { + if (js_unary_arith_bigint(ctx, sp - 1, OP_not, op1)) + goto exception; + } else { + int32_t v1; + if (unlikely(JS_ToInt32Free(ctx, &v1, op1))) + goto exception; + sp[-1] = js_int32(~v1); + } + return 0; + exception: + sp[-1] = JS_UNDEFINED; + return -1; +} + +static int js_binary_arith_bigint(JSContext *ctx, OPCodeEnum op, + JSValue *pres, JSValue op1, JSValue op2) +{ + bf_t a_s, b_s, *r, *a, *b; + int ret; + JSValue res; + + a = JS_ToBigIntFree(ctx, &a_s, op1); + if (!a) { + JS_FreeValue(ctx, op2); + return -1; + } + b = JS_ToBigIntFree(ctx, &b_s, op2); + if (!b) { + JS_FreeBigInt(ctx, a, &a_s); + return -1; + } + res = JS_NewBigInt(ctx); + if (JS_IsException(res)) { + JS_FreeBigInt(ctx, a, &a_s); + JS_FreeBigInt(ctx, b, &b_s); + return -1; + } + r = JS_GetBigInt(res); + ret = 0; + switch(op) { + case OP_add: + ret = bf_add(r, a, b, BF_PREC_INF, BF_RNDZ); + break; + case OP_sub: + ret = bf_sub(r, a, b, BF_PREC_INF, BF_RNDZ); + break; + case OP_mul: + ret = bf_mul(r, a, b, BF_PREC_INF, BF_RNDZ); + break; + case OP_div: + { + bf_t rem_s, *rem = &rem_s; + bf_init(ctx->bf_ctx, rem); + ret = bf_divrem(r, rem, a, b, BF_PREC_INF, BF_RNDZ, BF_RNDZ); + bf_delete(rem); + } + break; + case OP_mod: + ret = bf_rem(r, a, b, BF_PREC_INF, BF_RNDZ, + BF_RNDZ) & BF_ST_INVALID_OP; + break; + case OP_pow: + if (b->sign) { + ret = BF_ST_INVALID_OP; + } else { + ret = bf_pow(r, a, b, BF_PREC_INF, BF_RNDZ | BF_POW_JS_QUIRKS); + } + break; + + /* logical operations */ + case OP_shl: + case OP_sar: + { + slimb_t v2; +#if LIMB_BITS == 32 + bf_get_int32(&v2, b, 0); + if (v2 == INT32_MIN) + v2 = INT32_MIN + 1; +#else + bf_get_int64(&v2, b, 0); + if (v2 == INT64_MIN) + v2 = INT64_MIN + 1; +#endif + if (op == OP_sar) + v2 = -v2; + ret = bf_set(r, a); + ret |= bf_mul_2exp(r, v2, BF_PREC_INF, BF_RNDZ); + if (v2 < 0) { + ret |= bf_rint(r, BF_RNDD) & (BF_ST_OVERFLOW | BF_ST_MEM_ERROR); + } + } + break; + case OP_and: + ret = bf_logic_and(r, a, b); + break; + case OP_or: + ret = bf_logic_or(r, a, b); + break; + case OP_xor: + ret = bf_logic_xor(r, a, b); + break; + default: + abort(); + } + JS_FreeBigInt(ctx, a, &a_s); + JS_FreeBigInt(ctx, b, &b_s); + if (unlikely(ret)) { + JS_FreeValue(ctx, res); + throw_bf_exception(ctx, ret); + return -1; + } + *pres = JS_CompactBigInt(ctx, res); + return 0; +} + +static no_inline __exception int js_binary_arith_slow(JSContext *ctx, JSValue *sp, + OPCodeEnum op) +{ + JSValue op1, op2; + uint32_t tag1, tag2; + double d1, d2; + + op1 = sp[-2]; + op2 = sp[-1]; + tag1 = JS_VALUE_GET_NORM_TAG(op1); + tag2 = JS_VALUE_GET_NORM_TAG(op2); + /* fast path for float operations */ + if (tag1 == JS_TAG_FLOAT64 && tag2 == JS_TAG_FLOAT64) { + d1 = JS_VALUE_GET_FLOAT64(op1); + d2 = JS_VALUE_GET_FLOAT64(op2); + goto handle_float64; + } + + op1 = JS_ToNumericFree(ctx, op1); + if (JS_IsException(op1)) { + JS_FreeValue(ctx, op2); + goto exception; + } + op2 = JS_ToNumericFree(ctx, op2); + if (JS_IsException(op2)) { + JS_FreeValue(ctx, op1); + goto exception; + } + tag1 = JS_VALUE_GET_NORM_TAG(op1); + tag2 = JS_VALUE_GET_NORM_TAG(op2); + + if (tag1 == JS_TAG_INT && tag2 == JS_TAG_INT) { + int32_t v1, v2; + int64_t v; + v1 = JS_VALUE_GET_INT(op1); + v2 = JS_VALUE_GET_INT(op2); + switch(op) { + case OP_sub: + v = (int64_t)v1 - (int64_t)v2; + break; + case OP_mul: + v = (int64_t)v1 * (int64_t)v2; + if (v == 0 && (v1 | v2) < 0) { + sp[-2] = js_float64(-0.0); + return 0; + } + break; + case OP_div: + sp[-2] = js_float64((double)v1 / (double)v2); + return 0; + case OP_mod: + if (v1 < 0 || v2 <= 0) { + sp[-2] = js_number(fmod(v1, v2)); + return 0; + } else { + v = (int64_t)v1 % (int64_t)v2; + } + break; + case OP_pow: + sp[-2] = js_number(js_math_pow(v1, v2)); + return 0; + default: + abort(); + } + sp[-2] = js_int64(v); + } else if (tag1 == JS_TAG_BIG_INT || tag2 == JS_TAG_BIG_INT) { + if (js_binary_arith_bigint(ctx, op, sp - 2, op1, op2)) + goto exception; + } else { + double dr; + /* float64 result */ + if (JS_ToFloat64Free(ctx, &d1, op1)) { + JS_FreeValue(ctx, op2); + goto exception; + } + if (JS_ToFloat64Free(ctx, &d2, op2)) + goto exception; + handle_float64: + switch(op) { + case OP_sub: + dr = d1 - d2; + break; + case OP_mul: + dr = d1 * d2; + break; + case OP_div: + dr = d1 / d2; + break; + case OP_mod: + dr = fmod(d1, d2); + break; + case OP_pow: + dr = js_math_pow(d1, d2); + break; + default: + abort(); + } + sp[-2] = js_float64(dr); + } + return 0; + exception: + sp[-2] = JS_UNDEFINED; + sp[-1] = JS_UNDEFINED; + return -1; +} + +static no_inline __exception int js_add_slow(JSContext *ctx, JSValue *sp) +{ + JSValue op1, op2; + uint32_t tag1, tag2; + + op1 = sp[-2]; + op2 = sp[-1]; + + tag1 = JS_VALUE_GET_NORM_TAG(op1); + tag2 = JS_VALUE_GET_NORM_TAG(op2); + /* fast path for float64 */ + if (tag1 == JS_TAG_FLOAT64 && tag2 == JS_TAG_FLOAT64) { + double d1, d2; + d1 = JS_VALUE_GET_FLOAT64(op1); + d2 = JS_VALUE_GET_FLOAT64(op2); + sp[-2] = js_float64(d1 + d2); + return 0; + } + + if (tag1 == JS_TAG_OBJECT || tag2 == JS_TAG_OBJECT) { + op1 = JS_ToPrimitiveFree(ctx, op1, HINT_NONE); + if (JS_IsException(op1)) { + JS_FreeValue(ctx, op2); + goto exception; + } + + op2 = JS_ToPrimitiveFree(ctx, op2, HINT_NONE); + if (JS_IsException(op2)) { + JS_FreeValue(ctx, op1); + goto exception; + } + tag1 = JS_VALUE_GET_NORM_TAG(op1); + tag2 = JS_VALUE_GET_NORM_TAG(op2); + } + + if (tag1 == JS_TAG_STRING || tag2 == JS_TAG_STRING) { + sp[-2] = JS_ConcatString(ctx, op1, op2); + if (JS_IsException(sp[-2])) + goto exception; + return 0; + } + + op1 = JS_ToNumericFree(ctx, op1); + if (JS_IsException(op1)) { + JS_FreeValue(ctx, op2); + goto exception; + } + op2 = JS_ToNumericFree(ctx, op2); + if (JS_IsException(op2)) { + JS_FreeValue(ctx, op1); + goto exception; + } + tag1 = JS_VALUE_GET_NORM_TAG(op1); + tag2 = JS_VALUE_GET_NORM_TAG(op2); + + if (tag1 == JS_TAG_INT && tag2 == JS_TAG_INT) { + int32_t v1, v2; + int64_t v; + v1 = JS_VALUE_GET_INT(op1); + v2 = JS_VALUE_GET_INT(op2); + v = (int64_t)v1 + (int64_t)v2; + sp[-2] = js_int64(v); + } else if (tag1 == JS_TAG_BIG_INT || tag2 == JS_TAG_BIG_INT) { + if (js_binary_arith_bigint(ctx, OP_add, sp - 2, op1, op2)) + goto exception; + } else { + double d1, d2; + /* float64 result */ + if (JS_ToFloat64Free(ctx, &d1, op1)) { + JS_FreeValue(ctx, op2); + goto exception; + } + if (JS_ToFloat64Free(ctx, &d2, op2)) + goto exception; + sp[-2] = js_float64(d1 + d2); + } + return 0; + exception: + sp[-2] = JS_UNDEFINED; + sp[-1] = JS_UNDEFINED; + return -1; +} + +static no_inline __exception int js_binary_logic_slow(JSContext *ctx, + JSValue *sp, + OPCodeEnum op) +{ + JSValue op1, op2; + uint32_t tag1, tag2; + uint32_t v1, v2, r; + + op1 = sp[-2]; + op2 = sp[-1]; + tag1 = JS_VALUE_GET_NORM_TAG(op1); + tag2 = JS_VALUE_GET_NORM_TAG(op2); + + op1 = JS_ToNumericFree(ctx, op1); + if (JS_IsException(op1)) { + JS_FreeValue(ctx, op2); + goto exception; + } + op2 = JS_ToNumericFree(ctx, op2); + if (JS_IsException(op2)) { + JS_FreeValue(ctx, op1); + goto exception; + } + + tag1 = JS_VALUE_GET_TAG(op1); + tag2 = JS_VALUE_GET_TAG(op2); + if (tag1 == JS_TAG_BIG_INT || tag2 == JS_TAG_BIG_INT) { + if (tag1 != tag2) { + JS_FreeValue(ctx, op1); + JS_FreeValue(ctx, op2); + JS_ThrowTypeError(ctx, "both operands must be BigInt"); + goto exception; + } else if (js_binary_arith_bigint(ctx, op, sp - 2, op1, op2)) { + goto exception; + } + } else { + if (unlikely(JS_ToInt32Free(ctx, (int32_t *)&v1, op1))) { + JS_FreeValue(ctx, op2); + goto exception; + } + if (unlikely(JS_ToInt32Free(ctx, (int32_t *)&v2, op2))) + goto exception; + switch(op) { + case OP_shl: + r = v1 << (v2 & 0x1f); + break; + case OP_sar: + r = (int)v1 >> (v2 & 0x1f); + break; + case OP_and: + r = v1 & v2; + break; + case OP_or: + r = v1 | v2; + break; + case OP_xor: + r = v1 ^ v2; + break; + default: + abort(); + } + sp[-2] = js_int32(r); + } + return 0; + exception: + sp[-2] = JS_UNDEFINED; + sp[-1] = JS_UNDEFINED; + return -1; +} + +static int js_compare_bigint(JSContext *ctx, OPCodeEnum op, + JSValue op1, JSValue op2) +{ + bf_t a_s, b_s, *a, *b; + int res; + + a = JS_ToBigInt1(ctx, &a_s, op1); + if (!a) { + JS_FreeValue(ctx, op2); + return -1; + } + b = JS_ToBigInt1(ctx, &b_s, op2); + if (!b) { + if (a == &a_s) + bf_delete(a); + JS_FreeValue(ctx, op1); + return -1; + } + switch(op) { + case OP_lt: + res = bf_cmp_lt(a, b); /* if NaN return false */ + break; + case OP_lte: + res = bf_cmp_le(a, b); /* if NaN return false */ + break; + case OP_gt: + res = bf_cmp_lt(b, a); /* if NaN return false */ + break; + case OP_gte: + res = bf_cmp_le(b, a); /* if NaN return false */ + break; + case OP_eq: + res = bf_cmp_eq(a, b); /* if NaN return false */ + break; + default: + abort(); + } + if (a == &a_s) + bf_delete(a); + if (b == &b_s) + bf_delete(b); + JS_FreeValue(ctx, op1); + JS_FreeValue(ctx, op2); + return res; +} + +static no_inline int js_relational_slow(JSContext *ctx, JSValue *sp, + OPCodeEnum op) +{ + JSValue op1, op2; + int res; + uint32_t tag1, tag2; + + op1 = sp[-2]; + op2 = sp[-1]; + tag1 = JS_VALUE_GET_NORM_TAG(op1); + tag2 = JS_VALUE_GET_NORM_TAG(op2); + + op1 = JS_ToPrimitiveFree(ctx, op1, HINT_NUMBER); + if (JS_IsException(op1)) { + JS_FreeValue(ctx, op2); + goto exception; + } + op2 = JS_ToPrimitiveFree(ctx, op2, HINT_NUMBER); + if (JS_IsException(op2)) { + JS_FreeValue(ctx, op1); + goto exception; + } + tag1 = JS_VALUE_GET_NORM_TAG(op1); + tag2 = JS_VALUE_GET_NORM_TAG(op2); + + if (tag1 == JS_TAG_STRING && tag2 == JS_TAG_STRING) { + JSString *p1, *p2; + p1 = JS_VALUE_GET_STRING(op1); + p2 = JS_VALUE_GET_STRING(op2); + res = js_string_compare(ctx, p1, p2); + switch(op) { + case OP_lt: + res = (res < 0); + break; + case OP_lte: + res = (res <= 0); + break; + case OP_gt: + res = (res > 0); + break; + default: + case OP_gte: + res = (res >= 0); + break; + } + JS_FreeValue(ctx, op1); + JS_FreeValue(ctx, op2); + } else if ((tag1 <= JS_TAG_NULL || tag1 == JS_TAG_FLOAT64) && + (tag2 <= JS_TAG_NULL || tag2 == JS_TAG_FLOAT64)) { + /* fast path for float64/int */ + goto float64_compare; + } else { + if (((tag1 == JS_TAG_BIG_INT && tag2 == JS_TAG_STRING) || + (tag2 == JS_TAG_BIG_INT && tag1 == JS_TAG_STRING))) { + if (tag1 == JS_TAG_STRING) { + op1 = JS_StringToBigInt(ctx, op1); + if (JS_VALUE_GET_TAG(op1) != JS_TAG_BIG_INT) + goto invalid_bigint_string; + } + if (tag2 == JS_TAG_STRING) { + op2 = JS_StringToBigInt(ctx, op2); + if (JS_VALUE_GET_TAG(op2) != JS_TAG_BIG_INT) { + invalid_bigint_string: + JS_FreeValue(ctx, op1); + JS_FreeValue(ctx, op2); + res = FALSE; + goto done; + } + } + } else { + op1 = JS_ToNumericFree(ctx, op1); + if (JS_IsException(op1)) { + JS_FreeValue(ctx, op2); + goto exception; + } + op2 = JS_ToNumericFree(ctx, op2); + if (JS_IsException(op2)) { + JS_FreeValue(ctx, op1); + goto exception; + } + } + + tag1 = JS_VALUE_GET_NORM_TAG(op1); + tag2 = JS_VALUE_GET_NORM_TAG(op2); + + if (tag1 == JS_TAG_BIG_INT || tag2 == JS_TAG_BIG_INT) { + res = js_compare_bigint(ctx, op, op1, op2); + if (res < 0) + goto exception; + } else { + double d1, d2; + + float64_compare: + /* can use floating point comparison */ + if (tag1 == JS_TAG_FLOAT64) { + d1 = JS_VALUE_GET_FLOAT64(op1); + } else { + d1 = JS_VALUE_GET_INT(op1); + } + if (tag2 == JS_TAG_FLOAT64) { + d2 = JS_VALUE_GET_FLOAT64(op2); + } else { + d2 = JS_VALUE_GET_INT(op2); + } + switch(op) { + case OP_lt: + res = (d1 < d2); /* if NaN return false */ + break; + case OP_lte: + res = (d1 <= d2); /* if NaN return false */ + break; + case OP_gt: + res = (d1 > d2); /* if NaN return false */ + break; + default: + case OP_gte: + res = (d1 >= d2); /* if NaN return false */ + break; + } + } + } + done: + sp[-2] = js_bool(res); + return 0; + exception: + sp[-2] = JS_UNDEFINED; + sp[-1] = JS_UNDEFINED; + return -1; +} + +static BOOL tag_is_number(uint32_t tag) +{ + return (tag == JS_TAG_INT || tag == JS_TAG_BIG_INT || + tag == JS_TAG_FLOAT64); +} + +static no_inline __exception int js_eq_slow(JSContext *ctx, JSValue *sp, + BOOL is_neq) +{ + JSValue op1, op2; + int res; + uint32_t tag1, tag2; + + op1 = sp[-2]; + op2 = sp[-1]; + redo: + tag1 = JS_VALUE_GET_NORM_TAG(op1); + tag2 = JS_VALUE_GET_NORM_TAG(op2); + if (tag_is_number(tag1) && tag_is_number(tag2)) { + if (tag1 == JS_TAG_INT && tag2 == JS_TAG_INT) { + res = JS_VALUE_GET_INT(op1) == JS_VALUE_GET_INT(op2); + } else if ((tag1 == JS_TAG_FLOAT64 && + (tag2 == JS_TAG_INT || tag2 == JS_TAG_FLOAT64)) || + (tag2 == JS_TAG_FLOAT64 && + (tag1 == JS_TAG_INT || tag1 == JS_TAG_FLOAT64))) { + double d1, d2; + if (tag1 == JS_TAG_FLOAT64) { + d1 = JS_VALUE_GET_FLOAT64(op1); + } else { + d1 = JS_VALUE_GET_INT(op1); + } + if (tag2 == JS_TAG_FLOAT64) { + d2 = JS_VALUE_GET_FLOAT64(op2); + } else { + d2 = JS_VALUE_GET_INT(op2); + } + res = (d1 == d2); + } else { + res = js_compare_bigint(ctx, OP_eq, op1, op2); + if (res < 0) + goto exception; + } + } else if (tag1 == tag2) { + res = js_strict_eq2(ctx, op1, op2, JS_EQ_STRICT); + } else if ((tag1 == JS_TAG_NULL && tag2 == JS_TAG_UNDEFINED) || + (tag2 == JS_TAG_NULL && tag1 == JS_TAG_UNDEFINED)) { + res = TRUE; + } else if ((tag1 == JS_TAG_STRING && tag_is_number(tag2)) || + (tag2 == JS_TAG_STRING && tag_is_number(tag1))) { + + if ((tag1 == JS_TAG_BIG_INT || tag2 == JS_TAG_BIG_INT)) { + if (tag1 == JS_TAG_STRING) { + op1 = JS_StringToBigInt(ctx, op1); + if (JS_VALUE_GET_TAG(op1) != JS_TAG_BIG_INT) + goto invalid_bigint_string; + } + if (tag2 == JS_TAG_STRING) { + op2 = JS_StringToBigInt(ctx, op2); + if (JS_VALUE_GET_TAG(op2) != JS_TAG_BIG_INT) { + invalid_bigint_string: + JS_FreeValue(ctx, op1); + JS_FreeValue(ctx, op2); + res = FALSE; + goto done; + } + } + } else { + op1 = JS_ToNumericFree(ctx, op1); + if (JS_IsException(op1)) { + JS_FreeValue(ctx, op2); + goto exception; + } + op2 = JS_ToNumericFree(ctx, op2); + if (JS_IsException(op2)) { + JS_FreeValue(ctx, op1); + goto exception; + } + } + res = js_strict_eq(ctx, op1, op2); + } else if (tag1 == JS_TAG_BOOL) { + op1 = js_int32(JS_VALUE_GET_INT(op1)); + goto redo; + } else if (tag2 == JS_TAG_BOOL) { + op2 = js_int32(JS_VALUE_GET_INT(op2)); + goto redo; + } else if ((tag1 == JS_TAG_OBJECT && + (tag_is_number(tag2) || tag2 == JS_TAG_STRING || tag2 == JS_TAG_SYMBOL)) || + (tag2 == JS_TAG_OBJECT && + (tag_is_number(tag1) || tag1 == JS_TAG_STRING || tag1 == JS_TAG_SYMBOL))) { + op1 = JS_ToPrimitiveFree(ctx, op1, HINT_NONE); + if (JS_IsException(op1)) { + JS_FreeValue(ctx, op2); + goto exception; + } + op2 = JS_ToPrimitiveFree(ctx, op2, HINT_NONE); + if (JS_IsException(op2)) { + JS_FreeValue(ctx, op1); + goto exception; + } + goto redo; + } else { + /* IsHTMLDDA object is equivalent to undefined for '==' and '!=' */ + if ((JS_IsHTMLDDA(ctx, op1) && + (tag2 == JS_TAG_NULL || tag2 == JS_TAG_UNDEFINED)) || + (JS_IsHTMLDDA(ctx, op2) && + (tag1 == JS_TAG_NULL || tag1 == JS_TAG_UNDEFINED))) { + res = TRUE; + } else { + res = FALSE; + } + JS_FreeValue(ctx, op1); + JS_FreeValue(ctx, op2); + } + done: + sp[-2] = js_bool(res ^ is_neq); + return 0; + exception: + sp[-2] = JS_UNDEFINED; + sp[-1] = JS_UNDEFINED; + return -1; +} + +static no_inline int js_shr_slow(JSContext *ctx, JSValue *sp) +{ + JSValue op1, op2; + uint32_t v1, v2, r; + + op1 = sp[-2]; + op2 = sp[-1]; + op1 = JS_ToNumericFree(ctx, op1); + if (JS_IsException(op1)) { + JS_FreeValue(ctx, op2); + goto exception; + } + op2 = JS_ToNumericFree(ctx, op2); + if (JS_IsException(op2)) { + JS_FreeValue(ctx, op1); + goto exception; + } + + if ((JS_VALUE_GET_TAG(op1) == JS_TAG_BIG_INT || + JS_VALUE_GET_TAG(op2) == JS_TAG_BIG_INT)) { + JS_ThrowTypeError(ctx, "BigInt operands are forbidden for >>>"); + JS_FreeValue(ctx, op1); + JS_FreeValue(ctx, op2); + goto exception; + } + /* cannot give an exception */ + JS_ToUint32Free(ctx, &v1, op1); + JS_ToUint32Free(ctx, &v2, op2); + r = v1 >> (v2 & 0x1f); + sp[-2] = js_uint32(r); + return 0; + exception: + sp[-2] = JS_UNDEFINED; + sp[-1] = JS_UNDEFINED; + return -1; +} + +/* XXX: Should take JSValue arguments */ +static BOOL js_strict_eq2(JSContext *ctx, JSValue op1, JSValue op2, + JSStrictEqModeEnum eq_mode) +{ + BOOL res; + int tag1, tag2; + double d1, d2; + + tag1 = JS_VALUE_GET_NORM_TAG(op1); + tag2 = JS_VALUE_GET_NORM_TAG(op2); + switch(tag1) { + case JS_TAG_BOOL: + if (tag1 != tag2) { + res = FALSE; + } else { + res = JS_VALUE_GET_INT(op1) == JS_VALUE_GET_INT(op2); + goto done_no_free; + } + break; + case JS_TAG_NULL: + case JS_TAG_UNDEFINED: + res = (tag1 == tag2); + break; + case JS_TAG_STRING: + { + JSString *p1, *p2; + if (tag1 != tag2) { + res = FALSE; + } else { + p1 = JS_VALUE_GET_STRING(op1); + p2 = JS_VALUE_GET_STRING(op2); + res = (js_string_compare(ctx, p1, p2) == 0); + } + } + break; + case JS_TAG_SYMBOL: + { + JSAtomStruct *p1, *p2; + if (tag1 != tag2) { + res = FALSE; + } else { + p1 = JS_VALUE_GET_PTR(op1); + p2 = JS_VALUE_GET_PTR(op2); + res = (p1 == p2); + } + } + break; + case JS_TAG_OBJECT: + if (tag1 != tag2) + res = FALSE; + else + res = JS_VALUE_GET_OBJ(op1) == JS_VALUE_GET_OBJ(op2); + break; + case JS_TAG_INT: + d1 = JS_VALUE_GET_INT(op1); + if (tag2 == JS_TAG_INT) { + d2 = JS_VALUE_GET_INT(op2); + goto number_test; + } else if (tag2 == JS_TAG_FLOAT64) { + d2 = JS_VALUE_GET_FLOAT64(op2); + goto number_test; + } else { + res = FALSE; + } + break; + case JS_TAG_FLOAT64: + d1 = JS_VALUE_GET_FLOAT64(op1); + if (tag2 == JS_TAG_FLOAT64) { + d2 = JS_VALUE_GET_FLOAT64(op2); + } else if (tag2 == JS_TAG_INT) { + d2 = JS_VALUE_GET_INT(op2); + } else { + res = FALSE; + break; + } + number_test: + if (unlikely(eq_mode >= JS_EQ_SAME_VALUE)) { + JSFloat64Union u1, u2; + /* NaN is not always normalized, so this test is necessary */ + if (isnan(d1) || isnan(d2)) { + res = isnan(d1) == isnan(d2); + } else if (eq_mode == JS_EQ_SAME_VALUE_ZERO) { + res = (d1 == d2); /* +0 == -0 */ + } else { + u1.d = d1; + u2.d = d2; + res = (u1.u64 == u2.u64); /* +0 != -0 */ + } + } else { + res = (d1 == d2); /* if NaN return false and +0 == -0 */ + } + goto done_no_free; + case JS_TAG_BIG_INT: + { + bf_t a_s, *a, b_s, *b; + if (tag1 != tag2) { + res = FALSE; + break; + } + a = JS_ToBigInt1(ctx, &a_s, op1); + b = JS_ToBigInt1(ctx, &b_s, op2); + res = bf_cmp_eq(a, b); + if (a == &a_s) + bf_delete(a); + if (b == &b_s) + bf_delete(b); + } + break; + default: + res = FALSE; + break; + } + JS_FreeValue(ctx, op1); + JS_FreeValue(ctx, op2); + done_no_free: + return res; +} + +static BOOL js_strict_eq(JSContext *ctx, JSValue op1, JSValue op2) +{ + return js_strict_eq2(ctx, op1, op2, JS_EQ_STRICT); +} + +static BOOL js_same_value(JSContext *ctx, JSValue op1, JSValue op2) +{ + return js_strict_eq2(ctx, js_dup(op1), js_dup(op2), JS_EQ_SAME_VALUE); +} + +static BOOL js_same_value_zero(JSContext *ctx, JSValue op1, JSValue op2) +{ + return js_strict_eq2(ctx, js_dup(op1), js_dup(op2), JS_EQ_SAME_VALUE_ZERO); +} + +static no_inline int js_strict_eq_slow(JSContext *ctx, JSValue *sp, + BOOL is_neq) +{ + BOOL res; + res = js_strict_eq(ctx, sp[-2], sp[-1]); + sp[-2] = js_bool(res ^ is_neq); + return 0; +} + +static __exception int js_operator_in(JSContext *ctx, JSValue *sp) +{ + JSValue op1, op2; + JSAtom atom; + int ret; + + op1 = sp[-2]; + op2 = sp[-1]; + + if (JS_VALUE_GET_TAG(op2) != JS_TAG_OBJECT) { + JS_ThrowTypeError(ctx, "invalid 'in' operand"); + return -1; + } + atom = JS_ValueToAtom(ctx, op1); + if (unlikely(atom == JS_ATOM_NULL)) + return -1; + ret = JS_HasProperty(ctx, op2, atom); + JS_FreeAtom(ctx, atom); + if (ret < 0) + return -1; + JS_FreeValue(ctx, op1); + JS_FreeValue(ctx, op2); + sp[-2] = js_bool(ret); + return 0; +} + +static __exception int js_operator_private_in(JSContext *ctx, JSValue *sp) +{ + JSValue op1, op2; + int ret; + op1 = sp[-2]; /* object */ + op2 = sp[-1]; /* field name or method function */ + if (JS_VALUE_GET_TAG(op1) != JS_TAG_OBJECT) { + JS_ThrowTypeError(ctx, "invalid 'in' operand"); + return -1; + } + if (JS_IsObject(op2)) { + /* method: use the brand */ + ret = JS_CheckBrand(ctx, op1, op2); + if (ret < 0) + return -1; + } else { + JSAtom atom; + JSObject *p; + JSShapeProperty *prs; + JSProperty *pr; + /* field */ + atom = JS_ValueToAtom(ctx, op2); + if (unlikely(atom == JS_ATOM_NULL)) + return -1; + p = JS_VALUE_GET_OBJ(op1); + prs = find_own_property(&pr, p, atom); + JS_FreeAtom(ctx, atom); + ret = (prs != NULL); + } + JS_FreeValue(ctx, op1); + JS_FreeValue(ctx, op2); + sp[-2] = JS_NewBool(ctx, ret); + return 0; +} + +static __exception int js_has_unscopable(JSContext *ctx, JSValue obj, + JSAtom atom) +{ + JSValue arr, val; + int ret; + + arr = JS_GetProperty(ctx, obj, JS_ATOM_Symbol_unscopables); + if (JS_IsException(arr)) + return -1; + ret = 0; + if (JS_IsObject(arr)) { + val = JS_GetProperty(ctx, arr, atom); + ret = JS_ToBoolFree(ctx, val); + } + JS_FreeValue(ctx, arr); + return ret; +} + +static __exception int js_operator_instanceof(JSContext *ctx, JSValue *sp) +{ + JSValue op1, op2; + BOOL ret; + + op1 = sp[-2]; + op2 = sp[-1]; + ret = JS_IsInstanceOf(ctx, op1, op2); + if (ret < 0) + return ret; + JS_FreeValue(ctx, op1); + JS_FreeValue(ctx, op2); + sp[-2] = js_bool(ret); + return 0; +} + +static __exception int js_operator_typeof(JSContext *ctx, JSValue op1) +{ + JSAtom atom; + uint32_t tag; + + tag = JS_VALUE_GET_NORM_TAG(op1); + switch(tag) { + case JS_TAG_BIG_INT: + atom = JS_ATOM_bigint; + break; + case JS_TAG_INT: + case JS_TAG_FLOAT64: + atom = JS_ATOM_number; + break; + case JS_TAG_UNDEFINED: + atom = JS_ATOM_undefined; + break; + case JS_TAG_BOOL: + atom = JS_ATOM_boolean; + break; + case JS_TAG_STRING: + atom = JS_ATOM_string; + break; + case JS_TAG_OBJECT: + { + JSObject *p; + p = JS_VALUE_GET_OBJ(op1); + if (unlikely(p->is_HTMLDDA)) + atom = JS_ATOM_undefined; + else if (JS_IsFunction(ctx, op1)) + atom = JS_ATOM_function; + else + goto obj_type; + } + break; + case JS_TAG_NULL: + obj_type: + atom = JS_ATOM_object; + break; + case JS_TAG_SYMBOL: + atom = JS_ATOM_symbol; + break; + default: + atom = JS_ATOM_unknown; + break; + } + return atom; +} + +static __exception int js_operator_delete(JSContext *ctx, JSValue *sp) +{ + JSValue op1, op2; + JSAtom atom; + int ret; + + op1 = sp[-2]; + op2 = sp[-1]; + atom = JS_ValueToAtom(ctx, op2); + if (unlikely(atom == JS_ATOM_NULL)) + return -1; + ret = JS_DeleteProperty(ctx, op1, atom, JS_PROP_THROW_STRICT); + JS_FreeAtom(ctx, atom); + if (unlikely(ret < 0)) + return -1; + JS_FreeValue(ctx, op1); + JS_FreeValue(ctx, op2); + sp[-2] = js_bool(ret); + return 0; +} + +static JSValue js_throw_type_error(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + return JS_ThrowTypeError(ctx, "invalid property access"); +} + +/* XXX: not 100% compatible, but mozilla seems to use a similar + implementation to ensure that caller in non strict mode does not + throw (ES5 compatibility) */ +static JSValue js_function_proto_caller(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + JSFunctionBytecode *b = JS_GetFunctionBytecode(this_val); + if (!b || b->is_strict_mode || !b->has_prototype) { + return js_throw_type_error(ctx, this_val, 0, NULL); + } + return JS_UNDEFINED; +} + +static JSValue js_function_proto_fileName(JSContext *ctx, + JSValue this_val) +{ + JSFunctionBytecode *b = JS_GetFunctionBytecode(this_val); + if (b) { + return JS_AtomToString(ctx, b->filename); + } + return JS_UNDEFINED; +} + +static JSValue js_function_proto_int32(JSContext *ctx, + JSValue this_val, + int magic) +{ + JSFunctionBytecode *b = JS_GetFunctionBytecode(this_val); + if (b) { + int *field = (int *) ((char *)b + magic); + return js_int32(*field); + } + return JS_UNDEFINED; +} + +static int js_arguments_define_own_property(JSContext *ctx, + JSValue this_obj, + JSAtom prop, JSValue val, + JSValue getter, JSValue setter, int flags) +{ + JSObject *p; + uint32_t idx; + p = JS_VALUE_GET_OBJ(this_obj); + /* convert to normal array when redefining an existing numeric field */ + if (p->fast_array && JS_AtomIsArrayIndex(ctx, &idx, prop) && + idx < p->u.array.count) { + if (convert_fast_array_to_array(ctx, p)) + return -1; + } + /* run the default define own property */ + return JS_DefineProperty(ctx, this_obj, prop, val, getter, setter, + flags | JS_PROP_NO_EXOTIC); +} + +static const JSClassExoticMethods js_arguments_exotic_methods = { + .define_own_property = js_arguments_define_own_property, +}; + +static JSValue js_build_arguments(JSContext *ctx, int argc, JSValue *argv) +{ + JSValue val, *tab; + JSProperty *pr; + JSObject *p; + int i; + + val = JS_NewObjectProtoClass(ctx, ctx->class_proto[JS_CLASS_OBJECT], + JS_CLASS_ARGUMENTS); + if (JS_IsException(val)) + return val; + p = JS_VALUE_GET_OBJ(val); + + /* add the length field (cannot fail) */ + pr = add_property(ctx, p, JS_ATOM_length, + JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE); + pr->u.value = js_int32(argc); + + /* initialize the fast array part */ + tab = NULL; + if (argc > 0) { + tab = js_malloc(ctx, sizeof(tab[0]) * argc); + if (!tab) { + JS_FreeValue(ctx, val); + return JS_EXCEPTION; + } + for(i = 0; i < argc; i++) { + tab[i] = js_dup(argv[i]); + } + } + p->u.array.u.values = tab; + p->u.array.count = argc; + + JS_DefinePropertyValue(ctx, val, JS_ATOM_Symbol_iterator, + js_dup(ctx->array_proto_values), + JS_PROP_CONFIGURABLE | JS_PROP_WRITABLE); + /* add callee property to throw a TypeError in strict mode */ + JS_DefineProperty(ctx, val, JS_ATOM_callee, JS_UNDEFINED, + ctx->throw_type_error, ctx->throw_type_error, + JS_PROP_HAS_GET | JS_PROP_HAS_SET); + return val; +} + +#define GLOBAL_VAR_OFFSET 0x40000000 +#define ARGUMENT_VAR_OFFSET 0x20000000 + +/* legacy arguments object: add references to the function arguments */ +static JSValue js_build_mapped_arguments(JSContext *ctx, int argc, + JSValue *argv, + JSStackFrame *sf, int arg_count) +{ + JSValue val; + JSProperty *pr; + JSObject *p; + int i; + + val = JS_NewObjectProtoClass(ctx, ctx->class_proto[JS_CLASS_OBJECT], + JS_CLASS_MAPPED_ARGUMENTS); + if (JS_IsException(val)) + return val; + p = JS_VALUE_GET_OBJ(val); + + /* add the length field (cannot fail) */ + pr = add_property(ctx, p, JS_ATOM_length, + JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE); + pr->u.value = js_int32(argc); + + for(i = 0; i < arg_count; i++) { + JSVarRef *var_ref; + var_ref = get_var_ref(ctx, sf, i, TRUE); + if (!var_ref) + goto fail; + pr = add_property(ctx, p, __JS_AtomFromUInt32(i), JS_PROP_C_W_E | JS_PROP_VARREF); + if (!pr) { + free_var_ref(ctx->rt, var_ref); + goto fail; + } + pr->u.var_ref = var_ref; + } + + /* the arguments not mapped to the arguments of the function can + be normal properties */ + for(i = arg_count; i < argc; i++) { + if (JS_DefinePropertyValueUint32(ctx, val, i, + js_dup(argv[i]), + JS_PROP_C_W_E) < 0) + goto fail; + } + + JS_DefinePropertyValue(ctx, val, JS_ATOM_Symbol_iterator, + js_dup(ctx->array_proto_values), + JS_PROP_CONFIGURABLE | JS_PROP_WRITABLE); + /* callee returns this function in non strict mode */ + JS_DefinePropertyValue(ctx, val, JS_ATOM_callee, + js_dup(ctx->rt->current_stack_frame->cur_func), + JS_PROP_CONFIGURABLE | JS_PROP_WRITABLE); + return val; + fail: + JS_FreeValue(ctx, val); + return JS_EXCEPTION; +} + +static JSValue js_build_rest(JSContext *ctx, int first, int argc, JSValue *argv) +{ + JSValue val; + int i, ret; + + val = JS_NewArray(ctx); + if (JS_IsException(val)) + return val; + for (i = first; i < argc; i++) { + ret = JS_DefinePropertyValueUint32(ctx, val, i - first, + js_dup(argv[i]), + JS_PROP_C_W_E); + if (ret < 0) { + JS_FreeValue(ctx, val); + return JS_EXCEPTION; + } + } + return val; +} + +static JSValue build_for_in_iterator(JSContext *ctx, JSValue obj) +{ + JSObject *p; + JSPropertyEnum *tab_atom; + int i; + JSValue enum_obj, obj1; + JSForInIterator *it; + uint32_t tag, tab_atom_count; + + tag = JS_VALUE_GET_TAG(obj); + if (tag != JS_TAG_OBJECT && tag != JS_TAG_NULL && tag != JS_TAG_UNDEFINED) { + obj = JS_ToObjectFree(ctx, obj); + } + + it = js_malloc(ctx, sizeof(*it)); + if (!it) { + JS_FreeValue(ctx, obj); + return JS_EXCEPTION; + } + enum_obj = JS_NewObjectProtoClass(ctx, JS_NULL, JS_CLASS_FOR_IN_ITERATOR); + if (JS_IsException(enum_obj)) { + js_free(ctx, it); + JS_FreeValue(ctx, obj); + return JS_EXCEPTION; + } + it->is_array = FALSE; + it->obj = obj; + it->idx = 0; + p = JS_VALUE_GET_OBJ(enum_obj); + p->u.for_in_iterator = it; + + if (tag == JS_TAG_NULL || tag == JS_TAG_UNDEFINED) + return enum_obj; + + /* fast path: assume no enumerable properties in the prototype chain */ + obj1 = js_dup(obj); + for(;;) { + obj1 = JS_GetPrototypeFree(ctx, obj1); + if (JS_IsNull(obj1)) + break; + if (JS_IsException(obj1)) + goto fail; + if (JS_GetOwnPropertyNamesInternal(ctx, &tab_atom, &tab_atom_count, + JS_VALUE_GET_OBJ(obj1), + JS_GPN_STRING_MASK | JS_GPN_ENUM_ONLY)) { + JS_FreeValue(ctx, obj1); + goto fail; + } + js_free_prop_enum(ctx, tab_atom, tab_atom_count); + if (tab_atom_count != 0) { + JS_FreeValue(ctx, obj1); + goto slow_path; + } + /* must check for timeout to avoid infinite loop */ + if (js_poll_interrupts(ctx)) { + JS_FreeValue(ctx, obj1); + goto fail; + } + } + + p = JS_VALUE_GET_OBJ(obj); + + if (p->fast_array) { + JSShape *sh; + JSShapeProperty *prs; + /* check that there are no enumerable normal fields */ + sh = p->shape; + for(i = 0, prs = get_shape_prop(sh); i < sh->prop_count; i++, prs++) { + if (prs->flags & JS_PROP_ENUMERABLE) + goto normal_case; + } + /* for fast arrays, we only store the number of elements */ + it->is_array = TRUE; + it->array_length = p->u.array.count; + } else { + normal_case: + if (JS_GetOwnPropertyNamesInternal(ctx, &tab_atom, &tab_atom_count, p, + JS_GPN_STRING_MASK | JS_GPN_ENUM_ONLY)) + goto fail; + for(i = 0; i < tab_atom_count; i++) { + JS_SetPropertyInternal(ctx, enum_obj, tab_atom[i].atom, JS_NULL, 0); + } + js_free_prop_enum(ctx, tab_atom, tab_atom_count); + } + return enum_obj; + + slow_path: + /* non enumerable properties hide the enumerables ones in the + prototype chain */ + obj1 = js_dup(obj); + for(;;) { + if (JS_GetOwnPropertyNamesInternal(ctx, &tab_atom, &tab_atom_count, + JS_VALUE_GET_OBJ(obj1), + JS_GPN_STRING_MASK | JS_GPN_SET_ENUM)) { + JS_FreeValue(ctx, obj1); + goto fail; + } + for(i = 0; i < tab_atom_count; i++) { + JS_DefinePropertyValue(ctx, enum_obj, tab_atom[i].atom, JS_NULL, + (tab_atom[i].is_enumerable ? + JS_PROP_ENUMERABLE : 0)); + } + js_free_prop_enum(ctx, tab_atom, tab_atom_count); + obj1 = JS_GetPrototypeFree(ctx, obj1); + if (JS_IsNull(obj1)) + break; + if (JS_IsException(obj1)) + goto fail; + /* must check for timeout to avoid infinite loop */ + if (js_poll_interrupts(ctx)) { + JS_FreeValue(ctx, obj1); + goto fail; + } + } + return enum_obj; + + fail: + JS_FreeValue(ctx, enum_obj); + return JS_EXCEPTION; +} + +/* obj -> enum_obj */ +static __exception int js_for_in_start(JSContext *ctx, JSValue *sp) +{ + sp[-1] = build_for_in_iterator(ctx, sp[-1]); + if (JS_IsException(sp[-1])) + return -1; + return 0; +} + +/* enum_obj -> enum_obj value done */ +static __exception int js_for_in_next(JSContext *ctx, JSValue *sp) +{ + JSValue enum_obj; + JSObject *p; + JSAtom prop; + JSForInIterator *it; + int ret; + + enum_obj = sp[-1]; + /* fail safe */ + if (JS_VALUE_GET_TAG(enum_obj) != JS_TAG_OBJECT) + goto done; + p = JS_VALUE_GET_OBJ(enum_obj); + if (p->class_id != JS_CLASS_FOR_IN_ITERATOR) + goto done; + it = p->u.for_in_iterator; + + for(;;) { + if (it->is_array) { + if (it->idx >= it->array_length) + goto done; + prop = __JS_AtomFromUInt32(it->idx); + it->idx++; + } else { + JSShape *sh = p->shape; + JSShapeProperty *prs; + if (it->idx >= sh->prop_count) + goto done; + prs = get_shape_prop(sh) + it->idx; + prop = prs->atom; + it->idx++; + if (prop == JS_ATOM_NULL || !(prs->flags & JS_PROP_ENUMERABLE)) + continue; + } + // check if the property was deleted unless we're dealing with a proxy + JSValue obj = it->obj; + if (JS_VALUE_GET_TAG(obj) == JS_TAG_OBJECT) { + JSObject *p = JS_VALUE_GET_OBJ(obj); + if (p->class_id == JS_CLASS_PROXY) + break; + } + ret = JS_HasProperty(ctx, obj, prop); + if (ret < 0) + return ret; + if (ret) + break; + } + /* return the property */ + sp[0] = JS_AtomToValue(ctx, prop); + sp[1] = JS_FALSE; + return 0; + done: + /* return the end */ + sp[0] = JS_UNDEFINED; + sp[1] = JS_TRUE; + return 0; +} + +static JSValue JS_GetIterator2(JSContext *ctx, JSValue obj, + JSValue method) +{ + JSValue enum_obj; + + enum_obj = JS_Call(ctx, method, obj, 0, NULL); + if (JS_IsException(enum_obj)) + return enum_obj; + if (!JS_IsObject(enum_obj)) { + JS_FreeValue(ctx, enum_obj); + return JS_ThrowTypeErrorNotAnObject(ctx); + } + return enum_obj; +} + +static JSValue JS_GetIterator(JSContext *ctx, JSValue obj, BOOL is_async) +{ + JSValue method, ret, sync_iter; + + if (is_async) { + method = JS_GetProperty(ctx, obj, JS_ATOM_Symbol_asyncIterator); + if (JS_IsException(method)) + return method; + if (JS_IsUndefined(method) || JS_IsNull(method)) { + method = JS_GetProperty(ctx, obj, JS_ATOM_Symbol_iterator); + if (JS_IsException(method)) + return method; + sync_iter = JS_GetIterator2(ctx, obj, method); + JS_FreeValue(ctx, method); + if (JS_IsException(sync_iter)) + return sync_iter; + ret = JS_CreateAsyncFromSyncIterator(ctx, sync_iter); + JS_FreeValue(ctx, sync_iter); + return ret; + } + } else { + method = JS_GetProperty(ctx, obj, JS_ATOM_Symbol_iterator); + if (JS_IsException(method)) + return method; + } + if (!JS_IsFunction(ctx, method)) { + JS_FreeValue(ctx, method); + return JS_ThrowTypeError(ctx, "value is not iterable"); + } + ret = JS_GetIterator2(ctx, obj, method); + JS_FreeValue(ctx, method); + return ret; +} + +/* return *pdone = 2 if the iterator object is not parsed */ +static JSValue JS_IteratorNext2(JSContext *ctx, JSValue enum_obj, + JSValue method, + int argc, JSValue *argv, int *pdone) +{ + JSValue obj; + + /* fast path for the built-in iterators (avoid creating the + intermediate result object) */ + if (JS_IsObject(method)) { + JSObject *p = JS_VALUE_GET_OBJ(method); + if (p->class_id == JS_CLASS_C_FUNCTION && + p->u.cfunc.cproto == JS_CFUNC_iterator_next) { + JSCFunctionType func; + JSValue args[1]; + + /* in case the function expects one argument */ + if (argc == 0) { + args[0] = JS_UNDEFINED; + argv = args; + } + func = p->u.cfunc.c_function; + return func.iterator_next(ctx, enum_obj, argc, argv, + pdone, p->u.cfunc.magic); + } + } + obj = JS_Call(ctx, method, enum_obj, argc, argv); + if (JS_IsException(obj)) + goto fail; + if (!JS_IsObject(obj)) { + JS_FreeValue(ctx, obj); + JS_ThrowTypeError(ctx, "iterator must return an object"); + goto fail; + } + *pdone = 2; + return obj; + fail: + *pdone = FALSE; + return JS_EXCEPTION; +} + +static JSValue JS_IteratorNext(JSContext *ctx, JSValue enum_obj, + JSValue method, + int argc, JSValue *argv, BOOL *pdone) +{ + JSValue obj, value, done_val; + int done; + + obj = JS_IteratorNext2(ctx, enum_obj, method, argc, argv, &done); + if (JS_IsException(obj)) + goto fail; + if (done != 2) { + *pdone = done; + return obj; + } else { + done_val = JS_GetProperty(ctx, obj, JS_ATOM_done); + if (JS_IsException(done_val)) + goto fail; + *pdone = JS_ToBoolFree(ctx, done_val); + value = JS_UNDEFINED; + if (!*pdone) { + value = JS_GetProperty(ctx, obj, JS_ATOM_value); + } + JS_FreeValue(ctx, obj); + return value; + } + fail: + JS_FreeValue(ctx, obj); + *pdone = FALSE; + return JS_EXCEPTION; +} + +/* return < 0 in case of exception */ +static int JS_IteratorClose(JSContext *ctx, JSValue enum_obj, + BOOL is_exception_pending) +{ + JSValue method, ret, ex_obj; + int res; + + if (is_exception_pending) { + ex_obj = ctx->rt->current_exception; + ctx->rt->current_exception = JS_UNINITIALIZED; + res = -1; + } else { + ex_obj = JS_UNDEFINED; + res = 0; + } + method = JS_GetProperty(ctx, enum_obj, JS_ATOM_return); + if (JS_IsException(method)) { + res = -1; + goto done; + } + if (JS_IsUndefined(method) || JS_IsNull(method)) { + goto done; + } + ret = JS_CallFree(ctx, method, enum_obj, 0, NULL); + if (!is_exception_pending) { + if (JS_IsException(ret)) { + res = -1; + } else if (!JS_IsObject(ret)) { + JS_ThrowTypeErrorNotAnObject(ctx); + res = -1; + } + } + JS_FreeValue(ctx, ret); + done: + if (is_exception_pending) { + JS_Throw(ctx, ex_obj); + } + return res; +} + +/* obj -> enum_rec (3 slots) */ +static __exception int js_for_of_start(JSContext *ctx, JSValue *sp, + BOOL is_async) +{ + JSValue op1, obj, method; + op1 = sp[-1]; + obj = JS_GetIterator(ctx, op1, is_async); + if (JS_IsException(obj)) + return -1; + JS_FreeValue(ctx, op1); + sp[-1] = obj; + method = JS_GetProperty(ctx, obj, JS_ATOM_next); + if (JS_IsException(method)) + return -1; + sp[0] = method; + return 0; +} + +/* enum_rec [objs] -> enum_rec [objs] value done. There are 'offset' + objs. If 'done' is true or in case of exception, 'enum_rec' is set + to undefined. If 'done' is true, 'value' is always set to + undefined. */ +static __exception int js_for_of_next(JSContext *ctx, JSValue *sp, int offset) +{ + JSValue value = JS_UNDEFINED; + int done = 1; + + if (likely(!JS_IsUndefined(sp[offset]))) { + value = JS_IteratorNext(ctx, sp[offset], sp[offset + 1], 0, NULL, &done); + if (JS_IsException(value)) + done = -1; + if (done) { + /* value is JS_UNDEFINED or JS_EXCEPTION */ + /* replace the iteration object with undefined */ + JS_FreeValue(ctx, sp[offset]); + sp[offset] = JS_UNDEFINED; + if (done < 0) { + return -1; + } else { + JS_FreeValue(ctx, value); + value = JS_UNDEFINED; + } + } + } + sp[0] = value; + sp[1] = js_bool(done); + return 0; +} + +static JSValue JS_IteratorGetCompleteValue(JSContext *ctx, JSValue obj, + BOOL *pdone) +{ + JSValue done_val, value; + BOOL done; + done_val = JS_GetProperty(ctx, obj, JS_ATOM_done); + if (JS_IsException(done_val)) + goto fail; + done = JS_ToBoolFree(ctx, done_val); + value = JS_GetProperty(ctx, obj, JS_ATOM_value); + if (JS_IsException(value)) + goto fail; + *pdone = done; + return value; + fail: + *pdone = FALSE; + return JS_EXCEPTION; +} + +static __exception int js_iterator_get_value_done(JSContext *ctx, JSValue *sp) +{ + JSValue obj, value; + BOOL done; + obj = sp[-1]; + if (!JS_IsObject(obj)) { + JS_ThrowTypeError(ctx, "iterator must return an object"); + return -1; + } + value = JS_IteratorGetCompleteValue(ctx, obj, &done); + if (JS_IsException(value)) + return -1; + JS_FreeValue(ctx, obj); + sp[-1] = value; + sp[0] = js_bool(done); + return 0; +} + +static JSValue js_create_iterator_result(JSContext *ctx, + JSValue val, + BOOL done) +{ + JSValue obj; + obj = JS_NewObject(ctx); + if (JS_IsException(obj)) { + JS_FreeValue(ctx, val); + return obj; + } + if (JS_DefinePropertyValue(ctx, obj, JS_ATOM_value, + val, JS_PROP_C_W_E) < 0) { + goto fail; + } + if (JS_DefinePropertyValue(ctx, obj, JS_ATOM_done, + js_bool(done), JS_PROP_C_W_E) < 0) { + fail: + JS_FreeValue(ctx, obj); + return JS_EXCEPTION; + } + return obj; +} + +static JSValue js_array_iterator_next(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv, + BOOL *pdone, int magic); + +static JSValue js_create_array_iterator(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv, int magic); + +static BOOL js_is_fast_array(JSContext *ctx, JSValue obj) +{ + /* Try and handle fast arrays explicitly */ + if (JS_VALUE_GET_TAG(obj) == JS_TAG_OBJECT) { + JSObject *p = JS_VALUE_GET_OBJ(obj); + if (p->class_id == JS_CLASS_ARRAY && p->fast_array) { + return TRUE; + } + } + return FALSE; +} + +/* Access an Array's internal JSValue array if available */ +static BOOL js_get_fast_array(JSContext *ctx, JSValue obj, + JSValue **arrpp, uint32_t *countp) +{ + /* Try and handle fast arrays explicitly */ + if (JS_VALUE_GET_TAG(obj) == JS_TAG_OBJECT) { + JSObject *p = JS_VALUE_GET_OBJ(obj); + if (p->class_id == JS_CLASS_ARRAY && p->fast_array) { + *countp = p->u.array.count; + *arrpp = p->u.array.u.values; + return TRUE; + } + } + return FALSE; +} + +static __exception int js_append_enumerate(JSContext *ctx, JSValue *sp) +{ + JSValue iterator, enumobj, method, value; + int is_array_iterator; + JSValue *arrp; + uint32_t i, count32, pos; + + if (JS_VALUE_GET_TAG(sp[-2]) != JS_TAG_INT) { + JS_ThrowInternalError(ctx, "invalid index for append"); + return -1; + } + + pos = JS_VALUE_GET_INT(sp[-2]); + + /* XXX: further optimisations: + - use ctx->array_proto_values? + - check if array_iterator_prototype next method is built-in and + avoid constructing actual iterator object? + - build this into js_for_of_start and use in all `for (x of o)` loops + */ + iterator = JS_GetProperty(ctx, sp[-1], JS_ATOM_Symbol_iterator); + if (JS_IsException(iterator)) + return -1; + /* Used to squelch a -Wcast-function-type warning. */ + JSCFunctionType ft = { .generic_magic = js_create_array_iterator }; + is_array_iterator = JS_IsCFunction(ctx, iterator, + ft.generic, + JS_ITERATOR_KIND_VALUE); + JS_FreeValue(ctx, iterator); + + enumobj = JS_GetIterator(ctx, sp[-1], FALSE); + if (JS_IsException(enumobj)) + return -1; + method = JS_GetProperty(ctx, enumobj, JS_ATOM_next); + if (JS_IsException(method)) { + JS_FreeValue(ctx, enumobj); + return -1; + } + /* Used to squelch a -Wcast-function-type warning. */ + JSCFunctionType ft2 = { .iterator_next = js_array_iterator_next }; + if (is_array_iterator + && JS_IsCFunction(ctx, method, ft2.generic, 0) + && js_get_fast_array(ctx, sp[-1], &arrp, &count32)) { + uint32_t len; + if (js_get_length32(ctx, &len, sp[-1])) + goto exception; + /* if len > count32, the elements >= count32 might be read in + the prototypes and might have side effects */ + if (len != count32) + goto general_case; + /* Handle fast arrays explicitly */ + for (i = 0; i < count32; i++) { + if (JS_DefinePropertyValueUint32(ctx, sp[-3], pos++, + js_dup(arrp[i]), JS_PROP_C_W_E) < 0) + goto exception; + } + } else { + general_case: + for (;;) { + BOOL done; + value = JS_IteratorNext(ctx, enumobj, method, 0, NULL, &done); + if (JS_IsException(value)) + goto exception; + if (done) { + /* value is JS_UNDEFINED */ + break; + } + if (JS_DefinePropertyValueUint32(ctx, sp[-3], pos++, value, JS_PROP_C_W_E) < 0) + goto exception; + } + } + /* Note: could raise an error if too many elements */ + sp[-2] = js_int32(pos); + JS_FreeValue(ctx, enumobj); + JS_FreeValue(ctx, method); + return 0; + +exception: + JS_IteratorClose(ctx, enumobj, TRUE); + JS_FreeValue(ctx, enumobj); + JS_FreeValue(ctx, method); + return -1; +} + +static __exception int JS_CopyDataProperties(JSContext *ctx, + JSValue target, + JSValue source, + JSValue excluded, + BOOL setprop) +{ + JSPropertyEnum *tab_atom; + JSValue val; + uint32_t i, tab_atom_count; + JSObject *p; + JSObject *pexcl = NULL; + int ret, gpn_flags; + JSPropertyDescriptor desc; + BOOL is_enumerable; + + if (JS_VALUE_GET_TAG(source) != JS_TAG_OBJECT) + return 0; + + if (JS_VALUE_GET_TAG(excluded) == JS_TAG_OBJECT) + pexcl = JS_VALUE_GET_OBJ(excluded); + + p = JS_VALUE_GET_OBJ(source); + + gpn_flags = JS_GPN_STRING_MASK | JS_GPN_SYMBOL_MASK | JS_GPN_ENUM_ONLY; + if (p->is_exotic) { + const JSClassExoticMethods *em = ctx->rt->class_array[p->class_id].exotic; + /* cannot use JS_GPN_ENUM_ONLY with e.g. proxies because it + introduces a visible change */ + if (em && em->get_own_property_names) { + gpn_flags &= ~JS_GPN_ENUM_ONLY; + } + } + if (JS_GetOwnPropertyNamesInternal(ctx, &tab_atom, &tab_atom_count, p, + gpn_flags)) + return -1; + + for (i = 0; i < tab_atom_count; i++) { + if (pexcl) { + ret = JS_GetOwnPropertyInternal(ctx, NULL, pexcl, tab_atom[i].atom); + if (ret) { + if (ret < 0) + goto exception; + continue; + } + } + if (!(gpn_flags & JS_GPN_ENUM_ONLY)) { + /* test if the property is enumerable */ + ret = JS_GetOwnPropertyInternal(ctx, &desc, p, tab_atom[i].atom); + if (ret < 0) + goto exception; + if (!ret) + continue; + is_enumerable = (desc.flags & JS_PROP_ENUMERABLE) != 0; + js_free_desc(ctx, &desc); + if (!is_enumerable) + continue; + } + val = JS_GetProperty(ctx, source, tab_atom[i].atom); + if (JS_IsException(val)) + goto exception; + if (setprop) + ret = JS_SetProperty(ctx, target, tab_atom[i].atom, val); + else + ret = JS_DefinePropertyValue(ctx, target, tab_atom[i].atom, val, + JS_PROP_C_W_E); + if (ret < 0) + goto exception; + } + js_free_prop_enum(ctx, tab_atom, tab_atom_count); + return 0; + exception: + js_free_prop_enum(ctx, tab_atom, tab_atom_count); + return -1; +} + +/* only valid inside C functions */ +static JSValue JS_GetActiveFunction(JSContext *ctx) +{ + return ctx->rt->current_stack_frame->cur_func; +} + +static JSVarRef *get_var_ref(JSContext *ctx, JSStackFrame *sf, + int var_idx, BOOL is_arg) +{ + JSVarRef *var_ref; + struct list_head *el; + + list_for_each(el, &sf->var_ref_list) { + var_ref = list_entry(el, JSVarRef, header.link); + if (var_ref->var_idx == var_idx && var_ref->is_arg == is_arg) { + var_ref->header.ref_count++; + return var_ref; + } + } + /* create a new one */ + var_ref = js_malloc(ctx, sizeof(JSVarRef)); + if (!var_ref) + return NULL; + var_ref->header.ref_count = 1; + var_ref->is_detached = FALSE; + var_ref->is_arg = is_arg; + var_ref->var_idx = var_idx; + list_add_tail(&var_ref->header.link, &sf->var_ref_list); + if (is_arg) + var_ref->pvalue = &sf->arg_buf[var_idx]; + else + var_ref->pvalue = &sf->var_buf[var_idx]; + var_ref->value = JS_UNDEFINED; + return var_ref; +} + +static JSValue js_closure2(JSContext *ctx, JSValue func_obj, + JSFunctionBytecode *b, + JSVarRef **cur_var_refs, + JSStackFrame *sf) +{ + JSObject *p; + JSVarRef **var_refs; + int i; + + p = JS_VALUE_GET_OBJ(func_obj); + p->u.func.function_bytecode = b; + p->u.func.home_object = NULL; + p->u.func.var_refs = NULL; + if (b->closure_var_count) { + var_refs = js_mallocz(ctx, sizeof(var_refs[0]) * b->closure_var_count); + if (!var_refs) + goto fail; + p->u.func.var_refs = var_refs; + for(i = 0; i < b->closure_var_count; i++) { + JSClosureVar *cv = &b->closure_var[i]; + JSVarRef *var_ref; + if (cv->is_local) { + /* reuse the existing variable reference if it already exists */ + var_ref = get_var_ref(ctx, sf, cv->var_idx, cv->is_arg); + if (!var_ref) + goto fail; + } else { + var_ref = cur_var_refs[cv->var_idx]; + var_ref->header.ref_count++; + } + var_refs[i] = var_ref; + } + } + return func_obj; + fail: + /* bfunc is freed when func_obj is freed */ + JS_FreeValue(ctx, func_obj); + return JS_EXCEPTION; +} + +static JSValue js_instantiate_prototype(JSContext *ctx, JSObject *p, JSAtom atom, void *opaque) +{ + JSValue obj, this_val; + int ret; + + this_val = JS_MKPTR(JS_TAG_OBJECT, p); + obj = JS_NewObject(ctx); + if (JS_IsException(obj)) + return JS_EXCEPTION; + set_cycle_flag(ctx, obj); + set_cycle_flag(ctx, this_val); + ret = JS_DefinePropertyValue(ctx, obj, JS_ATOM_constructor, + js_dup(this_val), + JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE); + if (ret < 0) { + JS_FreeValue(ctx, obj); + return JS_EXCEPTION; + } + return obj; +} + +static const uint16_t func_kind_to_class_id[] = { + [JS_FUNC_NORMAL] = JS_CLASS_BYTECODE_FUNCTION, + [JS_FUNC_GENERATOR] = JS_CLASS_GENERATOR_FUNCTION, + [JS_FUNC_ASYNC] = JS_CLASS_ASYNC_FUNCTION, + [JS_FUNC_ASYNC_GENERATOR] = JS_CLASS_ASYNC_GENERATOR_FUNCTION, +}; + +static JSValue js_closure(JSContext *ctx, JSValue bfunc, + JSVarRef **cur_var_refs, + JSStackFrame *sf) +{ + JSFunctionBytecode *b; + JSValue func_obj; + JSAtom name_atom; + + b = JS_VALUE_GET_PTR(bfunc); + func_obj = JS_NewObjectClass(ctx, func_kind_to_class_id[b->func_kind]); + if (JS_IsException(func_obj)) { + JS_FreeValue(ctx, bfunc); + return JS_EXCEPTION; + } + func_obj = js_closure2(ctx, func_obj, b, cur_var_refs, sf); + if (JS_IsException(func_obj)) { + /* bfunc has been freed */ + goto fail; + } + name_atom = b->func_name; + if (name_atom == JS_ATOM_NULL) + name_atom = JS_ATOM_empty_string; + js_function_set_properties(ctx, func_obj, name_atom, + b->defined_arg_count); + + if (b->func_kind & JS_FUNC_GENERATOR) { + JSValue proto; + int proto_class_id; + /* generators have a prototype field which is used as + prototype for the generator object */ + if (b->func_kind == JS_FUNC_ASYNC_GENERATOR) + proto_class_id = JS_CLASS_ASYNC_GENERATOR; + else + proto_class_id = JS_CLASS_GENERATOR; + proto = JS_NewObjectProto(ctx, ctx->class_proto[proto_class_id]); + if (JS_IsException(proto)) + goto fail; + JS_DefinePropertyValue(ctx, func_obj, JS_ATOM_prototype, proto, + JS_PROP_WRITABLE); + } else if (b->has_prototype) { + /* add the 'prototype' property: delay instantiation to avoid + creating cycles for every javascript function. The prototype + object is created on the fly when first accessed */ + JS_SetConstructorBit(ctx, func_obj, TRUE); + JS_DefineAutoInitProperty(ctx, func_obj, JS_ATOM_prototype, + JS_AUTOINIT_ID_PROTOTYPE, NULL, + JS_PROP_WRITABLE); + } + return func_obj; + fail: + /* bfunc is freed when func_obj is freed */ + JS_FreeValue(ctx, func_obj); + return JS_EXCEPTION; +} + +#define JS_DEFINE_CLASS_HAS_HERITAGE (1 << 0) + +static int js_op_define_class(JSContext *ctx, JSValue *sp, + JSAtom class_name, int class_flags, + JSVarRef **cur_var_refs, + JSStackFrame *sf, BOOL is_computed_name) +{ + JSValue bfunc, parent_class, proto = JS_UNDEFINED; + JSValue ctor = JS_UNDEFINED, parent_proto = JS_UNDEFINED; + JSFunctionBytecode *b; + + parent_class = sp[-2]; + bfunc = sp[-1]; + + if (class_flags & JS_DEFINE_CLASS_HAS_HERITAGE) { + if (JS_IsNull(parent_class)) { + parent_proto = JS_NULL; + parent_class = js_dup(ctx->function_proto); + } else { + if (!JS_IsConstructor(ctx, parent_class)) { + JS_ThrowTypeError(ctx, "parent class must be constructor"); + goto fail; + } + parent_proto = JS_GetProperty(ctx, parent_class, JS_ATOM_prototype); + if (JS_IsException(parent_proto)) + goto fail; + if (!JS_IsNull(parent_proto) && !JS_IsObject(parent_proto)) { + JS_ThrowTypeError(ctx, "parent prototype must be an object or null"); + goto fail; + } + } + } else { + /* parent_class is JS_UNDEFINED in this case */ + parent_proto = js_dup(ctx->class_proto[JS_CLASS_OBJECT]); + parent_class = js_dup(ctx->function_proto); + } + proto = JS_NewObjectProto(ctx, parent_proto); + if (JS_IsException(proto)) + goto fail; + + b = JS_VALUE_GET_PTR(bfunc); + assert(b->func_kind == JS_FUNC_NORMAL); + ctor = JS_NewObjectProtoClass(ctx, parent_class, + JS_CLASS_BYTECODE_FUNCTION); + if (JS_IsException(ctor)) + goto fail; + ctor = js_closure2(ctx, ctor, b, cur_var_refs, sf); + bfunc = JS_UNDEFINED; + if (JS_IsException(ctor)) + goto fail; + js_method_set_home_object(ctx, ctor, proto); + JS_SetConstructorBit(ctx, ctor, TRUE); + + JS_DefinePropertyValue(ctx, ctor, JS_ATOM_length, + js_int32(b->defined_arg_count), + JS_PROP_CONFIGURABLE); + + if (is_computed_name) { + if (JS_DefineObjectNameComputed(ctx, ctor, sp[-3], + JS_PROP_CONFIGURABLE) < 0) + goto fail; + } else { + if (JS_DefineObjectName(ctx, ctor, class_name, JS_PROP_CONFIGURABLE) < 0) + goto fail; + } + + /* the constructor property must be first. It can be overriden by + computed property names */ + if (JS_DefinePropertyValue(ctx, proto, JS_ATOM_constructor, + js_dup(ctor), + JS_PROP_CONFIGURABLE | + JS_PROP_WRITABLE | JS_PROP_THROW) < 0) + goto fail; + /* set the prototype property */ + if (JS_DefinePropertyValue(ctx, ctor, JS_ATOM_prototype, + js_dup(proto), JS_PROP_THROW) < 0) + goto fail; + set_cycle_flag(ctx, ctor); + set_cycle_flag(ctx, proto); + + JS_FreeValue(ctx, parent_proto); + JS_FreeValue(ctx, parent_class); + + sp[-2] = ctor; + sp[-1] = proto; + return 0; + fail: + JS_FreeValue(ctx, parent_class); + JS_FreeValue(ctx, parent_proto); + JS_FreeValue(ctx, bfunc); + JS_FreeValue(ctx, proto); + JS_FreeValue(ctx, ctor); + sp[-2] = JS_UNDEFINED; + sp[-1] = JS_UNDEFINED; + return -1; +} + +static void close_var_refs(JSRuntime *rt, JSStackFrame *sf) +{ + struct list_head *el, *el1; + JSVarRef *var_ref; + int var_idx; + + list_for_each_safe(el, el1, &sf->var_ref_list) { + var_ref = list_entry(el, JSVarRef, header.link); + var_idx = var_ref->var_idx; + if (var_ref->is_arg) + var_ref->value = js_dup(sf->arg_buf[var_idx]); + else + var_ref->value = js_dup(sf->var_buf[var_idx]); + var_ref->pvalue = &var_ref->value; + /* the reference is no longer to a local variable */ + var_ref->is_detached = TRUE; + add_gc_object(rt, &var_ref->header, JS_GC_OBJ_TYPE_VAR_REF); + } +} + +static void close_lexical_var(JSContext *ctx, JSStackFrame *sf, int idx, int is_arg) +{ + struct list_head *el, *el1; + JSVarRef *var_ref; + int var_idx = idx; + + list_for_each_safe(el, el1, &sf->var_ref_list) { + var_ref = list_entry(el, JSVarRef, header.link); + if (var_idx == var_ref->var_idx && var_ref->is_arg == is_arg) { + var_ref->value = js_dup(sf->var_buf[var_idx]); + var_ref->pvalue = &var_ref->value; + list_del(&var_ref->header.link); + /* the reference is no longer to a local variable */ + var_ref->is_detached = TRUE; + add_gc_object(ctx->rt, &var_ref->header, JS_GC_OBJ_TYPE_VAR_REF); + } + } +} + +#define JS_CALL_FLAG_COPY_ARGV (1 << 1) +#define JS_CALL_FLAG_GENERATOR (1 << 2) + +static JSValue js_call_c_function(JSContext *ctx, JSValue func_obj, + JSValue this_obj, + int argc, JSValue *argv, int flags) +{ + JSRuntime *rt = ctx->rt; + JSCFunctionType func; + JSObject *p; + JSStackFrame sf_s, *sf = &sf_s, *prev_sf; + JSValue ret_val; + JSValue *arg_buf; + int arg_count, i; + JSCFunctionEnum cproto; + + p = JS_VALUE_GET_OBJ(func_obj); + cproto = p->u.cfunc.cproto; + arg_count = p->u.cfunc.length; + + /* better to always check stack overflow */ + if (js_check_stack_overflow(rt, sizeof(arg_buf[0]) * arg_count)) + return JS_ThrowStackOverflow(ctx); + + prev_sf = rt->current_stack_frame; + sf->prev_frame = prev_sf; + rt->current_stack_frame = sf; + ctx = p->u.cfunc.realm; /* change the current realm */ + + sf->is_strict_mode = FALSE; + sf->cur_func = func_obj; + sf->arg_count = argc; + arg_buf = argv; + + if (unlikely(argc < arg_count)) { + /* ensure that at least argc_count arguments are readable */ + arg_buf = alloca(sizeof(arg_buf[0]) * arg_count); + for(i = 0; i < argc; i++) + arg_buf[i] = argv[i]; + for(i = argc; i < arg_count; i++) + arg_buf[i] = JS_UNDEFINED; + sf->arg_count = arg_count; + } + sf->arg_buf = arg_buf; + + func = p->u.cfunc.c_function; + switch(cproto) { + case JS_CFUNC_constructor: + case JS_CFUNC_constructor_or_func: + if (!(flags & JS_CALL_FLAG_CONSTRUCTOR)) { + if (cproto == JS_CFUNC_constructor) { + not_a_constructor: + ret_val = JS_ThrowTypeError(ctx, "must be called with new"); + break; + } else { + this_obj = JS_UNDEFINED; + } + } + /* here this_obj is new_target */ + /* fall thru */ + case JS_CFUNC_generic: + ret_val = func.generic(ctx, this_obj, argc, arg_buf); + break; + case JS_CFUNC_constructor_magic: + case JS_CFUNC_constructor_or_func_magic: + if (!(flags & JS_CALL_FLAG_CONSTRUCTOR)) { + if (cproto == JS_CFUNC_constructor_magic) { + goto not_a_constructor; + } else { + this_obj = JS_UNDEFINED; + } + } + /* fall thru */ + case JS_CFUNC_generic_magic: + ret_val = func.generic_magic(ctx, this_obj, argc, arg_buf, + p->u.cfunc.magic); + break; + case JS_CFUNC_getter: + ret_val = func.getter(ctx, this_obj); + break; + case JS_CFUNC_setter: + ret_val = func.setter(ctx, this_obj, arg_buf[0]); + break; + case JS_CFUNC_getter_magic: + ret_val = func.getter_magic(ctx, this_obj, p->u.cfunc.magic); + break; + case JS_CFUNC_setter_magic: + ret_val = func.setter_magic(ctx, this_obj, arg_buf[0], p->u.cfunc.magic); + break; + case JS_CFUNC_f_f: + { + double d1; + + if (unlikely(JS_ToFloat64(ctx, &d1, arg_buf[0]))) { + ret_val = JS_EXCEPTION; + break; + } + ret_val = js_number(func.f_f(d1)); + } + break; + case JS_CFUNC_f_f_f: + { + double d1, d2; + + if (unlikely(JS_ToFloat64(ctx, &d1, arg_buf[0]))) { + ret_val = JS_EXCEPTION; + break; + } + if (unlikely(JS_ToFloat64(ctx, &d2, arg_buf[1]))) { + ret_val = JS_EXCEPTION; + break; + } + ret_val = js_number(func.f_f_f(d1, d2)); + } + break; + case JS_CFUNC_iterator_next: + { + int done; + ret_val = func.iterator_next(ctx, this_obj, argc, arg_buf, + &done, p->u.cfunc.magic); + if (!JS_IsException(ret_val) && done != 2) { + ret_val = js_create_iterator_result(ctx, ret_val, done); + } + } + break; + default: + abort(); + } + + rt->current_stack_frame = sf->prev_frame; + return ret_val; +} + +static JSValue js_call_bound_function(JSContext *ctx, JSValue func_obj, + JSValue this_obj, + int argc, JSValue *argv, int flags) +{ + JSObject *p; + JSBoundFunction *bf; + JSValue *arg_buf, new_target; + int arg_count, i; + + p = JS_VALUE_GET_OBJ(func_obj); + bf = p->u.bound_function; + arg_count = bf->argc + argc; + if (js_check_stack_overflow(ctx->rt, sizeof(JSValue) * arg_count)) + return JS_ThrowStackOverflow(ctx); + arg_buf = alloca(sizeof(JSValue) * arg_count); + for(i = 0; i < bf->argc; i++) { + arg_buf[i] = bf->argv[i]; + } + for(i = 0; i < argc; i++) { + arg_buf[bf->argc + i] = argv[i]; + } + if (flags & JS_CALL_FLAG_CONSTRUCTOR) { + new_target = this_obj; + if (js_same_value(ctx, func_obj, new_target)) + new_target = bf->func_obj; + return JS_CallConstructor2(ctx, bf->func_obj, new_target, + arg_count, arg_buf); + } else { + return JS_Call(ctx, bf->func_obj, bf->this_val, + arg_count, arg_buf); + } +} + +/* argument of OP_special_object */ +typedef enum { + OP_SPECIAL_OBJECT_ARGUMENTS, + OP_SPECIAL_OBJECT_MAPPED_ARGUMENTS, + OP_SPECIAL_OBJECT_THIS_FUNC, + OP_SPECIAL_OBJECT_NEW_TARGET, + OP_SPECIAL_OBJECT_HOME_OBJECT, + OP_SPECIAL_OBJECT_VAR_OBJECT, + OP_SPECIAL_OBJECT_IMPORT_META, +} OPSpecialObjectEnum; + +#define FUNC_RET_AWAIT 0 +#define FUNC_RET_YIELD 1 +#define FUNC_RET_YIELD_STAR 2 + +#if defined(DUMP_BYTECODE_FINAL) || \ + defined(DUMP_BYTECODE_PASS2) || \ + defined(DUMP_BYTECODE_PASS1) || \ + defined(DUMP_BYTECODE_STACK) || \ + defined(DUMP_BYTECODE_STEP) || \ + defined(DUMP_READ_OBJECT) +#define DUMP_BYTECODE +#endif + +#ifdef DUMP_BYTECODE +static void dump_single_byte_code(JSContext *ctx, const uint8_t *pc, + JSFunctionBytecode *b, int start_pos); +static void print_func_name(JSFunctionBytecode *b); +#endif + +/* argv[] is modified if (flags & JS_CALL_FLAG_COPY_ARGV) = 0. */ +static JSValue JS_CallInternal(JSContext *caller_ctx, JSValue func_obj, + JSValue this_obj, JSValue new_target, + int argc, JSValue *argv, int flags) +{ + JSRuntime *rt = caller_ctx->rt; + JSContext *ctx; + JSObject *p; + JSFunctionBytecode *b; + JSStackFrame sf_s, *sf = &sf_s; + uint8_t *pc; + int opcode, arg_allocated_size, i; + JSValue *local_buf, *stack_buf, *var_buf, *arg_buf, *sp, ret_val, *pval; + JSVarRef **var_refs; + size_t alloca_size; + JSInlineCache *ic; + +#ifdef DUMP_BYTECODE_STEP +#define DUMP_BYTECODE_OR_DONT(pc) \ + if (check_dump_flag(ctx->rt, DUMP_BYTECODE_STEP)) dump_single_byte_code(ctx, pc, b, 0); +#else +#define DUMP_BYTECODE_OR_DONT(pc) +#endif + +#if !DIRECT_DISPATCH +#define SWITCH(pc) DUMP_BYTECODE_OR_DONT(pc) switch (opcode = *pc++) +#define CASE(op) case op +#define DEFAULT default +#define BREAK break +#else + __extension__ static const void * const dispatch_table[256] = { +#define DEF(id, size, n_pop, n_push, f) && case_OP_ ## id, +#define def(id, size, n_pop, n_push, f) +#include "quickjs-opcode.h" + [ OP_COUNT ... 255 ] = &&case_default + }; +#define SWITCH(pc) DUMP_BYTECODE_OR_DONT(pc) __extension__ ({ goto *dispatch_table[opcode = *pc++]; }); +#define CASE(op) case_ ## op +#define DEFAULT case_default +#define BREAK SWITCH(pc) +#endif + + if (js_poll_interrupts(caller_ctx)) + return JS_EXCEPTION; + if (unlikely(JS_VALUE_GET_TAG(func_obj) != JS_TAG_OBJECT)) { + if (flags & JS_CALL_FLAG_GENERATOR) { + JSAsyncFunctionState *s = JS_VALUE_GET_PTR(func_obj); + /* func_obj get contains a pointer to JSFuncAsyncState */ + /* the stack frame is already allocated */ + sf = &s->frame; + p = JS_VALUE_GET_OBJ(sf->cur_func); + b = p->u.func.function_bytecode; + ctx = b->realm; + var_refs = p->u.func.var_refs; + local_buf = arg_buf = sf->arg_buf; + var_buf = sf->var_buf; + stack_buf = sf->var_buf + b->var_count; + sp = sf->cur_sp; + sf->cur_sp = NULL; /* cur_sp is NULL if the function is running */ + pc = sf->cur_pc; + sf->prev_frame = rt->current_stack_frame; + rt->current_stack_frame = sf; + ic = b->ic; + if (s->throw_flag) + goto exception; + else + goto restart; + } else { + goto not_a_function; + } + } + p = JS_VALUE_GET_OBJ(func_obj); + if (unlikely(p->class_id != JS_CLASS_BYTECODE_FUNCTION)) { + JSClassCall *call_func; + call_func = rt->class_array[p->class_id].call; + if (!call_func) { + not_a_function: + return JS_ThrowTypeError(caller_ctx, "not a function"); + } + return call_func(caller_ctx, func_obj, this_obj, argc, + argv, flags); + } + b = p->u.func.function_bytecode; + + if (unlikely(argc < b->arg_count || (flags & JS_CALL_FLAG_COPY_ARGV))) { + arg_allocated_size = b->arg_count; + } else { + arg_allocated_size = 0; + } + + alloca_size = sizeof(JSValue) * (arg_allocated_size + b->var_count + + b->stack_size); + if (js_check_stack_overflow(rt, alloca_size)) + return JS_ThrowStackOverflow(caller_ctx); + + sf->is_strict_mode = b->is_strict_mode; + arg_buf = argv; + sf->arg_count = argc; + sf->cur_func = func_obj; + init_list_head(&sf->var_ref_list); + var_refs = p->u.func.var_refs; + + local_buf = alloca(alloca_size); + if (unlikely(arg_allocated_size)) { + int n = min_int(argc, b->arg_count); + arg_buf = local_buf; + for(i = 0; i < n; i++) + arg_buf[i] = JS_DupValue(caller_ctx, argv[i]); + for(; i < b->arg_count; i++) + arg_buf[i] = JS_UNDEFINED; + sf->arg_count = b->arg_count; + } + var_buf = local_buf + arg_allocated_size; + sf->var_buf = var_buf; + sf->arg_buf = arg_buf; + + for(i = 0; i < b->var_count; i++) + var_buf[i] = JS_UNDEFINED; + + stack_buf = var_buf + b->var_count; + sp = stack_buf; + pc = b->byte_code_buf; + /* sf->cur_pc must we set to pc before any recursive calls to JS_CallInternal. */ + sf->cur_pc = NULL; + sf->prev_frame = rt->current_stack_frame; + rt->current_stack_frame = sf; + ctx = b->realm; /* set the current realm */ + ic = b->ic; + +#ifdef DUMP_BYTECODE_STEP + if (check_dump_flag(ctx->rt, DUMP_BYTECODE_STEP)) + print_func_name(b); +#endif + + restart: + for(;;) { + int call_argc; + JSValue *call_argv; + + SWITCH(pc) { + CASE(OP_push_i32): + *sp++ = js_int32(get_u32(pc)); + pc += 4; + BREAK; + CASE(OP_push_const): + *sp++ = js_dup(b->cpool[get_u32(pc)]); + pc += 4; + BREAK; + CASE(OP_push_minus1): + CASE(OP_push_0): + CASE(OP_push_1): + CASE(OP_push_2): + CASE(OP_push_3): + CASE(OP_push_4): + CASE(OP_push_5): + CASE(OP_push_6): + CASE(OP_push_7): + *sp++ = js_int32(opcode - OP_push_0); + BREAK; + CASE(OP_push_i8): + *sp++ = js_int32(get_i8(pc)); + pc += 1; + BREAK; + CASE(OP_push_i16): + *sp++ = js_int32(get_i16(pc)); + pc += 2; + BREAK; + CASE(OP_push_const8): + *sp++ = js_dup(b->cpool[*pc++]); + BREAK; + CASE(OP_fclosure8): + *sp++ = js_closure(ctx, js_dup(b->cpool[*pc++]), var_refs, sf); + if (unlikely(JS_IsException(sp[-1]))) + goto exception; + BREAK; + CASE(OP_push_empty_string): + *sp++ = JS_AtomToString(ctx, JS_ATOM_empty_string); + BREAK; + CASE(OP_get_length): + { + JSValue val; + + sf->cur_pc = pc; + val = JS_GetProperty(ctx, sp[-1], JS_ATOM_length); + if (unlikely(JS_IsException(val))) + goto exception; + JS_FreeValue(ctx, sp[-1]); + sp[-1] = val; + } + BREAK; + CASE(OP_push_atom_value): + *sp++ = JS_AtomToValue(ctx, get_u32(pc)); + pc += 4; + BREAK; + CASE(OP_undefined): + *sp++ = JS_UNDEFINED; + BREAK; + CASE(OP_null): + *sp++ = JS_NULL; + BREAK; + CASE(OP_push_this): + /* OP_push_this is only called at the start of a function */ + { + JSValue val; + if (!b->is_strict_mode) { + uint32_t tag = JS_VALUE_GET_TAG(this_obj); + if (likely(tag == JS_TAG_OBJECT)) + goto normal_this; + if (tag == JS_TAG_NULL || tag == JS_TAG_UNDEFINED) { + val = js_dup(ctx->global_obj); + } else { + val = JS_ToObject(ctx, this_obj); + if (JS_IsException(val)) + goto exception; + } + } else { + normal_this: + val = js_dup(this_obj); + } + *sp++ = val; + } + BREAK; + CASE(OP_push_false): + *sp++ = JS_FALSE; + BREAK; + CASE(OP_push_true): + *sp++ = JS_TRUE; + BREAK; + CASE(OP_object): + *sp++ = JS_NewObject(ctx); + if (unlikely(JS_IsException(sp[-1]))) + goto exception; + BREAK; + CASE(OP_special_object): + { + int arg = *pc++; + switch(arg) { + case OP_SPECIAL_OBJECT_ARGUMENTS: + *sp++ = js_build_arguments(ctx, argc, argv); + if (unlikely(JS_IsException(sp[-1]))) + goto exception; + break; + case OP_SPECIAL_OBJECT_MAPPED_ARGUMENTS: + *sp++ = js_build_mapped_arguments(ctx, argc, argv, + sf, min_int(argc, b->arg_count)); + if (unlikely(JS_IsException(sp[-1]))) + goto exception; + break; + case OP_SPECIAL_OBJECT_THIS_FUNC: + *sp++ = js_dup(sf->cur_func); + break; + case OP_SPECIAL_OBJECT_NEW_TARGET: + *sp++ = js_dup(new_target); + break; + case OP_SPECIAL_OBJECT_HOME_OBJECT: + { + JSObject *p1; + p1 = p->u.func.home_object; + if (unlikely(!p1)) + *sp++ = JS_UNDEFINED; + else + *sp++ = js_dup(JS_MKPTR(JS_TAG_OBJECT, p1)); + } + break; + case OP_SPECIAL_OBJECT_VAR_OBJECT: + *sp++ = JS_NewObjectProto(ctx, JS_NULL); + if (unlikely(JS_IsException(sp[-1]))) + goto exception; + break; + case OP_SPECIAL_OBJECT_IMPORT_META: + *sp++ = js_import_meta(ctx); + if (unlikely(JS_IsException(sp[-1]))) + goto exception; + break; + default: + abort(); + } + } + BREAK; + CASE(OP_rest): + { + int first = get_u16(pc); + pc += 2; + *sp++ = js_build_rest(ctx, first, argc, argv); + if (unlikely(JS_IsException(sp[-1]))) + goto exception; + } + BREAK; + + CASE(OP_drop): + JS_FreeValue(ctx, sp[-1]); + sp--; + BREAK; + CASE(OP_nip): + JS_FreeValue(ctx, sp[-2]); + sp[-2] = sp[-1]; + sp--; + BREAK; + CASE(OP_nip1): /* a b c -> b c */ + JS_FreeValue(ctx, sp[-3]); + sp[-3] = sp[-2]; + sp[-2] = sp[-1]; + sp--; + BREAK; + CASE(OP_dup): + sp[0] = js_dup(sp[-1]); + sp++; + BREAK; + CASE(OP_dup2): /* a b -> a b a b */ + sp[0] = js_dup(sp[-2]); + sp[1] = js_dup(sp[-1]); + sp += 2; + BREAK; + CASE(OP_dup3): /* a b c -> a b c a b c */ + sp[0] = js_dup(sp[-3]); + sp[1] = js_dup(sp[-2]); + sp[2] = js_dup(sp[-1]); + sp += 3; + BREAK; + CASE(OP_dup1): /* a b -> a a b */ + sp[0] = sp[-1]; + sp[-1] = js_dup(sp[-2]); + sp++; + BREAK; + CASE(OP_insert2): /* obj a -> a obj a (dup_x1) */ + sp[0] = sp[-1]; + sp[-1] = sp[-2]; + sp[-2] = js_dup(sp[0]); + sp++; + BREAK; + CASE(OP_insert3): /* obj prop a -> a obj prop a (dup_x2) */ + sp[0] = sp[-1]; + sp[-1] = sp[-2]; + sp[-2] = sp[-3]; + sp[-3] = js_dup(sp[0]); + sp++; + BREAK; + CASE(OP_insert4): /* this obj prop a -> a this obj prop a */ + sp[0] = sp[-1]; + sp[-1] = sp[-2]; + sp[-2] = sp[-3]; + sp[-3] = sp[-4]; + sp[-4] = js_dup(sp[0]); + sp++; + BREAK; + CASE(OP_perm3): /* obj a b -> a obj b (213) */ + { + JSValue tmp; + tmp = sp[-2]; + sp[-2] = sp[-3]; + sp[-3] = tmp; + } + BREAK; + CASE(OP_rot3l): /* x a b -> a b x (231) */ + { + JSValue tmp; + tmp = sp[-3]; + sp[-3] = sp[-2]; + sp[-2] = sp[-1]; + sp[-1] = tmp; + } + BREAK; + CASE(OP_rot4l): /* x a b c -> a b c x */ + { + JSValue tmp; + tmp = sp[-4]; + sp[-4] = sp[-3]; + sp[-3] = sp[-2]; + sp[-2] = sp[-1]; + sp[-1] = tmp; + } + BREAK; + CASE(OP_rot5l): /* x a b c d -> a b c d x */ + { + JSValue tmp; + tmp = sp[-5]; + sp[-5] = sp[-4]; + sp[-4] = sp[-3]; + sp[-3] = sp[-2]; + sp[-2] = sp[-1]; + sp[-1] = tmp; + } + BREAK; + CASE(OP_rot3r): /* a b x -> x a b (312) */ + { + JSValue tmp; + tmp = sp[-1]; + sp[-1] = sp[-2]; + sp[-2] = sp[-3]; + sp[-3] = tmp; + } + BREAK; + CASE(OP_perm4): /* obj prop a b -> a obj prop b */ + { + JSValue tmp; + tmp = sp[-2]; + sp[-2] = sp[-3]; + sp[-3] = sp[-4]; + sp[-4] = tmp; + } + BREAK; + CASE(OP_perm5): /* this obj prop a b -> a this obj prop b */ + { + JSValue tmp; + tmp = sp[-2]; + sp[-2] = sp[-3]; + sp[-3] = sp[-4]; + sp[-4] = sp[-5]; + sp[-5] = tmp; + } + BREAK; + CASE(OP_swap): /* a b -> b a */ + { + JSValue tmp; + tmp = sp[-2]; + sp[-2] = sp[-1]; + sp[-1] = tmp; + } + BREAK; + CASE(OP_swap2): /* a b c d -> c d a b */ + { + JSValue tmp1, tmp2; + tmp1 = sp[-4]; + tmp2 = sp[-3]; + sp[-4] = sp[-2]; + sp[-3] = sp[-1]; + sp[-2] = tmp1; + sp[-1] = tmp2; + } + BREAK; + + CASE(OP_fclosure): + { + JSValue bfunc = js_dup(b->cpool[get_u32(pc)]); + pc += 4; + *sp++ = js_closure(ctx, bfunc, var_refs, sf); + if (unlikely(JS_IsException(sp[-1]))) + goto exception; + } + BREAK; + CASE(OP_call0): + CASE(OP_call1): + CASE(OP_call2): + CASE(OP_call3): + call_argc = opcode - OP_call0; + goto has_call_argc; + CASE(OP_call): + CASE(OP_tail_call): + { + call_argc = get_u16(pc); + pc += 2; + goto has_call_argc; + has_call_argc: + call_argv = sp - call_argc; + sf->cur_pc = pc; + ret_val = JS_CallInternal(ctx, call_argv[-1], JS_UNDEFINED, + JS_UNDEFINED, call_argc, call_argv, 0); + if (unlikely(JS_IsException(ret_val))) + goto exception; + if (opcode == OP_tail_call) + goto done; + for(i = -1; i < call_argc; i++) + JS_FreeValue(ctx, call_argv[i]); + sp -= call_argc + 1; + *sp++ = ret_val; + } + BREAK; + CASE(OP_call_constructor): + { + call_argc = get_u16(pc); + pc += 2; + call_argv = sp - call_argc; + sf->cur_pc = pc; + ret_val = JS_CallConstructorInternal(ctx, call_argv[-2], + call_argv[-1], + call_argc, call_argv, 0); + if (unlikely(JS_IsException(ret_val))) + goto exception; + for(i = -2; i < call_argc; i++) + JS_FreeValue(ctx, call_argv[i]); + sp -= call_argc + 2; + *sp++ = ret_val; + } + BREAK; + CASE(OP_call_method): + CASE(OP_tail_call_method): + { + call_argc = get_u16(pc); + pc += 2; + call_argv = sp - call_argc; + sf->cur_pc = pc; + ret_val = JS_CallInternal(ctx, call_argv[-1], call_argv[-2], + JS_UNDEFINED, call_argc, call_argv, 0); + if (unlikely(JS_IsException(ret_val))) + goto exception; + if (opcode == OP_tail_call_method) + goto done; + for(i = -2; i < call_argc; i++) + JS_FreeValue(ctx, call_argv[i]); + sp -= call_argc + 2; + *sp++ = ret_val; + } + BREAK; + CASE(OP_array_from): + { + int i, ret; + + call_argc = get_u16(pc); + pc += 2; + ret_val = JS_NewArray(ctx); + if (unlikely(JS_IsException(ret_val))) + goto exception; + call_argv = sp - call_argc; + for(i = 0; i < call_argc; i++) { + ret = JS_DefinePropertyValue(ctx, ret_val, __JS_AtomFromUInt32(i), call_argv[i], + JS_PROP_C_W_E | JS_PROP_THROW); + call_argv[i] = JS_UNDEFINED; + if (ret < 0) { + JS_FreeValue(ctx, ret_val); + goto exception; + } + } + sp -= call_argc; + *sp++ = ret_val; + } + BREAK; + + CASE(OP_apply): + { + int magic; + magic = get_u16(pc); + pc += 2; + sf->cur_pc = pc; + + ret_val = js_function_apply(ctx, sp[-3], 2, &sp[-2], magic); + if (unlikely(JS_IsException(ret_val))) + goto exception; + JS_FreeValue(ctx, sp[-3]); + JS_FreeValue(ctx, sp[-2]); + JS_FreeValue(ctx, sp[-1]); + sp -= 3; + *sp++ = ret_val; + } + BREAK; + CASE(OP_return): + ret_val = *--sp; + goto done; + CASE(OP_return_undef): + ret_val = JS_UNDEFINED; + goto done; + + CASE(OP_check_ctor_return): + /* return TRUE if 'this' should be returned */ + if (!JS_IsObject(sp[-1])) { + if (!JS_IsUndefined(sp[-1])) { + JS_ThrowTypeError(caller_ctx, "derived class constructor must return an object or undefined"); + goto exception; + } + sp[0] = JS_TRUE; + } else { + sp[0] = JS_FALSE; + } + sp++; + BREAK; + CASE(OP_check_ctor): + if (JS_IsUndefined(new_target)) { + JS_ThrowTypeError(ctx, "class constructors must be invoked with 'new'"); + goto exception; + } + BREAK; + CASE(OP_check_brand): + { + int ret = JS_CheckBrand(ctx, sp[-2], sp[-1]); + if (ret < 0) + goto exception; + if (!ret) { + JS_ThrowTypeError(ctx, "invalid brand on object"); + goto exception; + } + } + BREAK; + CASE(OP_add_brand): + if (JS_AddBrand(ctx, sp[-2], sp[-1]) < 0) + goto exception; + JS_FreeValue(ctx, sp[-2]); + JS_FreeValue(ctx, sp[-1]); + sp -= 2; + BREAK; + + CASE(OP_throw): + JS_Throw(ctx, *--sp); + goto exception; + + CASE(OP_throw_error): +#define JS_THROW_VAR_RO 0 +#define JS_THROW_VAR_REDECL 1 +#define JS_THROW_VAR_UNINITIALIZED 2 +#define JS_THROW_ERROR_DELETE_SUPER 3 +#define JS_THROW_ERROR_ITERATOR_THROW 4 + { + JSAtom atom; + int type; + atom = get_u32(pc); + type = pc[4]; + pc += 5; + if (type == JS_THROW_VAR_RO) + JS_ThrowTypeErrorReadOnly(ctx, JS_PROP_THROW, atom); + else + if (type == JS_THROW_VAR_REDECL) + JS_ThrowSyntaxErrorVarRedeclaration(ctx, atom); + else + if (type == JS_THROW_VAR_UNINITIALIZED) + JS_ThrowReferenceErrorUninitialized(ctx, atom); + else + if (type == JS_THROW_ERROR_DELETE_SUPER) + JS_ThrowReferenceError(ctx, "unsupported reference to 'super'"); + else + if (type == JS_THROW_ERROR_ITERATOR_THROW) + JS_ThrowTypeError(ctx, "iterator does not have a throw method"); + else + JS_ThrowInternalError(ctx, "invalid throw var type %d", type); + } + goto exception; + + CASE(OP_eval): + { + JSValue obj; + int scope_idx; + call_argc = get_u16(pc); + scope_idx = get_u16(pc + 2) - 1; + pc += 4; + call_argv = sp - call_argc; + sf->cur_pc = pc; + if (js_same_value(ctx, call_argv[-1], ctx->eval_obj)) { + if (call_argc >= 1) + obj = call_argv[0]; + else + obj = JS_UNDEFINED; + ret_val = JS_EvalObject(ctx, JS_UNDEFINED, obj, + JS_EVAL_TYPE_DIRECT, scope_idx); + } else { + ret_val = JS_CallInternal(ctx, call_argv[-1], JS_UNDEFINED, + JS_UNDEFINED, call_argc, call_argv, 0); + } + if (unlikely(JS_IsException(ret_val))) + goto exception; + for(i = -1; i < call_argc; i++) + JS_FreeValue(ctx, call_argv[i]); + sp -= call_argc + 1; + *sp++ = ret_val; + } + BREAK; + /* could merge with OP_apply */ + CASE(OP_apply_eval): + { + int scope_idx; + uint32_t len; + JSValue *tab; + JSValue obj; + + scope_idx = get_u16(pc) - 1; + pc += 2; + sf->cur_pc = pc; + tab = build_arg_list(ctx, &len, sp[-1]); + if (!tab) + goto exception; + if (js_same_value(ctx, sp[-2], ctx->eval_obj)) { + if (len >= 1) + obj = tab[0]; + else + obj = JS_UNDEFINED; + ret_val = JS_EvalObject(ctx, JS_UNDEFINED, obj, + JS_EVAL_TYPE_DIRECT, scope_idx); + } else { + ret_val = JS_Call(ctx, sp[-2], JS_UNDEFINED, len, + tab); + } + free_arg_list(ctx, tab, len); + if (unlikely(JS_IsException(ret_val))) + goto exception; + JS_FreeValue(ctx, sp[-2]); + JS_FreeValue(ctx, sp[-1]); + sp -= 2; + *sp++ = ret_val; + } + BREAK; + + CASE(OP_regexp): + { + sp[-2] = js_regexp_constructor_internal(ctx, JS_UNDEFINED, + sp[-2], sp[-1]); + sp--; + } + BREAK; + + CASE(OP_get_super): + { + JSValue proto; + proto = JS_GetPrototype(ctx, sp[-1]); + if (JS_IsException(proto)) + goto exception; + JS_FreeValue(ctx, sp[-1]); + sp[-1] = proto; + } + BREAK; + + CASE(OP_import): + { + JSValue val; + sf->cur_pc = pc; + val = js_dynamic_import(ctx, sp[-1]); + if (JS_IsException(val)) + goto exception; + JS_FreeValue(ctx, sp[-1]); + sp[-1] = val; + } + BREAK; + + CASE(OP_check_var): + { + int ret; + JSAtom atom; + atom = get_u32(pc); + pc += 4; + + ret = JS_CheckGlobalVar(ctx, atom); + if (ret < 0) + goto exception; + *sp++ = js_bool(ret); + } + BREAK; + + CASE(OP_get_var_undef): + CASE(OP_get_var): + { + JSValue val; + JSAtom atom; + atom = get_u32(pc); + pc += 4; + sf->cur_pc = pc; + + val = JS_GetGlobalVar(ctx, atom, opcode - OP_get_var_undef); + if (unlikely(JS_IsException(val))) + goto exception; + *sp++ = val; + } + BREAK; + + CASE(OP_put_var): + CASE(OP_put_var_init): + { + int ret; + JSAtom atom; + atom = get_u32(pc); + pc += 4; + sf->cur_pc = pc; + + ret = JS_SetGlobalVar(ctx, atom, sp[-1], opcode - OP_put_var); + sp--; + if (unlikely(ret < 0)) + goto exception; + } + BREAK; + + CASE(OP_put_var_strict): + { + int ret; + JSAtom atom; + atom = get_u32(pc); + pc += 4; + sf->cur_pc = pc; + + /* sp[-2] is JS_TRUE or JS_FALSE */ + if (unlikely(!JS_VALUE_GET_INT(sp[-2]))) { + JS_ThrowReferenceErrorNotDefined(ctx, atom); + goto exception; + } + ret = JS_SetGlobalVar(ctx, atom, sp[-1], 2); + sp -= 2; + if (unlikely(ret < 0)) + goto exception; + } + BREAK; + + CASE(OP_check_define_var): + { + JSAtom atom; + int flags; + atom = get_u32(pc); + flags = pc[4]; + pc += 5; + if (JS_CheckDefineGlobalVar(ctx, atom, flags)) + goto exception; + } + BREAK; + CASE(OP_define_var): + { + JSAtom atom; + int flags; + atom = get_u32(pc); + flags = pc[4]; + pc += 5; + if (JS_DefineGlobalVar(ctx, atom, flags)) + goto exception; + } + BREAK; + CASE(OP_define_func): + { + JSAtom atom; + int flags; + atom = get_u32(pc); + flags = pc[4]; + pc += 5; + if (JS_DefineGlobalFunction(ctx, atom, sp[-1], flags)) + goto exception; + JS_FreeValue(ctx, sp[-1]); + sp--; + } + BREAK; + + CASE(OP_get_loc): + { + int idx; + idx = get_u16(pc); + pc += 2; + sp[0] = js_dup(var_buf[idx]); + sp++; + } + BREAK; + CASE(OP_put_loc): + { + int idx; + idx = get_u16(pc); + pc += 2; + set_value(ctx, &var_buf[idx], sp[-1]); + sp--; + } + BREAK; + CASE(OP_set_loc): + { + int idx; + idx = get_u16(pc); + pc += 2; + set_value(ctx, &var_buf[idx], js_dup(sp[-1])); + } + BREAK; + CASE(OP_get_arg): + { + int idx; + idx = get_u16(pc); + pc += 2; + sp[0] = js_dup(arg_buf[idx]); + sp++; + } + BREAK; + CASE(OP_put_arg): + { + int idx; + idx = get_u16(pc); + pc += 2; + set_value(ctx, &arg_buf[idx], sp[-1]); + sp--; + } + BREAK; + CASE(OP_set_arg): + { + int idx; + idx = get_u16(pc); + pc += 2; + set_value(ctx, &arg_buf[idx], js_dup(sp[-1])); + } + BREAK; + + CASE(OP_get_loc8): *sp++ = js_dup(var_buf[*pc++]); BREAK; + CASE(OP_put_loc8): set_value(ctx, &var_buf[*pc++], *--sp); BREAK; + CASE(OP_set_loc8): set_value(ctx, &var_buf[*pc++], js_dup(sp[-1])); BREAK; + + // Observation: get_loc0 and get_loc1 are individually very + // frequent opcodes _and_ they are very often paired together, + // making them ideal candidates for opcode fusion. + CASE(OP_get_loc0_loc1): + *sp++ = js_dup(var_buf[0]); + *sp++ = js_dup(var_buf[1]); + BREAK; + + CASE(OP_get_loc0): *sp++ = js_dup(var_buf[0]); BREAK; + CASE(OP_get_loc1): *sp++ = js_dup(var_buf[1]); BREAK; + CASE(OP_get_loc2): *sp++ = js_dup(var_buf[2]); BREAK; + CASE(OP_get_loc3): *sp++ = js_dup(var_buf[3]); BREAK; + CASE(OP_put_loc0): set_value(ctx, &var_buf[0], *--sp); BREAK; + CASE(OP_put_loc1): set_value(ctx, &var_buf[1], *--sp); BREAK; + CASE(OP_put_loc2): set_value(ctx, &var_buf[2], *--sp); BREAK; + CASE(OP_put_loc3): set_value(ctx, &var_buf[3], *--sp); BREAK; + CASE(OP_set_loc0): set_value(ctx, &var_buf[0], js_dup(sp[-1])); BREAK; + CASE(OP_set_loc1): set_value(ctx, &var_buf[1], js_dup(sp[-1])); BREAK; + CASE(OP_set_loc2): set_value(ctx, &var_buf[2], js_dup(sp[-1])); BREAK; + CASE(OP_set_loc3): set_value(ctx, &var_buf[3], js_dup(sp[-1])); BREAK; + CASE(OP_get_arg0): *sp++ = js_dup(arg_buf[0]); BREAK; + CASE(OP_get_arg1): *sp++ = js_dup(arg_buf[1]); BREAK; + CASE(OP_get_arg2): *sp++ = js_dup(arg_buf[2]); BREAK; + CASE(OP_get_arg3): *sp++ = js_dup(arg_buf[3]); BREAK; + CASE(OP_put_arg0): set_value(ctx, &arg_buf[0], *--sp); BREAK; + CASE(OP_put_arg1): set_value(ctx, &arg_buf[1], *--sp); BREAK; + CASE(OP_put_arg2): set_value(ctx, &arg_buf[2], *--sp); BREAK; + CASE(OP_put_arg3): set_value(ctx, &arg_buf[3], *--sp); BREAK; + CASE(OP_set_arg0): set_value(ctx, &arg_buf[0], js_dup(sp[-1])); BREAK; + CASE(OP_set_arg1): set_value(ctx, &arg_buf[1], js_dup(sp[-1])); BREAK; + CASE(OP_set_arg2): set_value(ctx, &arg_buf[2], js_dup(sp[-1])); BREAK; + CASE(OP_set_arg3): set_value(ctx, &arg_buf[3], js_dup(sp[-1])); BREAK; + CASE(OP_get_var_ref0): *sp++ = js_dup(*var_refs[0]->pvalue); BREAK; + CASE(OP_get_var_ref1): *sp++ = js_dup(*var_refs[1]->pvalue); BREAK; + CASE(OP_get_var_ref2): *sp++ = js_dup(*var_refs[2]->pvalue); BREAK; + CASE(OP_get_var_ref3): *sp++ = js_dup(*var_refs[3]->pvalue); BREAK; + CASE(OP_put_var_ref0): set_value(ctx, var_refs[0]->pvalue, *--sp); BREAK; + CASE(OP_put_var_ref1): set_value(ctx, var_refs[1]->pvalue, *--sp); BREAK; + CASE(OP_put_var_ref2): set_value(ctx, var_refs[2]->pvalue, *--sp); BREAK; + CASE(OP_put_var_ref3): set_value(ctx, var_refs[3]->pvalue, *--sp); BREAK; + CASE(OP_set_var_ref0): set_value(ctx, var_refs[0]->pvalue, js_dup(sp[-1])); BREAK; + CASE(OP_set_var_ref1): set_value(ctx, var_refs[1]->pvalue, js_dup(sp[-1])); BREAK; + CASE(OP_set_var_ref2): set_value(ctx, var_refs[2]->pvalue, js_dup(sp[-1])); BREAK; + CASE(OP_set_var_ref3): set_value(ctx, var_refs[3]->pvalue, js_dup(sp[-1])); BREAK; + + CASE(OP_get_var_ref): + { + int idx; + JSValue val; + idx = get_u16(pc); + pc += 2; + val = *var_refs[idx]->pvalue; + sp[0] = js_dup(val); + sp++; + } + BREAK; + CASE(OP_put_var_ref): + { + int idx; + idx = get_u16(pc); + pc += 2; + set_value(ctx, var_refs[idx]->pvalue, sp[-1]); + sp--; + } + BREAK; + CASE(OP_set_var_ref): + { + int idx; + idx = get_u16(pc); + pc += 2; + set_value(ctx, var_refs[idx]->pvalue, js_dup(sp[-1])); + } + BREAK; + CASE(OP_get_var_ref_check): + { + int idx; + JSValue val; + idx = get_u16(pc); + pc += 2; + val = *var_refs[idx]->pvalue; + if (unlikely(JS_IsUninitialized(val))) { + JS_ThrowReferenceErrorUninitialized2(ctx, b, idx, TRUE); + goto exception; + } + sp[0] = js_dup(val); + sp++; + } + BREAK; + CASE(OP_put_var_ref_check): + { + int idx; + idx = get_u16(pc); + pc += 2; + if (unlikely(JS_IsUninitialized(*var_refs[idx]->pvalue))) { + JS_ThrowReferenceErrorUninitialized2(ctx, b, idx, TRUE); + goto exception; + } + set_value(ctx, var_refs[idx]->pvalue, sp[-1]); + sp--; + } + BREAK; + CASE(OP_put_var_ref_check_init): + { + int idx; + idx = get_u16(pc); + pc += 2; + if (unlikely(!JS_IsUninitialized(*var_refs[idx]->pvalue))) { + JS_ThrowReferenceErrorUninitialized2(ctx, b, idx, TRUE); + goto exception; + } + set_value(ctx, var_refs[idx]->pvalue, sp[-1]); + sp--; + } + BREAK; + CASE(OP_set_loc_uninitialized): + { + int idx; + idx = get_u16(pc); + pc += 2; + set_value(ctx, &var_buf[idx], JS_UNINITIALIZED); + } + BREAK; + CASE(OP_get_loc_check): + { + int idx; + idx = get_u16(pc); + pc += 2; + if (unlikely(JS_IsUninitialized(var_buf[idx]))) { + JS_ThrowReferenceErrorUninitialized2(caller_ctx, b, idx, + FALSE); + goto exception; + } + sp[0] = js_dup(var_buf[idx]); + sp++; + } + BREAK; + CASE(OP_put_loc_check): + { + int idx; + idx = get_u16(pc); + pc += 2; + if (unlikely(JS_IsUninitialized(var_buf[idx]))) { + JS_ThrowReferenceErrorUninitialized2(caller_ctx, b, idx, + FALSE); + goto exception; + } + set_value(ctx, &var_buf[idx], sp[-1]); + sp--; + } + BREAK; + CASE(OP_put_loc_check_init): + { + int idx; + idx = get_u16(pc); + pc += 2; + if (unlikely(!JS_IsUninitialized(var_buf[idx]))) { + JS_ThrowReferenceError(caller_ctx, + "'this' can be initialized only once"); + goto exception; + } + set_value(ctx, &var_buf[idx], sp[-1]); + sp--; + } + BREAK; + CASE(OP_close_loc): + { + int idx; + idx = get_u16(pc); + pc += 2; + close_lexical_var(ctx, sf, idx, FALSE); + } + BREAK; + + CASE(OP_make_loc_ref): + CASE(OP_make_arg_ref): + CASE(OP_make_var_ref_ref): + { + JSVarRef *var_ref; + JSProperty *pr; + JSAtom atom; + int idx; + atom = get_u32(pc); + idx = get_u16(pc + 4); + pc += 6; + *sp++ = JS_NewObjectProto(ctx, JS_NULL); + if (unlikely(JS_IsException(sp[-1]))) + goto exception; + if (opcode == OP_make_var_ref_ref) { + var_ref = var_refs[idx]; + var_ref->header.ref_count++; + } else { + var_ref = get_var_ref(ctx, sf, idx, opcode == OP_make_arg_ref); + if (!var_ref) + goto exception; + } + pr = add_property(ctx, JS_VALUE_GET_OBJ(sp[-1]), atom, + JS_PROP_WRITABLE | JS_PROP_VARREF); + if (!pr) { + free_var_ref(rt, var_ref); + goto exception; + } + pr->u.var_ref = var_ref; + *sp++ = JS_AtomToValue(ctx, atom); + } + BREAK; + CASE(OP_make_var_ref): + { + JSAtom atom; + atom = get_u32(pc); + pc += 4; + + if (JS_GetGlobalVarRef(ctx, atom, sp)) + goto exception; + sp += 2; + } + BREAK; + + CASE(OP_goto): + pc += (int32_t)get_u32(pc); + if (unlikely(js_poll_interrupts(ctx))) + goto exception; + BREAK; + CASE(OP_goto16): + pc += (int16_t)get_u16(pc); + if (unlikely(js_poll_interrupts(ctx))) + goto exception; + BREAK; + CASE(OP_goto8): + pc += (int8_t)pc[0]; + if (unlikely(js_poll_interrupts(ctx))) + goto exception; + BREAK; + CASE(OP_if_true): + { + int res; + JSValue op1; + + op1 = sp[-1]; + pc += 4; + if ((uint32_t)JS_VALUE_GET_TAG(op1) <= JS_TAG_UNDEFINED) { + res = JS_VALUE_GET_INT(op1); + } else { + res = JS_ToBoolFree(ctx, op1); + } + sp--; + if (res) { + pc += (int32_t)get_u32(pc - 4) - 4; + } + if (unlikely(js_poll_interrupts(ctx))) + goto exception; + } + BREAK; + CASE(OP_if_false): + { + int res; + JSValue op1; + + op1 = sp[-1]; + pc += 4; + if ((uint32_t)JS_VALUE_GET_TAG(op1) <= JS_TAG_UNDEFINED) { + res = JS_VALUE_GET_INT(op1); + } else { + res = JS_ToBoolFree(ctx, op1); + } + sp--; + if (!res) { + pc += (int32_t)get_u32(pc - 4) - 4; + } + if (unlikely(js_poll_interrupts(ctx))) + goto exception; + } + BREAK; + CASE(OP_if_true8): + { + int res; + JSValue op1; + + op1 = sp[-1]; + pc += 1; + if ((uint32_t)JS_VALUE_GET_TAG(op1) <= JS_TAG_UNDEFINED) { + res = JS_VALUE_GET_INT(op1); + } else { + res = JS_ToBoolFree(ctx, op1); + } + sp--; + if (res) { + pc += (int8_t)pc[-1] - 1; + } + if (unlikely(js_poll_interrupts(ctx))) + goto exception; + } + BREAK; + CASE(OP_if_false8): + { + int res; + JSValue op1; + + op1 = sp[-1]; + pc += 1; + if ((uint32_t)JS_VALUE_GET_TAG(op1) <= JS_TAG_UNDEFINED) { + res = JS_VALUE_GET_INT(op1); + } else { + res = JS_ToBoolFree(ctx, op1); + } + sp--; + if (!res) { + pc += (int8_t)pc[-1] - 1; + } + if (unlikely(js_poll_interrupts(ctx))) + goto exception; + } + BREAK; + CASE(OP_catch): + { + int32_t diff; + diff = get_u32(pc); + sp[0] = JS_NewCatchOffset(ctx, pc + diff - b->byte_code_buf); + sp++; + pc += 4; + } + BREAK; + CASE(OP_gosub): + { + int32_t diff; + diff = get_u32(pc); + /* XXX: should have a different tag to avoid security flaw */ + sp[0] = js_int32(pc + 4 - b->byte_code_buf); + sp++; + pc += diff; + } + BREAK; + CASE(OP_ret): + { + JSValue op1; + uint32_t pos; + op1 = sp[-1]; + if (unlikely(JS_VALUE_GET_TAG(op1) != JS_TAG_INT)) + goto ret_fail; + pos = JS_VALUE_GET_INT(op1); + if (unlikely(pos >= b->byte_code_len)) { + ret_fail: + JS_ThrowInternalError(ctx, "invalid ret value"); + goto exception; + } + sp--; + pc = b->byte_code_buf + pos; + } + BREAK; + + CASE(OP_for_in_start): + sf->cur_pc = pc; + if (js_for_in_start(ctx, sp)) + goto exception; + BREAK; + CASE(OP_for_in_next): + sf->cur_pc = pc; + if (js_for_in_next(ctx, sp)) + goto exception; + sp += 2; + BREAK; + CASE(OP_for_of_start): + sf->cur_pc = pc; + if (js_for_of_start(ctx, sp, FALSE)) + goto exception; + sp += 1; + *sp++ = JS_NewCatchOffset(ctx, 0); + BREAK; + CASE(OP_for_of_next): + { + int offset = -3 - pc[0]; + pc += 1; + sf->cur_pc = pc; + if (js_for_of_next(ctx, sp, offset)) + goto exception; + sp += 2; + } + BREAK; + CASE(OP_for_await_of_start): + sf->cur_pc = pc; + if (js_for_of_start(ctx, sp, TRUE)) + goto exception; + sp += 1; + *sp++ = JS_NewCatchOffset(ctx, 0); + BREAK; + CASE(OP_iterator_get_value_done): + sf->cur_pc = pc; + if (js_iterator_get_value_done(ctx, sp)) + goto exception; + sp += 1; + BREAK; + CASE(OP_iterator_check_object): + if (unlikely(!JS_IsObject(sp[-1]))) { + JS_ThrowTypeError(ctx, "iterator must return an object"); + goto exception; + } + BREAK; + + CASE(OP_iterator_close): + /* iter_obj next catch_offset -> */ + sp--; /* drop the catch offset to avoid getting caught by exception */ + JS_FreeValue(ctx, sp[-1]); /* drop the next method */ + sp--; + if (!JS_IsUndefined(sp[-1])) { + sf->cur_pc = pc; + if (JS_IteratorClose(ctx, sp[-1], FALSE)) + goto exception; + JS_FreeValue(ctx, sp[-1]); + } + sp--; + BREAK; + CASE(OP_nip_catch): + { + JSValue ret_val; + /* catch_offset ... ret_val -> ret_eval */ + ret_val = *--sp; + while (sp > stack_buf && + JS_VALUE_GET_TAG(sp[-1]) != JS_TAG_CATCH_OFFSET) { + JS_FreeValue(ctx, *--sp); + } + if (unlikely(sp == stack_buf)) { + JS_ThrowInternalError(ctx, "nip_catch"); + JS_FreeValue(ctx, ret_val); + goto exception; + } + sp[-1] = ret_val; + } + BREAK; + + CASE(OP_iterator_next): + /* stack: iter_obj next catch_offset val */ + { + JSValue ret; + sf->cur_pc = pc; + ret = JS_Call(ctx, sp[-3], sp[-4], 1, (sp - 1)); + if (JS_IsException(ret)) + goto exception; + JS_FreeValue(ctx, sp[-1]); + sp[-1] = ret; + } + BREAK; + + CASE(OP_iterator_call): + /* stack: iter_obj next catch_offset val */ + { + JSValue method, ret; + BOOL ret_flag; + int flags; + flags = *pc++; + sf->cur_pc = pc; + method = JS_GetProperty(ctx, sp[-4], (flags & 1) ? + JS_ATOM_throw : JS_ATOM_return); + if (JS_IsException(method)) + goto exception; + if (JS_IsUndefined(method) || JS_IsNull(method)) { + ret_flag = TRUE; + } else { + if (flags & 2) { + /* no argument */ + ret = JS_CallFree(ctx, method, sp[-4], + 0, NULL); + } else { + ret = JS_CallFree(ctx, method, sp[-4], + 1, (sp - 1)); + } + if (JS_IsException(ret)) + goto exception; + JS_FreeValue(ctx, sp[-1]); + sp[-1] = ret; + ret_flag = FALSE; + } + sp[0] = js_bool(ret_flag); + sp += 1; + } + BREAK; + + CASE(OP_lnot): + { + int res; + JSValue op1; + + op1 = sp[-1]; + if ((uint32_t)JS_VALUE_GET_TAG(op1) <= JS_TAG_UNDEFINED) { + res = JS_VALUE_GET_INT(op1) != 0; + } else { + res = JS_ToBoolFree(ctx, op1); + } + sp[-1] = js_bool(!res); + } + BREAK; + + CASE(OP_get_field): + { + JSValue val; + JSAtom atom; + JSInlineCacheUpdate icu; + atom = get_u32(pc); + pc += 4; + sf->cur_pc = pc; + icu = (JSInlineCacheUpdate){ic, INLINE_CACHE_MISS}; + val = JS_GetPropertyInternal2(ctx, sp[-1], atom, sp[-1], &icu, FALSE); + if (unlikely(JS_IsException(val))) + goto exception; + if (icu.offset != INLINE_CACHE_MISS) { + put_u8(pc - 5, OP_get_field_ic); + put_u32(pc - 4, icu.offset); + JS_FreeAtom(ctx, atom); + } + JS_FreeValue(ctx, sp[-1]); + sp[-1] = val; + } + BREAK; + + CASE(OP_get_field_ic): + { + JSValue val; + JSAtom atom; + uint32_t ic_offset; + JSInlineCacheUpdate icu; + ic_offset = get_u32(pc); + atom = get_ic_atom(ic, ic_offset); + pc += 4; + sf->cur_pc = pc; + icu = (JSInlineCacheUpdate){ic, ic_offset}; + val = JS_GetPropertyInternalWithIC(ctx, sp[-1], atom, sp[-1], &icu, FALSE); + if (unlikely(JS_IsException(val))) + goto exception; + assert(icu.offset == ic_offset); + JS_FreeValue(ctx, sp[-1]); + sp[-1] = val; + } + BREAK; + + CASE(OP_get_field2): + { + JSValue val; + JSAtom atom; + JSInlineCacheUpdate icu; + atom = get_u32(pc); + pc += 4; + sf->cur_pc = pc; + icu = (JSInlineCacheUpdate){ic, INLINE_CACHE_MISS}; + val = JS_GetPropertyInternal2(ctx, sp[-1], atom, sp[-1], &icu, FALSE); + if (unlikely(JS_IsException(val))) + goto exception; + if (icu.offset != INLINE_CACHE_MISS) { + put_u8(pc - 5, OP_get_field2_ic); + put_u32(pc - 4, icu.offset); + JS_FreeAtom(ctx, atom); + } + *sp++ = val; + } + BREAK; + + CASE(OP_get_field2_ic): + { + JSValue val; + JSAtom atom; + uint32_t ic_offset; + JSInlineCacheUpdate icu; + ic_offset = get_u32(pc); + atom = get_ic_atom(ic, ic_offset); + pc += 4; + sf->cur_pc = pc; + icu = (JSInlineCacheUpdate){ic, ic_offset}; + val = JS_GetPropertyInternalWithIC(ctx, sp[-1], atom, sp[-1], &icu, FALSE); + if (unlikely(JS_IsException(val))) + goto exception; + assert(icu.offset == ic_offset); + *sp++ = val; + } + BREAK; + + CASE(OP_put_field): + { + int ret; + JSAtom atom; + JSInlineCacheUpdate icu; + atom = get_u32(pc); + pc += 4; + sf->cur_pc = pc; + icu = (JSInlineCacheUpdate){ic, INLINE_CACHE_MISS}; + ret = JS_SetPropertyInternal2(ctx, + sp[-2], atom, + sp[-1], sp[-2], + JS_PROP_THROW_STRICT, &icu); + JS_FreeValue(ctx, sp[-2]); + sp -= 2; + if (unlikely(ret < 0)) + goto exception; + if (icu.offset != INLINE_CACHE_MISS) { + put_u8(pc - 5, OP_put_field_ic); + put_u32(pc - 4, icu.offset); + JS_FreeAtom(ctx, atom); + } + } + BREAK; + + CASE(OP_put_field_ic): + { + int ret; + JSAtom atom; + uint32_t ic_offset; + JSInlineCacheUpdate icu; + ic_offset = get_u32(pc); + atom = get_ic_atom(ic, ic_offset); + pc += 4; + sf->cur_pc = pc; + icu = (JSInlineCacheUpdate){ic, ic_offset}; + ret = JS_SetPropertyInternalWithIC(ctx, sp[-2], atom, sp[-1], + JS_PROP_THROW_STRICT, &icu); + JS_FreeValue(ctx, sp[-2]); + sp -= 2; + if (unlikely(ret < 0)) + goto exception; + assert(icu.offset == ic_offset); + } + BREAK; + + CASE(OP_private_symbol): + { + JSAtom atom; + JSValue val; + + atom = get_u32(pc); + pc += 4; + val = JS_NewSymbolFromAtom(ctx, atom, JS_ATOM_TYPE_PRIVATE); + if (JS_IsException(val)) + goto exception; + *sp++ = val; + } + BREAK; + + CASE(OP_get_private_field): + { + JSValue val; + sf->cur_pc = pc; + val = JS_GetPrivateField(ctx, sp[-2], sp[-1]); + JS_FreeValue(ctx, sp[-1]); + JS_FreeValue(ctx, sp[-2]); + sp[-2] = val; + sp--; + if (unlikely(JS_IsException(val))) + goto exception; + } + BREAK; + + CASE(OP_put_private_field): + { + int ret; + sf->cur_pc = pc; + ret = JS_SetPrivateField(ctx, sp[-3], sp[-1], sp[-2]); + JS_FreeValue(ctx, sp[-3]); + JS_FreeValue(ctx, sp[-1]); + sp -= 3; + if (unlikely(ret < 0)) + goto exception; + } + BREAK; + + CASE(OP_define_private_field): + { + int ret; + ret = JS_DefinePrivateField(ctx, sp[-3], sp[-2], sp[-1]); + JS_FreeValue(ctx, sp[-2]); + sp -= 2; + if (unlikely(ret < 0)) + goto exception; + } + BREAK; + + CASE(OP_define_field): + { + int ret; + JSAtom atom; + atom = get_u32(pc); + pc += 4; + + ret = JS_DefinePropertyValue(ctx, sp[-2], atom, sp[-1], + JS_PROP_C_W_E | JS_PROP_THROW); + sp--; + if (unlikely(ret < 0)) + goto exception; + } + BREAK; + + CASE(OP_set_name): + { + int ret; + JSAtom atom; + atom = get_u32(pc); + pc += 4; + + ret = JS_DefineObjectName(ctx, sp[-1], atom, JS_PROP_CONFIGURABLE); + if (unlikely(ret < 0)) + goto exception; + } + BREAK; + CASE(OP_set_name_computed): + { + int ret; + ret = JS_DefineObjectNameComputed(ctx, sp[-1], sp[-2], JS_PROP_CONFIGURABLE); + if (unlikely(ret < 0)) + goto exception; + } + BREAK; + CASE(OP_set_proto): + { + JSValue proto; + proto = sp[-1]; + if (JS_IsObject(proto) || JS_IsNull(proto)) { + if (JS_SetPrototypeInternal(ctx, sp[-2], proto, TRUE) < 0) + goto exception; + } + JS_FreeValue(ctx, proto); + sp--; + } + BREAK; + CASE(OP_set_home_object): + js_method_set_home_object(ctx, sp[-1], sp[-2]); + BREAK; + CASE(OP_define_method): + CASE(OP_define_method_computed): + { + JSValue getter, setter, value; + JSValue obj; + JSAtom atom; + int flags, ret, op_flags; + BOOL is_computed; +#define OP_DEFINE_METHOD_METHOD 0 +#define OP_DEFINE_METHOD_GETTER 1 +#define OP_DEFINE_METHOD_SETTER 2 +#define OP_DEFINE_METHOD_ENUMERABLE 4 + + is_computed = (opcode == OP_define_method_computed); + if (is_computed) { + atom = JS_ValueToAtom(ctx, sp[-2]); + if (unlikely(atom == JS_ATOM_NULL)) + goto exception; + opcode += OP_define_method - OP_define_method_computed; + } else { + atom = get_u32(pc); + pc += 4; + } + op_flags = *pc++; + + obj = sp[-2 - is_computed]; + flags = JS_PROP_HAS_CONFIGURABLE | JS_PROP_CONFIGURABLE | + JS_PROP_HAS_ENUMERABLE | JS_PROP_THROW; + if (op_flags & OP_DEFINE_METHOD_ENUMERABLE) + flags |= JS_PROP_ENUMERABLE; + op_flags &= 3; + value = JS_UNDEFINED; + getter = JS_UNDEFINED; + setter = JS_UNDEFINED; + if (op_flags == OP_DEFINE_METHOD_METHOD) { + value = sp[-1]; + flags |= JS_PROP_HAS_VALUE | JS_PROP_HAS_WRITABLE | JS_PROP_WRITABLE; + } else if (op_flags == OP_DEFINE_METHOD_GETTER) { + getter = sp[-1]; + flags |= JS_PROP_HAS_GET; + } else { + setter = sp[-1]; + flags |= JS_PROP_HAS_SET; + } + ret = js_method_set_properties(ctx, sp[-1], atom, flags, obj); + if (ret >= 0) { + ret = JS_DefineProperty(ctx, obj, atom, value, + getter, setter, flags); + } + JS_FreeValue(ctx, sp[-1]); + if (is_computed) { + JS_FreeAtom(ctx, atom); + JS_FreeValue(ctx, sp[-2]); + } + sp -= 1 + is_computed; + if (unlikely(ret < 0)) + goto exception; + } + BREAK; + + CASE(OP_define_class): + CASE(OP_define_class_computed): + { + int class_flags; + JSAtom atom; + + atom = get_u32(pc); + class_flags = pc[4]; + pc += 5; + if (js_op_define_class(ctx, sp, atom, class_flags, + var_refs, sf, + (opcode == OP_define_class_computed)) < 0) + goto exception; + } + BREAK; + + CASE(OP_get_array_el): + { + JSValue val; + + sf->cur_pc = pc; + val = JS_GetPropertyValue(ctx, sp[-2], sp[-1]); + JS_FreeValue(ctx, sp[-2]); + sp[-2] = val; + sp--; + if (unlikely(JS_IsException(val))) + goto exception; + } + BREAK; + + CASE(OP_get_array_el2): + { + JSValue val; + + sf->cur_pc = pc; + val = JS_GetPropertyValue(ctx, sp[-2], sp[-1]); + sp[-1] = val; + if (unlikely(JS_IsException(val))) + goto exception; + } + BREAK; + + CASE(OP_get_ref_value): + { + JSValue val; + sf->cur_pc = pc; + if (unlikely(JS_IsUndefined(sp[-2]))) { + JSAtom atom = JS_ValueToAtom(ctx, sp[-1]); + if (atom != JS_ATOM_NULL) { + JS_ThrowReferenceErrorNotDefined(ctx, atom); + JS_FreeAtom(ctx, atom); + } + goto exception; + } + val = JS_GetPropertyValue(ctx, sp[-2], + js_dup(sp[-1])); + if (unlikely(JS_IsException(val))) + goto exception; + sp[0] = val; + sp++; + } + BREAK; + + CASE(OP_get_super_value): + { + JSValue val; + JSAtom atom; + sf->cur_pc = pc; + atom = JS_ValueToAtom(ctx, sp[-1]); + if (unlikely(atom == JS_ATOM_NULL)) + goto exception; + val = JS_GetPropertyInternal2(ctx, sp[-2], atom, sp[-3], NULL, FALSE); + JS_FreeAtom(ctx, atom); + if (unlikely(JS_IsException(val))) + goto exception; + JS_FreeValue(ctx, sp[-1]); + JS_FreeValue(ctx, sp[-2]); + JS_FreeValue(ctx, sp[-3]); + sp[-3] = val; + sp -= 2; + } + BREAK; + + CASE(OP_put_array_el): + { + int ret; + sf->cur_pc = pc; + ret = JS_SetPropertyValue(ctx, sp[-3], sp[-2], sp[-1], JS_PROP_THROW_STRICT); + JS_FreeValue(ctx, sp[-3]); + sp -= 3; + if (unlikely(ret < 0)) + goto exception; + } + BREAK; + + CASE(OP_put_ref_value): + { + int ret, flags; + sf->cur_pc = pc; + flags = JS_PROP_THROW_STRICT; + if (unlikely(JS_IsUndefined(sp[-3]))) { + if (is_strict_mode(ctx)) { + JSAtom atom = JS_ValueToAtom(ctx, sp[-2]); + if (atom != JS_ATOM_NULL) { + JS_ThrowReferenceErrorNotDefined(ctx, atom); + JS_FreeAtom(ctx, atom); + } + goto exception; + } else { + sp[-3] = js_dup(ctx->global_obj); + } + } else { + if (is_strict_mode(ctx)) + flags |= JS_PROP_NO_ADD; + } + ret = JS_SetPropertyValue(ctx, sp[-3], sp[-2], sp[-1], flags); + JS_FreeValue(ctx, sp[-3]); + sp -= 3; + if (unlikely(ret < 0)) + goto exception; + } + BREAK; + + CASE(OP_put_super_value): + { + int ret; + JSAtom atom; + sf->cur_pc = pc; + if (JS_VALUE_GET_TAG(sp[-3]) != JS_TAG_OBJECT) { + JS_ThrowTypeErrorNotAnObject(ctx); + goto exception; + } + atom = JS_ValueToAtom(ctx, sp[-2]); + if (unlikely(atom == JS_ATOM_NULL)) + goto exception; + ret = JS_SetPropertyInternal2(ctx, + sp[-3], atom, + sp[-1], sp[-4], + JS_PROP_THROW_STRICT, NULL); + JS_FreeAtom(ctx, atom); + JS_FreeValue(ctx, sp[-4]); + JS_FreeValue(ctx, sp[-3]); + JS_FreeValue(ctx, sp[-2]); + sp -= 4; + if (ret < 0) + goto exception; + } + BREAK; + + CASE(OP_define_array_el): + { + int ret; + ret = JS_DefinePropertyValueValue(ctx, sp[-3], js_dup(sp[-2]), sp[-1], + JS_PROP_C_W_E | JS_PROP_THROW); + sp -= 1; + if (unlikely(ret < 0)) + goto exception; + } + BREAK; + + CASE(OP_append): /* array pos enumobj -- array pos */ + { + sf->cur_pc = pc; + if (js_append_enumerate(ctx, sp)) + goto exception; + JS_FreeValue(ctx, *--sp); + } + BREAK; + + CASE(OP_copy_data_properties): /* target source excludeList */ + { + /* stack offsets (-1 based): + 2 bits for target, + 3 bits for source, + 2 bits for exclusionList */ + int mask; + + mask = *pc++; + sf->cur_pc = pc; + if (JS_CopyDataProperties(ctx, sp[-1 - (mask & 3)], + sp[-1 - ((mask >> 2) & 7)], + sp[-1 - ((mask >> 5) & 7)], 0)) + goto exception; + } + BREAK; + + CASE(OP_add): + { + JSValue op1, op2; + op1 = sp[-2]; + op2 = sp[-1]; + if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) { + int64_t r; + r = (int64_t)JS_VALUE_GET_INT(op1) + JS_VALUE_GET_INT(op2); + if (unlikely((int)r != r)) + goto add_slow; + sp[-2] = js_int32(r); + sp--; + } else if (JS_VALUE_IS_BOTH_FLOAT(op1, op2)) { + sp[-2] = js_float64(JS_VALUE_GET_FLOAT64(op1) + + JS_VALUE_GET_FLOAT64(op2)); + sp--; + } else { + add_slow: + sf->cur_pc = pc; + if (js_add_slow(ctx, sp)) + goto exception; + sp--; + } + } + BREAK; + CASE(OP_add_loc): + { + JSValue *pv; + int idx; + idx = *pc; + pc += 1; + + pv = &var_buf[idx]; + if (likely(JS_VALUE_IS_BOTH_INT(*pv, sp[-1]))) { + int64_t r; + r = (int64_t)JS_VALUE_GET_INT(*pv) + + JS_VALUE_GET_INT(sp[-1]); + if (unlikely((int)r != r)) + goto add_loc_slow; + *pv = js_int32(r); + sp--; + } else if (JS_VALUE_GET_TAG(*pv) == JS_TAG_STRING) { + JSValue op1; + op1 = sp[-1]; + sp--; + sf->cur_pc = pc; + op1 = JS_ToPrimitiveFree(ctx, op1, HINT_NONE); + if (JS_IsException(op1)) + goto exception; + op1 = JS_ConcatString(ctx, js_dup(*pv), op1); + if (JS_IsException(op1)) + goto exception; + set_value(ctx, pv, op1); + } else { + JSValue ops[2]; + add_loc_slow: + /* In case of exception, js_add_slow frees ops[0] + and ops[1], so we must duplicate *pv */ + sf->cur_pc = pc; + ops[0] = js_dup(*pv); + ops[1] = sp[-1]; + sp--; + if (js_add_slow(ctx, ops + 2)) + goto exception; + set_value(ctx, pv, ops[0]); + } + } + BREAK; + CASE(OP_sub): + { + JSValue op1, op2; + op1 = sp[-2]; + op2 = sp[-1]; + if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) { + int64_t r; + r = (int64_t)JS_VALUE_GET_INT(op1) - JS_VALUE_GET_INT(op2); + if (unlikely((int)r != r)) + goto binary_arith_slow; + sp[-2] = js_int32(r); + sp--; + } else if (JS_VALUE_IS_BOTH_FLOAT(op1, op2)) { + sp[-2] = js_float64(JS_VALUE_GET_FLOAT64(op1) - + JS_VALUE_GET_FLOAT64(op2)); + sp--; + } else { + goto binary_arith_slow; + } + } + BREAK; + CASE(OP_mul): + { + JSValue op1, op2; + double d; + op1 = sp[-2]; + op2 = sp[-1]; + if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) { + int32_t v1, v2; + int64_t r; + v1 = JS_VALUE_GET_INT(op1); + v2 = JS_VALUE_GET_INT(op2); + r = (int64_t)v1 * v2; + if (unlikely((int)r != r)) { + d = (double)r; + goto mul_fp_res; + } + /* need to test zero case for -0 result */ + if (unlikely(r == 0 && (v1 | v2) < 0)) { + d = -0.0; + goto mul_fp_res; + } + sp[-2] = js_int32(r); + sp--; + } else if (JS_VALUE_IS_BOTH_FLOAT(op1, op2)) { + d = JS_VALUE_GET_FLOAT64(op1) * JS_VALUE_GET_FLOAT64(op2); + mul_fp_res: + sp[-2] = js_float64(d); + sp--; + } else { + goto binary_arith_slow; + } + } + BREAK; + CASE(OP_div): + { + JSValue op1, op2; + op1 = sp[-2]; + op2 = sp[-1]; + if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) { + int v1, v2; + v1 = JS_VALUE_GET_INT(op1); + v2 = JS_VALUE_GET_INT(op2); + sp[-2] = js_number((double)v1 / (double)v2); + sp--; + } else { + goto binary_arith_slow; + } + } + BREAK; + CASE(OP_mod): + { + JSValue op1, op2; + op1 = sp[-2]; + op2 = sp[-1]; + if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) { + int v1, v2, r; + v1 = JS_VALUE_GET_INT(op1); + v2 = JS_VALUE_GET_INT(op2); + /* We must avoid v2 = 0, v1 = INT32_MIN and v2 = + -1 and the cases where the result is -0. */ + if (unlikely(v1 < 0 || v2 <= 0)) + goto binary_arith_slow; + r = v1 % v2; + sp[-2] = js_int32(r); + sp--; + } else { + goto binary_arith_slow; + } + } + BREAK; + CASE(OP_pow): + binary_arith_slow: + sf->cur_pc = pc; + if (js_binary_arith_slow(ctx, sp, opcode)) + goto exception; + sp--; + BREAK; + + CASE(OP_plus): + { + JSValue op1; + uint32_t tag; + op1 = sp[-1]; + tag = JS_VALUE_GET_TAG(op1); + if (tag == JS_TAG_INT || JS_TAG_IS_FLOAT64(tag)) { + } else { + sf->cur_pc = pc; + if (js_unary_arith_slow(ctx, sp, opcode)) + goto exception; + } + } + BREAK; + CASE(OP_neg): + { + JSValue op1; + uint32_t tag; + int val; + double d; + op1 = sp[-1]; + tag = JS_VALUE_GET_TAG(op1); + if (tag == JS_TAG_INT) { + val = JS_VALUE_GET_INT(op1); + /* Note: -0 cannot be expressed as integer */ + if (unlikely(val == 0)) { + d = -0.0; + goto neg_fp_res; + } + if (unlikely(val == INT32_MIN)) { + d = -(double)val; + goto neg_fp_res; + } + sp[-1] = js_int32(-val); + } else if (JS_TAG_IS_FLOAT64(tag)) { + d = -JS_VALUE_GET_FLOAT64(op1); + neg_fp_res: + sp[-1] = js_float64(d); + } else { + sf->cur_pc = pc; + if (js_unary_arith_slow(ctx, sp, opcode)) + goto exception; + } + } + BREAK; + CASE(OP_inc): + { + JSValue op1; + int val; + op1 = sp[-1]; + if (JS_VALUE_GET_TAG(op1) == JS_TAG_INT) { + val = JS_VALUE_GET_INT(op1); + if (unlikely(val == INT32_MAX)) + goto inc_slow; + sp[-1] = js_int32(val + 1); + } else { + inc_slow: + sf->cur_pc = pc; + if (js_unary_arith_slow(ctx, sp, opcode)) + goto exception; + } + } + BREAK; + CASE(OP_dec): + { + JSValue op1; + int val; + op1 = sp[-1]; + if (JS_VALUE_GET_TAG(op1) == JS_TAG_INT) { + val = JS_VALUE_GET_INT(op1); + if (unlikely(val == INT32_MIN)) + goto dec_slow; + sp[-1] = js_int32(val - 1); + } else { + dec_slow: + sf->cur_pc = pc; + if (js_unary_arith_slow(ctx, sp, opcode)) + goto exception; + } + } + BREAK; + CASE(OP_post_inc): + CASE(OP_post_dec): + sf->cur_pc = pc; + if (js_post_inc_slow(ctx, sp, opcode)) + goto exception; + sp++; + BREAK; + CASE(OP_inc_loc): + { + JSValue op1; + int val; + int idx; + idx = *pc; + pc += 1; + + op1 = var_buf[idx]; + if (JS_VALUE_GET_TAG(op1) == JS_TAG_INT) { + val = JS_VALUE_GET_INT(op1); + if (unlikely(val == INT32_MAX)) + goto inc_loc_slow; + var_buf[idx] = js_int32(val + 1); + } else { + inc_loc_slow: + sf->cur_pc = pc; + /* must duplicate otherwise the variable value may + be destroyed before JS code accesses it */ + op1 = js_dup(op1); + if (js_unary_arith_slow(ctx, &op1 + 1, OP_inc)) + goto exception; + set_value(ctx, &var_buf[idx], op1); + } + } + BREAK; + CASE(OP_dec_loc): + { + JSValue op1; + int val; + int idx; + idx = *pc; + pc += 1; + + op1 = var_buf[idx]; + if (JS_VALUE_GET_TAG(op1) == JS_TAG_INT) { + val = JS_VALUE_GET_INT(op1); + if (unlikely(val == INT32_MIN)) + goto dec_loc_slow; + var_buf[idx] = js_int32(val - 1); + } else { + dec_loc_slow: + sf->cur_pc = pc; + /* must duplicate otherwise the variable value may + be destroyed before JS code accesses it */ + op1 = js_dup(op1); + if (js_unary_arith_slow(ctx, &op1 + 1, OP_dec)) + goto exception; + set_value(ctx, &var_buf[idx], op1); + } + } + BREAK; + CASE(OP_not): + { + JSValue op1; + op1 = sp[-1]; + if (JS_VALUE_GET_TAG(op1) == JS_TAG_INT) { + sp[-1] = js_int32(~JS_VALUE_GET_INT(op1)); + } else { + sf->cur_pc = pc; + if (js_not_slow(ctx, sp)) + goto exception; + } + } + BREAK; + + CASE(OP_shl): + { + JSValue op1, op2; + op1 = sp[-2]; + op2 = sp[-1]; + if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) { + uint32_t v1, v2; + v1 = JS_VALUE_GET_INT(op1); + v2 = JS_VALUE_GET_INT(op2) & 0x1f; + sp[-2] = js_int32(v1 << v2); + sp--; + } else { + sf->cur_pc = pc; + if (js_binary_logic_slow(ctx, sp, opcode)) + goto exception; + sp--; + } + } + BREAK; + CASE(OP_shr): + { + JSValue op1, op2; + op1 = sp[-2]; + op2 = sp[-1]; + if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) { + uint32_t v2; + v2 = JS_VALUE_GET_INT(op2); + v2 &= 0x1f; + sp[-2] = js_uint32((uint32_t)JS_VALUE_GET_INT(op1) >> v2); + sp--; + } else { + sf->cur_pc = pc; + if (js_shr_slow(ctx, sp)) + goto exception; + sp--; + } + } + BREAK; + CASE(OP_sar): + { + JSValue op1, op2; + op1 = sp[-2]; + op2 = sp[-1]; + if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) { + uint32_t v2; + v2 = JS_VALUE_GET_INT(op2); + if (unlikely(v2 > 0x1f)) { + v2 &= 0x1f; + } + sp[-2] = js_int32((int)JS_VALUE_GET_INT(op1) >> v2); + sp--; + } else { + sf->cur_pc = pc; + if (js_binary_logic_slow(ctx, sp, opcode)) + goto exception; + sp--; + } + } + BREAK; + CASE(OP_and): + { + JSValue op1, op2; + op1 = sp[-2]; + op2 = sp[-1]; + if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) { + sp[-2] = js_int32(JS_VALUE_GET_INT(op1) & JS_VALUE_GET_INT(op2)); + sp--; + } else { + sf->cur_pc = pc; + if (js_binary_logic_slow(ctx, sp, opcode)) + goto exception; + sp--; + } + } + BREAK; + CASE(OP_or): + { + JSValue op1, op2; + op1 = sp[-2]; + op2 = sp[-1]; + if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) { + sp[-2] = js_int32(JS_VALUE_GET_INT(op1) | JS_VALUE_GET_INT(op2)); + sp--; + } else { + sf->cur_pc = pc; + if (js_binary_logic_slow(ctx, sp, opcode)) + goto exception; + sp--; + } + } + BREAK; + CASE(OP_xor): + { + JSValue op1, op2; + op1 = sp[-2]; + op2 = sp[-1]; + if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) { + sp[-2] = js_int32(JS_VALUE_GET_INT(op1) ^ JS_VALUE_GET_INT(op2)); + sp--; + } else { + sf->cur_pc = pc; + if (js_binary_logic_slow(ctx, sp, opcode)) + goto exception; + sp--; + } + } + BREAK; + + +#define OP_CMP(opcode, binary_op, slow_call) \ + CASE(opcode): \ + { \ + JSValue op1, op2; \ + op1 = sp[-2]; \ + op2 = sp[-1]; \ + if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) { \ + sp[-2] = js_bool(JS_VALUE_GET_INT(op1) binary_op JS_VALUE_GET_INT(op2)); \ + sp--; \ + } else { \ + sf->cur_pc = pc; \ + if (slow_call) \ + goto exception; \ + sp--; \ + } \ + } \ + BREAK + + OP_CMP(OP_lt, <, js_relational_slow(ctx, sp, opcode)); + OP_CMP(OP_lte, <=, js_relational_slow(ctx, sp, opcode)); + OP_CMP(OP_gt, >, js_relational_slow(ctx, sp, opcode)); + OP_CMP(OP_gte, >=, js_relational_slow(ctx, sp, opcode)); + OP_CMP(OP_eq, ==, js_eq_slow(ctx, sp, 0)); + OP_CMP(OP_neq, !=, js_eq_slow(ctx, sp, 1)); + OP_CMP(OP_strict_eq, ==, js_strict_eq_slow(ctx, sp, 0)); + OP_CMP(OP_strict_neq, !=, js_strict_eq_slow(ctx, sp, 1)); + + CASE(OP_in): + sf->cur_pc = pc; + if (js_operator_in(ctx, sp)) + goto exception; + sp--; + BREAK; + CASE(OP_private_in): + if (js_operator_private_in(ctx, sp)) + goto exception; + sp--; + BREAK; + CASE(OP_instanceof): + sf->cur_pc = pc; + if (js_operator_instanceof(ctx, sp)) + goto exception; + sp--; + BREAK; + CASE(OP_typeof): + { + JSValue op1; + JSAtom atom; + + op1 = sp[-1]; + atom = js_operator_typeof(ctx, op1); + JS_FreeValue(ctx, op1); + sp[-1] = JS_AtomToString(ctx, atom); + } + BREAK; + CASE(OP_delete): + sf->cur_pc = pc; + if (js_operator_delete(ctx, sp)) + goto exception; + sp--; + BREAK; + CASE(OP_delete_var): + { + JSAtom atom; + int ret; + + atom = get_u32(pc); + pc += 4; + + sf->cur_pc = pc; + ret = JS_DeleteProperty(ctx, ctx->global_obj, atom, 0); + if (unlikely(ret < 0)) + goto exception; + *sp++ = js_bool(ret); + } + BREAK; + + CASE(OP_to_object): + if (JS_VALUE_GET_TAG(sp[-1]) != JS_TAG_OBJECT) { + sf->cur_pc = pc; + ret_val = JS_ToObject(ctx, sp[-1]); + if (JS_IsException(ret_val)) + goto exception; + JS_FreeValue(ctx, sp[-1]); + sp[-1] = ret_val; + } + BREAK; + + CASE(OP_to_propkey): + switch (JS_VALUE_GET_TAG(sp[-1])) { + case JS_TAG_INT: + case JS_TAG_STRING: + case JS_TAG_SYMBOL: + break; + default: + sf->cur_pc = pc; + ret_val = JS_ToPropertyKey(ctx, sp[-1]); + if (JS_IsException(ret_val)) + goto exception; + JS_FreeValue(ctx, sp[-1]); + sp[-1] = ret_val; + break; + } + BREAK; + + CASE(OP_to_propkey2): + /* must be tested first */ + if (unlikely(JS_IsUndefined(sp[-2]) || JS_IsNull(sp[-2]))) { + JS_ThrowTypeError(ctx, "value has no property"); + goto exception; + } + switch (JS_VALUE_GET_TAG(sp[-1])) { + case JS_TAG_INT: + case JS_TAG_STRING: + case JS_TAG_SYMBOL: + break; + default: + sf->cur_pc = pc; + ret_val = JS_ToPropertyKey(ctx, sp[-1]); + if (JS_IsException(ret_val)) + goto exception; + JS_FreeValue(ctx, sp[-1]); + sp[-1] = ret_val; + break; + } + BREAK; + CASE(OP_with_get_var): + CASE(OP_with_put_var): + CASE(OP_with_delete_var): + CASE(OP_with_make_ref): + CASE(OP_with_get_ref): + CASE(OP_with_get_ref_undef): + { + JSAtom atom; + int32_t diff; + JSValue obj, val; + int ret, is_with; + atom = get_u32(pc); + diff = get_u32(pc + 4); + is_with = pc[8]; + pc += 9; + sf->cur_pc = pc; + + obj = sp[-1]; + ret = JS_HasProperty(ctx, obj, atom); + if (unlikely(ret < 0)) + goto exception; + if (ret) { + if (is_with) { + ret = js_has_unscopable(ctx, obj, atom); + if (unlikely(ret < 0)) + goto exception; + if (ret) + goto no_with; + } + switch (opcode) { + case OP_with_get_var: + val = JS_GetProperty(ctx, obj, atom); + if (unlikely(JS_IsException(val))) + goto exception; + set_value(ctx, &sp[-1], val); + break; + case OP_with_put_var: + /* XXX: check if strict mode */ + ret = JS_SetPropertyInternal(ctx, obj, atom, sp[-2], + JS_PROP_THROW_STRICT); + JS_FreeValue(ctx, sp[-1]); + sp -= 2; + if (unlikely(ret < 0)) + goto exception; + break; + case OP_with_delete_var: + ret = JS_DeleteProperty(ctx, obj, atom, 0); + if (unlikely(ret < 0)) + goto exception; + JS_FreeValue(ctx, sp[-1]); + sp[-1] = js_bool(ret); + break; + case OP_with_make_ref: + /* produce a pair object/propname on the stack */ + *sp++ = JS_AtomToValue(ctx, atom); + break; + case OP_with_get_ref: + /* produce a pair object/method on the stack */ + val = JS_GetProperty(ctx, obj, atom); + if (unlikely(JS_IsException(val))) + goto exception; + *sp++ = val; + break; + case OP_with_get_ref_undef: + /* produce a pair undefined/function on the stack */ + val = JS_GetProperty(ctx, obj, atom); + if (unlikely(JS_IsException(val))) + goto exception; + JS_FreeValue(ctx, sp[-1]); + sp[-1] = JS_UNDEFINED; + *sp++ = val; + break; + } + pc += diff - 5; + } else { + no_with: + /* if not jumping, drop the object argument */ + JS_FreeValue(ctx, sp[-1]); + sp--; + } + } + BREAK; + + CASE(OP_await): + ret_val = js_int32(FUNC_RET_AWAIT); + goto done_generator; + CASE(OP_yield): + ret_val = js_int32(FUNC_RET_YIELD); + goto done_generator; + CASE(OP_yield_star): + CASE(OP_async_yield_star): + ret_val = js_int32(FUNC_RET_YIELD_STAR); + goto done_generator; + CASE(OP_return_async): + CASE(OP_initial_yield): + ret_val = JS_UNDEFINED; + goto done_generator; + + CASE(OP_nop): + BREAK; + CASE(OP_is_undefined_or_null): + if (JS_VALUE_GET_TAG(sp[-1]) == JS_TAG_UNDEFINED || + JS_VALUE_GET_TAG(sp[-1]) == JS_TAG_NULL) { + goto set_true; + } else { + goto free_and_set_false; + } + CASE(OP_is_undefined): + if (JS_VALUE_GET_TAG(sp[-1]) == JS_TAG_UNDEFINED) { + goto set_true; + } else { + goto free_and_set_false; + } + CASE(OP_is_null): + if (JS_VALUE_GET_TAG(sp[-1]) == JS_TAG_NULL) { + goto set_true; + } else { + goto free_and_set_false; + } + /* XXX: could merge to a single opcode */ + CASE(OP_typeof_is_undefined): + /* different from OP_is_undefined because of isHTMLDDA */ + if (js_operator_typeof(ctx, sp[-1]) == JS_ATOM_undefined) { + goto free_and_set_true; + } else { + goto free_and_set_false; + } + CASE(OP_typeof_is_function): + if (js_operator_typeof(ctx, sp[-1]) == JS_ATOM_function) { + goto free_and_set_true; + } else { + goto free_and_set_false; + } + free_and_set_true: + JS_FreeValue(ctx, sp[-1]); + set_true: + sp[-1] = JS_TRUE; + BREAK; + free_and_set_false: + JS_FreeValue(ctx, sp[-1]); + sp[-1] = JS_FALSE; + BREAK; + CASE(OP_invalid): + DEFAULT: + JS_ThrowInternalError(ctx, "invalid opcode: pc=%u opcode=0x%02x", + (int)(pc - b->byte_code_buf - 1), opcode); + goto exception; + } + } + exception: + if (is_backtrace_needed(ctx, rt->current_exception)) { + /* add the backtrace information now (it is not done + before if the exception happens in a bytecode + operation */ + sf->cur_pc = pc; + build_backtrace(ctx, rt->current_exception, NULL, 0, 0, 0); + } + if (!JS_IsUncatchableError(ctx, rt->current_exception)) { + while (sp > stack_buf) { + JSValue val = *--sp; + JS_FreeValue(ctx, val); + if (JS_VALUE_GET_TAG(val) == JS_TAG_CATCH_OFFSET) { + int pos = JS_VALUE_GET_INT(val); + if (pos == 0) { + /* enumerator: close it with a throw */ + JS_FreeValue(ctx, sp[-1]); /* drop the next method */ + sp--; + JS_IteratorClose(ctx, sp[-1], TRUE); + } else { + *sp++ = rt->current_exception; + rt->current_exception = JS_UNINITIALIZED; + pc = b->byte_code_buf + pos; + goto restart; + } + } + } + } + ret_val = JS_EXCEPTION; + /* the local variables are freed by the caller in the generator + case. Hence the label 'done' should never be reached in a + generator function. */ + if (b->func_kind != JS_FUNC_NORMAL) { + done_generator: + sf->cur_pc = pc; + sf->cur_sp = sp; + } else { + done: + if (unlikely(!list_empty(&sf->var_ref_list))) { + /* variable references reference the stack: must close them */ + close_var_refs(rt, sf); + } + /* free the local variables and stack */ + for(pval = local_buf; pval < sp; pval++) { + JS_FreeValue(ctx, *pval); + } + } + rt->current_stack_frame = sf->prev_frame; + return ret_val; +} + +JSValue JS_Call(JSContext *ctx, JSValue func_obj, JSValue this_obj, + int argc, JSValue *argv) +{ + return JS_CallInternal(ctx, func_obj, this_obj, JS_UNDEFINED, + argc, argv, JS_CALL_FLAG_COPY_ARGV); +} + +static JSValue JS_CallFree(JSContext *ctx, JSValue func_obj, JSValue this_obj, + int argc, JSValue *argv) +{ + JSValue res = JS_CallInternal(ctx, func_obj, this_obj, JS_UNDEFINED, + argc, argv, JS_CALL_FLAG_COPY_ARGV); + JS_FreeValue(ctx, func_obj); + return res; +} + +/* warning: the refcount of the context is not incremented. Return + NULL in case of exception (case of revoked proxy only) */ +static JSContext *JS_GetFunctionRealm(JSContext *ctx, JSValue func_obj) +{ + JSObject *p; + JSContext *realm; + + if (JS_VALUE_GET_TAG(func_obj) != JS_TAG_OBJECT) + return ctx; + p = JS_VALUE_GET_OBJ(func_obj); + switch(p->class_id) { + case JS_CLASS_C_FUNCTION: + realm = p->u.cfunc.realm; + break; + case JS_CLASS_BYTECODE_FUNCTION: + case JS_CLASS_GENERATOR_FUNCTION: + case JS_CLASS_ASYNC_FUNCTION: + case JS_CLASS_ASYNC_GENERATOR_FUNCTION: + { + JSFunctionBytecode *b; + b = p->u.func.function_bytecode; + realm = b->realm; + } + break; + case JS_CLASS_PROXY: + { + JSProxyData *s = p->u.opaque; + if (!s) + return ctx; + if (s->is_revoked) { + JS_ThrowTypeErrorRevokedProxy(ctx); + return NULL; + } else { + realm = JS_GetFunctionRealm(ctx, s->target); + } + } + break; + case JS_CLASS_BOUND_FUNCTION: + { + JSBoundFunction *bf = p->u.bound_function; + realm = JS_GetFunctionRealm(ctx, bf->func_obj); + } + break; + default: + realm = ctx; + break; + } + return realm; +} + +static JSValue js_create_from_ctor(JSContext *ctx, JSValue ctor, + int class_id) +{ + JSValue proto, obj; + JSContext *realm; + + if (JS_IsUndefined(ctor)) { + proto = js_dup(ctx->class_proto[class_id]); + } else { + proto = JS_GetProperty(ctx, ctor, JS_ATOM_prototype); + if (JS_IsException(proto)) + return proto; + if (!JS_IsObject(proto)) { + JS_FreeValue(ctx, proto); + realm = JS_GetFunctionRealm(ctx, ctor); + if (!realm) + return JS_EXCEPTION; + proto = js_dup(realm->class_proto[class_id]); + } + } + obj = JS_NewObjectProtoClass(ctx, proto, class_id); + JS_FreeValue(ctx, proto); + return obj; +} + +/* argv[] is modified if (flags & JS_CALL_FLAG_COPY_ARGV) = 0. */ +static JSValue JS_CallConstructorInternal(JSContext *ctx, + JSValue func_obj, + JSValue new_target, + int argc, JSValue *argv, int flags) +{ + JSObject *p; + JSFunctionBytecode *b; + + if (js_poll_interrupts(ctx)) + return JS_EXCEPTION; + flags |= JS_CALL_FLAG_CONSTRUCTOR; + if (unlikely(JS_VALUE_GET_TAG(func_obj) != JS_TAG_OBJECT)) + goto not_a_function; + p = JS_VALUE_GET_OBJ(func_obj); + if (unlikely(!p->is_constructor)) + return JS_ThrowTypeError(ctx, "not a constructor"); + if (unlikely(p->class_id != JS_CLASS_BYTECODE_FUNCTION)) { + JSClassCall *call_func; + call_func = ctx->rt->class_array[p->class_id].call; + if (!call_func) { + not_a_function: + return JS_ThrowTypeError(ctx, "not a function"); + } + return call_func(ctx, func_obj, new_target, argc, + argv, flags); + } + + b = p->u.func.function_bytecode; + if (b->is_derived_class_constructor) { + return JS_CallInternal(ctx, func_obj, JS_UNDEFINED, new_target, argc, argv, flags); + } else { + JSValue obj, ret; + /* legacy constructor behavior */ + obj = js_create_from_ctor(ctx, new_target, JS_CLASS_OBJECT); + if (JS_IsException(obj)) + return JS_EXCEPTION; + ret = JS_CallInternal(ctx, func_obj, obj, new_target, argc, argv, flags); + if (JS_VALUE_GET_TAG(ret) == JS_TAG_OBJECT || + JS_IsException(ret)) { + JS_FreeValue(ctx, obj); + return ret; + } else { + JS_FreeValue(ctx, ret); + return obj; + } + } +} + +JSValue JS_CallConstructor2(JSContext *ctx, JSValue func_obj, + JSValue new_target, + int argc, JSValue *argv) +{ + return JS_CallConstructorInternal(ctx, func_obj, new_target, + argc, argv, + JS_CALL_FLAG_COPY_ARGV); +} + +JSValue JS_CallConstructor(JSContext *ctx, JSValue func_obj, + int argc, JSValue *argv) +{ + return JS_CallConstructorInternal(ctx, func_obj, func_obj, + argc, argv, + JS_CALL_FLAG_COPY_ARGV); +} + +JSValue JS_Invoke(JSContext *ctx, JSValue this_val, JSAtom atom, + int argc, JSValue *argv) +{ + JSValue func_obj; + func_obj = JS_GetProperty(ctx, this_val, atom); + if (JS_IsException(func_obj)) + return func_obj; + return JS_CallFree(ctx, func_obj, this_val, argc, argv); +} + +static JSValue JS_InvokeFree(JSContext *ctx, JSValue this_val, JSAtom atom, + int argc, JSValue *argv) +{ + JSValue res = JS_Invoke(ctx, this_val, atom, argc, argv); + JS_FreeValue(ctx, this_val); + return res; +} + +/* JSAsyncFunctionState (used by generator and async functions) */ +static __exception int async_func_init(JSContext *ctx, JSAsyncFunctionState *s, + JSValue func_obj, JSValue this_obj, + int argc, JSValue *argv) +{ + JSObject *p; + JSFunctionBytecode *b; + JSStackFrame *sf; + int local_count, i, arg_buf_len, n; + + sf = &s->frame; + init_list_head(&sf->var_ref_list); + p = JS_VALUE_GET_OBJ(func_obj); + b = p->u.func.function_bytecode; + sf->is_strict_mode = b->is_strict_mode; + sf->cur_pc = b->byte_code_buf; + arg_buf_len = max_int(b->arg_count, argc); + local_count = arg_buf_len + b->var_count + b->stack_size; + sf->arg_buf = js_malloc(ctx, sizeof(JSValue) * max_int(local_count, 1)); + if (!sf->arg_buf) + return -1; + sf->cur_func = js_dup(func_obj); + s->this_val = js_dup(this_obj); + s->argc = argc; + sf->arg_count = arg_buf_len; + sf->var_buf = sf->arg_buf + arg_buf_len; + sf->cur_sp = sf->var_buf + b->var_count; + for(i = 0; i < argc; i++) + sf->arg_buf[i] = js_dup(argv[i]); + n = arg_buf_len + b->var_count; + for(i = argc; i < n; i++) + sf->arg_buf[i] = JS_UNDEFINED; + return 0; +} + +static void async_func_mark(JSRuntime *rt, JSAsyncFunctionState *s, + JS_MarkFunc *mark_func) +{ + JSStackFrame *sf; + JSValue *sp; + + sf = &s->frame; + JS_MarkValue(rt, sf->cur_func, mark_func); + JS_MarkValue(rt, s->this_val, mark_func); + if (sf->cur_sp) { + /* if the function is running, cur_sp is not known so we + cannot mark the stack. Marking the variables is not needed + because a running function cannot be part of a removable + cycle */ + for(sp = sf->arg_buf; sp < sf->cur_sp; sp++) + JS_MarkValue(rt, *sp, mark_func); + } +} + +static void async_func_free(JSRuntime *rt, JSAsyncFunctionState *s) +{ + JSStackFrame *sf; + JSValue *sp; + + sf = &s->frame; + + /* close the closure variables. */ + close_var_refs(rt, sf); + + if (sf->arg_buf) { + /* cannot free the function if it is running */ + assert(sf->cur_sp != NULL); + for(sp = sf->arg_buf; sp < sf->cur_sp; sp++) { + JS_FreeValueRT(rt, *sp); + } + js_free_rt(rt, sf->arg_buf); + } + JS_FreeValueRT(rt, sf->cur_func); + JS_FreeValueRT(rt, s->this_val); +} + +static JSValue async_func_resume(JSContext *ctx, JSAsyncFunctionState *s) +{ + JSValue func_obj; + + if (js_check_stack_overflow(ctx->rt, 0)) + return JS_ThrowStackOverflow(ctx); + + /* the tag does not matter provided it is not an object */ + func_obj = JS_MKPTR(JS_TAG_INT, s); + return JS_CallInternal(ctx, func_obj, s->this_val, JS_UNDEFINED, + s->argc, s->frame.arg_buf, JS_CALL_FLAG_GENERATOR); +} + + +/* Generators */ + +typedef enum JSGeneratorStateEnum { + JS_GENERATOR_STATE_SUSPENDED_START, + JS_GENERATOR_STATE_SUSPENDED_YIELD, + JS_GENERATOR_STATE_SUSPENDED_YIELD_STAR, + JS_GENERATOR_STATE_EXECUTING, + JS_GENERATOR_STATE_COMPLETED, +} JSGeneratorStateEnum; + +typedef struct JSGeneratorData { + JSGeneratorStateEnum state; + JSAsyncFunctionState func_state; +} JSGeneratorData; + +static void free_generator_stack_rt(JSRuntime *rt, JSGeneratorData *s) +{ + if (s->state == JS_GENERATOR_STATE_COMPLETED) + return; + async_func_free(rt, &s->func_state); + s->state = JS_GENERATOR_STATE_COMPLETED; +} + +static void js_generator_finalizer(JSRuntime *rt, JSValue obj) +{ + JSGeneratorData *s = JS_GetOpaque(obj, JS_CLASS_GENERATOR); + + if (s) { + free_generator_stack_rt(rt, s); + js_free_rt(rt, s); + } +} + +static void free_generator_stack(JSContext *ctx, JSGeneratorData *s) +{ + free_generator_stack_rt(ctx->rt, s); +} + +static void js_generator_mark(JSRuntime *rt, JSValue val, + JS_MarkFunc *mark_func) +{ + JSObject *p = JS_VALUE_GET_OBJ(val); + JSGeneratorData *s = p->u.generator_data; + + if (!s || s->state == JS_GENERATOR_STATE_COMPLETED) + return; + async_func_mark(rt, &s->func_state, mark_func); +} + +/* XXX: use enum */ +#define GEN_MAGIC_NEXT 0 +#define GEN_MAGIC_RETURN 1 +#define GEN_MAGIC_THROW 2 + +static JSValue js_generator_next(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv, + BOOL *pdone, int magic) +{ + JSGeneratorData *s = JS_GetOpaque(this_val, JS_CLASS_GENERATOR); + JSStackFrame *sf; + JSValue ret, func_ret; + + *pdone = TRUE; + if (!s) + return JS_ThrowTypeError(ctx, "not a generator"); + sf = &s->func_state.frame; + switch(s->state) { + default: + case JS_GENERATOR_STATE_SUSPENDED_START: + if (magic == GEN_MAGIC_NEXT) { + goto exec_no_arg; + } else { + free_generator_stack(ctx, s); + goto done; + } + break; + case JS_GENERATOR_STATE_SUSPENDED_YIELD_STAR: + case JS_GENERATOR_STATE_SUSPENDED_YIELD: + /* cur_sp[-1] was set to JS_UNDEFINED in the previous call */ + ret = js_dup(argv[0]); + if (magic == GEN_MAGIC_THROW && + s->state == JS_GENERATOR_STATE_SUSPENDED_YIELD) { + JS_Throw(ctx, ret); + s->func_state.throw_flag = TRUE; + } else { + sf->cur_sp[-1] = ret; + sf->cur_sp[0] = js_int32(magic); + sf->cur_sp++; + exec_no_arg: + s->func_state.throw_flag = FALSE; + } + s->state = JS_GENERATOR_STATE_EXECUTING; + func_ret = async_func_resume(ctx, &s->func_state); + s->state = JS_GENERATOR_STATE_SUSPENDED_YIELD; + if (JS_IsException(func_ret)) { + /* finalize the execution in case of exception */ + free_generator_stack(ctx, s); + return func_ret; + } + if (JS_VALUE_GET_TAG(func_ret) == JS_TAG_INT) { + /* get the returned yield value at the top of the stack */ + ret = sf->cur_sp[-1]; + sf->cur_sp[-1] = JS_UNDEFINED; + if (JS_VALUE_GET_INT(func_ret) == FUNC_RET_YIELD_STAR) { + s->state = JS_GENERATOR_STATE_SUSPENDED_YIELD_STAR; + /* return (value, done) object */ + *pdone = 2; + } else { + *pdone = FALSE; + } + } else { + /* end of iterator */ + ret = sf->cur_sp[-1]; + sf->cur_sp[-1] = JS_UNDEFINED; + JS_FreeValue(ctx, func_ret); + free_generator_stack(ctx, s); + } + break; + case JS_GENERATOR_STATE_COMPLETED: + done: + /* execution is finished */ + switch(magic) { + default: + case GEN_MAGIC_NEXT: + ret = JS_UNDEFINED; + break; + case GEN_MAGIC_RETURN: + ret = js_dup(argv[0]); + break; + case GEN_MAGIC_THROW: + ret = JS_Throw(ctx, js_dup(argv[0])); + break; + } + break; + case JS_GENERATOR_STATE_EXECUTING: + ret = JS_ThrowTypeError(ctx, "cannot invoke a running generator"); + break; + } + return ret; +} + +static JSValue js_generator_function_call(JSContext *ctx, JSValue func_obj, + JSValue this_obj, + int argc, JSValue *argv, + int flags) +{ + JSValue obj, func_ret; + JSGeneratorData *s; + + s = js_mallocz(ctx, sizeof(*s)); + if (!s) + return JS_EXCEPTION; + s->state = JS_GENERATOR_STATE_SUSPENDED_START; + if (async_func_init(ctx, &s->func_state, func_obj, this_obj, argc, argv)) { + s->state = JS_GENERATOR_STATE_COMPLETED; + goto fail; + } + + /* execute the function up to 'OP_initial_yield' */ + func_ret = async_func_resume(ctx, &s->func_state); + if (JS_IsException(func_ret)) + goto fail; + JS_FreeValue(ctx, func_ret); + + obj = js_create_from_ctor(ctx, func_obj, JS_CLASS_GENERATOR); + if (JS_IsException(obj)) + goto fail; + JS_SetOpaqueInternal(obj, s); + return obj; + fail: + free_generator_stack_rt(ctx->rt, s); + js_free(ctx, s); + return JS_EXCEPTION; +} + +/* AsyncFunction */ + +static void js_async_function_terminate(JSRuntime *rt, JSAsyncFunctionData *s) +{ + if (s->is_active) { + async_func_free(rt, &s->func_state); + s->is_active = FALSE; + } +} + +static void js_async_function_free0(JSRuntime *rt, JSAsyncFunctionData *s) +{ + js_async_function_terminate(rt, s); + JS_FreeValueRT(rt, s->resolving_funcs[0]); + JS_FreeValueRT(rt, s->resolving_funcs[1]); + remove_gc_object(&s->header); + js_free_rt(rt, s); +} + +static void js_async_function_free(JSRuntime *rt, JSAsyncFunctionData *s) +{ + if (--s->header.ref_count == 0) { + js_async_function_free0(rt, s); + } +} + +static void js_async_function_resolve_finalizer(JSRuntime *rt, JSValue val) +{ + JSObject *p = JS_VALUE_GET_OBJ(val); + JSAsyncFunctionData *s = p->u.async_function_data; + if (s) { + js_async_function_free(rt, s); + } +} + +static void js_async_function_resolve_mark(JSRuntime *rt, JSValue val, + JS_MarkFunc *mark_func) +{ + JSObject *p = JS_VALUE_GET_OBJ(val); + JSAsyncFunctionData *s = p->u.async_function_data; + if (s) { + mark_func(rt, &s->header); + } +} + +static int js_async_function_resolve_create(JSContext *ctx, + JSAsyncFunctionData *s, + JSValue *resolving_funcs) +{ + int i; + JSObject *p; + + for(i = 0; i < 2; i++) { + resolving_funcs[i] = + JS_NewObjectProtoClass(ctx, ctx->function_proto, + JS_CLASS_ASYNC_FUNCTION_RESOLVE + i); + if (JS_IsException(resolving_funcs[i])) { + if (i == 1) + JS_FreeValue(ctx, resolving_funcs[0]); + return -1; + } + p = JS_VALUE_GET_OBJ(resolving_funcs[i]); + s->header.ref_count++; + p->u.async_function_data = s; + } + return 0; +} + +static void js_async_function_resume(JSContext *ctx, JSAsyncFunctionData *s) +{ + JSValue func_ret, ret2; + + func_ret = async_func_resume(ctx, &s->func_state); + if (JS_IsException(func_ret)) { + JSValue error; + fail: + error = JS_GetException(ctx); + ret2 = JS_Call(ctx, s->resolving_funcs[1], JS_UNDEFINED, + 1, &error); + JS_FreeValue(ctx, error); + js_async_function_terminate(ctx->rt, s); + JS_FreeValue(ctx, ret2); /* XXX: what to do if exception ? */ + } else { + JSValue value; + value = s->func_state.frame.cur_sp[-1]; + s->func_state.frame.cur_sp[-1] = JS_UNDEFINED; + if (JS_IsUndefined(func_ret)) { + /* function returned */ + ret2 = JS_Call(ctx, s->resolving_funcs[0], JS_UNDEFINED, + 1, &value); + JS_FreeValue(ctx, ret2); /* XXX: what to do if exception ? */ + JS_FreeValue(ctx, value); + js_async_function_terminate(ctx->rt, s); + } else { + JSValue promise, resolving_funcs[2], resolving_funcs1[2]; + int i, res; + + /* await */ + JS_FreeValue(ctx, func_ret); /* not used */ + promise = js_promise_resolve(ctx, ctx->promise_ctor, + 1, &value, 0); + JS_FreeValue(ctx, value); + if (JS_IsException(promise)) + goto fail; + if (js_async_function_resolve_create(ctx, s, resolving_funcs)) { + JS_FreeValue(ctx, promise); + goto fail; + } + + /* Note: no need to create 'thrownawayCapability' as in + the spec */ + for(i = 0; i < 2; i++) + resolving_funcs1[i] = JS_UNDEFINED; + res = perform_promise_then(ctx, promise, + resolving_funcs, + resolving_funcs1); + JS_FreeValue(ctx, promise); + for(i = 0; i < 2; i++) + JS_FreeValue(ctx, resolving_funcs[i]); + if (res) + goto fail; + } + } +} + +static JSValue js_async_function_resolve_call(JSContext *ctx, + JSValue func_obj, + JSValue this_obj, + int argc, JSValue *argv, + int flags) +{ + JSObject *p = JS_VALUE_GET_OBJ(func_obj); + JSAsyncFunctionData *s = p->u.async_function_data; + BOOL is_reject = p->class_id - JS_CLASS_ASYNC_FUNCTION_RESOLVE; + JSValue arg; + + if (argc > 0) + arg = argv[0]; + else + arg = JS_UNDEFINED; + s->func_state.throw_flag = is_reject; + if (is_reject) { + JS_Throw(ctx, js_dup(arg)); + } else { + /* return value of await */ + s->func_state.frame.cur_sp[-1] = js_dup(arg); + } + js_async_function_resume(ctx, s); + return JS_UNDEFINED; +} + +static JSValue js_async_function_call(JSContext *ctx, JSValue func_obj, + JSValue this_obj, + int argc, JSValue *argv, int flags) +{ + JSValue promise; + JSAsyncFunctionData *s; + + s = js_mallocz(ctx, sizeof(*s)); + if (!s) + return JS_EXCEPTION; + s->header.ref_count = 1; + add_gc_object(ctx->rt, &s->header, JS_GC_OBJ_TYPE_ASYNC_FUNCTION); + s->is_active = FALSE; + s->resolving_funcs[0] = JS_UNDEFINED; + s->resolving_funcs[1] = JS_UNDEFINED; + + promise = JS_NewPromiseCapability(ctx, s->resolving_funcs); + if (JS_IsException(promise)) + goto fail; + + if (async_func_init(ctx, &s->func_state, func_obj, this_obj, argc, argv)) { + fail: + JS_FreeValue(ctx, promise); + js_async_function_free(ctx->rt, s); + return JS_EXCEPTION; + } + s->is_active = TRUE; + + js_async_function_resume(ctx, s); + + js_async_function_free(ctx->rt, s); + + return promise; +} + +/* AsyncGenerator */ + +typedef enum JSAsyncGeneratorStateEnum { + JS_ASYNC_GENERATOR_STATE_SUSPENDED_START, + JS_ASYNC_GENERATOR_STATE_SUSPENDED_YIELD, + JS_ASYNC_GENERATOR_STATE_SUSPENDED_YIELD_STAR, + JS_ASYNC_GENERATOR_STATE_EXECUTING, + JS_ASYNC_GENERATOR_STATE_AWAITING_RETURN, + JS_ASYNC_GENERATOR_STATE_COMPLETED, +} JSAsyncGeneratorStateEnum; + +typedef struct JSAsyncGeneratorRequest { + struct list_head link; + /* completion */ + int completion_type; /* GEN_MAGIC_x */ + JSValue result; + /* promise capability */ + JSValue promise; + JSValue resolving_funcs[2]; +} JSAsyncGeneratorRequest; + +typedef struct JSAsyncGeneratorData { + JSObject *generator; /* back pointer to the object (const) */ + JSAsyncGeneratorStateEnum state; + JSAsyncFunctionState func_state; + struct list_head queue; /* list of JSAsyncGeneratorRequest.link */ +} JSAsyncGeneratorData; + +static void js_async_generator_free(JSRuntime *rt, + JSAsyncGeneratorData *s) +{ + struct list_head *el, *el1; + JSAsyncGeneratorRequest *req; + + list_for_each_safe(el, el1, &s->queue) { + req = list_entry(el, JSAsyncGeneratorRequest, link); + JS_FreeValueRT(rt, req->result); + JS_FreeValueRT(rt, req->promise); + JS_FreeValueRT(rt, req->resolving_funcs[0]); + JS_FreeValueRT(rt, req->resolving_funcs[1]); + js_free_rt(rt, req); + } + if (s->state != JS_ASYNC_GENERATOR_STATE_COMPLETED && + s->state != JS_ASYNC_GENERATOR_STATE_AWAITING_RETURN) { + async_func_free(rt, &s->func_state); + } + js_free_rt(rt, s); +} + +static void js_async_generator_finalizer(JSRuntime *rt, JSValue obj) +{ + JSAsyncGeneratorData *s = JS_GetOpaque(obj, JS_CLASS_ASYNC_GENERATOR); + + if (s) { + js_async_generator_free(rt, s); + } +} + +static void js_async_generator_mark(JSRuntime *rt, JSValue val, + JS_MarkFunc *mark_func) +{ + JSAsyncGeneratorData *s = JS_GetOpaque(val, JS_CLASS_ASYNC_GENERATOR); + struct list_head *el; + JSAsyncGeneratorRequest *req; + if (s) { + list_for_each(el, &s->queue) { + req = list_entry(el, JSAsyncGeneratorRequest, link); + JS_MarkValue(rt, req->result, mark_func); + JS_MarkValue(rt, req->promise, mark_func); + JS_MarkValue(rt, req->resolving_funcs[0], mark_func); + JS_MarkValue(rt, req->resolving_funcs[1], mark_func); + } + if (s->state != JS_ASYNC_GENERATOR_STATE_COMPLETED && + s->state != JS_ASYNC_GENERATOR_STATE_AWAITING_RETURN) { + async_func_mark(rt, &s->func_state, mark_func); + } + } +} + +static JSValue js_async_generator_resolve_function(JSContext *ctx, + JSValue this_obj, + int argc, JSValue *argv, + int magic, JSValue *func_data); + +static int js_async_generator_resolve_function_create(JSContext *ctx, + JSValue generator, + JSValue *resolving_funcs, + BOOL is_resume_next) +{ + int i; + JSValue func; + + for(i = 0; i < 2; i++) { + func = JS_NewCFunctionData(ctx, js_async_generator_resolve_function, 1, + i + is_resume_next * 2, 1, &generator); + if (JS_IsException(func)) { + if (i == 1) + JS_FreeValue(ctx, resolving_funcs[0]); + return -1; + } + resolving_funcs[i] = func; + } + return 0; +} + +static int js_async_generator_await(JSContext *ctx, + JSAsyncGeneratorData *s, + JSValue value) +{ + JSValue promise, resolving_funcs[2], resolving_funcs1[2]; + int i, res; + + promise = js_promise_resolve(ctx, ctx->promise_ctor, + 1, &value, 0); + if (JS_IsException(promise)) + goto fail; + + if (js_async_generator_resolve_function_create(ctx, JS_MKPTR(JS_TAG_OBJECT, s->generator), + resolving_funcs, FALSE)) { + JS_FreeValue(ctx, promise); + goto fail; + } + + /* Note: no need to create 'thrownawayCapability' as in + the spec */ + for(i = 0; i < 2; i++) + resolving_funcs1[i] = JS_UNDEFINED; + res = perform_promise_then(ctx, promise, + resolving_funcs, + resolving_funcs1); + JS_FreeValue(ctx, promise); + for(i = 0; i < 2; i++) + JS_FreeValue(ctx, resolving_funcs[i]); + if (res) + goto fail; + return 0; + fail: + return -1; +} + +static void js_async_generator_resolve_or_reject(JSContext *ctx, + JSAsyncGeneratorData *s, + JSValue result, + int is_reject) +{ + JSAsyncGeneratorRequest *next; + JSValue ret; + + next = list_entry(s->queue.next, JSAsyncGeneratorRequest, link); + list_del(&next->link); + ret = JS_Call(ctx, next->resolving_funcs[is_reject], JS_UNDEFINED, 1, + &result); + JS_FreeValue(ctx, ret); + JS_FreeValue(ctx, next->result); + JS_FreeValue(ctx, next->promise); + JS_FreeValue(ctx, next->resolving_funcs[0]); + JS_FreeValue(ctx, next->resolving_funcs[1]); + js_free(ctx, next); +} + +static void js_async_generator_resolve(JSContext *ctx, + JSAsyncGeneratorData *s, + JSValue value, + BOOL done) +{ + JSValue result; + result = js_create_iterator_result(ctx, js_dup(value), done); + /* XXX: better exception handling ? */ + js_async_generator_resolve_or_reject(ctx, s, result, 0); + JS_FreeValue(ctx, result); + } + +static void js_async_generator_reject(JSContext *ctx, + JSAsyncGeneratorData *s, + JSValue exception) +{ + js_async_generator_resolve_or_reject(ctx, s, exception, 1); +} + +static void js_async_generator_complete(JSContext *ctx, + JSAsyncGeneratorData *s) +{ + if (s->state != JS_ASYNC_GENERATOR_STATE_COMPLETED) { + s->state = JS_ASYNC_GENERATOR_STATE_COMPLETED; + async_func_free(ctx->rt, &s->func_state); + } +} + +static int js_async_generator_completed_return(JSContext *ctx, + JSAsyncGeneratorData *s, + JSValue value) +{ + JSValue promise, resolving_funcs[2], resolving_funcs1[2]; + int res; + + // Can fail looking up JS_ATOM_constructor when is_reject==0. + promise = js_promise_resolve(ctx, ctx->promise_ctor, 1, &value, + /*is_reject*/0); + // A poisoned .constructor property is observable and the resulting + // exception should be delivered to the catch handler. + if (JS_IsException(promise)) { + JSValue err = JS_GetException(ctx); + promise = js_promise_resolve(ctx, ctx->promise_ctor, 1, &err, + /*is_reject*/1); + JS_FreeValue(ctx, err); + if (JS_IsException(promise)) + return -1; + } + if (js_async_generator_resolve_function_create(ctx, + JS_MKPTR(JS_TAG_OBJECT, s->generator), + resolving_funcs1, + TRUE)) { + JS_FreeValue(ctx, promise); + return -1; + } + resolving_funcs[0] = JS_UNDEFINED; + resolving_funcs[1] = JS_UNDEFINED; + res = perform_promise_then(ctx, promise, + resolving_funcs1, + resolving_funcs); + JS_FreeValue(ctx, resolving_funcs1[0]); + JS_FreeValue(ctx, resolving_funcs1[1]); + JS_FreeValue(ctx, promise); + return res; +} + +static void js_async_generator_resume_next(JSContext *ctx, + JSAsyncGeneratorData *s) +{ + JSAsyncGeneratorRequest *next; + JSValue func_ret, value; + + for(;;) { + if (list_empty(&s->queue)) + break; + next = list_entry(s->queue.next, JSAsyncGeneratorRequest, link); + switch(s->state) { + case JS_ASYNC_GENERATOR_STATE_EXECUTING: + /* only happens when restarting execution after await() */ + goto resume_exec; + case JS_ASYNC_GENERATOR_STATE_AWAITING_RETURN: + goto done; + case JS_ASYNC_GENERATOR_STATE_SUSPENDED_START: + if (next->completion_type == GEN_MAGIC_NEXT) { + goto exec_no_arg; + } else { + js_async_generator_complete(ctx, s); + } + break; + case JS_ASYNC_GENERATOR_STATE_COMPLETED: + if (next->completion_type == GEN_MAGIC_NEXT) { + js_async_generator_resolve(ctx, s, JS_UNDEFINED, TRUE); + } else if (next->completion_type == GEN_MAGIC_RETURN) { + s->state = JS_ASYNC_GENERATOR_STATE_AWAITING_RETURN; + js_async_generator_completed_return(ctx, s, next->result); + } else { + js_async_generator_reject(ctx, s, next->result); + } + goto done; + case JS_ASYNC_GENERATOR_STATE_SUSPENDED_YIELD: + case JS_ASYNC_GENERATOR_STATE_SUSPENDED_YIELD_STAR: + value = js_dup(next->result); + if (next->completion_type == GEN_MAGIC_THROW && + s->state == JS_ASYNC_GENERATOR_STATE_SUSPENDED_YIELD) { + JS_Throw(ctx, value); + s->func_state.throw_flag = TRUE; + } else { + /* 'yield' returns a value. 'yield *' also returns a value + in case the 'throw' method is called */ + s->func_state.frame.cur_sp[-1] = value; + s->func_state.frame.cur_sp[0] = + js_int32(next->completion_type); + s->func_state.frame.cur_sp++; + exec_no_arg: + s->func_state.throw_flag = FALSE; + } + s->state = JS_ASYNC_GENERATOR_STATE_EXECUTING; + resume_exec: + func_ret = async_func_resume(ctx, &s->func_state); + if (JS_IsException(func_ret)) { + value = JS_GetException(ctx); + js_async_generator_complete(ctx, s); + js_async_generator_reject(ctx, s, value); + JS_FreeValue(ctx, value); + } else if (JS_VALUE_GET_TAG(func_ret) == JS_TAG_INT) { + int func_ret_code, ret; + value = s->func_state.frame.cur_sp[-1]; + s->func_state.frame.cur_sp[-1] = JS_UNDEFINED; + func_ret_code = JS_VALUE_GET_INT(func_ret); + switch(func_ret_code) { + case FUNC_RET_YIELD: + case FUNC_RET_YIELD_STAR: + if (func_ret_code == FUNC_RET_YIELD_STAR) + s->state = JS_ASYNC_GENERATOR_STATE_SUSPENDED_YIELD_STAR; + else + s->state = JS_ASYNC_GENERATOR_STATE_SUSPENDED_YIELD; + js_async_generator_resolve(ctx, s, value, FALSE); + JS_FreeValue(ctx, value); + break; + case FUNC_RET_AWAIT: + ret = js_async_generator_await(ctx, s, value); + JS_FreeValue(ctx, value); + if (ret < 0) { + /* exception: throw it */ + s->func_state.throw_flag = TRUE; + goto resume_exec; + } + goto done; + default: + abort(); + } + } else { + assert(JS_IsUndefined(func_ret)); + /* end of function */ + value = s->func_state.frame.cur_sp[-1]; + s->func_state.frame.cur_sp[-1] = JS_UNDEFINED; + js_async_generator_complete(ctx, s); + js_async_generator_resolve(ctx, s, value, TRUE); + JS_FreeValue(ctx, value); + } + break; + default: + abort(); + } + } + done: ; +} + +static JSValue js_async_generator_resolve_function(JSContext *ctx, + JSValue this_obj, + int argc, JSValue *argv, + int magic, JSValue *func_data) +{ + BOOL is_reject = magic & 1; + JSAsyncGeneratorData *s = JS_GetOpaque(func_data[0], JS_CLASS_ASYNC_GENERATOR); + JSValue arg = argv[0]; + + /* XXX: what if s == NULL */ + + if (magic >= 2) { + /* resume next case in AWAITING_RETURN state */ + assert(s->state == JS_ASYNC_GENERATOR_STATE_AWAITING_RETURN || + s->state == JS_ASYNC_GENERATOR_STATE_COMPLETED); + s->state = JS_ASYNC_GENERATOR_STATE_COMPLETED; + if (is_reject) { + js_async_generator_reject(ctx, s, arg); + } else { + js_async_generator_resolve(ctx, s, arg, TRUE); + } + } else { + /* restart function execution after await() */ + assert(s->state == JS_ASYNC_GENERATOR_STATE_EXECUTING); + s->func_state.throw_flag = is_reject; + if (is_reject) { + JS_Throw(ctx, js_dup(arg)); + } else { + /* return value of await */ + s->func_state.frame.cur_sp[-1] = js_dup(arg); + } + js_async_generator_resume_next(ctx, s); + } + return JS_UNDEFINED; +} + +/* magic = GEN_MAGIC_x */ +static JSValue js_async_generator_next(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv, + int magic) +{ + JSAsyncGeneratorData *s = JS_GetOpaque(this_val, JS_CLASS_ASYNC_GENERATOR); + JSValue promise, resolving_funcs[2]; + JSAsyncGeneratorRequest *req; + + promise = JS_NewPromiseCapability(ctx, resolving_funcs); + if (JS_IsException(promise)) + return JS_EXCEPTION; + if (!s) { + JSValue err, res2; + JS_ThrowTypeError(ctx, "not an AsyncGenerator object"); + err = JS_GetException(ctx); + res2 = JS_Call(ctx, resolving_funcs[1], JS_UNDEFINED, + 1, &err); + JS_FreeValue(ctx, err); + JS_FreeValue(ctx, res2); + JS_FreeValue(ctx, resolving_funcs[0]); + JS_FreeValue(ctx, resolving_funcs[1]); + return promise; + } + req = js_mallocz(ctx, sizeof(*req)); + if (!req) + goto fail; + req->completion_type = magic; + req->result = js_dup(argv[0]); + req->promise = js_dup(promise); + req->resolving_funcs[0] = resolving_funcs[0]; + req->resolving_funcs[1] = resolving_funcs[1]; + list_add_tail(&req->link, &s->queue); + if (s->state != JS_ASYNC_GENERATOR_STATE_EXECUTING) { + js_async_generator_resume_next(ctx, s); + } + return promise; + fail: + JS_FreeValue(ctx, resolving_funcs[0]); + JS_FreeValue(ctx, resolving_funcs[1]); + JS_FreeValue(ctx, promise); + return JS_EXCEPTION; +} + +static JSValue js_async_generator_function_call(JSContext *ctx, JSValue func_obj, + JSValue this_obj, + int argc, JSValue *argv, + int flags) +{ + JSValue obj, func_ret; + JSAsyncGeneratorData *s; + + s = js_mallocz(ctx, sizeof(*s)); + if (!s) + return JS_EXCEPTION; + s->state = JS_ASYNC_GENERATOR_STATE_SUSPENDED_START; + init_list_head(&s->queue); + if (async_func_init(ctx, &s->func_state, func_obj, this_obj, argc, argv)) { + s->state = JS_ASYNC_GENERATOR_STATE_COMPLETED; + goto fail; + } + + /* execute the function up to 'OP_initial_yield' (no yield nor + await are possible) */ + func_ret = async_func_resume(ctx, &s->func_state); + if (JS_IsException(func_ret)) + goto fail; + JS_FreeValue(ctx, func_ret); + + obj = js_create_from_ctor(ctx, func_obj, JS_CLASS_ASYNC_GENERATOR); + if (JS_IsException(obj)) + goto fail; + s->generator = JS_VALUE_GET_OBJ(obj); + JS_SetOpaqueInternal(obj, s); + return obj; + fail: + js_async_generator_free(ctx->rt, s); + return JS_EXCEPTION; +} + +/* JS parser */ + +enum { + TOK_NUMBER = -128, + TOK_STRING, + TOK_TEMPLATE, + TOK_IDENT, + TOK_REGEXP, + /* warning: order matters (see js_parse_assign_expr) */ + TOK_MUL_ASSIGN, + TOK_DIV_ASSIGN, + TOK_MOD_ASSIGN, + TOK_PLUS_ASSIGN, + TOK_MINUS_ASSIGN, + TOK_SHL_ASSIGN, + TOK_SAR_ASSIGN, + TOK_SHR_ASSIGN, + TOK_AND_ASSIGN, + TOK_XOR_ASSIGN, + TOK_OR_ASSIGN, + TOK_POW_ASSIGN, + TOK_LAND_ASSIGN, + TOK_LOR_ASSIGN, + TOK_DOUBLE_QUESTION_MARK_ASSIGN, + TOK_DEC, + TOK_INC, + TOK_SHL, + TOK_SAR, + TOK_SHR, + TOK_LT, + TOK_LTE, + TOK_GT, + TOK_GTE, + TOK_EQ, + TOK_STRICT_EQ, + TOK_NEQ, + TOK_STRICT_NEQ, + TOK_LAND, + TOK_LOR, + TOK_POW, + TOK_ARROW, + TOK_ELLIPSIS, + TOK_DOUBLE_QUESTION_MARK, + TOK_QUESTION_MARK_DOT, + TOK_ERROR, + TOK_PRIVATE_NAME, + TOK_EOF, + /* keywords: WARNING: same order as atoms */ + TOK_NULL, /* must be first */ + TOK_FALSE, + TOK_TRUE, + TOK_IF, + TOK_ELSE, + TOK_RETURN, + TOK_VAR, + TOK_THIS, + TOK_DELETE, + TOK_VOID, + TOK_TYPEOF, + TOK_NEW, + TOK_IN, + TOK_INSTANCEOF, + TOK_DO, + TOK_WHILE, + TOK_FOR, + TOK_BREAK, + TOK_CONTINUE, + TOK_SWITCH, + TOK_CASE, + TOK_DEFAULT, + TOK_THROW, + TOK_TRY, + TOK_CATCH, + TOK_FINALLY, + TOK_FUNCTION, + TOK_DEBUGGER, + TOK_WITH, + /* FutureReservedWord */ + TOK_CLASS, + TOK_CONST, + TOK_ENUM, + TOK_EXPORT, + TOK_EXTENDS, + TOK_IMPORT, + TOK_SUPER, + /* FutureReservedWords when parsing strict mode code */ + TOK_IMPLEMENTS, + TOK_INTERFACE, + TOK_LET, + TOK_PACKAGE, + TOK_PRIVATE, + TOK_PROTECTED, + TOK_PUBLIC, + TOK_STATIC, + TOK_YIELD, + TOK_AWAIT, /* must be last */ + TOK_OF, /* only used for js_parse_skip_parens_token() */ +}; + +#define TOK_FIRST_KEYWORD TOK_NULL +#define TOK_LAST_KEYWORD TOK_AWAIT + +/* unicode code points */ +#define CP_NBSP 0x00a0 +#define CP_BOM 0xfeff + +#define CP_LS 0x2028 +#define CP_PS 0x2029 + +typedef struct BlockEnv { + struct BlockEnv *prev; + JSAtom label_name; /* JS_ATOM_NULL if none */ + int label_break; /* -1 if none */ + int label_cont; /* -1 if none */ + int drop_count; /* number of stack elements to drop */ + int label_finally; /* -1 if none */ + int scope_level; + int has_iterator; +} BlockEnv; + +typedef struct JSGlobalVar { + int cpool_idx; /* if >= 0, index in the constant pool for hoisted + function defintion*/ + uint8_t force_init : 1; /* force initialization to undefined */ + uint8_t is_lexical : 1; /* global let/const definition */ + uint8_t is_const : 1; /* const definition */ + int scope_level; /* scope of definition */ + JSAtom var_name; /* variable name */ +} JSGlobalVar; + +typedef struct RelocEntry { + struct RelocEntry *next; + uint32_t addr; /* address to patch */ + int size; /* address size: 1, 2 or 4 bytes */ +} RelocEntry; + +typedef struct JumpSlot { + int op; + int size; + int pos; + int label; +} JumpSlot; + +typedef struct LabelSlot { + int ref_count; + int pos; /* phase 1 address, -1 means not resolved yet */ + int pos2; /* phase 2 address, -1 means not resolved yet */ + int addr; /* phase 3 address, -1 means not resolved yet */ + RelocEntry *first_reloc; +} LabelSlot; + +typedef struct SourceLocSlot { + uint32_t pc; + int line_num; + int col_num; +} SourceLocSlot; + +typedef enum JSParseFunctionEnum { + JS_PARSE_FUNC_STATEMENT, + JS_PARSE_FUNC_VAR, + JS_PARSE_FUNC_EXPR, + JS_PARSE_FUNC_ARROW, + JS_PARSE_FUNC_GETTER, + JS_PARSE_FUNC_SETTER, + JS_PARSE_FUNC_METHOD, + JS_PARSE_FUNC_CLASS_STATIC_INIT, + JS_PARSE_FUNC_CLASS_CONSTRUCTOR, + JS_PARSE_FUNC_DERIVED_CLASS_CONSTRUCTOR, +} JSParseFunctionEnum; + +typedef enum JSParseExportEnum { + JS_PARSE_EXPORT_NONE, + JS_PARSE_EXPORT_NAMED, + JS_PARSE_EXPORT_DEFAULT, +} JSParseExportEnum; + +typedef struct JSFunctionDef { + JSContext *ctx; + struct JSFunctionDef *parent; + int parent_cpool_idx; /* index in the constant pool of the parent + or -1 if none */ + int parent_scope_level; /* scope level in parent at point of definition */ + struct list_head child_list; /* list of JSFunctionDef.link */ + struct list_head link; + + BOOL is_eval; /* TRUE if eval code */ + int eval_type; /* only valid if is_eval = TRUE */ + BOOL is_global_var; /* TRUE if variables are not defined locally: + eval global, eval module or non strict eval */ + BOOL is_func_expr; /* TRUE if function expression */ + BOOL has_home_object; /* TRUE if the home object is available */ + BOOL has_prototype; /* true if a prototype field is necessary */ + BOOL has_simple_parameter_list; + BOOL has_parameter_expressions; /* if true, an argument scope is created */ + BOOL has_use_strict; /* to reject directive in special cases */ + BOOL has_eval_call; /* true if the function contains a call to eval() */ + BOOL has_arguments_binding; /* true if the 'arguments' binding is + available in the function */ + BOOL has_this_binding; /* true if the 'this' and new.target binding are + available in the function */ + BOOL new_target_allowed; /* true if the 'new.target' does not + throw a syntax error */ + BOOL super_call_allowed; /* true if super() is allowed */ + BOOL super_allowed; /* true if super. or super[] is allowed */ + BOOL arguments_allowed; /* true if the 'arguments' identifier is allowed */ + BOOL is_derived_class_constructor; + BOOL in_function_body; + BOOL backtrace_barrier; + JSFunctionKindEnum func_kind : 8; + JSParseFunctionEnum func_type : 7; + uint8_t is_strict_mode : 1; + JSAtom func_name; /* JS_ATOM_NULL if no name */ + + JSVarDef *vars; + uint32_t *vars_htab; // indexes into vars[] + int var_size; /* allocated size for vars[] */ + int var_count; + JSVarDef *args; + int arg_size; /* allocated size for args[] */ + int arg_count; /* number of arguments */ + int defined_arg_count; + int var_object_idx; /* -1 if none */ + int arg_var_object_idx; /* -1 if none (var object for the argument scope) */ + int arguments_var_idx; /* -1 if none */ + int arguments_arg_idx; /* argument variable definition in argument scope, + -1 if none */ + int func_var_idx; /* variable containing the current function (-1 + if none, only used if is_func_expr is true) */ + int eval_ret_idx; /* variable containing the return value of the eval, -1 if none */ + int this_var_idx; /* variable containg the 'this' value, -1 if none */ + int new_target_var_idx; /* variable containg the 'new.target' value, -1 if none */ + int this_active_func_var_idx; /* variable containg the 'this.active_func' value, -1 if none */ + int home_object_var_idx; + BOOL need_home_object; + + int scope_level; /* index into fd->scopes if the current lexical scope */ + int scope_first; /* index into vd->vars of first lexically scoped variable */ + int scope_size; /* allocated size of fd->scopes array */ + int scope_count; /* number of entries used in the fd->scopes array */ + JSVarScope *scopes; + JSVarScope def_scope_array[4]; + int body_scope; /* scope of the body of the function or eval */ + + int global_var_count; + int global_var_size; + JSGlobalVar *global_vars; + + DynBuf byte_code; + int last_opcode_pos; /* -1 if no last opcode */ + BOOL use_short_opcodes; /* true if short opcodes are used in byte_code */ + + LabelSlot *label_slots; + int label_size; /* allocated size for label_slots[] */ + int label_count; + BlockEnv *top_break; /* break/continue label stack */ + + /* constant pool (strings, functions, numbers) */ + JSValue *cpool; + int cpool_count; + int cpool_size; + + /* list of variables in the closure */ + int closure_var_count; + int closure_var_size; + JSClosureVar *closure_var; + + JumpSlot *jump_slots; + int jump_size; + int jump_count; + + SourceLocSlot *source_loc_slots; + int source_loc_size; + int source_loc_count; + int line_number_last; + int line_number_last_pc; + int col_number_last; + + /* pc2line table */ + JSAtom filename; + int line_num; + int col_num; + DynBuf pc2line; + + char *source; /* raw source, utf-8 encoded */ + int source_len; + + JSModuleDef *module; /* != NULL when parsing a module */ + BOOL has_await; /* TRUE if await is used (used in module eval) */ + JSInlineCache *ic; /* inline cache for field op */ +} JSFunctionDef; + +typedef struct JSToken { + int val; + int line_num; /* line number of token start */ + int col_num; /* column number of token start */ + const uint8_t *ptr; + union { + struct { + JSValue str; + int sep; + } str; + struct { + JSValue val; + } num; + struct { + JSAtom atom; + BOOL has_escape; + BOOL is_reserved; + } ident; + struct { + JSValue body; + JSValue flags; + } regexp; + } u; +} JSToken; + +typedef struct JSParseState { + JSContext *ctx; + int last_line_num; /* line number of last token */ + int last_col_num; /* column number of last token */ + int line_num; /* line number of current offset */ + int col_num; /* column number of current offset */ + const char *filename; + JSToken token; + BOOL got_lf; /* true if got line feed before the current token */ + const uint8_t *last_ptr; + const uint8_t *buf_start; + const uint8_t *buf_ptr; + const uint8_t *buf_end; + const uint8_t *eol; // most recently seen end-of-line character + const uint8_t *mark; // first token character, invariant: eol < mark + + /* current function code */ + JSFunctionDef *cur_func; + BOOL is_module; /* parsing a module */ + BOOL allow_html_comments; +} JSParseState; + +typedef struct JSOpCode { +#ifdef DUMP_BYTECODE + const char *name; +#endif + uint8_t size; /* in bytes */ + /* the opcodes remove n_pop items from the top of the stack, then + pushes n_push items */ + uint8_t n_pop; + uint8_t n_push; + uint8_t fmt; +} JSOpCode; + +static const JSOpCode opcode_info[OP_COUNT + (OP_TEMP_END - OP_TEMP_START)] = { +#define FMT(f) +#ifdef DUMP_BYTECODE +#define DEF(id, size, n_pop, n_push, f) { #id, size, n_pop, n_push, OP_FMT_ ## f }, +#else +#define DEF(id, size, n_pop, n_push, f) { size, n_pop, n_push, OP_FMT_ ## f }, +#endif +#include "quickjs-opcode.h" +#undef DEF +#undef FMT +}; + +/* After the final compilation pass, short opcodes are used. Their + opcodes overlap with the temporary opcodes which cannot appear in + the final bytecode. Their description is after the temporary + opcodes in opcode_info[]. */ +#define short_opcode_info(op) \ + opcode_info[(op) >= OP_TEMP_START ? \ + (op) + (OP_TEMP_END - OP_TEMP_START) : (op)] + +static __exception int next_token(JSParseState *s); + +static void free_token(JSParseState *s, JSToken *token) +{ + switch(token->val) { + case TOK_NUMBER: + JS_FreeValue(s->ctx, token->u.num.val); + break; + case TOK_STRING: + case TOK_TEMPLATE: + JS_FreeValue(s->ctx, token->u.str.str); + break; + case TOK_REGEXP: + JS_FreeValue(s->ctx, token->u.regexp.body); + JS_FreeValue(s->ctx, token->u.regexp.flags); + break; + case TOK_IDENT: + case TOK_PRIVATE_NAME: + JS_FreeAtom(s->ctx, token->u.ident.atom); + break; + default: + if (token->val >= TOK_FIRST_KEYWORD && + token->val <= TOK_LAST_KEYWORD) { + JS_FreeAtom(s->ctx, token->u.ident.atom); + } + break; + } +} + +static void __attribute((unused)) dump_token(JSParseState *s, + const JSToken *token) +{ + printf("%d:%d ", token->line_num, token->col_num); + switch(token->val) { + case TOK_NUMBER: + { + double d; + JS_ToFloat64(s->ctx, &d, token->u.num.val); /* no exception possible */ + printf("number: %.14g\n", d); + } + break; + case TOK_IDENT: + dump_atom: + { + char buf[ATOM_GET_STR_BUF_SIZE]; + printf("ident: '%s'\n", + JS_AtomGetStr(s->ctx, buf, sizeof(buf), token->u.ident.atom)); + } + break; + case TOK_STRING: + { + const char *str; + /* XXX: quote the string */ + str = JS_ToCString(s->ctx, token->u.str.str); + printf("string: '%s'\n", str); + JS_FreeCString(s->ctx, str); + } + break; + case TOK_TEMPLATE: + { + const char *str; + str = JS_ToCString(s->ctx, token->u.str.str); + printf("template: `%s`\n", str); + JS_FreeCString(s->ctx, str); + } + break; + case TOK_REGEXP: + { + const char *str, *str2; + str = JS_ToCString(s->ctx, token->u.regexp.body); + str2 = JS_ToCString(s->ctx, token->u.regexp.flags); + printf("regexp: '%s' '%s'\n", str, str2); + JS_FreeCString(s->ctx, str); + JS_FreeCString(s->ctx, str2); + } + break; + case TOK_EOF: + printf("eof\n"); + break; + default: + if (s->token.val >= TOK_NULL && s->token.val <= TOK_LAST_KEYWORD) { + goto dump_atom; + } else if (s->token.val >= 256) { + printf("token: %d\n", token->val); + } else { + printf("token: '%c'\n", token->val); + } + break; + } +} + +int __attribute__((format(printf, 2, 3))) js_parse_error(JSParseState *s, const char *fmt, ...) +{ + JSContext *ctx = s->ctx; + va_list ap; + int backtrace_flags; + + va_start(ap, fmt); + JS_ThrowError2(ctx, JS_SYNTAX_ERROR, fmt, ap, FALSE); + va_end(ap); + backtrace_flags = 0; + if (s->cur_func && s->cur_func->backtrace_barrier) + backtrace_flags = JS_BACKTRACE_FLAG_SINGLE_LEVEL; + build_backtrace(ctx, ctx->rt->current_exception, s->filename, + s->line_num, s->col_num, backtrace_flags); + return -1; +} + +static int js_parse_expect(JSParseState *s, int tok) +{ + char buf[ATOM_GET_STR_BUF_SIZE]; + + if (s->token.val == tok) + return next_token(s); + + switch(s->token.val) { + case TOK_EOF: + return js_parse_error(s, "Unexpected end of input"); + case TOK_NUMBER: + return js_parse_error(s, "Unexpected number"); + case TOK_STRING: + return js_parse_error(s, "Unexpected string"); + case TOK_TEMPLATE: + return js_parse_error(s, "Unexpected string template"); + case TOK_REGEXP: + return js_parse_error(s, "Unexpected regexp"); + case TOK_IDENT: + return js_parse_error(s, "Unexpected identifier '%s'", + JS_AtomGetStr(s->ctx, buf, sizeof(buf), + s->token.u.ident.atom)); + case TOK_ERROR: + return js_parse_error(s, "Invalid or unexpected token"); + default: + return js_parse_error(s, "Unexpected token '%.*s'", + (int)(s->buf_ptr - s->token.ptr), + (const char *)s->token.ptr); + } +} + +static int js_parse_expect_semi(JSParseState *s) +{ + if (s->token.val != ';') { + /* automatic insertion of ';' */ + if (s->token.val == TOK_EOF || s->token.val == '}' || s->got_lf) { + return 0; + } + return js_parse_error(s, "expecting '%c'", ';'); + } + return next_token(s); +} + +static int js_parse_error_reserved_identifier(JSParseState *s) +{ + char buf1[ATOM_GET_STR_BUF_SIZE]; + return js_parse_error(s, "'%s' is a reserved identifier", + JS_AtomGetStr(s->ctx, buf1, sizeof(buf1), + s->token.u.ident.atom)); +} + +static __exception int js_parse_template_part(JSParseState *s, + const uint8_t *p) +{ + const uint8_t *p_next; + uint32_t c; + StringBuffer b_s, *b = &b_s; + + /* p points to the first byte of the template part */ + if (string_buffer_init(s->ctx, b, 32)) + goto fail; + for(;;) { + if (p >= s->buf_end) + goto unexpected_eof; + c = *p++; + if (c == '`') { + /* template end part */ + break; + } + if (c == '$' && *p == '{') { + /* template start or middle part */ + p++; + break; + } + if (c == '\\') { + if (string_buffer_putc8(b, c)) + goto fail; + if (p >= s->buf_end) + goto unexpected_eof; + c = *p++; + } + /* newline sequences are normalized as single '\n' bytes */ + if (c == '\r') { + if (*p == '\n') + p++; + c = '\n'; + } + if (c == '\n') { + s->line_num++; + s->eol = &p[-1]; + s->mark = p; + } else if (c >= 0x80) { + c = utf8_decode(p - 1, &p_next); + if (p_next == p) { + js_parse_error(s, "invalid UTF-8 sequence"); + goto fail; + } + p = p_next; + } + if (string_buffer_putc(b, c)) + goto fail; + } + s->token.val = TOK_TEMPLATE; + s->token.u.str.sep = c; + s->token.u.str.str = string_buffer_end(b); + s->buf_ptr = p; + return 0; + + unexpected_eof: + js_parse_error(s, "unexpected end of string"); + fail: + string_buffer_free(b); + return -1; +} + +static __exception int js_parse_string(JSParseState *s, int sep, + BOOL do_throw, const uint8_t *p, + JSToken *token, const uint8_t **pp) +{ + const uint8_t *p_next; + int ret; + uint32_t c; + StringBuffer b_s, *b = &b_s; + + /* string */ + if (string_buffer_init(s->ctx, b, 32)) + goto fail; + for(;;) { + if (p >= s->buf_end) + goto invalid_char; + c = *p; + if (c < 0x20) { + if (sep == '`') { + if (c == '\r') { + if (p[1] == '\n') + p++; + c = '\n'; + } + /* do not update s->line_num */ + } else if (c == '\n' || c == '\r') + goto invalid_char; + } + p++; + if (c == sep) + break; + if (c == '$' && *p == '{' && sep == '`') { + /* template start or middle part */ + p++; + break; + } + if (c == '\\') { + c = *p; + switch(c) { + case '\0': + if (p >= s->buf_end) { + if (sep != '`') + goto invalid_char; + if (do_throw) + js_parse_error(s, "Unexpected end of input"); + goto fail; + } + p++; + break; + case '\'': + case '\"': + case '\\': + p++; + break; + case '\r': /* accept DOS and MAC newline sequences */ + if (p[1] == '\n') { + p++; + } + /* fall thru */ + case '\n': + /* ignore escaped newline sequence */ + p++; + if (sep != '`') { + s->line_num++; + s->eol = &p[-1]; + s->mark = p; + } + continue; + default: + if (c == '0' && !(p[1] >= '0' && p[1] <= '9')) { + /* accept isolated \0 */ + p++; + c = '\0'; + } else + if ((c >= '0' && c <= '9') + && (s->cur_func->is_strict_mode || sep == '`')) { + if (do_throw) { + js_parse_error(s, "%s are not allowed in %s", + (c >= '8') ? "\\8 and \\9" : "Octal escape sequences", + (sep == '`') ? "template strings" : "strict mode"); + } + goto fail; + } else if (c >= 0x80) { + c = utf8_decode(p, &p_next); + if (p_next == p + 1) { + goto invalid_utf8; + } + p = p_next; + /* LS or PS are skipped */ + if (c == CP_LS || c == CP_PS) + continue; + } else { + ret = lre_parse_escape(&p, TRUE); + if (ret == -1) { + if (do_throw) { + js_parse_error(s, "Invalid %s escape sequence", + c == 'u' ? "Unicode" : "hexadecimal"); + } + goto fail; + } else if (ret < 0) { + /* ignore the '\' (could output a warning) */ + p++; + } else { + c = ret; + } + } + break; + } + } else if (c >= 0x80) { + c = utf8_decode(p - 1, &p_next); + if (p_next == p) + goto invalid_utf8; + p = p_next; + } + if (string_buffer_putc(b, c)) + goto fail; + } + token->val = TOK_STRING; + token->u.str.sep = c; + token->u.str.str = string_buffer_end(b); + *pp = p; + return 0; + + invalid_utf8: + if (do_throw) + js_parse_error(s, "invalid UTF-8 sequence"); + goto fail; + invalid_char: + if (do_throw) + js_parse_error(s, "unexpected end of string"); + fail: + string_buffer_free(b); + return -1; +} + +static inline BOOL token_is_pseudo_keyword(JSParseState *s, JSAtom atom) { + return s->token.val == TOK_IDENT && s->token.u.ident.atom == atom && + !s->token.u.ident.has_escape; +} + +static __exception int js_parse_regexp(JSParseState *s) +{ + const uint8_t *p, *p_next; + BOOL in_class; + StringBuffer b_s, *b = &b_s; + StringBuffer b2_s, *b2 = &b2_s; + uint32_t c; + + p = s->buf_ptr; + p++; + in_class = FALSE; + if (string_buffer_init(s->ctx, b, 32)) + return -1; + if (string_buffer_init(s->ctx, b2, 1)) + goto fail; + for(;;) { + if (p >= s->buf_end) { + eof_error: + js_parse_error(s, "unexpected end of regexp"); + goto fail; + } + c = *p++; + if (c == '\n' || c == '\r') { + goto eol_error; + } else if (c == '/') { + if (!in_class) + break; + } else if (c == '[') { + in_class = TRUE; + } else if (c == ']') { + /* XXX: incorrect as the first character in a class */ + in_class = FALSE; + } else if (c == '\\') { + if (string_buffer_putc8(b, c)) + goto fail; + c = *p++; + if (c == '\n' || c == '\r') + goto eol_error; + else if (c == '\0' && p >= s->buf_end) + goto eof_error; + else if (c >= 0x80) { + c = utf8_decode(p - 1, &p_next); + if (p_next == p) { + goto invalid_utf8; + } + p = p_next; + if (c == CP_LS || c == CP_PS) + goto eol_error; + } + } else if (c >= 0x80) { + c = utf8_decode(p - 1, &p_next); + if (p_next == p) { + invalid_utf8: + js_parse_error(s, "invalid UTF-8 sequence"); + goto fail; + } + p = p_next; + /* LS or PS are considered as line terminator */ + if (c == CP_LS || c == CP_PS) { + eol_error: + js_parse_error(s, "unexpected line terminator in regexp"); + goto fail; + } + } + if (string_buffer_putc(b, c)) + goto fail; + } + + /* flags */ + for(;;) { + c = utf8_decode(p, &p_next); + /* no need to test for invalid UTF-8, 0xFFFD is not ident_next */ + if (!lre_js_is_ident_next(c)) + break; + if (string_buffer_putc(b2, c)) + goto fail; + p = p_next; + } + + s->token.val = TOK_REGEXP; + s->token.u.regexp.body = string_buffer_end(b); + s->token.u.regexp.flags = string_buffer_end(b2); + s->buf_ptr = p; + return 0; + fail: + string_buffer_free(b); + string_buffer_free(b2); + return -1; +} + +static __exception int ident_realloc(JSContext *ctx, char **pbuf, size_t *psize, + char *static_buf) +{ + char *buf, *new_buf; + size_t size, new_size; + + buf = *pbuf; + size = *psize; + if (size >= (SIZE_MAX / 3) * 2) + new_size = SIZE_MAX; + else + new_size = size + (size >> 1); + if (buf == static_buf) { + new_buf = js_malloc(ctx, new_size); + if (!new_buf) + return -1; + memcpy(new_buf, buf, size); + } else { + new_buf = js_realloc(ctx, buf, new_size); + if (!new_buf) + return -1; + } + *pbuf = new_buf; + *psize = new_size; + return 0; +} + +/* convert a TOK_IDENT to a keyword when needed */ +static void update_token_ident(JSParseState *s) +{ + if (s->token.u.ident.atom <= JS_ATOM_LAST_KEYWORD || + (s->token.u.ident.atom <= JS_ATOM_LAST_STRICT_KEYWORD && + s->cur_func->is_strict_mode) || + (s->token.u.ident.atom == JS_ATOM_yield && + ((s->cur_func->func_kind & JS_FUNC_GENERATOR) || + (s->cur_func->func_type == JS_PARSE_FUNC_ARROW && + !s->cur_func->in_function_body && s->cur_func->parent && + (s->cur_func->parent->func_kind & JS_FUNC_GENERATOR)))) || + (s->token.u.ident.atom == JS_ATOM_await && + (s->is_module || + (s->cur_func->func_kind & JS_FUNC_ASYNC) || + s->cur_func->func_type == JS_PARSE_FUNC_CLASS_STATIC_INIT || + (s->cur_func->func_type == JS_PARSE_FUNC_ARROW && + !s->cur_func->in_function_body && s->cur_func->parent && + ((s->cur_func->parent->func_kind & JS_FUNC_ASYNC) || + s->cur_func->parent->func_type == JS_PARSE_FUNC_CLASS_STATIC_INIT))))) { + if (s->token.u.ident.has_escape) { + s->token.u.ident.is_reserved = TRUE; + s->token.val = TOK_IDENT; + } else { + /* The keywords atoms are pre allocated */ + s->token.val = s->token.u.ident.atom - 1 + TOK_FIRST_KEYWORD; + } + } +} + +/* if the current token is an identifier or keyword, reparse it + according to the current function type */ +static void reparse_ident_token(JSParseState *s) +{ + if (s->token.val == TOK_IDENT || + (s->token.val >= TOK_FIRST_KEYWORD && + s->token.val <= TOK_LAST_KEYWORD)) { + s->token.val = TOK_IDENT; + s->token.u.ident.is_reserved = FALSE; + update_token_ident(s); + } +} + +/* 'c' is the first character. Return JS_ATOM_NULL in case of error */ +static JSAtom parse_ident(JSParseState *s, const uint8_t **pp, + BOOL *pident_has_escape, int c, BOOL is_private) +{ + const uint8_t *p, *p_next; + char ident_buf[128], *buf; + size_t ident_size, ident_pos; + JSAtom atom = JS_ATOM_NULL; + + p = *pp; + buf = ident_buf; + ident_size = sizeof(ident_buf); + ident_pos = 0; + if (is_private) + buf[ident_pos++] = '#'; + for(;;) { + if (c < 0x80) { + buf[ident_pos++] = c; + } else { + ident_pos += utf8_encode((uint8_t*)buf + ident_pos, c); + } + c = *p; + p_next = p + 1; + if (c == '\\' && *p_next == 'u') { + c = lre_parse_escape(&p_next, TRUE); + *pident_has_escape = TRUE; + } else if (c >= 0x80) { + c = utf8_decode(p, &p_next); + /* no need to test for invalid UTF-8, 0xFFFD is not ident_next */ + } + if (!lre_js_is_ident_next(c)) + break; + p = p_next; + if (unlikely(ident_pos >= ident_size - UTF8_CHAR_LEN_MAX)) { + if (ident_realloc(s->ctx, &buf, &ident_size, ident_buf)) + goto done; + } + } + /* buf is pure ASCII or UTF-8 encoded */ + atom = JS_NewAtomLen(s->ctx, buf, ident_pos); + done: + if (unlikely(buf != ident_buf)) + js_free(s->ctx, buf); + *pp = p; + return atom; +} + + +static __exception int next_token(JSParseState *s) +{ + const uint8_t *p, *p_next; + int c; + BOOL ident_has_escape; + JSAtom atom; + int flags, radix; + + if (js_check_stack_overflow(s->ctx->rt, 1000)) { + JS_ThrowStackOverflow(s->ctx); + return -1; + } + + free_token(s, &s->token); + + p = s->last_ptr = s->buf_ptr; + s->got_lf = FALSE; + s->last_line_num = s->token.line_num; + s->last_col_num = s->token.col_num; + redo: + s->token.line_num = s->line_num; + s->token.col_num = s->col_num; + s->token.ptr = p; + c = *p; + switch(c) { + case 0: + if (p >= s->buf_end) { + s->token.val = TOK_EOF; + } else { + goto def_token; + } + break; + case '`': + if (js_parse_template_part(s, p + 1)) + goto fail; + p = s->buf_ptr; + break; + case '\'': + case '\"': + if (js_parse_string(s, c, TRUE, p + 1, &s->token, &p)) + goto fail; + break; + case '\r': /* accept DOS and MAC newline sequences */ + if (p[1] == '\n') { + p++; + } + /* fall thru */ + case '\n': + p++; + line_terminator: + s->eol = &p[-1]; + s->mark = p; + s->got_lf = TRUE; + s->line_num++; + goto redo; + case '\f': + case '\v': + case ' ': + case '\t': + s->mark = ++p; + goto redo; + case '/': + if (p[1] == '*') { + /* comment */ + p += 2; + for(;;) { + if (*p == '\0' && p >= s->buf_end) { + js_parse_error(s, "unexpected end of comment"); + goto fail; + } + if (p[0] == '*' && p[1] == '/') { + p += 2; + break; + } + if (*p == '\n') { + s->line_num++; + s->got_lf = TRUE; /* considered as LF for ASI */ + s->eol = p++; + s->mark = p; + } else if (*p == '\r') { + s->got_lf = TRUE; /* considered as LF for ASI */ + p++; + } else if (*p >= 0x80) { + c = utf8_decode(p, &p); + /* ignore invalid UTF-8 in comments */ + if (c == CP_LS || c == CP_PS) { + s->got_lf = TRUE; /* considered as LF for ASI */ + } + } else { + p++; + } + } + s->mark = p; + goto redo; + } else if (p[1] == '/') { + /* line comment */ + p += 2; + skip_line_comment: + for(;;) { + if (*p == '\0' && p >= s->buf_end) + break; + if (*p == '\r' || *p == '\n') + break; + if (*p >= 0x80) { + c = utf8_decode(p, &p); + /* ignore invalid UTF-8 in comments */ + /* LS or PS are considered as line terminator */ + if (c == CP_LS || c == CP_PS) { + break; + } + } else { + p++; + } + } + s->mark = p; + goto redo; + } else if (p[1] == '=') { + p += 2; + s->token.val = TOK_DIV_ASSIGN; + } else { + p++; + s->token.val = c; + } + break; + case '\\': + if (p[1] == 'u') { + const uint8_t *p1 = p + 1; + int c1 = lre_parse_escape(&p1, TRUE); + if (c1 >= 0 && lre_js_is_ident_first(c1)) { + c = c1; + p = p1; + ident_has_escape = TRUE; + goto has_ident; + } else { + /* XXX: syntax error? */ + } + } + goto def_token; + case 'a': case 'b': case 'c': case 'd': + case 'e': case 'f': case 'g': case 'h': + case 'i': case 'j': case 'k': case 'l': + case 'm': case 'n': case 'o': case 'p': + case 'q': case 'r': case 's': case 't': + case 'u': case 'v': case 'w': case 'x': + case 'y': case 'z': + case 'A': case 'B': case 'C': case 'D': + case 'E': case 'F': case 'G': case 'H': + case 'I': case 'J': case 'K': case 'L': + case 'M': case 'N': case 'O': case 'P': + case 'Q': case 'R': case 'S': case 'T': + case 'U': case 'V': case 'W': case 'X': + case 'Y': case 'Z': + case '_': + case '$': + /* identifier */ + s->mark = p; + p++; + ident_has_escape = FALSE; + has_ident: + atom = parse_ident(s, &p, &ident_has_escape, c, FALSE); + if (atom == JS_ATOM_NULL) + goto fail; + s->token.u.ident.atom = atom; + s->token.u.ident.has_escape = ident_has_escape; + s->token.u.ident.is_reserved = FALSE; + s->token.val = TOK_IDENT; + update_token_ident(s); + break; + case '#': + /* private name */ + { + p++; + c = *p; + p_next = p + 1; + if (c == '\\' && *p_next == 'u') { + c = lre_parse_escape(&p_next, TRUE); + } else if (c >= 0x80) { + c = utf8_decode(p, &p_next); + if (p_next == p + 1) + goto invalid_utf8; + } + if (!lre_js_is_ident_first(c)) { + js_parse_error(s, "invalid first character of private name"); + goto fail; + } + p = p_next; + ident_has_escape = FALSE; /* not used */ + atom = parse_ident(s, &p, &ident_has_escape, c, TRUE); + if (atom == JS_ATOM_NULL) + goto fail; + s->token.u.ident.atom = atom; + s->token.val = TOK_PRIVATE_NAME; + } + break; + case '.': + if (p[1] == '.' && p[2] == '.') { + p += 3; + s->token.val = TOK_ELLIPSIS; + break; + } + if (p[1] >= '0' && p[1] <= '9') { + flags = ATOD_ACCEPT_UNDERSCORES | ATOD_ACCEPT_FLOAT; + radix = 10; + goto parse_number; + } + goto def_token; + case '0': + if (is_digit(p[1])) { /* handle legacy octal */ + if (s->cur_func->is_strict_mode) { + js_parse_error(s, "Octal literals are not allowed in strict mode"); + goto fail; + } + /* Legacy octal: no separators, no suffix, no floats, + base 8 unless non octal digits are detected */ + flags = 0; + radix = 8; + while (is_digit(*p)) { + if (*p >= '8' && *p <= '9') + radix = 10; + p++; + } + p = s->token.ptr; + goto parse_number; + } + if (p[1] == '_') { + js_parse_error(s, "Numeric separator can not be used after leading 0"); + goto fail; + } + flags = ATOD_ACCEPT_HEX_PREFIX | ATOD_ACCEPT_BIN_OCT | + ATOD_ACCEPT_FLOAT | ATOD_ACCEPT_UNDERSCORES | ATOD_ACCEPT_SUFFIX; + radix = 10; + goto parse_number; + case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': + case '9': + /* number */ + { + JSValue ret; + const char *p1; + + flags = ATOD_ACCEPT_FLOAT | ATOD_ACCEPT_UNDERSCORES | ATOD_ACCEPT_SUFFIX; + radix = 10; + parse_number: + p1 = (const char *)p; + ret = js_atof(s->ctx, p1, s->buf_end - p, &p1, radix, flags); + p = (const uint8_t *)p1; + if (JS_IsException(ret)) + goto fail; + /* reject `10instanceof Number` */ + if (JS_VALUE_IS_NAN(ret) || + lre_js_is_ident_next(utf8_decode(p, &p_next))) { + JS_FreeValue(s->ctx, ret); + js_parse_error(s, "invalid number literal"); + goto fail; + } + s->token.val = TOK_NUMBER; + s->token.u.num.val = ret; + } + break; + case '*': + if (p[1] == '=') { + p += 2; + s->token.val = TOK_MUL_ASSIGN; + } else if (p[1] == '*') { + if (p[2] == '=') { + p += 3; + s->token.val = TOK_POW_ASSIGN; + } else { + p += 2; + s->token.val = TOK_POW; + } + } else { + goto def_token; + } + break; + case '%': + if (p[1] == '=') { + p += 2; + s->token.val = TOK_MOD_ASSIGN; + } else { + goto def_token; + } + break; + case '+': + if (p[1] == '=') { + p += 2; + s->token.val = TOK_PLUS_ASSIGN; + } else if (p[1] == '+') { + p += 2; + s->token.val = TOK_INC; + } else { + goto def_token; + } + break; + case '-': + if (p[1] == '=') { + p += 2; + s->token.val = TOK_MINUS_ASSIGN; + } else if (p[1] == '-') { + if (s->allow_html_comments && p[2] == '>' && + (s->got_lf || s->last_ptr == s->buf_start)) { + /* Annex B: `-->` at beginning of line is an html comment end. + It extends to the end of the line. + */ + goto skip_line_comment; + } + p += 2; + s->token.val = TOK_DEC; + } else { + goto def_token; + } + break; + case '<': + if (p[1] == '=') { + p += 2; + s->token.val = TOK_LTE; + } else if (p[1] == '<') { + if (p[2] == '=') { + p += 3; + s->token.val = TOK_SHL_ASSIGN; + } else { + p += 2; + s->token.val = TOK_SHL; + } + } else if (s->allow_html_comments && + p[1] == '!' && p[2] == '-' && p[3] == '-') { + /* Annex B: handle `<!--` single line html comments */ + goto skip_line_comment; + } else { + goto def_token; + } + break; + case '>': + if (p[1] == '=') { + p += 2; + s->token.val = TOK_GTE; + } else if (p[1] == '>') { + if (p[2] == '>') { + if (p[3] == '=') { + p += 4; + s->token.val = TOK_SHR_ASSIGN; + } else { + p += 3; + s->token.val = TOK_SHR; + } + } else if (p[2] == '=') { + p += 3; + s->token.val = TOK_SAR_ASSIGN; + } else { + p += 2; + s->token.val = TOK_SAR; + } + } else { + goto def_token; + } + break; + case '=': + if (p[1] == '=') { + if (p[2] == '=') { + p += 3; + s->token.val = TOK_STRICT_EQ; + } else { + p += 2; + s->token.val = TOK_EQ; + } + } else if (p[1] == '>') { + p += 2; + s->token.val = TOK_ARROW; + } else { + goto def_token; + } + break; + case '!': + if (p[1] == '=') { + if (p[2] == '=') { + p += 3; + s->token.val = TOK_STRICT_NEQ; + } else { + p += 2; + s->token.val = TOK_NEQ; + } + } else { + goto def_token; + } + break; + case '&': + if (p[1] == '=') { + p += 2; + s->token.val = TOK_AND_ASSIGN; + } else if (p[1] == '&') { + if (p[2] == '=') { + p += 3; + s->token.val = TOK_LAND_ASSIGN; + } else { + p += 2; + s->token.val = TOK_LAND; + } + } else { + goto def_token; + } + break; + case '^': + if (p[1] == '=') { + p += 2; + s->token.val = TOK_XOR_ASSIGN; + } else { + goto def_token; + } + break; + case '|': + if (p[1] == '=') { + p += 2; + s->token.val = TOK_OR_ASSIGN; + } else if (p[1] == '|') { + if (p[2] == '=') { + p += 3; + s->token.val = TOK_LOR_ASSIGN; + } else { + p += 2; + s->token.val = TOK_LOR; + } + } else { + goto def_token; + } + break; + case '?': + if (p[1] == '?') { + if (p[2] == '=') { + p += 3; + s->token.val = TOK_DOUBLE_QUESTION_MARK_ASSIGN; + } else { + p += 2; + s->token.val = TOK_DOUBLE_QUESTION_MARK; + } + } else if (p[1] == '.' && !(p[2] >= '0' && p[2] <= '9')) { + p += 2; + s->token.val = TOK_QUESTION_MARK_DOT; + } else { + goto def_token; + } + break; + default: + if (c >= 0x80) { /* non-ASCII code-point */ + c = utf8_decode(p, &p_next); + if (p_next == p + 1) + goto invalid_utf8; + p = p_next; + switch(c) { + case CP_PS: + case CP_LS: + /* XXX: should avoid incrementing line_number, but + needed to handle HTML comments */ + goto line_terminator; + default: + if (lre_is_space(c)) { + s->mark = p; + goto redo; + } else if (lre_js_is_ident_first(c)) { + ident_has_escape = FALSE; + goto has_ident; + } else { + js_parse_error(s, "unexpected character"); + goto fail; + } + } + } + def_token: + s->token.val = c; + p++; + break; + } + s->token.col_num = max_int(1, s->mark - s->eol); + s->buf_ptr = p; + + // dump_token(s, &s->token); + return 0; + + invalid_utf8: + js_parse_error(s, "invalid UTF-8 sequence"); + fail: + s->token.val = TOK_ERROR; + return -1; +} + +static int json_parse_error(JSParseState *s, const uint8_t *curp, const char *msg) +{ + const uint8_t *p, *line_start; + int position = curp - s->buf_start; + int line = 1; + for (line_start = p = s->buf_start; p < curp; p++) { + /* column count does not account for TABs nor wide characters */ + if (*p == '\r' || *p == '\n') { + p += 1 + (p[0] == '\r' && p[1] == '\n'); + line++; + line_start = p; + } + } + return js_parse_error(s, "%s in JSON at position %d (line %d column %d)", + msg, position, line, (int)(p - line_start) + 1); +} + +static int json_parse_string(JSParseState *s, const uint8_t **pp) +{ + const uint8_t *p, *p_next; + int i; + uint32_t c; + StringBuffer b_s, *b = &b_s; + + if (string_buffer_init(s->ctx, b, 32)) + goto fail; + + p = *pp; + for(;;) { + if (p >= s->buf_end) { + goto end_of_input; + } + c = *p++; + if (c == '"') + break; + if (c < 0x20) { + json_parse_error(s, p - 1, "Bad control character in string literal"); + goto fail; + } + if (c == '\\') { + c = *p++; + switch(c) { + case 'b': c = '\b'; break; + case 'f': c = '\f'; break; + case 'n': c = '\n'; break; + case 'r': c = '\r'; break; + case 't': c = '\t'; break; + case '\"': break; + case '\\': break; + case '/': break; /* for v8 compatibility */ + case 'u': + c = 0; + for(i = 0; i < 4; i++) { + int h = from_hex(*p++); + if (h < 0) { + json_parse_error(s, p - 1, "Bad Unicode escape"); + goto fail; + } + c = (c << 4) | h; + } + break; + default: + if (p > s->buf_end) + goto end_of_input; + json_parse_error(s, p - 1, "Bad escaped character"); + goto fail; + } + } else + if (c >= 0x80) { + c = utf8_decode(p - 1, &p_next); + if (p_next == p) { + json_parse_error(s, p - 1, "Bad UTF-8 sequence"); + goto fail; + } + p = p_next; + } + if (string_buffer_putc(b, c)) + goto fail; + } + s->token.val = TOK_STRING; + s->token.u.str.sep = '"'; + s->token.u.str.str = string_buffer_end(b); + *pp = p; + return 0; + + end_of_input: + js_parse_error(s, "Unexpected end of JSON input"); + fail: + string_buffer_free(b); + return -1; +} + +static int json_parse_number(JSParseState *s, const uint8_t **pp) +{ + const uint8_t *p = *pp; + const uint8_t *p_start = p; + + if (*p == '+' || *p == '-') + p++; + + if (!is_digit(*p)) + return js_parse_error(s, "Unexpected token '%c'", *p_start); + + if (p[0] == '0' && is_digit(p[1])) + return json_parse_error(s, p, "Unexpected number"); + + while (is_digit(*p)) + p++; + + if (*p == '.') { + p++; + if (!is_digit(*p)) + return json_parse_error(s, p, "Unterminated fractional number"); + while (is_digit(*p)) + p++; + } + if (*p == 'e' || *p == 'E') { + p++; + if (*p == '+' || *p == '-') + p++; + if (!is_digit(*p)) + return json_parse_error(s, p, "Exponent part is missing a number"); + while (is_digit(*p)) + p++; + } + s->token.val = TOK_NUMBER; + s->token.u.num.val = js_float64(strtod((const char *)p_start, NULL)); + *pp = p; + return 0; +} + +/* 'c' is the first character. Return JS_ATOM_NULL in case of error */ +static JSAtom json_parse_ident(JSParseState *s, const uint8_t **pp, int c) +{ + const uint8_t *p; + char ident_buf[128], *buf; + size_t ident_size, ident_pos; + JSAtom atom; + + p = *pp; + buf = ident_buf; + ident_size = sizeof(ident_buf); + ident_pos = 0; + for(;;) { + buf[ident_pos++] = c; + c = *p; + if (c >= 128 || + !((lre_id_continue_table_ascii[c >> 5] >> (c & 31)) & 1)) + break; + p++; + if (unlikely(ident_pos >= ident_size - UTF8_CHAR_LEN_MAX)) { + if (ident_realloc(s->ctx, &buf, &ident_size, ident_buf)) { + atom = JS_ATOM_NULL; + goto done; + } + } + } + /* buf contains pure ASCII */ + atom = JS_NewAtomLen(s->ctx, buf, ident_pos); + done: + if (unlikely(buf != ident_buf)) + js_free(s->ctx, buf); + *pp = p; + return atom; +} + +static __exception int json_next_token(JSParseState *s) +{ + const uint8_t *p, *p_next; + int c; + JSAtom atom; + + if (js_check_stack_overflow(s->ctx->rt, 1000)) { + JS_ThrowStackOverflow(s->ctx); + return -1; + } + + free_token(s, &s->token); + + p = s->last_ptr = s->buf_ptr; + s->last_line_num = s->token.line_num; + s->last_col_num = s->token.col_num; + redo: + s->token.line_num = s->line_num; + s->token.col_num = s->col_num; + s->token.ptr = p; + c = *p; + switch(c) { + case 0: + if (p >= s->buf_end) { + s->token.val = TOK_EOF; + } else { + goto def_token; + } + break; + case '\'': + /* JSON does not accept single quoted strings */ + goto def_token; + case '\"': + p++; + if (json_parse_string(s, &p)) + goto fail; + break; + case '\r': /* accept DOS and MAC newline sequences */ + if (p[1] == '\n') { + p++; + } + /* fall thru */ + case '\n': + s->line_num++; + s->eol = p++; + s->mark = p; + goto redo; + case '\f': + case '\v': + /* JSONWhitespace does not match <VT>, nor <FF> */ + goto def_token; + case ' ': + case '\t': + p++; + s->mark = p; + goto redo; + case '/': + /* JSON does not accept comments */ + goto def_token; + case 'a': case 'b': case 'c': case 'd': + case 'e': case 'f': case 'g': case 'h': + case 'i': case 'j': case 'k': case 'l': + case 'm': case 'n': case 'o': case 'p': + case 'q': case 'r': case 's': case 't': + case 'u': case 'v': case 'w': case 'x': + case 'y': case 'z': + case 'A': case 'B': case 'C': case 'D': + case 'E': case 'F': case 'G': case 'H': + case 'I': case 'J': case 'K': case 'L': + case 'M': case 'N': case 'O': case 'P': + case 'Q': case 'R': case 'S': case 'T': + case 'U': case 'V': case 'W': case 'X': + case 'Y': case 'Z': + case '_': + case '$': + /* identifier : only pure ascii characters are accepted */ + p++; + atom = json_parse_ident(s, &p, c); + if (atom == JS_ATOM_NULL) + goto fail; + s->token.u.ident.atom = atom; + s->token.u.ident.has_escape = FALSE; + s->token.u.ident.is_reserved = FALSE; + s->token.val = TOK_IDENT; + break; + case '-': + if (!is_digit(p[1])) { + json_parse_error(s, p, "No number after minus sign"); + goto fail; + } + goto parse_number; + case '0': + if (is_digit(p[1])) { + json_parse_error(s, p, "Unexpected number"); + goto fail; + } + goto parse_number; + case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': + case '9': + /* number */ + parse_number: + if (json_parse_number(s, &p)) + goto fail; + break; + default: + if (c >= 0x80) { + c = utf8_decode(p, &p_next); + if (p_next == p + 1) { + js_parse_error(s, "Unexpected token '\\x%02x' in JSON", *p); + } else { + if (c > 0xFFFF) { + c = get_hi_surrogate(c); + } + js_parse_error(s, "Unexpected token '\\u%04x' in JSON", c); + } + goto fail; + } + def_token: + s->token.val = c; + p++; + break; + } + s->token.col_num = s->mark - s->eol; + s->buf_ptr = p; + + // dump_token(s, &s->token); + return 0; + + fail: + s->token.val = TOK_ERROR; + return -1; +} + +/* only used for ':' and '=>', 'let' or 'function' look-ahead. *pp is + only set if TOK_IMPORT is returned */ +/* XXX: handle all unicode cases */ +static int simple_next_token(const uint8_t **pp, BOOL no_line_terminator) +{ + const uint8_t *p; + uint32_t c; + + /* skip spaces and comments */ + p = *pp; + for (;;) { + switch(c = *p++) { + case '\r': + case '\n': + if (no_line_terminator) + return '\n'; + continue; + case ' ': + case '\t': + case '\v': + case '\f': + continue; + case '/': + if (*p == '/') { + if (no_line_terminator) + return '\n'; + while (*p && *p != '\r' && *p != '\n') + p++; + continue; + } + if (*p == '*') { + while (*++p) { + if ((*p == '\r' || *p == '\n') && no_line_terminator) + return '\n'; + if (*p == '*' && p[1] == '/') { + p += 2; + break; + } + } + continue; + } + break; + case '=': + if (*p == '>') + return TOK_ARROW; + break; + default: + if (lre_js_is_ident_first(c)) { + if (c == 'i') { + if (p[0] == 'n' && !lre_js_is_ident_next(p[1])) { + return TOK_IN; + } + if (p[0] == 'm' && p[1] == 'p' && p[2] == 'o' && + p[3] == 'r' && p[4] == 't' && + !lre_js_is_ident_next(p[5])) { + *pp = p + 5; + return TOK_IMPORT; + } + } else if (c == 'o' && *p == 'f' && !lre_js_is_ident_next(p[1])) { + return TOK_OF; + } else if (c == 'e' && + p[0] == 'x' && p[1] == 'p' && p[2] == 'o' && + p[3] == 'r' && p[4] == 't' && + !lre_js_is_ident_next(p[5])) { + *pp = p + 5; + return TOK_EXPORT; + } else if (c == 'f' && p[0] == 'u' && p[1] == 'n' && + p[2] == 'c' && p[3] == 't' && p[4] == 'i' && + p[5] == 'o' && p[6] == 'n' && !lre_js_is_ident_next(p[7])) { + return TOK_FUNCTION; + } else if (c == 'a' && p[0] == 'w' && p[1] == 'a' && + p[2] == 'i' && p[3] == 't' && !lre_js_is_ident_next(p[4])) { + return TOK_AWAIT; + } + return TOK_IDENT; + } + break; + } + return c; + } +} + +static int peek_token(JSParseState *s, BOOL no_line_terminator) +{ + const uint8_t *p = s->buf_ptr; + return simple_next_token(&p, no_line_terminator); +} + +static void skip_shebang(const uint8_t **pp, const uint8_t *buf_end) +{ + const uint8_t *p = *pp; + int c; + + if (p[0] == '#' && p[1] == '!') { + p += 2; + while (p < buf_end) { + if (*p == '\n' || *p == '\r') { + break; + } else if (*p >= 0x80) { + c = utf8_decode(p, &p); + /* purposely ignore UTF-8 encoding errors in this comment line */ + if (c == CP_LS || c == CP_PS) + break; + } else { + p++; + } + } + *pp = p; + } +} + +static inline int get_prev_opcode(JSFunctionDef *fd) { + if (fd->last_opcode_pos < 0) + return OP_invalid; + else + return fd->byte_code.buf[fd->last_opcode_pos]; +} + +static BOOL js_is_live_code(JSParseState *s) { + switch (get_prev_opcode(s->cur_func)) { + case OP_tail_call: + case OP_tail_call_method: + case OP_return: + case OP_return_undef: + case OP_return_async: + case OP_throw: + case OP_throw_error: + case OP_goto: + case OP_goto8: + case OP_goto16: + case OP_ret: + return FALSE; + default: + return TRUE; + } +} + +static void emit_u8(JSParseState *s, uint8_t val) +{ + dbuf_putc(&s->cur_func->byte_code, val); +} + +static void emit_u16(JSParseState *s, uint16_t val) +{ + dbuf_put_u16(&s->cur_func->byte_code, val); +} + +static void emit_u32(JSParseState *s, uint32_t val) +{ + dbuf_put_u32(&s->cur_func->byte_code, val); +} + +static void emit_source_loc(JSParseState *s) +{ + JSFunctionDef *fd = s->cur_func; + DynBuf *bc = &fd->byte_code; + + dbuf_putc(bc, OP_source_loc); + dbuf_put_u32(bc, s->last_line_num); + dbuf_put_u32(bc, s->last_col_num); +} + +static void emit_op(JSParseState *s, uint8_t val) +{ + JSFunctionDef *fd = s->cur_func; + DynBuf *bc = &fd->byte_code; + + fd->last_opcode_pos = bc->size; + dbuf_putc(bc, val); +} + +static void emit_atom(JSParseState *s, JSAtom name) +{ + emit_u32(s, JS_DupAtom(s->ctx, name)); +} + +static force_inline uint32_t get_index_hash(JSAtom atom, int hash_bits) +{ + return (atom * 0x9e370001) >> (32 - hash_bits); +} + +static void emit_ic(JSParseState *s, JSAtom atom) +{ + uint32_t h; + JSContext *ctx; + JSInlineCache *ic; + JSInlineCacheHashSlot *ch; + + ic = s->cur_func->ic; + ctx = s->ctx; + if (ic->count + 1 >= ic->capacity && resize_ic_hash(ctx, ic)) + return; + h = get_index_hash(atom, ic->hash_bits); + for (ch = ic->hash[h]; ch != NULL; ch = ch->next) + if (ch->atom == atom) + return; + ch = js_malloc(ctx, sizeof(*ch)); + if (unlikely(!ch)) + return; + ch->atom = JS_DupAtom(ctx, atom); + ch->index = 0; + ch->next = ic->hash[h]; + ic->hash[h] = ch; + ic->count += 1; +} + +static int update_label(JSFunctionDef *s, int label, int delta) +{ + LabelSlot *ls; + + assert(label >= 0 && label < s->label_count); + ls = &s->label_slots[label]; + ls->ref_count += delta; + assert(ls->ref_count >= 0); + return ls->ref_count; +} + +static int new_label_fd(JSFunctionDef *fd, int label) +{ + LabelSlot *ls; + + if (label < 0) { + if (js_resize_array(fd->ctx, (void *)&fd->label_slots, + sizeof(fd->label_slots[0]), + &fd->label_size, fd->label_count + 1)) + return -1; + label = fd->label_count++; + ls = &fd->label_slots[label]; + ls->ref_count = 0; + ls->pos = -1; + ls->pos2 = -1; + ls->addr = -1; + ls->first_reloc = NULL; + } + return label; +} + +static int new_label(JSParseState *s) +{ + return new_label_fd(s->cur_func, -1); +} + +/* don't update the last opcode and don't emit line number info */ +static void emit_label_raw(JSParseState *s, int label) +{ + emit_u8(s, OP_label); + emit_u32(s, label); + s->cur_func->label_slots[label].pos = s->cur_func->byte_code.size; +} + +/* return the label ID offset */ +static int emit_label(JSParseState *s, int label) +{ + if (label >= 0) { + emit_op(s, OP_label); + emit_u32(s, label); + s->cur_func->label_slots[label].pos = s->cur_func->byte_code.size; + return s->cur_func->byte_code.size - 4; + } else { + return -1; + } +} + +/* return label or -1 if dead code */ +static int emit_goto(JSParseState *s, int opcode, int label) +{ + if (js_is_live_code(s)) { + if (label < 0) + label = new_label(s); + emit_op(s, opcode); + emit_u32(s, label); + s->cur_func->label_slots[label].ref_count++; + return label; + } + return -1; +} + +/* return the constant pool index. 'val' is not duplicated. */ +static int cpool_add(JSParseState *s, JSValue val) +{ + JSFunctionDef *fd = s->cur_func; + + if (js_resize_array(s->ctx, (void *)&fd->cpool, sizeof(fd->cpool[0]), + &fd->cpool_size, fd->cpool_count + 1)) + return -1; + fd->cpool[fd->cpool_count++] = val; + return fd->cpool_count - 1; +} + +static __exception int emit_push_const(JSParseState *s, JSValue val, + BOOL as_atom) +{ + int idx; + + if (JS_VALUE_GET_TAG(val) == JS_TAG_STRING && as_atom) { + JSAtom atom; + /* warning: JS_NewAtomStr frees the string value */ + JS_DupValue(s->ctx, val); + atom = JS_NewAtomStr(s->ctx, JS_VALUE_GET_STRING(val)); + if (atom != JS_ATOM_NULL && !__JS_AtomIsTaggedInt(atom)) { + emit_op(s, OP_push_atom_value); + emit_u32(s, atom); + return 0; + } + } + + idx = cpool_add(s, JS_DupValue(s->ctx, val)); + if (idx < 0) + return -1; + emit_op(s, OP_push_const); + emit_u32(s, idx); + return 0; +} + +// perl hash; variation of k&r hash with a different magic multiplier +// and a final shuffle to improve distribution of the low-order bits +static uint32_t hash_bytes(uint32_t h, const void *b, size_t n) +{ + const char *p; + + for (p = b; p < (char *)b + n; p++) + h = 33*h + *p; + h += h >> 5; + return h; +} + +static uint32_t hash_atom(JSAtom atom) +{ + return hash_bytes(0, &atom, sizeof(atom)); +} + +// caveat emptor: the table size must be a power of two in order for +// masking to work, and the load factor constant must be an odd number (5) +// +// f(n)=n+n/t is used to estimate the load factor but changing t to an +// even number introduces gaps in the output of f, sometimes "jumping" +// over the next power of two; it's at powers of two when the hash table +// must be resized +static int update_var_htab(JSContext *ctx, JSFunctionDef *fd) +{ + uint32_t i, j, k, m, *p; + + if (fd->var_count < 27) // 27 + 27/5 == 32 + return 0; + k = fd->var_count - 1; + m = fd->var_count + fd->var_count/5; + if (m & (m - 1)) // unless power of two + goto insert; + m *= 2; + p = js_realloc(ctx, fd->vars_htab, m * sizeof(*fd->vars_htab)); + if (!p) + return -1; + for (i = 0; i < m; i++) + p[i] = UINT32_MAX; + fd->vars_htab = p; + k = 0; + m--; +insert: + m = UINT32_MAX >> clz32(m); + do { + i = hash_atom(fd->vars[k].var_name); + j = 1; + for (;;) { + p = &fd->vars_htab[i & m]; + if (*p == UINT32_MAX) + break; + i += j; + j += 1; // quadratic probing + } + *p = k++; + } while (k < (uint32_t)fd->var_count); + return 0; +} + +static int find_var_htab(JSFunctionDef *fd, JSAtom var_name) +{ + uint32_t i, j, m, *p; + + i = hash_atom(var_name); + j = 1; + m = fd->var_count + fd->var_count/5; + m = UINT32_MAX >> clz32(m); + for (;;) { + p = &fd->vars_htab[i & m]; + if (*p == UINT32_MAX) + return -1; + if (fd->vars[*p].var_name == var_name) + return *p; + i += j; + j += 1; // quadratic probing + } + return -1; // pacify compiler +} + +/* return the variable index or -1 if not found, + add ARGUMENT_VAR_OFFSET for argument variables */ +static int find_arg(JSContext *ctx, JSFunctionDef *fd, JSAtom name) +{ + int i; + for(i = fd->arg_count; i-- > 0;) { + if (fd->args[i].var_name == name) + return i | ARGUMENT_VAR_OFFSET; + } + return -1; +} + +static int find_var(JSContext *ctx, JSFunctionDef *fd, JSAtom name) +{ + JSVarDef *vd; + int i; + + if (fd->vars_htab) { + i = find_var_htab(fd, name); + if (i == -1) + goto not_found; + vd = &fd->vars[i]; + if (fd->vars[i].scope_level == 0) + return i; + } + for(i = fd->var_count; i-- > 0;) { + vd = &fd->vars[i]; + if (vd->var_name == name) + if (vd->scope_level == 0) + return i; + } +not_found: + return find_arg(ctx, fd, name); +} + +/* find a variable declaration in a given scope */ +static int find_var_in_scope(JSContext *ctx, JSFunctionDef *fd, + JSAtom name, int scope_level) +{ + int scope_idx; + for(scope_idx = fd->scopes[scope_level].first; scope_idx >= 0; + scope_idx = fd->vars[scope_idx].scope_next) { + if (fd->vars[scope_idx].scope_level != scope_level) + break; + if (fd->vars[scope_idx].var_name == name) + return scope_idx; + } + return -1; +} + +/* return true if scope == parent_scope or if scope is a child of + parent_scope */ +static BOOL is_child_scope(JSContext *ctx, JSFunctionDef *fd, + int scope, int parent_scope) +{ + while (scope >= 0) { + if (scope == parent_scope) + return TRUE; + scope = fd->scopes[scope].parent; + } + return FALSE; +} + +/* find a 'var' declaration in the same scope or a child scope */ +static int find_var_in_child_scope(JSContext *ctx, JSFunctionDef *fd, + JSAtom name, int scope_level) +{ + int i; + for(i = 0; i < fd->var_count; i++) { + JSVarDef *vd = &fd->vars[i]; + if (vd->var_name == name && vd->scope_level == 0) { + if (is_child_scope(ctx, fd, vd->scope_next, + scope_level)) + return i; + } + } + return -1; +} + + +static JSGlobalVar *find_global_var(JSFunctionDef *fd, JSAtom name) +{ + int i; + for(i = 0; i < fd->global_var_count; i++) { + JSGlobalVar *hf = &fd->global_vars[i]; + if (hf->var_name == name) + return hf; + } + return NULL; + +} + +static JSGlobalVar *find_lexical_global_var(JSFunctionDef *fd, JSAtom name) +{ + JSGlobalVar *hf = find_global_var(fd, name); + if (hf && hf->is_lexical) + return hf; + else + return NULL; +} + +static int find_lexical_decl(JSContext *ctx, JSFunctionDef *fd, JSAtom name, + int scope_idx, BOOL check_catch_var) +{ + while (scope_idx >= 0) { + JSVarDef *vd = &fd->vars[scope_idx]; + if (vd->var_name == name && + (vd->is_lexical || (vd->var_kind == JS_VAR_CATCH && + check_catch_var))) + return scope_idx; + scope_idx = vd->scope_next; + } + + if (fd->is_eval && fd->eval_type == JS_EVAL_TYPE_GLOBAL) { + if (find_lexical_global_var(fd, name)) + return GLOBAL_VAR_OFFSET; + } + return -1; +} + +static int push_scope(JSParseState *s) { + if (s->cur_func) { + JSFunctionDef *fd = s->cur_func; + int scope = fd->scope_count; + /* XXX: should check for scope overflow */ + if ((fd->scope_count + 1) > fd->scope_size) { + int new_size; + size_t slack; + JSVarScope *new_buf; + /* XXX: potential arithmetic overflow */ + new_size = max_int(fd->scope_count + 1, fd->scope_size * 3 / 2); + if (fd->scopes == fd->def_scope_array) { + new_buf = js_realloc2(s->ctx, NULL, new_size * sizeof(*fd->scopes), &slack); + if (!new_buf) + return -1; + memcpy(new_buf, fd->scopes, fd->scope_count * sizeof(*fd->scopes)); + } else { + new_buf = js_realloc2(s->ctx, fd->scopes, new_size * sizeof(*fd->scopes), &slack); + if (!new_buf) + return -1; + } + new_size += slack / sizeof(*new_buf); + fd->scopes = new_buf; + fd->scope_size = new_size; + } + fd->scope_count++; + fd->scopes[scope].parent = fd->scope_level; + fd->scopes[scope].first = fd->scope_first; + emit_op(s, OP_enter_scope); + emit_u16(s, scope); + return fd->scope_level = scope; + } + return 0; +} + +static int get_first_lexical_var(JSFunctionDef *fd, int scope) +{ + while (scope >= 0) { + int scope_idx = fd->scopes[scope].first; + if (scope_idx >= 0) + return scope_idx; + scope = fd->scopes[scope].parent; + } + return -1; +} + +static void pop_scope(JSParseState *s) { + if (s->cur_func) { + /* disable scoped variables */ + JSFunctionDef *fd = s->cur_func; + int scope = fd->scope_level; + emit_op(s, OP_leave_scope); + emit_u16(s, scope); + fd->scope_level = fd->scopes[scope].parent; + fd->scope_first = get_first_lexical_var(fd, fd->scope_level); + } +} + +static void close_scopes(JSParseState *s, int scope, int scope_stop) +{ + while (scope > scope_stop) { + emit_op(s, OP_leave_scope); + emit_u16(s, scope); + scope = s->cur_func->scopes[scope].parent; + } +} + +/* return the variable index or -1 if error */ +static int add_var(JSContext *ctx, JSFunctionDef *fd, JSAtom name) +{ + JSVarDef *vd; + + /* the local variable indexes are currently stored on 16 bits */ + if (fd->var_count >= JS_MAX_LOCAL_VARS) { + // XXX: add_var() should take JSParseState *s and use js_parse_error + JS_ThrowSyntaxError(ctx, "too many variables declared (only %d allowed)", + JS_MAX_LOCAL_VARS - 1); + return -1; + } + if (js_resize_array(ctx, (void **)&fd->vars, sizeof(fd->vars[0]), + &fd->var_size, fd->var_count + 1)) + return -1; + vd = &fd->vars[fd->var_count++]; + memset(vd, 0, sizeof(*vd)); + vd->var_name = JS_DupAtom(ctx, name); + vd->func_pool_idx = -1; + if (update_var_htab(ctx, fd)) + return -1; + return fd->var_count - 1; +} + +static int add_scope_var(JSContext *ctx, JSFunctionDef *fd, JSAtom name, + JSVarKindEnum var_kind) +{ + int idx = add_var(ctx, fd, name); + if (idx >= 0) { + JSVarDef *vd = &fd->vars[idx]; + vd->var_kind = var_kind; + vd->scope_level = fd->scope_level; + vd->scope_next = fd->scope_first; + fd->scopes[fd->scope_level].first = idx; + fd->scope_first = idx; + } + return idx; +} + +static int add_func_var(JSContext *ctx, JSFunctionDef *fd, JSAtom name) +{ + int idx = fd->func_var_idx; + if (idx < 0 && (idx = add_var(ctx, fd, name)) >= 0) { + fd->func_var_idx = idx; + fd->vars[idx].var_kind = JS_VAR_FUNCTION_NAME; + if (fd->is_strict_mode) + fd->vars[idx].is_const = TRUE; + } + return idx; +} + +static int add_arguments_var(JSContext *ctx, JSFunctionDef *fd) +{ + int idx = fd->arguments_var_idx; + if (idx < 0 && (idx = add_var(ctx, fd, JS_ATOM_arguments)) >= 0) { + fd->arguments_var_idx = idx; + } + return idx; +} + +/* add an argument definition in the argument scope. Only needed when + "eval()" may be called in the argument scope. Return 0 if OK. */ +static int add_arguments_arg(JSContext *ctx, JSFunctionDef *fd) +{ + int idx; + if (fd->arguments_arg_idx < 0) { + idx = find_var_in_scope(ctx, fd, JS_ATOM_arguments, ARG_SCOPE_INDEX); + if (idx < 0) { + /* XXX: the scope links are not fully updated. May be an + issue if there are child scopes of the argument + scope */ + idx = add_var(ctx, fd, JS_ATOM_arguments); + if (idx < 0) + return -1; + fd->vars[idx].scope_next = fd->scopes[ARG_SCOPE_INDEX].first; + fd->scopes[ARG_SCOPE_INDEX].first = idx; + fd->vars[idx].scope_level = ARG_SCOPE_INDEX; + fd->vars[idx].is_lexical = TRUE; + + fd->arguments_arg_idx = idx; + } + } + return 0; +} + +static int add_arg(JSContext *ctx, JSFunctionDef *fd, JSAtom name) +{ + JSVarDef *vd; + + /* the local variable indexes are currently stored on 16 bits */ + if (fd->arg_count >= JS_MAX_LOCAL_VARS) { + // XXX: add_arg() should take JSParseState *s and use js_parse_error + JS_ThrowSyntaxError(ctx, "too many parameters in function definition (only %d allowed)", + JS_MAX_LOCAL_VARS - 1); + return -1; + } + if (js_resize_array(ctx, (void **)&fd->args, sizeof(fd->args[0]), + &fd->arg_size, fd->arg_count + 1)) + return -1; + vd = &fd->args[fd->arg_count++]; + memset(vd, 0, sizeof(*vd)); + vd->var_name = JS_DupAtom(ctx, name); + vd->func_pool_idx = -1; + return fd->arg_count - 1; +} + +/* add a global variable definition */ +static JSGlobalVar *add_global_var(JSContext *ctx, JSFunctionDef *s, + JSAtom name) +{ + JSGlobalVar *hf; + + if (js_resize_array(ctx, (void **)&s->global_vars, + sizeof(s->global_vars[0]), + &s->global_var_size, s->global_var_count + 1)) + return NULL; + hf = &s->global_vars[s->global_var_count++]; + hf->cpool_idx = -1; + hf->force_init = FALSE; + hf->is_lexical = FALSE; + hf->is_const = FALSE; + hf->scope_level = s->scope_level; + hf->var_name = JS_DupAtom(ctx, name); + return hf; +} + +typedef enum { + JS_VAR_DEF_WITH, + JS_VAR_DEF_LET, + JS_VAR_DEF_CONST, + JS_VAR_DEF_FUNCTION_DECL, /* function declaration */ + JS_VAR_DEF_NEW_FUNCTION_DECL, /* async/generator function declaration */ + JS_VAR_DEF_CATCH, + JS_VAR_DEF_VAR, +} JSVarDefEnum; + +static int define_var(JSParseState *s, JSFunctionDef *fd, JSAtom name, + JSVarDefEnum var_def_type) +{ + JSContext *ctx = s->ctx; + JSVarDef *vd; + int idx; + + switch (var_def_type) { + case JS_VAR_DEF_WITH: + idx = add_scope_var(ctx, fd, name, JS_VAR_NORMAL); + break; + + case JS_VAR_DEF_LET: + case JS_VAR_DEF_CONST: + case JS_VAR_DEF_FUNCTION_DECL: + case JS_VAR_DEF_NEW_FUNCTION_DECL: + idx = find_lexical_decl(ctx, fd, name, fd->scope_first, TRUE); + if (idx >= 0) { + if (idx < GLOBAL_VAR_OFFSET) { + if (fd->vars[idx].scope_level == fd->scope_level) { + /* same scope: in non strict mode, functions + can be redefined (annex B.3.3.4). */ + if (!(!fd->is_strict_mode && + var_def_type == JS_VAR_DEF_FUNCTION_DECL && + fd->vars[idx].var_kind == JS_VAR_FUNCTION_DECL)) { + goto redef_lex_error; + } + } else if (fd->vars[idx].var_kind == JS_VAR_CATCH && (fd->vars[idx].scope_level + 2) == fd->scope_level) { + goto redef_lex_error; + } + } else { + if (fd->scope_level == fd->body_scope) { + redef_lex_error: + /* redefining a scoped var in the same scope: error */ + return js_parse_error(s, "invalid redefinition of lexical identifier"); + } + } + } + if (var_def_type != JS_VAR_DEF_FUNCTION_DECL && + var_def_type != JS_VAR_DEF_NEW_FUNCTION_DECL && + fd->scope_level == fd->body_scope && + find_arg(ctx, fd, name) >= 0) { + /* lexical variable redefines a parameter name */ + return js_parse_error(s, "invalid redefinition of parameter name"); + } + + if (find_var_in_child_scope(ctx, fd, name, fd->scope_level) >= 0) { + return js_parse_error(s, "invalid redefinition of a variable"); + } + + if (fd->is_global_var) { + JSGlobalVar *hf; + hf = find_global_var(fd, name); + if (hf && is_child_scope(ctx, fd, hf->scope_level, + fd->scope_level)) { + return js_parse_error(s, "invalid redefinition of global identifier"); + } + } + + if (fd->is_eval && + (fd->eval_type == JS_EVAL_TYPE_GLOBAL || + fd->eval_type == JS_EVAL_TYPE_MODULE) && + fd->scope_level == fd->body_scope) { + JSGlobalVar *hf; + hf = add_global_var(s->ctx, fd, name); + if (!hf) + return -1; + hf->is_lexical = TRUE; + hf->is_const = (var_def_type == JS_VAR_DEF_CONST); + idx = GLOBAL_VAR_OFFSET; + } else { + JSVarKindEnum var_kind; + if (var_def_type == JS_VAR_DEF_FUNCTION_DECL) + var_kind = JS_VAR_FUNCTION_DECL; + else if (var_def_type == JS_VAR_DEF_NEW_FUNCTION_DECL) + var_kind = JS_VAR_NEW_FUNCTION_DECL; + else + var_kind = JS_VAR_NORMAL; + idx = add_scope_var(ctx, fd, name, var_kind); + if (idx >= 0) { + vd = &fd->vars[idx]; + vd->is_lexical = 1; + vd->is_const = (var_def_type == JS_VAR_DEF_CONST); + } + } + break; + + case JS_VAR_DEF_CATCH: + idx = add_scope_var(ctx, fd, name, JS_VAR_CATCH); + break; + + case JS_VAR_DEF_VAR: + if (find_lexical_decl(ctx, fd, name, fd->scope_first, + FALSE) >= 0) { + invalid_lexical_redefinition: + /* error to redefine a var that inside a lexical scope */ + return js_parse_error(s, "invalid redefinition of lexical identifier"); + } + if (fd->is_global_var) { + JSGlobalVar *hf; + hf = find_global_var(fd, name); + if (hf && hf->is_lexical && hf->scope_level == fd->scope_level && + fd->eval_type == JS_EVAL_TYPE_MODULE) { + goto invalid_lexical_redefinition; + } + hf = add_global_var(s->ctx, fd, name); + if (!hf) + return -1; + idx = GLOBAL_VAR_OFFSET; + } else { + /* if the variable already exists, don't add it again */ + idx = find_var(ctx, fd, name); + if (idx >= 0) + break; + idx = add_var(ctx, fd, name); + if (idx >= 0) { + if (name == JS_ATOM_arguments && fd->has_arguments_binding) + fd->arguments_var_idx = idx; + fd->vars[idx].scope_next = fd->scope_level; + } + } + break; + default: + abort(); + } + return idx; +} + +/* add a private field variable in the current scope */ +static int add_private_class_field(JSParseState *s, JSFunctionDef *fd, + JSAtom name, JSVarKindEnum var_kind, BOOL is_static) +{ + JSContext *ctx = s->ctx; + JSVarDef *vd; + int idx; + + idx = add_scope_var(ctx, fd, name, var_kind); + if (idx < 0) + return idx; + vd = &fd->vars[idx]; + vd->is_lexical = 1; + vd->is_const = 1; + vd->is_static_private = is_static; + return idx; +} + +static __exception int js_parse_expr(JSParseState *s); +static __exception int js_parse_function_decl(JSParseState *s, + JSParseFunctionEnum func_type, + JSFunctionKindEnum func_kind, + JSAtom func_name, const uint8_t *ptr, + int start_line, int start_col); +static JSFunctionDef *js_parse_function_class_fields_init(JSParseState *s); +static __exception int js_parse_function_decl2(JSParseState *s, + JSParseFunctionEnum func_type, + JSFunctionKindEnum func_kind, + JSAtom func_name, + const uint8_t *ptr, + int function_line_num, + int function_col_num, + JSParseExportEnum export_flag, + JSFunctionDef **pfd); +static __exception int js_parse_assign_expr2(JSParseState *s, int parse_flags); +static __exception int js_parse_assign_expr(JSParseState *s); +static __exception int js_parse_unary(JSParseState *s, int parse_flags); +static void push_break_entry(JSFunctionDef *fd, BlockEnv *be, + JSAtom label_name, + int label_break, int label_cont, + int drop_count); +static void pop_break_entry(JSFunctionDef *fd); +static JSExportEntry *add_export_entry(JSParseState *s, JSModuleDef *m, + JSAtom local_name, JSAtom export_name, + JSExportTypeEnum export_type); + +/* Note: all the fields are already sealed except length */ +static int seal_template_obj(JSContext *ctx, JSValue obj) +{ + JSObject *p; + JSShapeProperty *prs; + + p = JS_VALUE_GET_OBJ(obj); + prs = find_own_property1(p, JS_ATOM_length); + if (prs) { + if (js_update_property_flags(ctx, p, &prs, + prs->flags & ~(JS_PROP_CONFIGURABLE | JS_PROP_WRITABLE))) + return -1; + } + p->extensible = FALSE; + return 0; +} + +static __exception int js_parse_template(JSParseState *s, int call, int *argc) +{ + JSContext *ctx = s->ctx; + JSValue raw_array, template_object; + JSToken cooked; + int depth, ret; + + raw_array = JS_UNDEFINED; /* avoid warning */ + template_object = JS_UNDEFINED; /* avoid warning */ + if (call) { + /* Create a template object: an array of cooked strings */ + /* Create an array of raw strings and store it to the raw property */ + template_object = JS_NewArray(ctx); + if (JS_IsException(template_object)) + return -1; + // pool_idx = s->cur_func->cpool_count; + ret = emit_push_const(s, template_object, 0); + JS_FreeValue(ctx, template_object); + if (ret) + return -1; + raw_array = JS_NewArray(ctx); + if (JS_IsException(raw_array)) + return -1; + if (JS_DefinePropertyValue(ctx, template_object, JS_ATOM_raw, + raw_array, JS_PROP_THROW) < 0) { + return -1; + } + } + + depth = 0; + while (s->token.val == TOK_TEMPLATE) { + const uint8_t *p = s->token.ptr + 1; + cooked = s->token; + if (call) { + if (JS_DefinePropertyValueUint32(ctx, raw_array, depth, + js_dup(s->token.u.str.str), + JS_PROP_ENUMERABLE | JS_PROP_THROW) < 0) { + return -1; + } + /* re-parse the string with escape sequences but do not throw a + syntax error if it contains invalid sequences + */ + if (js_parse_string(s, '`', FALSE, p, &cooked, &p)) { + cooked.u.str.str = JS_UNDEFINED; + } + if (JS_DefinePropertyValueUint32(ctx, template_object, depth, + cooked.u.str.str, + JS_PROP_ENUMERABLE | JS_PROP_THROW) < 0) { + return -1; + } + } else { + JSString *str; + /* re-parse the string with escape sequences and throw a + syntax error if it contains invalid sequences + */ + JS_FreeValue(ctx, s->token.u.str.str); + s->token.u.str.str = JS_UNDEFINED; + if (js_parse_string(s, '`', TRUE, p, &cooked, &p)) + return -1; + str = JS_VALUE_GET_STRING(cooked.u.str.str); + if (str->len != 0 || depth == 0) { + ret = emit_push_const(s, cooked.u.str.str, 1); + JS_FreeValue(s->ctx, cooked.u.str.str); + if (ret) + return -1; + if (depth == 0) { + if (s->token.u.str.sep == '`') + goto done1; + emit_op(s, OP_get_field2); + emit_atom(s, JS_ATOM_concat); + emit_ic(s, JS_ATOM_concat); + } + depth++; + } else { + JS_FreeValue(s->ctx, cooked.u.str.str); + } + } + if (s->token.u.str.sep == '`') + goto done; + if (next_token(s)) + return -1; + if (js_parse_expr(s)) + return -1; + depth++; + if (s->token.val != '}') { + return js_parse_error(s, "expected '}' after template expression"); + } + /* XXX: should convert to string at this stage? */ + free_token(s, &s->token); + /* Resume TOK_TEMPLATE parsing (s->token.line_num and + * s->token.ptr are OK) */ + s->got_lf = FALSE; + s->last_line_num = s->token.line_num; + s->last_col_num = s->token.col_num; + if (js_parse_template_part(s, s->buf_ptr)) + return -1; + } + return js_parse_expect(s, TOK_TEMPLATE); + + done: + if (call) { + /* Seal the objects */ + seal_template_obj(ctx, raw_array); + seal_template_obj(ctx, template_object); + *argc = depth + 1; + } else { + emit_op(s, OP_call_method); + emit_u16(s, depth - 1); + } + done1: + return next_token(s); +} + + +#define PROP_TYPE_IDENT 0 +#define PROP_TYPE_VAR 1 +#define PROP_TYPE_GET 2 +#define PROP_TYPE_SET 3 +#define PROP_TYPE_STAR 4 +#define PROP_TYPE_ASYNC 5 +#define PROP_TYPE_ASYNC_STAR 6 + +#define PROP_TYPE_PRIVATE (1 << 4) + +static BOOL token_is_ident(int tok) +{ + /* Accept keywords and reserved words as property names */ + return (tok == TOK_IDENT || + (tok >= TOK_FIRST_KEYWORD && + tok <= TOK_LAST_KEYWORD)); +} + +/* if the property is an expression, name = JS_ATOM_NULL */ +static int __exception js_parse_property_name(JSParseState *s, + JSAtom *pname, + BOOL allow_method, BOOL allow_var, + BOOL allow_private) +{ + int is_private = 0; + BOOL is_non_reserved_ident; + JSAtom name; + int prop_type; + + prop_type = PROP_TYPE_IDENT; + if (allow_method) { + if (token_is_pseudo_keyword(s, JS_ATOM_get) + || token_is_pseudo_keyword(s, JS_ATOM_set)) { + /* get x(), set x() */ + name = JS_DupAtom(s->ctx, s->token.u.ident.atom); + if (next_token(s)) + goto fail1; + if (s->token.val == ':' || s->token.val == ',' || + s->token.val == '}' || s->token.val == '(' || + s->token.val == '=' || s->token.val == ';') { + is_non_reserved_ident = TRUE; + goto ident_found; + } + prop_type = PROP_TYPE_GET + (name == JS_ATOM_set); + JS_FreeAtom(s->ctx, name); + } else if (s->token.val == '*') { + if (next_token(s)) + goto fail; + prop_type = PROP_TYPE_STAR; + } else if (token_is_pseudo_keyword(s, JS_ATOM_async) && + peek_token(s, TRUE) != '\n') { + name = JS_DupAtom(s->ctx, s->token.u.ident.atom); + if (next_token(s)) + goto fail1; + if (s->token.val == ':' || s->token.val == ',' || + s->token.val == '}' || s->token.val == '(' || + s->token.val == '=' || s->token.val == ';') { + is_non_reserved_ident = TRUE; + goto ident_found; + } + JS_FreeAtom(s->ctx, name); + if (s->token.val == '*') { + if (next_token(s)) + goto fail; + prop_type = PROP_TYPE_ASYNC_STAR; + } else { + prop_type = PROP_TYPE_ASYNC; + } + } + } + + if (token_is_ident(s->token.val)) { + /* variable can only be a non-reserved identifier */ + is_non_reserved_ident = + (s->token.val == TOK_IDENT && !s->token.u.ident.is_reserved); + /* keywords and reserved words have a valid atom */ + name = JS_DupAtom(s->ctx, s->token.u.ident.atom); + if (next_token(s)) + goto fail1; + ident_found: + if (is_non_reserved_ident && + prop_type == PROP_TYPE_IDENT && allow_var) { + if (!(s->token.val == ':' || + (s->token.val == '(' && allow_method))) { + prop_type = PROP_TYPE_VAR; + } + } + } else if (s->token.val == TOK_STRING) { + name = JS_ValueToAtom(s->ctx, s->token.u.str.str); + if (name == JS_ATOM_NULL) + goto fail; + if (next_token(s)) + goto fail1; + } else if (s->token.val == TOK_NUMBER) { + JSValue val; + val = s->token.u.num.val; + name = JS_ValueToAtom(s->ctx, val); + if (name == JS_ATOM_NULL) + goto fail; + if (next_token(s)) + goto fail1; + } else if (s->token.val == '[') { + if (next_token(s)) + goto fail; + if (js_parse_expr(s)) + goto fail; + if (js_parse_expect(s, ']')) + goto fail; + name = JS_ATOM_NULL; + } else if (s->token.val == TOK_PRIVATE_NAME && allow_private) { + name = JS_DupAtom(s->ctx, s->token.u.ident.atom); + if (next_token(s)) + goto fail1; + is_private = PROP_TYPE_PRIVATE; + } else { + goto invalid_prop; + } + if (prop_type != PROP_TYPE_IDENT && prop_type != PROP_TYPE_VAR && + s->token.val != '(') { + JS_FreeAtom(s->ctx, name); + invalid_prop: + js_parse_error(s, "invalid property name"); + goto fail; + } + *pname = name; + return prop_type | is_private; + fail1: + JS_FreeAtom(s->ctx, name); + fail: + *pname = JS_ATOM_NULL; + return -1; +} + +typedef struct JSParsePos { + int last_line_num; + int last_col_num; + int line_num; + int col_num; + BOOL got_lf; + const uint8_t *ptr; + const uint8_t *eol; + const uint8_t *mark; +} JSParsePos; + +static int js_parse_get_pos(JSParseState *s, JSParsePos *sp) +{ + sp->last_line_num = s->last_line_num; + sp->last_col_num = s->last_col_num; + sp->line_num = s->token.line_num; + sp->col_num = s->token.col_num; + sp->ptr = s->token.ptr; + sp->eol = s->eol; + sp->mark = s->mark; + sp->got_lf = s->got_lf; + return 0; +} + +static __exception int js_parse_seek_token(JSParseState *s, const JSParsePos *sp) +{ + s->token.line_num = sp->last_line_num; + s->token.col_num = sp->last_col_num; + s->line_num = sp->line_num; + s->col_num = sp->col_num; + s->buf_ptr = sp->ptr; + s->eol = sp->eol; + s->mark = sp->mark; + s->got_lf = sp->got_lf; + return next_token(s); +} + +/* return TRUE if a regexp literal is allowed after this token */ +static BOOL is_regexp_allowed(int tok) +{ + switch (tok) { + case TOK_NUMBER: + case TOK_STRING: + case TOK_REGEXP: + case TOK_DEC: + case TOK_INC: + case TOK_NULL: + case TOK_FALSE: + case TOK_TRUE: + case TOK_THIS: + case ')': + case ']': + case '}': /* XXX: regexp may occur after */ + case TOK_IDENT: + return FALSE; + default: + return TRUE; + } +} + +#define SKIP_HAS_SEMI (1 << 0) +#define SKIP_HAS_ELLIPSIS (1 << 1) +#define SKIP_HAS_ASSIGNMENT (1 << 2) + +/* XXX: improve speed with early bailout */ +/* XXX: no longer works if regexps are present. Could use previous + regexp parsing heuristics to handle most cases */ +static int js_parse_skip_parens_token(JSParseState *s, int *pbits, BOOL no_line_terminator) +{ + char state[256]; + size_t level = 0; + JSParsePos pos; + int last_tok, tok = TOK_EOF; + int c, tok_len, bits = 0; + + /* protect from underflow */ + state[level++] = 0; + + js_parse_get_pos(s, &pos); + last_tok = 0; + for (;;) { + switch(s->token.val) { + case '(': + case '[': + case '{': + if (level >= sizeof(state)) + goto done; + state[level++] = s->token.val; + break; + case ')': + if (state[--level] != '(') + goto done; + break; + case ']': + if (state[--level] != '[') + goto done; + break; + case '}': + c = state[--level]; + if (c == '`') { + /* continue the parsing of the template */ + free_token(s, &s->token); + /* Resume TOK_TEMPLATE parsing (s->token.line_num and + * s->token.ptr are OK) */ + s->got_lf = FALSE; + s->last_line_num = s->token.line_num; + s->last_col_num = s->token.col_num; + if (js_parse_template_part(s, s->buf_ptr)) + goto done; + goto handle_template; + } else if (c != '{') { + goto done; + } + break; + case TOK_TEMPLATE: + handle_template: + if (s->token.u.str.sep != '`') { + /* '${' inside the template : closing '}' and continue + parsing the template */ + if (level >= sizeof(state)) + goto done; + state[level++] = '`'; + } + break; + case TOK_EOF: + goto done; + case ';': + if (level == 2) { + bits |= SKIP_HAS_SEMI; + } + break; + case TOK_ELLIPSIS: + if (level == 2) { + bits |= SKIP_HAS_ELLIPSIS; + } + break; + case '=': + bits |= SKIP_HAS_ASSIGNMENT; + break; + + case TOK_DIV_ASSIGN: + tok_len = 2; + goto parse_regexp; + case '/': + tok_len = 1; + parse_regexp: + if (is_regexp_allowed(last_tok)) { + s->buf_ptr -= tok_len; + if (js_parse_regexp(s)) { + /* XXX: should clear the exception */ + goto done; + } + } + break; + } + /* last_tok is only used to recognize regexps */ + if (s->token.val == TOK_IDENT && + (token_is_pseudo_keyword(s, JS_ATOM_of) || + token_is_pseudo_keyword(s, JS_ATOM_yield))) { + last_tok = TOK_OF; + } else { + last_tok = s->token.val; + } + if (next_token(s)) { + /* XXX: should clear the exception generated by next_token() */ + break; + } + if (level <= 1) { + tok = s->token.val; + if (token_is_pseudo_keyword(s, JS_ATOM_of)) + tok = TOK_OF; + if (no_line_terminator && s->last_line_num != s->token.line_num) + tok = '\n'; + break; + } + } + done: + if (pbits) { + *pbits = bits; + } + if (js_parse_seek_token(s, &pos)) + return -1; + return tok; +} + +static void set_object_name(JSParseState *s, JSAtom name) +{ + JSFunctionDef *fd = s->cur_func; + int opcode; + + opcode = get_prev_opcode(fd); + if (opcode == OP_set_name) { + /* XXX: should free atom after OP_set_name? */ + fd->byte_code.size = fd->last_opcode_pos; + fd->last_opcode_pos = -1; + emit_op(s, OP_set_name); + emit_atom(s, name); + } else if (opcode == OP_set_class_name) { + int define_class_pos; + JSAtom atom; + define_class_pos = fd->last_opcode_pos + 1 - + get_u32(fd->byte_code.buf + fd->last_opcode_pos + 1); + assert(fd->byte_code.buf[define_class_pos] == OP_define_class); + /* for consistency we free the previous atom which is + JS_ATOM_empty_string */ + atom = get_u32(fd->byte_code.buf + define_class_pos + 1); + JS_FreeAtom(s->ctx, atom); + put_u32(fd->byte_code.buf + define_class_pos + 1, + JS_DupAtom(s->ctx, name)); + fd->last_opcode_pos = -1; + } +} + +static void set_object_name_computed(JSParseState *s) +{ + JSFunctionDef *fd = s->cur_func; + int opcode; + + opcode = get_prev_opcode(fd); + if (opcode == OP_set_name) { + /* XXX: should free atom after OP_set_name? */ + fd->byte_code.size = fd->last_opcode_pos; + fd->last_opcode_pos = -1; + emit_op(s, OP_set_name_computed); + } else if (opcode == OP_set_class_name) { + int define_class_pos; + define_class_pos = fd->last_opcode_pos + 1 - + get_u32(fd->byte_code.buf + fd->last_opcode_pos + 1); + assert(fd->byte_code.buf[define_class_pos] == OP_define_class); + fd->byte_code.buf[define_class_pos] = OP_define_class_computed; + fd->last_opcode_pos = -1; + } +} + +static __exception int js_parse_object_literal(JSParseState *s) +{ + JSAtom name = JS_ATOM_NULL; + const uint8_t *start_ptr; + int start_line, start_col, prop_type; + BOOL has_proto; + + if (next_token(s)) + goto fail; + /* XXX: add an initial length that will be patched back */ + emit_op(s, OP_object); + has_proto = FALSE; + while (s->token.val != '}') { + /* specific case for getter/setter */ + start_ptr = s->token.ptr; + start_line = s->token.line_num; + start_col = s->token.col_num; + + if (s->token.val == TOK_ELLIPSIS) { + if (next_token(s)) + return -1; + if (js_parse_assign_expr(s)) + return -1; + emit_op(s, OP_null); /* dummy excludeList */ + emit_op(s, OP_copy_data_properties); + emit_u8(s, 2 | (1 << 2) | (0 << 5)); + emit_op(s, OP_drop); /* pop excludeList */ + emit_op(s, OP_drop); /* pop src object */ + goto next; + } + + prop_type = js_parse_property_name(s, &name, TRUE, TRUE, FALSE); + if (prop_type < 0) + goto fail; + + if (prop_type == PROP_TYPE_VAR) { + /* shortcut for x: x */ + emit_op(s, OP_scope_get_var); + emit_atom(s, name); + emit_u16(s, s->cur_func->scope_level); + emit_op(s, OP_define_field); + emit_atom(s, name); + } else if (s->token.val == '(') { + BOOL is_getset = (prop_type == PROP_TYPE_GET || + prop_type == PROP_TYPE_SET); + JSParseFunctionEnum func_type; + JSFunctionKindEnum func_kind; + int op_flags; + + func_kind = JS_FUNC_NORMAL; + if (is_getset) { + func_type = JS_PARSE_FUNC_GETTER + prop_type - PROP_TYPE_GET; + } else { + func_type = JS_PARSE_FUNC_METHOD; + if (prop_type == PROP_TYPE_STAR) + func_kind = JS_FUNC_GENERATOR; + else if (prop_type == PROP_TYPE_ASYNC) + func_kind = JS_FUNC_ASYNC; + else if (prop_type == PROP_TYPE_ASYNC_STAR) + func_kind = JS_FUNC_ASYNC_GENERATOR; + } + if (js_parse_function_decl(s, func_type, func_kind, JS_ATOM_NULL, + start_ptr, start_line, start_col)) + goto fail; + if (name == JS_ATOM_NULL) { + emit_op(s, OP_define_method_computed); + } else { + emit_op(s, OP_define_method); + emit_atom(s, name); + } + if (is_getset) { + op_flags = OP_DEFINE_METHOD_GETTER + + prop_type - PROP_TYPE_GET; + } else { + op_flags = OP_DEFINE_METHOD_METHOD; + } + emit_u8(s, op_flags | OP_DEFINE_METHOD_ENUMERABLE); + } else { + if (js_parse_expect(s, ':')) + goto fail; + if (js_parse_assign_expr(s)) + goto fail; + if (name == JS_ATOM_NULL) { + set_object_name_computed(s); + emit_op(s, OP_define_array_el); + emit_op(s, OP_drop); + } else if (name == JS_ATOM___proto__) { + if (has_proto) { + js_parse_error(s, "duplicate __proto__ property name"); + goto fail; + } + emit_op(s, OP_set_proto); + has_proto = TRUE; + } else { + set_object_name(s, name); + emit_op(s, OP_define_field); + emit_atom(s, name); + } + } + JS_FreeAtom(s->ctx, name); + next: + name = JS_ATOM_NULL; + if (s->token.val != ',') + break; + if (next_token(s)) + goto fail; + } + if (js_parse_expect(s, '}')) + goto fail; + return 0; + fail: + JS_FreeAtom(s->ctx, name); + return -1; +} + +/* allow the 'in' binary operator */ +#define PF_IN_ACCEPTED (1 << 0) +/* allow function calls parsing in js_parse_postfix_expr() */ +#define PF_POSTFIX_CALL (1 << 1) +/* allow the exponentiation operator in js_parse_unary() */ +#define PF_POW_ALLOWED (1 << 2) +/* forbid the exponentiation operator in js_parse_unary() */ +#define PF_POW_FORBIDDEN (1 << 3) + +static __exception int js_parse_postfix_expr(JSParseState *s, int parse_flags); + +static __exception int js_parse_left_hand_side_expr(JSParseState *s) +{ + return js_parse_postfix_expr(s, PF_POSTFIX_CALL); +} + +/* find field in the current scope */ +static int find_private_class_field(JSContext *ctx, JSFunctionDef *fd, + JSAtom name, int scope_level) +{ + int idx; + idx = fd->scopes[scope_level].first; + while (idx != -1) { + if (fd->vars[idx].scope_level != scope_level) + break; + if (fd->vars[idx].var_name == name) + return idx; + idx = fd->vars[idx].scope_next; + } + return -1; +} + +/* initialize the class fields, called by the constructor. Note: + super() can be called in an arrow function, so <this> and + <class_fields_init> can be variable references */ +static void emit_class_field_init(JSParseState *s) +{ + int label_next; + + emit_op(s, OP_scope_get_var); + emit_atom(s, JS_ATOM_class_fields_init); + emit_u16(s, s->cur_func->scope_level); + + /* no need to call the class field initializer if not defined */ + emit_op(s, OP_dup); + label_next = emit_goto(s, OP_if_false, -1); + + emit_op(s, OP_scope_get_var); + emit_atom(s, JS_ATOM_this); + emit_u16(s, 0); + + emit_op(s, OP_swap); + + emit_op(s, OP_call_method); + emit_u16(s, 0); + + emit_label(s, label_next); + emit_op(s, OP_drop); +} + +/* build a private setter function name from the private getter name */ +static JSAtom get_private_setter_name(JSContext *ctx, JSAtom name) +{ + return js_atom_concat_str(ctx, name, "<set>"); +} + +typedef struct { + JSFunctionDef *fields_init_fd; + int computed_fields_count; + BOOL need_brand; + int brand_push_pos; + BOOL is_static; +} ClassFieldsDef; + +static __exception int emit_class_init_start(JSParseState *s, + ClassFieldsDef *cf) +{ + int label_add_brand; + + cf->fields_init_fd = js_parse_function_class_fields_init(s); + if (!cf->fields_init_fd) + return -1; + + s->cur_func = cf->fields_init_fd; + + if (!cf->is_static) { + /* add the brand to the newly created instance */ + /* XXX: would be better to add the code only if needed, maybe in a + later pass */ + emit_op(s, OP_push_false); /* will be patched later */ + cf->brand_push_pos = cf->fields_init_fd->last_opcode_pos; + label_add_brand = emit_goto(s, OP_if_false, -1); + + emit_op(s, OP_scope_get_var); + emit_atom(s, JS_ATOM_this); + emit_u16(s, 0); + + emit_op(s, OP_scope_get_var); + emit_atom(s, JS_ATOM_home_object); + emit_u16(s, 0); + + emit_op(s, OP_add_brand); + + emit_label(s, label_add_brand); + } + s->cur_func = s->cur_func->parent; + return 0; +} + +static void emit_class_init_end(JSParseState *s, ClassFieldsDef *cf) +{ + int cpool_idx; + + s->cur_func = cf->fields_init_fd; + emit_op(s, OP_return_undef); + s->cur_func = s->cur_func->parent; + + cpool_idx = cpool_add(s, JS_NULL); + cf->fields_init_fd->parent_cpool_idx = cpool_idx; + emit_op(s, OP_fclosure); + emit_u32(s, cpool_idx); + emit_op(s, OP_set_home_object); +} + +static void emit_return(JSParseState *s, BOOL hasval); + +static JSFunctionDef *js_new_function_def(JSContext *ctx, + JSFunctionDef *parent, + BOOL is_eval, + BOOL is_func_expr, + const char *filename, + int line_num, + int col_num); + +static __exception int js_parse_class_default_ctor(JSParseState *s, + BOOL has_super, + JSFunctionDef **pfd) +{ + JSParseFunctionEnum func_type; + JSFunctionDef *fd = s->cur_func; + + fd = js_new_function_def(s->ctx, fd, FALSE, FALSE, s->filename, + s->token.line_num, s->token.col_num); + if (!fd) + return -1; + + s->cur_func = fd; + fd->has_home_object = TRUE; + fd->super_allowed = TRUE; + fd->has_prototype = FALSE; + fd->has_this_binding = TRUE; + fd->new_target_allowed = TRUE; + + /* error if not invoked as a constructor */ + emit_op(s, OP_check_ctor); + + push_scope(s); /* enter body scope */ + fd->body_scope = fd->scope_level; + if (has_super) { + fd->is_derived_class_constructor = TRUE; + fd->super_call_allowed = TRUE; + fd->arguments_allowed = TRUE; + fd->has_arguments_binding = TRUE; + + func_type = JS_PARSE_FUNC_DERIVED_CLASS_CONSTRUCTOR; + /* super */ + emit_op(s, OP_scope_get_var); + emit_atom(s, JS_ATOM_this_active_func); + emit_u16(s, 0); + + emit_op(s, OP_get_super); + + emit_op(s, OP_scope_get_var); + emit_atom(s, JS_ATOM_new_target); + emit_u16(s, 0); + + emit_op(s, OP_array_from); + emit_u16(s, 0); + emit_op(s, OP_push_i32); + emit_u32(s, 0); + + /* arguments */ + emit_op(s, OP_scope_get_var); + emit_atom(s, JS_ATOM_arguments); + emit_u16(s, 0); + + emit_op(s, OP_append); + /* drop the index */ + emit_op(s, OP_drop); + + emit_op(s, OP_apply); + emit_u16(s, 1); + /* set the 'this' value */ + emit_op(s, OP_dup); + emit_op(s, OP_scope_put_var_init); + emit_atom(s, JS_ATOM_this); + emit_u16(s, 0); + emit_class_field_init(s); + } else { + func_type = JS_PARSE_FUNC_CLASS_CONSTRUCTOR; + emit_class_field_init(s); + } + + fd->func_kind = JS_FUNC_NORMAL; + fd->func_type = func_type; + emit_return(s, FALSE); + + s->cur_func = fd->parent; + if (pfd) + *pfd = fd; + + int idx; + /* the real object will be set at the end of the compilation */ + idx = cpool_add(s, JS_NULL); + fd->parent_cpool_idx = idx; + + return 0; +} + + +static __exception int js_parse_class(JSParseState *s, BOOL is_class_expr, + JSParseExportEnum export_flag) +{ + JSContext *ctx = s->ctx; + JSFunctionDef *fd = s->cur_func; + JSAtom name = JS_ATOM_NULL, class_name = JS_ATOM_NULL, class_name1; + JSAtom class_var_name = JS_ATOM_NULL; + JSFunctionDef *method_fd, *ctor_fd; + int class_name_var_idx, prop_type, ctor_cpool_offset; + int class_flags = 0, i, define_class_offset; + BOOL is_static, is_private, is_strict_mode; + const uint8_t *class_start_ptr = s->token.ptr; + const uint8_t *start_ptr; + ClassFieldsDef class_fields[2]; + + /* classes are parsed and executed in strict mode */ + is_strict_mode = fd->is_strict_mode; + fd->is_strict_mode = TRUE; + if (next_token(s)) + goto fail; + if (s->token.val == TOK_IDENT) { + if (s->token.u.ident.is_reserved) { + js_parse_error_reserved_identifier(s); + goto fail; + } + class_name = JS_DupAtom(ctx, s->token.u.ident.atom); + if (next_token(s)) + goto fail; + } else if (!is_class_expr && export_flag != JS_PARSE_EXPORT_DEFAULT) { + js_parse_error(s, "class statement requires a name"); + goto fail; + } + if (!is_class_expr) { + if (class_name == JS_ATOM_NULL) + class_var_name = JS_ATOM__default_; /* export default */ + else + class_var_name = class_name; + class_var_name = JS_DupAtom(ctx, class_var_name); + } + + push_scope(s); + + if (s->token.val == TOK_EXTENDS) { + class_flags = JS_DEFINE_CLASS_HAS_HERITAGE; + if (next_token(s)) + goto fail; + if (js_parse_left_hand_side_expr(s)) + goto fail; + } else { + emit_op(s, OP_undefined); + } + + /* add a 'const' definition for the class name */ + if (class_name != JS_ATOM_NULL) { + class_name_var_idx = define_var(s, fd, class_name, JS_VAR_DEF_CONST); + if (class_name_var_idx < 0) + goto fail; + } + + if (js_parse_expect(s, '{')) + goto fail; + + /* this scope contains the private fields */ + push_scope(s); + + emit_op(s, OP_push_const); + ctor_cpool_offset = fd->byte_code.size; + emit_u32(s, 0); /* will be patched at the end of the class parsing */ + + if (class_name == JS_ATOM_NULL) { + if (class_var_name != JS_ATOM_NULL) + class_name1 = JS_ATOM_default; + else + class_name1 = JS_ATOM_empty_string; + } else { + class_name1 = class_name; + } + + emit_op(s, OP_define_class); + emit_atom(s, class_name1); + emit_u8(s, class_flags); + define_class_offset = fd->last_opcode_pos; + + for(i = 0; i < 2; i++) { + ClassFieldsDef *cf = &class_fields[i]; + cf->fields_init_fd = NULL; + cf->computed_fields_count = 0; + cf->need_brand = FALSE; + cf->is_static = i; + } + + ctor_fd = NULL; + while (s->token.val != '}') { + if (s->token.val == ';') { + if (next_token(s)) + goto fail; + continue; + } + is_static = FALSE; + if (s->token.val == TOK_STATIC) { + int next = peek_token(s, TRUE); + if (!(next == ';' || next == '}' || next == '(' || next == '=')) + is_static = TRUE; + } + prop_type = -1; + if (is_static) { + if (next_token(s)) + goto fail; + if (s->token.val == '{') { + ClassFieldsDef *cf = &class_fields[is_static]; + if (!cf->fields_init_fd) + if (emit_class_init_start(s, cf)) + goto fail; + s->cur_func = cf->fields_init_fd; + // stack is now: <empty> + JSFunctionDef *init; + if (js_parse_function_decl2(s, JS_PARSE_FUNC_CLASS_STATIC_INIT, + JS_FUNC_NORMAL, JS_ATOM_NULL, + s->token.ptr, + s->token.line_num, + s->token.col_num, + JS_PARSE_EXPORT_NONE, &init) < 0) { + goto fail; + } + // stack is now: fclosure + push_scope(s); + emit_op(s, OP_scope_get_var); + emit_atom(s, JS_ATOM_this); + emit_u16(s, 0); + // stack is now: fclosure this + if (class_name != JS_ATOM_NULL) { + // TODO(bnoordhuis) pass as argument to init method? + emit_op(s, OP_dup); + emit_op(s, OP_scope_put_var_init); + emit_atom(s, class_name); + emit_u16(s, s->cur_func->scope_level); + } + emit_op(s, OP_swap); + // stack is now: this fclosure + emit_op(s, OP_call_method); + emit_u16(s, 0); + // stack is now: returnvalue + emit_op(s, OP_drop); + // stack is now: <empty> + pop_scope(s); + s->cur_func = s->cur_func->parent; + continue; + } + /* allow "static" field name */ + if (s->token.val == ';' || s->token.val == '=') { + is_static = FALSE; + name = JS_DupAtom(ctx, JS_ATOM_static); + prop_type = PROP_TYPE_IDENT; + } + } + if (is_static) + emit_op(s, OP_swap); + start_ptr = s->token.ptr; + if (prop_type < 0) { + prop_type = js_parse_property_name(s, &name, TRUE, FALSE, TRUE); + if (prop_type < 0) + goto fail; + } + is_private = prop_type & PROP_TYPE_PRIVATE; + prop_type &= ~PROP_TYPE_PRIVATE; + + if ((name == JS_ATOM_constructor && !is_static && + prop_type != PROP_TYPE_IDENT) || + (name == JS_ATOM_prototype && is_static) || + name == JS_ATOM_hash_constructor) { + js_parse_error(s, "invalid method name"); + goto fail; + } + if (prop_type == PROP_TYPE_GET || prop_type == PROP_TYPE_SET) { + BOOL is_set = prop_type - PROP_TYPE_GET; + JSFunctionDef *method_fd; + + if (is_private) { + int idx, var_kind, is_static1; + idx = find_private_class_field(ctx, fd, name, fd->scope_level); + if (idx >= 0) { + var_kind = fd->vars[idx].var_kind; + is_static1 = fd->vars[idx].is_static_private; + if (var_kind == JS_VAR_PRIVATE_FIELD || + var_kind == JS_VAR_PRIVATE_METHOD || + var_kind == JS_VAR_PRIVATE_GETTER_SETTER || + var_kind == (JS_VAR_PRIVATE_GETTER + is_set) || + (var_kind == (JS_VAR_PRIVATE_GETTER + 1 - is_set) && + is_static != is_static1)) { + goto private_field_already_defined; + } + fd->vars[idx].var_kind = JS_VAR_PRIVATE_GETTER_SETTER; + } else { + if (add_private_class_field(s, fd, name, + JS_VAR_PRIVATE_GETTER + is_set, is_static) < 0) + goto fail; + } + class_fields[is_static].need_brand = TRUE; + } + + if (js_parse_function_decl2(s, JS_PARSE_FUNC_GETTER + is_set, + JS_FUNC_NORMAL, JS_ATOM_NULL, + start_ptr, + s->token.line_num, + s->token.col_num, + JS_PARSE_EXPORT_NONE, &method_fd)) + goto fail; + if (is_private) { + method_fd->need_home_object = TRUE; /* needed for brand check */ + emit_op(s, OP_set_home_object); + /* XXX: missing function name */ + emit_op(s, OP_scope_put_var_init); + if (is_set) { + JSAtom setter_name; + int ret; + + setter_name = get_private_setter_name(ctx, name); + if (setter_name == JS_ATOM_NULL) + goto fail; + emit_atom(s, setter_name); + ret = add_private_class_field(s, fd, setter_name, + JS_VAR_PRIVATE_SETTER, is_static); + JS_FreeAtom(ctx, setter_name); + if (ret < 0) + goto fail; + } else { + emit_atom(s, name); + } + emit_u16(s, s->cur_func->scope_level); + } else { + if (name == JS_ATOM_NULL) { + emit_op(s, OP_define_method_computed); + } else { + emit_op(s, OP_define_method); + emit_atom(s, name); + } + emit_u8(s, OP_DEFINE_METHOD_GETTER + is_set); + } + } else if (prop_type == PROP_TYPE_IDENT && s->token.val != '(') { + ClassFieldsDef *cf = &class_fields[is_static]; + JSAtom field_var_name = JS_ATOM_NULL; + + /* class field */ + + /* XXX: spec: not consistent with method name checks */ + if (name == JS_ATOM_constructor || name == JS_ATOM_prototype) { + js_parse_error(s, "invalid field name"); + goto fail; + } + + if (is_private) { + if (find_private_class_field(ctx, fd, name, + fd->scope_level) >= 0) { + goto private_field_already_defined; + } + if (add_private_class_field(s, fd, name, + JS_VAR_PRIVATE_FIELD, is_static) < 0) + goto fail; + emit_op(s, OP_private_symbol); + emit_atom(s, name); + emit_op(s, OP_scope_put_var_init); + emit_atom(s, name); + emit_u16(s, s->cur_func->scope_level); + } + + if (!cf->fields_init_fd) { + if (emit_class_init_start(s, cf)) + goto fail; + } + if (name == JS_ATOM_NULL ) { + /* save the computed field name into a variable */ + field_var_name = js_atom_concat_num(ctx, JS_ATOM_computed_field + is_static, cf->computed_fields_count); + if (field_var_name == JS_ATOM_NULL) + goto fail; + if (define_var(s, fd, field_var_name, JS_VAR_DEF_CONST) < 0) { + JS_FreeAtom(ctx, field_var_name); + goto fail; + } + emit_op(s, OP_to_propkey); + emit_op(s, OP_scope_put_var_init); + emit_atom(s, field_var_name); + emit_u16(s, s->cur_func->scope_level); + } + s->cur_func = cf->fields_init_fd; + emit_op(s, OP_scope_get_var); + emit_atom(s, JS_ATOM_this); + emit_u16(s, 0); + + // expose class name to static initializers + if (is_static && class_name != JS_ATOM_NULL) { + emit_op(s, OP_dup); + emit_op(s, OP_scope_put_var_init); + emit_atom(s, class_name); + emit_u16(s, s->cur_func->scope_level); + } + + if (name == JS_ATOM_NULL) { + emit_op(s, OP_scope_get_var); + emit_atom(s, field_var_name); + emit_u16(s, s->cur_func->scope_level); + cf->computed_fields_count++; + JS_FreeAtom(ctx, field_var_name); + } else if (is_private) { + emit_op(s, OP_scope_get_var); + emit_atom(s, name); + emit_u16(s, s->cur_func->scope_level); + } + + if (s->token.val == '=') { + if (next_token(s)) + goto fail; + if (js_parse_assign_expr(s)) + goto fail; + } else { + emit_op(s, OP_undefined); + } + if (is_private) { + set_object_name_computed(s); + emit_op(s, OP_define_private_field); + } else if (name == JS_ATOM_NULL) { + set_object_name_computed(s); + emit_op(s, OP_define_array_el); + emit_op(s, OP_drop); + } else { + set_object_name(s, name); + emit_op(s, OP_define_field); + emit_atom(s, name); + } + s->cur_func = s->cur_func->parent; + if (js_parse_expect_semi(s)) + goto fail; + } else { + JSParseFunctionEnum func_type; + JSFunctionKindEnum func_kind; + + func_type = JS_PARSE_FUNC_METHOD; + func_kind = JS_FUNC_NORMAL; + if (prop_type == PROP_TYPE_STAR) { + func_kind = JS_FUNC_GENERATOR; + } else if (prop_type == PROP_TYPE_ASYNC) { + func_kind = JS_FUNC_ASYNC; + } else if (prop_type == PROP_TYPE_ASYNC_STAR) { + func_kind = JS_FUNC_ASYNC_GENERATOR; + } else if (name == JS_ATOM_constructor && !is_static) { + if (ctor_fd) { + js_parse_error(s, "property constructor appears more than once"); + goto fail; + } + if (class_flags & JS_DEFINE_CLASS_HAS_HERITAGE) + func_type = JS_PARSE_FUNC_DERIVED_CLASS_CONSTRUCTOR; + else + func_type = JS_PARSE_FUNC_CLASS_CONSTRUCTOR; + } + if (is_private) { + class_fields[is_static].need_brand = TRUE; + } + if (js_parse_function_decl2(s, func_type, func_kind, JS_ATOM_NULL, + start_ptr, + s->token.line_num, + s->token.col_num, + JS_PARSE_EXPORT_NONE, &method_fd)) + goto fail; + if (func_type == JS_PARSE_FUNC_DERIVED_CLASS_CONSTRUCTOR || + func_type == JS_PARSE_FUNC_CLASS_CONSTRUCTOR) { + ctor_fd = method_fd; + } else if (is_private) { + method_fd->need_home_object = TRUE; /* needed for brand check */ + if (find_private_class_field(ctx, fd, name, + fd->scope_level) >= 0) { + private_field_already_defined: + js_parse_error(s, "private class field is already defined"); + goto fail; + } + if (add_private_class_field(s, fd, name, + JS_VAR_PRIVATE_METHOD, is_static) < 0) + goto fail; + emit_op(s, OP_set_home_object); + emit_op(s, OP_set_name); + emit_atom(s, name); + emit_op(s, OP_scope_put_var_init); + emit_atom(s, name); + emit_u16(s, s->cur_func->scope_level); + } else { + if (name == JS_ATOM_NULL) { + emit_op(s, OP_define_method_computed); + } else { + emit_op(s, OP_define_method); + emit_atom(s, name); + } + emit_u8(s, OP_DEFINE_METHOD_METHOD); + } + } + if (is_static) + emit_op(s, OP_swap); + JS_FreeAtom(ctx, name); + name = JS_ATOM_NULL; + } + + if (s->token.val != '}') { + js_parse_error(s, "expecting '%c'", '}'); + goto fail; + } + + if (!ctor_fd) { + if (js_parse_class_default_ctor(s, class_flags & JS_DEFINE_CLASS_HAS_HERITAGE, &ctor_fd)) + goto fail; + } + /* patch the constant pool index for the constructor */ + put_u32(fd->byte_code.buf + ctor_cpool_offset, ctor_fd->parent_cpool_idx); + + /* store the class source code in the constructor. */ + js_free(ctx, ctor_fd->source); + ctor_fd->source_len = s->buf_ptr - class_start_ptr; + ctor_fd->source = js_strndup(ctx, (const char *)class_start_ptr, ctor_fd->source_len); + if (!ctor_fd->source) + goto fail; + + /* consume the '}' */ + if (next_token(s)) + goto fail; + + { + ClassFieldsDef *cf = &class_fields[0]; + int var_idx; + + if (cf->need_brand) { + /* add a private brand to the prototype */ + emit_op(s, OP_dup); + emit_op(s, OP_null); + emit_op(s, OP_swap); + emit_op(s, OP_add_brand); + + /* define the brand field in 'this' of the initializer */ + if (!cf->fields_init_fd) { + if (emit_class_init_start(s, cf)) + goto fail; + } + /* patch the start of the function to enable the + OP_add_brand_instance code */ + cf->fields_init_fd->byte_code.buf[cf->brand_push_pos] = OP_push_true; + } + + /* store the function to initialize the fields to that it can be + referenced by the constructor */ + var_idx = define_var(s, fd, JS_ATOM_class_fields_init, + JS_VAR_DEF_CONST); + if (var_idx < 0) + goto fail; + if (cf->fields_init_fd) { + emit_class_init_end(s, cf); + } else { + emit_op(s, OP_undefined); + } + emit_op(s, OP_scope_put_var_init); + emit_atom(s, JS_ATOM_class_fields_init); + emit_u16(s, s->cur_func->scope_level); + } + + /* drop the prototype */ + emit_op(s, OP_drop); + + if (class_fields[1].need_brand) { + /* add a private brand to the class */ + emit_op(s, OP_dup); + emit_op(s, OP_dup); + emit_op(s, OP_add_brand); + } + + /* initialize the static fields */ + if (class_fields[1].fields_init_fd != NULL) { + ClassFieldsDef *cf = &class_fields[1]; + emit_op(s, OP_dup); + emit_class_init_end(s, cf); + emit_op(s, OP_call_method); + emit_u16(s, 0); + emit_op(s, OP_drop); + } + + if (class_name != JS_ATOM_NULL) { + /* store the class name in the scoped class name variable (it + is independent from the class statement variable + definition) */ + emit_op(s, OP_dup); + emit_op(s, OP_scope_put_var_init); + emit_atom(s, class_name); + emit_u16(s, fd->scope_level); + } + pop_scope(s); + pop_scope(s); + + /* the class statements have a block level scope */ + if (class_var_name != JS_ATOM_NULL) { + if (define_var(s, fd, class_var_name, JS_VAR_DEF_LET) < 0) + goto fail; + emit_op(s, OP_scope_put_var_init); + emit_atom(s, class_var_name); + emit_u16(s, fd->scope_level); + } else { + if (class_name == JS_ATOM_NULL) { + /* cannot use OP_set_name because the name of the class + must be defined before the static initializers are + executed */ + emit_op(s, OP_set_class_name); + emit_u32(s, fd->last_opcode_pos + 1 - define_class_offset); + } + } + + if (export_flag != JS_PARSE_EXPORT_NONE) { + if (!add_export_entry(s, fd->module, + class_var_name, + export_flag == JS_PARSE_EXPORT_NAMED ? class_var_name : JS_ATOM_default, + JS_EXPORT_TYPE_LOCAL)) + goto fail; + } + + JS_FreeAtom(ctx, class_name); + JS_FreeAtom(ctx, class_var_name); + fd->is_strict_mode = is_strict_mode; + return 0; + fail: + JS_FreeAtom(ctx, name); + JS_FreeAtom(ctx, class_name); + JS_FreeAtom(ctx, class_var_name); + fd->is_strict_mode = is_strict_mode; + return -1; +} + +static __exception int js_parse_array_literal(JSParseState *s) +{ + uint32_t idx; + BOOL need_length; + + if (next_token(s)) + return -1; + /* small regular arrays are created on the stack */ + idx = 0; + while (s->token.val != ']' && idx < 32) { + if (s->token.val == ',' || s->token.val == TOK_ELLIPSIS) + break; + if (js_parse_assign_expr(s)) + return -1; + idx++; + /* accept trailing comma */ + if (s->token.val == ',') { + if (next_token(s)) + return -1; + } else + if (s->token.val != ']') + goto done; + } + emit_op(s, OP_array_from); + emit_u16(s, idx); + + /* larger arrays and holes are handled with explicit indices */ + need_length = FALSE; + while (s->token.val != ']' && idx < 0x7fffffff) { + if (s->token.val == TOK_ELLIPSIS) + break; + need_length = TRUE; + if (s->token.val != ',') { + if (js_parse_assign_expr(s)) + return -1; + emit_op(s, OP_define_field); + emit_u32(s, __JS_AtomFromUInt32(idx)); + need_length = FALSE; + } + idx++; + /* accept trailing comma */ + if (s->token.val == ',') { + if (next_token(s)) + return -1; + } + } + if (s->token.val == ']') { + if (need_length) { + /* Set the length: Cannot use OP_define_field because + length is not configurable */ + emit_op(s, OP_dup); + emit_op(s, OP_push_i32); + emit_u32(s, idx); + emit_op(s, OP_put_field); + emit_atom(s, JS_ATOM_length); + emit_ic(s, JS_ATOM_length); + } + goto done; + } + + /* huge arrays and spread elements require a dynamic index on the stack */ + emit_op(s, OP_push_i32); + emit_u32(s, idx); + + /* stack has array, index */ + while (s->token.val != ']') { + if (s->token.val == TOK_ELLIPSIS) { + if (next_token(s)) + return -1; + if (js_parse_assign_expr(s)) + return -1; + emit_op(s, OP_append); + } else { + need_length = TRUE; + if (s->token.val != ',') { + if (js_parse_assign_expr(s)) + return -1; + /* a idx val */ + emit_op(s, OP_define_array_el); + need_length = FALSE; + } + emit_op(s, OP_inc); + } + if (s->token.val != ',') + break; + if (next_token(s)) + return -1; + } + if (need_length) { + /* Set the length: cannot use OP_define_field because + length is not configurable */ + emit_op(s, OP_dup1); /* array length - array array length */ + emit_op(s, OP_put_field); + emit_atom(s, JS_ATOM_length); + emit_ic(s, JS_ATOM_length); + } else { + emit_op(s, OP_drop); /* array length - array */ + } +done: + return js_parse_expect(s, ']'); +} + +/* XXX: remove */ +static BOOL has_with_scope(JSFunctionDef *s, int scope_level) +{ + /* check if scope chain contains a with statement */ + while (s) { + int scope_idx = s->scopes[scope_level].first; + while (scope_idx >= 0) { + JSVarDef *vd = &s->vars[scope_idx]; + + if (vd->var_name == JS_ATOM__with_) + return TRUE; + scope_idx = vd->scope_next; + } + /* check parent scopes */ + scope_level = s->parent_scope_level; + s = s->parent; + } + return FALSE; +} + +static __exception int get_lvalue(JSParseState *s, int *popcode, int *pscope, + JSAtom *pname, int *plabel, int *pdepth, BOOL keep, + int tok) +{ + JSFunctionDef *fd; + int opcode, scope, label, depth; + JSAtom name; + + /* we check the last opcode to get the lvalue type */ + fd = s->cur_func; + scope = 0; + name = JS_ATOM_NULL; + label = -1; + depth = 0; + switch(opcode = get_prev_opcode(fd)) { + case OP_scope_get_var: + name = get_u32(fd->byte_code.buf + fd->last_opcode_pos + 1); + scope = get_u16(fd->byte_code.buf + fd->last_opcode_pos + 5); + if ((name == JS_ATOM_arguments || name == JS_ATOM_eval) && + fd->is_strict_mode) { + return js_parse_error(s, "invalid lvalue in strict mode"); + } + if (name == JS_ATOM_this || name == JS_ATOM_new_target) + goto invalid_lvalue; + depth = 2; /* will generate OP_get_ref_value */ + break; + case OP_get_field: + name = get_u32(fd->byte_code.buf + fd->last_opcode_pos + 1); + depth = 1; + break; + case OP_scope_get_private_field: + name = get_u32(fd->byte_code.buf + fd->last_opcode_pos + 1); + scope = get_u16(fd->byte_code.buf + fd->last_opcode_pos + 5); + depth = 1; + break; + case OP_get_array_el: + depth = 2; + break; + case OP_get_super_value: + depth = 3; + break; + default: + invalid_lvalue: + if (tok == TOK_FOR) { + return js_parse_error(s, "invalid for in/of left hand-side"); + } else if (tok == TOK_INC || tok == TOK_DEC) { + return js_parse_error(s, "invalid increment/decrement operand"); + } else if (tok == '[' || tok == '{') { + return js_parse_error(s, "invalid destructuring target"); + } else { + return js_parse_error(s, "invalid assignment left-hand side"); + } + } + /* remove the last opcode */ + fd->byte_code.size = fd->last_opcode_pos; + fd->last_opcode_pos = -1; + + if (keep) { + /* get the value but keep the object/fields on the stack */ + switch(opcode) { + case OP_scope_get_var: + label = new_label(s); + emit_op(s, OP_scope_make_ref); + emit_atom(s, name); + emit_u32(s, label); + emit_u16(s, scope); + update_label(fd, label, 1); + emit_op(s, OP_get_ref_value); + opcode = OP_get_ref_value; + break; + case OP_get_field: + emit_op(s, OP_get_field2); + emit_atom(s, name); + emit_ic(s, name); + break; + case OP_scope_get_private_field: + emit_op(s, OP_scope_get_private_field2); + emit_atom(s, name); + emit_u16(s, scope); + break; + case OP_get_array_el: + /* XXX: replace by a single opcode ? */ + emit_op(s, OP_to_propkey2); + emit_op(s, OP_dup2); + emit_op(s, OP_get_array_el); + break; + case OP_get_super_value: + emit_op(s, OP_to_propkey); + emit_op(s, OP_dup3); + emit_op(s, OP_get_super_value); + break; + default: + abort(); + } + } else { + switch(opcode) { + case OP_scope_get_var: + label = new_label(s); + emit_op(s, OP_scope_make_ref); + emit_atom(s, name); + emit_u32(s, label); + emit_u16(s, scope); + update_label(fd, label, 1); + opcode = OP_get_ref_value; + break; + case OP_get_array_el: + emit_op(s, OP_to_propkey2); + break; + case OP_get_super_value: + emit_op(s, OP_to_propkey); + break; + } + } + + *popcode = opcode; + *pscope = scope; + /* name has refcount for OP_get_field and OP_get_ref_value, + and JS_ATOM_NULL for other opcodes */ + *pname = name; + *plabel = label; + if (pdepth) + *pdepth = depth; + return 0; +} + +typedef enum { + PUT_LVALUE_NOKEEP, /* [depth] v -> */ + PUT_LVALUE_NOKEEP_DEPTH, /* [depth] v -> , keep depth (currently + just disable optimizations) */ + PUT_LVALUE_KEEP_TOP, /* [depth] v -> v */ + PUT_LVALUE_KEEP_SECOND, /* [depth] v0 v -> v0 */ + PUT_LVALUE_NOKEEP_BOTTOM, /* v [depth] -> */ +} PutLValueEnum; + +/* name has a live reference. 'is_let' is only used with opcode = + OP_scope_get_var which is never generated by get_lvalue(). */ +static void put_lvalue(JSParseState *s, int opcode, int scope, + JSAtom name, int label, PutLValueEnum special, + BOOL is_let) +{ + switch(opcode) { + case OP_get_field: + case OP_scope_get_private_field: + /* depth = 1 */ + switch(special) { + case PUT_LVALUE_NOKEEP: + case PUT_LVALUE_NOKEEP_DEPTH: + break; + case PUT_LVALUE_KEEP_TOP: + emit_op(s, OP_insert2); /* obj v -> v obj v */ + break; + case PUT_LVALUE_KEEP_SECOND: + emit_op(s, OP_perm3); /* obj v0 v -> v0 obj v */ + break; + case PUT_LVALUE_NOKEEP_BOTTOM: + emit_op(s, OP_swap); + break; + default: + abort(); + } + break; + case OP_get_array_el: + case OP_get_ref_value: + /* depth = 2 */ + if (opcode == OP_get_ref_value) { + JS_FreeAtom(s->ctx, name); + emit_label(s, label); + } + switch(special) { + case PUT_LVALUE_NOKEEP: + emit_op(s, OP_nop); /* will trigger optimization */ + break; + case PUT_LVALUE_NOKEEP_DEPTH: + break; + case PUT_LVALUE_KEEP_TOP: + emit_op(s, OP_insert3); /* obj prop v -> v obj prop v */ + break; + case PUT_LVALUE_KEEP_SECOND: + emit_op(s, OP_perm4); /* obj prop v0 v -> v0 obj prop v */ + break; + case PUT_LVALUE_NOKEEP_BOTTOM: + emit_op(s, OP_rot3l); + break; + default: + abort(); + } + break; + case OP_get_super_value: + /* depth = 3 */ + switch(special) { + case PUT_LVALUE_NOKEEP: + case PUT_LVALUE_NOKEEP_DEPTH: + break; + case PUT_LVALUE_KEEP_TOP: + emit_op(s, OP_insert4); /* this obj prop v -> v this obj prop v */ + break; + case PUT_LVALUE_KEEP_SECOND: + emit_op(s, OP_perm5); /* this obj prop v0 v -> v0 this obj prop v */ + break; + case PUT_LVALUE_NOKEEP_BOTTOM: + emit_op(s, OP_rot4l); + break; + default: + abort(); + } + break; + default: + break; + } + + switch(opcode) { + case OP_scope_get_var: /* val -- */ + assert(special == PUT_LVALUE_NOKEEP || + special == PUT_LVALUE_NOKEEP_DEPTH); + emit_op(s, is_let ? OP_scope_put_var_init : OP_scope_put_var); + emit_u32(s, name); /* has refcount */ + emit_u16(s, scope); + break; + case OP_get_field: + emit_op(s, OP_put_field); + emit_u32(s, name); /* name has refcount */ + emit_ic(s, name); + break; + case OP_scope_get_private_field: + emit_op(s, OP_scope_put_private_field); + emit_u32(s, name); /* name has refcount */ + emit_u16(s, scope); + break; + case OP_get_array_el: + emit_op(s, OP_put_array_el); + break; + case OP_get_ref_value: + emit_op(s, OP_put_ref_value); + break; + case OP_get_super_value: + emit_op(s, OP_put_super_value); + break; + default: + abort(); + } +} + +static __exception int js_parse_expr_paren(JSParseState *s) +{ + if (js_parse_expect(s, '(')) + return -1; + if (js_parse_expr(s)) + return -1; + if (js_parse_expect(s, ')')) + return -1; + return 0; +} + +static int js_unsupported_keyword(JSParseState *s, JSAtom atom) +{ + char buf[ATOM_GET_STR_BUF_SIZE]; + return js_parse_error(s, "unsupported keyword: %s", + JS_AtomGetStr(s->ctx, buf, sizeof(buf), atom)); +} + +static __exception int js_define_var(JSParseState *s, JSAtom name, int tok) +{ + JSFunctionDef *fd = s->cur_func; + JSVarDefEnum var_def_type; + + if (name == JS_ATOM_yield && fd->func_kind == JS_FUNC_GENERATOR) { + return js_parse_error(s, "yield is a reserved identifier"); + } + if ((name == JS_ATOM_arguments || name == JS_ATOM_eval) + && fd->is_strict_mode) { + return js_parse_error(s, "invalid variable name in strict mode"); + } + if (tok == TOK_LET || tok == TOK_CONST) { + if (name == JS_ATOM_let) + return js_parse_error(s, "invalid lexical variable name 'let'"); + // |undefined| is allowed as an identifier except at the global + // scope of a classic script; sloppy or strict doesn't matter + if (name == JS_ATOM_undefined + && fd->scope_level == 1 + && fd->is_global_var + && !fd->module) { + return js_parse_error(s, "'undefined' already declared"); + } + } + switch(tok) { + case TOK_LET: + var_def_type = JS_VAR_DEF_LET; + break; + case TOK_CONST: + var_def_type = JS_VAR_DEF_CONST; + break; + case TOK_VAR: + var_def_type = JS_VAR_DEF_VAR; + break; + case TOK_CATCH: + var_def_type = JS_VAR_DEF_CATCH; + break; + default: + abort(); + } + if (define_var(s, fd, name, var_def_type) < 0) + return -1; + return 0; +} + +static void js_emit_spread_code(JSParseState *s, int depth) +{ + int label_rest_next, label_rest_done; + + /* XXX: could check if enum object is an actual array and optimize + slice extraction. enumeration record and target array are in a + different order from OP_append case. */ + /* enum_rec xxx -- enum_rec xxx array 0 */ + emit_op(s, OP_array_from); + emit_u16(s, 0); + emit_op(s, OP_push_i32); + emit_u32(s, 0); + emit_label(s, label_rest_next = new_label(s)); + emit_op(s, OP_for_of_next); + emit_u8(s, 2 + depth); + label_rest_done = emit_goto(s, OP_if_true, -1); + /* array idx val -- array idx */ + emit_op(s, OP_define_array_el); + emit_op(s, OP_inc); + emit_goto(s, OP_goto, label_rest_next); + emit_label(s, label_rest_done); + /* enum_rec xxx array idx undef -- enum_rec xxx array */ + emit_op(s, OP_drop); + emit_op(s, OP_drop); +} + +static int js_parse_check_duplicate_parameter(JSParseState *s, JSAtom name) +{ + /* Check for duplicate parameter names */ + JSFunctionDef *fd = s->cur_func; + int i; + for (i = 0; i < fd->arg_count; i++) { + if (fd->args[i].var_name == name) + goto duplicate; + } + for (i = 0; i < fd->var_count; i++) { + if (fd->vars[i].var_name == name) + goto duplicate; + } + return 0; + +duplicate: + return js_parse_error(s, "Duplicate parameter name not allowed in this context"); +} + +static JSAtom js_parse_destructuring_var(JSParseState *s, int tok, int is_arg) +{ + JSAtom name; + + if (!(s->token.val == TOK_IDENT && !s->token.u.ident.is_reserved) + || (s->cur_func->is_strict_mode && + (s->token.u.ident.atom == JS_ATOM_eval || s->token.u.ident.atom == JS_ATOM_arguments))) { + js_parse_error(s, "invalid destructuring target"); + return JS_ATOM_NULL; + } + name = JS_DupAtom(s->ctx, s->token.u.ident.atom); + if (is_arg && js_parse_check_duplicate_parameter(s, name)) + goto fail; + if (next_token(s)) + goto fail; + + return name; +fail: + JS_FreeAtom(s->ctx, name); + return JS_ATOM_NULL; +} + +/* Return -1 if error, 0 if no initializer, 1 if an initializer is + present at the top level. */ +static int js_parse_destructuring_element(JSParseState *s, int tok, int is_arg, + int hasval, int has_ellipsis, + BOOL allow_initializer) +{ + int label_parse, label_assign, label_done, label_lvalue, depth_lvalue; + int start_addr, assign_addr; + JSAtom prop_name, var_name; + int opcode, scope, tok1, skip_bits; + BOOL has_initializer; + + label_lvalue = -1; + + if (has_ellipsis < 0) { + /* pre-parse destructuration target for spread detection */ + js_parse_skip_parens_token(s, &skip_bits, FALSE); + has_ellipsis = skip_bits & SKIP_HAS_ELLIPSIS; + } + + label_parse = new_label(s); + label_assign = new_label(s); + + start_addr = s->cur_func->byte_code.size; + if (hasval) { + /* consume value from the stack */ + emit_op(s, OP_dup); + emit_op(s, OP_undefined); + emit_op(s, OP_strict_eq); + emit_goto(s, OP_if_true, label_parse); + emit_label(s, label_assign); + } else { + emit_goto(s, OP_goto, label_parse); + emit_label(s, label_assign); + /* leave value on the stack */ + emit_op(s, OP_dup); + } + assign_addr = s->cur_func->byte_code.size; + if (s->token.val == '{') { + if (next_token(s)) + return -1; + /* throw an exception if the value cannot be converted to an object */ + emit_op(s, OP_to_object); + if (has_ellipsis) { + /* add excludeList on stack just below src object */ + emit_op(s, OP_object); + emit_op(s, OP_swap); + } + while (s->token.val != '}') { + int prop_type; + if (s->token.val == TOK_ELLIPSIS) { + if (!has_ellipsis) { + JS_ThrowInternalError(s->ctx, "unexpected ellipsis token"); + return -1; + } + if (next_token(s)) + return -1; + if (tok) { + var_name = js_parse_destructuring_var(s, tok, is_arg); + if (var_name == JS_ATOM_NULL) + return -1; + opcode = OP_scope_get_var; + scope = s->cur_func->scope_level; + label_lvalue = -1; + depth_lvalue = 0; + } else { + if (js_parse_left_hand_side_expr(s)) + return -1; + + if (get_lvalue(s, &opcode, &scope, &var_name, + &label_lvalue, &depth_lvalue, FALSE, '{')) + return -1; + } + if (s->token.val != '}') { + js_parse_error(s, "assignment rest property must be last"); + goto var_error; + } + emit_op(s, OP_object); /* target */ + emit_op(s, OP_copy_data_properties); + emit_u8(s, 0 | ((depth_lvalue + 1) << 2) | ((depth_lvalue + 2) << 5)); + goto set_val; + } + prop_type = js_parse_property_name(s, &prop_name, FALSE, TRUE, FALSE); + if (prop_type < 0) + return -1; + var_name = JS_ATOM_NULL; + opcode = OP_scope_get_var; + scope = s->cur_func->scope_level; + label_lvalue = -1; + depth_lvalue = 0; + if (prop_type == PROP_TYPE_IDENT) { + if (next_token(s)) + goto prop_error; + if ((s->token.val == '[' || s->token.val == '{') + && ((tok1 = js_parse_skip_parens_token(s, &skip_bits, FALSE)) == ',' || + tok1 == '=' || tok1 == '}')) { + if (prop_name == JS_ATOM_NULL) { + /* computed property name on stack */ + if (has_ellipsis) { + /* define the property in excludeList */ + emit_op(s, OP_to_propkey); /* avoid calling ToString twice */ + emit_op(s, OP_perm3); /* TOS: src excludeList prop */ + emit_op(s, OP_null); /* TOS: src excludeList prop null */ + emit_op(s, OP_define_array_el); /* TOS: src excludeList prop */ + emit_op(s, OP_perm3); /* TOS: excludeList src prop */ + } + /* get the computed property from the source object */ + emit_op(s, OP_get_array_el2); + } else { + /* named property */ + if (has_ellipsis) { + /* define the property in excludeList */ + emit_op(s, OP_swap); /* TOS: src excludeList */ + emit_op(s, OP_null); /* TOS: src excludeList null */ + emit_op(s, OP_define_field); /* TOS: src excludeList */ + emit_atom(s, prop_name); + emit_op(s, OP_swap); /* TOS: excludeList src */ + } + /* get the named property from the source object */ + emit_op(s, OP_get_field2); + emit_u32(s, prop_name); + emit_ic(s, prop_name); + } + if (js_parse_destructuring_element(s, tok, is_arg, TRUE, -1, TRUE) < 0) + return -1; + if (s->token.val == '}') + break; + /* accept a trailing comma before the '}' */ + if (js_parse_expect(s, ',')) + return -1; + continue; + } + if (prop_name == JS_ATOM_NULL) { + emit_op(s, OP_to_propkey2); + if (has_ellipsis) { + /* define the property in excludeList */ + emit_op(s, OP_perm3); + emit_op(s, OP_null); + emit_op(s, OP_define_array_el); + emit_op(s, OP_perm3); + } + /* source prop -- source source prop */ + emit_op(s, OP_dup1); + } else { + if (has_ellipsis) { + /* define the property in excludeList */ + emit_op(s, OP_swap); + emit_op(s, OP_null); + emit_op(s, OP_define_field); + emit_atom(s, prop_name); + emit_op(s, OP_swap); + } + /* source -- source source */ + emit_op(s, OP_dup); + } + if (tok) { + var_name = js_parse_destructuring_var(s, tok, is_arg); + if (var_name == JS_ATOM_NULL) + goto prop_error; + } else { + if (js_parse_left_hand_side_expr(s)) + goto prop_error; + lvalue: + if (get_lvalue(s, &opcode, &scope, &var_name, + &label_lvalue, &depth_lvalue, FALSE, '{')) + goto prop_error; + /* swap ref and lvalue object if any */ + if (prop_name == JS_ATOM_NULL) { + switch(depth_lvalue) { + case 1: + /* source prop x -> x source prop */ + emit_op(s, OP_rot3r); + break; + case 2: + /* source prop x y -> x y source prop */ + emit_op(s, OP_swap2); /* t p2 s p1 */ + break; + case 3: + /* source prop x y z -> x y z source prop */ + emit_op(s, OP_rot5l); + emit_op(s, OP_rot5l); + break; + } + } else { + switch(depth_lvalue) { + case 1: + /* source x -> x source */ + emit_op(s, OP_swap); + break; + case 2: + /* source x y -> x y source */ + emit_op(s, OP_rot3l); + break; + case 3: + /* source x y z -> x y z source */ + emit_op(s, OP_rot4l); + break; + } + } + } + if (prop_name == JS_ATOM_NULL) { + /* computed property name on stack */ + /* XXX: should have OP_get_array_el2x with depth */ + /* source prop -- val */ + emit_op(s, OP_get_array_el); + } else { + /* named property */ + /* XXX: should have OP_get_field2x with depth */ + /* source -- val */ + emit_op(s, OP_get_field); + emit_u32(s, prop_name); + emit_ic(s, prop_name); + } + } else { + /* prop_type = PROP_TYPE_VAR, cannot be a computed property */ + if (is_arg && js_parse_check_duplicate_parameter(s, prop_name)) + goto prop_error; + if (s->cur_func->is_strict_mode && + (prop_name == JS_ATOM_eval || prop_name == JS_ATOM_arguments)) { + js_parse_error(s, "invalid destructuring target"); + goto prop_error; + } + if (has_ellipsis) { + /* define the property in excludeList */ + emit_op(s, OP_swap); + emit_op(s, OP_null); + emit_op(s, OP_define_field); + emit_atom(s, prop_name); + emit_op(s, OP_swap); + } + if (!tok || tok == TOK_VAR) { + /* generate reference */ + /* source -- source source */ + emit_op(s, OP_dup); + emit_op(s, OP_scope_get_var); + emit_atom(s, prop_name); + emit_u16(s, s->cur_func->scope_level); + goto lvalue; + } + var_name = JS_DupAtom(s->ctx, prop_name); + /* source -- source val */ + emit_op(s, OP_get_field2); + emit_u32(s, prop_name); + emit_ic(s, prop_name); + } + set_val: + if (tok) { + if (js_define_var(s, var_name, tok)) + goto var_error; + scope = s->cur_func->scope_level; + } + if (s->token.val == '=') { /* handle optional default value */ + int label_hasval; + emit_op(s, OP_dup); + emit_op(s, OP_undefined); + emit_op(s, OP_strict_eq); + label_hasval = emit_goto(s, OP_if_false, -1); + if (next_token(s)) + goto var_error; + emit_op(s, OP_drop); + if (js_parse_assign_expr(s)) + goto var_error; + if (opcode == OP_scope_get_var || opcode == OP_get_ref_value) + set_object_name(s, var_name); + emit_label(s, label_hasval); + } + /* store value into lvalue object */ + put_lvalue(s, opcode, scope, var_name, label_lvalue, + PUT_LVALUE_NOKEEP_DEPTH, + (tok == TOK_CONST || tok == TOK_LET)); + if (s->token.val == '}') + break; + /* accept a trailing comma before the '}' */ + if (js_parse_expect(s, ',')) + return -1; + } + /* drop the source object */ + emit_op(s, OP_drop); + if (has_ellipsis) { + emit_op(s, OP_drop); /* pop excludeList */ + } + if (next_token(s)) + return -1; + } else if (s->token.val == '[') { + BOOL has_spread; + int enum_depth; + BlockEnv block_env; + + if (next_token(s)) + return -1; + /* the block environment is only needed in generators in case + 'yield' triggers a 'return' */ + push_break_entry(s->cur_func, &block_env, + JS_ATOM_NULL, -1, -1, 2); + block_env.has_iterator = TRUE; + emit_op(s, OP_for_of_start); + has_spread = FALSE; + while (s->token.val != ']') { + /* get the next value */ + if (s->token.val == TOK_ELLIPSIS) { + if (next_token(s)) + return -1; + if (s->token.val == ',' || s->token.val == ']') + return js_parse_error(s, "missing binding pattern..."); + has_spread = TRUE; + } + if (s->token.val == ',') { + /* do nothing, skip the value, has_spread is false */ + emit_op(s, OP_for_of_next); + emit_u8(s, 0); + emit_op(s, OP_drop); + emit_op(s, OP_drop); + } else if ((s->token.val == '[' || s->token.val == '{') + && ((tok1 = js_parse_skip_parens_token(s, &skip_bits, FALSE)) == ',' || + tok1 == '=' || tok1 == ']')) { + if (has_spread) { + if (tok1 == '=') + return js_parse_error(s, "rest element cannot have a default value"); + js_emit_spread_code(s, 0); + } else { + emit_op(s, OP_for_of_next); + emit_u8(s, 0); + emit_op(s, OP_drop); + } + if (js_parse_destructuring_element(s, tok, is_arg, TRUE, skip_bits & SKIP_HAS_ELLIPSIS, TRUE) < 0) + return -1; + } else { + var_name = JS_ATOM_NULL; + enum_depth = 0; + if (tok) { + var_name = js_parse_destructuring_var(s, tok, is_arg); + if (var_name == JS_ATOM_NULL) + goto var_error; + if (js_define_var(s, var_name, tok)) + goto var_error; + opcode = OP_scope_get_var; + scope = s->cur_func->scope_level; + } else { + if (js_parse_left_hand_side_expr(s)) + return -1; + if (get_lvalue(s, &opcode, &scope, &var_name, + &label_lvalue, &enum_depth, FALSE, '[')) { + return -1; + } + } + if (has_spread) { + js_emit_spread_code(s, enum_depth); + } else { + emit_op(s, OP_for_of_next); + emit_u8(s, enum_depth); + emit_op(s, OP_drop); + } + if (s->token.val == '=' && !has_spread) { + /* handle optional default value */ + int label_hasval; + emit_op(s, OP_dup); + emit_op(s, OP_undefined); + emit_op(s, OP_strict_eq); + label_hasval = emit_goto(s, OP_if_false, -1); + if (next_token(s)) + goto var_error; + emit_op(s, OP_drop); + if (js_parse_assign_expr(s)) + goto var_error; + if (opcode == OP_scope_get_var || opcode == OP_get_ref_value) + set_object_name(s, var_name); + emit_label(s, label_hasval); + } + /* store value into lvalue object */ + put_lvalue(s, opcode, scope, var_name, + label_lvalue, PUT_LVALUE_NOKEEP_DEPTH, + (tok == TOK_CONST || tok == TOK_LET)); + } + if (s->token.val == ']') + break; + if (has_spread) + return js_parse_error(s, "rest element must be the last one"); + /* accept a trailing comma before the ']' */ + if (js_parse_expect(s, ',')) + return -1; + } + /* close iterator object: + if completed, enum_obj has been replaced by undefined */ + emit_op(s, OP_iterator_close); + pop_break_entry(s->cur_func); + if (next_token(s)) + return -1; + } else { + return js_parse_error(s, "invalid assignment syntax"); + } + if (s->token.val == '=' && allow_initializer) { + label_done = emit_goto(s, OP_goto, -1); + if (next_token(s)) + return -1; + emit_label(s, label_parse); + if (hasval) + emit_op(s, OP_drop); + if (js_parse_assign_expr(s)) + return -1; + emit_goto(s, OP_goto, label_assign); + emit_label(s, label_done); + has_initializer = TRUE; + } else { + /* normally hasval is true except if + js_parse_skip_parens_token() was wrong in the parsing */ + // assert(hasval); + if (!hasval) { + js_parse_error(s, "too complicated destructuring expression"); + return -1; + } + /* remove test and decrement label ref count */ + memset(s->cur_func->byte_code.buf + start_addr, OP_nop, + assign_addr - start_addr); + s->cur_func->label_slots[label_parse].ref_count--; + has_initializer = FALSE; + } + return has_initializer; + + prop_error: + JS_FreeAtom(s->ctx, prop_name); + var_error: + JS_FreeAtom(s->ctx, var_name); + return -1; +} + +typedef enum FuncCallType { + FUNC_CALL_NORMAL, + FUNC_CALL_NEW, + FUNC_CALL_SUPER_CTOR, + FUNC_CALL_TEMPLATE, +} FuncCallType; + +static void optional_chain_test(JSParseState *s, int *poptional_chaining_label, + int drop_count) +{ + int label_next, i; + if (*poptional_chaining_label < 0) + *poptional_chaining_label = new_label(s); + /* XXX: could be more efficient with a specific opcode */ + emit_op(s, OP_dup); + emit_op(s, OP_is_undefined_or_null); + label_next = emit_goto(s, OP_if_false, -1); + for(i = 0; i < drop_count; i++) + emit_op(s, OP_drop); + emit_op(s, OP_undefined); + emit_goto(s, OP_goto, *poptional_chaining_label); + emit_label(s, label_next); +} + +/* allowed parse_flags: PF_POSTFIX_CALL */ +static __exception int js_parse_postfix_expr(JSParseState *s, int parse_flags) +{ + FuncCallType call_type; + int optional_chaining_label; + BOOL accept_lparen = (parse_flags & PF_POSTFIX_CALL) != 0; + + call_type = FUNC_CALL_NORMAL; + switch(s->token.val) { + case TOK_NUMBER: + { + JSValue val; + val = s->token.u.num.val; + + if (JS_VALUE_GET_TAG(val) == JS_TAG_INT) { + emit_op(s, OP_push_i32); + emit_u32(s, JS_VALUE_GET_INT(val)); + } else { + if (emit_push_const(s, val, 0) < 0) + return -1; + } + } + if (next_token(s)) + return -1; + break; + case TOK_TEMPLATE: + if (js_parse_template(s, 0, NULL)) + return -1; + break; + case TOK_STRING: + if (emit_push_const(s, s->token.u.str.str, 1)) + return -1; + if (next_token(s)) + return -1; + break; + + case TOK_DIV_ASSIGN: + s->buf_ptr -= 2; + goto parse_regexp; + case '/': + s->buf_ptr--; + parse_regexp: + { + JSValue str; + int ret, backtrace_flags; + if (!s->ctx->compile_regexp) + return js_parse_error(s, "RegExp are not supported"); + /* the previous token is '/' or '/=', so no need to free */ + if (js_parse_regexp(s)) + return -1; + ret = emit_push_const(s, s->token.u.regexp.body, 0); + str = s->ctx->compile_regexp(s->ctx, s->token.u.regexp.body, + s->token.u.regexp.flags); + if (JS_IsException(str)) { + /* add the line number info */ + backtrace_flags = 0; + if (s->cur_func && s->cur_func->backtrace_barrier) + backtrace_flags = JS_BACKTRACE_FLAG_SINGLE_LEVEL; + build_backtrace(s->ctx, s->ctx->rt->current_exception, + s->filename, + s->token.line_num, + s->token.col_num, + backtrace_flags); + return -1; + } + ret = emit_push_const(s, str, 0); + JS_FreeValue(s->ctx, str); + if (ret) + return -1; + /* we use a specific opcode to be sure the correct + function is called (otherwise the bytecode would have + to be verified by the RegExp constructor) */ + emit_op(s, OP_regexp); + if (next_token(s)) + return -1; + } + break; + case '(': + if (js_parse_expr_paren(s)) + return -1; + break; + case TOK_FUNCTION: + if (js_parse_function_decl(s, JS_PARSE_FUNC_EXPR, + JS_FUNC_NORMAL, JS_ATOM_NULL, + s->token.ptr, + s->token.line_num, + s->token.col_num)) + return -1; + break; + case TOK_CLASS: + if (js_parse_class(s, TRUE, JS_PARSE_EXPORT_NONE)) + return -1; + break; + case TOK_NULL: + if (next_token(s)) + return -1; + emit_op(s, OP_null); + break; + case TOK_THIS: + if (next_token(s)) + return -1; + emit_op(s, OP_scope_get_var); + emit_atom(s, JS_ATOM_this); + emit_u16(s, 0); + break; + case TOK_FALSE: + if (next_token(s)) + return -1; + emit_op(s, OP_push_false); + break; + case TOK_TRUE: + if (next_token(s)) + return -1; + emit_op(s, OP_push_true); + break; + case TOK_IDENT: + { + JSAtom name; + if (s->token.u.ident.is_reserved) { + return js_parse_error_reserved_identifier(s); + } + if (token_is_pseudo_keyword(s, JS_ATOM_async) && + peek_token(s, TRUE) != '\n') { + const uint8_t *source_ptr; + int source_line_num; + int source_col_num; + + source_ptr = s->token.ptr; + source_line_num = s->token.line_num; + source_col_num = s->token.col_num; + if (next_token(s)) + return -1; + if (s->token.val == TOK_FUNCTION) { + if (js_parse_function_decl(s, JS_PARSE_FUNC_EXPR, + JS_FUNC_ASYNC, JS_ATOM_NULL, + source_ptr, + source_line_num, + source_col_num)) + return -1; + } else { + name = JS_DupAtom(s->ctx, JS_ATOM_async); + goto do_get_var; + } + } else { + if (s->token.u.ident.atom == JS_ATOM_arguments && + !s->cur_func->arguments_allowed) { + js_parse_error(s, "'arguments' identifier is not allowed in class field initializer"); + return -1; + } + name = JS_DupAtom(s->ctx, s->token.u.ident.atom); + if (next_token(s)) { /* update line number before emitting code */ + JS_FreeAtom(s->ctx, name); + return -1; + } + do_get_var: + emit_op(s, OP_scope_get_var); + emit_u32(s, name); + emit_u16(s, s->cur_func->scope_level); + } + } + break; + case '{': + case '[': + { + int skip_bits; + if (js_parse_skip_parens_token(s, &skip_bits, FALSE) == '=') { + if (js_parse_destructuring_element(s, 0, 0, FALSE, skip_bits & SKIP_HAS_ELLIPSIS, TRUE) < 0) + return -1; + } else { + if (s->token.val == '{') { + if (js_parse_object_literal(s)) + return -1; + } else { + if (js_parse_array_literal(s)) + return -1; + } + } + } + break; + case TOK_NEW: + if (next_token(s)) + return -1; + if (s->token.val == '.') { + if (next_token(s)) + return -1; + if (!token_is_pseudo_keyword(s, JS_ATOM_target)) + return js_parse_error(s, "expecting target"); + if (!s->cur_func->new_target_allowed) + return js_parse_error(s, "new.target only allowed within functions"); + if (next_token(s)) + return -1; + emit_op(s, OP_scope_get_var); + emit_atom(s, JS_ATOM_new_target); + emit_u16(s, 0); + } else { + emit_source_loc(s); + if (js_parse_postfix_expr(s, 0)) + return -1; + accept_lparen = TRUE; + if (s->token.val != '(') { + /* new operator on an object */ + emit_op(s, OP_dup); + emit_op(s, OP_call_constructor); + emit_u16(s, 0); + } else { + call_type = FUNC_CALL_NEW; + } + } + break; + case TOK_SUPER: + if (next_token(s)) + return -1; + if (s->token.val == '(') { + if (!s->cur_func->super_call_allowed) + return js_parse_error(s, "super() is only valid in a derived class constructor"); + call_type = FUNC_CALL_SUPER_CTOR; + } else if (s->token.val == '.' || s->token.val == '[') { + if (!s->cur_func->super_allowed) + return js_parse_error(s, "'super' is only valid in a method"); + emit_op(s, OP_scope_get_var); + emit_atom(s, JS_ATOM_this); + emit_u16(s, 0); + emit_op(s, OP_scope_get_var); + emit_atom(s, JS_ATOM_home_object); + emit_u16(s, 0); + emit_op(s, OP_get_super); + } else { + return js_parse_error(s, "invalid use of 'super'"); + } + break; + case TOK_IMPORT: + if (next_token(s)) + return -1; + if (s->token.val == '.') { + if (next_token(s)) + return -1; + if (!token_is_pseudo_keyword(s, JS_ATOM_meta)) + return js_parse_error(s, "meta expected"); + if (!s->is_module) + return js_parse_error(s, "import.meta only valid in module code"); + if (next_token(s)) + return -1; + emit_op(s, OP_special_object); + emit_u8(s, OP_SPECIAL_OBJECT_IMPORT_META); + } else { + if (js_parse_expect(s, '(')) + return -1; + if (!accept_lparen) + return js_parse_error(s, "invalid use of 'import()'"); + if (js_parse_assign_expr(s)) + return -1; + if (js_parse_expect(s, ')')) + return -1; + emit_op(s, OP_import); + } + break; + default: + return js_parse_error(s, "unexpected token in expression: '%.*s'", + (int)(s->buf_ptr - s->token.ptr), s->token.ptr); + } + + optional_chaining_label = -1; + for(;;) { + JSFunctionDef *fd = s->cur_func; + BOOL has_optional_chain = FALSE; + + if (s->token.val == TOK_QUESTION_MARK_DOT) { + /* optional chaining */ + if (next_token(s)) + return -1; + has_optional_chain = TRUE; + if (s->token.val == '(' && accept_lparen) { + goto parse_func_call; + } else if (s->token.val == '[') { + goto parse_array_access; + } else { + goto parse_property; + } + } else if (s->token.val == TOK_TEMPLATE && + call_type == FUNC_CALL_NORMAL) { + if (optional_chaining_label >= 0) { + return js_parse_error(s, "template literal cannot appear in an optional chain"); + } + call_type = FUNC_CALL_TEMPLATE; + goto parse_func_call2; + } else if (s->token.val == '(' && accept_lparen) { + int opcode, arg_count, drop_count; + + /* function call */ + parse_func_call: + if (next_token(s)) + return -1; + + if (call_type == FUNC_CALL_NORMAL) { + parse_func_call2: + emit_source_loc(s); + switch(opcode = get_prev_opcode(fd)) { + case OP_get_field: + /* keep the object on the stack */ + fd->byte_code.buf[fd->last_opcode_pos] = OP_get_field2; + drop_count = 2; + break; + case OP_get_field_opt_chain: + { + int opt_chain_label, next_label; + opt_chain_label = get_u32(fd->byte_code.buf + + fd->last_opcode_pos + 1 + 4 + 1); + /* keep the object on the stack */ + fd->byte_code.buf[fd->last_opcode_pos] = OP_get_field2; + fd->byte_code.size = fd->last_opcode_pos + 1 + 4; + next_label = emit_goto(s, OP_goto, -1); + emit_label(s, opt_chain_label); + /* need an additional undefined value for the + case where the optional field does not + exists */ + emit_op(s, OP_undefined); + emit_label(s, next_label); + drop_count = 2; + opcode = OP_get_field; + } + break; + case OP_scope_get_private_field: + /* keep the object on the stack */ + fd->byte_code.buf[fd->last_opcode_pos] = OP_scope_get_private_field2; + drop_count = 2; + break; + case OP_get_array_el: + /* keep the object on the stack */ + fd->byte_code.buf[fd->last_opcode_pos] = OP_get_array_el2; + drop_count = 2; + break; + case OP_get_array_el_opt_chain: + { + int opt_chain_label, next_label; + opt_chain_label = get_u32(fd->byte_code.buf + + fd->last_opcode_pos + 1 + 1); + /* keep the object on the stack */ + fd->byte_code.buf[fd->last_opcode_pos] = OP_get_array_el2; + fd->byte_code.size = fd->last_opcode_pos + 1; + next_label = emit_goto(s, OP_goto, -1); + emit_label(s, opt_chain_label); + /* need an additional undefined value for the + case where the optional field does not + exists */ + emit_op(s, OP_undefined); + emit_label(s, next_label); + drop_count = 2; + opcode = OP_get_array_el; + } + break; + case OP_scope_get_var: + { + JSAtom name; + int scope; + name = get_u32(fd->byte_code.buf + fd->last_opcode_pos + 1); + scope = get_u16(fd->byte_code.buf + fd->last_opcode_pos + 5); + if (name == JS_ATOM_eval && call_type == FUNC_CALL_NORMAL && !has_optional_chain) { + /* direct 'eval' */ + opcode = OP_eval; + } else { + /* verify if function name resolves to a simple + get_loc/get_arg: a function call inside a `with` + statement can resolve to a method call of the + `with` context object + */ + /* XXX: always generate the OP_scope_get_ref + and remove it in variable resolution + pass ? */ + if (has_with_scope(fd, scope)) { + opcode = OP_scope_get_ref; + fd->byte_code.buf[fd->last_opcode_pos] = opcode; + } + } + drop_count = 1; + } + break; + case OP_get_super_value: + fd->byte_code.buf[fd->last_opcode_pos] = OP_get_array_el; + /* on stack: this func_obj */ + opcode = OP_get_array_el; + drop_count = 2; + break; + default: + opcode = OP_invalid; + drop_count = 1; + break; + } + if (has_optional_chain) { + optional_chain_test(s, &optional_chaining_label, + drop_count); + } + } else { + opcode = OP_invalid; + } + + if (call_type == FUNC_CALL_TEMPLATE) { + if (js_parse_template(s, 1, &arg_count)) + return -1; + goto emit_func_call; + } else if (call_type == FUNC_CALL_SUPER_CTOR) { + emit_op(s, OP_scope_get_var); + emit_atom(s, JS_ATOM_this_active_func); + emit_u16(s, 0); + + emit_op(s, OP_get_super); + + emit_op(s, OP_scope_get_var); + emit_atom(s, JS_ATOM_new_target); + emit_u16(s, 0); + } else if (call_type == FUNC_CALL_NEW) { + emit_op(s, OP_dup); /* new.target = function */ + } + + /* parse arguments */ + arg_count = 0; + while (s->token.val != ')') { + if (arg_count >= 65535) { + return js_parse_error(s, "Too many arguments in function call (only %d allowed)", + 65535 - 1); + } + if (s->token.val == TOK_ELLIPSIS) + break; + if (js_parse_assign_expr(s)) + return -1; + arg_count++; + if (s->token.val == ')') + break; + /* accept a trailing comma before the ')' */ + if (js_parse_expect(s, ',')) + return -1; + } + if (s->token.val == TOK_ELLIPSIS) { + emit_op(s, OP_array_from); + emit_u16(s, arg_count); + emit_op(s, OP_push_i32); + emit_u32(s, arg_count); + + /* on stack: array idx */ + while (s->token.val != ')') { + if (s->token.val == TOK_ELLIPSIS) { + if (next_token(s)) + return -1; + if (js_parse_assign_expr(s)) + return -1; + /* XXX: could pass is_last indicator? */ + emit_op(s, OP_append); + } else { + if (js_parse_assign_expr(s)) + return -1; + /* array idx val */ + emit_op(s, OP_define_array_el); + emit_op(s, OP_inc); + } + if (s->token.val == ')') + break; + /* accept a trailing comma before the ')' */ + if (js_parse_expect(s, ',')) + return -1; + } + if (next_token(s)) + return -1; + /* drop the index */ + emit_op(s, OP_drop); + + /* apply function call */ + switch(opcode) { + case OP_get_field: + case OP_scope_get_private_field: + case OP_get_array_el: + case OP_scope_get_ref: + /* obj func array -> func obj array */ + emit_op(s, OP_perm3); + emit_op(s, OP_apply); + emit_u16(s, call_type == FUNC_CALL_NEW); + break; + case OP_eval: + emit_op(s, OP_apply_eval); + emit_u16(s, fd->scope_level); + fd->has_eval_call = TRUE; + break; + default: + if (call_type == FUNC_CALL_SUPER_CTOR) { + emit_op(s, OP_apply); + emit_u16(s, 1); + /* set the 'this' value */ + emit_op(s, OP_dup); + emit_op(s, OP_scope_put_var_init); + emit_atom(s, JS_ATOM_this); + emit_u16(s, 0); + + emit_class_field_init(s); + } else if (call_type == FUNC_CALL_NEW) { + /* obj func array -> func obj array */ + emit_op(s, OP_perm3); + emit_op(s, OP_apply); + emit_u16(s, 1); + } else { + /* func array -> func undef array */ + emit_op(s, OP_undefined); + emit_op(s, OP_swap); + emit_op(s, OP_apply); + emit_u16(s, 0); + } + break; + } + } else { + if (next_token(s)) + return -1; + emit_func_call: + switch(opcode) { + case OP_get_field: + case OP_scope_get_private_field: + case OP_get_array_el: + case OP_scope_get_ref: + emit_op(s, OP_call_method); + emit_u16(s, arg_count); + break; + case OP_eval: + emit_op(s, OP_eval); + emit_u16(s, arg_count); + emit_u16(s, fd->scope_level); + fd->has_eval_call = TRUE; + break; + default: + if (call_type == FUNC_CALL_SUPER_CTOR) { + emit_op(s, OP_call_constructor); + emit_u16(s, arg_count); + + /* set the 'this' value */ + emit_op(s, OP_dup); + emit_op(s, OP_scope_put_var_init); + emit_atom(s, JS_ATOM_this); + emit_u16(s, 0); + + emit_class_field_init(s); + } else if (call_type == FUNC_CALL_NEW) { + emit_op(s, OP_call_constructor); + emit_u16(s, arg_count); + } else { + emit_op(s, OP_call); + emit_u16(s, arg_count); + } + break; + } + } + call_type = FUNC_CALL_NORMAL; + } else if (s->token.val == '.') { + if (next_token(s)) + return -1; + parse_property: + if (s->token.val == TOK_PRIVATE_NAME) { + /* private class field */ + if (get_prev_opcode(fd) == OP_get_super) { + return js_parse_error(s, "private class field forbidden after super"); + } + if (has_optional_chain) { + optional_chain_test(s, &optional_chaining_label, 1); + } + emit_op(s, OP_scope_get_private_field); + emit_atom(s, s->token.u.ident.atom); + emit_u16(s, s->cur_func->scope_level); + } else { + if (!token_is_ident(s->token.val)) { + return js_parse_error(s, "expecting field name"); + } + if (get_prev_opcode(fd) == OP_get_super) { + JSValue val; + int ret; + val = JS_AtomToValue(s->ctx, s->token.u.ident.atom); + ret = emit_push_const(s, val, 1); + JS_FreeValue(s->ctx, val); + if (ret) + return -1; + emit_op(s, OP_get_super_value); + } else { + if (has_optional_chain) { + optional_chain_test(s, &optional_chaining_label, 1); + } + emit_op(s, OP_get_field); + emit_atom(s, s->token.u.ident.atom); + emit_ic(s, s->token.u.ident.atom); + } + } + if (next_token(s)) + return -1; + } else if (s->token.val == '[') { + int prev_op; + + parse_array_access: + prev_op = get_prev_opcode(fd); + if (has_optional_chain) { + optional_chain_test(s, &optional_chaining_label, 1); + } + if (next_token(s)) + return -1; + if (js_parse_expr(s)) + return -1; + if (js_parse_expect(s, ']')) + return -1; + if (prev_op == OP_get_super) { + emit_op(s, OP_get_super_value); + } else { + emit_op(s, OP_get_array_el); + } + } else { + break; + } + } + if (optional_chaining_label >= 0) { + JSFunctionDef *fd = s->cur_func; + int opcode; + emit_label_raw(s, optional_chaining_label); + /* modify the last opcode so that it is an indicator of an + optional chain */ + opcode = get_prev_opcode(fd); + if (opcode == OP_get_field || opcode == OP_get_array_el) { + if (opcode == OP_get_field) + opcode = OP_get_field_opt_chain; + else + opcode = OP_get_array_el_opt_chain; + fd->byte_code.buf[fd->last_opcode_pos] = opcode; + } else { + fd->last_opcode_pos = -1; + } + } + return 0; +} + +static __exception int js_parse_delete(JSParseState *s) +{ + JSFunctionDef *fd = s->cur_func; + JSAtom name; + int opcode; + + if (next_token(s)) + return -1; + if (js_parse_unary(s, PF_POW_FORBIDDEN)) + return -1; + switch(opcode = get_prev_opcode(fd)) { + case OP_get_field: + case OP_get_field_opt_chain: + { + JSValue val; + int ret, opt_chain_label, next_label; + if (opcode == OP_get_field_opt_chain) { + opt_chain_label = get_u32(fd->byte_code.buf + + fd->last_opcode_pos + 1 + 4 + 1); + } else { + opt_chain_label = -1; + } + name = get_u32(fd->byte_code.buf + fd->last_opcode_pos + 1); + fd->byte_code.size = fd->last_opcode_pos; + val = JS_AtomToValue(s->ctx, name); + ret = emit_push_const(s, val, 1); + JS_FreeValue(s->ctx, val); + JS_FreeAtom(s->ctx, name); + if (ret) + return ret; + emit_op(s, OP_delete); + if (opt_chain_label >= 0) { + next_label = emit_goto(s, OP_goto, -1); + emit_label(s, opt_chain_label); + /* if the optional chain is not taken, return 'true' */ + emit_op(s, OP_drop); + emit_op(s, OP_push_true); + emit_label(s, next_label); + } + fd->last_opcode_pos = -1; + } + break; + case OP_get_array_el: + fd->byte_code.size = fd->last_opcode_pos; + fd->last_opcode_pos = -1; + emit_op(s, OP_delete); + break; + case OP_get_array_el_opt_chain: + { + int opt_chain_label, next_label; + opt_chain_label = get_u32(fd->byte_code.buf + + fd->last_opcode_pos + 1 + 1); + fd->byte_code.size = fd->last_opcode_pos; + emit_op(s, OP_delete); + next_label = emit_goto(s, OP_goto, -1); + emit_label(s, opt_chain_label); + /* if the optional chain is not taken, return 'true' */ + emit_op(s, OP_drop); + emit_op(s, OP_push_true); + emit_label(s, next_label); + fd->last_opcode_pos = -1; + } + break; + case OP_scope_get_var: + /* 'delete this': this is not a reference */ + name = get_u32(fd->byte_code.buf + fd->last_opcode_pos + 1); + if (name == JS_ATOM_this || name == JS_ATOM_new_target) + goto ret_true; + if (fd->is_strict_mode) { + return js_parse_error(s, "cannot delete a direct reference in strict mode"); + } else { + fd->byte_code.buf[fd->last_opcode_pos] = OP_scope_delete_var; + } + break; + case OP_scope_get_private_field: + return js_parse_error(s, "cannot delete a private class field"); + case OP_get_super_value: + fd->byte_code.size = fd->last_opcode_pos; + fd->last_opcode_pos = -1; + emit_op(s, OP_throw_error); + emit_atom(s, JS_ATOM_NULL); + emit_u8(s, JS_THROW_ERROR_DELETE_SUPER); + break; + default: + ret_true: + emit_op(s, OP_drop); + emit_op(s, OP_push_true); + break; + } + return 0; +} + +/* allowed parse_flags: PF_POW_ALLOWED, PF_POW_FORBIDDEN */ +static __exception int js_parse_unary(JSParseState *s, int parse_flags) +{ + int op; + + switch(s->token.val) { + case '+': + case '-': + case '!': + case '~': + case TOK_VOID: + op = s->token.val; + if (next_token(s)) + return -1; + if (js_parse_unary(s, PF_POW_FORBIDDEN)) + return -1; + switch(op) { + case '-': + emit_op(s, OP_neg); + break; + case '+': + emit_op(s, OP_plus); + break; + case '!': + emit_op(s, OP_lnot); + break; + case '~': + emit_op(s, OP_not); + break; + case TOK_VOID: + emit_op(s, OP_drop); + emit_op(s, OP_undefined); + break; + default: + abort(); + } + parse_flags = 0; + break; + case TOK_DEC: + case TOK_INC: + { + int opcode, op, scope, label; + JSAtom name; + op = s->token.val; + if (next_token(s)) + return -1; + if (js_parse_unary(s, 0)) + return -1; + if (get_lvalue(s, &opcode, &scope, &name, &label, NULL, TRUE, op)) + return -1; + emit_op(s, OP_dec + op - TOK_DEC); + put_lvalue(s, opcode, scope, name, label, PUT_LVALUE_KEEP_TOP, + FALSE); + } + break; + case TOK_TYPEOF: + { + JSFunctionDef *fd; + if (next_token(s)) + return -1; + if (js_parse_unary(s, PF_POW_FORBIDDEN)) + return -1; + /* reference access should not return an exception, so we + patch the get_var */ + fd = s->cur_func; + if (get_prev_opcode(fd) == OP_scope_get_var) { + fd->byte_code.buf[fd->last_opcode_pos] = OP_scope_get_var_undef; + } + emit_op(s, OP_typeof); + parse_flags = 0; + } + break; + case TOK_DELETE: + if (js_parse_delete(s)) + return -1; + parse_flags = 0; + break; + case TOK_AWAIT: + if (!(s->cur_func->func_kind & JS_FUNC_ASYNC)) + return js_parse_error(s, "unexpected 'await' keyword"); + if (!s->cur_func->in_function_body) + return js_parse_error(s, "await in default expression"); + if (next_token(s)) + return -1; + if (js_parse_unary(s, PF_POW_FORBIDDEN)) + return -1; + s->cur_func->has_await = TRUE; + emit_op(s, OP_await); + parse_flags = 0; + break; + default: + if (js_parse_postfix_expr(s, PF_POSTFIX_CALL)) + return -1; + if (!s->got_lf && + (s->token.val == TOK_DEC || s->token.val == TOK_INC)) { + int opcode, op, scope, label; + JSAtom name; + op = s->token.val; + if (get_lvalue(s, &opcode, &scope, &name, &label, NULL, TRUE, op)) + return -1; + emit_op(s, OP_post_dec + op - TOK_DEC); + put_lvalue(s, opcode, scope, name, label, PUT_LVALUE_KEEP_SECOND, + FALSE); + if (next_token(s)) + return -1; + } + break; + } + if (parse_flags & (PF_POW_ALLOWED | PF_POW_FORBIDDEN)) { + if (s->token.val == TOK_POW) { + /* Strict ES7 exponentiation syntax rules: To solve + conficting semantics between different implementations + regarding the precedence of prefix operators and the + postifx exponential, ES7 specifies that -2**2 is a + syntax error. */ + if (parse_flags & PF_POW_FORBIDDEN) + return js_parse_error(s, "unparenthesized unary expression can't appear on the left-hand side of '**'"); + if (next_token(s)) + return -1; + if (js_parse_unary(s, PF_POW_ALLOWED)) + return -1; + emit_op(s, OP_pow); + } + } + return 0; +} + +/* allowed parse_flags: PF_IN_ACCEPTED */ +static __exception int js_parse_expr_binary(JSParseState *s, int level, + int parse_flags) +{ + int op, opcode; + + if (level == 0) { + return js_parse_unary(s, PF_POW_ALLOWED); + } else if (s->token.val == TOK_PRIVATE_NAME && + (parse_flags & PF_IN_ACCEPTED) && level == 4 && + peek_token(s, FALSE) == TOK_IN) { + JSAtom atom; + atom = JS_DupAtom(s->ctx, s->token.u.ident.atom); + if (next_token(s)) + goto fail_private_in; + if (s->token.val != TOK_IN) + goto fail_private_in; + if (next_token(s)) + goto fail_private_in; + if (js_parse_expr_binary(s, level - 1, parse_flags)) { + fail_private_in: + JS_FreeAtom(s->ctx, atom); + return -1; + } + emit_op(s, OP_scope_in_private_field); + emit_atom(s, atom); + emit_u16(s, s->cur_func->scope_level); + JS_FreeAtom(s->ctx, atom); + return 0; + } else { + if (js_parse_expr_binary(s, level - 1, parse_flags)) + return -1; + } + for(;;) { + op = s->token.val; + switch(level) { + case 1: + switch(op) { + case '*': + opcode = OP_mul; + break; + case '/': + opcode = OP_div; + break; + case '%': + opcode = OP_mod; + break; + default: + return 0; + } + break; + case 2: + switch(op) { + case '+': + opcode = OP_add; + break; + case '-': + opcode = OP_sub; + break; + default: + return 0; + } + break; + case 3: + switch(op) { + case TOK_SHL: + opcode = OP_shl; + break; + case TOK_SAR: + opcode = OP_sar; + break; + case TOK_SHR: + opcode = OP_shr; + break; + default: + return 0; + } + break; + case 4: + switch(op) { + case '<': + opcode = OP_lt; + break; + case '>': + opcode = OP_gt; + break; + case TOK_LTE: + opcode = OP_lte; + break; + case TOK_GTE: + opcode = OP_gte; + break; + case TOK_INSTANCEOF: + opcode = OP_instanceof; + break; + case TOK_IN: + if (parse_flags & PF_IN_ACCEPTED) { + opcode = OP_in; + } else { + return 0; + } + break; + default: + return 0; + } + break; + case 5: + switch(op) { + case TOK_EQ: + opcode = OP_eq; + break; + case TOK_NEQ: + opcode = OP_neq; + break; + case TOK_STRICT_EQ: + opcode = OP_strict_eq; + break; + case TOK_STRICT_NEQ: + opcode = OP_strict_neq; + break; + default: + return 0; + } + break; + case 6: + switch(op) { + case '&': + opcode = OP_and; + break; + default: + return 0; + } + break; + case 7: + switch(op) { + case '^': + opcode = OP_xor; + break; + default: + return 0; + } + break; + case 8: + switch(op) { + case '|': + opcode = OP_or; + break; + default: + return 0; + } + break; + default: + abort(); + } + if (next_token(s)) + return -1; + if (js_parse_expr_binary(s, level - 1, parse_flags)) + return -1; + emit_op(s, opcode); + } + return 0; +} + +/* allowed parse_flags: PF_IN_ACCEPTED */ +static __exception int js_parse_logical_and_or(JSParseState *s, int op, + int parse_flags) +{ + int label1; + + if (op == TOK_LAND) { + if (js_parse_expr_binary(s, 8, parse_flags)) + return -1; + } else { + if (js_parse_logical_and_or(s, TOK_LAND, parse_flags)) + return -1; + } + if (s->token.val == op) { + label1 = new_label(s); + + for(;;) { + if (next_token(s)) + return -1; + emit_op(s, OP_dup); + emit_goto(s, op == TOK_LAND ? OP_if_false : OP_if_true, label1); + emit_op(s, OP_drop); + + if (op == TOK_LAND) { + if (js_parse_expr_binary(s, 8, parse_flags)) + return -1; + } else { + if (js_parse_logical_and_or(s, TOK_LAND, parse_flags)) + return -1; + } + if (s->token.val != op) { + if (s->token.val == TOK_DOUBLE_QUESTION_MARK) + return js_parse_error(s, "cannot mix ?? with && or ||"); + break; + } + } + + emit_label(s, label1); + } + return 0; +} + +static __exception int js_parse_coalesce_expr(JSParseState *s, int parse_flags) +{ + int label1; + + if (js_parse_logical_and_or(s, TOK_LOR, parse_flags)) + return -1; + if (s->token.val == TOK_DOUBLE_QUESTION_MARK) { + label1 = new_label(s); + for(;;) { + if (next_token(s)) + return -1; + + emit_op(s, OP_dup); + emit_op(s, OP_is_undefined_or_null); + emit_goto(s, OP_if_false, label1); + emit_op(s, OP_drop); + + if (js_parse_expr_binary(s, 8, parse_flags)) + return -1; + if (s->token.val != TOK_DOUBLE_QUESTION_MARK) + break; + } + emit_label(s, label1); + } + return 0; +} + +/* allowed parse_flags: PF_IN_ACCEPTED */ +static __exception int js_parse_cond_expr(JSParseState *s, int parse_flags) +{ + int label1, label2; + + if (js_parse_coalesce_expr(s, parse_flags)) + return -1; + if (s->token.val == '?') { + if (next_token(s)) + return -1; + label1 = emit_goto(s, OP_if_false, -1); + + if (js_parse_assign_expr(s)) + return -1; + if (js_parse_expect(s, ':')) + return -1; + + label2 = emit_goto(s, OP_goto, -1); + + emit_label(s, label1); + + if (js_parse_assign_expr2(s, parse_flags & PF_IN_ACCEPTED)) + return -1; + + emit_label(s, label2); + } + return 0; +} + +/* allowed parse_flags: PF_IN_ACCEPTED */ +static __exception int js_parse_assign_expr2(JSParseState *s, int parse_flags) +{ + int opcode, op, scope; + JSAtom name0 = JS_ATOM_NULL; + JSAtom name; + + if (s->token.val == TOK_YIELD) { + BOOL is_star = FALSE, is_async; + + if (!(s->cur_func->func_kind & JS_FUNC_GENERATOR)) + return js_parse_error(s, "unexpected 'yield' keyword"); + if (!s->cur_func->in_function_body) + return js_parse_error(s, "yield in default expression"); + if (next_token(s)) + return -1; + /* XXX: is there a better method to detect 'yield' without + parameters ? */ + if (s->token.val != ';' && s->token.val != ')' && + s->token.val != ']' && s->token.val != '}' && + s->token.val != ',' && s->token.val != ':' && !s->got_lf) { + if (s->token.val == '*') { + is_star = TRUE; + if (next_token(s)) + return -1; + } + if (js_parse_assign_expr2(s, parse_flags)) + return -1; + } else { + emit_op(s, OP_undefined); + } + is_async = (s->cur_func->func_kind == JS_FUNC_ASYNC_GENERATOR); + + if (is_star) { + int label_loop, label_return, label_next; + int label_return1, label_yield, label_throw, label_throw1; + int label_throw2; + + label_loop = new_label(s); + label_yield = new_label(s); + + emit_op(s, is_async ? OP_for_await_of_start : OP_for_of_start); + + /* remove the catch offset (XXX: could avoid pushing back + undefined) */ + emit_op(s, OP_drop); + emit_op(s, OP_undefined); + + emit_op(s, OP_undefined); /* initial value */ + + emit_label(s, label_loop); + emit_op(s, OP_iterator_next); + if (is_async) + emit_op(s, OP_await); + emit_op(s, OP_iterator_check_object); + emit_op(s, OP_get_field2); + emit_atom(s, JS_ATOM_done); + emit_ic(s, JS_ATOM_done); + label_next = emit_goto(s, OP_if_true, -1); /* end of loop */ + emit_label(s, label_yield); + if (is_async) { + /* OP_async_yield_star takes the value as parameter */ + emit_op(s, OP_get_field); + emit_atom(s, JS_ATOM_value); + emit_ic(s, JS_ATOM_value); + emit_op(s, OP_async_yield_star); + } else { + /* OP_yield_star takes (value, done) as parameter */ + emit_op(s, OP_yield_star); + } + emit_op(s, OP_dup); + label_return = emit_goto(s, OP_if_true, -1); + emit_op(s, OP_drop); + emit_goto(s, OP_goto, label_loop); + + emit_label(s, label_return); + emit_op(s, OP_push_i32); + emit_u32(s, 2); + emit_op(s, OP_strict_eq); + label_throw = emit_goto(s, OP_if_true, -1); + + /* return handling */ + if (is_async) + emit_op(s, OP_await); + emit_op(s, OP_iterator_call); + emit_u8(s, 0); + label_return1 = emit_goto(s, OP_if_true, -1); + if (is_async) + emit_op(s, OP_await); + emit_op(s, OP_iterator_check_object); + emit_op(s, OP_get_field2); + emit_atom(s, JS_ATOM_done); + emit_ic(s, JS_ATOM_done); + emit_goto(s, OP_if_false, label_yield); + + emit_op(s, OP_get_field); + emit_atom(s, JS_ATOM_value); + emit_ic(s, JS_ATOM_value); + + emit_label(s, label_return1); + emit_op(s, OP_nip); + emit_op(s, OP_nip); + emit_op(s, OP_nip); + emit_return(s, TRUE); + + /* throw handling */ + emit_label(s, label_throw); + emit_op(s, OP_iterator_call); + emit_u8(s, 1); + label_throw1 = emit_goto(s, OP_if_true, -1); + if (is_async) + emit_op(s, OP_await); + emit_op(s, OP_iterator_check_object); + emit_op(s, OP_get_field2); + emit_atom(s, JS_ATOM_done); + emit_ic(s, JS_ATOM_done); + emit_goto(s, OP_if_false, label_yield); + emit_goto(s, OP_goto, label_next); + /* close the iterator and throw a type error exception */ + emit_label(s, label_throw1); + emit_op(s, OP_iterator_call); + emit_u8(s, 2); + label_throw2 = emit_goto(s, OP_if_true, -1); + if (is_async) + emit_op(s, OP_await); + emit_label(s, label_throw2); + + emit_op(s, OP_throw_error); + emit_atom(s, JS_ATOM_NULL); + emit_u8(s, JS_THROW_ERROR_ITERATOR_THROW); + + emit_label(s, label_next); + emit_op(s, OP_get_field); + emit_atom(s, JS_ATOM_value); + emit_ic(s, JS_ATOM_value); + emit_op(s, OP_nip); /* keep the value associated with + done = true */ + emit_op(s, OP_nip); + emit_op(s, OP_nip); + } else { + int label_next; + + if (is_async) + emit_op(s, OP_await); + emit_op(s, OP_yield); + label_next = emit_goto(s, OP_if_false, -1); + emit_return(s, TRUE); + emit_label(s, label_next); + } + return 0; + } else if (s->token.val == '(' && + js_parse_skip_parens_token(s, NULL, TRUE) == TOK_ARROW) { + return js_parse_function_decl(s, JS_PARSE_FUNC_ARROW, + JS_FUNC_NORMAL, JS_ATOM_NULL, + s->token.ptr, s->token.line_num, + s->token.col_num); + } else if (token_is_pseudo_keyword(s, JS_ATOM_async)) { + const uint8_t *source_ptr; + int tok, source_line_num, source_col_num; + JSParsePos pos; + + /* fast test */ + tok = peek_token(s, TRUE); + if (tok == TOK_FUNCTION || tok == '\n') + goto next; + + source_ptr = s->token.ptr; + source_line_num = s->token.line_num; + source_col_num = s->token.col_num; + js_parse_get_pos(s, &pos); + if (next_token(s)) + return -1; + if ((s->token.val == '(' && + js_parse_skip_parens_token(s, NULL, TRUE) == TOK_ARROW) || + (s->token.val == TOK_IDENT && !s->token.u.ident.is_reserved && + peek_token(s, TRUE) == TOK_ARROW)) { + return js_parse_function_decl(s, JS_PARSE_FUNC_ARROW, + JS_FUNC_ASYNC, JS_ATOM_NULL, + source_ptr, source_line_num, + source_col_num); + } else { + /* undo the token parsing */ + if (js_parse_seek_token(s, &pos)) + return -1; + } + } else if (s->token.val == TOK_IDENT && + peek_token(s, TRUE) == TOK_ARROW) { + return js_parse_function_decl(s, JS_PARSE_FUNC_ARROW, + JS_FUNC_NORMAL, JS_ATOM_NULL, + s->token.ptr, s->token.line_num, + s->token.col_num); + } + next: + if (s->token.val == TOK_IDENT) { + /* name0 is used to check for OP_set_name pattern, not duplicated */ + name0 = s->token.u.ident.atom; + } + if (js_parse_cond_expr(s, parse_flags)) + return -1; + + op = s->token.val; + if (op == '=' || (op >= TOK_MUL_ASSIGN && op <= TOK_POW_ASSIGN)) { + int label; + if (next_token(s)) + return -1; + if (get_lvalue(s, &opcode, &scope, &name, &label, NULL, (op != '='), op) < 0) + return -1; + + // comply with rather obtuse evaluation order of computed properties: + // obj[key]=val evaluates val->obj->key when obj is null/undefined + // but key->obj->val when an object + // FIXME(bnoordhuis) less stack shuffling; don't to_propkey twice in + // happy path; replace `dup is_undefined_or_null if_true` with new + // opcode if_undefined_or_null? replace `swap dup` with over? + if (op == '=' && opcode == OP_get_array_el) { + int label_next = -1; + JSFunctionDef *fd = s->cur_func; + assert(OP_to_propkey2 == fd->byte_code.buf[fd->last_opcode_pos]); + fd->byte_code.size = fd->last_opcode_pos; + fd->last_opcode_pos = -1; + emit_op(s, OP_swap); // obj key -> key obj + emit_op(s, OP_dup); + emit_op(s, OP_is_undefined_or_null); + label_next = emit_goto(s, OP_if_true, -1); + emit_op(s, OP_swap); + emit_op(s, OP_to_propkey); + emit_op(s, OP_swap); + emit_label(s, label_next); + emit_op(s, OP_swap); + } + + if (js_parse_assign_expr2(s, parse_flags)) { + JS_FreeAtom(s->ctx, name); + return -1; + } + + if (op == '=' && opcode == OP_get_array_el) { + emit_op(s, OP_swap); // obj key val -> obj val key + emit_op(s, OP_to_propkey); + emit_op(s, OP_swap); + } + + if (op == '=') { + if (opcode == OP_get_ref_value && name == name0) { + set_object_name(s, name); + } + } else { + emit_op(s, op - TOK_MUL_ASSIGN + OP_mul); + } + put_lvalue(s, opcode, scope, name, label, PUT_LVALUE_KEEP_TOP, FALSE); + } else if (op >= TOK_LAND_ASSIGN && op <= TOK_DOUBLE_QUESTION_MARK_ASSIGN) { + int label, label1, depth_lvalue, label2; + + if (next_token(s)) + return -1; + if (get_lvalue(s, &opcode, &scope, &name, &label, + &depth_lvalue, TRUE, op) < 0) + return -1; + + emit_op(s, OP_dup); + if (op == TOK_DOUBLE_QUESTION_MARK_ASSIGN) + emit_op(s, OP_is_undefined_or_null); + label1 = emit_goto(s, op == TOK_LOR_ASSIGN ? OP_if_true : OP_if_false, + -1); + emit_op(s, OP_drop); + + if (js_parse_assign_expr2(s, parse_flags)) { + JS_FreeAtom(s->ctx, name); + return -1; + } + + if (opcode == OP_get_ref_value && name == name0) { + set_object_name(s, name); + } + + switch(depth_lvalue) { + case 1: + emit_op(s, OP_insert2); + break; + case 2: + emit_op(s, OP_insert3); + break; + case 3: + emit_op(s, OP_insert4); + break; + default: + abort(); + } + + /* XXX: we disable the OP_put_ref_value optimization by not + using put_lvalue() otherwise depth_lvalue is not correct */ + put_lvalue(s, opcode, scope, name, label, PUT_LVALUE_NOKEEP_DEPTH, + FALSE); + label2 = emit_goto(s, OP_goto, -1); + + emit_label(s, label1); + + /* remove the lvalue stack entries */ + while (depth_lvalue != 0) { + emit_op(s, OP_nip); + depth_lvalue--; + } + + emit_label(s, label2); + } + return 0; +} + +static __exception int js_parse_assign_expr(JSParseState *s) +{ + return js_parse_assign_expr2(s, PF_IN_ACCEPTED); +} + +/* allowed parse_flags: PF_IN_ACCEPTED */ +static __exception int js_parse_expr2(JSParseState *s, int parse_flags) +{ + BOOL comma = FALSE; + for(;;) { + if (js_parse_assign_expr2(s, parse_flags)) + return -1; + if (comma) { + /* prevent get_lvalue from using the last expression + as an lvalue. This also prevents the conversion of + of get_var to get_ref for method lookup in function + call inside `with` statement. + */ + s->cur_func->last_opcode_pos = -1; + } + if (s->token.val != ',') + break; + comma = TRUE; + if (next_token(s)) + return -1; + emit_op(s, OP_drop); + } + return 0; +} + +static __exception int js_parse_expr(JSParseState *s) +{ + return js_parse_expr2(s, PF_IN_ACCEPTED); +} + +static void push_break_entry(JSFunctionDef *fd, BlockEnv *be, + JSAtom label_name, + int label_break, int label_cont, + int drop_count) +{ + be->prev = fd->top_break; + fd->top_break = be; + be->label_name = label_name; + be->label_break = label_break; + be->label_cont = label_cont; + be->drop_count = drop_count; + be->label_finally = -1; + be->scope_level = fd->scope_level; + be->has_iterator = FALSE; +} + +static void pop_break_entry(JSFunctionDef *fd) +{ + BlockEnv *be; + be = fd->top_break; + fd->top_break = be->prev; +} + +static __exception int emit_break(JSParseState *s, JSAtom name, int is_cont) +{ + BlockEnv *top; + int i, scope_level; + + scope_level = s->cur_func->scope_level; + top = s->cur_func->top_break; + while (top != NULL) { + close_scopes(s, scope_level, top->scope_level); + scope_level = top->scope_level; + if (is_cont && + top->label_cont != -1 && + (name == JS_ATOM_NULL || top->label_name == name)) { + /* continue stays inside the same block */ + emit_goto(s, OP_goto, top->label_cont); + return 0; + } + if (!is_cont && + top->label_break != -1 && + (name == JS_ATOM_NULL || top->label_name == name)) { + emit_goto(s, OP_goto, top->label_break); + return 0; + } + i = 0; + if (top->has_iterator) { + emit_op(s, OP_iterator_close); + i += 3; + } + for(; i < top->drop_count; i++) + emit_op(s, OP_drop); + if (top->label_finally != -1) { + /* must push dummy value to keep same stack depth */ + emit_op(s, OP_undefined); + emit_goto(s, OP_gosub, top->label_finally); + emit_op(s, OP_drop); + } + top = top->prev; + } + if (name == JS_ATOM_NULL) { + if (is_cont) + return js_parse_error(s, "continue must be inside loop"); + else + return js_parse_error(s, "break must be inside loop or switch"); + } else { + return js_parse_error(s, "break/continue label not found"); + } +} + +/* execute the finally blocks before return */ +static void emit_return(JSParseState *s, BOOL hasval) +{ + BlockEnv *top; + + if (s->cur_func->func_kind != JS_FUNC_NORMAL) { + if (!hasval) { + /* no value: direct return in case of async generator */ + emit_op(s, OP_undefined); + hasval = TRUE; + } else if (s->cur_func->func_kind == JS_FUNC_ASYNC_GENERATOR) { + /* the await must be done before handling the "finally" in + case it raises an exception */ + emit_op(s, OP_await); + } + } + + top = s->cur_func->top_break; + while (top != NULL) { + if (top->has_iterator || top->label_finally != -1) { + if (!hasval) { + emit_op(s, OP_undefined); + hasval = TRUE; + } + /* Remove the stack elements up to and including the catch + offset. When 'yield' is used in an expression we have + no easy way to count them, so we use this specific + instruction instead. */ + emit_op(s, OP_nip_catch); + /* stack: iter_obj next ret_val */ + if (top->has_iterator) { + if (s->cur_func->func_kind == JS_FUNC_ASYNC_GENERATOR) { + int label_next, label_next2; + emit_op(s, OP_nip); /* next */ + emit_op(s, OP_swap); + emit_op(s, OP_get_field2); + emit_atom(s, JS_ATOM_return); + emit_ic(s, JS_ATOM_return); + /* stack: iter_obj return_func */ + emit_op(s, OP_dup); + emit_op(s, OP_is_undefined_or_null); + label_next = emit_goto(s, OP_if_true, -1); + emit_op(s, OP_call_method); + emit_u16(s, 0); + emit_op(s, OP_iterator_check_object); + emit_op(s, OP_await); + label_next2 = emit_goto(s, OP_goto, -1); + emit_label(s, label_next); + emit_op(s, OP_drop); + emit_label(s, label_next2); + emit_op(s, OP_drop); + } else { + emit_op(s, OP_rot3r); + emit_op(s, OP_undefined); /* dummy catch offset */ + emit_op(s, OP_iterator_close); + } + } else { + /* execute the "finally" block */ + emit_goto(s, OP_gosub, top->label_finally); + } + } + top = top->prev; + } + if (s->cur_func->is_derived_class_constructor) { + int label_return; + + /* 'this' can be uninitialized, so it may be accessed only if + the derived class constructor does not return an object */ + if (hasval) { + emit_op(s, OP_check_ctor_return); + label_return = emit_goto(s, OP_if_false, -1); + emit_op(s, OP_drop); + } else { + label_return = -1; + } + + /* XXX: if this is not initialized, should throw the + ReferenceError in the caller realm */ + emit_op(s, OP_scope_get_var); + emit_atom(s, JS_ATOM_this); + emit_u16(s, 0); + + emit_label(s, label_return); + emit_op(s, OP_return); + } else if (s->cur_func->func_kind != JS_FUNC_NORMAL) { + emit_op(s, OP_return_async); + } else { + emit_op(s, hasval ? OP_return : OP_return_undef); + } +} + +#define DECL_MASK_FUNC (1 << 0) /* allow normal function declaration */ +/* ored with DECL_MASK_FUNC if function declarations are allowed with a label */ +#define DECL_MASK_FUNC_WITH_LABEL (1 << 1) +#define DECL_MASK_OTHER (1 << 2) /* all other declarations */ +#define DECL_MASK_ALL (DECL_MASK_FUNC | DECL_MASK_FUNC_WITH_LABEL | DECL_MASK_OTHER) + +static __exception int js_parse_statement_or_decl(JSParseState *s, + int decl_mask); + +static __exception int js_parse_statement(JSParseState *s) +{ + return js_parse_statement_or_decl(s, 0); +} + +static __exception int js_parse_block(JSParseState *s) +{ + if (js_parse_expect(s, '{')) + return -1; + if (s->token.val != '}') { + push_scope(s); + for(;;) { + if (js_parse_statement_or_decl(s, DECL_MASK_ALL)) + return -1; + if (s->token.val == '}') + break; + } + pop_scope(s); + } + if (next_token(s)) + return -1; + return 0; +} + +/* allowed parse_flags: PF_IN_ACCEPTED */ +static __exception int js_parse_var(JSParseState *s, int parse_flags, int tok, + BOOL export_flag) +{ + JSContext *ctx = s->ctx; + JSFunctionDef *fd = s->cur_func; + JSAtom name = JS_ATOM_NULL; + + for (;;) { + if (s->token.val == TOK_IDENT) { + if (s->token.u.ident.is_reserved) { + return js_parse_error_reserved_identifier(s); + } + name = JS_DupAtom(ctx, s->token.u.ident.atom); + if (name == JS_ATOM_let && (tok == TOK_LET || tok == TOK_CONST)) { + js_parse_error(s, "'let' is not a valid lexical identifier"); + goto var_error; + } + if (next_token(s)) + goto var_error; + if (js_define_var(s, name, tok)) + goto var_error; + if (export_flag) { + if (!add_export_entry(s, s->cur_func->module, name, name, + JS_EXPORT_TYPE_LOCAL)) + goto var_error; + } + + if (s->token.val == '=') { + if (next_token(s)) + goto var_error; + if (tok == TOK_VAR) { + /* Must make a reference for proper `with` semantics */ + int opcode, scope, label; + JSAtom name1; + + emit_op(s, OP_scope_get_var); + emit_atom(s, name); + emit_u16(s, fd->scope_level); + if (get_lvalue(s, &opcode, &scope, &name1, &label, NULL, FALSE, '=') < 0) + goto var_error; + if (js_parse_assign_expr2(s, parse_flags)) { + JS_FreeAtom(ctx, name1); + goto var_error; + } + set_object_name(s, name); + put_lvalue(s, opcode, scope, name1, label, + PUT_LVALUE_NOKEEP, FALSE); + } else { + if (js_parse_assign_expr2(s, parse_flags)) + goto var_error; + set_object_name(s, name); + emit_op(s, (tok == TOK_CONST || tok == TOK_LET) ? + OP_scope_put_var_init : OP_scope_put_var); + emit_atom(s, name); + emit_u16(s, fd->scope_level); + } + } else { + if (tok == TOK_CONST) { + js_parse_error(s, "missing initializer for const variable"); + goto var_error; + } + if (tok == TOK_LET) { + /* initialize lexical variable upon entering its scope */ + emit_op(s, OP_undefined); + emit_op(s, OP_scope_put_var_init); + emit_atom(s, name); + emit_u16(s, fd->scope_level); + } + } + JS_FreeAtom(ctx, name); + } else { + int skip_bits; + if ((s->token.val == '[' || s->token.val == '{') + && js_parse_skip_parens_token(s, &skip_bits, FALSE) == '=') { + emit_op(s, OP_undefined); + if (js_parse_destructuring_element(s, tok, 0, TRUE, skip_bits & SKIP_HAS_ELLIPSIS, TRUE) < 0) + return -1; + } else { + return js_parse_error(s, "variable name expected"); + } + } + if (s->token.val != ',') + break; + if (next_token(s)) + return -1; + } + return 0; + + var_error: + JS_FreeAtom(ctx, name); + return -1; +} + +/* test if the current token is a label. Use simplistic look-ahead scanner */ +static BOOL is_label(JSParseState *s) +{ + return (s->token.val == TOK_IDENT && !s->token.u.ident.is_reserved && + peek_token(s, FALSE) == ':'); +} + +/* test if the current token is a let keyword. Use simplistic look-ahead scanner */ +static int is_let(JSParseState *s, int decl_mask) +{ + int res = FALSE; + + if (token_is_pseudo_keyword(s, JS_ATOM_let)) { + JSParsePos pos; + js_parse_get_pos(s, &pos); + for (;;) { + if (next_token(s)) { + res = -1; + break; + } + if (s->token.val == '[') { + /* let [ is a syntax restriction: + it never introduces an ExpressionStatement */ + res = TRUE; + break; + } + if (s->token.val == '{' || + (s->token.val == TOK_IDENT && !s->token.u.ident.is_reserved) || + s->token.val == TOK_LET || + s->token.val == TOK_YIELD || + s->token.val == TOK_AWAIT) { + /* Check for possible ASI if not scanning for Declaration */ + /* XXX: should also check that `{` introduces a BindingPattern, + but Firefox does not and rejects eval("let=1;let\n{if(1)2;}") */ + if (s->last_line_num == s->token.line_num || (decl_mask & DECL_MASK_OTHER)) { + res = TRUE; + break; + } + break; + } + break; + } + if (js_parse_seek_token(s, &pos)) { + res = -1; + } + } + return res; +} + +/* XXX: handle IteratorClose when exiting the loop before the + enumeration is done */ +static __exception int js_parse_for_in_of(JSParseState *s, int label_name, + BOOL is_async) +{ + JSContext *ctx = s->ctx; + JSFunctionDef *fd = s->cur_func; + JSAtom var_name; + BOOL has_initializer, is_for_of, has_destructuring; + int tok, tok1, opcode, scope, block_scope_level; + int label_next, label_expr, label_cont, label_body, label_break; + int pos_next, pos_expr; + BlockEnv break_entry; + + has_initializer = FALSE; + has_destructuring = FALSE; + is_for_of = FALSE; + block_scope_level = fd->scope_level; + label_cont = new_label(s); + label_body = new_label(s); + label_break = new_label(s); + label_next = new_label(s); + + /* create scope for the lexical variables declared in the enumeration + expressions. XXX: Not completely correct because of weird capturing + semantics in `for (i of o) a.push(function(){return i})` */ + push_scope(s); + + /* local for_in scope starts here so individual elements + can be closed in statement. */ + push_break_entry(s->cur_func, &break_entry, + label_name, label_break, label_cont, 1); + break_entry.scope_level = block_scope_level; + + label_expr = emit_goto(s, OP_goto, -1); + + pos_next = s->cur_func->byte_code.size; + emit_label(s, label_next); + + tok = s->token.val; + switch (is_let(s, DECL_MASK_OTHER)) { + case TRUE: + tok = TOK_LET; + break; + case FALSE: + break; + default: + return -1; + } + if (tok == TOK_VAR || tok == TOK_LET || tok == TOK_CONST) { + if (next_token(s)) + return -1; + + if (!(s->token.val == TOK_IDENT && !s->token.u.ident.is_reserved)) { + if (s->token.val == '[' || s->token.val == '{') { + if (js_parse_destructuring_element(s, tok, 0, TRUE, -1, FALSE) < 0) + return -1; + has_destructuring = TRUE; + } else { + return js_parse_error(s, "variable name expected"); + } + var_name = JS_ATOM_NULL; + } else { + var_name = JS_DupAtom(ctx, s->token.u.ident.atom); + if (next_token(s)) { + JS_FreeAtom(s->ctx, var_name); + return -1; + } + if (js_define_var(s, var_name, tok)) { + JS_FreeAtom(s->ctx, var_name); + return -1; + } + emit_op(s, (tok == TOK_CONST || tok == TOK_LET) ? + OP_scope_put_var_init : OP_scope_put_var); + emit_atom(s, var_name); + emit_u16(s, fd->scope_level); + } + } else if (!is_async && token_is_pseudo_keyword(s, JS_ATOM_async) && peek_token(s, FALSE) == TOK_OF) { + return js_parse_error(s, "'for of' expression cannot start with 'async'"); + } else { + int skip_bits; + if ((s->token.val == '[' || s->token.val == '{') + && ((tok1 = js_parse_skip_parens_token(s, &skip_bits, FALSE)) == TOK_IN || tok1 == TOK_OF)) { + if (js_parse_destructuring_element(s, 0, 0, TRUE, skip_bits & SKIP_HAS_ELLIPSIS, TRUE) < 0) + return -1; + } else { + int lvalue_label; + if (js_parse_left_hand_side_expr(s)) + return -1; + if (get_lvalue(s, &opcode, &scope, &var_name, &lvalue_label, + NULL, FALSE, TOK_FOR)) + return -1; + put_lvalue(s, opcode, scope, var_name, lvalue_label, + PUT_LVALUE_NOKEEP_BOTTOM, FALSE); + } + var_name = JS_ATOM_NULL; + } + emit_goto(s, OP_goto, label_body); + + pos_expr = s->cur_func->byte_code.size; + emit_label(s, label_expr); + if (s->token.val == '=') { + /* XXX: potential scoping issue if inside `with` statement */ + has_initializer = TRUE; + /* parse and evaluate initializer prior to evaluating the + object (only used with "for in" with a non lexical variable + in non strict mode */ + if (next_token(s) || js_parse_assign_expr2(s, 0)) { + JS_FreeAtom(ctx, var_name); + return -1; + } + if (var_name != JS_ATOM_NULL) { + emit_op(s, OP_scope_put_var); + emit_atom(s, var_name); + emit_u16(s, fd->scope_level); + } + } + JS_FreeAtom(ctx, var_name); + + if (token_is_pseudo_keyword(s, JS_ATOM_of)) { + break_entry.has_iterator = is_for_of = TRUE; + break_entry.drop_count += 2; + if (has_initializer) + goto initializer_error; + } else if (s->token.val == TOK_IN) { + if (is_async) + return js_parse_error(s, "'for await' loop should be used with 'of'"); + if (has_initializer && + (tok != TOK_VAR || fd->is_strict_mode || has_destructuring)) { + initializer_error: + return js_parse_error(s, "a declaration in the head of a for-%s loop can't have an initializer", + is_for_of ? "of" : "in"); + } + } else { + return js_parse_error(s, "expected 'of' or 'in' in for control expression"); + } + if (next_token(s)) + return -1; + if (is_for_of) { + if (js_parse_assign_expr(s)) + return -1; + } else { + if (js_parse_expr(s)) + return -1; + } + /* close the scope after having evaluated the expression so that + the TDZ values are in the closures */ + close_scopes(s, s->cur_func->scope_level, block_scope_level); + if (is_for_of) { + if (is_async) + emit_op(s, OP_for_await_of_start); + else + emit_op(s, OP_for_of_start); + /* on stack: enum_rec */ + } else { + emit_op(s, OP_for_in_start); + /* on stack: enum_obj */ + } + emit_goto(s, OP_goto, label_cont); + + if (js_parse_expect(s, ')')) + return -1; + + { + /* move the `next` code here */ + DynBuf *bc = &s->cur_func->byte_code; + int chunk_size = pos_expr - pos_next; + int offset = bc->size - pos_next; + int i; + dbuf_realloc(bc, bc->size + chunk_size); + dbuf_put(bc, bc->buf + pos_next, chunk_size); + memset(bc->buf + pos_next, OP_nop, chunk_size); + /* `next` part ends with a goto */ + s->cur_func->last_opcode_pos = bc->size - 5; + /* relocate labels */ + for (i = label_cont; i < s->cur_func->label_count; i++) { + LabelSlot *ls = &s->cur_func->label_slots[i]; + if (ls->pos >= pos_next && ls->pos < pos_expr) + ls->pos += offset; + } + } + + emit_label(s, label_body); + if (js_parse_statement(s)) + return -1; + + close_scopes(s, s->cur_func->scope_level, block_scope_level); + + emit_label(s, label_cont); + if (is_for_of) { + if (is_async) { + /* call the next method */ + /* stack: iter_obj next catch_offset */ + emit_op(s, OP_dup3); + emit_op(s, OP_drop); + emit_op(s, OP_call_method); + emit_u16(s, 0); + /* get the result of the promise */ + emit_op(s, OP_await); + /* unwrap the value and done values */ + emit_op(s, OP_iterator_get_value_done); + } else { + emit_op(s, OP_for_of_next); + emit_u8(s, 0); + } + } else { + emit_op(s, OP_for_in_next); + } + /* on stack: enum_rec / enum_obj value bool */ + emit_goto(s, OP_if_false, label_next); + /* drop the undefined value from for_xx_next */ + emit_op(s, OP_drop); + + emit_label(s, label_break); + if (is_for_of) { + /* close and drop enum_rec */ + emit_op(s, OP_iterator_close); + } else { + emit_op(s, OP_drop); + } + pop_break_entry(s->cur_func); + pop_scope(s); + return 0; +} + +static void set_eval_ret_undefined(JSParseState *s) +{ + if (s->cur_func->eval_ret_idx >= 0) { + emit_op(s, OP_undefined); + emit_op(s, OP_put_loc); + emit_u16(s, s->cur_func->eval_ret_idx); + } +} + +static __exception int js_parse_statement_or_decl(JSParseState *s, + int decl_mask) +{ + JSContext *ctx = s->ctx; + JSAtom label_name; + int tok; + + /* specific label handling */ + /* XXX: support multiple labels on loop statements */ + label_name = JS_ATOM_NULL; + if (is_label(s)) { + BlockEnv *be; + + label_name = JS_DupAtom(ctx, s->token.u.ident.atom); + + for (be = s->cur_func->top_break; be; be = be->prev) { + if (be->label_name == label_name) { + js_parse_error(s, "duplicate label name"); + goto fail; + } + } + + if (next_token(s)) + goto fail; + if (js_parse_expect(s, ':')) + goto fail; + if (s->token.val != TOK_FOR + && s->token.val != TOK_DO + && s->token.val != TOK_WHILE) { + /* labelled regular statement */ + int label_break, mask; + BlockEnv break_entry; + + label_break = new_label(s); + push_break_entry(s->cur_func, &break_entry, + label_name, label_break, -1, 0); + if (!s->cur_func->is_strict_mode && + (decl_mask & DECL_MASK_FUNC_WITH_LABEL)) { + mask = DECL_MASK_FUNC | DECL_MASK_FUNC_WITH_LABEL; + } else { + mask = 0; + } + if (js_parse_statement_or_decl(s, mask)) + goto fail; + emit_label(s, label_break); + pop_break_entry(s->cur_func); + goto done; + } + } + + switch(tok = s->token.val) { + case '{': + if (js_parse_block(s)) + goto fail; + break; + case TOK_RETURN: + if (s->cur_func->is_eval) { + js_parse_error(s, "return not in a function"); + goto fail; + } + if (s->cur_func->func_type == JS_PARSE_FUNC_CLASS_STATIC_INIT) { + js_parse_error(s, "return in a static initializer block"); + goto fail; + } + if (next_token(s)) + goto fail; + if (s->token.val != ';' && s->token.val != '}' && !s->got_lf) { + if (js_parse_expr(s)) + goto fail; + emit_return(s, TRUE); + } else { + emit_return(s, FALSE); + } + if (js_parse_expect_semi(s)) + goto fail; + break; + case TOK_THROW: + if (next_token(s)) + goto fail; + if (s->got_lf) { + js_parse_error(s, "line terminator not allowed after throw"); + goto fail; + } + if (js_parse_expr(s)) + goto fail; + emit_op(s, OP_throw); + if (js_parse_expect_semi(s)) + goto fail; + break; + case TOK_LET: + case TOK_CONST: + haslet: + if (!(decl_mask & DECL_MASK_OTHER)) { + js_parse_error(s, "lexical declarations can't appear in single-statement context"); + goto fail; + } + /* fall thru */ + case TOK_VAR: + if (next_token(s)) + goto fail; + if (js_parse_var(s, TRUE, tok, FALSE)) + goto fail; + if (js_parse_expect_semi(s)) + goto fail; + break; + case TOK_IF: + { + int label1, label2, mask; + if (next_token(s)) + goto fail; + /* create a new scope for `let f;if(1) function f(){}` */ + push_scope(s); + set_eval_ret_undefined(s); + if (js_parse_expr_paren(s)) + goto fail; + label1 = emit_goto(s, OP_if_false, -1); + if (s->cur_func->is_strict_mode) + mask = 0; + else + mask = DECL_MASK_FUNC; /* Annex B.3.4 */ + + if (js_parse_statement_or_decl(s, mask)) + goto fail; + + if (s->token.val == TOK_ELSE) { + label2 = emit_goto(s, OP_goto, -1); + if (next_token(s)) + goto fail; + + emit_label(s, label1); + if (js_parse_statement_or_decl(s, mask)) + goto fail; + + label1 = label2; + } + emit_label(s, label1); + pop_scope(s); + } + break; + case TOK_WHILE: + { + int label_cont, label_break; + BlockEnv break_entry; + + label_cont = new_label(s); + label_break = new_label(s); + + push_break_entry(s->cur_func, &break_entry, + label_name, label_break, label_cont, 0); + + if (next_token(s)) + goto fail; + + set_eval_ret_undefined(s); + + emit_label(s, label_cont); + if (js_parse_expr_paren(s)) + goto fail; + emit_goto(s, OP_if_false, label_break); + + if (js_parse_statement(s)) + goto fail; + emit_goto(s, OP_goto, label_cont); + + emit_label(s, label_break); + + pop_break_entry(s->cur_func); + } + break; + case TOK_DO: + { + int label_cont, label_break, label1; + BlockEnv break_entry; + + label_cont = new_label(s); + label_break = new_label(s); + label1 = new_label(s); + + push_break_entry(s->cur_func, &break_entry, + label_name, label_break, label_cont, 0); + + if (next_token(s)) + goto fail; + + emit_label(s, label1); + + set_eval_ret_undefined(s); + + if (js_parse_statement(s)) + goto fail; + + emit_label(s, label_cont); + if (js_parse_expect(s, TOK_WHILE)) + goto fail; + if (js_parse_expr_paren(s)) + goto fail; + /* Insert semicolon if missing */ + if (s->token.val == ';') { + if (next_token(s)) + goto fail; + } + emit_goto(s, OP_if_true, label1); + + emit_label(s, label_break); + + pop_break_entry(s->cur_func); + } + break; + case TOK_FOR: + { + int label_cont, label_break, label_body, label_test; + int pos_cont, pos_body, block_scope_level; + BlockEnv break_entry; + int tok, bits; + BOOL is_async; + + if (next_token(s)) + goto fail; + + set_eval_ret_undefined(s); + bits = 0; + is_async = FALSE; + if (s->token.val == '(') { + js_parse_skip_parens_token(s, &bits, FALSE); + } else if (s->token.val == TOK_AWAIT) { + if (!(s->cur_func->func_kind & JS_FUNC_ASYNC)) { + js_parse_error(s, "for await is only valid in asynchronous functions"); + goto fail; + } + is_async = TRUE; + if (next_token(s)) + goto fail; + s->cur_func->has_await = TRUE; + } + if (js_parse_expect(s, '(')) + goto fail; + + if (!(bits & SKIP_HAS_SEMI)) { + /* parse for/in or for/of */ + if (js_parse_for_in_of(s, label_name, is_async)) + goto fail; + break; + } + block_scope_level = s->cur_func->scope_level; + + /* create scope for the lexical variables declared in the initial, + test and increment expressions */ + push_scope(s); + /* initial expression */ + tok = s->token.val; + if (tok != ';') { + switch (is_let(s, DECL_MASK_OTHER)) { + case TRUE: + tok = TOK_LET; + break; + case FALSE: + break; + default: + goto fail; + } + if (tok == TOK_VAR || tok == TOK_LET || tok == TOK_CONST) { + if (next_token(s)) + goto fail; + if (js_parse_var(s, FALSE, tok, FALSE)) + goto fail; + } else { + if (js_parse_expr2(s, FALSE)) + goto fail; + emit_op(s, OP_drop); + } + + /* close the closures before the first iteration */ + close_scopes(s, s->cur_func->scope_level, block_scope_level); + } + if (js_parse_expect(s, ';')) + goto fail; + + label_test = new_label(s); + label_cont = new_label(s); + label_body = new_label(s); + label_break = new_label(s); + + push_break_entry(s->cur_func, &break_entry, + label_name, label_break, label_cont, 0); + + /* test expression */ + if (s->token.val == ';') { + /* no test expression */ + label_test = label_body; + } else { + emit_label(s, label_test); + if (js_parse_expr(s)) + goto fail; + emit_goto(s, OP_if_false, label_break); + } + if (js_parse_expect(s, ';')) + goto fail; + + if (s->token.val == ')') { + /* no end expression */ + break_entry.label_cont = label_cont = label_test; + pos_cont = 0; /* avoid warning */ + } else { + /* skip the end expression */ + emit_goto(s, OP_goto, label_body); + + pos_cont = s->cur_func->byte_code.size; + emit_label(s, label_cont); + if (js_parse_expr(s)) + goto fail; + emit_op(s, OP_drop); + if (label_test != label_body) + emit_goto(s, OP_goto, label_test); + } + if (js_parse_expect(s, ')')) + goto fail; + + pos_body = s->cur_func->byte_code.size; + emit_label(s, label_body); + if (js_parse_statement(s)) + goto fail; + + /* close the closures before the next iteration */ + /* XXX: check continue case */ + close_scopes(s, s->cur_func->scope_level, block_scope_level); + + if (label_test != label_body && label_cont != label_test) { + /* move the increment code here */ + DynBuf *bc = &s->cur_func->byte_code; + int chunk_size = pos_body - pos_cont; + int offset = bc->size - pos_cont; + int i; + dbuf_realloc(bc, bc->size + chunk_size); + dbuf_put(bc, bc->buf + pos_cont, chunk_size); + memset(bc->buf + pos_cont, OP_nop, chunk_size); + /* increment part ends with a goto */ + s->cur_func->last_opcode_pos = bc->size - 5; + /* relocate labels */ + for (i = label_cont; i < s->cur_func->label_count; i++) { + LabelSlot *ls = &s->cur_func->label_slots[i]; + if (ls->pos >= pos_cont && ls->pos < pos_body) + ls->pos += offset; + } + } else { + emit_goto(s, OP_goto, label_cont); + } + + emit_label(s, label_break); + + pop_break_entry(s->cur_func); + pop_scope(s); + } + break; + case TOK_BREAK: + case TOK_CONTINUE: + { + int is_cont = s->token.val - TOK_BREAK; + int label; + + if (next_token(s)) + goto fail; + if (!s->got_lf && s->token.val == TOK_IDENT && !s->token.u.ident.is_reserved) + label = s->token.u.ident.atom; + else + label = JS_ATOM_NULL; + if (emit_break(s, label, is_cont)) + goto fail; + if (label != JS_ATOM_NULL) { + if (next_token(s)) + goto fail; + } + if (js_parse_expect_semi(s)) + goto fail; + } + break; + case TOK_SWITCH: + { + int label_case, label_break, label1; + int default_label_pos; + BlockEnv break_entry; + + if (next_token(s)) + goto fail; + + set_eval_ret_undefined(s); + if (js_parse_expr_paren(s)) + goto fail; + + push_scope(s); + label_break = new_label(s); + push_break_entry(s->cur_func, &break_entry, + label_name, label_break, -1, 1); + + if (js_parse_expect(s, '{')) + goto fail; + + default_label_pos = -1; + label_case = -1; + while (s->token.val != '}') { + if (s->token.val == TOK_CASE) { + label1 = -1; + if (label_case >= 0) { + /* skip the case if needed */ + label1 = emit_goto(s, OP_goto, -1); + } + emit_label(s, label_case); + label_case = -1; + for (;;) { + /* parse a sequence of case clauses */ + if (next_token(s)) + goto fail; + emit_op(s, OP_dup); + if (js_parse_expr(s)) + goto fail; + if (js_parse_expect(s, ':')) + goto fail; + emit_op(s, OP_strict_eq); + if (s->token.val == TOK_CASE) { + label1 = emit_goto(s, OP_if_true, label1); + } else { + label_case = emit_goto(s, OP_if_false, -1); + emit_label(s, label1); + break; + } + } + } else if (s->token.val == TOK_DEFAULT) { + if (next_token(s)) + goto fail; + if (js_parse_expect(s, ':')) + goto fail; + if (default_label_pos >= 0) { + js_parse_error(s, "duplicate default"); + goto fail; + } + if (label_case < 0) { + /* falling thru direct from switch expression */ + label_case = emit_goto(s, OP_goto, -1); + } + /* Emit a dummy label opcode. Label will be patched after + the end of the switch body. Do not use emit_label(s, 0) + because it would clobber label 0 address, preventing + proper optimizer operation. + */ + emit_op(s, OP_label); + emit_u32(s, 0); + default_label_pos = s->cur_func->byte_code.size - 4; + } else { + if (label_case < 0) { + /* falling thru direct from switch expression */ + js_parse_error(s, "invalid switch statement"); + goto fail; + } + if (js_parse_statement_or_decl(s, DECL_MASK_ALL)) + goto fail; + } + } + if (js_parse_expect(s, '}')) + goto fail; + if (default_label_pos >= 0) { + /* Ugly patch for the the `default` label, shameful and risky */ + put_u32(s->cur_func->byte_code.buf + default_label_pos, + label_case); + s->cur_func->label_slots[label_case].pos = default_label_pos + 4; + } else { + emit_label(s, label_case); + } + emit_label(s, label_break); + emit_op(s, OP_drop); /* drop the switch expression */ + + pop_break_entry(s->cur_func); + pop_scope(s); + } + break; + case TOK_TRY: + { + int label_catch, label_catch2, label_finally, label_end; + JSAtom name; + BlockEnv block_env; + + set_eval_ret_undefined(s); + if (next_token(s)) + goto fail; + label_catch = new_label(s); + label_catch2 = new_label(s); + label_finally = new_label(s); + label_end = new_label(s); + + emit_goto(s, OP_catch, label_catch); + + push_break_entry(s->cur_func, &block_env, + JS_ATOM_NULL, -1, -1, 1); + block_env.label_finally = label_finally; + + if (js_parse_block(s)) + goto fail; + + pop_break_entry(s->cur_func); + + if (js_is_live_code(s)) { + /* drop the catch offset */ + emit_op(s, OP_drop); + /* must push dummy value to keep same stack size */ + emit_op(s, OP_undefined); + emit_goto(s, OP_gosub, label_finally); + emit_op(s, OP_drop); + + emit_goto(s, OP_goto, label_end); + } + + if (s->token.val == TOK_CATCH) { + if (next_token(s)) + goto fail; + + push_scope(s); /* catch variable */ + emit_label(s, label_catch); + + if (s->token.val == '{') { + /* support optional-catch-binding feature */ + emit_op(s, OP_drop); /* pop the exception object */ + } else { + if (js_parse_expect(s, '(')) + goto fail; + if (!(s->token.val == TOK_IDENT && !s->token.u.ident.is_reserved)) { + if (s->token.val == '[' || s->token.val == '{') { + /* XXX: TOK_LET is not completely correct */ + if (js_parse_destructuring_element(s, TOK_LET, 0, TRUE, -1, TRUE) < 0) + goto fail; + } else { + js_parse_error(s, "identifier expected"); + goto fail; + } + } else { + name = JS_DupAtom(ctx, s->token.u.ident.atom); + if (next_token(s) + || js_define_var(s, name, TOK_CATCH) < 0) { + JS_FreeAtom(ctx, name); + goto fail; + } + /* store the exception value in the catch variable */ + emit_op(s, OP_scope_put_var); + emit_u32(s, name); + emit_u16(s, s->cur_func->scope_level); + } + if (js_parse_expect(s, ')')) + goto fail; + } + /* XXX: should keep the address to nop it out if there is no finally block */ + emit_goto(s, OP_catch, label_catch2); + + push_scope(s); /* catch block */ + push_break_entry(s->cur_func, &block_env, JS_ATOM_NULL, + -1, -1, 1); + block_env.label_finally = label_finally; + + if (js_parse_block(s)) + goto fail; + + pop_break_entry(s->cur_func); + pop_scope(s); /* catch block */ + pop_scope(s); /* catch variable */ + + if (js_is_live_code(s)) { + /* drop the catch2 offset */ + emit_op(s, OP_drop); + /* XXX: should keep the address to nop it out if there is no finally block */ + /* must push dummy value to keep same stack size */ + emit_op(s, OP_undefined); + emit_goto(s, OP_gosub, label_finally); + emit_op(s, OP_drop); + emit_goto(s, OP_goto, label_end); + } + /* catch exceptions thrown in the catch block to execute the + * finally clause and rethrow the exception */ + emit_label(s, label_catch2); + /* catch value is at TOS, no need to push undefined */ + emit_goto(s, OP_gosub, label_finally); + emit_op(s, OP_throw); + + } else if (s->token.val == TOK_FINALLY) { + /* finally without catch : execute the finally clause + * and rethrow the exception */ + emit_label(s, label_catch); + /* catch value is at TOS, no need to push undefined */ + emit_goto(s, OP_gosub, label_finally); + emit_op(s, OP_throw); + } else { + js_parse_error(s, "expecting catch or finally"); + goto fail; + } + emit_label(s, label_finally); + if (s->token.val == TOK_FINALLY) { + int saved_eval_ret_idx = 0; /* avoid warning */ + + if (next_token(s)) + goto fail; + /* on the stack: ret_value gosub_ret_value */ + push_break_entry(s->cur_func, &block_env, JS_ATOM_NULL, + -1, -1, 2); + + if (s->cur_func->eval_ret_idx >= 0) { + /* 'finally' updates eval_ret only if not a normal + termination */ + saved_eval_ret_idx = + add_var(s->ctx, s->cur_func, JS_ATOM__ret_); + if (saved_eval_ret_idx < 0) + goto fail; + emit_op(s, OP_get_loc); + emit_u16(s, s->cur_func->eval_ret_idx); + emit_op(s, OP_put_loc); + emit_u16(s, saved_eval_ret_idx); + set_eval_ret_undefined(s); + } + + if (js_parse_block(s)) + goto fail; + + if (s->cur_func->eval_ret_idx >= 0) { + emit_op(s, OP_get_loc); + emit_u16(s, saved_eval_ret_idx); + emit_op(s, OP_put_loc); + emit_u16(s, s->cur_func->eval_ret_idx); + } + pop_break_entry(s->cur_func); + } + emit_op(s, OP_ret); + emit_label(s, label_end); + } + break; + case ';': + /* empty statement */ + if (next_token(s)) + goto fail; + break; + case TOK_WITH: + if (s->cur_func->is_strict_mode) { + js_parse_error(s, "invalid keyword: with"); + goto fail; + } else { + int with_idx; + + if (next_token(s)) + goto fail; + + if (js_parse_expr_paren(s)) + goto fail; + + push_scope(s); + with_idx = define_var(s, s->cur_func, JS_ATOM__with_, + JS_VAR_DEF_WITH); + if (with_idx < 0) + goto fail; + emit_op(s, OP_to_object); + emit_op(s, OP_put_loc); + emit_u16(s, with_idx); + + set_eval_ret_undefined(s); + if (js_parse_statement(s)) + goto fail; + + /* Popping scope drops lexical context for the with object variable */ + pop_scope(s); + } + break; + case TOK_FUNCTION: + /* ES6 Annex B.3.2 and B.3.3 semantics */ + if (!(decl_mask & DECL_MASK_FUNC)) + goto func_decl_error; + if (!(decl_mask & DECL_MASK_OTHER) && peek_token(s, FALSE) == '*') + goto func_decl_error; + goto parse_func_var; + case TOK_IDENT: + if (s->token.u.ident.is_reserved) { + js_parse_error_reserved_identifier(s); + goto fail; + } + /* Determine if `let` introduces a Declaration or an ExpressionStatement */ + switch (is_let(s, decl_mask)) { + case TRUE: + tok = TOK_LET; + goto haslet; + case FALSE: + break; + default: + goto fail; + } + if (token_is_pseudo_keyword(s, JS_ATOM_async) && + peek_token(s, TRUE) == TOK_FUNCTION) { + if (!(decl_mask & DECL_MASK_OTHER)) { + func_decl_error: + js_parse_error(s, "function declarations can't appear in single-statement context"); + goto fail; + } + parse_func_var: + if (js_parse_function_decl(s, JS_PARSE_FUNC_VAR, + JS_FUNC_NORMAL, JS_ATOM_NULL, + s->token.ptr, + s->token.line_num, + s->token.col_num)) + goto fail; + break; + } + goto hasexpr; + + case TOK_CLASS: + if (!(decl_mask & DECL_MASK_OTHER)) { + js_parse_error(s, "class declarations can't appear in single-statement context"); + goto fail; + } + if (js_parse_class(s, FALSE, JS_PARSE_EXPORT_NONE)) + return -1; + break; + + case TOK_DEBUGGER: + /* currently no debugger, so just skip the keyword */ + if (next_token(s)) + goto fail; + if (js_parse_expect_semi(s)) + goto fail; + break; + + case TOK_ENUM: + case TOK_EXPORT: + case TOK_EXTENDS: + js_unsupported_keyword(s, s->token.u.ident.atom); + goto fail; + + default: + hasexpr: + emit_source_loc(s); + if (js_parse_expr(s)) + goto fail; + if (s->cur_func->eval_ret_idx >= 0) { + /* store the expression value so that it can be returned + by eval() */ + emit_op(s, OP_put_loc); + emit_u16(s, s->cur_func->eval_ret_idx); + } else { + emit_op(s, OP_drop); /* drop the result */ + } + if (js_parse_expect_semi(s)) + goto fail; + break; + } +done: + JS_FreeAtom(ctx, label_name); + return 0; +fail: + JS_FreeAtom(ctx, label_name); + return -1; +} + +/* 'name' is freed */ +static JSModuleDef *js_new_module_def(JSContext *ctx, JSAtom name) +{ + JSModuleDef *m; + m = js_mallocz(ctx, sizeof(*m)); + if (!m) { + JS_FreeAtom(ctx, name); + return NULL; + } + m->header.ref_count = 1; + m->module_name = name; + m->module_ns = JS_UNDEFINED; + m->func_obj = JS_UNDEFINED; + m->eval_exception = JS_UNDEFINED; + m->meta_obj = JS_UNDEFINED; + m->promise = JS_UNDEFINED; + m->resolving_funcs[0] = JS_UNDEFINED; + m->resolving_funcs[1] = JS_UNDEFINED; + list_add_tail(&m->link, &ctx->loaded_modules); + return m; +} + +static void js_mark_module_def(JSRuntime *rt, JSModuleDef *m, + JS_MarkFunc *mark_func) +{ + int i; + + for(i = 0; i < m->export_entries_count; i++) { + JSExportEntry *me = &m->export_entries[i]; + if (me->export_type == JS_EXPORT_TYPE_LOCAL && + me->u.local.var_ref) { + mark_func(rt, &me->u.local.var_ref->header); + } + } + + JS_MarkValue(rt, m->module_ns, mark_func); + JS_MarkValue(rt, m->func_obj, mark_func); + JS_MarkValue(rt, m->eval_exception, mark_func); + JS_MarkValue(rt, m->meta_obj, mark_func); + JS_MarkValue(rt, m->promise, mark_func); + JS_MarkValue(rt, m->resolving_funcs[0], mark_func); + JS_MarkValue(rt, m->resolving_funcs[1], mark_func); +} + +static void js_free_module_def(JSContext *ctx, JSModuleDef *m) +{ + int i; + + JS_FreeAtom(ctx, m->module_name); + + for(i = 0; i < m->req_module_entries_count; i++) { + JSReqModuleEntry *rme = &m->req_module_entries[i]; + JS_FreeAtom(ctx, rme->module_name); + } + js_free(ctx, m->req_module_entries); + + for(i = 0; i < m->export_entries_count; i++) { + JSExportEntry *me = &m->export_entries[i]; + if (me->export_type == JS_EXPORT_TYPE_LOCAL) + free_var_ref(ctx->rt, me->u.local.var_ref); + JS_FreeAtom(ctx, me->export_name); + JS_FreeAtom(ctx, me->local_name); + } + js_free(ctx, m->export_entries); + + js_free(ctx, m->star_export_entries); + + for(i = 0; i < m->import_entries_count; i++) { + JSImportEntry *mi = &m->import_entries[i]; + JS_FreeAtom(ctx, mi->import_name); + } + js_free(ctx, m->import_entries); + js_free(ctx, m->async_parent_modules); + + JS_FreeValue(ctx, m->module_ns); + JS_FreeValue(ctx, m->func_obj); + JS_FreeValue(ctx, m->eval_exception); + JS_FreeValue(ctx, m->meta_obj); + JS_FreeValue(ctx, m->promise); + JS_FreeValue(ctx, m->resolving_funcs[0]); + JS_FreeValue(ctx, m->resolving_funcs[1]); + list_del(&m->link); + js_free(ctx, m); +} + +static int add_req_module_entry(JSContext *ctx, JSModuleDef *m, + JSAtom module_name) +{ + JSReqModuleEntry *rme; + int i; + + /* no need to add the module request if it is already present */ + for(i = 0; i < m->req_module_entries_count; i++) { + rme = &m->req_module_entries[i]; + if (rme->module_name == module_name) + return i; + } + + if (js_resize_array(ctx, (void **)&m->req_module_entries, + sizeof(JSReqModuleEntry), + &m->req_module_entries_size, + m->req_module_entries_count + 1)) + return -1; + rme = &m->req_module_entries[m->req_module_entries_count++]; + rme->module_name = JS_DupAtom(ctx, module_name); + rme->module = NULL; + return i; +} + +static JSExportEntry *find_export_entry(JSContext *ctx, const JSModuleDef *m, + JSAtom export_name) +{ + JSExportEntry *me; + int i; + for(i = 0; i < m->export_entries_count; i++) { + me = &m->export_entries[i]; + if (me->export_name == export_name) + return me; + } + return NULL; +} + +static JSExportEntry *add_export_entry2(JSContext *ctx, + JSParseState *s, JSModuleDef *m, + JSAtom local_name, JSAtom export_name, + JSExportTypeEnum export_type) +{ + JSExportEntry *me; + + if (find_export_entry(ctx, m, export_name)) { + char buf1[ATOM_GET_STR_BUF_SIZE]; + if (s) { + js_parse_error(s, "duplicate exported name '%s'", + JS_AtomGetStr(ctx, buf1, sizeof(buf1), export_name)); + } else { + JS_ThrowSyntaxErrorAtom(ctx, "duplicate exported name '%s'", export_name); + } + return NULL; + } + + if (js_resize_array(ctx, (void **)&m->export_entries, + sizeof(JSExportEntry), + &m->export_entries_size, + m->export_entries_count + 1)) + return NULL; + me = &m->export_entries[m->export_entries_count++]; + memset(me, 0, sizeof(*me)); + me->local_name = JS_DupAtom(ctx, local_name); + me->export_name = JS_DupAtom(ctx, export_name); + me->export_type = export_type; + return me; +} + +static JSExportEntry *add_export_entry(JSParseState *s, JSModuleDef *m, + JSAtom local_name, JSAtom export_name, + JSExportTypeEnum export_type) +{ + return add_export_entry2(s->ctx, s, m, local_name, export_name, + export_type); +} + +static int add_star_export_entry(JSContext *ctx, JSModuleDef *m, + int req_module_idx) +{ + JSStarExportEntry *se; + + if (js_resize_array(ctx, (void **)&m->star_export_entries, + sizeof(JSStarExportEntry), + &m->star_export_entries_size, + m->star_export_entries_count + 1)) + return -1; + se = &m->star_export_entries[m->star_export_entries_count++]; + se->req_module_idx = req_module_idx; + return 0; +} + +/* create a C module */ +/* `name_str` may be pure ASCII or UTF-8 encoded */ +JSModuleDef *JS_NewCModule(JSContext *ctx, const char *name_str, + JSModuleInitFunc *func) +{ + JSModuleDef *m; + JSAtom name; + name = JS_NewAtom(ctx, name_str); + if (name == JS_ATOM_NULL) + return NULL; + m = js_new_module_def(ctx, name); + m->init_func = func; + return m; +} + +/* `export_name` may be pure ASCII or UTF-8 encoded */ +int JS_AddModuleExport(JSContext *ctx, JSModuleDef *m, const char *export_name) +{ + JSExportEntry *me; + JSAtom name; + name = JS_NewAtom(ctx, export_name); + if (name == JS_ATOM_NULL) + return -1; + me = add_export_entry2(ctx, NULL, m, JS_ATOM_NULL, name, + JS_EXPORT_TYPE_LOCAL); + JS_FreeAtom(ctx, name); + if (!me) + return -1; + else + return 0; +} + +/* `export_name` may be pure ASCII or UTF-8 encoded */ +int JS_SetModuleExport(JSContext *ctx, JSModuleDef *m, const char *export_name, + JSValue val) +{ + JSExportEntry *me; + JSAtom name; + name = JS_NewAtom(ctx, export_name); + if (name == JS_ATOM_NULL) + goto fail; + me = find_export_entry(ctx, m, name); + JS_FreeAtom(ctx, name); + if (!me) + goto fail; + set_value(ctx, me->u.local.var_ref->pvalue, val); + return 0; + fail: + JS_FreeValue(ctx, val); + return -1; +} + +void JS_SetModuleLoaderFunc(JSRuntime *rt, + JSModuleNormalizeFunc *module_normalize, + JSModuleLoaderFunc *module_loader, void *opaque) +{ + rt->module_normalize_func = module_normalize; + rt->module_loader_func = module_loader; + rt->module_loader_opaque = opaque; +} + +/* default module filename normalizer */ +static char *js_default_module_normalize_name(JSContext *ctx, + const char *base_name, + const char *name) +{ + char *filename, *p; + const char *r; + int cap; + int len; + + if (name[0] != '.') { + /* if no initial dot, the module name is not modified */ + return js_strdup(ctx, name); + } + + p = strrchr(base_name, '/'); + if (p) + len = p - base_name; + else + len = 0; + + cap = len + strlen(name) + 1 + 1; + filename = js_malloc(ctx, cap); + if (!filename) + return NULL; + memcpy(filename, base_name, len); + filename[len] = '\0'; + + /* we only normalize the leading '..' or '.' */ + r = name; + for(;;) { + if (r[0] == '.' && r[1] == '/') { + r += 2; + } else if (r[0] == '.' && r[1] == '.' && r[2] == '/') { + /* remove the last path element of filename, except if "." + or ".." */ + if (filename[0] == '\0') + break; + p = strrchr(filename, '/'); + if (!p) + p = filename; + else + p++; + if (!strcmp(p, ".") || !strcmp(p, "..")) + break; + if (p > filename) + p--; + *p = '\0'; + r += 3; + } else { + break; + } + } + if (filename[0] != '\0') + pstrcat(filename, cap, "/"); + pstrcat(filename, cap, r); + // printf("normalize: %s %s -> %s\n", base_name, name, filename); + return filename; +} + +static JSModuleDef *js_find_loaded_module(JSContext *ctx, JSAtom name) +{ + struct list_head *el; + JSModuleDef *m; + + /* first look at the loaded modules */ + list_for_each(el, &ctx->loaded_modules) { + m = list_entry(el, JSModuleDef, link); + if (m->module_name == name) + return m; + } + return NULL; +} + +/* return NULL in case of exception (e.g. module could not be loaded) */ +/* `base_cname` and `cname1` may be pure ASCII or UTF-8 encoded */ +static JSModuleDef *js_host_resolve_imported_module(JSContext *ctx, + const char *base_cname, + const char *cname1) +{ + JSRuntime *rt = ctx->rt; + JSModuleDef *m; + char *cname; + JSAtom module_name; + + if (!rt->module_normalize_func) { + cname = js_default_module_normalize_name(ctx, base_cname, cname1); + } else { + cname = rt->module_normalize_func(ctx, base_cname, cname1, + rt->module_loader_opaque); + } + if (!cname) + return NULL; + + module_name = JS_NewAtom(ctx, cname); + if (module_name == JS_ATOM_NULL) { + js_free(ctx, cname); + return NULL; + } + + /* first look at the loaded modules */ + m = js_find_loaded_module(ctx, module_name); + if (m) { + js_free(ctx, cname); + JS_FreeAtom(ctx, module_name); + return m; + } + + JS_FreeAtom(ctx, module_name); + + /* load the module */ + if (!rt->module_loader_func) { + /* XXX: use a syntax error ? */ + // XXX: update JS_DetectModule when you change this + JS_ThrowReferenceError(ctx, "could not load module '%s'", + cname); + js_free(ctx, cname); + return NULL; + } + + m = rt->module_loader_func(ctx, cname, rt->module_loader_opaque); + js_free(ctx, cname); + return m; +} + +static JSModuleDef *js_host_resolve_imported_module_atom(JSContext *ctx, + JSAtom base_module_name, + JSAtom module_name1) +{ + const char *base_cname, *cname; + JSModuleDef *m; + + base_cname = JS_AtomToCString(ctx, base_module_name); + if (!base_cname) + return NULL; + cname = JS_AtomToCString(ctx, module_name1); + if (!cname) { + JS_FreeCString(ctx, base_cname); + return NULL; + } + m = js_host_resolve_imported_module(ctx, base_cname, cname); + JS_FreeCString(ctx, base_cname); + JS_FreeCString(ctx, cname); + return m; +} + +typedef struct JSResolveEntry { + JSModuleDef *module; + JSAtom name; +} JSResolveEntry; + +typedef struct JSResolveState { + JSResolveEntry *array; + int size; + int count; +} JSResolveState; + +static int find_resolve_entry(JSResolveState *s, + JSModuleDef *m, JSAtom name) +{ + int i; + for(i = 0; i < s->count; i++) { + JSResolveEntry *re = &s->array[i]; + if (re->module == m && re->name == name) + return i; + } + return -1; +} + +static int add_resolve_entry(JSContext *ctx, JSResolveState *s, + JSModuleDef *m, JSAtom name) +{ + JSResolveEntry *re; + + if (js_resize_array(ctx, (void **)&s->array, + sizeof(JSResolveEntry), + &s->size, s->count + 1)) + return -1; + re = &s->array[s->count++]; + re->module = m; + re->name = JS_DupAtom(ctx, name); + return 0; +} + +typedef enum JSResolveResultEnum { + JS_RESOLVE_RES_EXCEPTION = -1, /* memory alloc error */ + JS_RESOLVE_RES_FOUND = 0, + JS_RESOLVE_RES_NOT_FOUND, + JS_RESOLVE_RES_CIRCULAR, + JS_RESOLVE_RES_AMBIGUOUS, +} JSResolveResultEnum; + +static JSResolveResultEnum js_resolve_export1(JSContext *ctx, + JSModuleDef **pmodule, + JSExportEntry **pme, + JSModuleDef *m, + JSAtom export_name, + JSResolveState *s) +{ + JSExportEntry *me; + + *pmodule = NULL; + *pme = NULL; + if (find_resolve_entry(s, m, export_name) >= 0) + return JS_RESOLVE_RES_CIRCULAR; + if (add_resolve_entry(ctx, s, m, export_name) < 0) + return JS_RESOLVE_RES_EXCEPTION; + me = find_export_entry(ctx, m, export_name); + if (me) { + if (me->export_type == JS_EXPORT_TYPE_LOCAL) { + /* local export */ + *pmodule = m; + *pme = me; + return JS_RESOLVE_RES_FOUND; + } else { + /* indirect export */ + JSModuleDef *m1; + m1 = m->req_module_entries[me->u.req_module_idx].module; + if (me->local_name == JS_ATOM__star_) { + /* export ns from */ + *pmodule = m; + *pme = me; + return JS_RESOLVE_RES_FOUND; + } else { + return js_resolve_export1(ctx, pmodule, pme, m1, + me->local_name, s); + } + } + } else { + if (export_name != JS_ATOM_default) { + /* not found in direct or indirect exports: try star exports */ + int i; + + for(i = 0; i < m->star_export_entries_count; i++) { + JSStarExportEntry *se = &m->star_export_entries[i]; + JSModuleDef *m1, *res_m; + JSExportEntry *res_me; + JSResolveResultEnum ret; + + m1 = m->req_module_entries[se->req_module_idx].module; + ret = js_resolve_export1(ctx, &res_m, &res_me, m1, + export_name, s); + if (ret == JS_RESOLVE_RES_AMBIGUOUS || + ret == JS_RESOLVE_RES_EXCEPTION) { + return ret; + } else if (ret == JS_RESOLVE_RES_FOUND) { + if (*pme != NULL) { + if (*pmodule != res_m || + res_me->local_name != (*pme)->local_name) { + *pmodule = NULL; + *pme = NULL; + return JS_RESOLVE_RES_AMBIGUOUS; + } + } else { + *pmodule = res_m; + *pme = res_me; + } + } + } + if (*pme != NULL) + return JS_RESOLVE_RES_FOUND; + } + return JS_RESOLVE_RES_NOT_FOUND; + } +} + +/* If the return value is JS_RESOLVE_RES_FOUND, return the module + (*pmodule) and the corresponding local export entry + (*pme). Otherwise return (NULL, NULL) */ +static JSResolveResultEnum js_resolve_export(JSContext *ctx, + JSModuleDef **pmodule, + JSExportEntry **pme, + JSModuleDef *m, + JSAtom export_name) +{ + JSResolveState ss, *s = &ss; + int i; + JSResolveResultEnum ret; + + s->array = NULL; + s->size = 0; + s->count = 0; + + ret = js_resolve_export1(ctx, pmodule, pme, m, export_name, s); + + for(i = 0; i < s->count; i++) + JS_FreeAtom(ctx, s->array[i].name); + js_free(ctx, s->array); + + return ret; +} + +static void js_resolve_export_throw_error(JSContext *ctx, + JSResolveResultEnum res, + JSModuleDef *m, JSAtom export_name) +{ + char buf1[ATOM_GET_STR_BUF_SIZE]; + char buf2[ATOM_GET_STR_BUF_SIZE]; + switch(res) { + case JS_RESOLVE_RES_EXCEPTION: + break; + default: + case JS_RESOLVE_RES_NOT_FOUND: + JS_ThrowSyntaxError(ctx, "Could not find export '%s' in module '%s'", + JS_AtomGetStr(ctx, buf1, sizeof(buf1), export_name), + JS_AtomGetStr(ctx, buf2, sizeof(buf2), m->module_name)); + break; + case JS_RESOLVE_RES_CIRCULAR: + JS_ThrowSyntaxError(ctx, "circular reference when looking for export '%s' in module '%s'", + JS_AtomGetStr(ctx, buf1, sizeof(buf1), export_name), + JS_AtomGetStr(ctx, buf2, sizeof(buf2), m->module_name)); + break; + case JS_RESOLVE_RES_AMBIGUOUS: + JS_ThrowSyntaxError(ctx, "export '%s' in module '%s' is ambiguous", + JS_AtomGetStr(ctx, buf1, sizeof(buf1), export_name), + JS_AtomGetStr(ctx, buf2, sizeof(buf2), m->module_name)); + break; + } +} + + +typedef enum { + EXPORTED_NAME_AMBIGUOUS, + EXPORTED_NAME_NORMAL, + EXPORTED_NAME_NS, +} ExportedNameEntryEnum; + +typedef struct ExportedNameEntry { + JSAtom export_name; + ExportedNameEntryEnum export_type; + union { + JSExportEntry *me; /* using when the list is built */ + JSVarRef *var_ref; /* EXPORTED_NAME_NORMAL */ + JSModuleDef *module; /* for EXPORTED_NAME_NS */ + } u; +} ExportedNameEntry; + +typedef struct GetExportNamesState { + JSModuleDef **modules; + int modules_size; + int modules_count; + + ExportedNameEntry *exported_names; + int exported_names_size; + int exported_names_count; +} GetExportNamesState; + +static int find_exported_name(GetExportNamesState *s, JSAtom name) +{ + int i; + for(i = 0; i < s->exported_names_count; i++) { + if (s->exported_names[i].export_name == name) + return i; + } + return -1; +} + +static __exception int get_exported_names(JSContext *ctx, + GetExportNamesState *s, + JSModuleDef *m, BOOL from_star) +{ + ExportedNameEntry *en; + int i, j; + + /* check circular reference */ + for(i = 0; i < s->modules_count; i++) { + if (s->modules[i] == m) + return 0; + } + if (js_resize_array(ctx, (void **)&s->modules, sizeof(s->modules[0]), + &s->modules_size, s->modules_count + 1)) + return -1; + s->modules[s->modules_count++] = m; + + for(i = 0; i < m->export_entries_count; i++) { + JSExportEntry *me = &m->export_entries[i]; + if (from_star && me->export_name == JS_ATOM_default) + continue; + j = find_exported_name(s, me->export_name); + if (j < 0) { + if (js_resize_array(ctx, (void **)&s->exported_names, sizeof(s->exported_names[0]), + &s->exported_names_size, + s->exported_names_count + 1)) + return -1; + en = &s->exported_names[s->exported_names_count++]; + en->export_name = me->export_name; + /* avoid a second lookup for simple module exports */ + if (from_star || me->export_type != JS_EXPORT_TYPE_LOCAL) + en->u.me = NULL; + else + en->u.me = me; + } else { + en = &s->exported_names[j]; + en->u.me = NULL; + } + } + for(i = 0; i < m->star_export_entries_count; i++) { + JSStarExportEntry *se = &m->star_export_entries[i]; + JSModuleDef *m1; + m1 = m->req_module_entries[se->req_module_idx].module; + if (get_exported_names(ctx, s, m1, TRUE)) + return -1; + } + return 0; +} + +/* Unfortunately, the spec gives a different behavior from GetOwnProperty ! */ +static int js_module_ns_has(JSContext *ctx, JSValue obj, JSAtom atom) +{ + return (find_own_property1(JS_VALUE_GET_OBJ(obj), atom) != NULL); +} + +static const JSClassExoticMethods js_module_ns_exotic_methods = { + .has_property = js_module_ns_has, +}; + +static int exported_names_cmp(const void *p1, const void *p2, void *opaque) +{ + JSContext *ctx = opaque; + const ExportedNameEntry *me1 = p1; + const ExportedNameEntry *me2 = p2; + JSValue str1, str2; + int ret; + + /* XXX: should avoid allocation memory in atom comparison */ + str1 = JS_AtomToString(ctx, me1->export_name); + str2 = JS_AtomToString(ctx, me2->export_name); + if (JS_IsException(str1) || JS_IsException(str2)) { + /* XXX: raise an error ? */ + ret = 0; + } else { + ret = js_string_compare(ctx, JS_VALUE_GET_STRING(str1), + JS_VALUE_GET_STRING(str2)); + } + JS_FreeValue(ctx, str1); + JS_FreeValue(ctx, str2); + return ret; +} + +static JSValue js_module_ns_autoinit(JSContext *ctx, JSObject *p, JSAtom atom, + void *opaque) +{ + JSModuleDef *m = opaque; + return JS_GetModuleNamespace(ctx, m); +} + +static JSValue js_build_module_ns(JSContext *ctx, JSModuleDef *m) +{ + JSValue obj; + JSObject *p; + GetExportNamesState s_s, *s = &s_s; + int i, ret; + JSProperty *pr; + + obj = JS_NewObjectClass(ctx, JS_CLASS_MODULE_NS); + if (JS_IsException(obj)) + return obj; + p = JS_VALUE_GET_OBJ(obj); + + memset(s, 0, sizeof(*s)); + ret = get_exported_names(ctx, s, m, FALSE); + js_free(ctx, s->modules); + if (ret) + goto fail; + + /* Resolve the exported names. The ambiguous exports are removed */ + for(i = 0; i < s->exported_names_count; i++) { + ExportedNameEntry *en = &s->exported_names[i]; + JSResolveResultEnum res; + JSExportEntry *res_me; + JSModuleDef *res_m; + + if (en->u.me) { + res_me = en->u.me; /* fast case: no resolution needed */ + res_m = m; + res = JS_RESOLVE_RES_FOUND; + } else { + res = js_resolve_export(ctx, &res_m, &res_me, m, + en->export_name); + } + if (res != JS_RESOLVE_RES_FOUND) { + if (res != JS_RESOLVE_RES_AMBIGUOUS) { + js_resolve_export_throw_error(ctx, res, m, en->export_name); + goto fail; + } + en->export_type = EXPORTED_NAME_AMBIGUOUS; + } else { + if (res_me->local_name == JS_ATOM__star_) { + en->export_type = EXPORTED_NAME_NS; + en->u.module = res_m->req_module_entries[res_me->u.req_module_idx].module; + } else { + en->export_type = EXPORTED_NAME_NORMAL; + if (res_me->u.local.var_ref) { + en->u.var_ref = res_me->u.local.var_ref; + } else { + JSObject *p1 = JS_VALUE_GET_OBJ(res_m->func_obj); + p1 = JS_VALUE_GET_OBJ(res_m->func_obj); + en->u.var_ref = p1->u.func.var_refs[res_me->u.local.var_idx]; + } + } + } + } + + /* sort the exported names */ + rqsort(s->exported_names, s->exported_names_count, + sizeof(s->exported_names[0]), exported_names_cmp, ctx); + + for(i = 0; i < s->exported_names_count; i++) { + ExportedNameEntry *en = &s->exported_names[i]; + switch(en->export_type) { + case EXPORTED_NAME_NORMAL: + { + JSVarRef *var_ref = en->u.var_ref; + if (!var_ref) { + js_resolve_export_throw_error(ctx, JS_RESOLVE_RES_CIRCULAR, + m, en->export_name); + goto fail; + } + pr = add_property(ctx, p, en->export_name, + JS_PROP_ENUMERABLE | JS_PROP_WRITABLE | + JS_PROP_VARREF); + if (!pr) + goto fail; + var_ref->header.ref_count++; + pr->u.var_ref = var_ref; + } + break; + case EXPORTED_NAME_NS: + /* the exported namespace must be created on demand */ + if (JS_DefineAutoInitProperty(ctx, obj, + en->export_name, + JS_AUTOINIT_ID_MODULE_NS, + en->u.module, JS_PROP_ENUMERABLE | JS_PROP_WRITABLE) < 0) + goto fail; + break; + default: + break; + } + } + + js_free(ctx, s->exported_names); + + JS_DefinePropertyValue(ctx, obj, JS_ATOM_Symbol_toStringTag, + JS_AtomToString(ctx, JS_ATOM_Module), + 0); + + p->extensible = FALSE; + return obj; + fail: + js_free(ctx, s->exported_names); + JS_FreeValue(ctx, obj); + return JS_EXCEPTION; +} + +JSValue JS_GetModuleNamespace(JSContext *ctx, JSModuleDef *m) +{ + if (JS_IsUndefined(m->module_ns)) { + JSValue val; + val = js_build_module_ns(ctx, m); + if (JS_IsException(val)) + return JS_EXCEPTION; + m->module_ns = val; + } + return js_dup(m->module_ns); +} + +#ifdef DUMP_MODULE_RESOLVE +#define module_trace(ctx, ...) \ + do { \ + if (check_dump_flag(ctx->rt, DUMP_MODULE_RESOLVE)) \ + printf(__VA_ARGS__); \ + } while (0) +#else +#define module_trace(...) +#endif + +/* Load all the required modules for module 'm' */ +static int js_resolve_module(JSContext *ctx, JSModuleDef *m) +{ + int i; + JSModuleDef *m1; + + if (m->resolved) + return 0; +#ifdef DUMP_MODULE_RESOLVE + if (check_dump_flag(ctx->rt, DUMP_MODULE_RESOLVE)) { + char buf1[ATOM_GET_STR_BUF_SIZE]; + printf("resolving module '%s':\n", JS_AtomGetStr(ctx, buf1, sizeof(buf1), m->module_name)); + } +#endif + m->resolved = TRUE; + /* resolve each requested module */ + for(i = 0; i < m->req_module_entries_count; i++) { + JSReqModuleEntry *rme = &m->req_module_entries[i]; + m1 = js_host_resolve_imported_module_atom(ctx, m->module_name, + rme->module_name); + if (!m1) + return -1; + rme->module = m1; + /* already done in js_host_resolve_imported_module() except if + the module was loaded with JS_EvalBinary() */ + if (js_resolve_module(ctx, m1) < 0) + return -1; + } + return 0; +} + +static JSVarRef *js_create_module_var(JSContext *ctx, BOOL is_lexical) +{ + JSVarRef *var_ref; + var_ref = js_malloc(ctx, sizeof(JSVarRef)); + if (!var_ref) + return NULL; + var_ref->header.ref_count = 1; + if (is_lexical) + var_ref->value = JS_UNINITIALIZED; + else + var_ref->value = JS_UNDEFINED; + var_ref->pvalue = &var_ref->value; + var_ref->is_detached = TRUE; + add_gc_object(ctx->rt, &var_ref->header, JS_GC_OBJ_TYPE_VAR_REF); + return var_ref; +} + +/* Create the <eval> function associated with the module */ +static int js_create_module_bytecode_function(JSContext *ctx, JSModuleDef *m) +{ + JSFunctionBytecode *b; + int i; + JSVarRef **var_refs; + JSValue func_obj, bfunc; + JSObject *p; + + bfunc = m->func_obj; + func_obj = JS_NewObjectProtoClass(ctx, ctx->function_proto, + JS_CLASS_BYTECODE_FUNCTION); + + if (JS_IsException(func_obj)) + return -1; + b = JS_VALUE_GET_PTR(bfunc); + + p = JS_VALUE_GET_OBJ(func_obj); + p->u.func.function_bytecode = b; + b->header.ref_count++; + p->u.func.home_object = NULL; + p->u.func.var_refs = NULL; + if (b->closure_var_count) { + var_refs = js_mallocz(ctx, sizeof(var_refs[0]) * b->closure_var_count); + if (!var_refs) + goto fail; + p->u.func.var_refs = var_refs; + + /* create the global variables. The other variables are + imported from other modules */ + for(i = 0; i < b->closure_var_count; i++) { + JSClosureVar *cv = &b->closure_var[i]; + JSVarRef *var_ref; + if (cv->is_local) { + var_ref = js_create_module_var(ctx, cv->is_lexical); + if (!var_ref) + goto fail; + + module_trace(ctx, "local %d: %p\n", i, var_ref); + + var_refs[i] = var_ref; + } + } + } + m->func_obj = func_obj; + JS_FreeValue(ctx, bfunc); + return 0; + fail: + JS_FreeValue(ctx, func_obj); + return -1; +} + +/* must be done before js_link_module() because of cyclic references */ +static int js_create_module_function(JSContext *ctx, JSModuleDef *m) +{ + BOOL is_c_module; + int i; + JSVarRef *var_ref; + + if (m->func_created) + return 0; + + is_c_module = (m->init_func != NULL); + + if (is_c_module) { + /* initialize the exported variables */ + for(i = 0; i < m->export_entries_count; i++) { + JSExportEntry *me = &m->export_entries[i]; + if (me->export_type == JS_EXPORT_TYPE_LOCAL) { + var_ref = js_create_module_var(ctx, FALSE); + if (!var_ref) + return -1; + me->u.local.var_ref = var_ref; + } + } + } else { + if (js_create_module_bytecode_function(ctx, m)) + return -1; + } + m->func_created = TRUE; + + /* do it on the dependencies */ + + for(i = 0; i < m->req_module_entries_count; i++) { + JSReqModuleEntry *rme = &m->req_module_entries[i]; + if (js_create_module_function(ctx, rme->module) < 0) + return -1; + } + + return 0; +} + + +/* Prepare a module to be executed by resolving all the imported + variables. */ +static int js_inner_module_linking(JSContext *ctx, JSModuleDef *m, + JSModuleDef **pstack_top, int index) +{ + int i; + JSImportEntry *mi; + JSModuleDef *m1; + JSVarRef **var_refs, *var_ref; + JSObject *p; + BOOL is_c_module; + JSValue ret_val; + + if (js_check_stack_overflow(ctx->rt, 0)) { + JS_ThrowStackOverflow(ctx); + return -1; + } + +#ifdef DUMP_MODULE_RESOLVE + if (check_dump_flag(ctx->rt, DUMP_MODULE_RESOLVE)) { + char buf1[ATOM_GET_STR_BUF_SIZE]; + printf("js_inner_module_linking '%s':\n", JS_AtomGetStr(ctx, buf1, sizeof(buf1), m->module_name)); + } +#endif + + if (m->status == JS_MODULE_STATUS_LINKING || + m->status == JS_MODULE_STATUS_LINKED || + m->status == JS_MODULE_STATUS_EVALUATING_ASYNC || + m->status == JS_MODULE_STATUS_EVALUATED) + return index; + + assert(m->status == JS_MODULE_STATUS_UNLINKED); + m->status = JS_MODULE_STATUS_LINKING; + m->dfs_index = index; + m->dfs_ancestor_index = index; + index++; + /* push 'm' on stack */ + m->stack_prev = *pstack_top; + *pstack_top = m; + + for(i = 0; i < m->req_module_entries_count; i++) { + JSReqModuleEntry *rme = &m->req_module_entries[i]; + m1 = rme->module; + index = js_inner_module_linking(ctx, m1, pstack_top, index); + if (index < 0) + goto fail; + assert(m1->status == JS_MODULE_STATUS_LINKING || + m1->status == JS_MODULE_STATUS_LINKED || + m1->status == JS_MODULE_STATUS_EVALUATING_ASYNC || + m1->status == JS_MODULE_STATUS_EVALUATED); + if (m1->status == JS_MODULE_STATUS_LINKING) { + m->dfs_ancestor_index = min_int(m->dfs_ancestor_index, + m1->dfs_ancestor_index); + } + } + +#ifdef DUMP_MODULE_RESOLVE + if (check_dump_flag(ctx->rt, DUMP_MODULE_RESOLVE)) { + char buf1[ATOM_GET_STR_BUF_SIZE]; + printf("instantiating module '%s':\n", JS_AtomGetStr(ctx, buf1, sizeof(buf1), m->module_name)); + } +#endif + /* check the indirect exports */ + for(i = 0; i < m->export_entries_count; i++) { + JSExportEntry *me = &m->export_entries[i]; + if (me->export_type == JS_EXPORT_TYPE_INDIRECT && + me->local_name != JS_ATOM__star_) { + JSResolveResultEnum ret; + JSExportEntry *res_me; + JSModuleDef *res_m, *m1; + m1 = m->req_module_entries[me->u.req_module_idx].module; + ret = js_resolve_export(ctx, &res_m, &res_me, m1, me->local_name); + if (ret != JS_RESOLVE_RES_FOUND) { + js_resolve_export_throw_error(ctx, ret, m, me->export_name); + goto fail; + } + } + } + +#ifdef DUMP_MODULE_RESOLVE + if (check_dump_flag(ctx->rt, DUMP_MODULE_RESOLVE)) { + printf("exported bindings:\n"); + for(i = 0; i < m->export_entries_count; i++) { + JSExportEntry *me = &m->export_entries[i]; + printf(" name="); print_atom(ctx, me->export_name); + printf(" local="); print_atom(ctx, me->local_name); + printf(" type=%d idx=%d\n", me->export_type, me->u.local.var_idx); + } + } +#endif + + is_c_module = (m->init_func != NULL); + + if (!is_c_module) { + p = JS_VALUE_GET_OBJ(m->func_obj); + var_refs = p->u.func.var_refs; + + for(i = 0; i < m->import_entries_count; i++) { + mi = &m->import_entries[i]; +#ifdef DUMP_MODULE_RESOLVE + if (check_dump_flag(ctx->rt, DUMP_MODULE_RESOLVE)) { + printf("import var_idx=%d name=", mi->var_idx); + print_atom(ctx, mi->import_name); + printf(": "); + } +#endif + m1 = m->req_module_entries[mi->req_module_idx].module; + if (mi->import_name == JS_ATOM__star_) { + JSValue val; + /* name space import */ + val = JS_GetModuleNamespace(ctx, m1); + if (JS_IsException(val)) + goto fail; + set_value(ctx, &var_refs[mi->var_idx]->value, val); + + module_trace(ctx, "namespace\n"); + + } else { + JSResolveResultEnum ret; + JSExportEntry *res_me; + JSModuleDef *res_m; + JSObject *p1; + + ret = js_resolve_export(ctx, &res_m, + &res_me, m1, mi->import_name); + if (ret != JS_RESOLVE_RES_FOUND) { + js_resolve_export_throw_error(ctx, ret, m1, mi->import_name); + goto fail; + } + if (res_me->local_name == JS_ATOM__star_) { + JSValue val; + JSModuleDef *m2; + /* name space import from */ + m2 = res_m->req_module_entries[res_me->u.req_module_idx].module; + val = JS_GetModuleNamespace(ctx, m2); + if (JS_IsException(val)) + goto fail; + var_ref = js_create_module_var(ctx, TRUE); + if (!var_ref) { + JS_FreeValue(ctx, val); + goto fail; + } + set_value(ctx, &var_ref->value, val); + var_refs[mi->var_idx] = var_ref; + + module_trace(ctx, "namespace from\n"); + + } else { + var_ref = res_me->u.local.var_ref; + if (!var_ref) { + p1 = JS_VALUE_GET_OBJ(res_m->func_obj); + var_ref = p1->u.func.var_refs[res_me->u.local.var_idx]; + } + var_ref->header.ref_count++; + var_refs[mi->var_idx] = var_ref; + + module_trace(ctx, "local export (var_ref=%p)\n", var_ref); + } + } + } + + /* keep the exported variables in the module export entries (they + are used when the eval function is deleted and cannot be + initialized before in case imports are exported) */ + for(i = 0; i < m->export_entries_count; i++) { + JSExportEntry *me = &m->export_entries[i]; + if (me->export_type == JS_EXPORT_TYPE_LOCAL) { + var_ref = var_refs[me->u.local.var_idx]; + var_ref->header.ref_count++; + me->u.local.var_ref = var_ref; + } + } + + /* initialize the global variables */ + ret_val = JS_Call(ctx, m->func_obj, JS_TRUE, 0, NULL); + if (JS_IsException(ret_val)) + goto fail; + JS_FreeValue(ctx, ret_val); + } + + assert(m->dfs_ancestor_index <= m->dfs_index); + if (m->dfs_index == m->dfs_ancestor_index) { + for(;;) { + /* pop m1 from stack */ + m1 = *pstack_top; + *pstack_top = m1->stack_prev; + m1->status = JS_MODULE_STATUS_LINKED; + if (m1 == m) + break; + } + } + +#ifdef DUMP_MODULE_RESOLVE + if (check_dump_flag(ctx->rt, DUMP_MODULE_RESOLVE)) { + printf("js_inner_module_linking done\n"); + } +#endif + return index; + fail: + return -1; +} + +/* Prepare a module to be executed by resolving all the imported + variables. */ +static int js_link_module(JSContext *ctx, JSModuleDef *m) +{ + JSModuleDef *stack_top, *m1; + +#ifdef DUMP_MODULE_RESOLVE + if (check_dump_flag(ctx->rt, DUMP_MODULE_RESOLVE)) { + char buf1[ATOM_GET_STR_BUF_SIZE]; + printf("js_link_module '%s':\n", JS_AtomGetStr(ctx, buf1, sizeof(buf1), m->module_name)); + } +#endif + assert(m->status == JS_MODULE_STATUS_UNLINKED || + m->status == JS_MODULE_STATUS_LINKED || + m->status == JS_MODULE_STATUS_EVALUATING_ASYNC || + m->status == JS_MODULE_STATUS_EVALUATED); + stack_top = NULL; + if (js_inner_module_linking(ctx, m, &stack_top, 0) < 0) { + while (stack_top != NULL) { + m1 = stack_top; + assert(m1->status == JS_MODULE_STATUS_LINKING); + m1->status = JS_MODULE_STATUS_UNLINKED; + stack_top = m1->stack_prev; + } + return -1; + } + assert(stack_top == NULL); + assert(m->status == JS_MODULE_STATUS_LINKED || + m->status == JS_MODULE_STATUS_EVALUATING_ASYNC || + m->status == JS_MODULE_STATUS_EVALUATED); + return 0; +} + +/* return JS_ATOM_NULL if the name cannot be found. Only works with + not striped bytecode functions. */ +JSAtom JS_GetScriptOrModuleName(JSContext *ctx, int n_stack_levels) +{ + JSStackFrame *sf; + JSFunctionBytecode *b; + JSObject *p; + /* XXX: currently we just use the filename of the englobing + function. It does not work for eval(). Need to add a + ScriptOrModule info in JSFunctionBytecode */ + sf = ctx->rt->current_stack_frame; + if (!sf) + return JS_ATOM_NULL; + while (n_stack_levels-- > 0) { + sf = sf->prev_frame; + if (!sf) + return JS_ATOM_NULL; + } + if (JS_VALUE_GET_TAG(sf->cur_func) != JS_TAG_OBJECT) + return JS_ATOM_NULL; + p = JS_VALUE_GET_OBJ(sf->cur_func); + if (!js_class_has_bytecode(p->class_id)) + return JS_ATOM_NULL; + b = p->u.func.function_bytecode; + return JS_DupAtom(ctx, b->filename); +} + +JSAtom JS_GetModuleName(JSContext *ctx, JSModuleDef *m) +{ + return JS_DupAtom(ctx, m->module_name); +} + +JSValue JS_GetImportMeta(JSContext *ctx, JSModuleDef *m) +{ + JSValue obj; + /* allocate meta_obj only if requested to save memory */ + obj = m->meta_obj; + if (JS_IsUndefined(obj)) { + obj = JS_NewObjectProto(ctx, JS_NULL); + if (JS_IsException(obj)) + return JS_EXCEPTION; + m->meta_obj = obj; + } + return js_dup(obj); +} + +static JSValue js_import_meta(JSContext *ctx) +{ + JSAtom filename; + JSModuleDef *m; + + filename = JS_GetScriptOrModuleName(ctx, 0); + if (filename == JS_ATOM_NULL) + goto fail; + + /* XXX: inefficient, need to add a module or script pointer in + JSFunctionBytecode */ + m = js_find_loaded_module(ctx, filename); + JS_FreeAtom(ctx, filename); + if (!m) { + fail: + JS_ThrowTypeError(ctx, "import.meta not supported in this context"); + return JS_EXCEPTION; + } + return JS_GetImportMeta(ctx, m); +} + +static JSValue JS_NewModuleValue(JSContext *ctx, JSModuleDef *m) +{ + return JS_DupValue(ctx, JS_MKPTR(JS_TAG_MODULE, m)); +} + +static JSValue js_load_module_rejected(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv, int magic, JSValue *func_data) +{ + JSValueConst *resolving_funcs = (JSValueConst *)func_data; + JSValueConst error; + JSValue ret; + + /* XXX: check if the test is necessary */ + if (argc >= 1) + error = argv[0]; + else + error = JS_UNDEFINED; + ret = JS_Call(ctx, resolving_funcs[1], JS_UNDEFINED, + 1, &error); + JS_FreeValue(ctx, ret); + return JS_UNDEFINED; +} + +static JSValue js_load_module_fulfilled(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv, int magic, JSValue *func_data) +{ + JSValueConst *resolving_funcs = (JSValueConst *)func_data; + JSModuleDef *m = JS_VALUE_GET_PTR(func_data[2]); + JSValue ret, ns; + + /* return the module namespace */ + ns = JS_GetModuleNamespace(ctx, m); + if (JS_IsException(ns)) { + JSValue err = JS_GetException(ctx); + js_load_module_rejected(ctx, JS_UNDEFINED, 1, (JSValueConst *)&err, 0, func_data); + return JS_UNDEFINED; + } + ret = JS_Call(ctx, resolving_funcs[0], JS_UNDEFINED, + 1, (JSValueConst *)&ns); + JS_FreeValue(ctx, ret); + JS_FreeValue(ctx, ns); + return JS_UNDEFINED; +} + +static void JS_LoadModuleInternal(JSContext *ctx, const char *basename, + const char *filename, + JSValueConst *resolving_funcs) +{ + JSValue evaluate_promise; + JSModuleDef *m; + JSValue ret, err, func_obj, evaluate_resolving_funcs[2]; + JSValueConst func_data[3]; + + m = js_host_resolve_imported_module(ctx, basename, filename); + if (!m) + goto fail; + + if (js_resolve_module(ctx, m) < 0) { + js_free_modules(ctx, JS_FREE_MODULE_NOT_RESOLVED); + goto fail; + } + + /* Evaluate the module code */ + func_obj = JS_NewModuleValue(ctx, m); + evaluate_promise = JS_EvalFunction(ctx, func_obj); + if (JS_IsException(evaluate_promise)) { + fail: + err = JS_GetException(ctx); + ret = JS_Call(ctx, resolving_funcs[1], JS_UNDEFINED, + 1, (JSValueConst *)&err); + JS_FreeValue(ctx, ret); /* XXX: what to do if exception ? */ + JS_FreeValue(ctx, err); + return; + } + + func_obj = JS_NewModuleValue(ctx, m); + func_data[0] = resolving_funcs[0]; + func_data[1] = resolving_funcs[1]; + func_data[2] = func_obj; + evaluate_resolving_funcs[0] = JS_NewCFunctionData(ctx, js_load_module_fulfilled, 0, 0, 3, func_data); + evaluate_resolving_funcs[1] = JS_NewCFunctionData(ctx, js_load_module_rejected, 0, 0, 3, func_data); + JS_FreeValue(ctx, func_obj); + ret = js_promise_then(ctx, evaluate_promise, 2, (JSValueConst *)evaluate_resolving_funcs); + JS_FreeValue(ctx, ret); + JS_FreeValue(ctx, evaluate_resolving_funcs[0]); + JS_FreeValue(ctx, evaluate_resolving_funcs[1]); + JS_FreeValue(ctx, evaluate_promise); +} + +/* Return a promise or an exception in case of memory error. Used by + os.Worker() */ +JSValue JS_LoadModule(JSContext *ctx, const char *basename, + const char *filename) +{ + JSValue promise, resolving_funcs[2]; + + promise = JS_NewPromiseCapability(ctx, resolving_funcs); + if (JS_IsException(promise)) + return JS_EXCEPTION; + JS_LoadModuleInternal(ctx, basename, filename, + (JSValueConst *)resolving_funcs); + JS_FreeValue(ctx, resolving_funcs[0]); + JS_FreeValue(ctx, resolving_funcs[1]); + return promise; +} + +static JSValue js_dynamic_import_job(JSContext *ctx, + int argc, JSValue *argv) +{ + JSValue *resolving_funcs = argv; + JSValue basename_val = argv[2]; + JSValue specifier = argv[3]; + const char *basename = NULL, *filename; + JSValue ret, err; + + if (!JS_IsString(basename_val)) { + JS_ThrowTypeError(ctx, "no function filename for import()"); + goto exception; + } + basename = JS_ToCString(ctx, basename_val); + if (!basename) + goto exception; + + filename = JS_ToCString(ctx, specifier); + if (!filename) + goto exception; + + JS_LoadModuleInternal(ctx, basename, filename, + resolving_funcs); + JS_FreeCString(ctx, filename); + JS_FreeCString(ctx, basename); + return JS_UNDEFINED; + exception: + err = JS_GetException(ctx); + ret = JS_Call(ctx, resolving_funcs[1], JS_UNDEFINED, + 1, &err); + JS_FreeValue(ctx, ret); /* XXX: what to do if exception ? */ + JS_FreeValue(ctx, err); + JS_FreeCString(ctx, basename); + return JS_UNDEFINED; +} + +static JSValue js_dynamic_import(JSContext *ctx, JSValue specifier) +{ + JSAtom basename; + JSValue promise, resolving_funcs[2], basename_val; + JSValue args[4]; + + basename = JS_GetScriptOrModuleName(ctx, 0); + if (basename == JS_ATOM_NULL) + basename_val = JS_NULL; + else + basename_val = JS_AtomToValue(ctx, basename); + JS_FreeAtom(ctx, basename); + if (JS_IsException(basename_val)) + return basename_val; + + promise = JS_NewPromiseCapability(ctx, resolving_funcs); + if (JS_IsException(promise)) { + JS_FreeValue(ctx, basename_val); + return promise; + } + + args[0] = resolving_funcs[0]; + args[1] = resolving_funcs[1]; + args[2] = basename_val; + args[3] = specifier; + + /* cannot run JS_LoadModuleInternal synchronously because it would + cause an unexpected recursion in js_evaluate_module() */ + JS_EnqueueJob(ctx, js_dynamic_import_job, 4, args); + + JS_FreeValue(ctx, basename_val); + JS_FreeValue(ctx, resolving_funcs[0]); + JS_FreeValue(ctx, resolving_funcs[1]); + return promise; +} + +static void js_set_module_evaluated(JSContext *ctx, JSModuleDef *m) +{ + m->status = JS_MODULE_STATUS_EVALUATED; + if (!JS_IsUndefined(m->promise)) { + JSValue value, ret_val; + assert(m->cycle_root == m); + value = JS_UNDEFINED; + ret_val = JS_Call(ctx, m->resolving_funcs[0], JS_UNDEFINED, + 1, (JSValueConst *)&value); + JS_FreeValue(ctx, ret_val); + } +} + +typedef struct { + JSModuleDef **tab; + int count; + int size; +} ExecModuleList; + +/* XXX: slow. Could use a linked list instead of ExecModuleList */ +static BOOL find_in_exec_module_list(ExecModuleList *exec_list, JSModuleDef *m) +{ + int i; + for(i = 0; i < exec_list->count; i++) { + if (exec_list->tab[i] == m) + return TRUE; + } + return FALSE; +} + +static int gather_available_ancestors(JSContext *ctx, JSModuleDef *module, + ExecModuleList *exec_list) +{ + int i; + + if (js_check_stack_overflow(ctx->rt, 0)) { + JS_ThrowStackOverflow(ctx); + return -1; + } + for(i = 0; i < module->async_parent_modules_count; i++) { + JSModuleDef *m = module->async_parent_modules[i]; + if (!find_in_exec_module_list(exec_list, m) && + !m->cycle_root->eval_has_exception) { + assert(m->status == JS_MODULE_STATUS_EVALUATING_ASYNC); + assert(!m->eval_has_exception); + assert(m->async_evaluation); + assert(m->pending_async_dependencies > 0); + m->pending_async_dependencies--; + if (m->pending_async_dependencies == 0) { + if (js_resize_array(ctx, (void **)&exec_list->tab, sizeof(exec_list->tab[0]), &exec_list->size, exec_list->count + 1)) { + return -1; + } + exec_list->tab[exec_list->count++] = m; + if (!m->has_tla) { + if (gather_available_ancestors(ctx, m, exec_list)) + return -1; + } + } + } + } + return 0; +} + +static int exec_module_list_cmp(const void *p1, const void *p2, void *opaque) +{ + JSModuleDef *m1 = *(JSModuleDef **)p1; + JSModuleDef *m2 = *(JSModuleDef **)p2; + return (m1->async_evaluation_timestamp > m2->async_evaluation_timestamp) - + (m1->async_evaluation_timestamp < m2->async_evaluation_timestamp); +} + +static int js_execute_async_module(JSContext *ctx, JSModuleDef *m); +static int js_execute_sync_module(JSContext *ctx, JSModuleDef *m, + JSValue *pvalue); + +static JSValue js_async_module_execution_rejected(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv, int magic, JSValue *func_data) +{ + JSModuleDef *module = JS_VALUE_GET_PTR(func_data[0]); + JSValueConst error = argv[0]; + int i; + + if (js_check_stack_overflow(ctx->rt, 0)) + return JS_ThrowStackOverflow(ctx); + + if (module->status == JS_MODULE_STATUS_EVALUATED) { + assert(module->eval_has_exception); + return JS_UNDEFINED; + } + + assert(module->status == JS_MODULE_STATUS_EVALUATING_ASYNC); + assert(!module->eval_has_exception); + assert(module->async_evaluation); + + module->eval_has_exception = TRUE; + module->eval_exception = JS_DupValue(ctx, error); + module->status = JS_MODULE_STATUS_EVALUATED; + + for(i = 0; i < module->async_parent_modules_count; i++) { + JSModuleDef *m = module->async_parent_modules[i]; + JSValue m_obj = JS_NewModuleValue(ctx, m); + js_async_module_execution_rejected(ctx, JS_UNDEFINED, 1, &error, 0, + &m_obj); + JS_FreeValue(ctx, m_obj); + } + + if (!JS_IsUndefined(module->promise)) { + JSValue ret_val; + assert(module->cycle_root == module); + ret_val = JS_Call(ctx, module->resolving_funcs[1], JS_UNDEFINED, + 1, &error); + JS_FreeValue(ctx, ret_val); + } + return JS_UNDEFINED; +} + +static JSValue js_async_module_execution_fulfilled(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv, int magic, JSValue *func_data) +{ + JSModuleDef *module = JS_VALUE_GET_PTR(func_data[0]); + ExecModuleList exec_list_s, *exec_list = &exec_list_s; + int i; + + if (module->status == JS_MODULE_STATUS_EVALUATED) { + assert(module->eval_has_exception); + return JS_UNDEFINED; + } + assert(module->status == JS_MODULE_STATUS_EVALUATING_ASYNC); + assert(!module->eval_has_exception); + assert(module->async_evaluation); + module->async_evaluation = FALSE; + js_set_module_evaluated(ctx, module); + + exec_list->tab = NULL; + exec_list->count = 0; + exec_list->size = 0; + + if (gather_available_ancestors(ctx, module, exec_list) < 0) { + js_free(ctx, exec_list->tab); + return JS_EXCEPTION; + } + + /* sort by increasing async_evaluation timestamp */ + rqsort(exec_list->tab, exec_list->count, sizeof(exec_list->tab[0]), + exec_module_list_cmp, NULL); + + for(i = 0; i < exec_list->count; i++) { + JSModuleDef *m = exec_list->tab[i]; + if (m->status == JS_MODULE_STATUS_EVALUATED) { + assert(m->eval_has_exception); + } else if (m->has_tla) { + js_execute_async_module(ctx, m); + } else { + JSValue error; + if (js_execute_sync_module(ctx, m, &error) < 0) { + JSValue m_obj = JS_NewModuleValue(ctx, m); + js_async_module_execution_rejected(ctx, JS_UNDEFINED, + 1, (JSValueConst *)&error, 0, + &m_obj); + JS_FreeValue(ctx, m_obj); + JS_FreeValue(ctx, error); + } else { + js_set_module_evaluated(ctx, m); + } + } + } + js_free(ctx, exec_list->tab); + return JS_UNDEFINED; +} + +static int js_execute_async_module(JSContext *ctx, JSModuleDef *m) +{ + JSValue promise, m_obj; + JSValue resolve_funcs[2], ret_val; + promise = js_async_function_call(ctx, m->func_obj, JS_UNDEFINED, 0, NULL, 0); + if (JS_IsException(promise)) + return -1; + m_obj = JS_NewModuleValue(ctx, m); + resolve_funcs[0] = JS_NewCFunctionData(ctx, js_async_module_execution_fulfilled, 0, 0, 1, (JSValueConst *)&m_obj); + resolve_funcs[1] = JS_NewCFunctionData(ctx, js_async_module_execution_rejected, 0, 0, 1, (JSValueConst *)&m_obj); + ret_val = js_promise_then(ctx, promise, 2, (JSValueConst *)resolve_funcs); + JS_FreeValue(ctx, ret_val); + JS_FreeValue(ctx, m_obj); + JS_FreeValue(ctx, resolve_funcs[0]); + JS_FreeValue(ctx, resolve_funcs[1]); + JS_FreeValue(ctx, promise); + return 0; +} + +/* return < 0 in case of exception. *pvalue contains the exception. */ +static int js_execute_sync_module(JSContext *ctx, JSModuleDef *m, + JSValue *pvalue) +{ + if (m->init_func) { + /* C module init : no asynchronous execution */ + if (m->init_func(ctx, m) < 0) + goto fail; + } else { + JSValue promise; + JSPromiseStateEnum state; + + promise = js_async_function_call(ctx, m->func_obj, JS_UNDEFINED, 0, NULL, 0); + if (JS_IsException(promise)) + goto fail; + state = JS_PromiseState(ctx, promise); + if (state == JS_PROMISE_FULFILLED) { + JS_FreeValue(ctx, promise); + } else if (state == JS_PROMISE_REJECTED) { + *pvalue = JS_PromiseResult(ctx, promise); + JS_FreeValue(ctx, promise); + return -1; + } else { + JS_FreeValue(ctx, promise); + JS_ThrowTypeError(ctx, "promise is pending"); + fail: + *pvalue = JS_GetException(ctx); + return -1; + } + } + *pvalue = JS_UNDEFINED; + return 0; +} + +/* spec: InnerModuleEvaluation. Return (index, JS_UNDEFINED) or (-1, + exception) */ +static int js_inner_module_evaluation(JSContext *ctx, JSModuleDef *m, + int index, JSModuleDef **pstack_top, + JSValue *pvalue) +{ + JSModuleDef *m1; + int i; + + if (js_check_stack_overflow(ctx->rt, 0)) { + JS_ThrowStackOverflow(ctx); + *pvalue = JS_GetException(ctx); + return -1; + } + +#ifdef DUMP_MODULE_RESOLVE + if (check_dump_flag(ctx->rt, DUMP_MODULE_RESOLVE)) { + char buf1[ATOM_GET_STR_BUF_SIZE]; + printf("js_inner_module_evaluation '%s':\n", JS_AtomGetStr(ctx, buf1, sizeof(buf1), m->module_name)); + } +#endif + + if (m->status == JS_MODULE_STATUS_EVALUATING_ASYNC || + m->status == JS_MODULE_STATUS_EVALUATED) { + if (m->eval_has_exception) { + *pvalue = JS_DupValue(ctx, m->eval_exception); + return -1; + } else { + *pvalue = JS_UNDEFINED; + return index; + } + } + if (m->status == JS_MODULE_STATUS_EVALUATING) { + *pvalue = JS_UNDEFINED; + return index; + } + assert(m->status == JS_MODULE_STATUS_LINKED); + + m->status = JS_MODULE_STATUS_EVALUATING; + m->dfs_index = index; + m->dfs_ancestor_index = index; + m->pending_async_dependencies = 0; + index++; + /* push 'm' on stack */ + m->stack_prev = *pstack_top; + *pstack_top = m; + + for(i = 0; i < m->req_module_entries_count; i++) { + JSReqModuleEntry *rme = &m->req_module_entries[i]; + m1 = rme->module; + index = js_inner_module_evaluation(ctx, m1, index, pstack_top, pvalue); + if (index < 0) + return -1; + assert(m1->status == JS_MODULE_STATUS_EVALUATING || + m1->status == JS_MODULE_STATUS_EVALUATING_ASYNC || + m1->status == JS_MODULE_STATUS_EVALUATED); + if (m1->status == JS_MODULE_STATUS_EVALUATING) { + m->dfs_ancestor_index = min_int(m->dfs_ancestor_index, + m1->dfs_ancestor_index); + } else { + m1 = m1->cycle_root; + assert(m1->status == JS_MODULE_STATUS_EVALUATING_ASYNC || + m1->status == JS_MODULE_STATUS_EVALUATED); + if (m1->eval_has_exception) { + *pvalue = JS_DupValue(ctx, m1->eval_exception); + return -1; + } + } + if (m1->async_evaluation) { + m->pending_async_dependencies++; + if (js_resize_array(ctx, (void **)&m1->async_parent_modules, sizeof(m1->async_parent_modules[0]), &m1->async_parent_modules_size, m1->async_parent_modules_count + 1)) { + *pvalue = JS_GetException(ctx); + return -1; + } + m1->async_parent_modules[m1->async_parent_modules_count++] = m; + } + } + + if (m->pending_async_dependencies > 0) { + assert(!m->async_evaluation); + m->async_evaluation = TRUE; + m->async_evaluation_timestamp = + ctx->rt->module_async_evaluation_next_timestamp++; + } else if (m->has_tla) { + assert(!m->async_evaluation); + m->async_evaluation = TRUE; + m->async_evaluation_timestamp = + ctx->rt->module_async_evaluation_next_timestamp++; + js_execute_async_module(ctx, m); + } else { + if (js_execute_sync_module(ctx, m, pvalue) < 0) + return -1; + } + + assert(m->dfs_ancestor_index <= m->dfs_index); + if (m->dfs_index == m->dfs_ancestor_index) { + for(;;) { + /* pop m1 from stack */ + m1 = *pstack_top; + *pstack_top = m1->stack_prev; + if (!m1->async_evaluation) { + m1->status = JS_MODULE_STATUS_EVALUATED; + } else { + m1->status = JS_MODULE_STATUS_EVALUATING_ASYNC; + } + /* spec bug: cycle_root must be assigned before the test */ + m1->cycle_root = m; + if (m1 == m) + break; + } + } + *pvalue = JS_UNDEFINED; + return index; +} + +/* Run the <eval> function of the module and of all its requested + modules. Return a promise or an exception. */ +static JSValue js_evaluate_module(JSContext *ctx, JSModuleDef *m) +{ + JSModuleDef *m1, *stack_top; + JSValue ret_val, result; + + assert(m->status == JS_MODULE_STATUS_LINKED || + m->status == JS_MODULE_STATUS_EVALUATING_ASYNC || + m->status == JS_MODULE_STATUS_EVALUATED); + if (m->status == JS_MODULE_STATUS_EVALUATING_ASYNC || + m->status == JS_MODULE_STATUS_EVALUATED) { + m = m->cycle_root; + } + /* a promise may be created only on the cycle_root of a cycle */ + if (!JS_IsUndefined(m->promise)) + return JS_DupValue(ctx, m->promise); + m->promise = JS_NewPromiseCapability(ctx, m->resolving_funcs); + if (JS_IsException(m->promise)) + return JS_EXCEPTION; + + stack_top = NULL; + if (js_inner_module_evaluation(ctx, m, 0, &stack_top, &result) < 0) { + while (stack_top != NULL) { + m1 = stack_top; + assert(m1->status == JS_MODULE_STATUS_EVALUATING); + m1->status = JS_MODULE_STATUS_EVALUATED; + m1->eval_has_exception = TRUE; + m1->eval_exception = JS_DupValue(ctx, result); + m1->cycle_root = m; /* spec bug: should be present */ + stack_top = m1->stack_prev; + } + JS_FreeValue(ctx, result); + assert(m->status == JS_MODULE_STATUS_EVALUATED); + assert(m->eval_has_exception); + ret_val = JS_Call(ctx, m->resolving_funcs[1], JS_UNDEFINED, + 1, (JSValueConst *)&m->eval_exception); + JS_FreeValue(ctx, ret_val); + } else { + assert(m->status == JS_MODULE_STATUS_EVALUATING_ASYNC || + m->status == JS_MODULE_STATUS_EVALUATED); + assert(!m->eval_has_exception); + if (!m->async_evaluation) { + JSValue value; + assert(m->status == JS_MODULE_STATUS_EVALUATED); + value = JS_UNDEFINED; + ret_val = JS_Call(ctx, m->resolving_funcs[0], JS_UNDEFINED, + 1, (JSValueConst *)&value); + JS_FreeValue(ctx, ret_val); + } + assert(stack_top == NULL); + } + return JS_DupValue(ctx, m->promise); +} + +static __exception JSAtom js_parse_from_clause(JSParseState *s) +{ + JSAtom module_name; + if (!token_is_pseudo_keyword(s, JS_ATOM_from)) { + js_parse_error(s, "from clause expected"); + return JS_ATOM_NULL; + } + if (next_token(s)) + return JS_ATOM_NULL; + if (s->token.val != TOK_STRING) { + js_parse_error(s, "string expected"); + return JS_ATOM_NULL; + } + module_name = JS_ValueToAtom(s->ctx, s->token.u.str.str); + if (module_name == JS_ATOM_NULL) + return JS_ATOM_NULL; + if (next_token(s)) { + JS_FreeAtom(s->ctx, module_name); + return JS_ATOM_NULL; + } + return module_name; +} + +static __exception int js_parse_export(JSParseState *s) +{ + JSContext *ctx = s->ctx; + JSModuleDef *m = s->cur_func->module; + JSAtom local_name, export_name; + int first_export, idx, i, tok; + JSAtom module_name; + JSExportEntry *me; + + if (next_token(s)) + return -1; + + tok = s->token.val; + if (tok == TOK_CLASS) { + return js_parse_class(s, FALSE, JS_PARSE_EXPORT_NAMED); + } else if (tok == TOK_FUNCTION || + (token_is_pseudo_keyword(s, JS_ATOM_async) && + peek_token(s, TRUE) == TOK_FUNCTION)) { + return js_parse_function_decl2(s, JS_PARSE_FUNC_STATEMENT, + JS_FUNC_NORMAL, JS_ATOM_NULL, + s->token.ptr, + s->token.line_num, + s->token.col_num, + JS_PARSE_EXPORT_NAMED, NULL); + } + + if (next_token(s)) + return -1; + + switch(tok) { + case '{': + first_export = m->export_entries_count; + while (s->token.val != '}') { + if (!token_is_ident(s->token.val)) { + js_parse_error(s, "identifier expected"); + return -1; + } + local_name = JS_DupAtom(ctx, s->token.u.ident.atom); + export_name = JS_ATOM_NULL; + if (next_token(s)) + goto fail; + if (token_is_pseudo_keyword(s, JS_ATOM_as)) { + if (next_token(s)) + goto fail; + if (!token_is_ident(s->token.val)) { + js_parse_error(s, "identifier expected"); + goto fail; + } + export_name = JS_DupAtom(ctx, s->token.u.ident.atom); + if (next_token(s)) { + fail: + JS_FreeAtom(ctx, local_name); + fail1: + JS_FreeAtom(ctx, export_name); + return -1; + } + } else { + export_name = JS_DupAtom(ctx, local_name); + } + me = add_export_entry(s, m, local_name, export_name, + JS_EXPORT_TYPE_LOCAL); + JS_FreeAtom(ctx, local_name); + JS_FreeAtom(ctx, export_name); + if (!me) + return -1; + if (s->token.val != ',') + break; + if (next_token(s)) + return -1; + } + if (js_parse_expect(s, '}')) + return -1; + if (token_is_pseudo_keyword(s, JS_ATOM_from)) { + module_name = js_parse_from_clause(s); + if (module_name == JS_ATOM_NULL) + return -1; + idx = add_req_module_entry(ctx, m, module_name); + JS_FreeAtom(ctx, module_name); + if (idx < 0) + return -1; + for(i = first_export; i < m->export_entries_count; i++) { + me = &m->export_entries[i]; + me->export_type = JS_EXPORT_TYPE_INDIRECT; + me->u.req_module_idx = idx; + } + } + break; + case '*': + if (token_is_pseudo_keyword(s, JS_ATOM_as)) { + /* export ns from */ + if (next_token(s)) + return -1; + if (!token_is_ident(s->token.val)) { + js_parse_error(s, "identifier expected"); + return -1; + } + export_name = JS_DupAtom(ctx, s->token.u.ident.atom); + if (next_token(s)) + goto fail1; + module_name = js_parse_from_clause(s); + if (module_name == JS_ATOM_NULL) + goto fail1; + idx = add_req_module_entry(ctx, m, module_name); + JS_FreeAtom(ctx, module_name); + if (idx < 0) + goto fail1; + me = add_export_entry(s, m, JS_ATOM__star_, export_name, + JS_EXPORT_TYPE_INDIRECT); + JS_FreeAtom(ctx, export_name); + if (!me) + return -1; + me->u.req_module_idx = idx; + } else { + module_name = js_parse_from_clause(s); + if (module_name == JS_ATOM_NULL) + return -1; + idx = add_req_module_entry(ctx, m, module_name); + JS_FreeAtom(ctx, module_name); + if (idx < 0) + return -1; + if (add_star_export_entry(ctx, m, idx) < 0) + return -1; + } + break; + case TOK_DEFAULT: + if (s->token.val == TOK_CLASS) { + return js_parse_class(s, FALSE, JS_PARSE_EXPORT_DEFAULT); + } else if (s->token.val == TOK_FUNCTION || + (token_is_pseudo_keyword(s, JS_ATOM_async) && + peek_token(s, TRUE) == TOK_FUNCTION)) { + return js_parse_function_decl2(s, JS_PARSE_FUNC_STATEMENT, + JS_FUNC_NORMAL, JS_ATOM_NULL, + s->token.ptr, + s->token.line_num, + s->token.col_num, + JS_PARSE_EXPORT_DEFAULT, NULL); + } else { + if (js_parse_assign_expr(s)) + return -1; + } + /* set the name of anonymous functions */ + set_object_name(s, JS_ATOM_default); + + /* store the value in the _default_ global variable and export + it */ + local_name = JS_ATOM__default_; + if (define_var(s, s->cur_func, local_name, JS_VAR_DEF_LET) < 0) + return -1; + emit_op(s, OP_scope_put_var_init); + emit_atom(s, local_name); + emit_u16(s, 0); + + if (!add_export_entry(s, m, local_name, JS_ATOM_default, + JS_EXPORT_TYPE_LOCAL)) + return -1; + break; + case TOK_VAR: + case TOK_LET: + case TOK_CONST: + return js_parse_var(s, TRUE, tok, TRUE); + default: + return js_parse_error(s, "invalid export syntax"); + } + return js_parse_expect_semi(s); +} + +static int add_closure_var(JSContext *ctx, JSFunctionDef *s, + BOOL is_local, BOOL is_arg, + int var_idx, JSAtom var_name, + BOOL is_const, BOOL is_lexical, + JSVarKindEnum var_kind); + +static int add_import(JSParseState *s, JSModuleDef *m, + JSAtom local_name, JSAtom import_name) +{ + JSContext *ctx = s->ctx; + int i, var_idx; + JSImportEntry *mi; + BOOL is_local; + + if (local_name == JS_ATOM_arguments || local_name == JS_ATOM_eval) + return js_parse_error(s, "invalid import binding"); + + if (local_name != JS_ATOM_default) { + for (i = 0; i < s->cur_func->closure_var_count; i++) { + if (s->cur_func->closure_var[i].var_name == local_name) + return js_parse_error(s, "duplicate import binding"); + } + } + + is_local = (import_name == JS_ATOM__star_); + var_idx = add_closure_var(ctx, s->cur_func, is_local, FALSE, + m->import_entries_count, + local_name, TRUE, TRUE, JS_VAR_NORMAL); + if (var_idx < 0) + return -1; + if (js_resize_array(ctx, (void **)&m->import_entries, + sizeof(JSImportEntry), + &m->import_entries_size, + m->import_entries_count + 1)) + return -1; + mi = &m->import_entries[m->import_entries_count++]; + mi->import_name = JS_DupAtom(ctx, import_name); + mi->var_idx = var_idx; + return 0; +} + +static __exception int js_parse_import(JSParseState *s) +{ + JSContext *ctx = s->ctx; + JSModuleDef *m = s->cur_func->module; + JSAtom local_name, import_name, module_name; + int first_import, i, idx; + + if (next_token(s)) + return -1; + + first_import = m->import_entries_count; + if (s->token.val == TOK_STRING) { + module_name = JS_ValueToAtom(ctx, s->token.u.str.str); + if (module_name == JS_ATOM_NULL) + return -1; + if (next_token(s)) { + JS_FreeAtom(ctx, module_name); + return -1; + } + } else { + if (s->token.val == TOK_IDENT) { + if (s->token.u.ident.is_reserved) { + return js_parse_error_reserved_identifier(s); + } + /* "default" import */ + local_name = JS_DupAtom(ctx, s->token.u.ident.atom); + import_name = JS_ATOM_default; + if (next_token(s)) + goto fail; + if (add_import(s, m, local_name, import_name)) + goto fail; + JS_FreeAtom(ctx, local_name); + + if (s->token.val != ',') + goto end_import_clause; + if (next_token(s)) + return -1; + } + + if (s->token.val == '*') { + /* name space import */ + if (next_token(s)) + return -1; + if (!token_is_pseudo_keyword(s, JS_ATOM_as)) + return js_parse_error(s, "expecting 'as'"); + if (next_token(s)) + return -1; + if (!token_is_ident(s->token.val)) { + js_parse_error(s, "identifier expected"); + return -1; + } + local_name = JS_DupAtom(ctx, s->token.u.ident.atom); + import_name = JS_ATOM__star_; + if (next_token(s)) + goto fail; + if (add_import(s, m, local_name, import_name)) + goto fail; + JS_FreeAtom(ctx, local_name); + } else if (s->token.val == '{') { + if (next_token(s)) + return -1; + + while (s->token.val != '}') { + if (!token_is_ident(s->token.val)) { + js_parse_error(s, "identifier expected"); + return -1; + } + import_name = JS_DupAtom(ctx, s->token.u.ident.atom); + local_name = JS_ATOM_NULL; + if (next_token(s)) + goto fail; + if (token_is_pseudo_keyword(s, JS_ATOM_as)) { + if (next_token(s)) + goto fail; + if (!token_is_ident(s->token.val)) { + js_parse_error(s, "identifier expected"); + goto fail; + } + local_name = JS_DupAtom(ctx, s->token.u.ident.atom); + if (next_token(s)) { + fail: + JS_FreeAtom(ctx, local_name); + JS_FreeAtom(ctx, import_name); + return -1; + } + } else { + local_name = JS_DupAtom(ctx, import_name); + } + if (add_import(s, m, local_name, import_name)) + goto fail; + JS_FreeAtom(ctx, local_name); + JS_FreeAtom(ctx, import_name); + if (s->token.val != ',') + break; + if (next_token(s)) + return -1; + } + if (js_parse_expect(s, '}')) + return -1; + } + end_import_clause: + module_name = js_parse_from_clause(s); + if (module_name == JS_ATOM_NULL) + return -1; + } + idx = add_req_module_entry(ctx, m, module_name); + JS_FreeAtom(ctx, module_name); + if (idx < 0) + return -1; + for(i = first_import; i < m->import_entries_count; i++) + m->import_entries[i].req_module_idx = idx; + + return js_parse_expect_semi(s); +} + +static __exception int js_parse_source_element(JSParseState *s) +{ + JSFunctionDef *fd = s->cur_func; + int tok; + + if (s->token.val == TOK_FUNCTION || + (token_is_pseudo_keyword(s, JS_ATOM_async) && + peek_token(s, TRUE) == TOK_FUNCTION)) { + if (js_parse_function_decl(s, JS_PARSE_FUNC_STATEMENT, + JS_FUNC_NORMAL, JS_ATOM_NULL, + s->token.ptr, + s->token.line_num, + s->token.col_num)) + return -1; + } else if (s->token.val == TOK_EXPORT && fd->module) { + if (js_parse_export(s)) + return -1; + } else if (s->token.val == TOK_IMPORT && fd->module && + ((tok = peek_token(s, FALSE)) != '(' && tok != '.')) { + /* the peek_token is needed to avoid confusion with ImportCall + (dynamic import) or import.meta */ + if (js_parse_import(s)) + return -1; + } else { + if (js_parse_statement_or_decl(s, DECL_MASK_ALL)) + return -1; + } + return 0; +} + +/* `filename` may be pure ASCII or UTF-8 encoded */ +static JSFunctionDef *js_new_function_def(JSContext *ctx, + JSFunctionDef *parent, + BOOL is_eval, + BOOL is_func_expr, + const char *filename, + int line_num, + int col_num) +{ + JSFunctionDef *fd; + + fd = js_mallocz(ctx, sizeof(*fd)); + if (!fd) + return NULL; + + fd->ctx = ctx; + init_list_head(&fd->child_list); + + /* insert in parent list */ + fd->parent = parent; + fd->parent_cpool_idx = -1; + if (parent) { + list_add_tail(&fd->link, &parent->child_list); + fd->is_strict_mode = parent->is_strict_mode; + fd->parent_scope_level = parent->scope_level; + } + + fd->is_eval = is_eval; + fd->is_func_expr = is_func_expr; + js_dbuf_init(ctx, &fd->byte_code); + fd->last_opcode_pos = -1; + fd->func_name = JS_ATOM_NULL; + fd->var_object_idx = -1; + fd->arg_var_object_idx = -1; + fd->arguments_var_idx = -1; + fd->arguments_arg_idx = -1; + fd->func_var_idx = -1; + fd->eval_ret_idx = -1; + fd->this_var_idx = -1; + fd->new_target_var_idx = -1; + fd->this_active_func_var_idx = -1; + fd->home_object_var_idx = -1; + + /* XXX: should distinguish arg, var and var object and body scopes */ + fd->scopes = fd->def_scope_array; + fd->scope_size = countof(fd->def_scope_array); + fd->scope_count = 1; + fd->scopes[0].first = -1; + fd->scopes[0].parent = -1; + fd->scope_level = 0; /* 0: var/arg scope */ + fd->scope_first = -1; + fd->body_scope = -1; + + fd->filename = JS_NewAtom(ctx, filename); + fd->line_num = line_num; + fd->col_num = col_num; + + js_dbuf_init(ctx, &fd->pc2line); + //fd->pc2line_last_line_num = line_num; + //fd->pc2line_last_pc = 0; + + fd->ic = init_ic(ctx); + return fd; +} + +static void free_bytecode_atoms(JSRuntime *rt, + const uint8_t *bc_buf, int bc_len, + BOOL use_short_opcodes) +{ + int pos, len, op; + JSAtom atom; + const JSOpCode *oi; + + pos = 0; + while (pos < bc_len) { + op = bc_buf[pos]; + if (use_short_opcodes) + oi = &short_opcode_info(op); + else + oi = &opcode_info[op]; + + len = oi->size; + switch(oi->fmt) { + case OP_FMT_atom: + case OP_FMT_atom_u8: + case OP_FMT_atom_u16: + case OP_FMT_atom_label_u8: + case OP_FMT_atom_label_u16: + atom = get_u32(bc_buf + pos + 1); + JS_FreeAtomRT(rt, atom); + break; + default: + break; + } + pos += len; + } +} + +static void js_free_function_def(JSContext *ctx, JSFunctionDef *fd) +{ + int i; + struct list_head *el, *el1; + + /* free the child functions */ + list_for_each_safe(el, el1, &fd->child_list) { + JSFunctionDef *fd1; + fd1 = list_entry(el, JSFunctionDef, link); + js_free_function_def(ctx, fd1); + } + + free_bytecode_atoms(ctx->rt, fd->byte_code.buf, fd->byte_code.size, + fd->use_short_opcodes); + dbuf_free(&fd->byte_code); + js_free(ctx, fd->jump_slots); + js_free(ctx, fd->label_slots); + js_free(ctx, fd->source_loc_slots); + + /* free ic */ + if (fd->ic) + free_ic(ctx->rt, fd->ic); + + for(i = 0; i < fd->cpool_count; i++) { + JS_FreeValue(ctx, fd->cpool[i]); + } + js_free(ctx, fd->cpool); + + JS_FreeAtom(ctx, fd->func_name); + + for(i = 0; i < fd->var_count; i++) { + JS_FreeAtom(ctx, fd->vars[i].var_name); + } + js_free(ctx, fd->vars); + js_free(ctx, fd->vars_htab); // XXX can probably be freed earlier? + for(i = 0; i < fd->arg_count; i++) { + JS_FreeAtom(ctx, fd->args[i].var_name); + } + js_free(ctx, fd->args); + + for(i = 0; i < fd->global_var_count; i++) { + JS_FreeAtom(ctx, fd->global_vars[i].var_name); + } + js_free(ctx, fd->global_vars); + + for(i = 0; i < fd->closure_var_count; i++) { + JSClosureVar *cv = &fd->closure_var[i]; + JS_FreeAtom(ctx, cv->var_name); + } + js_free(ctx, fd->closure_var); + + if (fd->scopes != fd->def_scope_array) + js_free(ctx, fd->scopes); + + JS_FreeAtom(ctx, fd->filename); + dbuf_free(&fd->pc2line); + + js_free(ctx, fd->source); + + if (fd->parent) { + /* remove in parent list */ + list_del(&fd->link); + } + js_free(ctx, fd); +} + +#ifdef DUMP_BYTECODE +static const char *skip_lines(const char *p, int n) { + while (p && n-- > 0 && *p) { + while (*p && *p++ != '\n') + continue; + } + return p; +} + +static void print_lines(const char *source, int line, int line1) { + const char *s = source; + const char *p = skip_lines(s, line); + if (p && *p) { + while (line++ < line1) { + p = skip_lines(s = p, 1); + printf(";; %.*s", (int)(p - s), s); + if (!*p) { + if (p[-1] != '\n') + printf("\n"); + break; + } + } + } +} + +static void dump_byte_code(JSContext *ctx, int pass, + const uint8_t *tab, int len, + const JSVarDef *args, int arg_count, + const JSVarDef *vars, int var_count, + const JSClosureVar *closure_var, int closure_var_count, + const JSValue *cpool, uint32_t cpool_count, + const char *source, int line_num, + const LabelSlot *label_slots, JSFunctionBytecode *b, + int start_pos) +{ + const JSOpCode *oi; + int pos, pos_next, op, size, idx, addr, line, line1, in_source; + uint8_t *bits = js_mallocz(ctx, len * sizeof(*bits)); + BOOL use_short_opcodes = (b != NULL); + + if (start_pos != 0 || bits == NULL) + goto no_labels; + + /* scan for jump targets */ + for (pos = 0; pos < len; pos = pos_next) { + op = tab[pos]; + if (use_short_opcodes) + oi = &short_opcode_info(op); + else + oi = &opcode_info[op]; + pos_next = pos + oi->size; + if (op < OP_COUNT) { + switch (oi->fmt) { + case OP_FMT_label8: + pos++; + addr = (int8_t)tab[pos]; + goto has_addr; + case OP_FMT_label16: + pos++; + addr = (int16_t)get_u16(tab + pos); + goto has_addr; + case OP_FMT_atom_label_u8: + case OP_FMT_atom_label_u16: + pos += 4; + /* fall thru */ + case OP_FMT_label: + case OP_FMT_label_u16: + pos++; + addr = get_u32(tab + pos); + goto has_addr; + has_addr: + if (pass == 1) + addr = label_slots[addr].pos; + if (pass == 2) + addr = label_slots[addr].pos2; + if (pass == 3) + addr += pos; + if (addr >= 0 && addr < len) + bits[addr] |= 1; + break; + } + } + } + no_labels: + in_source = 0; + if (source) { + /* Always print first line: needed if single line */ + print_lines(source, 0, 1); + in_source = 1; + } + line1 = line = 1; + pos = 0; + while (pos < len) { + op = tab[pos]; + if (source) { + if (b) { + int col1; + line1 = find_line_num(ctx, b, pos, &col1) - line_num + 1; + } else if (op == OP_source_loc) { + line1 = get_u32(tab + pos + 1) - line_num + 1; + } + if (line1 > line) { + if (!in_source) + printf("\n"); + in_source = 1; + print_lines(source, line, line1); + line = line1; + //bits[pos] |= 2; + } + } + if (in_source) + printf("\n"); + in_source = 0; + if (op >= OP_COUNT) { + printf("invalid opcode (0x%02x)\n", op); + pos++; + continue; + } + if (use_short_opcodes) + oi = &short_opcode_info(op); + else + oi = &opcode_info[op]; + size = oi->size; + if (pos + size > len) { + printf("truncated opcode (0x%02x)\n", op); + break; + } +#ifdef DUMP_BYTECODE_HEX + if (check_dump_flag(ctx->rt, DUMP_BYTECODE_HEX)) { + int i, x, x0; + x = x0 = printf("%5d ", pos); + for (i = 0; i < size; i++) { + if (i == 6) { + printf("\n%*s", x = x0, ""); + } + x += printf(" %02X", tab[pos + i]); + } + printf("%*s", x0 + 20 - x, ""); + } +#endif + if (bits && bits[pos]) { + printf("%5d: ", pos); + } else { + printf(" "); + } + printf("%-15s", oi->name); /* align opcode arguments */ + pos++; + switch(oi->fmt) { + case OP_FMT_none_int: + printf(" %d", op - OP_push_0); + break; + case OP_FMT_npopx: + printf(" %d", op - OP_call0); + break; + case OP_FMT_u8: + printf(" %u", get_u8(tab + pos)); + break; + case OP_FMT_i8: + printf(" %d", get_i8(tab + pos)); + break; + case OP_FMT_u16: + case OP_FMT_npop: + printf(" %u", get_u16(tab + pos)); + break; + case OP_FMT_npop_u16: + printf(" %u,%u", get_u16(tab + pos), get_u16(tab + pos + 2)); + break; + case OP_FMT_i16: + printf(" %d", get_i16(tab + pos)); + break; + case OP_FMT_i32: + printf(" %d", get_i32(tab + pos)); + break; + case OP_FMT_u32: + printf(" %u", get_u32(tab + pos)); + break; + case OP_FMT_u32x2: + printf(" %u:%u", get_u32(tab + pos), get_u32(tab + pos + 4)); + break; + case OP_FMT_label8: + addr = get_i8(tab + pos); + goto has_addr1; + case OP_FMT_label16: + addr = get_i16(tab + pos); + goto has_addr1; + case OP_FMT_label: + case OP_FMT_label_u16: + addr = get_u32(tab + pos); + has_addr1: + if (pass == 1) + printf(" %d:%u", addr, label_slots[addr].pos); + if (pass == 2) + printf(" %d:%u", addr, label_slots[addr].pos2); + if (pass == 3) { + if (start_pos) + printf(" %04x", addr + pos + start_pos); + else + printf(" %d", addr + pos); + } + if (oi->fmt == OP_FMT_label_u16) + printf(",%u", get_u16(tab + pos + 4)); + break; + case OP_FMT_const8: + idx = get_u8(tab + pos); + goto has_pool_idx; + case OP_FMT_const: + idx = get_u32(tab + pos); + goto has_pool_idx; + has_pool_idx: + printf(" %-4u ; ", idx); + if (idx < cpool_count) { + JS_DumpValue(ctx->rt, cpool[idx]); + } + break; + case OP_FMT_atom: + printf(" "); + print_atom(ctx, get_u32(tab + pos)); + break; + case OP_FMT_atom_u8: + printf(" "); + print_atom(ctx, get_u32(tab + pos)); + printf(",%d", get_u8(tab + pos + 4)); + break; + case OP_FMT_atom_u16: + printf(" "); + print_atom(ctx, get_u32(tab + pos)); + printf(",%d", get_u16(tab + pos + 4)); + break; + case OP_FMT_atom_label_u8: + case OP_FMT_atom_label_u16: + printf(" "); + print_atom(ctx, get_u32(tab + pos)); + addr = get_u32(tab + pos + 4); + if (pass == 1) + printf(",%u:%u", addr, label_slots[addr].pos); + if (pass == 2) + printf(",%u:%u", addr, label_slots[addr].pos2); + if (pass == 3) + printf(",%u", addr + pos + 4); + if (oi->fmt == OP_FMT_atom_label_u8) + printf(",%u", get_u8(tab + pos + 8)); + else + printf(",%u", get_u16(tab + pos + 8)); + break; + case OP_FMT_none_loc: + if (op == OP_get_loc0_loc1) { + printf(" 0, 1 ; "); + if (var_count > 0) + print_atom(ctx, vars[0].var_name); + if (var_count > 1) + print_atom(ctx, vars[1].var_name); + } else { + idx = (op - OP_get_loc0) % 4; + goto has_loc; + } + break; + case OP_FMT_loc8: + idx = get_u8(tab + pos); + goto has_loc; + case OP_FMT_loc: + idx = get_u16(tab + pos); + has_loc: + printf(" %-4d ; ", idx); + if (idx < var_count) { + print_atom(ctx, vars[idx].var_name); + } + break; + case OP_FMT_none_arg: + idx = (op - OP_get_arg0) % 4; + goto has_arg; + case OP_FMT_arg: + idx = get_u16(tab + pos); + has_arg: + printf(" %-4d ; ", idx); + if (idx < arg_count) { + print_atom(ctx, args[idx].var_name); + } + break; + case OP_FMT_none_var_ref: + idx = (op - OP_get_var_ref0) % 4; + goto has_var_ref; + case OP_FMT_var_ref: + idx = get_u16(tab + pos); + has_var_ref: + printf(" %-4d ; ", idx); + if (idx < closure_var_count) { + print_atom(ctx, closure_var[idx].var_name); + } + break; + default: + break; + } + printf("\n"); + pos += oi->size - 1; + } + if (source) { + if (!in_source) + printf("\n"); + print_lines(source, line, INT32_MAX); + } + js_free(ctx, bits); +} + +// caveat emptor: intended to be called during execution of bytecode +// and only works for pass3 bytecode +static __maybe_unused void dump_single_byte_code(JSContext *ctx, + const uint8_t *pc, + JSFunctionBytecode *b, + int start_pos) +{ + JSVarDef *args, *vars; + + args = vars = b->vardefs; + if (vars) + vars = &vars[b->arg_count]; + + dump_byte_code(ctx, /*pass*/3, pc, short_opcode_info(*pc).size, + args, b->arg_count, vars, b->var_count, + b->closure_var, b->closure_var_count, + b->cpool, b->cpool_count, + NULL, b->line_num, + NULL, b, start_pos); +} + +static __maybe_unused void print_func_name(JSFunctionBytecode *b) +{ + print_lines(b->source, 0, 1); +} + +static __maybe_unused void dump_pc2line(JSContext *ctx, + const uint8_t *buf, int len, + int line_num, int col_num) +{ + const uint8_t *p_end, *p_next, *p; + int pc, v; + unsigned int op; + + if (len <= 0) + return; + + printf("%5s %5s %5s\n", "PC", "LINE", "COLUMN"); + + p = buf; + p_end = buf + len; + pc = 0; + while (p < p_end) { + op = *p++; + if (op == 0) { + v = utf8_decode_len(p, p_end - p, &p_next); + if (v < 0) + goto fail; + pc += v; + p = p_next; + v = utf8_decode_len(p, p_end - p, &p_next); + if (v < 0) + goto fail; + if (v & 1) { + v = -(v >> 1) - 1; + } else { + v = v >> 1; + } + line_num += v; + p = p_next; + } else { + op -= PC2LINE_OP_FIRST; + pc += (op / PC2LINE_RANGE); + line_num += (op % PC2LINE_RANGE) + PC2LINE_BASE; + } + v = utf8_decode_len(p, p_end - p, &p_next); + if (v < 0) + goto fail; + if (v & 1) { + v = -(v >> 1) - 1; + } else { + v = v >> 1; + } + col_num += v; + p = p_next; + printf("%5d %5d %5d\n", pc, line_num, col_num); + } + return; +fail: + printf("invalid pc2line encode pos=%d\n", (int)(p - buf)); +} + +static __maybe_unused void js_dump_function_bytecode(JSContext *ctx, JSFunctionBytecode *b) +{ + int i; + char atom_buf[ATOM_GET_STR_BUF_SIZE]; + const char *str; + + if (b->filename != JS_ATOM_NULL) { + str = JS_AtomGetStr(ctx, atom_buf, sizeof(atom_buf), b->filename); + printf("%s:%d:%d: ", str, b->line_num, b->col_num); + } + + str = JS_AtomGetStr(ctx, atom_buf, sizeof(atom_buf), b->func_name); + printf("function: %s%s\n", &"*"[b->func_kind != JS_FUNC_GENERATOR], str); + printf(" mode: %s\n", b->is_strict_mode ? "strict" : "sloppy"); + if (b->arg_count && b->vardefs) { + printf(" args:"); + for(i = 0; i < b->arg_count; i++) { + printf(" %s", JS_AtomGetStr(ctx, atom_buf, sizeof(atom_buf), + b->vardefs[i].var_name)); + } + printf("\n"); + } + if (b->var_count && b->vardefs) { + printf(" locals:\n"); + for(i = 0; i < b->var_count; i++) { + JSVarDef *vd = &b->vardefs[b->arg_count + i]; + printf("%5d: %s %s", i, + vd->var_kind == JS_VAR_CATCH ? "catch" : + (vd->var_kind == JS_VAR_FUNCTION_DECL || + vd->var_kind == JS_VAR_NEW_FUNCTION_DECL) ? "function" : + vd->is_const ? "const" : + vd->is_lexical ? "let" : "var", + JS_AtomGetStr(ctx, atom_buf, sizeof(atom_buf), vd->var_name)); + if (vd->scope_level) + printf(" [level:%d next:%d]", vd->scope_level, vd->scope_next); + printf("\n"); + } + } + if (b->closure_var_count) { + printf(" closure vars:\n"); + for(i = 0; i < b->closure_var_count; i++) { + JSClosureVar *cv = &b->closure_var[i]; + printf("%5d: %s %s:%s%d %s\n", i, + JS_AtomGetStr(ctx, atom_buf, sizeof(atom_buf), cv->var_name), + cv->is_local ? "local" : "parent", + cv->is_arg ? "arg" : "loc", cv->var_idx, + cv->is_const ? "const" : + cv->is_lexical ? "let" : "var"); + } + } + printf(" stack_size: %d\n", b->stack_size); + printf(" opcodes:\n"); + dump_byte_code(ctx, 3, b->byte_code_buf, b->byte_code_len, + b->vardefs, b->arg_count, + b->vardefs ? b->vardefs + b->arg_count : NULL, b->var_count, + b->closure_var, b->closure_var_count, + b->cpool, b->cpool_count, + b->source, b->line_num, NULL, b, 0); +#ifdef DUMP_BYTECODE_PC2LINE + if (check_dump_flag(ctx->rt, DUMP_BYTECODE_PC2LINE)) + dump_pc2line(ctx, b->pc2line_buf, b->pc2line_len, b->line_num, b->col_num); +#endif + printf("\n"); +} +#endif + +static int add_closure_var(JSContext *ctx, JSFunctionDef *s, + BOOL is_local, BOOL is_arg, + int var_idx, JSAtom var_name, + BOOL is_const, BOOL is_lexical, + JSVarKindEnum var_kind) +{ + JSClosureVar *cv; + + /* the closure variable indexes are currently stored on 16 bits */ + if (s->closure_var_count >= JS_MAX_LOCAL_VARS) { + // XXX: add_closure_var() should take JSParseState *s and use js_parse_error + JS_ThrowSyntaxError(ctx, "too many closure variables used (only %d allowed)", + JS_MAX_LOCAL_VARS - 1); + return -1; + } + + if (js_resize_array(ctx, (void **)&s->closure_var, + sizeof(s->closure_var[0]), + &s->closure_var_size, s->closure_var_count + 1)) + return -1; + cv = &s->closure_var[s->closure_var_count++]; + cv->is_local = is_local; + cv->is_arg = is_arg; + cv->is_const = is_const; + cv->is_lexical = is_lexical; + cv->var_kind = var_kind; + cv->var_idx = var_idx; + cv->var_name = JS_DupAtom(ctx, var_name); + return s->closure_var_count - 1; +} + +static int find_closure_var(JSContext *ctx, JSFunctionDef *s, + JSAtom var_name) +{ + int i; + for(i = 0; i < s->closure_var_count; i++) { + JSClosureVar *cv = &s->closure_var[i]; + if (cv->var_name == var_name) + return i; + } + return -1; +} + +/* 'fd' must be a parent of 's'. Create in 's' a closure referencing a + local variable (is_local = TRUE) or a closure (is_local = FALSE) in + 'fd' */ +static int get_closure_var2(JSContext *ctx, JSFunctionDef *s, + JSFunctionDef *fd, BOOL is_local, + BOOL is_arg, int var_idx, JSAtom var_name, + BOOL is_const, BOOL is_lexical, + JSVarKindEnum var_kind) +{ + int i; + + if (fd != s->parent) { + var_idx = get_closure_var2(ctx, s->parent, fd, is_local, + is_arg, var_idx, var_name, + is_const, is_lexical, var_kind); + if (var_idx < 0) + return -1; + is_local = FALSE; + } + for(i = 0; i < s->closure_var_count; i++) { + JSClosureVar *cv = &s->closure_var[i]; + if (cv->var_idx == var_idx && cv->is_arg == is_arg && + cv->is_local == is_local) + return i; + } + return add_closure_var(ctx, s, is_local, is_arg, var_idx, var_name, + is_const, is_lexical, var_kind); +} + +static int get_closure_var(JSContext *ctx, JSFunctionDef *s, + JSFunctionDef *fd, BOOL is_arg, + int var_idx, JSAtom var_name, + BOOL is_const, BOOL is_lexical, + JSVarKindEnum var_kind) +{ + return get_closure_var2(ctx, s, fd, TRUE, is_arg, + var_idx, var_name, is_const, is_lexical, + var_kind); +} + +static int get_with_scope_opcode(int op) +{ + if (op == OP_scope_get_var_undef) + return OP_with_get_var; + else + return OP_with_get_var + (op - OP_scope_get_var); +} + +static BOOL can_opt_put_ref_value(const uint8_t *bc_buf, int pos) +{ + int opcode = bc_buf[pos]; + return (bc_buf[pos + 1] == OP_put_ref_value && + (opcode == OP_insert3 || + opcode == OP_perm4 || + opcode == OP_nop || + opcode == OP_rot3l)); +} + +static BOOL can_opt_put_global_ref_value(const uint8_t *bc_buf, int pos) +{ + int opcode = bc_buf[pos]; + return (bc_buf[pos + 1] == OP_put_ref_value && + (opcode == OP_insert3 || + opcode == OP_perm4 || + opcode == OP_nop || + opcode == OP_rot3l)); +} + +static int optimize_scope_make_ref(JSContext *ctx, JSFunctionDef *s, + DynBuf *bc, uint8_t *bc_buf, + LabelSlot *ls, int pos_next, + int get_op, int var_idx) +{ + int label_pos, end_pos, pos; + + /* XXX: should optimize `loc(a) += expr` as `expr add_loc(a)` + but only if expr does not modify `a`. + should scan the code between pos_next and label_pos + for operations that can potentially change `a`: + OP_scope_make_ref(a), function calls, jumps and gosub. + */ + /* replace the reference get/put with normal variable + accesses */ + if (bc_buf[pos_next] == OP_get_ref_value) { + dbuf_putc(bc, get_op); + dbuf_put_u16(bc, var_idx); + pos_next++; + } + /* remove the OP_label to make room for replacement */ + /* label should have a refcount of 0 anyway */ + /* XXX: should avoid this patch by inserting nops in phase 1 */ + label_pos = ls->pos; + pos = label_pos - 5; + assert(bc_buf[pos] == OP_label); + /* label points to an instruction pair: + - insert3 / put_ref_value + - perm4 / put_ref_value + - rot3l / put_ref_value + - nop / put_ref_value + */ + end_pos = label_pos + 2; + if (bc_buf[label_pos] == OP_insert3) + bc_buf[pos++] = OP_dup; + bc_buf[pos] = get_op + 1; + put_u16(bc_buf + pos + 1, var_idx); + pos += 3; + /* pad with OP_nop */ + while (pos < end_pos) + bc_buf[pos++] = OP_nop; + return pos_next; +} + +static int optimize_scope_make_global_ref(JSContext *ctx, JSFunctionDef *s, + DynBuf *bc, uint8_t *bc_buf, + LabelSlot *ls, int pos_next, + JSAtom var_name) +{ + int label_pos, end_pos, pos, op; + BOOL is_strict_mode = s->is_strict_mode; + + /* replace the reference get/put with normal variable + accesses */ + if (is_strict_mode) { + /* need to check if the variable exists before evaluating the right + expression */ + /* XXX: need an extra OP_true if destructuring an array */ + dbuf_putc(bc, OP_check_var); + dbuf_put_u32(bc, JS_DupAtom(ctx, var_name)); + } else { + /* XXX: need 2 extra OP_true if destructuring an array */ + } + if (bc_buf[pos_next] == OP_get_ref_value) { + dbuf_putc(bc, OP_get_var); + dbuf_put_u32(bc, JS_DupAtom(ctx, var_name)); + pos_next++; + } + /* remove the OP_label to make room for replacement */ + /* label should have a refcount of 0 anyway */ + /* XXX: should have emitted several OP_nop to avoid this kludge */ + label_pos = ls->pos; + pos = label_pos - 5; + assert(bc_buf[pos] == OP_label); + end_pos = label_pos + 2; + op = bc_buf[label_pos]; + if (is_strict_mode) { + if (op != OP_nop) { + switch(op) { + case OP_insert3: + op = OP_insert2; + break; + case OP_perm4: + op = OP_perm3; + break; + case OP_rot3l: + op = OP_swap; + break; + default: + abort(); + } + bc_buf[pos++] = op; + } + } else { + if (op == OP_insert3) + bc_buf[pos++] = OP_dup; + } + if (is_strict_mode) { + bc_buf[pos] = OP_put_var_strict; + /* XXX: need 1 extra OP_drop if destructuring an array */ + } else { + bc_buf[pos] = OP_put_var; + /* XXX: need 2 extra OP_drop if destructuring an array */ + } + put_u32(bc_buf + pos + 1, JS_DupAtom(ctx, var_name)); + pos += 5; + /* pad with OP_nop */ + while (pos < end_pos) + bc_buf[pos++] = OP_nop; + return pos_next; +} + +static int add_var_this(JSContext *ctx, JSFunctionDef *fd) +{ + int idx; + idx = add_var(ctx, fd, JS_ATOM_this); + if (idx >= 0 && fd->is_derived_class_constructor) { + JSVarDef *vd = &fd->vars[idx]; + /* XXX: should have is_this flag or var type */ + vd->is_lexical = 1; /* used to trigger 'uninitialized' checks + in a derived class constructor */ + } + return idx; +} + +static int resolve_pseudo_var(JSContext *ctx, JSFunctionDef *s, + JSAtom var_name) +{ + int var_idx; + + if (!s->has_this_binding) + return -1; + switch(var_name) { + case JS_ATOM_home_object: + /* 'home_object' pseudo variable */ + if (s->home_object_var_idx < 0) + s->home_object_var_idx = add_var(ctx, s, var_name); + var_idx = s->home_object_var_idx; + break; + case JS_ATOM_this_active_func: + /* 'this.active_func' pseudo variable */ + if (s->this_active_func_var_idx < 0) + s->this_active_func_var_idx = add_var(ctx, s, var_name); + var_idx = s->this_active_func_var_idx; + break; + case JS_ATOM_new_target: + /* 'new.target' pseudo variable */ + if (s->new_target_var_idx < 0) + s->new_target_var_idx = add_var(ctx, s, var_name); + var_idx = s->new_target_var_idx; + break; + case JS_ATOM_this: + /* 'this' pseudo variable */ + if (s->this_var_idx < 0) + s->this_var_idx = add_var_this(ctx, s); + var_idx = s->this_var_idx; + break; + default: + var_idx = -1; + break; + } + return var_idx; +} + +/* test if 'var_name' is in the variable object on the stack. If is it + the case, handle it and jump to 'label_done' */ +static void var_object_test(JSContext *ctx, JSFunctionDef *s, + JSAtom var_name, int op, DynBuf *bc, + int *plabel_done, BOOL is_with) +{ + dbuf_putc(bc, get_with_scope_opcode(op)); + dbuf_put_u32(bc, JS_DupAtom(ctx, var_name)); + *plabel_done = new_label_fd(s, *plabel_done); + dbuf_put_u32(bc, *plabel_done); + dbuf_putc(bc, is_with); + update_label(s, *plabel_done, 1); + s->jump_size++; +} + +/* return the position of the next opcode */ +static int resolve_scope_var(JSContext *ctx, JSFunctionDef *s, + JSAtom var_name, int scope_level, int op, + DynBuf *bc, uint8_t *bc_buf, + LabelSlot *ls, int pos_next) +{ + int idx, var_idx, is_put; + int label_done; + JSFunctionDef *fd; + JSVarDef *vd; + BOOL is_pseudo_var, is_arg_scope; + + label_done = -1; + + /* XXX: could be simpler to use a specific function to + resolve the pseudo variables */ + is_pseudo_var = (var_name == JS_ATOM_home_object || + var_name == JS_ATOM_this_active_func || + var_name == JS_ATOM_new_target || + var_name == JS_ATOM_this); + + /* resolve local scoped variables */ + var_idx = -1; + for (idx = s->scopes[scope_level].first; idx >= 0;) { + vd = &s->vars[idx]; + if (vd->var_name == var_name) { + if (op == OP_scope_put_var || op == OP_scope_make_ref) { + if (vd->is_const) { + dbuf_putc(bc, OP_throw_error); + dbuf_put_u32(bc, JS_DupAtom(ctx, var_name)); + dbuf_putc(bc, JS_THROW_VAR_RO); + goto done; + } + } + var_idx = idx; + break; + } else + if (vd->var_name == JS_ATOM__with_ && !is_pseudo_var) { + dbuf_putc(bc, OP_get_loc); + dbuf_put_u16(bc, idx); + var_object_test(ctx, s, var_name, op, bc, &label_done, 1); + } + idx = vd->scope_next; + } + is_arg_scope = (idx == ARG_SCOPE_END); + if (var_idx < 0) { + /* argument scope: variables are not visible but pseudo + variables are visible */ + if (!is_arg_scope) { + var_idx = find_var(ctx, s, var_name); + } + + if (var_idx < 0 && is_pseudo_var) + var_idx = resolve_pseudo_var(ctx, s, var_name); + + if (var_idx < 0 && var_name == JS_ATOM_arguments && + s->has_arguments_binding) { + /* 'arguments' pseudo variable */ + var_idx = add_arguments_var(ctx, s); + } + if (var_idx < 0 && s->is_func_expr && var_name == s->func_name) { + /* add a new variable with the function name */ + var_idx = add_func_var(ctx, s, var_name); + } + } + if (var_idx >= 0) { + if ((op == OP_scope_put_var || op == OP_scope_make_ref) && + !(var_idx & ARGUMENT_VAR_OFFSET) && + s->vars[var_idx].is_const) { + /* only happens when assigning a function expression name + in strict mode */ + dbuf_putc(bc, OP_throw_error); + dbuf_put_u32(bc, JS_DupAtom(ctx, var_name)); + dbuf_putc(bc, JS_THROW_VAR_RO); + goto done; + } + /* OP_scope_put_var_init is only used to initialize a + lexical variable, so it is never used in a with or var object. It + can be used with a closure (module global variable case). */ + switch (op) { + case OP_scope_make_ref: + if (!(var_idx & ARGUMENT_VAR_OFFSET) && + s->vars[var_idx].var_kind == JS_VAR_FUNCTION_NAME) { + /* Create a dummy object reference for the func_var */ + dbuf_putc(bc, OP_object); + dbuf_putc(bc, OP_get_loc); + dbuf_put_u16(bc, var_idx); + dbuf_putc(bc, OP_define_field); + dbuf_put_u32(bc, JS_DupAtom(ctx, var_name)); + dbuf_putc(bc, OP_push_atom_value); + dbuf_put_u32(bc, JS_DupAtom(ctx, var_name)); + } else + if (label_done == -1 && can_opt_put_ref_value(bc_buf, ls->pos)) { + int get_op; + if (var_idx & ARGUMENT_VAR_OFFSET) { + get_op = OP_get_arg; + var_idx -= ARGUMENT_VAR_OFFSET; + } else { + if (s->vars[var_idx].is_lexical) + get_op = OP_get_loc_check; + else + get_op = OP_get_loc; + } + pos_next = optimize_scope_make_ref(ctx, s, bc, bc_buf, ls, + pos_next, get_op, var_idx); + } else { + /* Create a dummy object with a named slot that is + a reference to the local variable */ + if (var_idx & ARGUMENT_VAR_OFFSET) { + dbuf_putc(bc, OP_make_arg_ref); + dbuf_put_u32(bc, JS_DupAtom(ctx, var_name)); + dbuf_put_u16(bc, var_idx - ARGUMENT_VAR_OFFSET); + } else { + dbuf_putc(bc, OP_make_loc_ref); + dbuf_put_u32(bc, JS_DupAtom(ctx, var_name)); + dbuf_put_u16(bc, var_idx); + } + } + break; + case OP_scope_get_ref: + dbuf_putc(bc, OP_undefined); + /* fall thru */ + case OP_scope_get_var_undef: + case OP_scope_get_var: + case OP_scope_put_var: + case OP_scope_put_var_init: + is_put = (op == OP_scope_put_var || op == OP_scope_put_var_init); + if (var_idx & ARGUMENT_VAR_OFFSET) { + dbuf_putc(bc, OP_get_arg + is_put); + dbuf_put_u16(bc, var_idx - ARGUMENT_VAR_OFFSET); + } else { + if (is_put) { + if (s->vars[var_idx].is_lexical) { + if (op == OP_scope_put_var_init) { + /* 'this' can only be initialized once */ + if (var_name == JS_ATOM_this) + dbuf_putc(bc, OP_put_loc_check_init); + else + dbuf_putc(bc, OP_put_loc); + } else { + dbuf_putc(bc, OP_put_loc_check); + } + } else { + dbuf_putc(bc, OP_put_loc); + } + } else { + if (s->vars[var_idx].is_lexical) { + dbuf_putc(bc, OP_get_loc_check); + } else { + dbuf_putc(bc, OP_get_loc); + } + } + dbuf_put_u16(bc, var_idx); + } + break; + case OP_scope_delete_var: + dbuf_putc(bc, OP_push_false); + break; + } + goto done; + } + /* check eval object */ + if (!is_arg_scope && s->var_object_idx >= 0 && !is_pseudo_var) { + dbuf_putc(bc, OP_get_loc); + dbuf_put_u16(bc, s->var_object_idx); + var_object_test(ctx, s, var_name, op, bc, &label_done, 0); + } + /* check eval object in argument scope */ + if (s->arg_var_object_idx >= 0 && !is_pseudo_var) { + dbuf_putc(bc, OP_get_loc); + dbuf_put_u16(bc, s->arg_var_object_idx); + var_object_test(ctx, s, var_name, op, bc, &label_done, 0); + } + + /* check parent scopes */ + for (fd = s; fd->parent;) { + scope_level = fd->parent_scope_level; + fd = fd->parent; + for (idx = fd->scopes[scope_level].first; idx >= 0;) { + vd = &fd->vars[idx]; + if (vd->var_name == var_name) { + if (op == OP_scope_put_var || op == OP_scope_make_ref) { + if (vd->is_const) { + dbuf_putc(bc, OP_throw_error); + dbuf_put_u32(bc, JS_DupAtom(ctx, var_name)); + dbuf_putc(bc, JS_THROW_VAR_RO); + goto done; + } + } + var_idx = idx; + break; + } else if (vd->var_name == JS_ATOM__with_ && !is_pseudo_var) { + vd->is_captured = 1; + idx = get_closure_var(ctx, s, fd, FALSE, idx, vd->var_name, FALSE, FALSE, JS_VAR_NORMAL); + if (idx >= 0) { + dbuf_putc(bc, OP_get_var_ref); + dbuf_put_u16(bc, idx); + var_object_test(ctx, s, var_name, op, bc, &label_done, 1); + } + } + idx = vd->scope_next; + } + is_arg_scope = (idx == ARG_SCOPE_END); + if (var_idx >= 0) + break; + + if (!is_arg_scope) { + var_idx = find_var(ctx, fd, var_name); + if (var_idx >= 0) + break; + } + if (is_pseudo_var) { + var_idx = resolve_pseudo_var(ctx, fd, var_name); + if (var_idx >= 0) + break; + } + if (var_name == JS_ATOM_arguments && fd->has_arguments_binding) { + var_idx = add_arguments_var(ctx, fd); + break; + } + if (fd->is_func_expr && fd->func_name == var_name) { + /* add a new variable with the function name */ + var_idx = add_func_var(ctx, fd, var_name); + break; + } + + /* check eval object */ + if (!is_arg_scope && fd->var_object_idx >= 0 && !is_pseudo_var) { + vd = &fd->vars[fd->var_object_idx]; + vd->is_captured = 1; + idx = get_closure_var(ctx, s, fd, FALSE, + fd->var_object_idx, vd->var_name, + FALSE, FALSE, JS_VAR_NORMAL); + dbuf_putc(bc, OP_get_var_ref); + dbuf_put_u16(bc, idx); + var_object_test(ctx, s, var_name, op, bc, &label_done, 0); + } + + /* check eval object in argument scope */ + if (fd->arg_var_object_idx >= 0 && !is_pseudo_var) { + vd = &fd->vars[fd->arg_var_object_idx]; + vd->is_captured = 1; + idx = get_closure_var(ctx, s, fd, FALSE, + fd->arg_var_object_idx, vd->var_name, + FALSE, FALSE, JS_VAR_NORMAL); + dbuf_putc(bc, OP_get_var_ref); + dbuf_put_u16(bc, idx); + var_object_test(ctx, s, var_name, op, bc, &label_done, 0); + } + + if (fd->is_eval) + break; /* it it necessarily the top level function */ + } + + /* check direct eval scope (in the closure of the eval function + which is necessarily at the top level) */ + if (!fd) + fd = s; + if (var_idx < 0 && fd->is_eval) { + int idx1; + for (idx1 = 0; idx1 < fd->closure_var_count; idx1++) { + JSClosureVar *cv = &fd->closure_var[idx1]; + if (var_name == cv->var_name) { + if (fd != s) { + idx = get_closure_var2(ctx, s, fd, + FALSE, + cv->is_arg, idx1, + cv->var_name, cv->is_const, + cv->is_lexical, cv->var_kind); + } else { + idx = idx1; + } + goto has_idx; + } else if ((cv->var_name == JS_ATOM__var_ || + cv->var_name == JS_ATOM__arg_var_ || + cv->var_name == JS_ATOM__with_) && !is_pseudo_var) { + int is_with = (cv->var_name == JS_ATOM__with_); + if (fd != s) { + idx = get_closure_var2(ctx, s, fd, + FALSE, + cv->is_arg, idx1, + cv->var_name, FALSE, FALSE, + JS_VAR_NORMAL); + } else { + idx = idx1; + } + dbuf_putc(bc, OP_get_var_ref); + dbuf_put_u16(bc, idx); + var_object_test(ctx, s, var_name, op, bc, &label_done, is_with); + } + } + } + + if (var_idx >= 0) { + /* find the corresponding closure variable */ + if (var_idx & ARGUMENT_VAR_OFFSET) { + fd->args[var_idx - ARGUMENT_VAR_OFFSET].is_captured = 1; + idx = get_closure_var(ctx, s, fd, + TRUE, var_idx - ARGUMENT_VAR_OFFSET, + var_name, FALSE, FALSE, JS_VAR_NORMAL); + } else { + fd->vars[var_idx].is_captured = 1; + idx = get_closure_var(ctx, s, fd, + FALSE, var_idx, + var_name, + fd->vars[var_idx].is_const, + fd->vars[var_idx].is_lexical, + fd->vars[var_idx].var_kind); + } + if (idx >= 0) { + has_idx: + if ((op == OP_scope_put_var || op == OP_scope_make_ref) && + s->closure_var[idx].is_const) { + dbuf_putc(bc, OP_throw_error); + dbuf_put_u32(bc, JS_DupAtom(ctx, var_name)); + dbuf_putc(bc, JS_THROW_VAR_RO); + goto done; + } + switch (op) { + case OP_scope_make_ref: + if (s->closure_var[idx].var_kind == JS_VAR_FUNCTION_NAME) { + /* Create a dummy object reference for the func_var */ + dbuf_putc(bc, OP_object); + dbuf_putc(bc, OP_get_var_ref); + dbuf_put_u16(bc, idx); + dbuf_putc(bc, OP_define_field); + dbuf_put_u32(bc, JS_DupAtom(ctx, var_name)); + dbuf_putc(bc, OP_push_atom_value); + dbuf_put_u32(bc, JS_DupAtom(ctx, var_name)); + } else + if (label_done == -1 && + can_opt_put_ref_value(bc_buf, ls->pos)) { + int get_op; + if (s->closure_var[idx].is_lexical) + get_op = OP_get_var_ref_check; + else + get_op = OP_get_var_ref; + pos_next = optimize_scope_make_ref(ctx, s, bc, bc_buf, ls, + pos_next, + get_op, idx); + } else { + /* Create a dummy object with a named slot that is + a reference to the closure variable */ + dbuf_putc(bc, OP_make_var_ref_ref); + dbuf_put_u32(bc, JS_DupAtom(ctx, var_name)); + dbuf_put_u16(bc, idx); + } + break; + case OP_scope_get_ref: + /* XXX: should create a dummy object with a named slot that is + a reference to the closure variable */ + dbuf_putc(bc, OP_undefined); + /* fall thru */ + case OP_scope_get_var_undef: + case OP_scope_get_var: + case OP_scope_put_var: + case OP_scope_put_var_init: + is_put = (op == OP_scope_put_var || + op == OP_scope_put_var_init); + if (is_put) { + if (s->closure_var[idx].is_lexical) { + if (op == OP_scope_put_var_init) { + /* 'this' can only be initialized once */ + if (var_name == JS_ATOM_this) + dbuf_putc(bc, OP_put_var_ref_check_init); + else + dbuf_putc(bc, OP_put_var_ref); + } else { + dbuf_putc(bc, OP_put_var_ref_check); + } + } else { + dbuf_putc(bc, OP_put_var_ref); + } + } else { + if (s->closure_var[idx].is_lexical) { + dbuf_putc(bc, OP_get_var_ref_check); + } else { + dbuf_putc(bc, OP_get_var_ref); + } + } + dbuf_put_u16(bc, idx); + break; + case OP_scope_delete_var: + dbuf_putc(bc, OP_push_false); + break; + } + goto done; + } + } + + /* global variable access */ + + switch (op) { + case OP_scope_make_ref: + if (label_done == -1 && can_opt_put_global_ref_value(bc_buf, ls->pos)) { + pos_next = optimize_scope_make_global_ref(ctx, s, bc, bc_buf, ls, + pos_next, var_name); + } else { + dbuf_putc(bc, OP_make_var_ref); + dbuf_put_u32(bc, JS_DupAtom(ctx, var_name)); + } + break; + case OP_scope_get_ref: + /* XXX: should create a dummy object with a named slot that is + a reference to the global variable */ + dbuf_putc(bc, OP_undefined); + dbuf_putc(bc, OP_get_var); + dbuf_put_u32(bc, JS_DupAtom(ctx, var_name)); + break; + case OP_scope_get_var_undef: + case OP_scope_get_var: + case OP_scope_put_var: + dbuf_putc(bc, OP_get_var_undef + (op - OP_scope_get_var_undef)); + dbuf_put_u32(bc, JS_DupAtom(ctx, var_name)); + break; + case OP_scope_put_var_init: + dbuf_putc(bc, OP_put_var_init); + dbuf_put_u32(bc, JS_DupAtom(ctx, var_name)); + break; + case OP_scope_delete_var: + dbuf_putc(bc, OP_delete_var); + dbuf_put_u32(bc, JS_DupAtom(ctx, var_name)); + break; + } +done: + if (label_done >= 0) { + dbuf_putc(bc, OP_label); + dbuf_put_u32(bc, label_done); + s->label_slots[label_done].pos2 = bc->size; + } + return pos_next; +} + +/* search in all scopes */ +static int find_private_class_field_all(JSContext *ctx, JSFunctionDef *fd, + JSAtom name, int scope_level) +{ + int idx; + + idx = fd->scopes[scope_level].first; + while (idx >= 0) { + if (fd->vars[idx].var_name == name) + return idx; + idx = fd->vars[idx].scope_next; + } + return -1; +} + +static void get_loc_or_ref(DynBuf *bc, BOOL is_ref, int idx) +{ + /* if the field is not initialized, the error is catched when + accessing it */ + if (is_ref) + dbuf_putc(bc, OP_get_var_ref); + else + dbuf_putc(bc, OP_get_loc); + dbuf_put_u16(bc, idx); +} + +static int resolve_scope_private_field1(JSContext *ctx, + BOOL *pis_ref, int *pvar_kind, + JSFunctionDef *s, + JSAtom var_name, int scope_level) +{ + int idx, var_kind; + JSFunctionDef *fd; + BOOL is_ref; + + fd = s; + is_ref = FALSE; + for(;;) { + idx = find_private_class_field_all(ctx, fd, var_name, scope_level); + if (idx >= 0) { + var_kind = fd->vars[idx].var_kind; + if (is_ref) { + idx = get_closure_var(ctx, s, fd, FALSE, idx, var_name, + TRUE, TRUE, JS_VAR_NORMAL); + if (idx < 0) + return -1; + } + break; + } + scope_level = fd->parent_scope_level; + if (!fd->parent) { + if (fd->is_eval) { + /* closure of the eval function (top level) */ + for (idx = 0; idx < fd->closure_var_count; idx++) { + JSClosureVar *cv = &fd->closure_var[idx]; + if (cv->var_name == var_name) { + var_kind = cv->var_kind; + is_ref = TRUE; + if (fd != s) { + idx = get_closure_var2(ctx, s, fd, + FALSE, + cv->is_arg, idx, + cv->var_name, cv->is_const, + cv->is_lexical, + cv->var_kind); + if (idx < 0) + return -1; + } + goto done; + } + } + } + /* XXX: no line number info */ + // XXX: resolve_scope_private_field1() should take JSParseState *s and use js_parse_error_atom + JS_ThrowSyntaxErrorAtom(ctx, "undefined private field '%s'", + var_name); + return -1; + } else { + fd = fd->parent; + } + is_ref = TRUE; + } + done: + *pis_ref = is_ref; + *pvar_kind = var_kind; + return idx; +} + +/* return 0 if OK or -1 if the private field could not be resolved */ +static int resolve_scope_private_field(JSContext *ctx, JSFunctionDef *s, + JSAtom var_name, int scope_level, int op, + DynBuf *bc) +{ + int idx, var_kind; + BOOL is_ref; + + idx = resolve_scope_private_field1(ctx, &is_ref, &var_kind, s, + var_name, scope_level); + if (idx < 0) + return -1; + assert(var_kind != JS_VAR_NORMAL); + switch (op) { + case OP_scope_get_private_field: + case OP_scope_get_private_field2: + switch(var_kind) { + case JS_VAR_PRIVATE_FIELD: + if (op == OP_scope_get_private_field2) + dbuf_putc(bc, OP_dup); + get_loc_or_ref(bc, is_ref, idx); + dbuf_putc(bc, OP_get_private_field); + break; + case JS_VAR_PRIVATE_METHOD: + get_loc_or_ref(bc, is_ref, idx); + dbuf_putc(bc, OP_check_brand); + if (op != OP_scope_get_private_field2) + dbuf_putc(bc, OP_nip); + break; + case JS_VAR_PRIVATE_GETTER: + case JS_VAR_PRIVATE_GETTER_SETTER: + if (op == OP_scope_get_private_field2) + dbuf_putc(bc, OP_dup); + get_loc_or_ref(bc, is_ref, idx); + dbuf_putc(bc, OP_check_brand); + dbuf_putc(bc, OP_call_method); + dbuf_put_u16(bc, 0); + break; + case JS_VAR_PRIVATE_SETTER: + /* XXX: add clearer error message */ + dbuf_putc(bc, OP_throw_error); + dbuf_put_u32(bc, JS_DupAtom(ctx, var_name)); + dbuf_putc(bc, JS_THROW_VAR_RO); + break; + default: + abort(); + } + break; + case OP_scope_put_private_field: + switch(var_kind) { + case JS_VAR_PRIVATE_FIELD: + get_loc_or_ref(bc, is_ref, idx); + dbuf_putc(bc, OP_put_private_field); + break; + case JS_VAR_PRIVATE_METHOD: + case JS_VAR_PRIVATE_GETTER: + /* XXX: add clearer error message */ + dbuf_putc(bc, OP_throw_error); + dbuf_put_u32(bc, JS_DupAtom(ctx, var_name)); + dbuf_putc(bc, JS_THROW_VAR_RO); + break; + case JS_VAR_PRIVATE_SETTER: + case JS_VAR_PRIVATE_GETTER_SETTER: + { + JSAtom setter_name = get_private_setter_name(ctx, var_name); + if (setter_name == JS_ATOM_NULL) + return -1; + idx = resolve_scope_private_field1(ctx, &is_ref, + &var_kind, s, + setter_name, scope_level); + JS_FreeAtom(ctx, setter_name); + if (idx < 0) + return -1; + assert(var_kind == JS_VAR_PRIVATE_SETTER); + get_loc_or_ref(bc, is_ref, idx); + dbuf_putc(bc, OP_swap); + /* obj func value */ + dbuf_putc(bc, OP_rot3r); + /* value obj func */ + dbuf_putc(bc, OP_check_brand); + dbuf_putc(bc, OP_rot3l); + /* obj func value */ + dbuf_putc(bc, OP_call_method); + dbuf_put_u16(bc, 1); + dbuf_putc(bc, OP_drop); + } + break; + default: + abort(); + } + break; + case OP_scope_in_private_field: + get_loc_or_ref(bc, is_ref, idx); + dbuf_putc(bc, OP_private_in); + break; + default: + abort(); + } + return 0; +} + +static void mark_eval_captured_variables(JSContext *ctx, JSFunctionDef *s, + int scope_level) +{ + int idx; + JSVarDef *vd; + + for (idx = s->scopes[scope_level].first; idx >= 0;) { + vd = &s->vars[idx]; + vd->is_captured = 1; + idx = vd->scope_next; + } +} + +/* XXX: should handle the argument scope generically */ +static BOOL is_var_in_arg_scope(const JSVarDef *vd) +{ + return (vd->var_name == JS_ATOM_home_object || + vd->var_name == JS_ATOM_this_active_func || + vd->var_name == JS_ATOM_new_target || + vd->var_name == JS_ATOM_this || + vd->var_name == JS_ATOM__arg_var_ || + vd->var_kind == JS_VAR_FUNCTION_NAME); +} + +static void add_eval_variables(JSContext *ctx, JSFunctionDef *s) +{ + JSFunctionDef *fd; + JSVarDef *vd; + int i, scope_level, scope_idx; + BOOL has_arguments_binding, has_this_binding, is_arg_scope; + + /* in non strict mode, variables are created in the caller's + environment object */ + if (!s->is_eval && !s->is_strict_mode) { + s->var_object_idx = add_var(ctx, s, JS_ATOM__var_); + if (s->has_parameter_expressions) { + /* an additional variable object is needed for the + argument scope */ + s->arg_var_object_idx = add_var(ctx, s, JS_ATOM__arg_var_); + } + } + + /* eval can potentially use 'arguments' so we must define it */ + has_this_binding = s->has_this_binding; + if (has_this_binding) { + if (s->this_var_idx < 0) + s->this_var_idx = add_var_this(ctx, s); + if (s->new_target_var_idx < 0) + s->new_target_var_idx = add_var(ctx, s, JS_ATOM_new_target); + if (s->is_derived_class_constructor && s->this_active_func_var_idx < 0) + s->this_active_func_var_idx = add_var(ctx, s, JS_ATOM_this_active_func); + if (s->has_home_object && s->home_object_var_idx < 0) + s->home_object_var_idx = add_var(ctx, s, JS_ATOM_home_object); + } + has_arguments_binding = s->has_arguments_binding; + if (has_arguments_binding) { + add_arguments_var(ctx, s); + /* also add an arguments binding in the argument scope to + raise an error if a direct eval in the argument scope tries + to redefine it */ + if (s->has_parameter_expressions && !s->is_strict_mode) + add_arguments_arg(ctx, s); + } + if (s->is_func_expr && s->func_name != JS_ATOM_NULL) + add_func_var(ctx, s, s->func_name); + + /* eval can use all the variables of the enclosing functions, so + they must be all put in the closure. The closure variables are + ordered by scope. It works only because no closure are created + before. */ + assert(s->is_eval || s->closure_var_count == 0); + + /* XXX: inefficient, but eval performance is less critical */ + fd = s; + for(;;) { + scope_level = fd->parent_scope_level; + fd = fd->parent; + if (!fd) + break; + /* add 'this' if it was not previously added */ + if (!has_this_binding && fd->has_this_binding) { + if (fd->this_var_idx < 0) + fd->this_var_idx = add_var_this(ctx, fd); + if (fd->new_target_var_idx < 0) + fd->new_target_var_idx = add_var(ctx, fd, JS_ATOM_new_target); + if (fd->is_derived_class_constructor && fd->this_active_func_var_idx < 0) + fd->this_active_func_var_idx = add_var(ctx, fd, JS_ATOM_this_active_func); + if (fd->has_home_object && fd->home_object_var_idx < 0) + fd->home_object_var_idx = add_var(ctx, fd, JS_ATOM_home_object); + has_this_binding = TRUE; + } + /* add 'arguments' if it was not previously added */ + if (!has_arguments_binding && fd->has_arguments_binding) { + add_arguments_var(ctx, fd); + has_arguments_binding = TRUE; + } + /* add function name */ + if (fd->is_func_expr && fd->func_name != JS_ATOM_NULL) + add_func_var(ctx, fd, fd->func_name); + + /* add lexical variables */ + scope_idx = fd->scopes[scope_level].first; + while (scope_idx >= 0) { + vd = &fd->vars[scope_idx]; + vd->is_captured = 1; + get_closure_var(ctx, s, fd, FALSE, scope_idx, + vd->var_name, vd->is_const, vd->is_lexical, vd->var_kind); + scope_idx = vd->scope_next; + } + is_arg_scope = (scope_idx == ARG_SCOPE_END); + if (!is_arg_scope) { + /* add unscoped variables */ + /* XXX: propagate is_const and var_kind too ? */ + for(i = 0; i < fd->arg_count; i++) { + vd = &fd->args[i]; + if (vd->var_name != JS_ATOM_NULL) { + get_closure_var(ctx, s, fd, + TRUE, i, vd->var_name, FALSE, + vd->is_lexical, JS_VAR_NORMAL); + } + } + for(i = 0; i < fd->var_count; i++) { + vd = &fd->vars[i]; + /* do not close top level last result */ + if (vd->scope_level == 0 && + vd->var_name != JS_ATOM__ret_ && + vd->var_name != JS_ATOM_NULL) { + get_closure_var(ctx, s, fd, + FALSE, i, vd->var_name, FALSE, + vd->is_lexical, JS_VAR_NORMAL); + } + } + } else { + for(i = 0; i < fd->var_count; i++) { + vd = &fd->vars[i]; + /* do not close top level last result */ + if (vd->scope_level == 0 && is_var_in_arg_scope(vd)) { + get_closure_var(ctx, s, fd, + FALSE, i, vd->var_name, FALSE, + vd->is_lexical, JS_VAR_NORMAL); + } + } + } + if (fd->is_eval) { + int idx; + /* add direct eval variables (we are necessarily at the + top level) */ + for (idx = 0; idx < fd->closure_var_count; idx++) { + JSClosureVar *cv = &fd->closure_var[idx]; + get_closure_var2(ctx, s, fd, + FALSE, cv->is_arg, + idx, cv->var_name, cv->is_const, + cv->is_lexical, cv->var_kind); + } + } + } +} + +static void set_closure_from_var(JSContext *ctx, JSClosureVar *cv, + JSVarDef *vd, int var_idx) +{ + cv->is_local = TRUE; + cv->is_arg = FALSE; + cv->is_const = vd->is_const; + cv->is_lexical = vd->is_lexical; + cv->var_kind = vd->var_kind; + cv->var_idx = var_idx; + cv->var_name = JS_DupAtom(ctx, vd->var_name); +} + +/* for direct eval compilation: add references to the variables of the + calling function */ +static __exception int add_closure_variables(JSContext *ctx, JSFunctionDef *s, + JSFunctionBytecode *b, int scope_idx) +{ + int i, count; + JSVarDef *vd; + BOOL is_arg_scope; + + count = b->arg_count + b->var_count + b->closure_var_count; + s->closure_var = NULL; + s->closure_var_count = 0; + s->closure_var_size = count; + if (count == 0) + return 0; + s->closure_var = js_malloc(ctx, sizeof(s->closure_var[0]) * count); + if (!s->closure_var) + return -1; + /* Add lexical variables in scope at the point of evaluation */ + for (i = scope_idx; i >= 0;) { + vd = &b->vardefs[b->arg_count + i]; + if (vd->scope_level > 0) { + JSClosureVar *cv = &s->closure_var[s->closure_var_count++]; + set_closure_from_var(ctx, cv, vd, i); + } + i = vd->scope_next; + } + is_arg_scope = (i == ARG_SCOPE_END); + if (!is_arg_scope) { + /* Add argument variables */ + for(i = 0; i < b->arg_count; i++) { + JSClosureVar *cv = &s->closure_var[s->closure_var_count++]; + vd = &b->vardefs[i]; + cv->is_local = TRUE; + cv->is_arg = TRUE; + cv->is_const = FALSE; + cv->is_lexical = FALSE; + cv->var_kind = JS_VAR_NORMAL; + cv->var_idx = i; + cv->var_name = JS_DupAtom(ctx, vd->var_name); + } + /* Add local non lexical variables */ + for(i = 0; i < b->var_count; i++) { + vd = &b->vardefs[b->arg_count + i]; + if (vd->scope_level == 0 && vd->var_name != JS_ATOM__ret_) { + JSClosureVar *cv = &s->closure_var[s->closure_var_count++]; + set_closure_from_var(ctx, cv, vd, i); + } + } + } else { + /* only add pseudo variables */ + for(i = 0; i < b->var_count; i++) { + vd = &b->vardefs[b->arg_count + i]; + if (vd->scope_level == 0 && is_var_in_arg_scope(vd)) { + JSClosureVar *cv = &s->closure_var[s->closure_var_count++]; + set_closure_from_var(ctx, cv, vd, i); + } + } + } + for(i = 0; i < b->closure_var_count; i++) { + JSClosureVar *cv0 = &b->closure_var[i]; + JSClosureVar *cv = &s->closure_var[s->closure_var_count++]; + cv->is_local = FALSE; + cv->is_arg = cv0->is_arg; + cv->is_const = cv0->is_const; + cv->is_lexical = cv0->is_lexical; + cv->var_kind = cv0->var_kind; + cv->var_idx = i; + cv->var_name = JS_DupAtom(ctx, cv0->var_name); + } + return 0; +} + +typedef struct CodeContext { + const uint8_t *bc_buf; /* code buffer */ + int bc_len; /* length of the code buffer */ + int pos; /* position past the matched code pattern */ + int line_num; /* last visited OP_source_loc parameter or -1 */ + int col_num; /* last visited OP_source_loc parameter or -1 */ + int op; + int idx; + int label; + int val; + JSAtom atom; +} CodeContext; + +#define M2(op1, op2) ((uint32_t)(op1) | ((uint32_t)(op2) << 8)) +#define M3(op1, op2, op3) ((uint32_t)(op1) | ((uint32_t)(op2) << 8) | ((uint32_t)(op3) << 16)) +#define M4(op1, op2, op3, op4) ((uint32_t)(op1) | ((uint32_t)(op2) << 8) | ((uint32_t)(op3) << 16) | ((uint32_t)(op4) << 24)) + +static BOOL code_match(CodeContext *s, int pos, ...) +{ + const uint8_t *tab = s->bc_buf; + int op, len, op1, line_num, col_num, pos_next; + va_list ap; + BOOL ret = FALSE; + + line_num = -1; + col_num = -1; + va_start(ap, pos); + + for(;;) { + op1 = va_arg(ap, int); + if (op1 == -1) { + s->pos = pos; + s->line_num = line_num; + s->col_num = col_num; + ret = TRUE; + break; + } + for (;;) { + if (pos >= s->bc_len) + goto done; + op = tab[pos]; + len = opcode_info[op].size; + pos_next = pos + len; + if (pos_next > s->bc_len) + goto done; + if (op == OP_source_loc) { + line_num = get_u32(tab + pos + 1); + col_num = get_u32(tab + pos + 5); + pos = pos_next; + } else { + break; + } + } + if (op != op1) { + if (op1 == (uint8_t)op1 || !op) + break; + if (op != (uint8_t)op1 + && op != (uint8_t)(op1 >> 8) + && op != (uint8_t)(op1 >> 16) + && op != (uint8_t)(op1 >> 24)) { + break; + } + s->op = op; + } + + pos++; + switch(opcode_info[op].fmt) { + case OP_FMT_loc8: + case OP_FMT_u8: + { + int idx = tab[pos]; + int arg = va_arg(ap, int); + if (arg == -1) { + s->idx = idx; + } else { + if (arg != idx) + goto done; + } + break; + } + case OP_FMT_u16: + case OP_FMT_npop: + case OP_FMT_loc: + case OP_FMT_arg: + case OP_FMT_var_ref: + { + int idx = get_u16(tab + pos); + int arg = va_arg(ap, int); + if (arg == -1) { + s->idx = idx; + } else { + if (arg != idx) + goto done; + } + break; + } + case OP_FMT_i32: + case OP_FMT_u32: + case OP_FMT_label: + case OP_FMT_const: + { + s->label = get_u32(tab + pos); + break; + } + case OP_FMT_label_u16: + { + s->label = get_u32(tab + pos); + s->val = get_u16(tab + pos + 4); + break; + } + case OP_FMT_atom: + { + s->atom = get_u32(tab + pos); + break; + } + case OP_FMT_atom_u8: + { + s->atom = get_u32(tab + pos); + s->val = get_u8(tab + pos + 4); + break; + } + case OP_FMT_atom_u16: + { + s->atom = get_u32(tab + pos); + s->val = get_u16(tab + pos + 4); + break; + } + case OP_FMT_atom_label_u8: + { + s->atom = get_u32(tab + pos); + s->label = get_u32(tab + pos + 4); + s->val = get_u8(tab + pos + 8); + break; + } + default: + break; + } + pos = pos_next; + } + done: + va_end(ap); + return ret; +} + +static void instantiate_hoisted_definitions(JSContext *ctx, JSFunctionDef *s, DynBuf *bc) +{ + int i, idx, label_next = -1; + + /* add the hoisted functions in arguments and local variables */ + for(i = 0; i < s->arg_count; i++) { + JSVarDef *vd = &s->args[i]; + if (vd->func_pool_idx >= 0) { + dbuf_putc(bc, OP_fclosure); + dbuf_put_u32(bc, vd->func_pool_idx); + dbuf_putc(bc, OP_put_arg); + dbuf_put_u16(bc, i); + } + } + for(i = 0; i < s->var_count; i++) { + JSVarDef *vd = &s->vars[i]; + if (vd->scope_level == 0 && vd->func_pool_idx >= 0) { + dbuf_putc(bc, OP_fclosure); + dbuf_put_u32(bc, vd->func_pool_idx); + dbuf_putc(bc, OP_put_loc); + dbuf_put_u16(bc, i); + } + } + + /* the module global variables must be initialized before + evaluating the module so that the exported functions are + visible if there are cyclic module references */ + if (s->module) { + label_next = new_label_fd(s, -1); + + /* if 'this' is true, initialize the global variables and return */ + dbuf_putc(bc, OP_push_this); + dbuf_putc(bc, OP_if_false); + dbuf_put_u32(bc, label_next); + update_label(s, label_next, 1); + s->jump_size++; + } + + /* add the global variables (only happens if s->is_global_var is + true) */ + for(i = 0; i < s->global_var_count; i++) { + JSGlobalVar *hf = &s->global_vars[i]; + int has_closure = 0; + BOOL force_init = hf->force_init; + /* we are in an eval, so the closure contains all the + enclosing variables */ + /* If the outer function has a variable environment, + create a property for the variable there */ + for(idx = 0; idx < s->closure_var_count; idx++) { + JSClosureVar *cv = &s->closure_var[idx]; + if (cv->var_name == hf->var_name) { + has_closure = 2; + force_init = FALSE; + break; + } + if (cv->var_name == JS_ATOM__var_ || + cv->var_name == JS_ATOM__arg_var_) { + dbuf_putc(bc, OP_get_var_ref); + dbuf_put_u16(bc, idx); + has_closure = 1; + force_init = TRUE; + break; + } + } + if (!has_closure) { + int flags; + + flags = 0; + if (s->eval_type != JS_EVAL_TYPE_GLOBAL) + flags |= JS_PROP_CONFIGURABLE; + if (hf->cpool_idx >= 0 && !hf->is_lexical) { + /* global function definitions need a specific handling */ + dbuf_putc(bc, OP_fclosure); + dbuf_put_u32(bc, hf->cpool_idx); + + dbuf_putc(bc, OP_define_func); + dbuf_put_u32(bc, JS_DupAtom(ctx, hf->var_name)); + dbuf_putc(bc, flags); + + goto done_global_var; + } else { + if (hf->is_lexical) { + flags |= DEFINE_GLOBAL_LEX_VAR; + if (!hf->is_const) + flags |= JS_PROP_WRITABLE; + } + dbuf_putc(bc, OP_define_var); + dbuf_put_u32(bc, JS_DupAtom(ctx, hf->var_name)); + dbuf_putc(bc, flags); + } + } + if (hf->cpool_idx >= 0 || force_init) { + if (hf->cpool_idx >= 0) { + dbuf_putc(bc, OP_fclosure); + dbuf_put_u32(bc, hf->cpool_idx); + if (hf->var_name == JS_ATOM__default_) { + /* set default export function name */ + dbuf_putc(bc, OP_set_name); + dbuf_put_u32(bc, JS_DupAtom(ctx, JS_ATOM_default)); + } + } else { + dbuf_putc(bc, OP_undefined); + } + if (has_closure == 2) { + dbuf_putc(bc, OP_put_var_ref); + dbuf_put_u16(bc, idx); + } else if (has_closure == 1) { + dbuf_putc(bc, OP_define_field); + dbuf_put_u32(bc, JS_DupAtom(ctx, hf->var_name)); + dbuf_putc(bc, OP_drop); + } else { + /* XXX: Check if variable is writable and enumerable */ + dbuf_putc(bc, OP_put_var); + dbuf_put_u32(bc, JS_DupAtom(ctx, hf->var_name)); + } + } + done_global_var: + JS_FreeAtom(ctx, hf->var_name); + } + + if (s->module) { + dbuf_putc(bc, OP_return_undef); + + dbuf_putc(bc, OP_label); + dbuf_put_u32(bc, label_next); + s->label_slots[label_next].pos2 = bc->size; + } + + js_free(ctx, s->global_vars); + s->global_vars = NULL; + s->global_var_count = 0; + s->global_var_size = 0; +} + +static int skip_dead_code(JSFunctionDef *s, const uint8_t *bc_buf, int bc_len, + int pos, int *linep, int *colp) +{ + int op, len, label; + + for (; pos < bc_len; pos += len) { + op = bc_buf[pos]; + len = opcode_info[op].size; + if (op == OP_source_loc) { + *linep = get_u32(bc_buf + pos + 1); + *colp = get_u32(bc_buf + pos + 5); + } else if (op == OP_label) { + label = get_u32(bc_buf + pos + 1); + if (update_label(s, label, 0) > 0) + break; + assert(s->label_slots[label].first_reloc == NULL); + } else { + /* XXX: output a warning for unreachable code? */ + JSAtom atom; + switch(opcode_info[op].fmt) { + case OP_FMT_label: + case OP_FMT_label_u16: + label = get_u32(bc_buf + pos + 1); + update_label(s, label, -1); + break; + case OP_FMT_atom_label_u8: + case OP_FMT_atom_label_u16: + label = get_u32(bc_buf + pos + 5); + update_label(s, label, -1); + /* fall thru */ + case OP_FMT_atom: + case OP_FMT_atom_u8: + case OP_FMT_atom_u16: + atom = get_u32(bc_buf + pos + 1); + JS_FreeAtom(s->ctx, atom); + break; + default: + break; + } + } + } + return pos; +} + +static int get_label_pos(JSFunctionDef *s, int label) +{ + int i, pos; + for (i = 0; i < 20; i++) { + pos = s->label_slots[label].pos; + for (;;) { + switch (s->byte_code.buf[pos]) { + case OP_source_loc: + pos += 9; + continue; + case OP_label: + pos += 5; + continue; + case OP_goto: + label = get_u32(s->byte_code.buf + pos + 1); + break; + default: + return pos; + } + break; + } + } + return pos; +} + +/* convert global variable accesses to local variables or closure + variables when necessary */ +static __exception int resolve_variables(JSContext *ctx, JSFunctionDef *s) +{ + int pos, pos_next, bc_len, op, len, i, idx, line_num, col_num; + uint8_t *bc_buf; + JSAtom var_name; + DynBuf bc_out; + CodeContext cc; + int scope; + + cc.bc_buf = bc_buf = s->byte_code.buf; + cc.bc_len = bc_len = s->byte_code.size; + js_dbuf_init(ctx, &bc_out); + + /* first pass for runtime checks (must be done before the + variables are created) */ + for(i = 0; i < s->global_var_count; i++) { + JSGlobalVar *hf = &s->global_vars[i]; + int flags; + + /* check if global variable (XXX: simplify) */ + for(idx = 0; idx < s->closure_var_count; idx++) { + JSClosureVar *cv = &s->closure_var[idx]; + if (cv->var_name == hf->var_name) { + if (s->eval_type == JS_EVAL_TYPE_DIRECT && + cv->is_lexical) { + /* Check if a lexical variable is + redefined as 'var'. XXX: Could abort + compilation here, but for consistency + with the other checks, we delay the + error generation. */ + dbuf_putc(&bc_out, OP_throw_error); + dbuf_put_u32(&bc_out, JS_DupAtom(ctx, hf->var_name)); + dbuf_putc(&bc_out, JS_THROW_VAR_REDECL); + } + goto next; + } + if (cv->var_name == JS_ATOM__var_ || + cv->var_name == JS_ATOM__arg_var_) + goto next; + } + + dbuf_putc(&bc_out, OP_check_define_var); + dbuf_put_u32(&bc_out, JS_DupAtom(ctx, hf->var_name)); + flags = 0; + if (hf->is_lexical) + flags |= DEFINE_GLOBAL_LEX_VAR; + if (hf->cpool_idx >= 0) + flags |= DEFINE_GLOBAL_FUNC_VAR; + dbuf_putc(&bc_out, flags); + next: ; + } + + line_num = 0; /* avoid warning */ + col_num = 0; + for (pos = 0; pos < bc_len; pos = pos_next) { + op = bc_buf[pos]; + len = opcode_info[op].size; + pos_next = pos + len; + switch(op) { + case OP_source_loc: + line_num = get_u32(bc_buf + pos + 1); + col_num = get_u32(bc_buf + pos + 5); + s->source_loc_size++; + goto no_change; + + case OP_eval: /* convert scope index to adjusted variable index */ + { + int call_argc = get_u16(bc_buf + pos + 1); + scope = get_u16(bc_buf + pos + 1 + 2); + mark_eval_captured_variables(ctx, s, scope); + dbuf_putc(&bc_out, op); + dbuf_put_u16(&bc_out, call_argc); + dbuf_put_u16(&bc_out, s->scopes[scope].first + 1); + } + break; + case OP_apply_eval: /* convert scope index to adjusted variable index */ + scope = get_u16(bc_buf + pos + 1); + mark_eval_captured_variables(ctx, s, scope); + dbuf_putc(&bc_out, op); + dbuf_put_u16(&bc_out, s->scopes[scope].first + 1); + break; + case OP_scope_get_var_undef: + case OP_scope_get_var: + case OP_scope_put_var: + case OP_scope_delete_var: + case OP_scope_get_ref: + case OP_scope_put_var_init: + var_name = get_u32(bc_buf + pos + 1); + scope = get_u16(bc_buf + pos + 5); + pos_next = resolve_scope_var(ctx, s, var_name, scope, op, &bc_out, + NULL, NULL, pos_next); + JS_FreeAtom(ctx, var_name); + break; + case OP_scope_make_ref: + { + int label; + LabelSlot *ls; + var_name = get_u32(bc_buf + pos + 1); + label = get_u32(bc_buf + pos + 5); + scope = get_u16(bc_buf + pos + 9); + ls = &s->label_slots[label]; + ls->ref_count--; /* always remove label reference */ + pos_next = resolve_scope_var(ctx, s, var_name, scope, op, &bc_out, + bc_buf, ls, pos_next); + JS_FreeAtom(ctx, var_name); + } + break; + case OP_scope_get_private_field: + case OP_scope_get_private_field2: + case OP_scope_put_private_field: + case OP_scope_in_private_field: + { + int ret; + var_name = get_u32(bc_buf + pos + 1); + scope = get_u16(bc_buf + pos + 5); + ret = resolve_scope_private_field(ctx, s, var_name, scope, op, &bc_out); + if (ret < 0) + goto fail; + JS_FreeAtom(ctx, var_name); + } + break; + case OP_gosub: + s->jump_size++; + { + /* remove calls to empty finalizers */ + int label; + LabelSlot *ls; + + label = get_u32(bc_buf + pos + 1); + assert(label >= 0 && label < s->label_count); + ls = &s->label_slots[label]; + if (code_match(&cc, ls->pos, OP_ret, -1)) { + ls->ref_count--; + break; + } + } + goto no_change; + case OP_insert3: + /* Transformation: insert3 put_array_el|put_ref_value drop -> put_array_el|put_ref_value */ + if (code_match(&cc, pos_next, M2(OP_put_array_el, OP_put_ref_value), OP_drop, -1)) { + dbuf_putc(&bc_out, cc.op); + pos_next = cc.pos; + if (cc.line_num == -1) + break; + if (cc.line_num != line_num || cc.col_num != col_num) { + line_num = cc.line_num; + col_num = cc.col_num; + s->source_loc_size++; + dbuf_putc(&bc_out, OP_source_loc); + dbuf_put_u32(&bc_out, line_num); + dbuf_put_u32(&bc_out, col_num); + } + break; + } + goto no_change; + + case OP_goto: + s->jump_size++; + /* fall thru */ + case OP_tail_call: + case OP_tail_call_method: + case OP_return: + case OP_return_undef: + case OP_throw: + case OP_throw_error: + case OP_ret: + { + /* remove dead code */ + int line = -1; + int col = -1; + dbuf_put(&bc_out, bc_buf + pos, len); + pos = skip_dead_code(s, bc_buf, bc_len, pos + len, + &line, &col); + pos_next = pos; + if (line < 0 || pos >= bc_len) + break; + if (line_num != line || col_num != col) { + line_num = line; + col_num = col; + s->source_loc_size++; + dbuf_putc(&bc_out, OP_source_loc); + dbuf_put_u32(&bc_out, line_num); + dbuf_put_u32(&bc_out, col_num); + } + break; + } + goto no_change; + + case OP_label: + { + int label; + LabelSlot *ls; + + label = get_u32(bc_buf + pos + 1); + assert(label >= 0 && label < s->label_count); + ls = &s->label_slots[label]; + ls->pos2 = bc_out.size + opcode_info[op].size; + } + goto no_change; + + case OP_enter_scope: + { + int scope_idx, scope = get_u16(bc_buf + pos + 1); + + if (scope == s->body_scope) { + instantiate_hoisted_definitions(ctx, s, &bc_out); + } + + for(scope_idx = s->scopes[scope].first; scope_idx >= 0;) { + JSVarDef *vd = &s->vars[scope_idx]; + if (vd->scope_level == scope) { + if (scope_idx != s->arguments_arg_idx) { + if (vd->var_kind == JS_VAR_FUNCTION_DECL || + vd->var_kind == JS_VAR_NEW_FUNCTION_DECL) { + /* Initialize lexical variable upon entering scope */ + dbuf_putc(&bc_out, OP_fclosure); + dbuf_put_u32(&bc_out, vd->func_pool_idx); + dbuf_putc(&bc_out, OP_put_loc); + dbuf_put_u16(&bc_out, scope_idx); + } else { + /* XXX: should check if variable can be used + before initialization */ + dbuf_putc(&bc_out, OP_set_loc_uninitialized); + dbuf_put_u16(&bc_out, scope_idx); + } + } + scope_idx = vd->scope_next; + } else { + break; + } + } + } + break; + + case OP_leave_scope: + { + int scope_idx, scope = get_u16(bc_buf + pos + 1); + + for(scope_idx = s->scopes[scope].first; scope_idx >= 0;) { + JSVarDef *vd = &s->vars[scope_idx]; + if (vd->scope_level == scope) { + if (vd->is_captured) { + dbuf_putc(&bc_out, OP_close_loc); + dbuf_put_u16(&bc_out, scope_idx); + } + scope_idx = vd->scope_next; + } else { + break; + } + } + } + break; + + case OP_set_name: + { + /* remove dummy set_name opcodes */ + JSAtom name = get_u32(bc_buf + pos + 1); + if (name == JS_ATOM_NULL) + break; + } + goto no_change; + + case OP_if_false: + case OP_if_true: + case OP_catch: + s->jump_size++; + goto no_change; + + case OP_dup: + /* Transformation: dup if_false(l1) drop, l1: if_false(l2) -> if_false(l2) */ + /* Transformation: dup if_true(l1) drop, l1: if_true(l2) -> if_true(l2) */ + if (code_match(&cc, pos_next, M2(OP_if_false, OP_if_true), OP_drop, -1)) { + int lab0, lab1, op1, pos1, line1, col1, pos2; + lab0 = lab1 = cc.label; + assert(lab1 >= 0 && lab1 < s->label_count); + op1 = cc.op; + pos1 = cc.pos; + line1 = cc.line_num; + col1 = cc.col_num; + while (code_match(&cc, (pos2 = get_label_pos(s, lab1)), OP_dup, op1, OP_drop, -1)) { + lab1 = cc.label; + } + if (code_match(&cc, pos2, op1, -1)) { + s->jump_size++; + update_label(s, lab0, -1); + update_label(s, cc.label, +1); + dbuf_putc(&bc_out, op1); + dbuf_put_u32(&bc_out, cc.label); + pos_next = pos1; + if (line1 == -1) + break; + if (line1 != line_num || col1 != col_num) { + line_num = line1; + col_num = col1; + s->source_loc_size++; + dbuf_putc(&bc_out, OP_source_loc); + dbuf_put_u32(&bc_out, line_num); + dbuf_put_u32(&bc_out, col_num); + } + break; + } + } + goto no_change; + + case OP_nop: + /* remove erased code */ + break; + case OP_set_class_name: + /* only used during parsing */ + break; + + case OP_get_field_opt_chain: /* equivalent to OP_get_field */ + { + JSAtom name = get_u32(bc_buf + pos + 1); + dbuf_putc(&bc_out, OP_get_field); + dbuf_put_u32(&bc_out, name); + } + break; + case OP_get_array_el_opt_chain: /* equivalent to OP_get_array_el */ + dbuf_putc(&bc_out, OP_get_array_el); + break; + + default: + no_change: + dbuf_put(&bc_out, bc_buf + pos, len); + break; + } + } + + /* set the new byte code */ + dbuf_free(&s->byte_code); + s->byte_code = bc_out; + if (dbuf_error(&s->byte_code)) { + JS_ThrowOutOfMemory(ctx); + return -1; + } + return 0; + fail: + /* continue the copy to keep the atom refcounts consistent */ + /* XXX: find a better solution ? */ + for (; pos < bc_len; pos = pos_next) { + op = bc_buf[pos]; + len = opcode_info[op].size; + pos_next = pos + len; + dbuf_put(&bc_out, bc_buf + pos, len); + } + dbuf_free(&s->byte_code); + s->byte_code = bc_out; + return -1; +} + +/* the pc2line table gives a line number for each PC value */ +static void add_pc2line_info(JSFunctionDef *s, uint32_t pc, + int line_num, int col_num) +{ + if (s->source_loc_slots == NULL) + return; + if (s->source_loc_count >= s->source_loc_size) + return; + if (pc < s->line_number_last_pc) + return; + if (line_num == s->line_number_last) + if (col_num == s->col_number_last) + return; + s->source_loc_slots[s->source_loc_count].pc = pc; + s->source_loc_slots[s->source_loc_count].line_num = line_num; + s->source_loc_slots[s->source_loc_count].col_num = col_num; + s->source_loc_count++; + s->line_number_last_pc = pc; + s->line_number_last = line_num; + s->col_number_last = col_num; +} + +static void compute_pc2line_info(JSFunctionDef *s) +{ + if (s->source_loc_slots) { + int last_line_num = s->line_num; + int last_col_num = s->col_num; + uint32_t last_pc = 0; + int i; + + js_dbuf_init(s->ctx, &s->pc2line); + for (i = 0; i < s->source_loc_count; i++) { + uint32_t pc = s->source_loc_slots[i].pc; + int line_num = s->source_loc_slots[i].line_num; + int col_num = s->source_loc_slots[i].col_num; + int diff_pc, diff_line, diff_col; + + if (line_num < 0) + continue; + + diff_pc = pc - last_pc; + if (diff_pc < 0) + continue; + + diff_line = line_num - last_line_num; + diff_col = col_num - last_col_num; + if (diff_line == 0 && diff_col == 0) + continue; + + if (diff_line >= PC2LINE_BASE && + diff_line < PC2LINE_BASE + PC2LINE_RANGE && + diff_pc <= PC2LINE_DIFF_PC_MAX) { + dbuf_putc(&s->pc2line, (diff_line - PC2LINE_BASE) + + diff_pc * PC2LINE_RANGE + PC2LINE_OP_FIRST); + } else { + /* longer encoding */ + dbuf_putc(&s->pc2line, 0); + dbuf_put_leb128(&s->pc2line, diff_pc); + dbuf_put_sleb128(&s->pc2line, diff_line); + } + dbuf_put_sleb128(&s->pc2line, diff_col); + + last_pc = pc; + last_line_num = line_num; + last_col_num = col_num; + } + } +} + +static RelocEntry *add_reloc(JSContext *ctx, LabelSlot *ls, uint32_t addr, int size) +{ + RelocEntry *re; + re = js_malloc(ctx, sizeof(*re)); + if (!re) + return NULL; + re->addr = addr; + re->size = size; + re->next = ls->first_reloc; + ls->first_reloc = re; + return re; +} + +static BOOL code_has_label(CodeContext *s, int pos, int label) +{ + while (pos < s->bc_len) { + int op = s->bc_buf[pos]; + if (op == OP_source_loc) { + pos += 9; + continue; + } + if (op == OP_label) { + int lab = get_u32(s->bc_buf + pos + 1); + if (lab == label) + return TRUE; + pos += 5; + continue; + } + if (op == OP_goto) { + int lab = get_u32(s->bc_buf + pos + 1); + if (lab == label) + return TRUE; + } + break; + } + return FALSE; +} + +/* return the target label, following the OP_goto jumps + the first opcode at destination is stored in *pop + */ +static int find_jump_target(JSFunctionDef *s, int label, int *pop) +{ + int i, pos, op; + + update_label(s, label, -1); + for (i = 0; i < 10; i++) { + assert(label >= 0 && label < s->label_count); + pos = s->label_slots[label].pos2; + for (;;) { + switch(op = s->byte_code.buf[pos]) { + case OP_source_loc: + case OP_label: + pos += opcode_info[op].size; + continue; + case OP_goto: + label = get_u32(s->byte_code.buf + pos + 1); + break; + case OP_drop: + /* ignore drop opcodes if followed by OP_return_undef */ + while (s->byte_code.buf[++pos] == OP_drop) + continue; + if (s->byte_code.buf[pos] == OP_return_undef) + op = OP_return_undef; + /* fall thru */ + default: + goto done; + } + break; + } + } + /* cycle detected, could issue a warning */ + done: + *pop = op; + update_label(s, label, +1); + return label; +} + +static void push_short_int(DynBuf *bc_out, int val) +{ + if (val >= -1 && val <= 7) { + dbuf_putc(bc_out, OP_push_0 + val); + return; + } + if (val == (int8_t)val) { + dbuf_putc(bc_out, OP_push_i8); + dbuf_putc(bc_out, val); + return; + } + if (val == (int16_t)val) { + dbuf_putc(bc_out, OP_push_i16); + dbuf_put_u16(bc_out, val); + return; + } + dbuf_putc(bc_out, OP_push_i32); + dbuf_put_u32(bc_out, val); +} + +static void put_short_code(DynBuf *bc_out, int op, int idx) +{ + if (idx < 4) { + switch (op) { + case OP_get_loc: + dbuf_putc(bc_out, OP_get_loc0 + idx); + return; + case OP_put_loc: + dbuf_putc(bc_out, OP_put_loc0 + idx); + return; + case OP_set_loc: + dbuf_putc(bc_out, OP_set_loc0 + idx); + return; + case OP_get_arg: + dbuf_putc(bc_out, OP_get_arg0 + idx); + return; + case OP_put_arg: + dbuf_putc(bc_out, OP_put_arg0 + idx); + return; + case OP_set_arg: + dbuf_putc(bc_out, OP_set_arg0 + idx); + return; + case OP_get_var_ref: + dbuf_putc(bc_out, OP_get_var_ref0 + idx); + return; + case OP_put_var_ref: + dbuf_putc(bc_out, OP_put_var_ref0 + idx); + return; + case OP_set_var_ref: + dbuf_putc(bc_out, OP_set_var_ref0 + idx); + return; + case OP_call: + dbuf_putc(bc_out, OP_call0 + idx); + return; + } + } + if (idx < 256) { + switch (op) { + case OP_get_loc: + dbuf_putc(bc_out, OP_get_loc8); + dbuf_putc(bc_out, idx); + return; + case OP_put_loc: + dbuf_putc(bc_out, OP_put_loc8); + dbuf_putc(bc_out, idx); + return; + case OP_set_loc: + dbuf_putc(bc_out, OP_set_loc8); + dbuf_putc(bc_out, idx); + return; + } + } + dbuf_putc(bc_out, op); + dbuf_put_u16(bc_out, idx); +} + +/* peephole optimizations and resolve goto/labels */ +static __exception int resolve_labels(JSContext *ctx, JSFunctionDef *s) +{ + int pos, pos_next, bc_len, op, op1, len, i, line_num, col_num, patch_offsets; + const uint8_t *bc_buf; + DynBuf bc_out; + LabelSlot *label_slots, *ls; + RelocEntry *re, *re_next; + CodeContext cc; + int label; + JumpSlot *jp; + + label_slots = s->label_slots; + + line_num = s->line_num; + col_num = s->col_num; + + cc.bc_buf = bc_buf = s->byte_code.buf; + cc.bc_len = bc_len = s->byte_code.size; + js_dbuf_init(ctx, &bc_out); + + if (s->jump_size) { + s->jump_slots = js_mallocz(s->ctx, sizeof(*s->jump_slots) * s->jump_size); + if (s->jump_slots == NULL) + return -1; + } + + if (s->source_loc_size) { + s->source_loc_slots = js_mallocz(s->ctx, sizeof(*s->source_loc_slots) * s->source_loc_size); + if (s->source_loc_slots == NULL) + return -1; + s->line_number_last = s->line_num; + s->col_number_last = s->col_num; + s->line_number_last_pc = 0; + } + + /* initialize the 'home_object' variable if needed */ + if (s->home_object_var_idx >= 0) { + dbuf_putc(&bc_out, OP_special_object); + dbuf_putc(&bc_out, OP_SPECIAL_OBJECT_HOME_OBJECT); + put_short_code(&bc_out, OP_put_loc, s->home_object_var_idx); + } + /* initialize the 'this.active_func' variable if needed */ + if (s->this_active_func_var_idx >= 0) { + dbuf_putc(&bc_out, OP_special_object); + dbuf_putc(&bc_out, OP_SPECIAL_OBJECT_THIS_FUNC); + put_short_code(&bc_out, OP_put_loc, s->this_active_func_var_idx); + } + /* initialize the 'new.target' variable if needed */ + if (s->new_target_var_idx >= 0) { + dbuf_putc(&bc_out, OP_special_object); + dbuf_putc(&bc_out, OP_SPECIAL_OBJECT_NEW_TARGET); + put_short_code(&bc_out, OP_put_loc, s->new_target_var_idx); + } + /* initialize the 'this' variable if needed. In a derived class + constructor, this is initially uninitialized. */ + if (s->this_var_idx >= 0) { + if (s->is_derived_class_constructor) { + dbuf_putc(&bc_out, OP_set_loc_uninitialized); + dbuf_put_u16(&bc_out, s->this_var_idx); + } else { + dbuf_putc(&bc_out, OP_push_this); + put_short_code(&bc_out, OP_put_loc, s->this_var_idx); + } + } + /* initialize the 'arguments' variable if needed */ + if (s->arguments_var_idx >= 0) { + if (s->is_strict_mode || !s->has_simple_parameter_list) { + dbuf_putc(&bc_out, OP_special_object); + dbuf_putc(&bc_out, OP_SPECIAL_OBJECT_ARGUMENTS); + } else { + dbuf_putc(&bc_out, OP_special_object); + dbuf_putc(&bc_out, OP_SPECIAL_OBJECT_MAPPED_ARGUMENTS); + } + if (s->arguments_arg_idx >= 0) + put_short_code(&bc_out, OP_set_loc, s->arguments_arg_idx); + put_short_code(&bc_out, OP_put_loc, s->arguments_var_idx); + } + /* initialize a reference to the current function if needed */ + if (s->func_var_idx >= 0) { + dbuf_putc(&bc_out, OP_special_object); + dbuf_putc(&bc_out, OP_SPECIAL_OBJECT_THIS_FUNC); + put_short_code(&bc_out, OP_put_loc, s->func_var_idx); + } + /* initialize the variable environment object if needed */ + if (s->var_object_idx >= 0) { + dbuf_putc(&bc_out, OP_special_object); + dbuf_putc(&bc_out, OP_SPECIAL_OBJECT_VAR_OBJECT); + put_short_code(&bc_out, OP_put_loc, s->var_object_idx); + } + if (s->arg_var_object_idx >= 0) { + dbuf_putc(&bc_out, OP_special_object); + dbuf_putc(&bc_out, OP_SPECIAL_OBJECT_VAR_OBJECT); + put_short_code(&bc_out, OP_put_loc, s->arg_var_object_idx); + } + + for (pos = 0; pos < bc_len; pos = pos_next) { + int val; + op = bc_buf[pos]; + len = opcode_info[op].size; + pos_next = pos + len; + switch(op) { + case OP_source_loc: + /* line number info (for debug). We put it in a separate + compressed table to reduce memory usage and get better + performance */ + line_num = get_u32(bc_buf + pos + 1); + col_num = get_u32(bc_buf + pos + 5); + break; + + case OP_label: + { + label = get_u32(bc_buf + pos + 1); + assert(label >= 0 && label < s->label_count); + ls = &label_slots[label]; + assert(ls->addr == -1); + ls->addr = bc_out.size; + /* resolve the relocation entries */ + for(re = ls->first_reloc; re != NULL; re = re_next) { + int diff = ls->addr - re->addr; + re_next = re->next; + switch (re->size) { + case 4: + put_u32(bc_out.buf + re->addr, diff); + break; + case 2: + assert(diff == (int16_t)diff); + put_u16(bc_out.buf + re->addr, diff); + break; + case 1: + assert(diff == (int8_t)diff); + put_u8(bc_out.buf + re->addr, diff); + break; + } + js_free(ctx, re); + } + ls->first_reloc = NULL; + } + break; + + case OP_call: + case OP_call_method: + { + /* detect and transform tail calls */ + int argc; + argc = get_u16(bc_buf + pos + 1); + if (code_match(&cc, pos_next, OP_return, -1)) { + if (cc.line_num >= 0) line_num = cc.line_num; + if (cc.col_num >= 0) col_num = cc.col_num; + add_pc2line_info(s, bc_out.size, line_num, col_num); + put_short_code(&bc_out, op + 1, argc); + pos_next = skip_dead_code(s, bc_buf, bc_len, cc.pos, + &line_num, &col_num); + break; + } + add_pc2line_info(s, bc_out.size, line_num, col_num); + put_short_code(&bc_out, op, argc); + break; + } + goto no_change; + + case OP_return: + case OP_return_undef: + case OP_return_async: + case OP_throw: + case OP_throw_error: + pos_next = skip_dead_code(s, bc_buf, bc_len, pos_next, + &line_num, &col_num); + goto no_change; + + case OP_goto: + label = get_u32(bc_buf + pos + 1); + has_goto: + { + /* Use custom matcher because multiple labels can follow */ + label = find_jump_target(s, label, &op1); + if (code_has_label(&cc, pos_next, label)) { + /* jump to next instruction: remove jump */ + update_label(s, label, -1); + break; + } + if (op1 == OP_return || op1 == OP_return_undef || op1 == OP_throw) { + /* jump to return/throw: remove jump, append return/throw */ + /* updating the line number obfuscates assembly listing */ + update_label(s, label, -1); + add_pc2line_info(s, bc_out.size, line_num, col_num); + dbuf_putc(&bc_out, op1); + pos_next = skip_dead_code(s, bc_buf, bc_len, pos_next, + &line_num, &col_num); + break; + } + /* XXX: should duplicate single instructions followed by goto or return */ + /* For example, can match one of these followed by return: + push_i32 / push_const / push_atom_value / get_var / + undefined / null / push_false / push_true / get_ref_value / + get_loc / get_arg / get_var_ref + */ + } + goto has_label; + + case OP_gosub: + label = get_u32(bc_buf + pos + 1); + goto has_label; + + case OP_catch: + label = get_u32(bc_buf + pos + 1); + goto has_label; + + case OP_if_true: + case OP_if_false: + label = get_u32(bc_buf + pos + 1); + label = find_jump_target(s, label, &op1); + /* transform if_false/if_true(l1) label(l1) -> drop label(l1) */ + if (code_has_label(&cc, pos_next, label)) { + update_label(s, label, -1); + dbuf_putc(&bc_out, OP_drop); + break; + } + /* transform if_false(l1) goto(l2) label(l1) -> if_false(l2) label(l1) */ + if (code_match(&cc, pos_next, OP_goto, -1)) { + int pos1 = cc.pos; + int line1 = cc.line_num; + int col1 = cc.col_num; + if (code_has_label(&cc, pos1, label)) { + if (line1 >= 0) line_num = line1; + if (col1 >= 0) col_num = col1; + pos_next = pos1; + update_label(s, label, -1); + label = cc.label; + op ^= OP_if_true ^ OP_if_false; + } + } + has_label: + add_pc2line_info(s, bc_out.size, line_num, col_num); + if (op == OP_goto) { + pos_next = skip_dead_code(s, bc_buf, bc_len, pos_next, + &line_num, &col_num); + } + assert(label >= 0 && label < s->label_count); + ls = &label_slots[label]; + jp = &s->jump_slots[s->jump_count++]; + jp->op = op; + jp->size = 4; + jp->pos = bc_out.size + 1; + jp->label = label; + + if (ls->addr == -1) { + int diff = ls->pos2 - pos - 1; + if (diff < 128 && (op == OP_if_false || op == OP_if_true || op == OP_goto)) { + jp->size = 1; + jp->op = OP_if_false8 + (op - OP_if_false); + dbuf_putc(&bc_out, OP_if_false8 + (op - OP_if_false)); + dbuf_putc(&bc_out, 0); + if (!add_reloc(ctx, ls, bc_out.size - 1, 1)) + goto fail; + break; + } + if (diff < 32768 && op == OP_goto) { + jp->size = 2; + jp->op = OP_goto16; + dbuf_putc(&bc_out, OP_goto16); + dbuf_put_u16(&bc_out, 0); + if (!add_reloc(ctx, ls, bc_out.size - 2, 2)) + goto fail; + break; + } + } else { + int diff = ls->addr - bc_out.size - 1; + if (diff == (int8_t)diff && (op == OP_if_false || op == OP_if_true || op == OP_goto)) { + jp->size = 1; + jp->op = OP_if_false8 + (op - OP_if_false); + dbuf_putc(&bc_out, OP_if_false8 + (op - OP_if_false)); + dbuf_putc(&bc_out, diff); + break; + } + if (diff == (int16_t)diff && op == OP_goto) { + jp->size = 2; + jp->op = OP_goto16; + dbuf_putc(&bc_out, OP_goto16); + dbuf_put_u16(&bc_out, diff); + break; + } + } + dbuf_putc(&bc_out, op); + dbuf_put_u32(&bc_out, ls->addr - bc_out.size); + if (ls->addr == -1) { + /* unresolved yet: create a new relocation entry */ + if (!add_reloc(ctx, ls, bc_out.size - 4, 4)) + goto fail; + } + break; + case OP_with_get_var: + case OP_with_put_var: + case OP_with_delete_var: + case OP_with_make_ref: + case OP_with_get_ref: + case OP_with_get_ref_undef: + { + JSAtom atom; + int is_with; + + atom = get_u32(bc_buf + pos + 1); + label = get_u32(bc_buf + pos + 5); + is_with = bc_buf[pos + 9]; + label = find_jump_target(s, label, &op1); + assert(label >= 0 && label < s->label_count); + ls = &label_slots[label]; + add_pc2line_info(s, bc_out.size, line_num, col_num); + jp = &s->jump_slots[s->jump_count++]; + jp->op = op; + jp->size = 4; + jp->pos = bc_out.size + 5; + jp->label = label; + dbuf_putc(&bc_out, op); + dbuf_put_u32(&bc_out, atom); + dbuf_put_u32(&bc_out, ls->addr - bc_out.size); + if (ls->addr == -1) { + /* unresolved yet: create a new relocation entry */ + if (!add_reloc(ctx, ls, bc_out.size - 4, 4)) + goto fail; + } + dbuf_putc(&bc_out, is_with); + } + break; + + case OP_drop: + /* remove useless drops before return */ + if (code_match(&cc, pos_next, OP_return_undef, -1)) { + if (cc.line_num >= 0) line_num = cc.line_num; + if (cc.col_num >= 0) col_num = cc.col_num; + break; + } + goto no_change; + + case OP_null: + /* transform null strict_eq into is_null */ + if (code_match(&cc, pos_next, OP_strict_eq, -1)) { + if (cc.line_num >= 0) line_num = cc.line_num; + if (cc.col_num >= 0) col_num = cc.col_num; + add_pc2line_info(s, bc_out.size, line_num, col_num); + dbuf_putc(&bc_out, OP_is_null); + pos_next = cc.pos; + break; + } + /* transform null strict_neq if_false/if_true -> is_null if_true/if_false */ + if (code_match(&cc, pos_next, OP_strict_neq, M2(OP_if_false, OP_if_true), -1)) { + if (cc.line_num >= 0) line_num = cc.line_num; + if (cc.col_num >= 0) col_num = cc.col_num; + add_pc2line_info(s, bc_out.size, line_num, col_num); + dbuf_putc(&bc_out, OP_is_null); + pos_next = cc.pos; + label = cc.label; + op = cc.op ^ OP_if_false ^ OP_if_true; + goto has_label; + } + /* fall thru */ + case OP_push_false: + case OP_push_true: + val = (op == OP_push_true); + if (code_match(&cc, pos_next, M2(OP_if_false, OP_if_true), -1)) { + has_constant_test: + if (cc.line_num >= 0) line_num = cc.line_num; + if (cc.col_num >= 0) col_num = cc.col_num; + if (val == cc.op - OP_if_false) { + /* transform null if_false(l1) -> goto l1 */ + /* transform false if_false(l1) -> goto l1 */ + /* transform true if_true(l1) -> goto l1 */ + pos_next = cc.pos; + op = OP_goto; + label = cc.label; + goto has_goto; + } else { + /* transform null if_true(l1) -> nop */ + /* transform false if_true(l1) -> nop */ + /* transform true if_false(l1) -> nop */ + pos_next = cc.pos; + update_label(s, cc.label, -1); + break; + } + } + goto no_change; + + case OP_push_i32: + /* transform i32(val) neg -> i32(-val) */ + val = get_i32(bc_buf + pos + 1); + if ((val != INT32_MIN && val != 0) + && code_match(&cc, pos_next, OP_neg, -1)) { + if (cc.line_num >= 0) line_num = cc.line_num; + if (cc.col_num >= 0) col_num = cc.col_num; + if (code_match(&cc, cc.pos, OP_drop, -1)) { + if (cc.line_num >= 0) line_num = cc.line_num; + if (cc.col_num >= 0) col_num = cc.col_num; + } else { + add_pc2line_info(s, bc_out.size, line_num, col_num); + push_short_int(&bc_out, -val); + } + pos_next = cc.pos; + break; + } + /* remove push/drop pairs generated by the parser */ + if (code_match(&cc, pos_next, OP_drop, -1)) { + if (cc.line_num >= 0) line_num = cc.line_num; + if (cc.col_num >= 0) col_num = cc.col_num; + pos_next = cc.pos; + break; + } + /* Optimize constant tests: `if (0)`, `if (1)`, `if (!0)`... */ + if (code_match(&cc, pos_next, M2(OP_if_false, OP_if_true), -1)) { + val = (val != 0); + goto has_constant_test; + } + add_pc2line_info(s, bc_out.size, line_num, col_num); + push_short_int(&bc_out, val); + break; + + case OP_push_const: + case OP_fclosure: + { + int idx = get_u32(bc_buf + pos + 1); + if (idx < 256) { + add_pc2line_info(s, bc_out.size, line_num, col_num); + dbuf_putc(&bc_out, OP_push_const8 + op - OP_push_const); + dbuf_putc(&bc_out, idx); + break; + } + } + goto no_change; + + case OP_get_field: + { + JSAtom atom = get_u32(bc_buf + pos + 1); + if (atom == JS_ATOM_length) { + JS_FreeAtom(ctx, atom); + add_pc2line_info(s, bc_out.size, line_num, col_num); + dbuf_putc(&bc_out, OP_get_length); + break; + } + } + goto no_change; + + case OP_push_atom_value: + { + JSAtom atom = get_u32(bc_buf + pos + 1); + /* remove push/drop pairs generated by the parser */ + if (code_match(&cc, pos_next, OP_drop, -1)) { + JS_FreeAtom(ctx, atom); + if (cc.line_num >= 0) line_num = cc.line_num; + if (cc.col_num >= 0) col_num = cc.col_num; + pos_next = cc.pos; + break; + } + if (atom == JS_ATOM_empty_string) { + JS_FreeAtom(ctx, atom); + add_pc2line_info(s, bc_out.size, line_num, col_num); + dbuf_putc(&bc_out, OP_push_empty_string); + break; + } + } + goto no_change; + + case OP_to_propkey: + case OP_to_propkey2: + /* remove redundant to_propkey/to_propkey2 opcodes when storing simple data */ + if (code_match(&cc, pos_next, M3(OP_get_loc, OP_get_arg, OP_get_var_ref), -1, OP_put_array_el, -1) + || code_match(&cc, pos_next, M3(OP_push_i32, OP_push_const, OP_push_atom_value), OP_put_array_el, -1) + || code_match(&cc, pos_next, M4(OP_undefined, OP_null, OP_push_true, OP_push_false), OP_put_array_el, -1)) { + break; + } + goto no_change; + + case OP_undefined: + /* remove push/drop pairs generated by the parser */ + if (code_match(&cc, pos_next, OP_drop, -1)) { + if (cc.line_num >= 0) line_num = cc.line_num; + if (cc.col_num >= 0) col_num = cc.col_num; + pos_next = cc.pos; + break; + } + /* transform undefined return -> return_undefined */ + if (code_match(&cc, pos_next, OP_return, -1)) { + if (cc.line_num >= 0) line_num = cc.line_num; + if (cc.col_num >= 0) col_num = cc.col_num; + add_pc2line_info(s, bc_out.size, line_num, col_num); + dbuf_putc(&bc_out, OP_return_undef); + pos_next = cc.pos; + break; + } + /* transform undefined if_true(l1)/if_false(l1) -> nop/goto(l1) */ + if (code_match(&cc, pos_next, M2(OP_if_false, OP_if_true), -1)) { + val = 0; + goto has_constant_test; + } + /* transform undefined strict_eq -> is_undefined */ + if (code_match(&cc, pos_next, OP_strict_eq, -1)) { + if (cc.line_num >= 0) line_num = cc.line_num; + if (cc.col_num >= 0) col_num = cc.col_num; + add_pc2line_info(s, bc_out.size, line_num, col_num); + dbuf_putc(&bc_out, OP_is_undefined); + pos_next = cc.pos; + break; + } + /* transform undefined strict_neq if_false/if_true -> is_undefined if_true/if_false */ + if (code_match(&cc, pos_next, OP_strict_neq, M2(OP_if_false, OP_if_true), -1)) { + if (cc.line_num >= 0) line_num = cc.line_num; + if (cc.col_num >= 0) col_num = cc.col_num; + add_pc2line_info(s, bc_out.size, line_num, col_num); + dbuf_putc(&bc_out, OP_is_undefined); + pos_next = cc.pos; + label = cc.label; + op = cc.op ^ OP_if_false ^ OP_if_true; + goto has_label; + } + goto no_change; + + case OP_insert2: + /* Transformation: + insert2 put_field(a) drop -> put_field(a) + insert2 put_var_strict(a) drop -> put_var_strict(a) + */ + if (code_match(&cc, pos_next, M2(OP_put_field, OP_put_var_strict), OP_drop, -1)) { + if (cc.line_num >= 0) line_num = cc.line_num; + if (cc.col_num >= 0) col_num = cc.col_num; + add_pc2line_info(s, bc_out.size, line_num, col_num); + dbuf_putc(&bc_out, cc.op); + dbuf_put_u32(&bc_out, cc.atom); + pos_next = cc.pos; + break; + } + goto no_change; + + case OP_dup: + { + /* Transformation: dup put_x(n) drop -> put_x(n) */ + int op1, line2 = -1; + /* Transformation: dup put_x(n) -> set_x(n) */ + if (code_match(&cc, pos_next, M3(OP_put_loc, OP_put_arg, OP_put_var_ref), -1, -1)) { + if (cc.line_num >= 0) line_num = cc.line_num; + if (cc.col_num >= 0) col_num = cc.col_num; + op1 = cc.op + 1; /* put_x -> set_x */ + pos_next = cc.pos; + if (code_match(&cc, cc.pos, OP_drop, -1)) { + if (cc.line_num >= 0) line_num = cc.line_num; + if (cc.col_num >= 0) col_num = cc.col_num; + op1 -= 1; /* set_x drop -> put_x */ + pos_next = cc.pos; + if (code_match(&cc, cc.pos, op1 - 1, cc.idx, -1)) { + line2 = cc.line_num; /* delay line number update */ + op1 += 1; /* put_x(n) get_x(n) -> set_x(n) */ + pos_next = cc.pos; + } + } + add_pc2line_info(s, bc_out.size, line_num, col_num); + put_short_code(&bc_out, op1, cc.idx); + if (line2 >= 0) line_num = line2; + break; + } + } + goto no_change; + + case OP_swap: + // transformation: swap swap -> nothing! + if (code_match(&cc, pos_next, OP_swap, -1, -1)) { + if (cc.line_num >= 0) line_num = cc.line_num; + if (cc.col_num >= 0) col_num = cc.col_num; + pos_next = cc.pos; + break; + } + goto no_change; + + case OP_get_loc: + { + /* transformation: + get_loc(n) post_dec put_loc(n) drop -> dec_loc(n) + get_loc(n) post_inc put_loc(n) drop -> inc_loc(n) + get_loc(n) dec dup put_loc(n) drop -> dec_loc(n) + get_loc(n) inc dup put_loc(n) drop -> inc_loc(n) + */ + int idx; + idx = get_u16(bc_buf + pos + 1); + if (idx >= 256) + goto no_change; + if (code_match(&cc, pos_next, M2(OP_post_dec, OP_post_inc), OP_put_loc, idx, OP_drop, -1) || + code_match(&cc, pos_next, M2(OP_dec, OP_inc), OP_dup, OP_put_loc, idx, OP_drop, -1)) { + if (cc.line_num >= 0) line_num = cc.line_num; + if (cc.col_num >= 0) col_num = cc.col_num; + add_pc2line_info(s, bc_out.size, line_num, col_num); + dbuf_putc(&bc_out, (cc.op == OP_inc || cc.op == OP_post_inc) ? OP_inc_loc : OP_dec_loc); + dbuf_putc(&bc_out, idx); + pos_next = cc.pos; + break; + } + /* transformation: + get_loc(n) push_atom_value(x) add dup put_loc(n) drop -> push_atom_value(x) add_loc(n) + */ + if (code_match(&cc, pos_next, OP_push_atom_value, OP_add, OP_dup, OP_put_loc, idx, OP_drop, -1)) { + if (cc.line_num >= 0) line_num = cc.line_num; + if (cc.col_num >= 0) col_num = cc.col_num; + add_pc2line_info(s, bc_out.size, line_num, col_num); + if (cc.atom == JS_ATOM_empty_string) { + JS_FreeAtom(ctx, cc.atom); + dbuf_putc(&bc_out, OP_push_empty_string); + } else { + dbuf_putc(&bc_out, OP_push_atom_value); + dbuf_put_u32(&bc_out, cc.atom); + } + dbuf_putc(&bc_out, OP_add_loc); + dbuf_putc(&bc_out, idx); + pos_next = cc.pos; + break; + } + /* transformation: + get_loc(n) push_i32(x) add dup put_loc(n) drop -> push_i32(x) add_loc(n) + */ + if (code_match(&cc, pos_next, OP_push_i32, OP_add, OP_dup, OP_put_loc, idx, OP_drop, -1)) { + if (cc.line_num >= 0) line_num = cc.line_num; + if (cc.col_num >= 0) col_num = cc.col_num; + add_pc2line_info(s, bc_out.size, line_num, col_num); + push_short_int(&bc_out, cc.label); + dbuf_putc(&bc_out, OP_add_loc); + dbuf_putc(&bc_out, idx); + pos_next = cc.pos; + break; + } + /* transformation: XXX: also do these: + get_loc(n) get_loc(x) add dup put_loc(n) drop -> get_loc(x) add_loc(n) + get_loc(n) get_arg(x) add dup put_loc(n) drop -> get_arg(x) add_loc(n) + get_loc(n) get_var_ref(x) add dup put_loc(n) drop -> get_var_ref(x) add_loc(n) + */ + if (code_match(&cc, pos_next, M3(OP_get_loc, OP_get_arg, OP_get_var_ref), -1, OP_add, OP_dup, OP_put_loc, idx, OP_drop, -1)) { + if (cc.line_num >= 0) line_num = cc.line_num; + if (cc.col_num >= 0) col_num = cc.col_num; + add_pc2line_info(s, bc_out.size, line_num, col_num); + put_short_code(&bc_out, cc.op, cc.idx); + dbuf_putc(&bc_out, OP_add_loc); + dbuf_putc(&bc_out, idx); + pos_next = cc.pos; + break; + } + /* transformation: get_loc(0) get_loc(1) -> get_loc0_loc1 */ + if (idx == 0 && code_match(&cc, pos_next, OP_get_loc, 1, -1)) { + if (cc.line_num >= 0) line_num = cc.line_num; + if (cc.col_num >= 0) col_num = cc.col_num; + add_pc2line_info(s, bc_out.size, line_num, col_num); + dbuf_putc(&bc_out, OP_get_loc0_loc1); + pos_next = cc.pos; + break; + } + add_pc2line_info(s, bc_out.size, line_num, col_num); + put_short_code(&bc_out, op, idx); + } + break; + case OP_get_arg: + case OP_get_var_ref: + { + int idx; + idx = get_u16(bc_buf + pos + 1); + add_pc2line_info(s, bc_out.size, line_num, col_num); + put_short_code(&bc_out, op, idx); + } + break; + case OP_put_loc: + case OP_put_arg: + case OP_put_var_ref: + { + /* transformation: put_x(n) get_x(n) -> set_x(n) */ + int idx; + idx = get_u16(bc_buf + pos + 1); + if (code_match(&cc, pos_next, op - 1, idx, -1)) { + if (cc.line_num >= 0) line_num = cc.line_num; + if (cc.col_num >= 0) col_num = cc.col_num; + add_pc2line_info(s, bc_out.size, line_num, col_num); + put_short_code(&bc_out, op + 1, idx); + pos_next = cc.pos; + break; + } + add_pc2line_info(s, bc_out.size, line_num, col_num); + put_short_code(&bc_out, op, idx); + } + break; + + case OP_post_inc: + case OP_post_dec: + { + /* transformation: + post_inc put_x drop -> inc put_x + post_inc perm3 put_field drop -> inc put_field + post_inc perm3 put_var_strict drop -> inc put_var_strict + post_inc perm4 put_array_el drop -> inc put_array_el + */ + int op1, idx; + if (code_match(&cc, pos_next, M3(OP_put_loc, OP_put_arg, OP_put_var_ref), -1, OP_drop, -1)) { + if (cc.line_num >= 0) line_num = cc.line_num; + if (cc.col_num >= 0) col_num = cc.col_num; + op1 = cc.op; + idx = cc.idx; + pos_next = cc.pos; + if (code_match(&cc, cc.pos, op1 - 1, idx, -1)) { + if (cc.line_num >= 0) line_num = cc.line_num; + if (cc.col_num >= 0) col_num = cc.col_num; + op1 += 1; /* put_x(n) get_x(n) -> set_x(n) */ + pos_next = cc.pos; + } + add_pc2line_info(s, bc_out.size, line_num, col_num); + dbuf_putc(&bc_out, OP_dec + (op - OP_post_dec)); + put_short_code(&bc_out, op1, idx); + break; + } + if (code_match(&cc, pos_next, OP_perm3, M2(OP_put_field, OP_put_var_strict), OP_drop, -1)) { + if (cc.line_num >= 0) line_num = cc.line_num; + if (cc.col_num >= 0) col_num = cc.col_num; + add_pc2line_info(s, bc_out.size, line_num, col_num); + dbuf_putc(&bc_out, OP_dec + (op - OP_post_dec)); + dbuf_putc(&bc_out, cc.op); + dbuf_put_u32(&bc_out, cc.atom); + pos_next = cc.pos; + break; + } + if (code_match(&cc, pos_next, OP_perm4, OP_put_array_el, OP_drop, -1)) { + if (cc.line_num >= 0) line_num = cc.line_num; + if (cc.col_num >= 0) col_num = cc.col_num; + add_pc2line_info(s, bc_out.size, line_num, col_num); + dbuf_putc(&bc_out, OP_dec + (op - OP_post_dec)); + dbuf_putc(&bc_out, OP_put_array_el); + pos_next = cc.pos; + break; + } + } + goto no_change; + + case OP_typeof: + /* simplify typeof tests */ + if (code_match(&cc, pos_next, OP_push_atom_value, M4(OP_strict_eq, OP_strict_neq, OP_eq, OP_neq), -1)) { + if (cc.line_num >= 0) line_num = cc.line_num; + if (cc.col_num >= 0) col_num = cc.col_num; + int op1 = (cc.op == OP_strict_eq || cc.op == OP_eq) ? OP_strict_eq : OP_strict_neq; + int op2 = -1; + switch (cc.atom) { + case JS_ATOM_undefined: + op2 = OP_typeof_is_undefined; + break; + case JS_ATOM_function: + op2 = OP_typeof_is_function; + break; + } + if (op2 >= 0) { + /* transform typeof(s) == "<type>" into is_<type> */ + if (op1 == OP_strict_eq) { + add_pc2line_info(s, bc_out.size, line_num, col_num); + dbuf_putc(&bc_out, op2); + JS_FreeAtom(ctx, cc.atom); + pos_next = cc.pos; + break; + } + if (op1 == OP_strict_neq && code_match(&cc, cc.pos, OP_if_false, -1)) { + /* transform typeof(s) != "<type>" if_false into is_<type> if_true */ + if (cc.line_num >= 0) line_num = cc.line_num; + if (cc.col_num >= 0) col_num = cc.col_num; + add_pc2line_info(s, bc_out.size, line_num, col_num); + dbuf_putc(&bc_out, op2); + JS_FreeAtom(ctx, cc.atom); + pos_next = cc.pos; + label = cc.label; + op = OP_if_true; + goto has_label; + } + } + } + goto no_change; + + default: + no_change: + add_pc2line_info(s, bc_out.size, line_num, col_num); + dbuf_put(&bc_out, bc_buf + pos, len); + break; + } + } + + /* check that there were no missing labels */ + for(i = 0; i < s->label_count; i++) { + assert(label_slots[i].first_reloc == NULL); + } + + /* more jump optimizations */ + patch_offsets = 0; + for (i = 0, jp = s->jump_slots; i < s->jump_count; i++, jp++) { + LabelSlot *ls; + JumpSlot *jp1; + int j, pos, diff, delta; + + delta = 3; + switch (op = jp->op) { + case OP_goto16: + delta = 1; + /* fall thru */ + case OP_if_false: + case OP_if_true: + case OP_goto: + pos = jp->pos; + diff = s->label_slots[jp->label].addr - pos; + if (diff >= -128 && diff <= 127 + delta) { + //put_u8(bc_out.buf + pos, diff); + jp->size = 1; + if (op == OP_goto16) { + bc_out.buf[pos - 1] = jp->op = OP_goto8; + } else { + bc_out.buf[pos - 1] = jp->op = OP_if_false8 + (op - OP_if_false); + } + goto shrink; + } else + if (diff == (int16_t)diff && op == OP_goto) { + //put_u16(bc_out.buf + pos, diff); + jp->size = 2; + delta = 2; + bc_out.buf[pos - 1] = jp->op = OP_goto16; + shrink: + /* XXX: should reduce complexity, using 2 finger copy scheme */ + memmove(bc_out.buf + pos + jp->size, bc_out.buf + pos + jp->size + delta, + bc_out.size - pos - jp->size - delta); + bc_out.size -= delta; + patch_offsets++; + for (j = 0, ls = s->label_slots; j < s->label_count; j++, ls++) { + if (ls->addr > pos) + ls->addr -= delta; + } + for (j = i + 1, jp1 = jp + 1; j < s->jump_count; j++, jp1++) { + if (jp1->pos > pos) + jp1->pos -= delta; + } + for (j = 0; j < s->source_loc_count; j++) { + if (s->source_loc_slots[j].pc > pos) + s->source_loc_slots[j].pc -= delta; + } + continue; + } + break; + } + } + + if (patch_offsets) { + JumpSlot *jp1; + int j; + for (j = 0, jp1 = s->jump_slots; j < s->jump_count; j++, jp1++) { + int diff1 = s->label_slots[jp1->label].addr - jp1->pos; + switch (jp1->size) { + case 1: + put_u8(bc_out.buf + jp1->pos, diff1); + break; + case 2: + put_u16(bc_out.buf + jp1->pos, diff1); + break; + case 4: + put_u32(bc_out.buf + jp1->pos, diff1); + break; + } + } + } + + js_free(ctx, s->jump_slots); + s->jump_slots = NULL; + js_free(ctx, s->label_slots); + s->label_slots = NULL; + /* XXX: should delay until copying to runtime bytecode function */ + compute_pc2line_info(s); + js_free(ctx, s->source_loc_slots); + s->source_loc_slots = NULL; + /* set the new byte code */ + dbuf_free(&s->byte_code); + s->byte_code = bc_out; + s->use_short_opcodes = TRUE; + if (dbuf_error(&s->byte_code)) { + JS_ThrowOutOfMemory(ctx); + return -1; + } + return 0; + fail: + /* XXX: not safe */ + dbuf_free(&bc_out); + return -1; +} + +/* compute the maximum stack size needed by the function */ + +typedef struct StackSizeState { + int bc_len; + int stack_len_max; + uint16_t *stack_level_tab; + int32_t *catch_pos_tab; + int *pc_stack; + int pc_stack_len; + int pc_stack_size; +} StackSizeState; + +/* 'op' is only used for error indication */ +static __exception int ss_check(JSContext *ctx, StackSizeState *s, + int pos, int op, int stack_len, int catch_pos) +{ + if ((unsigned)pos >= s->bc_len) { + JS_ThrowInternalError(ctx, "bytecode buffer overflow (op=%d, pc=%d)", op, pos); + return -1; + } + if (stack_len > s->stack_len_max) { + s->stack_len_max = stack_len; + if (s->stack_len_max > JS_STACK_SIZE_MAX) { + JS_ThrowInternalError(ctx, "stack overflow (op=%d, pc=%d)", op, pos); + return -1; + } + } + if (s->stack_level_tab[pos] != 0xffff) { + /* already explored: check that the stack size is consistent */ + if (s->stack_level_tab[pos] != stack_len) { + JS_ThrowInternalError(ctx, "inconsistent stack size: %d %d (pc=%d)", + s->stack_level_tab[pos], stack_len, pos); + return -1; + } else if (s->catch_pos_tab[pos] != catch_pos) { + JS_ThrowInternalError(ctx, "inconsistent catch position: %d %d (pc=%d)", + s->catch_pos_tab[pos], catch_pos, pos); + return -1; + } else { + return 0; + } + } + + /* mark as explored and store the stack size */ + s->stack_level_tab[pos] = stack_len; + s->catch_pos_tab[pos] = catch_pos; + + /* queue the new PC to explore */ + if (js_resize_array(ctx, (void **)&s->pc_stack, sizeof(s->pc_stack[0]), + &s->pc_stack_size, s->pc_stack_len + 1)) + return -1; + s->pc_stack[s->pc_stack_len++] = pos; + return 0; +} + +static __exception int compute_stack_size(JSContext *ctx, + JSFunctionDef *fd, + int *pstack_size) +{ + StackSizeState s_s, *s = &s_s; + int i, diff, n_pop, pos_next, stack_len, pos, op, catch_pos, catch_level; + const JSOpCode *oi; + const uint8_t *bc_buf; + + bc_buf = fd->byte_code.buf; + s->bc_len = fd->byte_code.size; + /* bc_len > 0 */ + s->stack_level_tab = js_malloc(ctx, sizeof(s->stack_level_tab[0]) * + s->bc_len); + if (!s->stack_level_tab) + return -1; + for(i = 0; i < s->bc_len; i++) + s->stack_level_tab[i] = 0xffff; + s->pc_stack = NULL; + s->catch_pos_tab = js_malloc(ctx, sizeof(s->catch_pos_tab[0]) * s->bc_len); + if (!s->catch_pos_tab) + goto fail; + + s->stack_len_max = 0; + s->pc_stack_len = 0; + s->pc_stack_size = 0; + + /* breadth-first graph exploration */ + if (ss_check(ctx, s, 0, OP_invalid, 0, -1)) + goto fail; + + while (s->pc_stack_len > 0) { + pos = s->pc_stack[--s->pc_stack_len]; + stack_len = s->stack_level_tab[pos]; + catch_pos = s->catch_pos_tab[pos]; + op = bc_buf[pos]; + if (op == 0 || op >= OP_COUNT) { + JS_ThrowInternalError(ctx, "invalid opcode (op=%d, pc=%d)", op, pos); + goto fail; + } + oi = &short_opcode_info(op); +#ifdef DUMP_BYTECODE_STACK + if (check_dump_flag(ctx->rt, DUMP_BYTECODE_STACK)) + printf("%5d: %10s %5d %5d\n", pos, oi->name, stack_len, catch_pos); +#endif + pos_next = pos + oi->size; + if (pos_next > s->bc_len) { + JS_ThrowInternalError(ctx, "bytecode buffer overflow (op=%d, pc=%d)", op, pos); + goto fail; + } + n_pop = oi->n_pop; + /* call pops a variable number of arguments */ + if (oi->fmt == OP_FMT_npop || oi->fmt == OP_FMT_npop_u16) { + n_pop += get_u16(bc_buf + pos + 1); + } else if (oi->fmt == OP_FMT_npopx) { + n_pop += op - OP_call0; + } + + if (stack_len < n_pop) { + JS_ThrowInternalError(ctx, "stack underflow (op=%d, pc=%d)", op, pos); + goto fail; + } + stack_len += oi->n_push - n_pop; + if (stack_len > s->stack_len_max) { + s->stack_len_max = stack_len; + if (s->stack_len_max > JS_STACK_SIZE_MAX) { + JS_ThrowInternalError(ctx, "stack overflow (op=%d, pc=%d)", op, pos); + goto fail; + } + } + switch(op) { + case OP_tail_call: + case OP_tail_call_method: + case OP_return: + case OP_return_undef: + case OP_return_async: + case OP_throw: + case OP_throw_error: + case OP_ret: + goto done_insn; + case OP_goto: + diff = get_u32(bc_buf + pos + 1); + pos_next = pos + 1 + diff; + break; + case OP_goto16: + diff = (int16_t)get_u16(bc_buf + pos + 1); + pos_next = pos + 1 + diff; + break; + case OP_goto8: + diff = (int8_t)bc_buf[pos + 1]; + pos_next = pos + 1 + diff; + break; + case OP_if_true8: + case OP_if_false8: + diff = (int8_t)bc_buf[pos + 1]; + if (ss_check(ctx, s, pos + 1 + diff, op, stack_len, catch_pos)) + goto fail; + break; + case OP_if_true: + case OP_if_false: + diff = get_u32(bc_buf + pos + 1); + if (ss_check(ctx, s, pos + 1 + diff, op, stack_len, catch_pos)) + goto fail; + break; + case OP_gosub: + diff = get_u32(bc_buf + pos + 1); + if (ss_check(ctx, s, pos + 1 + diff, op, stack_len + 1, catch_pos)) + goto fail; + break; + case OP_with_get_var: + case OP_with_delete_var: + diff = get_u32(bc_buf + pos + 5); + if (ss_check(ctx, s, pos + 5 + diff, op, stack_len + 1, catch_pos)) + goto fail; + break; + case OP_with_make_ref: + case OP_with_get_ref: + case OP_with_get_ref_undef: + diff = get_u32(bc_buf + pos + 5); + if (ss_check(ctx, s, pos + 5 + diff, op, stack_len + 2, catch_pos)) + goto fail; + break; + case OP_with_put_var: + diff = get_u32(bc_buf + pos + 5); + if (ss_check(ctx, s, pos + 5 + diff, op, stack_len - 1, catch_pos)) + goto fail; + break; + case OP_catch: + diff = get_u32(bc_buf + pos + 1); + if (ss_check(ctx, s, pos + 1 + diff, op, stack_len, catch_pos)) + goto fail; + catch_pos = pos; + break; + case OP_for_of_start: + case OP_for_await_of_start: + catch_pos = pos; + break; + /* we assume the catch offset entry is only removed with + some op codes */ + case OP_drop: + catch_level = stack_len; + goto check_catch; + case OP_nip: + catch_level = stack_len - 1; + goto check_catch; + case OP_nip1: + catch_level = stack_len - 1; + goto check_catch; + case OP_iterator_close: + catch_level = stack_len + 2; + check_catch: + /* Note: for for_of_start/for_await_of_start we consider + the catch offset is on the first stack entry instead of + the thirst */ + if (catch_pos >= 0) { + int level; + level = s->stack_level_tab[catch_pos]; + if (bc_buf[catch_pos] != OP_catch) + level++; /* for_of_start, for_wait_of_start */ + /* catch_level = stack_level before op_catch is executed ? */ + if (catch_level == level) { + catch_pos = s->catch_pos_tab[catch_pos]; + } + } + break; + case OP_nip_catch: + if (catch_pos < 0) { + JS_ThrowInternalError(ctx, "nip_catch: no catch op (pc=%d)", pos); + goto fail; + } + stack_len = s->stack_level_tab[catch_pos]; + if (bc_buf[catch_pos] != OP_catch) + stack_len++; /* for_of_start, for_wait_of_start */ + stack_len++; /* no stack overflow is possible by construction */ + catch_pos = s->catch_pos_tab[catch_pos]; + break; + default: + break; + } + if (ss_check(ctx, s, pos_next, op, stack_len, catch_pos)) + goto fail; + done_insn: ; + } + js_free(ctx, s->pc_stack); + js_free(ctx, s->catch_pos_tab); + js_free(ctx, s->stack_level_tab); + *pstack_size = s->stack_len_max; + return 0; + fail: + js_free(ctx, s->pc_stack); + js_free(ctx, s->catch_pos_tab); + js_free(ctx, s->stack_level_tab); + *pstack_size = 0; + return -1; +} + +static int add_module_variables(JSContext *ctx, JSFunctionDef *fd) +{ + int i, idx; + JSModuleDef *m = fd->module; + JSExportEntry *me; + JSGlobalVar *hf; + + /* The imported global variables were added as closure variables + in js_parse_import(). We add here the module global + variables. */ + + for(i = 0; i < fd->global_var_count; i++) { + hf = &fd->global_vars[i]; + if (add_closure_var(ctx, fd, TRUE, FALSE, i, hf->var_name, hf->is_const, + hf->is_lexical, JS_VAR_NORMAL) < 0) + return -1; + } + + /* resolve the variable names of the local exports */ + for(i = 0; i < m->export_entries_count; i++) { + me = &m->export_entries[i]; + if (me->export_type == JS_EXPORT_TYPE_LOCAL) { + idx = find_closure_var(ctx, fd, me->local_name); + if (idx < 0) { + // XXX: add_module_variables() should take JSParseState *s and use js_parse_error_atom + JS_ThrowSyntaxErrorAtom(ctx, "exported variable '%s' does not exist", + me->local_name); + return -1; + } + me->u.local.var_idx = idx; + } + } + return 0; +} + +/* create a function object from a function definition. The function + definition is freed. All the child functions are also created. It + must be done this way to resolve all the variables. */ +static JSValue js_create_function(JSContext *ctx, JSFunctionDef *fd) +{ + JSValue func_obj; + JSFunctionBytecode *b; + struct list_head *el, *el1; + int stack_size, scope, idx; + int function_size, byte_code_offset, cpool_offset; + int closure_var_offset, vardefs_offset; + + /* recompute scope linkage */ + for (scope = 0; scope < fd->scope_count; scope++) { + fd->scopes[scope].first = -1; + } + if (fd->has_parameter_expressions) { + /* special end of variable list marker for the argument scope */ + fd->scopes[ARG_SCOPE_INDEX].first = ARG_SCOPE_END; + } + for (idx = 0; idx < fd->var_count; idx++) { + JSVarDef *vd = &fd->vars[idx]; + vd->scope_next = fd->scopes[vd->scope_level].first; + fd->scopes[vd->scope_level].first = idx; + } + for (scope = 2; scope < fd->scope_count; scope++) { + JSVarScope *sd = &fd->scopes[scope]; + if (sd->first < 0) + sd->first = fd->scopes[sd->parent].first; + } + for (idx = 0; idx < fd->var_count; idx++) { + JSVarDef *vd = &fd->vars[idx]; + if (vd->scope_next < 0 && vd->scope_level > 1) { + scope = fd->scopes[vd->scope_level].parent; + vd->scope_next = fd->scopes[scope].first; + } + } + + /* if the function contains an eval call, the closure variables + are used to compile the eval and they must be ordered by scope, + so it is necessary to create the closure variables before any + other variable lookup is done. */ + if (fd->has_eval_call) + add_eval_variables(ctx, fd); + + /* add the module global variables in the closure */ + if (fd->module) { + if (add_module_variables(ctx, fd)) + goto fail; + } + + /* first create all the child functions */ + list_for_each_safe(el, el1, &fd->child_list) { + JSFunctionDef *fd1; + int cpool_idx; + + fd1 = list_entry(el, JSFunctionDef, link); + cpool_idx = fd1->parent_cpool_idx; + func_obj = js_create_function(ctx, fd1); + if (JS_IsException(func_obj)) + goto fail; + /* save it in the constant pool */ + assert(cpool_idx >= 0); + fd->cpool[cpool_idx] = func_obj; + } + +#ifdef DUMP_BYTECODE_PASS1 + if (check_dump_flag(ctx->rt, DUMP_BYTECODE_PASS1)) { + printf("pass 1\n"); + dump_byte_code(ctx, 1, fd->byte_code.buf, fd->byte_code.size, + fd->args, fd->arg_count, fd->vars, fd->var_count, + fd->closure_var, fd->closure_var_count, + fd->cpool, fd->cpool_count, fd->source, fd->line_num, + fd->label_slots, NULL, 0); + printf("\n"); + } +#endif + + if (resolve_variables(ctx, fd)) + goto fail; + +#ifdef DUMP_BYTECODE_PASS2 + if (check_dump_flag(ctx->rt, DUMP_BYTECODE_PASS2)) { + printf("pass 2\n"); + dump_byte_code(ctx, 2, fd->byte_code.buf, fd->byte_code.size, + fd->args, fd->arg_count, fd->vars, fd->var_count, + fd->closure_var, fd->closure_var_count, + fd->cpool, fd->cpool_count, fd->source, fd->line_num, + fd->label_slots, NULL, 0); + printf("\n"); + } +#endif + + if (resolve_labels(ctx, fd)) + goto fail; + + if (compute_stack_size(ctx, fd, &stack_size) < 0) + goto fail; + + function_size = sizeof(*b); + cpool_offset = function_size; + function_size += fd->cpool_count * sizeof(*fd->cpool); + vardefs_offset = function_size; + function_size += (fd->arg_count + fd->var_count) * sizeof(*b->vardefs); + closure_var_offset = function_size; + function_size += fd->closure_var_count * sizeof(*fd->closure_var); + byte_code_offset = function_size; + function_size += fd->byte_code.size; + + b = js_mallocz(ctx, function_size); + if (!b) + goto fail; + b->header.ref_count = 1; + + b->byte_code_buf = (void *)((uint8_t*)b + byte_code_offset); + b->byte_code_len = fd->byte_code.size; + memcpy(b->byte_code_buf, fd->byte_code.buf, fd->byte_code.size); + js_free(ctx, fd->byte_code.buf); + fd->byte_code.buf = NULL; + + b->func_name = fd->func_name; + if (fd->arg_count + fd->var_count > 0) { + b->vardefs = (void *)((uint8_t*)b + vardefs_offset); + if (fd->arg_count > 0) + memcpy(b->vardefs, fd->args, fd->arg_count * sizeof(fd->args[0])); + if (fd->var_count > 0) + memcpy(b->vardefs + fd->arg_count, fd->vars, fd->var_count * sizeof(fd->vars[0])); + b->var_count = fd->var_count; + b->arg_count = fd->arg_count; + b->defined_arg_count = fd->defined_arg_count; + js_free(ctx, fd->args); + js_free(ctx, fd->vars); + js_free(ctx, fd->vars_htab); + } + b->cpool_count = fd->cpool_count; + if (b->cpool_count) { + b->cpool = (void *)((uint8_t*)b + cpool_offset); + memcpy(b->cpool, fd->cpool, b->cpool_count * sizeof(*b->cpool)); + } + js_free(ctx, fd->cpool); + fd->cpool = NULL; + + b->stack_size = stack_size; + + /* XXX: source and pc2line info should be packed at the end of the + JSFunctionBytecode structure, avoiding allocation overhead + */ + b->filename = fd->filename; + b->line_num = fd->line_num; + b->col_num = fd->col_num; + + b->pc2line_buf = js_realloc(ctx, fd->pc2line.buf, fd->pc2line.size); + if (!b->pc2line_buf) + b->pc2line_buf = fd->pc2line.buf; + b->pc2line_len = fd->pc2line.size; + b->source = fd->source; + b->source_len = fd->source_len; + + if (fd->scopes != fd->def_scope_array) + js_free(ctx, fd->scopes); + + b->closure_var_count = fd->closure_var_count; + if (b->closure_var_count) { + b->closure_var = (void *)((uint8_t*)b + closure_var_offset); + memcpy(b->closure_var, fd->closure_var, b->closure_var_count * sizeof(*b->closure_var)); + } + js_free(ctx, fd->closure_var); + fd->closure_var = NULL; + + b->has_prototype = fd->has_prototype; + b->has_simple_parameter_list = fd->has_simple_parameter_list; + b->is_strict_mode = fd->is_strict_mode; + b->is_derived_class_constructor = fd->is_derived_class_constructor; + b->func_kind = fd->func_kind; + b->need_home_object = (fd->home_object_var_idx >= 0 || + fd->need_home_object); + b->new_target_allowed = fd->new_target_allowed; + b->super_call_allowed = fd->super_call_allowed; + b->super_allowed = fd->super_allowed; + b->arguments_allowed = fd->arguments_allowed; + b->backtrace_barrier = fd->backtrace_barrier; + b->realm = JS_DupContext(ctx); + b->ic = fd->ic; + fd->ic = NULL; + rebuild_ic(ctx, b->ic); + if (b->ic->count == 0) { + free_ic(ctx->rt, b->ic); + b->ic = NULL; + } + + add_gc_object(ctx->rt, &b->header, JS_GC_OBJ_TYPE_FUNCTION_BYTECODE); + +#ifdef DUMP_BYTECODE_FINAL + if (check_dump_flag(ctx->rt, DUMP_BYTECODE_FINAL)) + js_dump_function_bytecode(ctx, b); +#endif + + if (fd->parent) { + /* remove from parent list */ + list_del(&fd->link); + } + + js_free(ctx, fd); + return JS_MKPTR(JS_TAG_FUNCTION_BYTECODE, b); + fail: + js_free_function_def(ctx, fd); + return JS_EXCEPTION; +} + +static void free_function_bytecode(JSRuntime *rt, JSFunctionBytecode *b) +{ + int i; + + free_bytecode_atoms(rt, b->byte_code_buf, b->byte_code_len, TRUE); + + if (b->ic) + free_ic(rt, b->ic); + + if (b->vardefs) { + for(i = 0; i < b->arg_count + b->var_count; i++) { + JS_FreeAtomRT(rt, b->vardefs[i].var_name); + } + } + for(i = 0; i < b->cpool_count; i++) + JS_FreeValueRT(rt, b->cpool[i]); + + for(i = 0; i < b->closure_var_count; i++) { + JSClosureVar *cv = &b->closure_var[i]; + JS_FreeAtomRT(rt, cv->var_name); + } + if (b->realm) + JS_FreeContext(b->realm); + + JS_FreeAtomRT(rt, b->func_name); + JS_FreeAtomRT(rt, b->filename); + js_free_rt(rt, b->pc2line_buf); + js_free_rt(rt, b->source); + + remove_gc_object(&b->header); + if (rt->gc_phase == JS_GC_PHASE_REMOVE_CYCLES && b->header.ref_count != 0) { + list_add_tail(&b->header.link, &rt->gc_zero_ref_count_list); + } else { + js_free_rt(rt, b); + } +} + +static __exception int js_parse_directives(JSParseState *s) +{ + char str[20]; + JSParsePos pos; + BOOL has_semi; + + if (s->token.val != TOK_STRING) + return 0; + + js_parse_get_pos(s, &pos); + + while(s->token.val == TOK_STRING) { + /* Copy actual source string representation */ + snprintf(str, sizeof str, "%.*s", + (int)(s->buf_ptr - s->token.ptr - 2), s->token.ptr + 1); + + if (next_token(s)) + return -1; + + has_semi = FALSE; + switch (s->token.val) { + case ';': + if (next_token(s)) + return -1; + has_semi = TRUE; + break; + case '}': + case TOK_EOF: + has_semi = TRUE; + break; + case TOK_NUMBER: + case TOK_STRING: + case TOK_TEMPLATE: + case TOK_IDENT: + case TOK_REGEXP: + case TOK_DEC: + case TOK_INC: + case TOK_NULL: + case TOK_FALSE: + case TOK_TRUE: + case TOK_IF: + case TOK_RETURN: + case TOK_VAR: + case TOK_THIS: + case TOK_DELETE: + case TOK_TYPEOF: + case TOK_NEW: + case TOK_DO: + case TOK_WHILE: + case TOK_FOR: + case TOK_SWITCH: + case TOK_THROW: + case TOK_TRY: + case TOK_FUNCTION: + case TOK_DEBUGGER: + case TOK_WITH: + case TOK_CLASS: + case TOK_CONST: + case TOK_ENUM: + case TOK_EXPORT: + case TOK_IMPORT: + case TOK_SUPER: + case TOK_INTERFACE: + case TOK_LET: + case TOK_PACKAGE: + case TOK_PRIVATE: + case TOK_PROTECTED: + case TOK_PUBLIC: + case TOK_STATIC: + /* automatic insertion of ';' */ + if (s->got_lf) + has_semi = TRUE; + break; + default: + break; + } + if (!has_semi) + break; + if (!strcmp(str, "use strict")) { + s->cur_func->has_use_strict = TRUE; + s->cur_func->is_strict_mode = TRUE; + } + } + return js_parse_seek_token(s, &pos); +} + +static BOOL js_invalid_strict_name(JSAtom name) { + switch (name) { + case JS_ATOM_eval: + case JS_ATOM_arguments: + case JS_ATOM_implements: // future strict reserved words + case JS_ATOM_interface: + case JS_ATOM_let: + case JS_ATOM_package: + case JS_ATOM_private: + case JS_ATOM_protected: + case JS_ATOM_public: + case JS_ATOM_static: + case JS_ATOM_yield: + return TRUE; + default: + return FALSE; + } +} + +static int js_parse_function_check_names(JSParseState *s, JSFunctionDef *fd, + JSAtom func_name) +{ + JSAtom name; + int i, idx; + + if (fd->is_strict_mode) { + if (!fd->has_simple_parameter_list && fd->has_use_strict) { + return js_parse_error(s, "\"use strict\" not allowed in function with default or destructuring parameter"); + } + if (js_invalid_strict_name(func_name)) { + return js_parse_error(s, "invalid function name in strict code"); + } + for (idx = 0; idx < fd->arg_count; idx++) { + name = fd->args[idx].var_name; + if (js_invalid_strict_name(name)) { + return js_parse_error(s, "invalid argument name in strict code"); + } + } + } + /* check async_generator case */ + if (fd->is_strict_mode + || !fd->has_simple_parameter_list + || (fd->func_type == JS_PARSE_FUNC_METHOD && fd->func_kind == JS_FUNC_ASYNC) + || fd->func_type == JS_PARSE_FUNC_ARROW + || fd->func_type == JS_PARSE_FUNC_METHOD) { + for (idx = 0; idx < fd->arg_count; idx++) { + name = fd->args[idx].var_name; + if (name != JS_ATOM_NULL) { + for (i = 0; i < idx; i++) { + if (fd->args[i].var_name == name) + goto duplicate; + } + /* Check if argument name duplicates a destructuring parameter */ + /* XXX: should have a flag for such variables */ + for (i = 0; i < fd->var_count; i++) { + if (fd->vars[i].var_name == name && + fd->vars[i].scope_level == 0) + goto duplicate; + } + } + } + } + return 0; + +duplicate: + return js_parse_error(s, "Duplicate parameter name not allowed in this context"); +} + +/* create a function to initialize class fields */ +static JSFunctionDef *js_parse_function_class_fields_init(JSParseState *s) +{ + JSFunctionDef *fd; + + fd = js_new_function_def(s->ctx, s->cur_func, FALSE, FALSE, + s->filename, 0, 0); + if (!fd) + return NULL; + fd->func_name = JS_ATOM_NULL; + fd->has_prototype = FALSE; + fd->has_home_object = TRUE; + + fd->has_arguments_binding = FALSE; + fd->has_this_binding = TRUE; + fd->is_derived_class_constructor = FALSE; + fd->new_target_allowed = TRUE; + fd->super_call_allowed = FALSE; + fd->super_allowed = fd->has_home_object; + fd->arguments_allowed = FALSE; + + fd->func_kind = JS_FUNC_NORMAL; + fd->func_type = JS_PARSE_FUNC_METHOD; + return fd; +} + +/* func_name must be JS_ATOM_NULL for JS_PARSE_FUNC_STATEMENT and + JS_PARSE_FUNC_EXPR, JS_PARSE_FUNC_ARROW and JS_PARSE_FUNC_VAR */ +static __exception int js_parse_function_decl2(JSParseState *s, + JSParseFunctionEnum func_type, + JSFunctionKindEnum func_kind, + JSAtom func_name, + const uint8_t *ptr, + int function_line_num, + int function_col_num, + JSParseExportEnum export_flag, + JSFunctionDef **pfd) +{ + JSContext *ctx = s->ctx; + JSFunctionDef *fd = s->cur_func; + BOOL is_expr; + int func_idx, lexical_func_idx = -1; + BOOL has_opt_arg; + BOOL create_func_var = FALSE; + + is_expr = (func_type != JS_PARSE_FUNC_STATEMENT && + func_type != JS_PARSE_FUNC_VAR); + + if (func_type == JS_PARSE_FUNC_STATEMENT || + func_type == JS_PARSE_FUNC_VAR || + func_type == JS_PARSE_FUNC_EXPR) { + if (func_kind == JS_FUNC_NORMAL && + token_is_pseudo_keyword(s, JS_ATOM_async) && + peek_token(s, TRUE) != '\n') { + if (next_token(s)) + return -1; + func_kind = JS_FUNC_ASYNC; + } + if (next_token(s)) + return -1; + if (s->token.val == '*') { + if (next_token(s)) + return -1; + func_kind |= JS_FUNC_GENERATOR; + } + + if (s->token.val == TOK_IDENT) { + if (s->token.u.ident.is_reserved || + (s->token.u.ident.atom == JS_ATOM_yield && + func_type == JS_PARSE_FUNC_EXPR && + (func_kind & JS_FUNC_GENERATOR)) || + (s->token.u.ident.atom == JS_ATOM_await && + ((func_type == JS_PARSE_FUNC_EXPR && + (func_kind & JS_FUNC_ASYNC)) || + func_type == JS_PARSE_FUNC_CLASS_STATIC_INIT))) { + return js_parse_error_reserved_identifier(s); + } + } + if (s->token.val == TOK_IDENT || + (((s->token.val == TOK_YIELD && !fd->is_strict_mode) || + (s->token.val == TOK_AWAIT && !s->is_module)) && + func_type == JS_PARSE_FUNC_EXPR)) { + func_name = JS_DupAtom(ctx, s->token.u.ident.atom); + if (next_token(s)) { + JS_FreeAtom(ctx, func_name); + return -1; + } + } else { + if (func_type != JS_PARSE_FUNC_EXPR && + export_flag != JS_PARSE_EXPORT_DEFAULT) { + return js_parse_error(s, "function name expected"); + } + } + } else if (func_type != JS_PARSE_FUNC_ARROW) { + func_name = JS_DupAtom(ctx, func_name); + } + + if (fd->is_eval && fd->eval_type == JS_EVAL_TYPE_MODULE && + (func_type == JS_PARSE_FUNC_STATEMENT || func_type == JS_PARSE_FUNC_VAR)) { + JSGlobalVar *hf; + hf = find_global_var(fd, func_name); + /* XXX: should check scope chain */ + if (hf && hf->scope_level == fd->scope_level) { + js_parse_error(s, "invalid redefinition of global identifier in module code"); + JS_FreeAtom(ctx, func_name); + return -1; + } + } + + if (func_type == JS_PARSE_FUNC_VAR) { + if (!fd->is_strict_mode + && func_kind == JS_FUNC_NORMAL + && find_lexical_decl(ctx, fd, func_name, fd->scope_first, FALSE) < 0 + && !((func_idx = find_var(ctx, fd, func_name)) >= 0 && (func_idx & ARGUMENT_VAR_OFFSET)) + && !(func_name == JS_ATOM_arguments && fd->has_arguments_binding)) { + create_func_var = TRUE; + } + /* Create the lexical name here so that the function closure + contains it */ + if (fd->is_eval && + (fd->eval_type == JS_EVAL_TYPE_GLOBAL || + fd->eval_type == JS_EVAL_TYPE_MODULE) && + fd->scope_level == fd->body_scope) { + /* avoid creating a lexical variable in the global + scope. XXX: check annex B */ + JSGlobalVar *hf; + hf = find_global_var(fd, func_name); + /* XXX: should check scope chain */ + if (hf && hf->scope_level == fd->scope_level) { + js_parse_error(s, "invalid redefinition of global identifier"); + JS_FreeAtom(ctx, func_name); + return -1; + } + } else { + /* Always create a lexical name, fail if at the same scope as + existing name */ + /* Lexical variable will be initialized upon entering scope */ + lexical_func_idx = define_var(s, fd, func_name, + func_kind != JS_FUNC_NORMAL ? + JS_VAR_DEF_NEW_FUNCTION_DECL : + JS_VAR_DEF_FUNCTION_DECL); + if (lexical_func_idx < 0) { + JS_FreeAtom(ctx, func_name); + return -1; + } + } + } + + fd = js_new_function_def(ctx, fd, FALSE, is_expr, s->filename, + function_line_num, function_col_num); + if (!fd) { + JS_FreeAtom(ctx, func_name); + return -1; + } + if (pfd) + *pfd = fd; + s->cur_func = fd; + fd->func_name = func_name; + /* XXX: test !fd->is_generator is always false */ + fd->has_prototype = (func_type == JS_PARSE_FUNC_STATEMENT || + func_type == JS_PARSE_FUNC_VAR || + func_type == JS_PARSE_FUNC_EXPR) && + func_kind == JS_FUNC_NORMAL; + fd->has_home_object = (func_type == JS_PARSE_FUNC_METHOD || + func_type == JS_PARSE_FUNC_GETTER || + func_type == JS_PARSE_FUNC_SETTER || + func_type == JS_PARSE_FUNC_CLASS_CONSTRUCTOR || + func_type == JS_PARSE_FUNC_DERIVED_CLASS_CONSTRUCTOR); + fd->has_arguments_binding = (func_type != JS_PARSE_FUNC_ARROW && + func_type != JS_PARSE_FUNC_CLASS_STATIC_INIT); + fd->has_this_binding = fd->has_arguments_binding; + fd->is_derived_class_constructor = (func_type == JS_PARSE_FUNC_DERIVED_CLASS_CONSTRUCTOR); + if (func_type == JS_PARSE_FUNC_ARROW) { + fd->new_target_allowed = fd->parent->new_target_allowed; + fd->super_call_allowed = fd->parent->super_call_allowed; + fd->super_allowed = fd->parent->super_allowed; + fd->arguments_allowed = fd->parent->arguments_allowed; + } else if (func_type == JS_PARSE_FUNC_CLASS_STATIC_INIT) { + fd->new_target_allowed = TRUE; // although new.target === undefined + fd->super_call_allowed = FALSE; + fd->super_allowed = TRUE; + fd->arguments_allowed = FALSE; + } else { + fd->new_target_allowed = TRUE; + fd->super_call_allowed = fd->is_derived_class_constructor; + fd->super_allowed = fd->has_home_object; + fd->arguments_allowed = TRUE; + } + + /* fd->in_function_body == FALSE prevents yield/await during the parsing + of the arguments in generator/async functions. They are parsed as + regular identifiers for other function kinds. */ + fd->func_kind = func_kind; + fd->func_type = func_type; + + if (func_type == JS_PARSE_FUNC_CLASS_CONSTRUCTOR || + func_type == JS_PARSE_FUNC_DERIVED_CLASS_CONSTRUCTOR) { + /* error if not invoked as a constructor */ + emit_op(s, OP_check_ctor); + } + + if (func_type == JS_PARSE_FUNC_CLASS_CONSTRUCTOR) { + emit_class_field_init(s); + } + + /* parse arguments */ + fd->has_simple_parameter_list = TRUE; + fd->has_parameter_expressions = FALSE; + has_opt_arg = FALSE; + if (func_type == JS_PARSE_FUNC_ARROW && s->token.val == TOK_IDENT) { + JSAtom name; + if (s->token.u.ident.is_reserved) { + js_parse_error_reserved_identifier(s); + goto fail; + } + name = s->token.u.ident.atom; + if (add_arg(ctx, fd, name) < 0) + goto fail; + fd->defined_arg_count = 1; + } else if (func_type != JS_PARSE_FUNC_CLASS_STATIC_INIT) { + if (s->token.val == '(') { + int skip_bits; + /* if there is an '=' inside the parameter list, we + consider there is a parameter expression inside */ + js_parse_skip_parens_token(s, &skip_bits, FALSE); + if (skip_bits & SKIP_HAS_ASSIGNMENT) + fd->has_parameter_expressions = TRUE; + if (next_token(s)) + goto fail; + } else { + if (js_parse_expect(s, '(')) + goto fail; + } + + if (fd->has_parameter_expressions) { + fd->scope_level = -1; /* force no parent scope */ + if (push_scope(s) < 0) + return -1; + } + + while (s->token.val != ')') { + JSAtom name; + BOOL rest = FALSE; + int idx, has_initializer; + + if (s->token.val == TOK_ELLIPSIS) { + fd->has_simple_parameter_list = FALSE; + rest = TRUE; + if (next_token(s)) + goto fail; + } + if (s->token.val == '[' || s->token.val == '{') { + fd->has_simple_parameter_list = FALSE; + if (rest) { + emit_op(s, OP_rest); + emit_u16(s, fd->arg_count); + } else { + /* unnamed arg for destructuring */ + idx = add_arg(ctx, fd, JS_ATOM_NULL); + emit_op(s, OP_get_arg); + emit_u16(s, idx); + } + has_initializer = js_parse_destructuring_element(s, fd->has_parameter_expressions ? TOK_LET : TOK_VAR, 1, TRUE, -1, TRUE); + if (has_initializer < 0) + goto fail; + if (has_initializer) + has_opt_arg = TRUE; + if (!has_opt_arg) + fd->defined_arg_count++; + } else if (s->token.val == TOK_IDENT) { + if (s->token.u.ident.is_reserved) { + js_parse_error_reserved_identifier(s); + goto fail; + } + name = s->token.u.ident.atom; + if (name == JS_ATOM_yield && fd->func_kind == JS_FUNC_GENERATOR) { + js_parse_error_reserved_identifier(s); + goto fail; + } + if (fd->has_parameter_expressions) { + if (js_parse_check_duplicate_parameter(s, name)) + goto fail; + if (define_var(s, fd, name, JS_VAR_DEF_LET) < 0) + goto fail; + } + /* XXX: could avoid allocating an argument if rest is true */ + idx = add_arg(ctx, fd, name); + if (idx < 0) + goto fail; + if (next_token(s)) + goto fail; + if (rest) { + emit_op(s, OP_rest); + emit_u16(s, idx); + if (fd->has_parameter_expressions) { + emit_op(s, OP_dup); + emit_op(s, OP_scope_put_var_init); + emit_atom(s, name); + emit_u16(s, fd->scope_level); + } + emit_op(s, OP_put_arg); + emit_u16(s, idx); + fd->has_simple_parameter_list = FALSE; + has_opt_arg = TRUE; + } else if (s->token.val == '=') { + int label; + + fd->has_simple_parameter_list = FALSE; + has_opt_arg = TRUE; + + if (next_token(s)) + goto fail; + + label = new_label(s); + emit_op(s, OP_get_arg); + emit_u16(s, idx); + emit_op(s, OP_dup); + emit_op(s, OP_undefined); + emit_op(s, OP_strict_eq); + emit_goto(s, OP_if_false, label); + emit_op(s, OP_drop); + if (js_parse_assign_expr(s)) + goto fail; + set_object_name(s, name); + emit_op(s, OP_dup); + emit_op(s, OP_put_arg); + emit_u16(s, idx); + emit_label(s, label); + emit_op(s, OP_scope_put_var_init); + emit_atom(s, name); + emit_u16(s, fd->scope_level); + } else { + if (!has_opt_arg) { + fd->defined_arg_count++; + } + if (fd->has_parameter_expressions) { + /* copy the argument to the argument scope */ + emit_op(s, OP_get_arg); + emit_u16(s, idx); + emit_op(s, OP_scope_put_var_init); + emit_atom(s, name); + emit_u16(s, fd->scope_level); + } + } + } else { + js_parse_error(s, "missing formal parameter"); + goto fail; + } + if (rest && s->token.val != ')') { + js_parse_expect(s, ')'); + goto fail; + } + if (s->token.val == ')') + break; + if (js_parse_expect(s, ',')) + goto fail; + } + if ((func_type == JS_PARSE_FUNC_GETTER && fd->arg_count != 0) || + (func_type == JS_PARSE_FUNC_SETTER && fd->arg_count != 1)) { + js_parse_error(s, "invalid number of arguments for getter or setter"); + goto fail; + } + } + + if (fd->has_parameter_expressions) { + int idx; + + /* Copy the variables in the argument scope to the variable + scope (see FunctionDeclarationInstantiation() in spec). The + normal arguments are already present, so no need to copy + them. */ + idx = fd->scopes[fd->scope_level].first; + while (idx >= 0) { + JSVarDef *vd = &fd->vars[idx]; + if (vd->scope_level != fd->scope_level) + break; + if (find_var(ctx, fd, vd->var_name) < 0) { + if (add_var(ctx, fd, vd->var_name) < 0) + goto fail; + vd = &fd->vars[idx]; /* fd->vars may have been reallocated */ + emit_op(s, OP_scope_get_var); + emit_atom(s, vd->var_name); + emit_u16(s, fd->scope_level); + emit_op(s, OP_scope_put_var); + emit_atom(s, vd->var_name); + emit_u16(s, 0); + } + idx = vd->scope_next; + } + + /* the argument scope has no parent, hence we don't use pop_scope(s) */ + emit_op(s, OP_leave_scope); + emit_u16(s, fd->scope_level); + + /* set the variable scope as the current scope */ + fd->scope_level = 0; + fd->scope_first = fd->scopes[fd->scope_level].first; + } + + if (next_token(s)) + goto fail; + + /* generator function: yield after the parameters are evaluated */ + if (func_kind == JS_FUNC_GENERATOR || + func_kind == JS_FUNC_ASYNC_GENERATOR) + emit_op(s, OP_initial_yield); + + /* in generators, yield expression is forbidden during the parsing + of the arguments */ + fd->in_function_body = TRUE; + push_scope(s); /* enter body scope */ + fd->body_scope = fd->scope_level; + + if (s->token.val == TOK_ARROW) { + if (next_token(s)) + goto fail; + + if (s->token.val != '{') { + if (js_parse_function_check_names(s, fd, func_name)) + goto fail; + + if (js_parse_assign_expr(s)) + goto fail; + + if (func_kind != JS_FUNC_NORMAL) + emit_op(s, OP_return_async); + else + emit_op(s, OP_return); + + /* save the function source code */ + /* the end of the function source code is after the last + token of the function source stored into s->last_ptr */ + fd->source_len = s->last_ptr - ptr; + fd->source = js_strndup(ctx, (const char *)ptr, fd->source_len); + if (!fd->source) + goto fail; + + goto done; + } + } + + // js_parse_class() already consumed the '{' + if (func_type != JS_PARSE_FUNC_CLASS_STATIC_INIT) + if (js_parse_expect(s, '{')) + goto fail; + + if (js_parse_directives(s)) + goto fail; + + /* in strict_mode, check function and argument names */ + if (js_parse_function_check_names(s, fd, func_name)) + goto fail; + + while (s->token.val != '}') { + if (js_parse_source_element(s)) + goto fail; + } + + /* save the function source code */ + fd->source_len = s->buf_ptr - ptr; + fd->source = js_strndup(ctx, (const char *)ptr, fd->source_len); + if (!fd->source) + goto fail; + + if (next_token(s)) { + /* consume the '}' */ + goto fail; + } + + /* in case there is no return, add one */ + if (js_is_live_code(s)) { + emit_return(s, FALSE); + } +done: + s->cur_func = fd->parent; + + /* Reparse identifiers after the function is terminated so that + the token is parsed in the englobing function. It could be done + by just using next_token() here for normal functions, but it is + necessary for arrow functions with an expression body. */ + reparse_ident_token(s); + + /* create the function object */ + { + int idx; + JSAtom func_name = fd->func_name; + + /* the real object will be set at the end of the compilation */ + idx = cpool_add(s, JS_NULL); + fd->parent_cpool_idx = idx; + + if (is_expr) { + /* for constructors, no code needs to be generated here */ + if (func_type != JS_PARSE_FUNC_CLASS_CONSTRUCTOR && + func_type != JS_PARSE_FUNC_DERIVED_CLASS_CONSTRUCTOR) { + /* OP_fclosure creates the function object from the bytecode + and adds the scope information */ + emit_op(s, OP_fclosure); + emit_u32(s, idx); + if (func_name == JS_ATOM_NULL) { + emit_op(s, OP_set_name); + emit_u32(s, JS_ATOM_NULL); + } + } + } else if (func_type == JS_PARSE_FUNC_VAR) { + emit_op(s, OP_fclosure); + emit_u32(s, idx); + if (create_func_var) { + if (s->cur_func->is_global_var) { + JSGlobalVar *hf; + /* the global variable must be defined at the start of the + function */ + hf = add_global_var(ctx, s->cur_func, func_name); + if (!hf) + goto fail; + /* it is considered as defined at the top level + (needed for annex B.3.3.4 and B.3.3.5 + checks) */ + hf->scope_level = 0; + hf->force_init = s->cur_func->is_strict_mode; + /* store directly into global var, bypass lexical scope */ + emit_op(s, OP_dup); + emit_op(s, OP_scope_put_var); + emit_atom(s, func_name); + emit_u16(s, 0); + } else { + /* do not call define_var to bypass lexical scope check */ + func_idx = find_var(ctx, s->cur_func, func_name); + if (func_idx < 0) { + func_idx = add_var(ctx, s->cur_func, func_name); + if (func_idx < 0) + goto fail; + } + /* store directly into local var, bypass lexical catch scope */ + emit_op(s, OP_dup); + emit_op(s, OP_scope_put_var); + emit_atom(s, func_name); + emit_u16(s, 0); + } + } + if (lexical_func_idx >= 0) { + /* lexical variable will be initialized upon entering scope */ + s->cur_func->vars[lexical_func_idx].func_pool_idx = idx; + emit_op(s, OP_drop); + } else { + /* store function object into its lexical name */ + /* XXX: could use OP_put_loc directly */ + emit_op(s, OP_scope_put_var_init); + emit_atom(s, func_name); + emit_u16(s, s->cur_func->scope_level); + } + } else { + if (!s->cur_func->is_global_var) { + int var_idx = define_var(s, s->cur_func, func_name, JS_VAR_DEF_VAR); + + if (var_idx < 0) + goto fail; + /* the variable will be assigned at the top of the function */ + if (var_idx & ARGUMENT_VAR_OFFSET) { + s->cur_func->args[var_idx - ARGUMENT_VAR_OFFSET].func_pool_idx = idx; + } else { + s->cur_func->vars[var_idx].func_pool_idx = idx; + } + } else { + JSAtom func_var_name; + JSGlobalVar *hf; + if (func_name == JS_ATOM_NULL) + func_var_name = JS_ATOM__default_; /* export default */ + else + func_var_name = func_name; + /* the variable will be assigned at the top of the function */ + hf = add_global_var(ctx, s->cur_func, func_var_name); + if (!hf) + goto fail; + hf->cpool_idx = idx; + if (export_flag != JS_PARSE_EXPORT_NONE) { + if (!add_export_entry(s, s->cur_func->module, func_var_name, + export_flag == JS_PARSE_EXPORT_NAMED ? func_var_name : JS_ATOM_default, JS_EXPORT_TYPE_LOCAL)) + goto fail; + } + } + } + } + return 0; + fail: + s->cur_func = fd->parent; + js_free_function_def(ctx, fd); + if (pfd) + *pfd = NULL; + return -1; +} + +static __exception int js_parse_function_decl(JSParseState *s, + JSParseFunctionEnum func_type, + JSFunctionKindEnum func_kind, + JSAtom func_name, + const uint8_t *ptr, + int start_line, + int start_col) +{ + return js_parse_function_decl2(s, func_type, func_kind, func_name, ptr, + start_line, start_col, + JS_PARSE_EXPORT_NONE, NULL); +} + +static __exception int js_parse_program(JSParseState *s) +{ + JSFunctionDef *fd = s->cur_func; + int idx; + + if (next_token(s)) + return -1; + + if (js_parse_directives(s)) + return -1; + + fd->is_global_var = (fd->eval_type == JS_EVAL_TYPE_GLOBAL) || + (fd->eval_type == JS_EVAL_TYPE_MODULE) || + !fd->is_strict_mode; + + if (!s->is_module) { + /* hidden variable for the return value */ + fd->eval_ret_idx = idx = add_var(s->ctx, fd, JS_ATOM__ret_); + if (idx < 0) + return -1; + } + + while (s->token.val != TOK_EOF) { + if (js_parse_source_element(s)) + return -1; + } + + if (!s->is_module) { + /* return the value of the hidden variable eval_ret_idx */ + if (fd->func_kind == JS_FUNC_ASYNC) { + /* wrap the return value in an object so that promises can + be safely returned */ + emit_op(s, OP_object); + emit_op(s, OP_dup); + + emit_op(s, OP_get_loc); + emit_u16(s, fd->eval_ret_idx); + + emit_op(s, OP_put_field); + emit_atom(s, JS_ATOM_value); + } else { + emit_op(s, OP_get_loc); + emit_u16(s, fd->eval_ret_idx); + } + emit_return(s, TRUE); + } else { + emit_return(s, FALSE); + } + + return 0; +} + +static void js_parse_init(JSContext *ctx, JSParseState *s, + const char *input, size_t input_len, + const char *filename) +{ + memset(s, 0, sizeof(*s)); + s->ctx = ctx; + s->filename = filename; + s->line_num = 1; + s->col_num = 1; + s->buf_start = s->buf_ptr = (const uint8_t *)input; + s->buf_end = s->buf_ptr + input_len; + s->mark = s->buf_ptr + min_int(1, input_len); + s->eol = s->buf_ptr; + s->token.val = ' '; + s->token.line_num = 1; + s->token.col_num = 1; +} + +static JSValue JS_EvalFunctionInternal(JSContext *ctx, JSValue fun_obj, + JSValue this_obj, + JSVarRef **var_refs, JSStackFrame *sf) +{ + JSValue ret_val; + uint32_t tag; + + tag = JS_VALUE_GET_TAG(fun_obj); + if (tag == JS_TAG_FUNCTION_BYTECODE) { + fun_obj = js_closure(ctx, fun_obj, var_refs, sf); + ret_val = JS_CallFree(ctx, fun_obj, this_obj, 0, NULL); + } else if (tag == JS_TAG_MODULE) { + JSModuleDef *m; + m = JS_VALUE_GET_PTR(fun_obj); + /* the module refcount should be >= 2 */ + JS_FreeValue(ctx, fun_obj); + if (js_create_module_function(ctx, m) < 0) + goto fail; + if (js_link_module(ctx, m) < 0) + goto fail; + ret_val = js_evaluate_module(ctx, m); + if (JS_IsException(ret_val)) { + fail: + return JS_EXCEPTION; + } + } else { + JS_FreeValue(ctx, fun_obj); + ret_val = JS_ThrowTypeError(ctx, "bytecode function expected"); + } + return ret_val; +} + +JSValue JS_EvalFunction(JSContext *ctx, JSValue fun_obj) +{ + return JS_EvalFunctionInternal(ctx, fun_obj, ctx->global_obj, NULL, NULL); +} + +/* 'input' must be zero terminated i.e. input[input_len] = '\0'. */ +/* `export_name` and `input` may be pure ASCII or UTF-8 encoded */ +static JSValue __JS_EvalInternal(JSContext *ctx, JSValue this_obj, + const char *input, size_t input_len, + const char *filename, int flags, int scope_idx) +{ + JSParseState s1, *s = &s1; + int err, eval_type; + JSValue fun_obj, ret_val; + JSStackFrame *sf; + JSVarRef **var_refs; + JSFunctionBytecode *b; + JSFunctionDef *fd; + JSModuleDef *m; + BOOL is_strict_mode; + + js_parse_init(ctx, s, input, input_len, filename); + skip_shebang(&s->buf_ptr, s->buf_end); + + eval_type = flags & JS_EVAL_TYPE_MASK; + m = NULL; + if (eval_type == JS_EVAL_TYPE_DIRECT) { + JSObject *p; + sf = ctx->rt->current_stack_frame; + assert(sf != NULL); + assert(JS_VALUE_GET_TAG(sf->cur_func) == JS_TAG_OBJECT); + p = JS_VALUE_GET_OBJ(sf->cur_func); + assert(js_class_has_bytecode(p->class_id)); + b = p->u.func.function_bytecode; + var_refs = p->u.func.var_refs; + is_strict_mode = b->is_strict_mode; + } else { + sf = NULL; + b = NULL; + var_refs = NULL; + is_strict_mode = (flags & JS_EVAL_FLAG_STRICT) != 0; + if (eval_type == JS_EVAL_TYPE_MODULE) { + JSAtom module_name = JS_NewAtom(ctx, filename); + if (module_name == JS_ATOM_NULL) + return JS_EXCEPTION; + m = js_new_module_def(ctx, module_name); + if (!m) + return JS_EXCEPTION; + is_strict_mode = TRUE; + } + } + fd = js_new_function_def(ctx, NULL, TRUE, FALSE, filename, 1, 1); + if (!fd) + goto fail1; + s->cur_func = fd; + fd->eval_type = eval_type; + fd->has_this_binding = (eval_type != JS_EVAL_TYPE_DIRECT); + fd->backtrace_barrier = ((flags & JS_EVAL_FLAG_BACKTRACE_BARRIER) != 0); + if (eval_type == JS_EVAL_TYPE_DIRECT) { + fd->new_target_allowed = b->new_target_allowed; + fd->super_call_allowed = b->super_call_allowed; + fd->super_allowed = b->super_allowed; + fd->arguments_allowed = b->arguments_allowed; + } else { + fd->new_target_allowed = FALSE; + fd->super_call_allowed = FALSE; + fd->super_allowed = FALSE; + fd->arguments_allowed = TRUE; + } + fd->is_strict_mode = is_strict_mode; + fd->func_name = JS_DupAtom(ctx, JS_ATOM__eval_); + if (b) { + if (add_closure_variables(ctx, fd, b, scope_idx)) + goto fail; + } + fd->module = m; + if (m != NULL || (flags & JS_EVAL_FLAG_ASYNC)) { + fd->in_function_body = TRUE; + fd->func_kind = JS_FUNC_ASYNC; + } + s->is_module = (m != NULL); + s->allow_html_comments = !s->is_module; + + push_scope(s); /* body scope */ + fd->body_scope = fd->scope_level; + + err = js_parse_program(s); + if (err) { + fail: + free_token(s, &s->token); + js_free_function_def(ctx, fd); + goto fail1; + } + + if (m != NULL) + m->has_tla = fd->has_await; + + /* create the function object and all the enclosed functions */ + fun_obj = js_create_function(ctx, fd); + if (JS_IsException(fun_obj)) + goto fail1; + /* Could add a flag to avoid resolution if necessary */ + if (m) { + m->func_obj = fun_obj; + if (js_resolve_module(ctx, m) < 0) + goto fail1; + fun_obj = JS_NewModuleValue(ctx, m); + } + if (flags & JS_EVAL_FLAG_COMPILE_ONLY) { + ret_val = fun_obj; + } else { + ret_val = JS_EvalFunctionInternal(ctx, fun_obj, this_obj, var_refs, sf); + } + return ret_val; + fail1: + /* XXX: should free all the unresolved dependencies */ + if (m) + js_free_module_def(ctx, m); + return JS_EXCEPTION; +} + +/* the indirection is needed to make 'eval' optional */ +static JSValue JS_EvalInternal(JSContext *ctx, JSValue this_obj, + const char *input, size_t input_len, + const char *filename, int flags, int scope_idx) +{ + if (unlikely(!ctx->eval_internal)) { + return JS_ThrowTypeError(ctx, "eval is not supported"); + } + return ctx->eval_internal(ctx, this_obj, input, input_len, filename, + flags, scope_idx); +} + +static JSValue JS_EvalObject(JSContext *ctx, JSValue this_obj, + JSValue val, int flags, int scope_idx) +{ + JSValue ret; + const char *str; + size_t len; + + if (!JS_IsString(val)) + return js_dup(val); + str = JS_ToCStringLen(ctx, &len, val); + if (!str) + return JS_EXCEPTION; + ret = JS_EvalInternal(ctx, this_obj, str, len, "<input>", flags, scope_idx); + JS_FreeCString(ctx, str); + return ret; + +} + +JSValue JS_EvalThis(JSContext *ctx, JSValue this_obj, + const char *input, size_t input_len, + const char *filename, int eval_flags) +{ + JSValue ret; + + assert((eval_flags & JS_EVAL_TYPE_MASK) == JS_EVAL_TYPE_GLOBAL || + (eval_flags & JS_EVAL_TYPE_MASK) == JS_EVAL_TYPE_MODULE); + ret = JS_EvalInternal(ctx, this_obj, input, input_len, filename, + eval_flags, -1); + return ret; +} + +JSValue JS_Eval(JSContext *ctx, const char *input, size_t input_len, + const char *filename, int eval_flags) +{ + return JS_EvalThis(ctx, ctx->global_obj, input, input_len, filename, + eval_flags); +} + +int JS_ResolveModule(JSContext *ctx, JSValue obj) +{ + if (JS_VALUE_GET_TAG(obj) == JS_TAG_MODULE) { + JSModuleDef *m = JS_VALUE_GET_PTR(obj); + if (js_resolve_module(ctx, m) < 0) { + js_free_modules(ctx, JS_FREE_MODULE_NOT_RESOLVED); + return -1; + } + } + return 0; +} + +/*******************************************************************/ +/* object list */ + +typedef struct { + JSObject *obj; + uint32_t hash_next; /* -1 if no next entry */ +} JSObjectListEntry; + +/* XXX: reuse it to optimize weak references */ +typedef struct { + JSObjectListEntry *object_tab; + int object_count; + int object_size; + uint32_t *hash_table; + uint32_t hash_size; +} JSObjectList; + +static void js_object_list_init(JSObjectList *s) +{ + memset(s, 0, sizeof(*s)); +} + +static uint32_t js_object_list_get_hash(JSObject *p, uint32_t hash_size) +{ + return ((uintptr_t)p * 3163) & (hash_size - 1); +} + +static int js_object_list_resize_hash(JSContext *ctx, JSObjectList *s, + uint32_t new_hash_size) +{ + JSObjectListEntry *e; + uint32_t i, h, *new_hash_table; + + new_hash_table = js_malloc(ctx, sizeof(new_hash_table[0]) * new_hash_size); + if (!new_hash_table) + return -1; + js_free(ctx, s->hash_table); + s->hash_table = new_hash_table; + s->hash_size = new_hash_size; + + for(i = 0; i < s->hash_size; i++) { + s->hash_table[i] = -1; + } + for(i = 0; i < s->object_count; i++) { + e = &s->object_tab[i]; + h = js_object_list_get_hash(e->obj, s->hash_size); + e->hash_next = s->hash_table[h]; + s->hash_table[h] = i; + } + return 0; +} + +/* the reference count of 'obj' is not modified. Return 0 if OK, -1 if + memory error */ +static int js_object_list_add(JSContext *ctx, JSObjectList *s, JSObject *obj) +{ + JSObjectListEntry *e; + uint32_t h, new_hash_size; + + if (js_resize_array(ctx, (void *)&s->object_tab, + sizeof(s->object_tab[0]), + &s->object_size, s->object_count + 1)) + return -1; + if (unlikely((s->object_count + 1) >= s->hash_size)) { + new_hash_size = max_uint32(s->hash_size, 4); + while (new_hash_size <= s->object_count) + new_hash_size *= 2; + if (js_object_list_resize_hash(ctx, s, new_hash_size)) + return -1; + } + e = &s->object_tab[s->object_count++]; + h = js_object_list_get_hash(obj, s->hash_size); + e->obj = obj; + e->hash_next = s->hash_table[h]; + s->hash_table[h] = s->object_count - 1; + return 0; +} + +/* return -1 if not present or the object index */ +static int js_object_list_find(JSContext *ctx, JSObjectList *s, JSObject *obj) +{ + JSObjectListEntry *e; + uint32_t h, p; + + /* must test empty size because there is no hash table */ + if (s->object_count == 0) + return -1; + h = js_object_list_get_hash(obj, s->hash_size); + p = s->hash_table[h]; + while (p != -1) { + e = &s->object_tab[p]; + if (e->obj == obj) + return p; + p = e->hash_next; + } + return -1; +} + +static void js_object_list_end(JSContext *ctx, JSObjectList *s) +{ + js_free(ctx, s->object_tab); + js_free(ctx, s->hash_table); +} + +/*******************************************************************/ +/* binary object writer & reader */ + +typedef enum BCTagEnum { + BC_TAG_NULL = 1, + BC_TAG_UNDEFINED, + BC_TAG_BOOL_FALSE, + BC_TAG_BOOL_TRUE, + BC_TAG_INT32, + BC_TAG_FLOAT64, + BC_TAG_STRING, + BC_TAG_OBJECT, + BC_TAG_ARRAY, + BC_TAG_BIG_INT, + BC_TAG_TEMPLATE_OBJECT, + BC_TAG_FUNCTION_BYTECODE, + BC_TAG_MODULE, + BC_TAG_TYPED_ARRAY, + BC_TAG_ARRAY_BUFFER, + BC_TAG_SHARED_ARRAY_BUFFER, + BC_TAG_REGEXP, + BC_TAG_DATE, + BC_TAG_OBJECT_VALUE, + BC_TAG_OBJECT_REFERENCE, + BC_TAG_MAP, + BC_TAG_SET, + BC_TAG_SYMBOL, +} BCTagEnum; + +#define BC_VERSION 19 + +typedef struct BCWriterState { + JSContext *ctx; + DynBuf dbuf; + BOOL allow_bytecode : 8; + BOOL allow_sab : 8; + BOOL allow_reference : 8; + BOOL allow_source : 1; + BOOL allow_debug : 1; + uint32_t first_atom; + uint32_t *atom_to_idx; + int atom_to_idx_size; + JSAtom *idx_to_atom; + int idx_to_atom_count; + int idx_to_atom_size; + uint8_t **sab_tab; + int sab_tab_len; + int sab_tab_size; + /* list of referenced objects (used if allow_reference = TRUE) */ + JSObjectList object_list; +} BCWriterState; + +#ifdef DUMP_READ_OBJECT +static const char * const bc_tag_str[] = { + "invalid", + "null", + "undefined", + "false", + "true", + "int32", + "float64", + "string", + "object", + "array", + "BigInt", + "template", + "function", + "module", + "TypedArray", + "ArrayBuffer", + "SharedArrayBuffer", + "RegExp", + "Date", + "ObjectValue", + "ObjectReference", + "Map", + "Set", + "Symbol", +}; +#endif + +static void bc_put_u8(BCWriterState *s, uint8_t v) +{ + dbuf_putc(&s->dbuf, v); +} + +static void bc_put_u16(BCWriterState *s, uint16_t v) +{ + if (is_be()) + v = bswap16(v); + dbuf_put_u16(&s->dbuf, v); +} + +static __maybe_unused void bc_put_u32(BCWriterState *s, uint32_t v) +{ + if (is_be()) + v = bswap32(v); + dbuf_put_u32(&s->dbuf, v); +} + +static void bc_put_u64(BCWriterState *s, uint64_t v) +{ + if (is_be()) + v = bswap64(v); + dbuf_put(&s->dbuf, (uint8_t *)&v, sizeof(v)); +} + +static void bc_put_leb128(BCWriterState *s, uint32_t v) +{ + dbuf_put_leb128(&s->dbuf, v); +} + +static void bc_put_sleb128(BCWriterState *s, int32_t v) +{ + dbuf_put_sleb128(&s->dbuf, v); +} + +static void bc_set_flags(uint32_t *pflags, int *pidx, uint32_t val, int n) +{ + *pflags = *pflags | (val << *pidx); + *pidx += n; +} + +static int bc_atom_to_idx(BCWriterState *s, uint32_t *pres, JSAtom atom) +{ + uint32_t v; + + if (atom < s->first_atom || __JS_AtomIsTaggedInt(atom)) { + *pres = atom; + return 0; + } + atom -= s->first_atom; + if (atom < s->atom_to_idx_size && s->atom_to_idx[atom] != 0) { + *pres = s->atom_to_idx[atom]; + return 0; + } + if (atom >= s->atom_to_idx_size) { + int old_size, i; + old_size = s->atom_to_idx_size; + if (js_resize_array(s->ctx, (void **)&s->atom_to_idx, + sizeof(s->atom_to_idx[0]), &s->atom_to_idx_size, + atom + 1)) + return -1; + /* XXX: could add a specific js_resize_array() function to do it */ + for(i = old_size; i < s->atom_to_idx_size; i++) + s->atom_to_idx[i] = 0; + } + if (js_resize_array(s->ctx, (void **)&s->idx_to_atom, + sizeof(s->idx_to_atom[0]), + &s->idx_to_atom_size, s->idx_to_atom_count + 1)) + goto fail; + + v = s->idx_to_atom_count++; + s->idx_to_atom[v] = atom + s->first_atom; + v += s->first_atom; + s->atom_to_idx[atom] = v; + *pres = v; + return 0; + fail: + *pres = 0; + return -1; +} + +static int bc_put_atom(BCWriterState *s, JSAtom atom) +{ + uint32_t v; + + if (__JS_AtomIsTaggedInt(atom)) { + v = (__JS_AtomToUInt32(atom) << 1) | 1; + } else { + if (bc_atom_to_idx(s, &v, atom)) + return -1; + v <<= 1; + } + bc_put_leb128(s, v); + return 0; +} + +static void bc_byte_swap(uint8_t *bc_buf, int bc_len) +{ + int pos, len, op, fmt; + + pos = 0; + while (pos < bc_len) { + op = bc_buf[pos]; + len = short_opcode_info(op).size; + fmt = short_opcode_info(op).fmt; + switch(fmt) { + case OP_FMT_u16: + case OP_FMT_i16: + case OP_FMT_label16: + case OP_FMT_npop: + case OP_FMT_loc: + case OP_FMT_arg: + case OP_FMT_var_ref: + put_u16(bc_buf + pos + 1, + bswap16(get_u16(bc_buf + pos + 1))); + break; + case OP_FMT_i32: + case OP_FMT_u32: + case OP_FMT_const: + case OP_FMT_label: + case OP_FMT_atom: + case OP_FMT_atom_u8: + put_u32(bc_buf + pos + 1, + bswap32(get_u32(bc_buf + pos + 1))); + break; + case OP_FMT_atom_u16: + case OP_FMT_label_u16: + put_u32(bc_buf + pos + 1, + bswap32(get_u32(bc_buf + pos + 1))); + put_u16(bc_buf + pos + 1 + 4, + bswap16(get_u16(bc_buf + pos + 1 + 4))); + break; + case OP_FMT_atom_label_u8: + case OP_FMT_atom_label_u16: + put_u32(bc_buf + pos + 1, + bswap32(get_u32(bc_buf + pos + 1))); + put_u32(bc_buf + pos + 1 + 4, + bswap32(get_u32(bc_buf + pos + 1 + 4))); + if (fmt == OP_FMT_atom_label_u16) { + put_u16(bc_buf + pos + 1 + 4 + 4, + bswap16(get_u16(bc_buf + pos + 1 + 4 + 4))); + } + break; + case OP_FMT_npop_u16: + put_u16(bc_buf + pos + 1, + bswap16(get_u16(bc_buf + pos + 1))); + put_u16(bc_buf + pos + 1 + 2, + bswap16(get_u16(bc_buf + pos + 1 + 2))); + break; + default: + break; + } + pos += len; + } +} + +static BOOL is_ic_op(uint8_t op) +{ + return op >= OP_get_field_ic && op <= OP_put_field_ic; +} + +static int JS_WriteFunctionBytecode(BCWriterState *s, + const JSFunctionBytecode *b) +{ + int pos, len, bc_len, op; + JSAtom atom; + uint8_t *bc_buf; + uint32_t val; + + bc_len = b->byte_code_len; + bc_buf = js_malloc(s->ctx, bc_len); + if (!bc_buf) + return -1; + memcpy(bc_buf, b->byte_code_buf, bc_len); + + pos = 0; + while (pos < bc_len) { + op = bc_buf[pos]; + len = short_opcode_info(op).size; + switch(short_opcode_info(op).fmt) { + case OP_FMT_atom: + case OP_FMT_atom_u8: + case OP_FMT_atom_u16: + case OP_FMT_atom_label_u8: + case OP_FMT_atom_label_u16: + atom = get_u32(bc_buf + pos + 1); + if (bc_atom_to_idx(s, &val, atom)) + goto fail; + put_u32(bc_buf + pos + 1, val); + break; + default: + // IC (inline cache) opcodes should not end up in the serialized + // bytecode; translate them to their non-IC counterparts here + if (is_ic_op(op)) { + val = get_u32(bc_buf + pos + 1); + atom = get_ic_atom(b->ic, val); + if (bc_atom_to_idx(s, &val, atom)) + goto fail; + put_u32(bc_buf + pos + 1, val); + bc_buf[pos] -= (OP_get_field_ic - OP_get_field); + } + break; + } + pos += len; + } + + if (is_be()) + bc_byte_swap(bc_buf, bc_len); + + dbuf_put(&s->dbuf, bc_buf, bc_len); + + js_free(s->ctx, bc_buf); + return 0; + fail: + js_free(s->ctx, bc_buf); + return -1; +} + +static void JS_WriteString(BCWriterState *s, JSString *p) +{ + int i; + bc_put_leb128(s, ((uint32_t)p->len << 1) | p->is_wide_char); + if (p->is_wide_char) { + for(i = 0; i < p->len; i++) + bc_put_u16(s, p->u.str16[i]); + } else { + dbuf_put(&s->dbuf, p->u.str8, p->len); + } +} + +static int JS_WriteBigInt(BCWriterState *s, JSValue obj) +{ + uint32_t tag, tag1; + int64_t e; + JSBigInt *bf = JS_VALUE_GET_PTR(obj); + bf_t *a = &bf->num; + size_t len, i, n1, j; + limb_t v; + + tag = JS_VALUE_GET_TAG(obj); + switch(tag) { + case JS_TAG_BIG_INT: + tag1 = BC_TAG_BIG_INT; + break; + default: + abort(); + } + bc_put_u8(s, tag1); + + /* sign + exponent */ + if (a->expn == BF_EXP_ZERO) + e = 0; + else if (a->expn == BF_EXP_INF) + e = 1; + else if (a->expn == BF_EXP_NAN) + e = 2; + else if (a->expn >= 0) + e = a->expn + 3; + else + e = a->expn; + e = (e * 2) | a->sign; + if (e < INT32_MIN || e > INT32_MAX) { + JS_ThrowRangeError(s->ctx, "maximum BigInt size exceeded"); + return -1; + } + bc_put_sleb128(s, e); + + /* mantissa */ + if (a->len != 0) { + i = 0; + while (i < a->len && a->tab[i] == 0) + i++; + assert(i < a->len); + v = a->tab[i]; + n1 = sizeof(limb_t); + while ((v & 0xff) == 0) { + n1--; + v >>= 8; + } + i++; + len = (a->len - i) * sizeof(limb_t) + n1; + if (len > INT32_MAX) { + JS_ThrowRangeError(s->ctx, "maximum BigInt size exceeded"); + return -1; + } + bc_put_leb128(s, len); + /* always saved in byte based little endian representation */ + for(j = 0; j < n1; j++) { + bc_put_u8(s, v >> (j * 8)); + } + for(; i < a->len; i++) { + limb_t v = a->tab[i]; +#if LIMB_BITS == 32 + bc_put_u32(s, v); +#else + bc_put_u64(s, v); +#endif + } + } + return 0; +} + +static int JS_WriteObjectRec(BCWriterState *s, JSValue obj); + +static int JS_WriteFunctionTag(BCWriterState *s, JSValue obj) +{ + JSFunctionBytecode *b = JS_VALUE_GET_PTR(obj); + uint32_t flags; + int idx, i; + + bc_put_u8(s, BC_TAG_FUNCTION_BYTECODE); + flags = idx = 0; + bc_set_flags(&flags, &idx, b->has_prototype, 1); + bc_set_flags(&flags, &idx, b->has_simple_parameter_list, 1); + bc_set_flags(&flags, &idx, b->is_derived_class_constructor, 1); + bc_set_flags(&flags, &idx, b->need_home_object, 1); + bc_set_flags(&flags, &idx, b->func_kind, 2); + bc_set_flags(&flags, &idx, b->new_target_allowed, 1); + bc_set_flags(&flags, &idx, b->super_call_allowed, 1); + bc_set_flags(&flags, &idx, b->super_allowed, 1); + bc_set_flags(&flags, &idx, b->arguments_allowed, 1); + bc_set_flags(&flags, &idx, b->backtrace_barrier, 1); + bc_set_flags(&flags, &idx, s->allow_debug, 1); + assert(idx <= 16); + bc_put_u16(s, flags); + bc_put_u8(s, b->is_strict_mode); + bc_put_atom(s, b->func_name); + + bc_put_leb128(s, b->arg_count); + bc_put_leb128(s, b->var_count); + bc_put_leb128(s, b->defined_arg_count); + bc_put_leb128(s, b->stack_size); + bc_put_leb128(s, b->closure_var_count); + bc_put_leb128(s, b->cpool_count); + bc_put_leb128(s, b->byte_code_len); + if (b->vardefs) { + /* XXX: this field is redundant */ + bc_put_leb128(s, b->arg_count + b->var_count); + for(i = 0; i < b->arg_count + b->var_count; i++) { + JSVarDef *vd = &b->vardefs[i]; + bc_put_atom(s, vd->var_name); + bc_put_leb128(s, vd->scope_level); + bc_put_leb128(s, vd->scope_next + 1); + flags = idx = 0; + bc_set_flags(&flags, &idx, vd->var_kind, 4); + bc_set_flags(&flags, &idx, vd->is_const, 1); + bc_set_flags(&flags, &idx, vd->is_lexical, 1); + bc_set_flags(&flags, &idx, vd->is_captured, 1); + assert(idx <= 8); + bc_put_u8(s, flags); + } + } else { + bc_put_leb128(s, 0); + } + + for(i = 0; i < b->closure_var_count; i++) { + JSClosureVar *cv = &b->closure_var[i]; + bc_put_atom(s, cv->var_name); + bc_put_leb128(s, cv->var_idx); + flags = idx = 0; + bc_set_flags(&flags, &idx, cv->is_local, 1); + bc_set_flags(&flags, &idx, cv->is_arg, 1); + bc_set_flags(&flags, &idx, cv->is_const, 1); + bc_set_flags(&flags, &idx, cv->is_lexical, 1); + bc_set_flags(&flags, &idx, cv->var_kind, 4); + assert(idx <= 8); + bc_put_u8(s, flags); + } + + // write constant pool before code so code can be disassembled + // on the fly at read time + for(i = 0; i < b->cpool_count; i++) { + if (JS_WriteObjectRec(s, b->cpool[i])) + goto fail; + } + + if (JS_WriteFunctionBytecode(s, b)) + goto fail; + + if (s->allow_debug) { + bc_put_atom(s, b->filename); + bc_put_leb128(s, b->line_num); + bc_put_leb128(s, b->col_num); + bc_put_leb128(s, b->pc2line_len); + dbuf_put(&s->dbuf, b->pc2line_buf, b->pc2line_len); + if (s->allow_source && b->source) { + bc_put_leb128(s, b->source_len); + dbuf_put(&s->dbuf, b->source, b->source_len); + } else { + bc_put_leb128(s, 0); + } + } + return 0; + fail: + return -1; +} + +static int JS_WriteModule(BCWriterState *s, JSValue obj) +{ + JSModuleDef *m = JS_VALUE_GET_PTR(obj); + int i; + + bc_put_u8(s, BC_TAG_MODULE); + bc_put_atom(s, m->module_name); + + bc_put_leb128(s, m->req_module_entries_count); + for(i = 0; i < m->req_module_entries_count; i++) { + JSReqModuleEntry *rme = &m->req_module_entries[i]; + bc_put_atom(s, rme->module_name); + } + + bc_put_leb128(s, m->export_entries_count); + for(i = 0; i < m->export_entries_count; i++) { + JSExportEntry *me = &m->export_entries[i]; + bc_put_u8(s, me->export_type); + if (me->export_type == JS_EXPORT_TYPE_LOCAL) { + bc_put_leb128(s, me->u.local.var_idx); + } else { + bc_put_leb128(s, me->u.req_module_idx); + bc_put_atom(s, me->local_name); + } + bc_put_atom(s, me->export_name); + } + + bc_put_leb128(s, m->star_export_entries_count); + for(i = 0; i < m->star_export_entries_count; i++) { + JSStarExportEntry *se = &m->star_export_entries[i]; + bc_put_leb128(s, se->req_module_idx); + } + + bc_put_leb128(s, m->import_entries_count); + for(i = 0; i < m->import_entries_count; i++) { + JSImportEntry *mi = &m->import_entries[i]; + bc_put_leb128(s, mi->var_idx); + bc_put_atom(s, mi->import_name); + bc_put_leb128(s, mi->req_module_idx); + } + + bc_put_u8(s, m->has_tla); + + if (JS_WriteObjectRec(s, m->func_obj)) + goto fail; + return 0; + fail: + return -1; +} + +static int JS_WriteArray(BCWriterState *s, JSValue obj) +{ + JSObject *p = JS_VALUE_GET_OBJ(obj); + uint32_t i, len; + JSValue val; + int ret; + BOOL is_template; + + if (s->allow_bytecode && !p->extensible) { + /* not extensible array: we consider it is a + template when we are saving bytecode */ + bc_put_u8(s, BC_TAG_TEMPLATE_OBJECT); + is_template = TRUE; + } else { + bc_put_u8(s, BC_TAG_ARRAY); + is_template = FALSE; + } + if (js_get_length32(s->ctx, &len, obj)) + goto fail1; + bc_put_leb128(s, len); + for(i = 0; i < len; i++) { + val = JS_GetPropertyUint32(s->ctx, obj, i); + if (JS_IsException(val)) + goto fail1; + ret = JS_WriteObjectRec(s, val); + JS_FreeValue(s->ctx, val); + if (ret) + goto fail1; + } + if (is_template) { + val = JS_GetProperty(s->ctx, obj, JS_ATOM_raw); + if (JS_IsException(val)) + goto fail1; + ret = JS_WriteObjectRec(s, val); + JS_FreeValue(s->ctx, val); + if (ret) + goto fail1; + } + return 0; + fail1: + return -1; +} + +static int JS_WriteObjectTag(BCWriterState *s, JSValue obj) +{ + JSObject *p = JS_VALUE_GET_OBJ(obj); + uint32_t i, prop_count; + JSShape *sh; + JSShapeProperty *pr; + int pass; + JSAtom atom; + + bc_put_u8(s, BC_TAG_OBJECT); + prop_count = 0; + sh = p->shape; + for(pass = 0; pass < 2; pass++) { + if (pass == 1) + bc_put_leb128(s, prop_count); + for(i = 0, pr = get_shape_prop(sh); i < sh->prop_count; i++, pr++) { + atom = pr->atom; + if (atom != JS_ATOM_NULL && (pr->flags & JS_PROP_ENUMERABLE)) { + if (pr->flags & JS_PROP_TMASK) { + JS_ThrowTypeError(s->ctx, "only value properties are supported"); + goto fail; + } + if (pass == 0) { + prop_count++; + } else { + bc_put_atom(s, atom); + if (JS_WriteObjectRec(s, p->prop[i].u.value)) + goto fail; + } + } + } + } + return 0; + fail: + return -1; +} + +static int JS_WriteTypedArray(BCWriterState *s, JSValue obj) +{ + JSObject *p = JS_VALUE_GET_OBJ(obj); + JSTypedArray *ta = p->u.typed_array; + + bc_put_u8(s, BC_TAG_TYPED_ARRAY); + bc_put_u8(s, p->class_id - JS_CLASS_UINT8C_ARRAY); + bc_put_leb128(s, p->u.array.count); + bc_put_leb128(s, ta->offset); + if (JS_WriteObjectRec(s, JS_MKPTR(JS_TAG_OBJECT, ta->buffer))) + return -1; + return 0; +} + +static int JS_WriteArrayBuffer(BCWriterState *s, JSValue obj) +{ + JSObject *p = JS_VALUE_GET_OBJ(obj); + JSArrayBuffer *abuf = p->u.array_buffer; + if (abuf->detached) { + JS_ThrowTypeErrorDetachedArrayBuffer(s->ctx); + return -1; + } + bc_put_u8(s, BC_TAG_ARRAY_BUFFER); + bc_put_leb128(s, abuf->byte_length); + bc_put_leb128(s, abuf->max_byte_length); + dbuf_put(&s->dbuf, abuf->data, abuf->byte_length); + return 0; +} + +static int JS_WriteSharedArrayBuffer(BCWriterState *s, JSValue obj) +{ + JSObject *p = JS_VALUE_GET_OBJ(obj); + JSArrayBuffer *abuf = p->u.array_buffer; + assert(!abuf->detached); /* SharedArrayBuffer are never detached */ + bc_put_u8(s, BC_TAG_SHARED_ARRAY_BUFFER); + bc_put_leb128(s, abuf->byte_length); + bc_put_leb128(s, abuf->max_byte_length); + bc_put_u64(s, (uintptr_t)abuf->data); + if (js_resize_array(s->ctx, (void **)&s->sab_tab, sizeof(s->sab_tab[0]), + &s->sab_tab_size, s->sab_tab_len + 1)) + return -1; + /* keep the SAB pointer so that the user can clone it or free it */ + s->sab_tab[s->sab_tab_len++] = abuf->data; + return 0; +} + +static int JS_WriteRegExp(BCWriterState *s, JSRegExp regexp) +{ + JSString *bc = regexp.bytecode; + assert(!bc->is_wide_char); + + JS_WriteString(s, regexp.pattern); + + if (is_be()) + lre_byte_swap(bc->u.str8, bc->len, /*is_byte_swapped*/FALSE); + + JS_WriteString(s, bc); + + if (is_be()) + lre_byte_swap(bc->u.str8, bc->len, /*is_byte_swapped*/TRUE); + + return 0; +} + +static int JS_WriteMap(BCWriterState *s, struct JSMapState *map_state); +static int JS_WriteSet(BCWriterState *s, struct JSMapState *map_state); + +static int JS_WriteObjectRec(BCWriterState *s, JSValue obj) +{ + uint32_t tag; + + if (js_check_stack_overflow(s->ctx->rt, 0)) { + JS_ThrowStackOverflow(s->ctx); + return -1; + } + + tag = JS_VALUE_GET_NORM_TAG(obj); + switch(tag) { + case JS_TAG_NULL: + bc_put_u8(s, BC_TAG_NULL); + break; + case JS_TAG_UNDEFINED: + bc_put_u8(s, BC_TAG_UNDEFINED); + break; + case JS_TAG_BOOL: + bc_put_u8(s, BC_TAG_BOOL_FALSE + JS_VALUE_GET_INT(obj)); + break; + case JS_TAG_INT: + bc_put_u8(s, BC_TAG_INT32); + bc_put_sleb128(s, JS_VALUE_GET_INT(obj)); + break; + case JS_TAG_FLOAT64: + { + JSFloat64Union u; + bc_put_u8(s, BC_TAG_FLOAT64); + u.d = JS_VALUE_GET_FLOAT64(obj); + bc_put_u64(s, u.u64); + } + break; + case JS_TAG_STRING: + { + JSString *p = JS_VALUE_GET_STRING(obj); + bc_put_u8(s, BC_TAG_STRING); + JS_WriteString(s, p); + } + break; + case JS_TAG_FUNCTION_BYTECODE: + if (!s->allow_bytecode) + goto invalid_tag; + if (JS_WriteFunctionTag(s, obj)) + goto fail; + break; + case JS_TAG_MODULE: + if (!s->allow_bytecode) + goto invalid_tag; + if (JS_WriteModule(s, obj)) + goto fail; + break; + case JS_TAG_OBJECT: + { + JSObject *p = JS_VALUE_GET_OBJ(obj); + int ret, idx; + + if (s->allow_reference) { + idx = js_object_list_find(s->ctx, &s->object_list, p); + if (idx >= 0) { + bc_put_u8(s, BC_TAG_OBJECT_REFERENCE); + bc_put_leb128(s, idx); + break; + } else { + if (js_object_list_add(s->ctx, &s->object_list, p)) + goto fail; + } + } else { + if (p->tmp_mark) { + JS_ThrowTypeError(s->ctx, "circular reference"); + goto fail; + } + p->tmp_mark = 1; + } + switch(p->class_id) { + case JS_CLASS_ARRAY: + ret = JS_WriteArray(s, obj); + break; + case JS_CLASS_OBJECT: + ret = JS_WriteObjectTag(s, obj); + break; + case JS_CLASS_ARRAY_BUFFER: + ret = JS_WriteArrayBuffer(s, obj); + break; + case JS_CLASS_SHARED_ARRAY_BUFFER: + if (!s->allow_sab) + goto invalid_tag; + ret = JS_WriteSharedArrayBuffer(s, obj); + break; + case JS_CLASS_REGEXP: + bc_put_u8(s, BC_TAG_REGEXP); + ret = JS_WriteRegExp(s, p->u.regexp); + break; + case JS_CLASS_DATE: + bc_put_u8(s, BC_TAG_DATE); + ret = JS_WriteObjectRec(s, p->u.object_data); + break; + case JS_CLASS_NUMBER: + case JS_CLASS_STRING: + case JS_CLASS_BOOLEAN: + case JS_CLASS_BIG_INT: + bc_put_u8(s, BC_TAG_OBJECT_VALUE); + ret = JS_WriteObjectRec(s, p->u.object_data); + break; + case JS_CLASS_MAP: + bc_put_u8(s, BC_TAG_MAP); + ret = JS_WriteMap(s, p->u.map_state); + break; + case JS_CLASS_SET: + bc_put_u8(s, BC_TAG_SET); + ret = JS_WriteSet(s, p->u.map_state); + break; + default: + if (p->class_id >= JS_CLASS_UINT8C_ARRAY && + p->class_id <= JS_CLASS_FLOAT64_ARRAY) { + ret = JS_WriteTypedArray(s, obj); + } else { + JS_ThrowTypeError(s->ctx, "unsupported object class"); + ret = -1; + } + break; + } + p->tmp_mark = 0; + if (ret) + goto fail; + } + break; + case JS_TAG_BIG_INT: + if (JS_WriteBigInt(s, obj)) + goto fail; + break; + case JS_TAG_SYMBOL: + { + JSAtomStruct *p = JS_VALUE_GET_PTR(obj); + if (p->atom_type != JS_ATOM_TYPE_GLOBAL_SYMBOL && p->atom_type != JS_ATOM_TYPE_SYMBOL) { + JS_ThrowTypeError(s->ctx, "unsupported symbol type"); + goto fail; + } + JSAtom atom = js_get_atom_index(s->ctx->rt, p); + bc_put_u8(s, BC_TAG_SYMBOL); + bc_put_atom(s, atom); + } + break; + default: + invalid_tag: + JS_ThrowInternalError(s->ctx, "unsupported tag (%d)", tag); + goto fail; + } + return 0; + + fail: + return -1; +} + +/* create the atom table */ +static int JS_WriteObjectAtoms(BCWriterState *s) +{ + JSRuntime *rt = s->ctx->rt; + DynBuf dbuf1; + int i, atoms_size; + + dbuf1 = s->dbuf; + js_dbuf_init(s->ctx, &s->dbuf); + bc_put_u8(s, BC_VERSION); + + bc_put_leb128(s, s->idx_to_atom_count); + for(i = 0; i < s->idx_to_atom_count; i++) { + JSAtom atom = s->idx_to_atom[i]; + if (__JS_AtomIsConst(atom)) { + bc_put_u8(s, 0 /* the type */); + /* TODO(saghul): encoding for tagged integers and keyword-ish atoms could be + more efficient. */ + bc_put_u32(s, atom); + } else { + JSAtomStruct *p = rt->atom_array[atom]; + uint8_t type = p->atom_type; + assert(type != JS_ATOM_TYPE_PRIVATE); + bc_put_u8(s, type); + JS_WriteString(s, p); + } + } + /* XXX: should check for OOM in above phase */ + + /* move the atoms at the start */ + /* XXX: could just append dbuf1 data, but it uses more memory if + dbuf1 is larger than dbuf */ + atoms_size = s->dbuf.size; + if (dbuf_realloc(&dbuf1, dbuf1.size + atoms_size)) + goto fail; + memmove(dbuf1.buf + atoms_size, dbuf1.buf, dbuf1.size); + memcpy(dbuf1.buf, s->dbuf.buf, atoms_size); + dbuf1.size += atoms_size; + dbuf_free(&s->dbuf); + s->dbuf = dbuf1; + return 0; + fail: + dbuf_free(&dbuf1); + return -1; +} + +uint8_t *JS_WriteObject2(JSContext *ctx, size_t *psize, JSValue obj, + int flags, JSSABTab *psab_tab) +{ + BCWriterState ss, *s = &ss; + + memset(s, 0, sizeof(*s)); + s->ctx = ctx; + s->allow_bytecode = ((flags & JS_WRITE_OBJ_BYTECODE) != 0); + s->allow_sab = ((flags & JS_WRITE_OBJ_SAB) != 0); + s->allow_reference = ((flags & JS_WRITE_OBJ_REFERENCE) != 0); + s->allow_source = ((flags & JS_WRITE_OBJ_STRIP_SOURCE) == 0); + s->allow_debug = ((flags & JS_WRITE_OBJ_STRIP_DEBUG) == 0); + /* XXX: could use a different version when bytecode is included */ + if (s->allow_bytecode) + s->first_atom = JS_ATOM_END; + else + s->first_atom = 1; + js_dbuf_init(ctx, &s->dbuf); + js_object_list_init(&s->object_list); + + if (JS_WriteObjectRec(s, obj)) + goto fail; + if (JS_WriteObjectAtoms(s)) + goto fail; + js_object_list_end(ctx, &s->object_list); + js_free(ctx, s->atom_to_idx); + js_free(ctx, s->idx_to_atom); + *psize = s->dbuf.size; + if (psab_tab) { + psab_tab->tab = s->sab_tab; + psab_tab->len = s->sab_tab_len; + } else { + js_free(ctx, s->sab_tab); + } + return s->dbuf.buf; + fail: + js_object_list_end(ctx, &s->object_list); + js_free(ctx, s->atom_to_idx); + js_free(ctx, s->idx_to_atom); + dbuf_free(&s->dbuf); + *psize = 0; + if (psab_tab) { + psab_tab->tab = NULL; + psab_tab->len = 0; + } + return NULL; +} + +uint8_t *JS_WriteObject(JSContext *ctx, size_t *psize, JSValue obj, + int flags) +{ + return JS_WriteObject2(ctx, psize, obj, flags, NULL); +} + +typedef struct BCReaderState { + JSContext *ctx; + const uint8_t *buf_start, *ptr, *buf_end; + uint32_t first_atom; + uint32_t idx_to_atom_count; + JSAtom *idx_to_atom; + int error_state; + BOOL allow_sab : 8; + BOOL allow_bytecode : 8; + BOOL allow_reference : 8; + /* object references */ + JSObject **objects; + int objects_count; + int objects_size; + /* SAB references */ + uint8_t **sab_tab; + int sab_tab_len; + int sab_tab_size; + /* used for DUMP_READ_OBJECT */ + const uint8_t *ptr_last; + int level; +} BCReaderState; + +#ifdef DUMP_READ_OBJECT +static void __attribute__((format(printf, 2, 3))) bc_read_trace(BCReaderState *s, const char *fmt, ...) { + va_list ap; + int i, n, n0; + + if (!check_dump_flag(s->ctx->rt, DUMP_READ_OBJECT)) + return; + + if (!s->ptr_last) + s->ptr_last = s->buf_start; + + n = n0 = 0; + if (s->ptr > s->ptr_last || s->ptr == s->buf_start) { + n0 = printf("%04x: ", (int)(s->ptr_last - s->buf_start)); + n += n0; + } + for (i = 0; s->ptr_last < s->ptr; i++) { + if ((i & 7) == 0 && i > 0) { + printf("\n%*s", n0, ""); + n = n0; + } + n += printf(" %02x", *s->ptr_last++); + } + if (*fmt == '}') + s->level--; + if (n < 32 + s->level * 2) { + printf("%*s", 32 + s->level * 2 - n, ""); + } + va_start(ap, fmt); + vfprintf(stdout, fmt, ap); + va_end(ap); + if (strchr(fmt, '{')) + s->level++; +} +#else +#define bc_read_trace(...) +#endif + +static int bc_read_error_end(BCReaderState *s) +{ + if (!s->error_state) { + JS_ThrowSyntaxError(s->ctx, "read after the end of the buffer"); + } + return s->error_state = -1; +} + +static int bc_get_u8(BCReaderState *s, uint8_t *pval) +{ + if (unlikely(s->buf_end - s->ptr < 1)) { + *pval = 0; /* avoid warning */ + return bc_read_error_end(s); + } + *pval = *s->ptr++; + return 0; +} + +static int bc_get_u16(BCReaderState *s, uint16_t *pval) +{ + uint16_t v; + if (unlikely(s->buf_end - s->ptr < 2)) { + *pval = 0; /* avoid warning */ + return bc_read_error_end(s); + } + v = get_u16(s->ptr); + if (is_be()) + v = bswap16(v); + *pval = v; + s->ptr += 2; + return 0; +} + +static __maybe_unused int bc_get_u32(BCReaderState *s, uint32_t *pval) +{ + uint32_t v; + if (unlikely(s->buf_end - s->ptr < 4)) { + *pval = 0; /* avoid warning */ + return bc_read_error_end(s); + } + v = get_u32(s->ptr); + if (is_be()) + v = bswap32(v); + *pval = v; + s->ptr += 4; + return 0; +} + +static int bc_get_u64(BCReaderState *s, uint64_t *pval) +{ + uint64_t v; + if (unlikely(s->buf_end - s->ptr < 8)) { + *pval = 0; /* avoid warning */ + return bc_read_error_end(s); + } + v = get_u64(s->ptr); + if (is_be()) + v = bswap64(v); + *pval = v; + s->ptr += 8; + return 0; +} + +static int bc_get_leb128(BCReaderState *s, uint32_t *pval) +{ + int ret; + ret = get_leb128(pval, s->ptr, s->buf_end); + if (unlikely(ret < 0)) + return bc_read_error_end(s); + s->ptr += ret; + return 0; +} + +static int bc_get_sleb128(BCReaderState *s, int32_t *pval) +{ + int ret; + ret = get_sleb128(pval, s->ptr, s->buf_end); + if (unlikely(ret < 0)) + return bc_read_error_end(s); + s->ptr += ret; + return 0; +} + +/* XXX: used to read an `int` with a positive value */ +static int bc_get_leb128_int(BCReaderState *s, int *pval) +{ + return bc_get_leb128(s, (uint32_t *)pval); +} + +static int bc_get_leb128_u16(BCReaderState *s, uint16_t *pval) +{ + uint32_t val; + if (bc_get_leb128(s, &val)) { + *pval = 0; + return -1; + } + *pval = val; + return 0; +} + +static int bc_get_buf(BCReaderState *s, void *buf, uint32_t buf_len) +{ + if (buf_len != 0) { + if (unlikely(!buf || s->buf_end - s->ptr < buf_len)) + return bc_read_error_end(s); + memcpy(buf, s->ptr, buf_len); + s->ptr += buf_len; + } + return 0; +} + +static int bc_idx_to_atom(BCReaderState *s, JSAtom *patom, uint32_t idx) +{ + JSAtom atom; + + if (__JS_AtomIsTaggedInt(idx)) { + atom = idx; + } else if (idx < s->first_atom) { + atom = JS_DupAtom(s->ctx, idx); + } else { + idx -= s->first_atom; + if (idx >= s->idx_to_atom_count) { + JS_ThrowSyntaxError(s->ctx, "invalid atom index (pos=%u)", + (unsigned int)(s->ptr - s->buf_start)); + *patom = JS_ATOM_NULL; + return s->error_state = -1; + } + atom = JS_DupAtom(s->ctx, s->idx_to_atom[idx]); + } + *patom = atom; + return 0; +} + +static int bc_get_atom(BCReaderState *s, JSAtom *patom) +{ + uint32_t v; + if (bc_get_leb128(s, &v)) + return -1; + if (v & 1) { + *patom = __JS_AtomFromUInt32(v >> 1); + return 0; + } else { + return bc_idx_to_atom(s, patom, v >> 1); + } +} + +static JSString *JS_ReadString(BCReaderState *s) +{ + uint32_t len; + size_t size; + BOOL is_wide_char; + JSString *p; + + if (bc_get_leb128(s, &len)) + return NULL; + is_wide_char = len & 1; + len >>= 1; + p = js_alloc_string(s->ctx, len, is_wide_char); + if (!p) { + s->error_state = -1; + return NULL; + } + size = (size_t)len << is_wide_char; + if ((s->buf_end - s->ptr) < size) { + bc_read_error_end(s); + js_free_string(s->ctx->rt, p); + return NULL; + } + memcpy(p->u.str8, s->ptr, size); + s->ptr += size; + if (is_wide_char) { + if (is_be()) { + uint32_t i; + for (i = 0; i < len; i++) + p->u.str16[i] = bswap16(p->u.str16[i]); + } + } else { + p->u.str8[size] = '\0'; /* add the trailing zero for 8 bit strings */ + } +#ifdef DUMP_READ_OBJECT + if (check_dump_flag(s->ctx->rt, DUMP_READ_OBJECT)) { + bc_read_trace(s, "%s", ""); // hex dump and indentation + JS_DumpString(s->ctx->rt, p); + printf("\n"); + } +#endif + return p; +} + +static uint32_t bc_get_flags(uint32_t flags, int *pidx, int n) +{ + uint32_t val; + /* XXX: this does not work for n == 32 */ + val = (flags >> *pidx) & ((1U << n) - 1); + *pidx += n; + return val; +} + +static int JS_ReadFunctionBytecode(BCReaderState *s, JSFunctionBytecode *b, + int byte_code_offset, uint32_t bc_len) +{ + uint8_t *bc_buf; + int pos, len, op; + JSAtom atom; + uint32_t idx; + + bc_buf = (uint8_t*)b + byte_code_offset; + if (bc_get_buf(s, bc_buf, bc_len)) + return -1; + b->byte_code_buf = bc_buf; + + if (is_be()) + bc_byte_swap(bc_buf, bc_len); + + pos = 0; + while (pos < bc_len) { + op = bc_buf[pos]; + len = short_opcode_info(op).size; + switch(short_opcode_info(op).fmt) { + case OP_FMT_atom: + case OP_FMT_atom_u8: + case OP_FMT_atom_u16: + case OP_FMT_atom_label_u8: + case OP_FMT_atom_label_u16: + idx = get_u32(bc_buf + pos + 1); + if (bc_idx_to_atom(s, &atom, idx)) { + /* Note: the atoms will be freed up to this position */ + b->byte_code_len = pos; + return -1; + } + put_u32(bc_buf + pos + 1, atom); + break; + default: + assert(!is_ic_op(op)); // should not end up in serialized bytecode + break; + } +#ifdef DUMP_READ_OBJECT + if (check_dump_flag(s->ctx->rt, DUMP_READ_OBJECT)) { + const uint8_t *save_ptr = s->ptr; + s->ptr = s->ptr_last + len; + s->level -= 4; + bc_read_trace(s, "%s", ""); // hex dump + indent + dump_single_byte_code(s->ctx, bc_buf + pos, b, + s->ptr - s->buf_start - len); + s->level += 4; + s->ptr = save_ptr; + } +#endif + pos += len; + } + return 0; +} + +static JSValue JS_ReadBigInt(BCReaderState *s) +{ + JSValue obj; + uint8_t v8; + int32_t e; + uint32_t len; + limb_t l, i, n; + limb_t v; + bf_t *a; + + obj = JS_NewBigInt(s->ctx); + if (JS_IsException(obj)) + goto fail; + + /* sign + exponent */ + if (bc_get_sleb128(s, &e)) + goto fail; + + a = JS_GetBigInt(obj); + a->sign = e & 1; + e >>= 1; + if (e == 0) + a->expn = BF_EXP_ZERO; + else if (e == 1) + a->expn = BF_EXP_INF; + else if (e == 2) + a->expn = BF_EXP_NAN; + else if (e >= 3) + a->expn = e - 3; + else + a->expn = e; + + /* mantissa */ + if (a->expn != BF_EXP_ZERO && + a->expn != BF_EXP_INF && + a->expn != BF_EXP_NAN) { + if (bc_get_leb128(s, &len)) + goto fail; + bc_read_trace(s, "len=%" PRId64 "\n", (int64_t)len); + if (len == 0) { + JS_ThrowRangeError(s->ctx, "maximum BigInt size exceeded"); + goto fail; + } + l = (len + sizeof(limb_t) - 1) / sizeof(limb_t); + if (bf_resize(a, l)) { + JS_ThrowOutOfMemory(s->ctx); + goto fail; + } + n = len & (sizeof(limb_t) - 1); + if (n != 0) { + v = 0; + for(i = 0; i < n; i++) { + if (bc_get_u8(s, &v8)) + goto fail; + v |= (limb_t)v8 << ((sizeof(limb_t) - n + i) * 8); + } + a->tab[0] = v; + i = 1; + } else { + i = 0; + } + for(; i < l; i++) { +#if LIMB_BITS == 32 + if (bc_get_u32(s, &v)) + goto fail; +#else + if (bc_get_u64(s, &v)) + goto fail; +#endif + a->tab[i] = v; + } + } + return obj; + fail: + JS_FreeValue(s->ctx, obj); + return JS_EXCEPTION; +} + +static JSValue JS_ReadObjectRec(BCReaderState *s); + +static int BC_add_object_ref1(BCReaderState *s, JSObject *p) +{ + if (s->allow_reference) { + if (js_resize_array(s->ctx, (void *)&s->objects, + sizeof(s->objects[0]), + &s->objects_size, s->objects_count + 1)) + return -1; + s->objects[s->objects_count++] = p; + } + return 0; +} + +static int BC_add_object_ref(BCReaderState *s, JSValue obj) +{ + return BC_add_object_ref1(s, JS_VALUE_GET_OBJ(obj)); +} + +static JSValue JS_ReadFunctionTag(BCReaderState *s) +{ + JSContext *ctx = s->ctx; + JSFunctionBytecode bc, *b; + JSValue obj = JS_UNDEFINED; + uint16_t v16; + uint8_t v8; + int idx, i, local_count, has_debug_info; + int function_size, cpool_offset, byte_code_offset; + int closure_var_offset, vardefs_offset; + + memset(&bc, 0, sizeof(bc)); + bc.header.ref_count = 1; + //bc.gc_header.mark = 0; + + if (bc_get_u16(s, &v16)) + goto fail; + idx = 0; + bc.has_prototype = bc_get_flags(v16, &idx, 1); + bc.has_simple_parameter_list = bc_get_flags(v16, &idx, 1); + bc.is_derived_class_constructor = bc_get_flags(v16, &idx, 1); + bc.need_home_object = bc_get_flags(v16, &idx, 1); + bc.func_kind = bc_get_flags(v16, &idx, 2); + bc.new_target_allowed = bc_get_flags(v16, &idx, 1); + bc.super_call_allowed = bc_get_flags(v16, &idx, 1); + bc.super_allowed = bc_get_flags(v16, &idx, 1); + bc.arguments_allowed = bc_get_flags(v16, &idx, 1); + bc.backtrace_barrier = bc_get_flags(v16, &idx, 1); + has_debug_info = bc_get_flags(v16, &idx, 1); + if (bc_get_u8(s, &v8)) + goto fail; + bc.is_strict_mode = (v8 > 0); + if (bc_get_atom(s, &bc.func_name)) + goto fail; + if (bc_get_leb128_u16(s, &bc.arg_count)) + goto fail; + if (bc_get_leb128_u16(s, &bc.var_count)) + goto fail; + if (bc_get_leb128_u16(s, &bc.defined_arg_count)) + goto fail; + if (bc_get_leb128_u16(s, &bc.stack_size)) + goto fail; + if (bc_get_leb128_int(s, &bc.closure_var_count)) + goto fail; + if (bc_get_leb128_int(s, &bc.cpool_count)) + goto fail; + if (bc_get_leb128_int(s, &bc.byte_code_len)) + goto fail; + if (bc_get_leb128_int(s, &local_count)) + goto fail; + + function_size = sizeof(*b); + cpool_offset = function_size; + function_size += bc.cpool_count * sizeof(*bc.cpool); + vardefs_offset = function_size; + function_size += local_count * sizeof(*bc.vardefs); + closure_var_offset = function_size; + function_size += bc.closure_var_count * sizeof(*bc.closure_var); + byte_code_offset = function_size; + function_size += bc.byte_code_len; + + b = js_mallocz(ctx, function_size); + if (!b) + goto fail; + + memcpy(b, &bc, sizeof(*b)); + bc.func_name = JS_ATOM_NULL; + b->header.ref_count = 1; + if (local_count != 0) { + b->vardefs = (void *)((uint8_t*)b + vardefs_offset); + } + if (b->closure_var_count != 0) { + b->closure_var = (void *)((uint8_t*)b + closure_var_offset); + } + if (b->cpool_count != 0) { + b->cpool = (void *)((uint8_t*)b + cpool_offset); + } + + add_gc_object(ctx->rt, &b->header, JS_GC_OBJ_TYPE_FUNCTION_BYTECODE); + + obj = JS_MKPTR(JS_TAG_FUNCTION_BYTECODE, b); + +#ifdef DUMP_READ_OBJECT + if (check_dump_flag(s->ctx->rt, DUMP_READ_OBJECT)) { + if (b->func_name) { + bc_read_trace(s, "name: "); + print_atom(s->ctx, b->func_name); + printf("\n"); + } + } +#endif + bc_read_trace(s, "args=%d vars=%d defargs=%d closures=%d cpool=%d\n", + b->arg_count, b->var_count, b->defined_arg_count, + b->closure_var_count, b->cpool_count); + bc_read_trace(s, "stack=%d bclen=%d locals=%d\n", + b->stack_size, b->byte_code_len, local_count); + + if (local_count != 0) { + bc_read_trace(s, "vars {\n"); + bc_read_trace(s, "off flags scope name\n"); + for(i = 0; i < local_count; i++) { + JSVarDef *vd = &b->vardefs[i]; + if (bc_get_atom(s, &vd->var_name)) + goto fail; + if (bc_get_leb128_int(s, &vd->scope_level)) + goto fail; + if (bc_get_leb128_int(s, &vd->scope_next)) + goto fail; + vd->scope_next--; + if (bc_get_u8(s, &v8)) + goto fail; + idx = 0; + vd->var_kind = bc_get_flags(v8, &idx, 4); + vd->is_const = bc_get_flags(v8, &idx, 1); + vd->is_lexical = bc_get_flags(v8, &idx, 1); + vd->is_captured = bc_get_flags(v8, &idx, 1); +#ifdef DUMP_READ_OBJECT + if (check_dump_flag(s->ctx->rt, DUMP_READ_OBJECT)) { + bc_read_trace(s, "%3d %d%c%c%c %4d ", + i, vd->var_kind, + vd->is_const ? 'C' : '.', + vd->is_lexical ? 'L' : '.', + vd->is_captured ? 'X' : '.', + vd->scope_level); + print_atom(s->ctx, vd->var_name); + printf("\n"); + } +#endif + } + bc_read_trace(s, "}\n"); + } + if (b->closure_var_count != 0) { + bc_read_trace(s, "closure vars {\n"); + bc_read_trace(s, "off flags idx name\n"); + for(i = 0; i < b->closure_var_count; i++) { + JSClosureVar *cv = &b->closure_var[i]; + int var_idx; + if (bc_get_atom(s, &cv->var_name)) + goto fail; + if (bc_get_leb128_int(s, &var_idx)) + goto fail; + cv->var_idx = var_idx; + if (bc_get_u8(s, &v8)) + goto fail; + idx = 0; + cv->is_local = bc_get_flags(v8, &idx, 1); + cv->is_arg = bc_get_flags(v8, &idx, 1); + cv->is_const = bc_get_flags(v8, &idx, 1); + cv->is_lexical = bc_get_flags(v8, &idx, 1); + cv->var_kind = bc_get_flags(v8, &idx, 4); +#ifdef DUMP_READ_OBJECT + if (check_dump_flag(s->ctx->rt, DUMP_READ_OBJECT)) { + bc_read_trace(s, "%3d %d%c%c%c%c %3d ", + i, cv->var_kind, + cv->is_local ? 'L' : '.', + cv->is_arg ? 'A' : '.', + cv->is_const ? 'C' : '.', + cv->is_lexical ? 'X' : '.', + cv->var_idx); + print_atom(s->ctx, cv->var_name); + printf("\n"); + } +#endif + } + bc_read_trace(s, "}\n"); + } + if (b->cpool_count != 0) { + bc_read_trace(s, "cpool {\n"); + for(i = 0; i < b->cpool_count; i++) { + JSValue val; + val = JS_ReadObjectRec(s); + if (JS_IsException(val)) + goto fail; + b->cpool[i] = val; + } + bc_read_trace(s, "}\n"); + } + { + bc_read_trace(s, "bytecode {\n"); + if (JS_ReadFunctionBytecode(s, b, byte_code_offset, b->byte_code_len)) + goto fail; + bc_read_trace(s, "}\n"); + } + if (!has_debug_info) + goto nodebug; + + /* read optional debug information */ + bc_read_trace(s, "debug {\n"); + if (bc_get_atom(s, &b->filename)) + goto fail; + if (bc_get_leb128_int(s, &b->line_num)) + goto fail; + if (bc_get_leb128_int(s, &b->col_num)) + goto fail; +#ifdef DUMP_READ_OBJECT + if (check_dump_flag(s->ctx->rt, DUMP_READ_OBJECT)) { + bc_read_trace(s, "filename: "); + print_atom(s->ctx, b->filename); + printf(", line: %d, column: %d\n", b->line_num, b->col_num); + } +#endif + if (bc_get_leb128_int(s, &b->pc2line_len)) + goto fail; + if (b->pc2line_len) { + bc_read_trace(s, "positions: %d bytes\n", b->pc2line_len); + b->pc2line_buf = js_mallocz(ctx, b->pc2line_len); + if (!b->pc2line_buf) + goto fail; + if (bc_get_buf(s, b->pc2line_buf, b->pc2line_len)) + goto fail; + } + if (bc_get_leb128_int(s, &b->source_len)) + goto fail; + if (b->source_len) { + bc_read_trace(s, "source: %d bytes\n", b->source_len); + if (s->ptr_last) + s->ptr_last += b->source_len; // omit source code hex dump + /* b->source is a UTF-8 encoded null terminated C string */ + b->source = js_mallocz(ctx, b->source_len + 1); + if (!b->source) + goto fail; + if (bc_get_buf(s, b->source, b->source_len)) + goto fail; + } + bc_read_trace(s, "}\n"); + + nodebug: + b->realm = JS_DupContext(ctx); + return obj; + + fail: + JS_FreeAtom(ctx, bc.func_name); + JS_FreeValue(ctx, obj); + return JS_EXCEPTION; +} + +static JSValue JS_ReadModule(BCReaderState *s) +{ + JSContext *ctx = s->ctx; + JSValue obj; + JSModuleDef *m = NULL; + JSAtom module_name; + int i; + uint8_t v8; + + if (bc_get_atom(s, &module_name)) + goto fail; +#ifdef DUMP_READ_OBJECT + if (check_dump_flag(s->ctx->rt, DUMP_READ_OBJECT)) { + bc_read_trace(s, "name: "); + print_atom(s->ctx, module_name); + printf("\n"); + } +#endif + m = js_new_module_def(ctx, module_name); + if (!m) + goto fail; + obj = js_dup(JS_MKPTR(JS_TAG_MODULE, m)); + if (bc_get_leb128_int(s, &m->req_module_entries_count)) + goto fail; + obj = JS_NewModuleValue(ctx, m); + if (m->req_module_entries_count != 0) { + m->req_module_entries_size = m->req_module_entries_count; + m->req_module_entries = js_mallocz(ctx, sizeof(m->req_module_entries[0]) * m->req_module_entries_size); + if (!m->req_module_entries) + goto fail; + for(i = 0; i < m->req_module_entries_count; i++) { + JSReqModuleEntry *rme = &m->req_module_entries[i]; + if (bc_get_atom(s, &rme->module_name)) + goto fail; + } + } + + if (bc_get_leb128_int(s, &m->export_entries_count)) + goto fail; + if (m->export_entries_count != 0) { + m->export_entries_size = m->export_entries_count; + m->export_entries = js_mallocz(ctx, sizeof(m->export_entries[0]) * m->export_entries_size); + if (!m->export_entries) + goto fail; + for(i = 0; i < m->export_entries_count; i++) { + JSExportEntry *me = &m->export_entries[i]; + if (bc_get_u8(s, &v8)) + goto fail; + me->export_type = v8; + if (me->export_type == JS_EXPORT_TYPE_LOCAL) { + if (bc_get_leb128_int(s, &me->u.local.var_idx)) + goto fail; + } else { + if (bc_get_leb128_int(s, &me->u.req_module_idx)) + goto fail; + if (bc_get_atom(s, &me->local_name)) + goto fail; + } + if (bc_get_atom(s, &me->export_name)) + goto fail; + } + } + + if (bc_get_leb128_int(s, &m->star_export_entries_count)) + goto fail; + if (m->star_export_entries_count != 0) { + m->star_export_entries_size = m->star_export_entries_count; + m->star_export_entries = js_mallocz(ctx, sizeof(m->star_export_entries[0]) * m->star_export_entries_size); + if (!m->star_export_entries) + goto fail; + for(i = 0; i < m->star_export_entries_count; i++) { + JSStarExportEntry *se = &m->star_export_entries[i]; + if (bc_get_leb128_int(s, &se->req_module_idx)) + goto fail; + } + } + + if (bc_get_leb128_int(s, &m->import_entries_count)) + goto fail; + if (m->import_entries_count != 0) { + m->import_entries_size = m->import_entries_count; + m->import_entries = js_mallocz(ctx, sizeof(m->import_entries[0]) * m->import_entries_size); + if (!m->import_entries) + goto fail; + for(i = 0; i < m->import_entries_count; i++) { + JSImportEntry *mi = &m->import_entries[i]; + if (bc_get_leb128_int(s, &mi->var_idx)) + goto fail; + if (bc_get_atom(s, &mi->import_name)) + goto fail; + if (bc_get_leb128_int(s, &mi->req_module_idx)) + goto fail; + } + } + + if (bc_get_u8(s, &v8)) + goto fail; + m->has_tla = (v8 != 0); + + m->func_obj = JS_ReadObjectRec(s); + if (JS_IsException(m->func_obj)) + goto fail; + return obj; + fail: + if (m) { + js_free_module_def(ctx, m); + } + return JS_EXCEPTION; +} + +static JSValue JS_ReadObjectTag(BCReaderState *s) +{ + JSContext *ctx = s->ctx; + JSValue obj; + uint32_t prop_count, i; + JSAtom atom; + JSValue val; + int ret; + + obj = JS_NewObject(ctx); + if (BC_add_object_ref(s, obj)) + goto fail; + if (bc_get_leb128(s, &prop_count)) + goto fail; + for(i = 0; i < prop_count; i++) { + if (bc_get_atom(s, &atom)) + goto fail; +#ifdef DUMP_READ_OBJECT + if (check_dump_flag(s->ctx->rt, DUMP_READ_OBJECT)) { + bc_read_trace(s, "propname: "); + print_atom(s->ctx, atom); + printf("\n"); + } +#endif + val = JS_ReadObjectRec(s); + if (JS_IsException(val)) { + JS_FreeAtom(ctx, atom); + goto fail; + } + ret = JS_DefinePropertyValue(ctx, obj, atom, val, JS_PROP_C_W_E); + JS_FreeAtom(ctx, atom); + if (ret < 0) + goto fail; + } + return obj; + fail: + JS_FreeValue(ctx, obj); + return JS_EXCEPTION; +} + +static JSValue JS_ReadArray(BCReaderState *s, int tag) +{ + JSContext *ctx = s->ctx; + JSValue obj; + uint32_t len, i; + JSValue val; + int ret, prop_flags; + BOOL is_template; + + obj = JS_NewArray(ctx); + if (BC_add_object_ref(s, obj)) + goto fail; + is_template = (tag == BC_TAG_TEMPLATE_OBJECT); + if (bc_get_leb128(s, &len)) + goto fail; + for(i = 0; i < len; i++) { + val = JS_ReadObjectRec(s); + if (JS_IsException(val)) + goto fail; + if (is_template) + prop_flags = JS_PROP_ENUMERABLE; + else + prop_flags = JS_PROP_C_W_E; + ret = JS_DefinePropertyValueUint32(ctx, obj, i, val, + prop_flags); + if (ret < 0) + goto fail; + } + if (is_template) { + val = JS_ReadObjectRec(s); + if (JS_IsException(val)) + goto fail; + if (!JS_IsUndefined(val)) { + ret = JS_DefinePropertyValue(ctx, obj, JS_ATOM_raw, val, 0); + if (ret < 0) + goto fail; + } + JS_PreventExtensions(ctx, obj); + } + return obj; + fail: + JS_FreeValue(ctx, obj); + return JS_EXCEPTION; +} + +static JSValue JS_ReadTypedArray(BCReaderState *s) +{ + JSContext *ctx = s->ctx; + JSValue obj = JS_UNDEFINED, array_buffer = JS_UNDEFINED; + uint8_t array_tag; + JSValue args[3]; + uint32_t offset, len, idx; + + if (bc_get_u8(s, &array_tag)) + return JS_EXCEPTION; + if (array_tag >= JS_TYPED_ARRAY_COUNT) + return JS_ThrowTypeError(ctx, "invalid typed array"); + if (bc_get_leb128(s, &len)) + return JS_EXCEPTION; + if (bc_get_leb128(s, &offset)) + return JS_EXCEPTION; + /* XXX: this hack could be avoided if the typed array could be + created before the array buffer */ + idx = s->objects_count; + if (BC_add_object_ref1(s, NULL)) + goto fail; + array_buffer = JS_ReadObjectRec(s); + if (JS_IsException(array_buffer)) + return JS_EXCEPTION; + if (!js_get_array_buffer(ctx, array_buffer)) { + JS_FreeValue(ctx, array_buffer); + return JS_EXCEPTION; + } + args[0] = array_buffer; + args[1] = js_int64(offset); + args[2] = js_int64(len); + obj = js_typed_array_constructor(ctx, JS_UNDEFINED, + 3, args, + JS_CLASS_UINT8C_ARRAY + array_tag); + if (JS_IsException(obj)) + goto fail; + if (s->allow_reference) { + s->objects[idx] = JS_VALUE_GET_OBJ(obj); + } + JS_FreeValue(ctx, array_buffer); + return obj; + fail: + JS_FreeValue(ctx, array_buffer); + JS_FreeValue(ctx, obj); + return JS_EXCEPTION; +} + +static JSValue JS_ReadArrayBuffer(BCReaderState *s) +{ + JSContext *ctx = s->ctx; + uint32_t byte_length, max_byte_length; + uint64_t max_byte_length_u64, *pmax_byte_length = NULL; + JSValue obj; + + if (bc_get_leb128(s, &byte_length)) + return JS_EXCEPTION; + if (bc_get_leb128(s, &max_byte_length)) + return JS_EXCEPTION; + if (max_byte_length < byte_length) + return JS_ThrowTypeError(ctx, "invalid array buffer"); + if (max_byte_length != UINT32_MAX) { + max_byte_length_u64 = max_byte_length; + pmax_byte_length = &max_byte_length_u64; + } + if (unlikely(s->buf_end - s->ptr < byte_length)) { + bc_read_error_end(s); + return JS_EXCEPTION; + } + // makes a copy of the input + obj = js_array_buffer_constructor3(ctx, JS_UNDEFINED, + byte_length, pmax_byte_length, + JS_CLASS_ARRAY_BUFFER, + (uint8_t*)s->ptr, + js_array_buffer_free, NULL, + /*alloc_flag*/TRUE); + if (JS_IsException(obj)) + goto fail; + if (BC_add_object_ref(s, obj)) + goto fail; + s->ptr += byte_length; + return obj; + fail: + JS_FreeValue(ctx, obj); + return JS_EXCEPTION; +} + +static JSValue JS_ReadSharedArrayBuffer(BCReaderState *s) +{ + JSContext *ctx = s->ctx; + uint32_t byte_length, max_byte_length; + uint64_t max_byte_length_u64, *pmax_byte_length = NULL; + uint8_t *data_ptr; + JSValue obj; + uint64_t u64; + + if (bc_get_leb128(s, &byte_length)) + return JS_EXCEPTION; + if (bc_get_leb128(s, &max_byte_length)) + return JS_EXCEPTION; + if (max_byte_length < byte_length) + return JS_ThrowTypeError(ctx, "invalid array buffer"); + if (max_byte_length != UINT32_MAX) { + max_byte_length_u64 = max_byte_length; + pmax_byte_length = &max_byte_length_u64; + } + if (bc_get_u64(s, &u64)) + return JS_EXCEPTION; + data_ptr = (uint8_t *)(uintptr_t)u64; + if (js_resize_array(s->ctx, (void **)&s->sab_tab, sizeof(s->sab_tab[0]), + &s->sab_tab_size, s->sab_tab_len + 1)) + return JS_EXCEPTION; + /* keep the SAB pointer so that the user can clone it or free it */ + s->sab_tab[s->sab_tab_len++] = data_ptr; + /* the SharedArrayBuffer is cloned */ + obj = js_array_buffer_constructor3(ctx, JS_UNDEFINED, + byte_length, pmax_byte_length, + JS_CLASS_SHARED_ARRAY_BUFFER, + data_ptr, + NULL, NULL, FALSE); + if (JS_IsException(obj)) + goto fail; + if (BC_add_object_ref(s, obj)) + goto fail; + return obj; + fail: + JS_FreeValue(ctx, obj); + return JS_EXCEPTION; +} + +static JSValue JS_ReadRegExp(BCReaderState *s) +{ + JSContext *ctx = s->ctx; + JSString *pattern; + JSString *bc; + + pattern = JS_ReadString(s); + if (!pattern) + return JS_EXCEPTION; + + bc = JS_ReadString(s); + if (!bc) { + js_free_string(ctx->rt, pattern); + return JS_EXCEPTION; + } + + if (bc->is_wide_char) { + js_free_string(ctx->rt, pattern); + js_free_string(ctx->rt, bc); + return JS_ThrowInternalError(ctx, "bad regexp bytecode"); + } + + if (is_be()) + lre_byte_swap(bc->u.str8, bc->len, /*is_byte_swapped*/TRUE); + + return js_regexp_constructor_internal(ctx, JS_UNDEFINED, + JS_MKPTR(JS_TAG_STRING, pattern), + JS_MKPTR(JS_TAG_STRING, bc)); +} + +static JSValue JS_ReadDate(BCReaderState *s) +{ + JSContext *ctx = s->ctx; + JSValue val, obj = JS_UNDEFINED; + + val = JS_ReadObjectRec(s); + if (JS_IsException(val)) + goto fail; + if (!JS_IsNumber(val)) { + JS_ThrowTypeError(ctx, "Number tag expected for date"); + goto fail; + } + obj = JS_NewObjectProtoClass(ctx, ctx->class_proto[JS_CLASS_DATE], + JS_CLASS_DATE); + if (JS_IsException(obj)) + goto fail; + if (BC_add_object_ref(s, obj)) + goto fail; + JS_SetObjectData(ctx, obj, val); + return obj; + fail: + JS_FreeValue(ctx, val); + JS_FreeValue(ctx, obj); + return JS_EXCEPTION; +} + +static JSValue JS_ReadObjectValue(BCReaderState *s) +{ + JSContext *ctx = s->ctx; + JSValue val, obj = JS_UNDEFINED; + + val = JS_ReadObjectRec(s); + if (JS_IsException(val)) + goto fail; + obj = JS_ToObject(ctx, val); + if (JS_IsException(obj)) + goto fail; + if (BC_add_object_ref(s, obj)) + goto fail; + JS_FreeValue(ctx, val); + return obj; + fail: + JS_FreeValue(ctx, val); + JS_FreeValue(ctx, obj); + return JS_EXCEPTION; +} + +static JSValue JS_ReadMap(BCReaderState *s); +static JSValue JS_ReadSet(BCReaderState *s); + +static JSValue JS_ReadObjectRec(BCReaderState *s) +{ + JSContext *ctx = s->ctx; + uint8_t tag; + JSValue obj = JS_UNDEFINED; + + if (js_check_stack_overflow(ctx->rt, 0)) + return JS_ThrowStackOverflow(ctx); + + if (bc_get_u8(s, &tag)) + return JS_EXCEPTION; + + bc_read_trace(s, "%s {\n", bc_tag_str[tag]); + + switch(tag) { + case BC_TAG_NULL: + obj = JS_NULL; + break; + case BC_TAG_UNDEFINED: + obj = JS_UNDEFINED; + break; + case BC_TAG_BOOL_FALSE: + case BC_TAG_BOOL_TRUE: + obj = js_bool(tag - BC_TAG_BOOL_FALSE); + break; + case BC_TAG_INT32: + { + int32_t val; + if (bc_get_sleb128(s, &val)) + return JS_EXCEPTION; + bc_read_trace(s, "%d\n", val); + obj = js_int32(val); + } + break; + case BC_TAG_FLOAT64: + { + JSFloat64Union u; + if (bc_get_u64(s, &u.u64)) + return JS_EXCEPTION; + bc_read_trace(s, "%g\n", u.d); + obj = js_float64(u.d); + } + break; + case BC_TAG_STRING: + { + JSString *p; + p = JS_ReadString(s); + if (!p) + return JS_EXCEPTION; + obj = JS_MKPTR(JS_TAG_STRING, p); + } + break; + case BC_TAG_FUNCTION_BYTECODE: + if (!s->allow_bytecode) + goto no_allow_bytecode; + obj = JS_ReadFunctionTag(s); + break; + case BC_TAG_MODULE: + if (!s->allow_bytecode) { + no_allow_bytecode: + return JS_ThrowSyntaxError(ctx, "no bytecode allowed"); + } + obj = JS_ReadModule(s); + break; + case BC_TAG_OBJECT: + obj = JS_ReadObjectTag(s); + break; + case BC_TAG_ARRAY: + case BC_TAG_TEMPLATE_OBJECT: + obj = JS_ReadArray(s, tag); + break; + case BC_TAG_TYPED_ARRAY: + obj = JS_ReadTypedArray(s); + break; + case BC_TAG_ARRAY_BUFFER: + obj = JS_ReadArrayBuffer(s); + break; + case BC_TAG_SHARED_ARRAY_BUFFER: + if (!s->allow_sab || !ctx->rt->sab_funcs.sab_dup) + goto invalid_tag; + obj = JS_ReadSharedArrayBuffer(s); + break; + case BC_TAG_REGEXP: + obj = JS_ReadRegExp(s); + break; + case BC_TAG_DATE: + obj = JS_ReadDate(s); + break; + case BC_TAG_OBJECT_VALUE: + obj = JS_ReadObjectValue(s); + break; + case BC_TAG_BIG_INT: + obj = JS_ReadBigInt(s); + break; + case BC_TAG_OBJECT_REFERENCE: + { + uint32_t val; + if (!s->allow_reference) + return JS_ThrowSyntaxError(ctx, "object references are not allowed"); + if (bc_get_leb128(s, &val)) + return JS_EXCEPTION; + bc_read_trace(s, "%u\n", val); + if (val >= s->objects_count) { + return JS_ThrowSyntaxError(ctx, "invalid object reference (%u >= %u)", + val, s->objects_count); + } + obj = js_dup(JS_MKPTR(JS_TAG_OBJECT, s->objects[val])); + } + break; + case BC_TAG_MAP: + obj = JS_ReadMap(s); + break; + case BC_TAG_SET: + obj = JS_ReadSet(s); + break; + case BC_TAG_SYMBOL: + { + JSAtom atom; + if (bc_get_atom(s, &atom)) + return JS_EXCEPTION; + if (__JS_AtomIsConst(atom)) { + obj = JS_AtomToValue(s->ctx, atom); + } else { + JSAtomStruct *p = s->ctx->rt->atom_array[atom]; + obj = JS_NewSymbolFromAtom(s->ctx, atom, p->atom_type); + } + } + break; + default: + invalid_tag: + return JS_ThrowSyntaxError(ctx, "invalid tag (tag=%d pos=%u)", + tag, (unsigned int)(s->ptr - s->buf_start)); + } + bc_read_trace(s, "}\n"); + return obj; +} + +static int JS_ReadObjectAtoms(BCReaderState *s) +{ + uint8_t v8, type; + JSString *p; + int i; + JSAtom atom; + + if (bc_get_u8(s, &v8)) + return -1; + if (v8 != BC_VERSION) { + JS_ThrowSyntaxError(s->ctx, "invalid version (%d expected=%d)", + v8, BC_VERSION); + return -1; + } + if (bc_get_leb128(s, &s->idx_to_atom_count)) + return -1; + if (s->idx_to_atom_count > 1000*1000) { + JS_ThrowInternalError(s->ctx, "unreasonable atom count: %u", + s->idx_to_atom_count); + return -1; + } + + bc_read_trace(s, "%u atom indexes {\n", s->idx_to_atom_count); + + if (s->idx_to_atom_count != 0) { + s->idx_to_atom = js_mallocz(s->ctx, s->idx_to_atom_count * + sizeof(s->idx_to_atom[0])); + if (!s->idx_to_atom) + return s->error_state = -1; + } + for(i = 0; i < s->idx_to_atom_count; i++) { + if (bc_get_u8(s, &type)) { + return -1; + } + if (type == 0) { + if (bc_get_u32(s, &atom)) + return -1; + if (!__JS_AtomIsConst(atom)) { + JS_ThrowInternalError(s->ctx, "out of range atom"); + return -1; + } + } else { + if (type < JS_ATOM_TYPE_STRING || type >= JS_ATOM_TYPE_PRIVATE) { + JS_ThrowInternalError(s->ctx, "invalid symbol type %d", type); + return -1; + } + p = JS_ReadString(s); + if (!p) + return -1; + atom = __JS_NewAtom(s->ctx->rt, p, type); + } + if (atom == JS_ATOM_NULL) + return s->error_state = -1; + s->idx_to_atom[i] = atom; + } + bc_read_trace(s, "}\n"); + return 0; +} + +static void bc_reader_free(BCReaderState *s) +{ + int i; + if (s->idx_to_atom) { + for(i = 0; i < s->idx_to_atom_count; i++) { + JS_FreeAtom(s->ctx, s->idx_to_atom[i]); + } + js_free(s->ctx, s->idx_to_atom); + } + js_free(s->ctx, s->objects); +} + +JSValue JS_ReadObject2(JSContext *ctx, const uint8_t *buf, size_t buf_len, + int flags, JSSABTab *psab_tab) +{ + BCReaderState ss, *s = &ss; + JSValue obj; + + ctx->binary_object_count += 1; + ctx->binary_object_size += buf_len; + + memset(s, 0, sizeof(*s)); + s->ctx = ctx; + s->buf_start = buf; + s->buf_end = buf + buf_len; + s->ptr = buf; + s->allow_bytecode = ((flags & JS_READ_OBJ_BYTECODE) != 0); + s->allow_sab = ((flags & JS_READ_OBJ_SAB) != 0); + s->allow_reference = ((flags & JS_READ_OBJ_REFERENCE) != 0); + if (s->allow_bytecode) + s->first_atom = JS_ATOM_END; + else + s->first_atom = 1; + if (JS_ReadObjectAtoms(s)) { + obj = JS_EXCEPTION; + } else { + obj = JS_ReadObjectRec(s); + } + if (psab_tab) { + psab_tab->tab = s->sab_tab; + psab_tab->len = s->sab_tab_len; + } else { + js_free(ctx, s->sab_tab); + } + bc_reader_free(s); + return obj; +} + +JSValue JS_ReadObject(JSContext *ctx, const uint8_t *buf, size_t buf_len, + int flags) +{ + return JS_ReadObject2(ctx, buf, buf_len, flags, NULL); +} + +/*******************************************************************/ +/* runtime functions & objects */ + +static JSValue js_string_constructor(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv); +static JSValue js_boolean_constructor(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv); +static JSValue js_number_constructor(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv); + +static int check_function(JSContext *ctx, JSValue obj) +{ + if (likely(JS_IsFunction(ctx, obj))) + return 0; + JS_ThrowTypeError(ctx, "not a function"); + return -1; +} + +static int check_exception_free(JSContext *ctx, JSValue obj) +{ + JS_FreeValue(ctx, obj); + return JS_IsException(obj); +} + +/* `export_name` may be pure ASCII or UTF-8 encoded */ +static JSAtom find_atom(JSContext *ctx, const char *name) +{ + JSAtom atom; + int len; + + if (*name == '[') { + name++; + len = strlen(name) - 1; + /* We assume 8 bit non null strings, which is the case for these + symbols */ + for(atom = JS_ATOM_Symbol_toPrimitive; atom < JS_ATOM_END; atom++) { + JSAtomStruct *p = ctx->rt->atom_array[atom]; + JSString *str = p; + if (str->len == len && !memcmp(str->u.str8, name, len)) + return JS_DupAtom(ctx, atom); + } + abort(); + } else { + atom = JS_NewAtom(ctx, name); + } + return atom; +} + +static JSValue JS_InstantiateFunctionListItem2(JSContext *ctx, JSObject *p, + JSAtom atom, void *opaque) +{ + const JSCFunctionListEntry *e = opaque; + JSValue val; + + switch(e->def_type) { + case JS_DEF_CFUNC: + val = JS_NewCFunction2(ctx, e->u.func.cfunc.generic, + e->name, e->u.func.length, e->u.func.cproto, e->magic); + break; + case JS_DEF_PROP_STRING: + val = JS_NewAtomString(ctx, e->u.str); + break; + case JS_DEF_OBJECT: + val = JS_NewObject(ctx); + JS_SetPropertyFunctionList(ctx, val, e->u.prop_list.tab, e->u.prop_list.len); + break; + default: + abort(); + } + return val; +} + +static int JS_InstantiateFunctionListItem(JSContext *ctx, JSValue obj, + JSAtom atom, + const JSCFunctionListEntry *e) +{ + JSValue val; + int prop_flags = e->prop_flags; + + switch(e->def_type) { + case JS_DEF_ALIAS: /* using autoinit for aliases is not safe */ + { + JSAtom atom1 = find_atom(ctx, e->u.alias.name); + switch (e->u.alias.base) { + case -1: + val = JS_GetProperty(ctx, obj, atom1); + break; + case 0: + val = JS_GetProperty(ctx, ctx->global_obj, atom1); + break; + case 1: + val = JS_GetProperty(ctx, ctx->class_proto[JS_CLASS_ARRAY], atom1); + break; + default: + abort(); + } + JS_FreeAtom(ctx, atom1); + if (atom == JS_ATOM_Symbol_toPrimitive) { + /* Symbol.toPrimitive functions are not writable */ + prop_flags = JS_PROP_CONFIGURABLE; + } else if (atom == JS_ATOM_Symbol_hasInstance) { + /* Function.prototype[Symbol.hasInstance] is not writable nor configurable */ + prop_flags = 0; + } + } + break; + case JS_DEF_CFUNC: + if (atom == JS_ATOM_Symbol_toPrimitive) { + /* Symbol.toPrimitive functions are not writable */ + prop_flags = JS_PROP_CONFIGURABLE; + } else if (atom == JS_ATOM_Symbol_hasInstance) { + /* Function.prototype[Symbol.hasInstance] is not writable nor configurable */ + prop_flags = 0; + } + JS_DefineAutoInitProperty(ctx, obj, atom, JS_AUTOINIT_ID_PROP, + (void *)e, prop_flags); + return 0; + case JS_DEF_CGETSET: /* XXX: use autoinit again ? */ + case JS_DEF_CGETSET_MAGIC: + { + JSValue getter, setter; + char buf[64]; + + getter = JS_UNDEFINED; + if (e->u.getset.get.generic) { + snprintf(buf, sizeof(buf), "get %s", e->name); + getter = JS_NewCFunction2(ctx, e->u.getset.get.generic, + buf, 0, e->def_type == JS_DEF_CGETSET_MAGIC ? JS_CFUNC_getter_magic : JS_CFUNC_getter, + e->magic); + } + setter = JS_UNDEFINED; + if (e->u.getset.set.generic) { + snprintf(buf, sizeof(buf), "set %s", e->name); + setter = JS_NewCFunction2(ctx, e->u.getset.set.generic, + buf, 1, e->def_type == JS_DEF_CGETSET_MAGIC ? JS_CFUNC_setter_magic : JS_CFUNC_setter, + e->magic); + } + JS_DefinePropertyGetSet(ctx, obj, atom, getter, setter, prop_flags); + return 0; + } + break; + case JS_DEF_PROP_INT32: + val = js_int32(e->u.i32); + break; + case JS_DEF_PROP_INT64: + val = js_int64(e->u.i64); + break; + case JS_DEF_PROP_DOUBLE: + val = js_float64(e->u.f64); + break; + case JS_DEF_PROP_UNDEFINED: + val = JS_UNDEFINED; + break; + case JS_DEF_PROP_STRING: + case JS_DEF_OBJECT: + JS_DefineAutoInitProperty(ctx, obj, atom, JS_AUTOINIT_ID_PROP, + (void *)e, prop_flags); + return 0; + default: + abort(); + } + JS_DefinePropertyValue(ctx, obj, atom, val, prop_flags); + return 0; +} + +void JS_SetPropertyFunctionList(JSContext *ctx, JSValue obj, + const JSCFunctionListEntry *tab, int len) +{ + int i; + + for (i = 0; i < len; i++) { + const JSCFunctionListEntry *e = &tab[i]; + JSAtom atom = find_atom(ctx, e->name); + JS_InstantiateFunctionListItem(ctx, obj, atom, e); + JS_FreeAtom(ctx, atom); + } +} + +int JS_AddModuleExportList(JSContext *ctx, JSModuleDef *m, + const JSCFunctionListEntry *tab, int len) +{ + int i; + for(i = 0; i < len; i++) { + if (JS_AddModuleExport(ctx, m, tab[i].name)) + return -1; + } + return 0; +} + +int JS_SetModuleExportList(JSContext *ctx, JSModuleDef *m, + const JSCFunctionListEntry *tab, int len) +{ + int i; + JSValue val; + + for(i = 0; i < len; i++) { + const JSCFunctionListEntry *e = &tab[i]; + switch(e->def_type) { + case JS_DEF_CFUNC: + val = JS_NewCFunction2(ctx, e->u.func.cfunc.generic, + e->name, e->u.func.length, e->u.func.cproto, e->magic); + break; + case JS_DEF_PROP_STRING: + /* `e->u.str` may be pure ASCII or UTF-8 encoded */ + val = JS_NewString(ctx, e->u.str); + break; + case JS_DEF_PROP_INT32: + val = js_int32(e->u.i32); + break; + case JS_DEF_PROP_INT64: + val = js_int64(e->u.i64); + break; + case JS_DEF_PROP_DOUBLE: + val = js_float64(e->u.f64); + break; + case JS_DEF_OBJECT: + val = JS_NewObject(ctx); + JS_SetPropertyFunctionList(ctx, val, e->u.prop_list.tab, e->u.prop_list.len); + break; + default: + abort(); + } + if (JS_SetModuleExport(ctx, m, e->name, val)) + return -1; + } + return 0; +} + +/* Note: 'func_obj' is not necessarily a constructor */ +static void JS_SetConstructor2(JSContext *ctx, + JSValue func_obj, + JSValue proto, + int proto_flags, int ctor_flags) +{ + JS_DefinePropertyValue(ctx, func_obj, JS_ATOM_prototype, + js_dup(proto), proto_flags); + JS_DefinePropertyValue(ctx, proto, JS_ATOM_constructor, + js_dup(func_obj), ctor_flags); + set_cycle_flag(ctx, func_obj); + set_cycle_flag(ctx, proto); +} + +void JS_SetConstructor(JSContext *ctx, JSValue func_obj, + JSValue proto) +{ + JS_SetConstructor2(ctx, func_obj, proto, + 0, JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE); +} + +static void JS_NewGlobalCConstructor2(JSContext *ctx, + JSValue func_obj, + const char *name, + JSValue proto) +{ + JS_DefinePropertyValueStr(ctx, ctx->global_obj, name, + js_dup(func_obj), + JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE); + JS_SetConstructor(ctx, func_obj, proto); + JS_FreeValue(ctx, func_obj); +} + +static JSValue JS_NewGlobalCConstructor(JSContext *ctx, const char *name, + JSCFunction *func, int length, + JSValue proto) +{ + JSValue func_obj; + func_obj = JS_NewCFunction2(ctx, func, name, length, JS_CFUNC_constructor_or_func, 0); + JS_NewGlobalCConstructor2(ctx, func_obj, name, proto); + return func_obj; +} + +static JSValue JS_NewGlobalCConstructorOnly(JSContext *ctx, const char *name, + JSCFunction *func, int length, + JSValue proto) +{ + JSValue func_obj; + func_obj = JS_NewCFunction2(ctx, func, name, length, JS_CFUNC_constructor, 0); + JS_NewGlobalCConstructor2(ctx, func_obj, name, proto); + return func_obj; +} + +static JSValue js_global_eval(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + return JS_EvalObject(ctx, ctx->global_obj, argv[0], JS_EVAL_TYPE_INDIRECT, -1); +} + +static JSValue js_global_isNaN(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + double d; + + if (unlikely(JS_ToFloat64(ctx, &d, argv[0]))) + return JS_EXCEPTION; + return js_bool(isnan(d)); +} + +static JSValue js_global_isFinite(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + BOOL res; + double d; + if (unlikely(JS_ToFloat64(ctx, &d, argv[0]))) + return JS_EXCEPTION; + res = isfinite(d); + return js_bool(res); +} + +static JSValue js_microtask_job(JSContext *ctx, + int argc, JSValue *argv) +{ + return JS_Call(ctx, argv[0], ctx->global_obj, 0, NULL); +} + +static JSValue js_global_queueMicrotask(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + if (check_function(ctx, argv[0])) + return JS_EXCEPTION; + if (JS_EnqueueJob(ctx, js_microtask_job, 1, &argv[0])) + return JS_EXCEPTION; + return JS_UNDEFINED; +} + +/* Object class */ + +static JSValue JS_ToObject(JSContext *ctx, JSValue val) +{ + int tag = JS_VALUE_GET_NORM_TAG(val); + JSValue obj; + + switch(tag) { + default: + case JS_TAG_NULL: + case JS_TAG_UNDEFINED: + return JS_ThrowTypeError(ctx, "Cannot convert undefined or null to object"); + case JS_TAG_OBJECT: + case JS_TAG_EXCEPTION: + return js_dup(val); + case JS_TAG_BIG_INT: + obj = JS_NewObjectClass(ctx, JS_CLASS_BIG_INT); + goto set_value; + case JS_TAG_INT: + case JS_TAG_FLOAT64: + obj = JS_NewObjectClass(ctx, JS_CLASS_NUMBER); + goto set_value; + case JS_TAG_STRING: + /* XXX: should call the string constructor */ + { + JSString *p1 = JS_VALUE_GET_STRING(val); + obj = JS_NewObjectClass(ctx, JS_CLASS_STRING); + JS_DefinePropertyValue(ctx, obj, JS_ATOM_length, js_int32(p1->len), 0); + } + goto set_value; + case JS_TAG_BOOL: + obj = JS_NewObjectClass(ctx, JS_CLASS_BOOLEAN); + goto set_value; + case JS_TAG_SYMBOL: + obj = JS_NewObjectClass(ctx, JS_CLASS_SYMBOL); + set_value: + if (!JS_IsException(obj)) + JS_SetObjectData(ctx, obj, js_dup(val)); + return obj; + } +} + +static JSValue JS_ToObjectFree(JSContext *ctx, JSValue val) +{ + JSValue obj = JS_ToObject(ctx, val); + JS_FreeValue(ctx, val); + return obj; +} + +static int js_obj_to_desc(JSContext *ctx, JSPropertyDescriptor *d, + JSValue desc) +{ + JSValue val, getter, setter; + int present; + int flags; + + if (!JS_IsObject(desc)) { + JS_ThrowTypeError(ctx, "Property description must be an object"); + return -1; + } + flags = 0; + val = JS_UNDEFINED; + getter = JS_UNDEFINED; + setter = JS_UNDEFINED; + present = JS_HasProperty(ctx, desc, JS_ATOM_enumerable); + if (present < 0) + goto fail; + if (present) { + JSValue prop = JS_GetProperty(ctx, desc, JS_ATOM_enumerable); + if (JS_IsException(prop)) + goto fail; + flags |= JS_PROP_HAS_ENUMERABLE; + if (JS_ToBoolFree(ctx, prop)) + flags |= JS_PROP_ENUMERABLE; + } + present = JS_HasProperty(ctx, desc, JS_ATOM_configurable); + if (present < 0) + goto fail; + if (present) { + JSValue prop = JS_GetProperty(ctx, desc, JS_ATOM_configurable); + if (JS_IsException(prop)) + goto fail; + flags |= JS_PROP_HAS_CONFIGURABLE; + if (JS_ToBoolFree(ctx, prop)) + flags |= JS_PROP_CONFIGURABLE; + } + present = JS_HasProperty(ctx, desc, JS_ATOM_value); + if (present < 0) + goto fail; + if (present) { + flags |= JS_PROP_HAS_VALUE; + val = JS_GetProperty(ctx, desc, JS_ATOM_value); + if (JS_IsException(val)) + goto fail; + } + present = JS_HasProperty(ctx, desc, JS_ATOM_writable); + if (present < 0) + goto fail; + if (present) { + JSValue prop = JS_GetProperty(ctx, desc, JS_ATOM_writable); + if (JS_IsException(prop)) + goto fail; + flags |= JS_PROP_HAS_WRITABLE; + if (JS_ToBoolFree(ctx, prop)) + flags |= JS_PROP_WRITABLE; + } + present = JS_HasProperty(ctx, desc, JS_ATOM_get); + if (present < 0) + goto fail; + if (present) { + flags |= JS_PROP_HAS_GET; + getter = JS_GetProperty(ctx, desc, JS_ATOM_get); + if (JS_IsException(getter) || + !(JS_IsUndefined(getter) || JS_IsFunction(ctx, getter))) { + JS_ThrowTypeError(ctx, "Getter must be a function"); + goto fail; + } + } + present = JS_HasProperty(ctx, desc, JS_ATOM_set); + if (present < 0) + goto fail; + if (present) { + flags |= JS_PROP_HAS_SET; + setter = JS_GetProperty(ctx, desc, JS_ATOM_set); + if (JS_IsException(setter) || + !(JS_IsUndefined(setter) || JS_IsFunction(ctx, setter))) { + JS_ThrowTypeError(ctx, "Setter must be a function"); + goto fail; + } + } + if ((flags & (JS_PROP_HAS_SET | JS_PROP_HAS_GET)) && + (flags & (JS_PROP_HAS_VALUE | JS_PROP_HAS_WRITABLE))) { + JS_ThrowTypeError(ctx, "Invalid property descriptor. Cannot both specify accessors and a value or writable attribute"); + goto fail; + } + d->flags = flags; + d->value = val; + d->getter = getter; + d->setter = setter; + return 0; + fail: + JS_FreeValue(ctx, val); + JS_FreeValue(ctx, getter); + JS_FreeValue(ctx, setter); + return -1; +} + +static __exception int JS_DefinePropertyDesc(JSContext *ctx, JSValue obj, + JSAtom prop, JSValue desc, + int flags) +{ + JSPropertyDescriptor d; + int ret; + + if (js_obj_to_desc(ctx, &d, desc) < 0) + return -1; + + ret = JS_DefineProperty(ctx, obj, prop, + d.value, d.getter, d.setter, d.flags | flags); + js_free_desc(ctx, &d); + return ret; +} + +static __exception int JS_ObjectDefineProperties(JSContext *ctx, + JSValue obj, + JSValue properties) +{ + JSValue props, desc; + JSObject *p; + JSPropertyEnum *atoms; + uint32_t len, i; + int ret = -1; + + if (!JS_IsObject(obj)) { + JS_ThrowTypeError(ctx, "Object.defineProperties called on non-object"); + return -1; + } + desc = JS_UNDEFINED; + props = JS_ToObject(ctx, properties); + if (JS_IsException(props)) + return -1; + p = JS_VALUE_GET_OBJ(props); + if (JS_GetOwnPropertyNamesInternal(ctx, &atoms, &len, p, JS_GPN_ENUM_ONLY | JS_GPN_STRING_MASK | JS_GPN_SYMBOL_MASK) < 0) + goto exception; + // XXX: ECMA specifies that all descriptions should be validated before + // modifying the object. This would require allocating an array + // JSPropertyDescriptor and use 2 separate loops. + for(i = 0; i < len; i++) { + JS_FreeValue(ctx, desc); + desc = JS_GetProperty(ctx, props, atoms[i].atom); + if (JS_IsException(desc)) + goto exception; + if (JS_DefinePropertyDesc(ctx, obj, atoms[i].atom, desc, + JS_PROP_THROW | JS_PROP_DEFINE_PROPERTY) < 0) + goto exception; + } + ret = 0; + +exception: + js_free_prop_enum(ctx, atoms, len); + JS_FreeValue(ctx, props); + JS_FreeValue(ctx, desc); + return ret; +} + +static JSValue js_object_constructor(JSContext *ctx, JSValue new_target, + int argc, JSValue *argv) +{ + JSValue ret; + if (!JS_IsUndefined(new_target) && + JS_VALUE_GET_OBJ(new_target) != + JS_VALUE_GET_OBJ(JS_GetActiveFunction(ctx))) { + ret = js_create_from_ctor(ctx, new_target, JS_CLASS_OBJECT); + } else { + int tag = JS_VALUE_GET_NORM_TAG(argv[0]); + switch(tag) { + case JS_TAG_NULL: + case JS_TAG_UNDEFINED: + ret = JS_NewObject(ctx); + break; + default: + ret = JS_ToObject(ctx, argv[0]); + break; + } + } + return ret; +} + +static JSValue js_object_create(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + JSValue proto, props; + JSValue obj; + + proto = argv[0]; + if (!JS_IsObject(proto) && !JS_IsNull(proto)) + return JS_ThrowTypeError(ctx, "object prototype may only be an Object or null"); + obj = JS_NewObjectProto(ctx, proto); + if (JS_IsException(obj)) + return JS_EXCEPTION; + props = argv[1]; + if (!JS_IsUndefined(props)) { + if (JS_ObjectDefineProperties(ctx, obj, props)) { + JS_FreeValue(ctx, obj); + return JS_EXCEPTION; + } + } + return obj; +} + +static JSValue js_object_getPrototypeOf(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv, int magic) +{ + JSValue val; + + val = argv[0]; + if (JS_VALUE_GET_TAG(val) != JS_TAG_OBJECT) { + /* ES6 feature non compatible with ES5.1: primitive types are + accepted */ + if (magic || JS_VALUE_GET_TAG(val) == JS_TAG_NULL || + JS_VALUE_GET_TAG(val) == JS_TAG_UNDEFINED) + return JS_ThrowTypeErrorNotAnObject(ctx); + } + return JS_GetPrototype(ctx, val); +} + +static JSValue js_object_setPrototypeOf(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + JSValue obj; + obj = argv[0]; + if (JS_SetPrototypeInternal(ctx, obj, argv[1], TRUE) < 0) + return JS_EXCEPTION; + return js_dup(obj); +} + +/* magic = 1 if called as Reflect.defineProperty */ +static JSValue js_object_defineProperty(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv, int magic) +{ + JSValue obj, prop, desc; + int ret, flags; + JSAtom atom; + + obj = argv[0]; + prop = argv[1]; + desc = argv[2]; + + if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT) + return JS_ThrowTypeErrorNotAnObject(ctx); + atom = JS_ValueToAtom(ctx, prop); + if (unlikely(atom == JS_ATOM_NULL)) + return JS_EXCEPTION; + flags = JS_PROP_THROW | JS_PROP_DEFINE_PROPERTY; + if (magic) + flags = JS_PROP_REFLECT_DEFINE_PROPERTY; + ret = JS_DefinePropertyDesc(ctx, obj, atom, desc, flags); + JS_FreeAtom(ctx, atom); + if (ret < 0) { + return JS_EXCEPTION; + } else if (magic) { + return js_bool(ret); + } else { + return js_dup(obj); + } +} + +static JSValue js_object_defineProperties(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + // defineProperties(obj, properties) + JSValue obj = argv[0]; + + if (JS_ObjectDefineProperties(ctx, obj, argv[1])) + return JS_EXCEPTION; + else + return js_dup(obj); +} + +/* magic = 1 if called as __defineSetter__ */ +static JSValue js_object___defineGetter__(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv, int magic) +{ + JSValue obj; + JSValue prop, value, get, set; + int ret, flags; + JSAtom atom; + + prop = argv[0]; + value = argv[1]; + + obj = JS_ToObject(ctx, this_val); + if (JS_IsException(obj)) + return JS_EXCEPTION; + + if (check_function(ctx, value)) { + JS_FreeValue(ctx, obj); + return JS_EXCEPTION; + } + atom = JS_ValueToAtom(ctx, prop); + if (unlikely(atom == JS_ATOM_NULL)) { + JS_FreeValue(ctx, obj); + return JS_EXCEPTION; + } + flags = JS_PROP_THROW | + JS_PROP_HAS_ENUMERABLE | JS_PROP_ENUMERABLE | + JS_PROP_HAS_CONFIGURABLE | JS_PROP_CONFIGURABLE; + if (magic) { + get = JS_UNDEFINED; + set = value; + flags |= JS_PROP_HAS_SET; + } else { + get = value; + set = JS_UNDEFINED; + flags |= JS_PROP_HAS_GET; + } + ret = JS_DefineProperty(ctx, obj, atom, JS_UNDEFINED, get, set, flags); + JS_FreeValue(ctx, obj); + JS_FreeAtom(ctx, atom); + if (ret < 0) { + return JS_EXCEPTION; + } else { + return JS_UNDEFINED; + } +} + +static JSValue js_object_getOwnPropertyDescriptor(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv, int magic) +{ + JSValue prop; + JSAtom atom; + JSValue ret, obj; + JSPropertyDescriptor desc; + int res, flags; + + if (magic) { + /* Reflect.getOwnPropertyDescriptor case */ + if (JS_VALUE_GET_TAG(argv[0]) != JS_TAG_OBJECT) + return JS_ThrowTypeErrorNotAnObject(ctx); + obj = js_dup(argv[0]); + } else { + obj = JS_ToObject(ctx, argv[0]); + if (JS_IsException(obj)) + return obj; + } + prop = argv[1]; + atom = JS_ValueToAtom(ctx, prop); + if (unlikely(atom == JS_ATOM_NULL)) + goto exception; + ret = JS_UNDEFINED; + if (JS_VALUE_GET_TAG(obj) == JS_TAG_OBJECT) { + res = JS_GetOwnPropertyInternal(ctx, &desc, JS_VALUE_GET_OBJ(obj), atom); + if (res < 0) + goto exception; + if (res) { + ret = JS_NewObject(ctx); + if (JS_IsException(ret)) + goto exception1; + flags = JS_PROP_C_W_E | JS_PROP_THROW; + if (desc.flags & JS_PROP_GETSET) { + if (JS_DefinePropertyValue(ctx, ret, JS_ATOM_get, js_dup(desc.getter), flags) < 0 + || JS_DefinePropertyValue(ctx, ret, JS_ATOM_set, js_dup(desc.setter), flags) < 0) + goto exception1; + } else { + if (JS_DefinePropertyValue(ctx, ret, JS_ATOM_value, js_dup(desc.value), flags) < 0 + || JS_DefinePropertyValue(ctx, ret, JS_ATOM_writable, + js_bool(desc.flags & JS_PROP_WRITABLE), + flags) < 0) + goto exception1; + } + if (JS_DefinePropertyValue(ctx, ret, JS_ATOM_enumerable, + js_bool(desc.flags & JS_PROP_ENUMERABLE), + flags) < 0 + || JS_DefinePropertyValue(ctx, ret, JS_ATOM_configurable, + js_bool(desc.flags & JS_PROP_CONFIGURABLE), + flags) < 0) + goto exception1; + js_free_desc(ctx, &desc); + } + } + JS_FreeAtom(ctx, atom); + JS_FreeValue(ctx, obj); + return ret; + +exception1: + js_free_desc(ctx, &desc); + JS_FreeValue(ctx, ret); +exception: + JS_FreeAtom(ctx, atom); + JS_FreeValue(ctx, obj); + return JS_EXCEPTION; +} + +static JSValue js_object_getOwnPropertyDescriptors(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + //getOwnPropertyDescriptors(obj) + JSValue obj, r; + JSObject *p; + JSPropertyEnum *props; + uint32_t len, i; + + r = JS_UNDEFINED; + obj = JS_ToObject(ctx, argv[0]); + if (JS_IsException(obj)) + return JS_EXCEPTION; + + p = JS_VALUE_GET_OBJ(obj); + if (JS_GetOwnPropertyNamesInternal(ctx, &props, &len, p, + JS_GPN_STRING_MASK | JS_GPN_SYMBOL_MASK)) + goto exception; + r = JS_NewObject(ctx); + if (JS_IsException(r)) + goto exception; + for(i = 0; i < len; i++) { + JSValue atomValue, desc; + JSValue args[2]; + + atomValue = JS_AtomToValue(ctx, props[i].atom); + if (JS_IsException(atomValue)) + goto exception; + args[0] = obj; + args[1] = atomValue; + desc = js_object_getOwnPropertyDescriptor(ctx, JS_UNDEFINED, 2, args, 0); + JS_FreeValue(ctx, atomValue); + if (JS_IsException(desc)) + goto exception; + if (!JS_IsUndefined(desc)) { + if (JS_DefinePropertyValue(ctx, r, props[i].atom, desc, + JS_PROP_C_W_E | JS_PROP_THROW) < 0) + goto exception; + } + } + js_free_prop_enum(ctx, props, len); + JS_FreeValue(ctx, obj); + return r; + +exception: + js_free_prop_enum(ctx, props, len); + JS_FreeValue(ctx, obj); + JS_FreeValue(ctx, r); + return JS_EXCEPTION; +} + +static JSValue JS_GetOwnPropertyNames2(JSContext *ctx, JSValue obj1, + int flags, int kind) +{ + JSValue obj, r, val, key, value; + JSObject *p; + JSPropertyEnum *atoms; + uint32_t len, i, j; + + r = JS_UNDEFINED; + val = JS_UNDEFINED; + obj = JS_ToObject(ctx, obj1); + if (JS_IsException(obj)) + return JS_EXCEPTION; + p = JS_VALUE_GET_OBJ(obj); + if (JS_GetOwnPropertyNamesInternal(ctx, &atoms, &len, p, flags & ~JS_GPN_ENUM_ONLY)) + goto exception; + r = JS_NewArray(ctx); + if (JS_IsException(r)) + goto exception; + for(j = i = 0; i < len; i++) { + JSAtom atom = atoms[i].atom; + if (flags & JS_GPN_ENUM_ONLY) { + JSPropertyDescriptor desc; + int res; + + /* Check if property is still enumerable */ + res = JS_GetOwnPropertyInternal(ctx, &desc, p, atom); + if (res < 0) + goto exception; + if (!res) + continue; + js_free_desc(ctx, &desc); + if (!(desc.flags & JS_PROP_ENUMERABLE)) + continue; + } + switch(kind) { + default: + case JS_ITERATOR_KIND_KEY: + val = JS_AtomToValue(ctx, atom); + if (JS_IsException(val)) + goto exception; + break; + case JS_ITERATOR_KIND_VALUE: + val = JS_GetProperty(ctx, obj, atom); + if (JS_IsException(val)) + goto exception; + break; + case JS_ITERATOR_KIND_KEY_AND_VALUE: + val = JS_NewArray(ctx); + if (JS_IsException(val)) + goto exception; + key = JS_AtomToValue(ctx, atom); + if (JS_IsException(key)) + goto exception1; + if (JS_CreateDataPropertyUint32(ctx, val, 0, key, JS_PROP_THROW) < 0) + goto exception1; + value = JS_GetProperty(ctx, obj, atom); + if (JS_IsException(value)) + goto exception1; + if (JS_CreateDataPropertyUint32(ctx, val, 1, value, JS_PROP_THROW) < 0) + goto exception1; + break; + } + if (JS_CreateDataPropertyUint32(ctx, r, j++, val, 0) < 0) + goto exception; + } + goto done; + +exception1: + JS_FreeValue(ctx, val); +exception: + JS_FreeValue(ctx, r); + r = JS_EXCEPTION; +done: + js_free_prop_enum(ctx, atoms, len); + JS_FreeValue(ctx, obj); + return r; +} + +static JSValue js_object_getOwnPropertyNames(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + return JS_GetOwnPropertyNames2(ctx, argv[0], + JS_GPN_STRING_MASK, JS_ITERATOR_KIND_KEY); +} + +static JSValue js_object_getOwnPropertySymbols(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + return JS_GetOwnPropertyNames2(ctx, argv[0], + JS_GPN_SYMBOL_MASK, JS_ITERATOR_KIND_KEY); +} + +static JSValue js_object_groupBy(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + JSValue cb, res, iter, next, groups, k, v, prop; + JSValue args[2]; + int64_t idx; + BOOL done; + + // "is function?" check must be observed before argv[0] is accessed + cb = argv[1]; + if (check_function(ctx, cb)) + return JS_EXCEPTION; + + // TODO(bnoordhuis) add fast path for arrays but as groupBy() is + // defined in terms of iterators, the fast path must check that + // this[Symbol.iterator] is the built-in array iterator + iter = JS_GetIterator(ctx, argv[0], /*is_async*/FALSE); + if (JS_IsException(iter)) + return JS_EXCEPTION; + + k = JS_UNDEFINED; + v = JS_UNDEFINED; + prop = JS_UNDEFINED; + groups = JS_UNDEFINED; + + next = JS_GetProperty(ctx, iter, JS_ATOM_next); + if (JS_IsException(next)) + goto exception; + + groups = JS_NewObjectProto(ctx, JS_NULL); + if (JS_IsException(groups)) + goto exception; + + for (idx = 0; ; idx++) { + v = JS_IteratorNext(ctx, iter, next, 0, NULL, &done); + if (JS_IsException(v)) + goto exception; + if (done) + break; // v is JS_UNDEFINED + + args[0] = v; + args[1] = js_int64(idx); + k = JS_Call(ctx, cb, ctx->global_obj, 2, args); + if (JS_IsException(k)) + goto exception; + + k = js_dup(k); + prop = JS_GetPropertyValue(ctx, groups, k); + if (JS_IsException(prop)) + goto exception; + + if (JS_IsUndefined(prop)) { + prop = JS_NewArray(ctx); + if (JS_IsException(prop)) + goto exception; + k = js_dup(k); + prop = js_dup(prop); + if (JS_SetPropertyValue(ctx, groups, k, prop, + JS_PROP_C_W_E|JS_PROP_THROW) < 0) { + goto exception; + } + } + + res = js_array_push(ctx, prop, 1, &v, /*unshift*/0); + if (JS_IsException(res)) + goto exception; + // res is an int64 + + JS_FreeValue(ctx, prop); + JS_FreeValue(ctx, k); + JS_FreeValue(ctx, v); + prop = JS_UNDEFINED; + k = JS_UNDEFINED; + v = JS_UNDEFINED; + } + + JS_FreeValue(ctx, iter); + JS_FreeValue(ctx, next); + return groups; + +exception: + JS_FreeValue(ctx, prop); + JS_FreeValue(ctx, k); + JS_FreeValue(ctx, v); + JS_FreeValue(ctx, groups); + JS_FreeValue(ctx, iter); + JS_FreeValue(ctx, next); + return JS_EXCEPTION; +} + +static JSValue js_object_keys(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv, int kind) +{ + return JS_GetOwnPropertyNames2(ctx, argv[0], + JS_GPN_ENUM_ONLY | JS_GPN_STRING_MASK, kind); +} + +static JSValue js_object_isExtensible(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv, int reflect) +{ + JSValue obj; + int ret; + + obj = argv[0]; + if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT) { + if (reflect) + return JS_ThrowTypeErrorNotAnObject(ctx); + else + return JS_FALSE; + } + ret = JS_IsExtensible(ctx, obj); + if (ret < 0) + return JS_EXCEPTION; + else + return js_bool(ret); +} + +static JSValue js_object_preventExtensions(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv, int reflect) +{ + JSValue obj; + int ret; + + obj = argv[0]; + if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT) { + if (reflect) + return JS_ThrowTypeErrorNotAnObject(ctx); + else + return js_dup(obj); + } + ret = JS_PreventExtensions(ctx, obj); + if (ret < 0) + return JS_EXCEPTION; + if (reflect) { + return js_bool(ret); + } else { + if (!ret) + return JS_ThrowTypeError(ctx, "proxy preventExtensions handler returned false"); + return js_dup(obj); + } +} + +static JSValue js_object_hasOwnProperty(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + JSValue obj; + JSAtom atom; + JSObject *p; + BOOL ret; + + atom = JS_ValueToAtom(ctx, argv[0]); /* must be done first */ + if (unlikely(atom == JS_ATOM_NULL)) + return JS_EXCEPTION; + obj = JS_ToObject(ctx, this_val); + if (JS_IsException(obj)) { + JS_FreeAtom(ctx, atom); + return obj; + } + p = JS_VALUE_GET_OBJ(obj); + ret = JS_GetOwnPropertyInternal(ctx, NULL, p, atom); + JS_FreeAtom(ctx, atom); + JS_FreeValue(ctx, obj); + if (ret < 0) + return JS_EXCEPTION; + else + return js_bool(ret); +} + +static JSValue js_object_hasOwn(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + JSValue obj; + JSAtom atom; + JSObject *p; + BOOL ret; + + obj = JS_ToObject(ctx, argv[0]); + if (JS_IsException(obj)) + return obj; + atom = JS_ValueToAtom(ctx, argv[1]); + if (unlikely(atom == JS_ATOM_NULL)) { + JS_FreeValue(ctx, obj); + return JS_EXCEPTION; + } + p = JS_VALUE_GET_OBJ(obj); + ret = JS_GetOwnPropertyInternal(ctx, NULL, p, atom); + JS_FreeAtom(ctx, atom); + JS_FreeValue(ctx, obj); + if (ret < 0) + return JS_EXCEPTION; + else + return js_bool(ret); +} + +static JSValue js_object_valueOf(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + return JS_ToObject(ctx, this_val); +} + +static JSValue js_object_toString(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + JSValue obj, tag; + int is_array; + JSAtom atom; + JSObject *p; + + if (JS_IsNull(this_val)) { + tag = js_new_string8(ctx, "Null"); + } else if (JS_IsUndefined(this_val)) { + tag = js_new_string8(ctx, "Undefined"); + } else { + obj = JS_ToObject(ctx, this_val); + if (JS_IsException(obj)) + return obj; + is_array = JS_IsArray(ctx, obj); + if (is_array < 0) { + JS_FreeValue(ctx, obj); + return JS_EXCEPTION; + } + if (is_array) { + atom = JS_ATOM_Array; + } else if (JS_IsFunction(ctx, obj)) { + atom = JS_ATOM_Function; + } else { + p = JS_VALUE_GET_OBJ(obj); + switch(p->class_id) { + case JS_CLASS_STRING: + case JS_CLASS_ARGUMENTS: + case JS_CLASS_MAPPED_ARGUMENTS: + case JS_CLASS_ERROR: + case JS_CLASS_BOOLEAN: + case JS_CLASS_NUMBER: + case JS_CLASS_DATE: + case JS_CLASS_REGEXP: + atom = ctx->rt->class_array[p->class_id].class_name; + break; + default: + atom = JS_ATOM_Object; + break; + } + } + tag = JS_GetProperty(ctx, obj, JS_ATOM_Symbol_toStringTag); + JS_FreeValue(ctx, obj); + if (JS_IsException(tag)) + return JS_EXCEPTION; + if (!JS_IsString(tag)) { + JS_FreeValue(ctx, tag); + tag = JS_AtomToString(ctx, atom); + } + } + return JS_ConcatString3(ctx, "[object ", tag, "]"); +} + +static JSValue js_object_toLocaleString(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + return JS_Invoke(ctx, this_val, JS_ATOM_toString, 0, NULL); +} + +static JSValue js_object_assign(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + // Object.assign(obj, source1) + JSValue obj, s; + int i; + + s = JS_UNDEFINED; + obj = JS_ToObject(ctx, argv[0]); + if (JS_IsException(obj)) + goto exception; + for (i = 1; i < argc; i++) { + if (!JS_IsNull(argv[i]) && !JS_IsUndefined(argv[i])) { + s = JS_ToObject(ctx, argv[i]); + if (JS_IsException(s)) + goto exception; + if (JS_CopyDataProperties(ctx, obj, s, JS_UNDEFINED, TRUE)) + goto exception; + JS_FreeValue(ctx, s); + } + } + return obj; +exception: + JS_FreeValue(ctx, obj); + JS_FreeValue(ctx, s); + return JS_EXCEPTION; +} + +static JSValue js_object_seal(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv, int freeze_flag) +{ + JSValue obj = argv[0]; + JSObject *p; + JSPropertyEnum *props; + uint32_t len, i; + int flags, desc_flags, res; + + if (!JS_IsObject(obj)) + return js_dup(obj); + + p = JS_VALUE_GET_OBJ(obj); + if (p->class_id == JS_CLASS_MODULE_NS) { + return JS_ThrowTypeError(ctx, "cannot %s module namespace", + freeze_flag ? "freeze" : "seal"); + } + + res = JS_PreventExtensions(ctx, obj); + if (res < 0) + return JS_EXCEPTION; + if (!res) { + return JS_ThrowTypeError(ctx, "proxy preventExtensions handler returned false"); + } + + flags = JS_GPN_STRING_MASK | JS_GPN_SYMBOL_MASK; + if (JS_GetOwnPropertyNamesInternal(ctx, &props, &len, p, flags)) + return JS_EXCEPTION; + + for(i = 0; i < len; i++) { + JSPropertyDescriptor desc; + JSAtom prop = props[i].atom; + + desc_flags = JS_PROP_THROW | JS_PROP_HAS_CONFIGURABLE; + if (freeze_flag) { + res = JS_GetOwnPropertyInternal(ctx, &desc, p, prop); + if (res < 0) + goto exception; + if (res) { + if (desc.flags & JS_PROP_WRITABLE) + desc_flags |= JS_PROP_HAS_WRITABLE; + js_free_desc(ctx, &desc); + } + } + if (JS_DefineProperty(ctx, obj, prop, JS_UNDEFINED, + JS_UNDEFINED, JS_UNDEFINED, desc_flags) < 0) + goto exception; + } + js_free_prop_enum(ctx, props, len); + return js_dup(obj); + + exception: + js_free_prop_enum(ctx, props, len); + return JS_EXCEPTION; +} + +static JSValue js_object_isSealed(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv, int is_frozen) +{ + JSValue obj = argv[0]; + JSObject *p; + JSPropertyEnum *props; + uint32_t len, i; + int flags, res; + + if (!JS_IsObject(obj)) + return JS_TRUE; + + p = JS_VALUE_GET_OBJ(obj); + flags = JS_GPN_STRING_MASK | JS_GPN_SYMBOL_MASK; + if (JS_GetOwnPropertyNamesInternal(ctx, &props, &len, p, flags)) + return JS_EXCEPTION; + + for(i = 0; i < len; i++) { + JSPropertyDescriptor desc; + JSAtom prop = props[i].atom; + + res = JS_GetOwnPropertyInternal(ctx, &desc, p, prop); + if (res < 0) + goto exception; + if (res) { + js_free_desc(ctx, &desc); + if ((desc.flags & JS_PROP_CONFIGURABLE) + || (is_frozen && (desc.flags & JS_PROP_WRITABLE))) { + res = FALSE; + goto done; + } + } + } + res = JS_IsExtensible(ctx, obj); + if (res < 0) + return JS_EXCEPTION; + res ^= 1; +done: + js_free_prop_enum(ctx, props, len); + return js_bool(res); + +exception: + js_free_prop_enum(ctx, props, len); + return JS_EXCEPTION; +} + +static JSValue js_object_fromEntries(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + JSValue obj, iter, next_method = JS_UNDEFINED; + JSValue iterable; + BOOL done; + + /* RequireObjectCoercible() not necessary because it is tested in + JS_GetIterator() by JS_GetProperty() */ + iterable = argv[0]; + + obj = JS_NewObject(ctx); + if (JS_IsException(obj)) + return obj; + + iter = JS_GetIterator(ctx, iterable, FALSE); + if (JS_IsException(iter)) + goto fail; + next_method = JS_GetProperty(ctx, iter, JS_ATOM_next); + if (JS_IsException(next_method)) + goto fail; + + for(;;) { + JSValue key, value, item; + item = JS_IteratorNext(ctx, iter, next_method, 0, NULL, &done); + if (JS_IsException(item)) + goto fail; + if (done) { + JS_FreeValue(ctx, item); + break; + } + + key = JS_UNDEFINED; + value = JS_UNDEFINED; + if (!JS_IsObject(item)) { + JS_ThrowTypeErrorNotAnObject(ctx); + goto fail1; + } + key = JS_GetPropertyUint32(ctx, item, 0); + if (JS_IsException(key)) + goto fail1; + value = JS_GetPropertyUint32(ctx, item, 1); + if (JS_IsException(value)) { + JS_FreeValue(ctx, key); + goto fail1; + } + if (JS_DefinePropertyValueValue(ctx, obj, key, value, + JS_PROP_C_W_E | JS_PROP_THROW) < 0) { + fail1: + JS_FreeValue(ctx, item); + goto fail; + } + JS_FreeValue(ctx, item); + } + JS_FreeValue(ctx, next_method); + JS_FreeValue(ctx, iter); + return obj; + fail: + if (JS_IsObject(iter)) { + /* close the iterator object, preserving pending exception */ + JS_IteratorClose(ctx, iter, TRUE); + } + JS_FreeValue(ctx, next_method); + JS_FreeValue(ctx, iter); + JS_FreeValue(ctx, obj); + return JS_EXCEPTION; +} + +static JSValue js_object_is(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + return js_bool(js_same_value(ctx, argv[0], argv[1])); +} + +static JSValue JS_SpeciesConstructor(JSContext *ctx, JSValue obj, + JSValue defaultConstructor) +{ + JSValue ctor, species; + + if (!JS_IsObject(obj)) + return JS_ThrowTypeErrorNotAnObject(ctx); + ctor = JS_GetProperty(ctx, obj, JS_ATOM_constructor); + if (JS_IsException(ctor)) + return ctor; + if (JS_IsUndefined(ctor)) + return js_dup(defaultConstructor); + if (!JS_IsObject(ctor)) { + JS_FreeValue(ctx, ctor); + return JS_ThrowTypeErrorNotAnObject(ctx); + } + species = JS_GetProperty(ctx, ctor, JS_ATOM_Symbol_species); + JS_FreeValue(ctx, ctor); + if (JS_IsException(species)) + return species; + if (JS_IsUndefined(species) || JS_IsNull(species)) + return js_dup(defaultConstructor); + if (!JS_IsConstructor(ctx, species)) { + JS_FreeValue(ctx, species); + return JS_ThrowTypeError(ctx, "not a constructor"); + } + return species; +} + +static JSValue js_object_get___proto__(JSContext *ctx, JSValue this_val) +{ + JSValue val, ret; + + val = JS_ToObject(ctx, this_val); + if (JS_IsException(val)) + return val; + ret = JS_GetPrototype(ctx, val); + JS_FreeValue(ctx, val); + return ret; +} + +static JSValue js_object_set___proto__(JSContext *ctx, JSValue this_val, + JSValue proto) +{ + if (JS_IsUndefined(this_val) || JS_IsNull(this_val)) + return JS_ThrowTypeErrorNotAnObject(ctx); + if (!JS_IsObject(proto) && !JS_IsNull(proto)) + return JS_UNDEFINED; + if (JS_SetPrototypeInternal(ctx, this_val, proto, TRUE) < 0) + return JS_EXCEPTION; + else + return JS_UNDEFINED; +} + +static JSValue js_object_isPrototypeOf(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + JSValue obj, v1; + JSValue v; + int res; + + v = argv[0]; + if (!JS_IsObject(v)) + return JS_FALSE; + obj = JS_ToObject(ctx, this_val); + if (JS_IsException(obj)) + return JS_EXCEPTION; + v1 = js_dup(v); + for(;;) { + v1 = JS_GetPrototypeFree(ctx, v1); + if (JS_IsException(v1)) + goto exception; + if (JS_IsNull(v1)) { + res = FALSE; + break; + } + if (JS_VALUE_GET_OBJ(obj) == JS_VALUE_GET_OBJ(v1)) { + res = TRUE; + break; + } + /* avoid infinite loop (possible with proxies) */ + if (js_poll_interrupts(ctx)) + goto exception; + } + JS_FreeValue(ctx, v1); + JS_FreeValue(ctx, obj); + return js_bool(res); + +exception: + JS_FreeValue(ctx, v1); + JS_FreeValue(ctx, obj); + return JS_EXCEPTION; +} + +static JSValue js_object_propertyIsEnumerable(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + JSValue obj, res = JS_EXCEPTION; + JSAtom prop = JS_ATOM_NULL; + JSPropertyDescriptor desc; + int has_prop; + + obj = JS_ToObject(ctx, this_val); + if (JS_IsException(obj)) + goto exception; + prop = JS_ValueToAtom(ctx, argv[0]); + if (unlikely(prop == JS_ATOM_NULL)) + goto exception; + + has_prop = JS_GetOwnPropertyInternal(ctx, &desc, JS_VALUE_GET_OBJ(obj), prop); + if (has_prop < 0) + goto exception; + if (has_prop) { + res = js_bool(desc.flags & JS_PROP_ENUMERABLE); + js_free_desc(ctx, &desc); + } else { + res = JS_FALSE; + } + +exception: + JS_FreeAtom(ctx, prop); + JS_FreeValue(ctx, obj); + return res; +} + +static JSValue js_object___lookupGetter__(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv, int setter) +{ + JSValue obj, res = JS_EXCEPTION; + JSAtom prop = JS_ATOM_NULL; + JSPropertyDescriptor desc; + int has_prop; + + obj = JS_ToObject(ctx, this_val); + if (JS_IsException(obj)) + goto exception; + prop = JS_ValueToAtom(ctx, argv[0]); + if (unlikely(prop == JS_ATOM_NULL)) + goto exception; + + for (;;) { + has_prop = JS_GetOwnPropertyInternal(ctx, &desc, JS_VALUE_GET_OBJ(obj), prop); + if (has_prop < 0) + goto exception; + if (has_prop) { + if (desc.flags & JS_PROP_GETSET) + res = js_dup(setter ? desc.setter : desc.getter); + else + res = JS_UNDEFINED; + js_free_desc(ctx, &desc); + break; + } + obj = JS_GetPrototypeFree(ctx, obj); + if (JS_IsException(obj)) + goto exception; + if (JS_IsNull(obj)) { + res = JS_UNDEFINED; + break; + } + /* avoid infinite loop (possible with proxies) */ + if (js_poll_interrupts(ctx)) + goto exception; + } + +exception: + JS_FreeAtom(ctx, prop); + JS_FreeValue(ctx, obj); + return res; +} + +static const JSCFunctionListEntry js_object_funcs[] = { + JS_CFUNC_DEF("create", 2, js_object_create ), + JS_CFUNC_MAGIC_DEF("getPrototypeOf", 1, js_object_getPrototypeOf, 0 ), + JS_CFUNC_DEF("setPrototypeOf", 2, js_object_setPrototypeOf ), + JS_CFUNC_MAGIC_DEF("defineProperty", 3, js_object_defineProperty, 0 ), + JS_CFUNC_DEF("defineProperties", 2, js_object_defineProperties ), + JS_CFUNC_DEF("getOwnPropertyNames", 1, js_object_getOwnPropertyNames ), + JS_CFUNC_DEF("getOwnPropertySymbols", 1, js_object_getOwnPropertySymbols ), + JS_CFUNC_DEF("groupBy", 2, js_object_groupBy ), + JS_CFUNC_MAGIC_DEF("keys", 1, js_object_keys, JS_ITERATOR_KIND_KEY ), + JS_CFUNC_MAGIC_DEF("values", 1, js_object_keys, JS_ITERATOR_KIND_VALUE ), + JS_CFUNC_MAGIC_DEF("entries", 1, js_object_keys, JS_ITERATOR_KIND_KEY_AND_VALUE ), + JS_CFUNC_MAGIC_DEF("isExtensible", 1, js_object_isExtensible, 0 ), + JS_CFUNC_MAGIC_DEF("preventExtensions", 1, js_object_preventExtensions, 0 ), + JS_CFUNC_MAGIC_DEF("getOwnPropertyDescriptor", 2, js_object_getOwnPropertyDescriptor, 0 ), + JS_CFUNC_DEF("getOwnPropertyDescriptors", 1, js_object_getOwnPropertyDescriptors ), + JS_CFUNC_DEF("is", 2, js_object_is ), + JS_CFUNC_DEF("assign", 2, js_object_assign ), + JS_CFUNC_MAGIC_DEF("seal", 1, js_object_seal, 0 ), + JS_CFUNC_MAGIC_DEF("freeze", 1, js_object_seal, 1 ), + JS_CFUNC_MAGIC_DEF("isSealed", 1, js_object_isSealed, 0 ), + JS_CFUNC_MAGIC_DEF("isFrozen", 1, js_object_isSealed, 1 ), + JS_CFUNC_DEF("fromEntries", 1, js_object_fromEntries ), + JS_CFUNC_DEF("hasOwn", 2, js_object_hasOwn ), +}; + +static const JSCFunctionListEntry js_object_proto_funcs[] = { + JS_CFUNC_DEF("toString", 0, js_object_toString ), + JS_CFUNC_DEF("toLocaleString", 0, js_object_toLocaleString ), + JS_CFUNC_DEF("valueOf", 0, js_object_valueOf ), + JS_CFUNC_DEF("hasOwnProperty", 1, js_object_hasOwnProperty ), + JS_CFUNC_DEF("isPrototypeOf", 1, js_object_isPrototypeOf ), + JS_CFUNC_DEF("propertyIsEnumerable", 1, js_object_propertyIsEnumerable ), + JS_CGETSET_DEF("__proto__", js_object_get___proto__, js_object_set___proto__ ), + JS_CFUNC_MAGIC_DEF("__defineGetter__", 2, js_object___defineGetter__, 0 ), + JS_CFUNC_MAGIC_DEF("__defineSetter__", 2, js_object___defineGetter__, 1 ), + JS_CFUNC_MAGIC_DEF("__lookupGetter__", 1, js_object___lookupGetter__, 0 ), + JS_CFUNC_MAGIC_DEF("__lookupSetter__", 1, js_object___lookupGetter__, 1 ), +}; + +/* Function class */ + +static JSValue js_function_proto(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + return JS_UNDEFINED; +} + +/* XXX: add a specific eval mode so that Function("}), ({") is rejected */ +static JSValue js_function_constructor(JSContext *ctx, JSValue new_target, + int argc, JSValue *argv, int magic) +{ + JSFunctionKindEnum func_kind = magic; + int i, n, ret; + JSValue s, proto, obj = JS_UNDEFINED; + StringBuffer b_s, *b = &b_s; + + string_buffer_init(ctx, b, 0); + string_buffer_putc8(b, '('); + + if (func_kind == JS_FUNC_ASYNC || func_kind == JS_FUNC_ASYNC_GENERATOR) { + string_buffer_puts8(b, "async "); + } + string_buffer_puts8(b, "function"); + + if (func_kind == JS_FUNC_GENERATOR || func_kind == JS_FUNC_ASYNC_GENERATOR) { + string_buffer_putc8(b, '*'); + } + string_buffer_puts8(b, " anonymous("); + + n = argc - 1; + for(i = 0; i < n; i++) { + if (i != 0) { + string_buffer_putc8(b, ','); + } + if (string_buffer_concat_value(b, argv[i])) + goto fail; + } + string_buffer_puts8(b, "\n) {\n"); + if (n >= 0) { + if (string_buffer_concat_value(b, argv[n])) + goto fail; + } + string_buffer_puts8(b, "\n})"); + s = string_buffer_end(b); + if (JS_IsException(s)) + goto fail1; + + obj = JS_EvalObject(ctx, ctx->global_obj, s, JS_EVAL_TYPE_INDIRECT, -1); + JS_FreeValue(ctx, s); + if (JS_IsException(obj)) + goto fail1; + if (!JS_IsUndefined(new_target)) { + /* set the prototype */ + proto = JS_GetProperty(ctx, new_target, JS_ATOM_prototype); + if (JS_IsException(proto)) + goto fail1; + if (!JS_IsObject(proto)) { + JSContext *realm; + JS_FreeValue(ctx, proto); + realm = JS_GetFunctionRealm(ctx, new_target); + if (!realm) + goto fail1; + proto = js_dup(realm->class_proto[func_kind_to_class_id[func_kind]]); + } + ret = JS_SetPrototypeInternal(ctx, obj, proto, TRUE); + JS_FreeValue(ctx, proto); + if (ret < 0) + goto fail1; + } + return obj; + + fail: + string_buffer_free(b); + fail1: + JS_FreeValue(ctx, obj); + return JS_EXCEPTION; +} + +static __exception int js_get_length32(JSContext *ctx, uint32_t *pres, + JSValue obj) +{ + JSValue len_val; + len_val = JS_GetProperty(ctx, obj, JS_ATOM_length); + if (JS_IsException(len_val)) { + *pres = 0; + return -1; + } + return JS_ToUint32Free(ctx, pres, len_val); +} + +static __exception int js_get_length64(JSContext *ctx, int64_t *pres, + JSValue obj) +{ + JSValue len_val; + len_val = JS_GetProperty(ctx, obj, JS_ATOM_length); + if (JS_IsException(len_val)) { + *pres = 0; + return -1; + } + return JS_ToLengthFree(ctx, pres, len_val); +} + +static __exception int js_set_length64(JSContext *ctx, JSValue obj, int64_t len) +{ + return JS_SetProperty(ctx, obj, JS_ATOM_length, js_int64(len)); +} + +static void free_arg_list(JSContext *ctx, JSValue *tab, uint32_t len) +{ + uint32_t i; + for(i = 0; i < len; i++) { + JS_FreeValue(ctx, tab[i]); + } + js_free(ctx, tab); +} + +/* XXX: should use ValueArray */ +static JSValue *build_arg_list(JSContext *ctx, uint32_t *plen, + JSValue array_arg) +{ + uint32_t len, i; + JSValue *tab, ret; + JSObject *p; + + if (JS_VALUE_GET_TAG(array_arg) != JS_TAG_OBJECT) { + JS_ThrowTypeError(ctx, "not a object"); + return NULL; + } + if (js_get_length32(ctx, &len, array_arg)) + return NULL; + if (len > JS_MAX_LOCAL_VARS) { + // XXX: check for stack overflow? + JS_ThrowRangeError(ctx, "too many arguments in function call (only %d allowed)", + JS_MAX_LOCAL_VARS); + return NULL; + } + /* avoid allocating 0 bytes */ + tab = js_mallocz(ctx, sizeof(tab[0]) * max_uint32(1, len)); + if (!tab) + return NULL; + p = JS_VALUE_GET_OBJ(array_arg); + if ((p->class_id == JS_CLASS_ARRAY || p->class_id == JS_CLASS_ARGUMENTS) && + p->fast_array && + len == p->u.array.count) { + for(i = 0; i < len; i++) { + tab[i] = js_dup(p->u.array.u.values[i]); + } + } else { + for(i = 0; i < len; i++) { + ret = JS_GetPropertyUint32(ctx, array_arg, i); + if (JS_IsException(ret)) { + free_arg_list(ctx, tab, i); + return NULL; + } + tab[i] = ret; + } + } + *plen = len; + return tab; +} + +/* magic value: 0 = normal apply, 1 = apply for constructor, 2 = + Reflect.apply */ +static JSValue js_function_apply(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv, int magic) +{ + JSValue this_arg, array_arg; + uint32_t len; + JSValue *tab, ret; + + if (check_function(ctx, this_val)) + return JS_EXCEPTION; + this_arg = argv[0]; + array_arg = argv[1]; + if ((JS_VALUE_GET_TAG(array_arg) == JS_TAG_UNDEFINED || + JS_VALUE_GET_TAG(array_arg) == JS_TAG_NULL) && magic != 2) { + return JS_Call(ctx, this_val, this_arg, 0, NULL); + } + tab = build_arg_list(ctx, &len, array_arg); + if (!tab) + return JS_EXCEPTION; + if (magic & 1) { + ret = JS_CallConstructor2(ctx, this_val, this_arg, len, tab); + } else { + ret = JS_Call(ctx, this_val, this_arg, len, tab); + } + free_arg_list(ctx, tab, len); + return ret; +} + +static JSValue js_function_call(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + if (argc <= 0) { + return JS_Call(ctx, this_val, JS_UNDEFINED, 0, NULL); + } else { + return JS_Call(ctx, this_val, argv[0], argc - 1, argv + 1); + } +} + +static JSValue js_function_bind(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + JSBoundFunction *bf; + JSValue func_obj, name1, len_val; + JSObject *p; + int arg_count, i, ret; + + if (check_function(ctx, this_val)) + return JS_EXCEPTION; + + func_obj = JS_NewObjectProtoClass(ctx, ctx->function_proto, + JS_CLASS_BOUND_FUNCTION); + if (JS_IsException(func_obj)) + return JS_EXCEPTION; + p = JS_VALUE_GET_OBJ(func_obj); + p->is_constructor = JS_IsConstructor(ctx, this_val); + arg_count = max_int(0, argc - 1); + bf = js_malloc(ctx, sizeof(*bf) + arg_count * sizeof(JSValue)); + if (!bf) + goto exception; + bf->func_obj = js_dup(this_val); + bf->this_val = js_dup(argv[0]); + bf->argc = arg_count; + for(i = 0; i < arg_count; i++) { + bf->argv[i] = js_dup(argv[i + 1]); + } + p->u.bound_function = bf; + + /* XXX: the spec could be simpler by only using GetOwnProperty */ + ret = JS_GetOwnProperty(ctx, NULL, this_val, JS_ATOM_length); + if (ret < 0) + goto exception; + if (!ret) { + len_val = js_int32(0); + } else { + len_val = JS_GetProperty(ctx, this_val, JS_ATOM_length); + if (JS_IsException(len_val)) + goto exception; + if (JS_VALUE_GET_TAG(len_val) == JS_TAG_INT) { + /* most common case */ + int len1 = JS_VALUE_GET_INT(len_val); + if (len1 <= arg_count) + len1 = 0; + else + len1 -= arg_count; + len_val = js_int32(len1); + } else if (JS_VALUE_GET_NORM_TAG(len_val) == JS_TAG_FLOAT64) { + double d = JS_VALUE_GET_FLOAT64(len_val); + if (isnan(d)) { + d = 0.0; + } else { + d = trunc(d); + if (d <= (double)arg_count) + d = 0.0; + else + d -= (double)arg_count; /* also converts -0 to +0 */ + } + len_val = js_number(d); + } else { + JS_FreeValue(ctx, len_val); + len_val = js_int32(0); + } + } + JS_DefinePropertyValue(ctx, func_obj, JS_ATOM_length, + len_val, JS_PROP_CONFIGURABLE); + + name1 = JS_GetProperty(ctx, this_val, JS_ATOM_name); + if (JS_IsException(name1)) + goto exception; + if (!JS_IsString(name1)) { + JS_FreeValue(ctx, name1); + name1 = JS_AtomToString(ctx, JS_ATOM_empty_string); + } + name1 = JS_ConcatString3(ctx, "bound ", name1, ""); + if (JS_IsException(name1)) + goto exception; + JS_DefinePropertyValue(ctx, func_obj, JS_ATOM_name, name1, + JS_PROP_CONFIGURABLE); + return func_obj; + exception: + JS_FreeValue(ctx, func_obj); + return JS_EXCEPTION; +} + +static JSValue js_function_toString(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + JSObject *p; + JSFunctionKindEnum func_kind = JS_FUNC_NORMAL; + + if (check_function(ctx, this_val)) + return JS_EXCEPTION; + + p = JS_VALUE_GET_OBJ(this_val); + if (js_class_has_bytecode(p->class_id)) { + JSFunctionBytecode *b = p->u.func.function_bytecode; + /* `b->source` must be pure ASCII or UTF-8 encoded */ + if (b->source) + return JS_NewStringLen(ctx, b->source, b->source_len); + } + { + JSValue name; + const char *pref, *suff; + + switch(func_kind) { + default: + case JS_FUNC_NORMAL: + pref = "function "; + break; + case JS_FUNC_GENERATOR: + pref = "function *"; + break; + case JS_FUNC_ASYNC: + pref = "async function "; + break; + case JS_FUNC_ASYNC_GENERATOR: + pref = "async function *"; + break; + } + suff = "() {\n [native code]\n}"; + name = JS_GetProperty(ctx, this_val, JS_ATOM_name); + if (JS_IsUndefined(name)) + name = JS_AtomToString(ctx, JS_ATOM_empty_string); + return JS_ConcatString3(ctx, pref, name, suff); + } +} + +static JSValue js_function_hasInstance(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + int ret; + ret = JS_OrdinaryIsInstanceOf(ctx, argv[0], this_val); + if (ret < 0) + return JS_EXCEPTION; + else + return js_bool(ret); +} + +static const JSCFunctionListEntry js_function_proto_funcs[] = { + JS_CFUNC_DEF("call", 1, js_function_call ), + JS_CFUNC_MAGIC_DEF("apply", 2, js_function_apply, 0 ), + JS_CFUNC_DEF("bind", 1, js_function_bind ), + JS_CFUNC_DEF("toString", 0, js_function_toString ), + JS_CFUNC_DEF("[Symbol.hasInstance]", 1, js_function_hasInstance ), + JS_CGETSET_DEF("fileName", js_function_proto_fileName, NULL ), + JS_CGETSET_MAGIC_DEF("lineNumber", js_function_proto_int32, NULL, + offsetof(JSFunctionBytecode, line_num)), + JS_CGETSET_MAGIC_DEF("columnNumber", js_function_proto_int32, NULL, + offsetof(JSFunctionBytecode, col_num)), +}; + +/* Error class */ + +static JSValue iterator_to_array(JSContext *ctx, JSValue items) +{ + JSValue iter, next_method = JS_UNDEFINED; + JSValue v, r = JS_UNDEFINED; + int64_t k; + BOOL done; + + iter = JS_GetIterator(ctx, items, FALSE); + if (JS_IsException(iter)) + goto exception; + next_method = JS_GetProperty(ctx, iter, JS_ATOM_next); + if (JS_IsException(next_method)) + goto exception; + r = JS_NewArray(ctx); + if (JS_IsException(r)) + goto exception; + for (k = 0;; k++) { + v = JS_IteratorNext(ctx, iter, next_method, 0, NULL, &done); + if (JS_IsException(v)) + goto exception_close; + if (done) + break; + if (JS_DefinePropertyValueInt64(ctx, r, k, v, + JS_PROP_C_W_E | JS_PROP_THROW) < 0) + goto exception_close; + } + done: + JS_FreeValue(ctx, next_method); + JS_FreeValue(ctx, iter); + return r; + exception_close: + JS_IteratorClose(ctx, iter, TRUE); + exception: + JS_FreeValue(ctx, r); + r = JS_EXCEPTION; + goto done; +} + +static JSValue js_error_constructor(JSContext *ctx, JSValue new_target, + int argc, JSValue *argv, int magic) +{ + JSValue obj, msg, proto, cause; + JSValue message; + int opts; + BOOL present; + + if (JS_IsUndefined(new_target)) + new_target = JS_GetActiveFunction(ctx); + proto = JS_GetProperty(ctx, new_target, JS_ATOM_prototype); + if (JS_IsException(proto)) + return proto; + if (!JS_IsObject(proto)) { + JSContext *realm; + JSValue proto1; + + JS_FreeValue(ctx, proto); + realm = JS_GetFunctionRealm(ctx, new_target); + if (!realm) + return JS_EXCEPTION; + if (magic < 0) { + proto1 = realm->class_proto[JS_CLASS_ERROR]; + } else { + proto1 = realm->native_error_proto[magic]; + } + proto = js_dup(proto1); + } + obj = JS_NewObjectProtoClass(ctx, proto, JS_CLASS_ERROR); + JS_FreeValue(ctx, proto); + if (JS_IsException(obj)) + return obj; + if (magic == JS_AGGREGATE_ERROR) { + message = argv[1]; + opts = 2; + } else { + message = argv[0]; + opts = 1; + } + + if (!JS_IsUndefined(message)) { + msg = JS_ToString(ctx, message); + if (unlikely(JS_IsException(msg))) + goto exception; + JS_DefinePropertyValue(ctx, obj, JS_ATOM_message, msg, + JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE); + } + + if (argc > opts && JS_VALUE_GET_TAG(argv[opts]) == JS_TAG_OBJECT) { + present = JS_HasProperty(ctx, argv[opts], JS_ATOM_cause); + if (unlikely(present < 0)) + goto exception; + if (present) { + cause = JS_GetProperty(ctx, argv[opts], JS_ATOM_cause); + if (unlikely(JS_IsException(cause))) + goto exception; + JS_DefinePropertyValue(ctx, obj, JS_ATOM_cause, cause, + JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE); + } + } + + if (magic == JS_AGGREGATE_ERROR) { + JSValue error_list = iterator_to_array(ctx, argv[0]); + if (JS_IsException(error_list)) + goto exception; + JS_DefinePropertyValue(ctx, obj, JS_ATOM_errors, error_list, + JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE); + } + + /* skip the Error() function in the backtrace */ + build_backtrace(ctx, obj, NULL, 0, 0, JS_BACKTRACE_FLAG_SKIP_FIRST_LEVEL); + return obj; + exception: + JS_FreeValue(ctx, obj); + return JS_EXCEPTION; +} + +static JSValue js_error_toString(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + JSValue name, msg; + + if (!JS_IsObject(this_val)) + return JS_ThrowTypeErrorNotAnObject(ctx); + name = JS_GetProperty(ctx, this_val, JS_ATOM_name); + if (JS_IsUndefined(name)) + name = JS_AtomToString(ctx, JS_ATOM_Error); + else + name = JS_ToStringFree(ctx, name); + if (JS_IsException(name)) + return JS_EXCEPTION; + + msg = JS_GetProperty(ctx, this_val, JS_ATOM_message); + if (JS_IsUndefined(msg)) + msg = JS_AtomToString(ctx, JS_ATOM_empty_string); + else + msg = JS_ToStringFree(ctx, msg); + if (JS_IsException(msg)) { + JS_FreeValue(ctx, name); + return JS_EXCEPTION; + } + if (!JS_IsEmptyString(name) && !JS_IsEmptyString(msg)) + name = JS_ConcatString3(ctx, "", name, ": "); + return JS_ConcatString(ctx, name, msg); +} + +static const JSCFunctionListEntry js_error_proto_funcs[] = { + JS_CFUNC_DEF("toString", 0, js_error_toString ), + JS_PROP_STRING_DEF("name", "Error", JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE ), + JS_PROP_STRING_DEF("message", "", JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE ), +}; + +static JSValue js_error_isError(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + return js_bool(JS_IsError(ctx, argv[0])); +} + +static JSValue js_error_get_stackTraceLimit(JSContext *ctx, JSValue this_val) +{ + JSValue val; + + val = JS_ToObject(ctx, this_val); + if (JS_IsException(val)) + return val; + JS_FreeValue(ctx, val); + return js_int32(ctx->error_stack_trace_limit); +} + +static JSValue js_error_set_stackTraceLimit(JSContext *ctx, JSValue this_val, JSValue value) +{ + if (JS_IsUndefined(this_val) || JS_IsNull(this_val)) + return JS_ThrowTypeErrorNotAnObject(ctx); + int limit; + if (JS_ToInt32(ctx, &limit, value) < 0) + return JS_EXCEPTION; + ctx->error_stack_trace_limit = limit; + return JS_UNDEFINED; +} + +static JSValue js_error_get_prepareStackTrace(JSContext *ctx, JSValue this_val) +{ + JSValue val; + + val = JS_ToObject(ctx, this_val); + if (JS_IsException(val)) + return val; + JS_FreeValue(ctx, val); + return js_dup(ctx->error_prepare_stack); +} + +static JSValue js_error_set_prepareStackTrace(JSContext *ctx, JSValue this_val, JSValue value) +{ + if (JS_IsUndefined(this_val) || JS_IsNull(this_val)) + return JS_ThrowTypeErrorNotAnObject(ctx); + JS_FreeValue(ctx, ctx->error_prepare_stack); + ctx->error_prepare_stack = js_dup(value); + return JS_UNDEFINED; +} + +static const JSCFunctionListEntry js_error_funcs[] = { + JS_CFUNC_DEF("isError", 1, js_error_isError ), + JS_CGETSET_DEF("stackTraceLimit", js_error_get_stackTraceLimit, js_error_set_stackTraceLimit ), + JS_CGETSET_DEF("prepareStackTrace", js_error_get_prepareStackTrace, js_error_set_prepareStackTrace ), +}; + +/* AggregateError */ + +/* used by C code. */ +static JSValue js_aggregate_error_constructor(JSContext *ctx, + JSValue errors) +{ + JSValue obj; + + obj = JS_NewObjectProtoClass(ctx, + ctx->native_error_proto[JS_AGGREGATE_ERROR], + JS_CLASS_ERROR); + if (JS_IsException(obj)) + return obj; + JS_DefinePropertyValue(ctx, obj, JS_ATOM_errors, js_dup(errors), + JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE); + return obj; +} + +/* Array */ + +static int JS_CopySubArray(JSContext *ctx, + JSValue obj, int64_t to_pos, + int64_t from_pos, int64_t count, int dir) +{ + JSObject *p; + int64_t i, from, to, len; + JSValue val; + int fromPresent; + + p = NULL; + if (JS_VALUE_GET_TAG(obj) == JS_TAG_OBJECT) { + p = JS_VALUE_GET_OBJ(obj); + if (p->class_id != JS_CLASS_ARRAY || !p->fast_array) { + p = NULL; + } + } + + for (i = 0; i < count; ) { + if (dir < 0) { + from = from_pos + count - i - 1; + to = to_pos + count - i - 1; + } else { + from = from_pos + i; + to = to_pos + i; + } + if (p && p->fast_array && + from >= 0 && from < (len = p->u.array.count) && + to >= 0 && to < len) { + int64_t l, j; + /* Fast path for fast arrays. Since we don't look at the + prototype chain, we can optimize only the cases where + all the elements are present in the array. */ + l = count - i; + if (dir < 0) { + l = min_int64(l, from + 1); + l = min_int64(l, to + 1); + for(j = 0; j < l; j++) { + set_value(ctx, &p->u.array.u.values[to - j], + js_dup(p->u.array.u.values[from - j])); + } + } else { + l = min_int64(l, len - from); + l = min_int64(l, len - to); + for(j = 0; j < l; j++) { + set_value(ctx, &p->u.array.u.values[to + j], + js_dup(p->u.array.u.values[from + j])); + } + } + i += l; + } else { + fromPresent = JS_TryGetPropertyInt64(ctx, obj, from, &val); + if (fromPresent < 0) + goto exception; + + if (fromPresent) { + if (JS_SetPropertyInt64(ctx, obj, to, val) < 0) + goto exception; + } else { + if (JS_DeletePropertyInt64(ctx, obj, to, JS_PROP_THROW) < 0) + goto exception; + } + i++; + } + } + return 0; + + exception: + return -1; +} + +static JSValue js_array_constructor(JSContext *ctx, JSValue new_target, + int argc, JSValue *argv) +{ + JSValue obj; + int i; + + obj = js_create_from_ctor(ctx, new_target, JS_CLASS_ARRAY); + if (JS_IsException(obj)) + return obj; + if (argc == 1 && JS_IsNumber(argv[0])) { + uint32_t len; + if (JS_ToArrayLengthFree(ctx, &len, js_dup(argv[0]), TRUE)) + goto fail; + if (JS_SetProperty(ctx, obj, JS_ATOM_length, js_uint32(len)) < 0) + goto fail; + } else { + for(i = 0; i < argc; i++) { + if (JS_SetPropertyUint32(ctx, obj, i, js_dup(argv[i])) < 0) + goto fail; + } + } + return obj; +fail: + JS_FreeValue(ctx, obj); + return JS_EXCEPTION; +} + +static JSValue js_array_from(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + // from(items, mapfn = void 0, this_arg = void 0) + JSValue items = argv[0], mapfn, this_arg; + JSValue args[2]; + JSValue stack[2]; + JSValue iter, r, v, v2, arrayLike; + int64_t k, len; + int done, mapping; + + mapping = FALSE; + mapfn = JS_UNDEFINED; + this_arg = JS_UNDEFINED; + r = JS_UNDEFINED; + arrayLike = JS_UNDEFINED; + stack[0] = JS_UNDEFINED; + stack[1] = JS_UNDEFINED; + + if (argc > 1) { + mapfn = argv[1]; + if (!JS_IsUndefined(mapfn)) { + if (check_function(ctx, mapfn)) + goto exception; + mapping = 1; + if (argc > 2) + this_arg = argv[2]; + } + } + iter = JS_GetProperty(ctx, items, JS_ATOM_Symbol_iterator); + if (JS_IsException(iter)) + goto exception; + if (!JS_IsUndefined(iter)) { + JS_FreeValue(ctx, iter); + if (JS_IsConstructor(ctx, this_val)) + r = JS_CallConstructor(ctx, this_val, 0, NULL); + else + r = JS_NewArray(ctx); + if (JS_IsException(r)) + goto exception; + stack[0] = js_dup(items); + if (js_for_of_start(ctx, &stack[1], FALSE)) + goto exception; + for (k = 0;; k++) { + v = JS_IteratorNext(ctx, stack[0], stack[1], 0, NULL, &done); + if (JS_IsException(v)) + goto exception_close; + if (done) + break; + if (mapping) { + args[0] = v; + args[1] = js_int32(k); + v2 = JS_Call(ctx, mapfn, this_arg, 2, args); + JS_FreeValue(ctx, v); + v = v2; + if (JS_IsException(v)) + goto exception_close; + } + if (JS_DefinePropertyValueInt64(ctx, r, k, v, + JS_PROP_C_W_E | JS_PROP_THROW) < 0) + goto exception_close; + } + } else { + arrayLike = JS_ToObject(ctx, items); + if (JS_IsException(arrayLike)) + goto exception; + if (js_get_length64(ctx, &len, arrayLike) < 0) + goto exception; + v = js_int64(len); + args[0] = v; + if (JS_IsConstructor(ctx, this_val)) { + r = JS_CallConstructor(ctx, this_val, 1, args); + } else { + r = js_array_constructor(ctx, JS_UNDEFINED, 1, args); + } + JS_FreeValue(ctx, v); + if (JS_IsException(r)) + goto exception; + for(k = 0; k < len; k++) { + v = JS_GetPropertyInt64(ctx, arrayLike, k); + if (JS_IsException(v)) + goto exception; + if (mapping) { + args[0] = v; + args[1] = js_int32(k); + v2 = JS_Call(ctx, mapfn, this_arg, 2, args); + JS_FreeValue(ctx, v); + v = v2; + if (JS_IsException(v)) + goto exception; + } + if (JS_DefinePropertyValueInt64(ctx, r, k, v, + JS_PROP_C_W_E | JS_PROP_THROW) < 0) + goto exception; + } + } + if (JS_SetProperty(ctx, r, JS_ATOM_length, js_uint32(k)) < 0) + goto exception; + goto done; + + exception_close: + if (!JS_IsUndefined(stack[0])) + JS_IteratorClose(ctx, stack[0], TRUE); + exception: + JS_FreeValue(ctx, r); + r = JS_EXCEPTION; + done: + JS_FreeValue(ctx, arrayLike); + JS_FreeValue(ctx, stack[0]); + JS_FreeValue(ctx, stack[1]); + return r; +} + +static JSValue js_array_of(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + JSValue obj, args[1]; + int i; + + if (JS_IsConstructor(ctx, this_val)) { + args[0] = js_int32(argc); + obj = JS_CallConstructor(ctx, this_val, 1, args); + } else { + obj = JS_NewArray(ctx); + } + if (JS_IsException(obj)) + return JS_EXCEPTION; + for(i = 0; i < argc; i++) { + if (JS_CreateDataPropertyUint32(ctx, obj, i, js_dup(argv[i]), + JS_PROP_THROW) < 0) { + goto fail; + } + } + if (JS_SetProperty(ctx, obj, JS_ATOM_length, js_uint32(argc)) < 0) { + fail: + JS_FreeValue(ctx, obj); + return JS_EXCEPTION; + } + return obj; +} + +static JSValue js_array_isArray(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + int ret; + ret = JS_IsArray(ctx, argv[0]); + if (ret < 0) + return JS_EXCEPTION; + else + return js_bool(ret); +} + +static JSValue js_get_this(JSContext *ctx, + JSValue this_val) +{ + return js_dup(this_val); +} + +static JSValue JS_ArraySpeciesCreate(JSContext *ctx, JSValue obj, + JSValue len_val) +{ + JSValue ctor, ret, species; + int res; + JSContext *realm; + + res = JS_IsArray(ctx, obj); + if (res < 0) + return JS_EXCEPTION; + if (!res) + return js_array_constructor(ctx, JS_UNDEFINED, 1, &len_val); + ctor = JS_GetProperty(ctx, obj, JS_ATOM_constructor); + if (JS_IsException(ctor)) + return ctor; + if (JS_IsConstructor(ctx, ctor)) { + /* legacy web compatibility */ + realm = JS_GetFunctionRealm(ctx, ctor); + if (!realm) { + JS_FreeValue(ctx, ctor); + return JS_EXCEPTION; + } + if (realm != ctx && + js_same_value(ctx, ctor, realm->array_ctor)) { + JS_FreeValue(ctx, ctor); + ctor = JS_UNDEFINED; + } + } + if (JS_IsObject(ctor)) { + species = JS_GetProperty(ctx, ctor, JS_ATOM_Symbol_species); + JS_FreeValue(ctx, ctor); + if (JS_IsException(species)) + return species; + ctor = species; + if (JS_IsNull(ctor)) + ctor = JS_UNDEFINED; + } + if (JS_IsUndefined(ctor)) { + return js_array_constructor(ctx, JS_UNDEFINED, 1, &len_val); + } else { + ret = JS_CallConstructor(ctx, ctor, 1, &len_val); + JS_FreeValue(ctx, ctor); + return ret; + } +} + +static const JSCFunctionListEntry js_array_funcs[] = { + JS_CFUNC_DEF("isArray", 1, js_array_isArray ), + JS_CFUNC_DEF("from", 1, js_array_from ), + JS_CFUNC_DEF("of", 0, js_array_of ), + JS_CGETSET_DEF("[Symbol.species]", js_get_this, NULL ), +}; + +static int JS_isConcatSpreadable(JSContext *ctx, JSValue obj) +{ + JSValue val; + + if (!JS_IsObject(obj)) + return FALSE; + val = JS_GetProperty(ctx, obj, JS_ATOM_Symbol_isConcatSpreadable); + if (JS_IsException(val)) + return -1; + if (!JS_IsUndefined(val)) + return JS_ToBoolFree(ctx, val); + return JS_IsArray(ctx, obj); +} + +static JSValue js_array_at(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + JSValue obj, ret; + int64_t len, idx; + + ret = JS_EXCEPTION; + obj = JS_ToObject(ctx, this_val); + if (js_get_length64(ctx, &len, obj)) + goto exception; + + if (JS_ToInt64Sat(ctx, &idx, argv[0])) + goto exception; + + if (idx < 0) + idx = len + idx; + + if (idx < 0 || idx >= len) { + ret = JS_UNDEFINED; + } else { + ret = JS_GetPropertyInt64(ctx, obj, idx); + } + + exception: + JS_FreeValue(ctx, obj); + return ret; +} + +static JSValue js_array_with(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + JSValue arr, obj, ret, *arrp, *pval; + JSObject *p; + int64_t i, len, idx; + uint32_t count32; + + ret = JS_EXCEPTION; + arr = JS_UNDEFINED; + obj = JS_ToObject(ctx, this_val); + if (js_get_length64(ctx, &len, obj)) + goto exception; + + if (len > UINT32_MAX) { + JS_ThrowRangeError(ctx, "invalid array length"); + goto exception; + } + + if (JS_ToInt64Sat(ctx, &idx, argv[0])) + goto exception; + + if (idx < 0) + idx = len + idx; + + if (idx < 0 || idx >= len) { + JS_ThrowRangeError(ctx, "invalid array index: %" PRId64, idx); + goto exception; + } + + arr = JS_NewArray(ctx); + if (JS_IsException(arr)) + goto exception; + + p = JS_VALUE_GET_OBJ(arr); + if (expand_fast_array(ctx, p, len) < 0) + goto exception; + p->u.array.count = len; + + i = 0; + pval = p->u.array.u.values; + if (js_get_fast_array(ctx, obj, &arrp, &count32) && count32 == len) { + for (; i < idx; i++, pval++) + *pval = js_dup(arrp[i]); + *pval = js_dup(argv[1]); + for (i++, pval++; i < len; i++, pval++) + *pval = js_dup(arrp[i]); + } else { + for (; i < idx; i++, pval++) + if (-1 == JS_TryGetPropertyInt64(ctx, obj, i, pval)) + goto fill_and_fail; + *pval = js_dup(argv[1]); + for (i++, pval++; i < len; i++, pval++) { + if (-1 == JS_TryGetPropertyInt64(ctx, obj, i, pval)) { + fill_and_fail: + for (; i < len; i++, pval++) + *pval = JS_UNDEFINED; + goto exception; + } + } + } + + if (JS_SetProperty(ctx, arr, JS_ATOM_length, js_int64(len)) < 0) + goto exception; + + ret = arr; + arr = JS_UNDEFINED; + +exception: + JS_FreeValue(ctx, arr); + JS_FreeValue(ctx, obj); + return ret; +} + +static JSValue js_array_concat(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + JSValue obj, arr, val; + JSValue e; + int64_t len, k, n; + int i, res; + + arr = JS_UNDEFINED; + obj = JS_ToObject(ctx, this_val); + if (JS_IsException(obj)) + goto exception; + + arr = JS_ArraySpeciesCreate(ctx, obj, js_int32(0)); + if (JS_IsException(arr)) + goto exception; + n = 0; + for (i = -1; i < argc; i++) { + if (i < 0) + e = obj; + else + e = argv[i]; + + res = JS_isConcatSpreadable(ctx, e); + if (res < 0) + goto exception; + if (res) { + if (js_get_length64(ctx, &len, e)) + goto exception; + if (n + len > MAX_SAFE_INTEGER) { + JS_ThrowTypeError(ctx, "Array loo long"); + goto exception; + } + for (k = 0; k < len; k++, n++) { + res = JS_TryGetPropertyInt64(ctx, e, k, &val); + if (res < 0) + goto exception; + if (res) { + if (JS_DefinePropertyValueInt64(ctx, arr, n, val, + JS_PROP_C_W_E | JS_PROP_THROW) < 0) + goto exception; + } + } + } else { + if (n >= MAX_SAFE_INTEGER) { + JS_ThrowTypeError(ctx, "Array loo long"); + goto exception; + } + if (JS_DefinePropertyValueInt64(ctx, arr, n, js_dup(e), + JS_PROP_C_W_E | JS_PROP_THROW) < 0) + goto exception; + n++; + } + } + if (JS_SetProperty(ctx, arr, JS_ATOM_length, js_int64(n)) < 0) + goto exception; + + JS_FreeValue(ctx, obj); + return arr; + +exception: + JS_FreeValue(ctx, arr); + JS_FreeValue(ctx, obj); + return JS_EXCEPTION; +} + +#define special_every 0 +#define special_some 1 +#define special_forEach 2 +#define special_map 3 +#define special_filter 4 +#define special_TA 8 + +static JSObject *get_typed_array(JSContext *ctx, JSValue this_val) +{ + JSObject *p; + if (JS_VALUE_GET_TAG(this_val) != JS_TAG_OBJECT) + goto fail; + p = JS_VALUE_GET_OBJ(this_val); + if (!(p->class_id >= JS_CLASS_UINT8C_ARRAY && + p->class_id <= JS_CLASS_FLOAT64_ARRAY)) { + fail: + JS_ThrowTypeError(ctx, "not a TypedArray"); + return NULL; + } + return p; +} + +// Be *very* careful if you touch the typed array's memory directly: +// the length is only valid until the next call into JS land because +// JS code can detach or resize the backing array buffer. Functions +// like JS_GetProperty and JS_ToIndex call JS code. +// +// Exclusively reading or writing elements with JS_GetProperty, +// JS_GetPropertyInt64, JS_SetProperty, etc. is safe because they +// perform bounds checks, as does js_get_fast_array_element. +static int js_typed_array_get_length_unsafe(JSContext *ctx, JSValue obj) +{ + JSObject *p; + p = get_typed_array(ctx, obj); + if (!p) + return -1; + if (typed_array_is_oob(p)) { + JS_ThrowTypeErrorArrayBufferOOB(ctx); + return -1; + } + return p->u.array.count; +} + +static JSValue js_typed_array___speciesCreate(JSContext *ctx, + JSValue this_val, + int argc, JSValue *argv); + +static JSValue js_array_every(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv, int special) +{ + JSValue obj, val, index_val, res, ret; + JSValue args[3]; + JSValue func, this_arg; + int64_t len, k, n; + int present; + + ret = JS_UNDEFINED; + val = JS_UNDEFINED; + if (special & special_TA) { + obj = js_dup(this_val); + len = js_typed_array_get_length_unsafe(ctx, obj); + if (len < 0) + goto exception; + } else { + obj = JS_ToObject(ctx, this_val); + if (js_get_length64(ctx, &len, obj)) + goto exception; + } + func = argv[0]; + this_arg = JS_UNDEFINED; + if (argc > 1) + this_arg = argv[1]; + + if (check_function(ctx, func)) + goto exception; + + switch (special) { + case special_every: + case special_every | special_TA: + ret = JS_TRUE; + break; + case special_some: + case special_some | special_TA: + ret = JS_FALSE; + break; + case special_map: + /* XXX: JS_ArraySpeciesCreate should take int64_t */ + ret = JS_ArraySpeciesCreate(ctx, obj, js_int64(len)); + if (JS_IsException(ret)) + goto exception; + break; + case special_filter: + ret = JS_ArraySpeciesCreate(ctx, obj, js_int32(0)); + if (JS_IsException(ret)) + goto exception; + break; + case special_map | special_TA: + args[0] = obj; + args[1] = js_int32(len); + ret = js_typed_array___speciesCreate(ctx, JS_UNDEFINED, 2, args); + if (JS_IsException(ret)) + goto exception; + break; + case special_filter | special_TA: + ret = JS_NewArray(ctx); + if (JS_IsException(ret)) + goto exception; + break; + } + n = 0; + + for(k = 0; k < len; k++) { + if (special & special_TA) { + val = JS_GetPropertyInt64(ctx, obj, k); + if (JS_IsException(val)) + goto exception; + present = TRUE; + } else { + present = JS_TryGetPropertyInt64(ctx, obj, k, &val); + if (present < 0) + goto exception; + } + if (present) { + index_val = js_int64(k); + args[0] = val; + args[1] = index_val; + args[2] = obj; + res = JS_Call(ctx, func, this_arg, 3, args); + JS_FreeValue(ctx, index_val); + if (JS_IsException(res)) + goto exception; + switch (special) { + case special_every: + case special_every | special_TA: + if (!JS_ToBoolFree(ctx, res)) { + ret = JS_FALSE; + goto done; + } + break; + case special_some: + case special_some | special_TA: + if (JS_ToBoolFree(ctx, res)) { + ret = JS_TRUE; + goto done; + } + break; + case special_map: + if (JS_DefinePropertyValueInt64(ctx, ret, k, res, + JS_PROP_C_W_E | JS_PROP_THROW) < 0) + goto exception; + break; + case special_map | special_TA: + if (JS_SetPropertyValue(ctx, ret, js_int32(k), res, JS_PROP_THROW) < 0) + goto exception; + break; + case special_filter: + case special_filter | special_TA: + if (JS_ToBoolFree(ctx, res)) { + if (JS_DefinePropertyValueInt64(ctx, ret, n++, js_dup(val), + JS_PROP_C_W_E | JS_PROP_THROW) < 0) + goto exception; + } + break; + default: + JS_FreeValue(ctx, res); + break; + } + JS_FreeValue(ctx, val); + val = JS_UNDEFINED; + } + } +done: + if (special == (special_filter | special_TA)) { + JSValue arr; + args[0] = obj; + args[1] = js_int32(n); + arr = js_typed_array___speciesCreate(ctx, JS_UNDEFINED, 2, args); + if (JS_IsException(arr)) + goto exception; + args[0] = ret; + res = JS_Invoke(ctx, arr, JS_ATOM_set, 1, args); + if (check_exception_free(ctx, res)) + goto exception; + JS_FreeValue(ctx, ret); + ret = arr; + } + JS_FreeValue(ctx, val); + JS_FreeValue(ctx, obj); + return ret; + +exception: + JS_FreeValue(ctx, ret); + JS_FreeValue(ctx, val); + JS_FreeValue(ctx, obj); + return JS_EXCEPTION; +} + +#define special_reduce 0 +#define special_reduceRight 1 + +static JSValue js_array_reduce(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv, int special) +{ + JSValue obj, val, index_val, acc, acc1; + JSValue args[4]; + JSValue func; + int64_t len, k, k1; + int present; + + acc = JS_UNDEFINED; + val = JS_UNDEFINED; + if (special & special_TA) { + obj = js_dup(this_val); + len = js_typed_array_get_length_unsafe(ctx, obj); + if (len < 0) + goto exception; + } else { + obj = JS_ToObject(ctx, this_val); + if (js_get_length64(ctx, &len, obj)) + goto exception; + } + func = argv[0]; + + if (check_function(ctx, func)) + goto exception; + + k = 0; + if (argc > 1) { + acc = js_dup(argv[1]); + } else { + for(;;) { + if (k >= len) { + JS_ThrowTypeError(ctx, "empty array"); + goto exception; + } + k1 = (special & special_reduceRight) ? len - k - 1 : k; + k++; + if (special & special_TA) { + acc = JS_GetPropertyInt64(ctx, obj, k1); + if (JS_IsException(acc)) + goto exception; + break; + } else { + present = JS_TryGetPropertyInt64(ctx, obj, k1, &acc); + if (present < 0) + goto exception; + if (present) + break; + } + } + } + for (; k < len; k++) { + k1 = (special & special_reduceRight) ? len - k - 1 : k; + if (special & special_TA) { + val = JS_GetPropertyInt64(ctx, obj, k1); + if (JS_IsException(val)) + goto exception; + present = TRUE; + } else { + present = JS_TryGetPropertyInt64(ctx, obj, k1, &val); + if (present < 0) + goto exception; + } + if (present) { + index_val = js_int64(k1); + args[0] = acc; + args[1] = val; + args[2] = index_val; + args[3] = obj; + acc1 = JS_Call(ctx, func, JS_UNDEFINED, 4, args); + JS_FreeValue(ctx, index_val); + JS_FreeValue(ctx, val); + val = JS_UNDEFINED; + if (JS_IsException(acc1)) + goto exception; + JS_FreeValue(ctx, acc); + acc = acc1; + } + } + JS_FreeValue(ctx, obj); + return acc; + +exception: + JS_FreeValue(ctx, acc); + JS_FreeValue(ctx, val); + JS_FreeValue(ctx, obj); + return JS_EXCEPTION; +} + +static JSValue js_array_fill(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + JSValue obj; + int64_t len, start, end; + + obj = JS_ToObject(ctx, this_val); + if (js_get_length64(ctx, &len, obj)) + goto exception; + + start = 0; + if (argc > 1 && !JS_IsUndefined(argv[1])) { + if (JS_ToInt64Clamp(ctx, &start, argv[1], 0, len, len)) + goto exception; + } + + end = len; + if (argc > 2 && !JS_IsUndefined(argv[2])) { + if (JS_ToInt64Clamp(ctx, &end, argv[2], 0, len, len)) + goto exception; + } + + /* XXX: should special case fast arrays */ + while (start < end) { + if (JS_SetPropertyInt64(ctx, obj, start, js_dup(argv[0])) < 0) + goto exception; + start++; + } + return obj; + + exception: + JS_FreeValue(ctx, obj); + return JS_EXCEPTION; +} + +static JSValue js_array_includes(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + JSValue obj, val; + int64_t len, n; + JSValue *arrp; + uint32_t count; + int res; + + obj = JS_ToObject(ctx, this_val); + if (js_get_length64(ctx, &len, obj)) + goto exception; + + res = TRUE; + if (len > 0) { + n = 0; + if (argc > 1) { + if (JS_ToInt64Clamp(ctx, &n, argv[1], 0, len, len)) + goto exception; + } + if (js_get_fast_array(ctx, obj, &arrp, &count)) { + for (; n < count; n++) { + if (js_strict_eq2(ctx, js_dup(argv[0]), js_dup(arrp[n]), + JS_EQ_SAME_VALUE_ZERO)) { + goto done; + } + } + } + for (; n < len; n++) { + val = JS_GetPropertyInt64(ctx, obj, n); + if (JS_IsException(val)) + goto exception; + if (js_strict_eq2(ctx, js_dup(argv[0]), val, + JS_EQ_SAME_VALUE_ZERO)) { + goto done; + } + } + } + res = FALSE; + done: + JS_FreeValue(ctx, obj); + return js_bool(res); + + exception: + JS_FreeValue(ctx, obj); + return JS_EXCEPTION; +} + +static JSValue js_array_indexOf(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + JSValue obj, val; + int64_t len, n; + JSValue *arrp; + uint32_t count; + + obj = JS_ToObject(ctx, this_val); + if (js_get_length64(ctx, &len, obj)) + goto exception; + + if (len > 0) { + n = 0; + if (argc > 1) { + if (JS_ToInt64Clamp(ctx, &n, argv[1], 0, len, len)) + goto exception; + } + if (js_get_fast_array(ctx, obj, &arrp, &count)) { + for (; n < count; n++) { + if (js_strict_eq2(ctx, js_dup(argv[0]), js_dup(arrp[n]), + JS_EQ_STRICT)) { + goto done; + } + } + } + for (; n < len; n++) { + int present = JS_TryGetPropertyInt64(ctx, obj, n, &val); + if (present < 0) + goto exception; + if (present) { + if (js_strict_eq2(ctx, js_dup(argv[0]), val, JS_EQ_STRICT)) { + goto done; + } + } + } + } + n = -1; + done: + JS_FreeValue(ctx, obj); + return js_int64(n); + + exception: + JS_FreeValue(ctx, obj); + return JS_EXCEPTION; +} + +static JSValue js_array_lastIndexOf(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + JSValue obj, val; + int64_t len, n; + JSValue *arrp; + uint32_t count; + + obj = JS_ToObject(ctx, this_val); + if (js_get_length64(ctx, &len, obj)) + goto exception; + + if (len > 0) { + n = len - 1; + if (argc > 1) { + if (JS_ToInt64Clamp(ctx, &n, argv[1], -1, len - 1, len)) + goto exception; + } + if (js_get_fast_array(ctx, obj, &arrp, &count) && count == len) { + for (; n >= 0; n--) { + if (js_strict_eq2(ctx, js_dup(argv[0]), js_dup(arrp[n]), + JS_EQ_STRICT)) { + goto done; + } + } + } + for (; n >= 0; n--) { + int present = JS_TryGetPropertyInt64(ctx, obj, n, &val); + if (present < 0) + goto exception; + if (present) { + if (js_strict_eq2(ctx, js_dup(argv[0]), val, JS_EQ_STRICT)) { + goto done; + } + } + } + } + n = -1; + done: + JS_FreeValue(ctx, obj); + return js_int64(n); + + exception: + JS_FreeValue(ctx, obj); + return JS_EXCEPTION; +} + +enum { + ArrayFind, + ArrayFindIndex, + ArrayFindLast, + ArrayFindLastIndex, +}; + +static JSValue js_array_find(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv, int mode) +{ + JSValue func, this_arg; + JSValue args[3]; + JSValue obj, val, index_val, res; + int64_t len, k, end; + int dir; + + index_val = JS_UNDEFINED; + val = JS_UNDEFINED; + obj = JS_ToObject(ctx, this_val); + if (js_get_length64(ctx, &len, obj)) + goto exception; + + func = argv[0]; + if (check_function(ctx, func)) + goto exception; + + this_arg = JS_UNDEFINED; + if (argc > 1) + this_arg = argv[1]; + + k = 0; + dir = 1; + end = len; + if (mode == ArrayFindLast || mode == ArrayFindLastIndex) { + k = len - 1; + dir = -1; + end = -1; + } + + // TODO(bnoordhuis) add fast path for fast arrays + for(; k != end; k += dir) { + index_val = js_int64(k); + val = JS_GetPropertyValue(ctx, obj, index_val); + if (JS_IsException(val)) + goto exception; + args[0] = val; + args[1] = index_val; + args[2] = this_val; + res = JS_Call(ctx, func, this_arg, 3, args); + if (JS_IsException(res)) + goto exception; + if (JS_ToBoolFree(ctx, res)) { + if (mode == ArrayFindIndex || mode == ArrayFindLastIndex) { + JS_FreeValue(ctx, val); + JS_FreeValue(ctx, obj); + return index_val; + } else { + JS_FreeValue(ctx, index_val); + JS_FreeValue(ctx, obj); + return val; + } + } + JS_FreeValue(ctx, val); + JS_FreeValue(ctx, index_val); + } + JS_FreeValue(ctx, obj); + if (mode == ArrayFindIndex || mode == ArrayFindLastIndex) + return js_int32(-1); + else + return JS_UNDEFINED; + +exception: + JS_FreeValue(ctx, index_val); + JS_FreeValue(ctx, val); + JS_FreeValue(ctx, obj); + return JS_EXCEPTION; +} + +static JSValue js_array_toString(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + JSValue obj, method, ret; + + obj = JS_ToObject(ctx, this_val); + if (JS_IsException(obj)) + return JS_EXCEPTION; + method = JS_GetProperty(ctx, obj, JS_ATOM_join); + if (JS_IsException(method)) { + ret = JS_EXCEPTION; + } else + if (!JS_IsFunction(ctx, method)) { + /* Use intrinsic Object.prototype.toString */ + JS_FreeValue(ctx, method); + ret = js_object_toString(ctx, obj, 0, NULL); + } else { + ret = JS_CallFree(ctx, method, obj, 0, NULL); + } + JS_FreeValue(ctx, obj); + return ret; +} + +static JSValue js_array_join(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv, int toLocaleString) +{ + JSValue obj, sep = JS_UNDEFINED, el; + StringBuffer b_s, *b = &b_s; + JSString *p = NULL; + int64_t i, n; + int c; + + obj = JS_ToObject(ctx, this_val); + if (js_get_length64(ctx, &n, obj)) + goto exception; + + c = ','; /* default separator */ + if (!toLocaleString && argc > 0 && !JS_IsUndefined(argv[0])) { + sep = JS_ToString(ctx, argv[0]); + if (JS_IsException(sep)) + goto exception; + p = JS_VALUE_GET_STRING(sep); + if (p->len == 1 && !p->is_wide_char) + c = p->u.str8[0]; + else + c = -1; + } + string_buffer_init(ctx, b, 0); + + for(i = 0; i < n; i++) { + if (i > 0) { + if (c >= 0) { + string_buffer_putc8(b, c); + } else { + string_buffer_concat(b, p, 0, p->len); + } + } + el = JS_GetPropertyUint32(ctx, obj, i); + if (JS_IsException(el)) + goto fail; + if (!JS_IsNull(el) && !JS_IsUndefined(el)) { + if (toLocaleString) { + el = JS_ToLocaleStringFree(ctx, el); + } + if (string_buffer_concat_value_free(b, el)) + goto fail; + } + } + JS_FreeValue(ctx, sep); + JS_FreeValue(ctx, obj); + return string_buffer_end(b); + +fail: + string_buffer_free(b); + JS_FreeValue(ctx, sep); +exception: + JS_FreeValue(ctx, obj); + return JS_EXCEPTION; +} + +static JSValue js_array_pop(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv, int shift) +{ + JSValue obj, res = JS_UNDEFINED; + int64_t len, newLen; + JSValue *arrp; + uint32_t count32; + + obj = JS_ToObject(ctx, this_val); + if (js_get_length64(ctx, &len, obj)) + goto exception; + newLen = 0; + if (len > 0) { + newLen = len - 1; + /* Special case fast arrays */ + if (js_get_fast_array(ctx, obj, &arrp, &count32) && count32 == len) { + JSObject *p = JS_VALUE_GET_OBJ(obj); + if (shift) { + res = arrp[0]; + memmove(arrp, arrp + 1, (count32 - 1) * sizeof(*arrp)); + p->u.array.count--; + } else { + res = arrp[count32 - 1]; + p->u.array.count--; + } + } else { + if (shift) { + res = JS_GetPropertyInt64(ctx, obj, 0); + if (JS_IsException(res)) + goto exception; + if (JS_CopySubArray(ctx, obj, 0, 1, len - 1, +1)) + goto exception; + } else { + res = JS_GetPropertyInt64(ctx, obj, newLen); + if (JS_IsException(res)) + goto exception; + } + if (JS_DeletePropertyInt64(ctx, obj, newLen, JS_PROP_THROW) < 0) + goto exception; + } + } + if (JS_SetProperty(ctx, obj, JS_ATOM_length, js_int64(newLen)) < 0) + goto exception; + + JS_FreeValue(ctx, obj); + return res; + + exception: + JS_FreeValue(ctx, res); + JS_FreeValue(ctx, obj); + return JS_EXCEPTION; +} + +static JSValue js_array_push(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv, int unshift) +{ + JSValue obj; + int i; + int64_t len, from, newLen; + + obj = JS_ToObject(ctx, this_val); + if (js_get_length64(ctx, &len, obj)) + goto exception; + newLen = len + argc; + if (newLen > MAX_SAFE_INTEGER) { + JS_ThrowTypeError(ctx, "Array loo long"); + goto exception; + } + from = len; + if (unshift && argc > 0) { + if (JS_CopySubArray(ctx, obj, argc, 0, len, -1)) + goto exception; + from = 0; + } + for(i = 0; i < argc; i++) { + if (JS_SetPropertyInt64(ctx, obj, from + i, js_dup(argv[i])) < 0) + goto exception; + } + if (JS_SetProperty(ctx, obj, JS_ATOM_length, js_int64(newLen)) < 0) + goto exception; + + JS_FreeValue(ctx, obj); + return js_int64(newLen); + + exception: + JS_FreeValue(ctx, obj); + return JS_EXCEPTION; +} + +static JSValue js_array_reverse(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + JSValue obj, lval, hval; + JSValue *arrp; + int64_t len, l, h; + int l_present, h_present; + uint32_t count32; + + lval = JS_UNDEFINED; + obj = JS_ToObject(ctx, this_val); + if (js_get_length64(ctx, &len, obj)) + goto exception; + + /* Special case fast arrays */ + if (js_get_fast_array(ctx, obj, &arrp, &count32) && count32 == len) { + uint32_t ll, hh; + + if (count32 > 1) { + for (ll = 0, hh = count32 - 1; ll < hh; ll++, hh--) { + lval = arrp[ll]; + arrp[ll] = arrp[hh]; + arrp[hh] = lval; + } + } + return obj; + } + + for (l = 0, h = len - 1; l < h; l++, h--) { + l_present = JS_TryGetPropertyInt64(ctx, obj, l, &lval); + if (l_present < 0) + goto exception; + h_present = JS_TryGetPropertyInt64(ctx, obj, h, &hval); + if (h_present < 0) + goto exception; + if (h_present) { + if (JS_SetPropertyInt64(ctx, obj, l, hval) < 0) + goto exception; + + if (l_present) { + if (JS_SetPropertyInt64(ctx, obj, h, lval) < 0) { + lval = JS_UNDEFINED; + goto exception; + } + lval = JS_UNDEFINED; + } else { + if (JS_DeletePropertyInt64(ctx, obj, h, JS_PROP_THROW) < 0) + goto exception; + } + } else { + if (l_present) { + if (JS_DeletePropertyInt64(ctx, obj, l, JS_PROP_THROW) < 0) + goto exception; + if (JS_SetPropertyInt64(ctx, obj, h, lval) < 0) { + lval = JS_UNDEFINED; + goto exception; + } + lval = JS_UNDEFINED; + } + } + } + return obj; + + exception: + JS_FreeValue(ctx, lval); + JS_FreeValue(ctx, obj); + return JS_EXCEPTION; +} + +// Note: a.toReversed() is a.slice().reverse() with the twist that a.slice() +// leaves holes in sparse arrays intact whereas a.toReversed() replaces them +// with undefined, thus in effect creating a dense array. +// Does not use Array[@@species], always returns a base Array. +static JSValue js_array_toReversed(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + JSValue arr, obj, ret, *arrp, *pval; + JSObject *p; + int64_t i, len; + uint32_t count32; + + ret = JS_EXCEPTION; + arr = JS_UNDEFINED; + obj = JS_ToObject(ctx, this_val); + if (js_get_length64(ctx, &len, obj)) + goto exception; + + if (len > UINT32_MAX) { + JS_ThrowRangeError(ctx, "invalid array length"); + goto exception; + } + + arr = JS_NewArray(ctx); + if (JS_IsException(arr)) + goto exception; + + if (len > 0) { + p = JS_VALUE_GET_OBJ(arr); + if (expand_fast_array(ctx, p, len) < 0) + goto exception; + p->u.array.count = len; + + i = len - 1; + pval = p->u.array.u.values; + if (js_get_fast_array(ctx, obj, &arrp, &count32) && count32 == len) { + for (; i >= 0; i--, pval++) + *pval = js_dup(arrp[i]); + } else { + // Query order is observable; test262 expects descending order. + for (; i >= 0; i--, pval++) { + if (-1 == JS_TryGetPropertyInt64(ctx, obj, i, pval)) { + // Exception; initialize remaining elements. + for (; i >= 0; i--, pval++) + *pval = JS_UNDEFINED; + goto exception; + } + } + } + + if (JS_SetProperty(ctx, arr, JS_ATOM_length, js_int64(len)) < 0) + goto exception; + } + + ret = arr; + arr = JS_UNDEFINED; + +exception: + JS_FreeValue(ctx, arr); + JS_FreeValue(ctx, obj); + return ret; +} + +static JSValue js_array_slice(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv, int splice) +{ + JSValue obj, arr, val, len_val; + int64_t len, start, k, final, n, count, del_count, new_len; + int kPresent; + JSValue *arrp; + uint32_t count32, i, item_count; + + arr = JS_UNDEFINED; + obj = JS_ToObject(ctx, this_val); + if (js_get_length64(ctx, &len, obj)) + goto exception; + + if (JS_ToInt64Clamp(ctx, &start, argv[0], 0, len, len)) + goto exception; + + if (splice) { + if (argc == 0) { + item_count = 0; + del_count = 0; + } else if (argc == 1) { + item_count = 0; + del_count = len - start; + } else { + item_count = argc - 2; + if (JS_ToInt64Clamp(ctx, &del_count, argv[1], 0, len - start, 0)) + goto exception; + } + if (len + item_count - del_count > MAX_SAFE_INTEGER) { + JS_ThrowTypeError(ctx, "Array loo long"); + goto exception; + } + count = del_count; + } else { + item_count = 0; /* avoid warning */ + final = len; + if (!JS_IsUndefined(argv[1])) { + if (JS_ToInt64Clamp(ctx, &final, argv[1], 0, len, len)) + goto exception; + } + count = max_int64(final - start, 0); + } + len_val = js_int64(count); + arr = JS_ArraySpeciesCreate(ctx, obj, len_val); + JS_FreeValue(ctx, len_val); + if (JS_IsException(arr)) + goto exception; + + k = start; + final = start + count; + n = 0; + /* The fast array test on arr ensures that + JS_CreateDataPropertyUint32() won't modify obj in case arr is + an exotic object */ + /* Special case fast arrays */ + if (js_get_fast_array(ctx, obj, &arrp, &count32) && + js_is_fast_array(ctx, arr)) { + /* XXX: should share code with fast array constructor */ + for (; k < final && k < count32; k++, n++) { + if (JS_CreateDataPropertyUint32(ctx, arr, n, js_dup(arrp[k]), JS_PROP_THROW) < 0) + goto exception; + } + } + /* Copy the remaining elements if any (handle case of inherited properties) */ + for (; k < final; k++, n++) { + kPresent = JS_TryGetPropertyInt64(ctx, obj, k, &val); + if (kPresent < 0) + goto exception; + if (kPresent) { + if (JS_CreateDataPropertyUint32(ctx, arr, n, val, JS_PROP_THROW) < 0) + goto exception; + } + } + if (JS_SetProperty(ctx, arr, JS_ATOM_length, js_int64(n)) < 0) + goto exception; + + if (splice) { + new_len = len + item_count - del_count; + if (item_count != del_count) { + if (JS_CopySubArray(ctx, obj, start + item_count, + start + del_count, len - (start + del_count), + item_count <= del_count ? +1 : -1) < 0) + goto exception; + + for (k = len; k-- > new_len; ) { + if (JS_DeletePropertyInt64(ctx, obj, k, JS_PROP_THROW) < 0) + goto exception; + } + } + for (i = 0; i < item_count; i++) { + if (JS_SetPropertyInt64(ctx, obj, start + i, js_dup(argv[i + 2])) < 0) + goto exception; + } + if (JS_SetProperty(ctx, obj, JS_ATOM_length, js_int64(new_len)) < 0) + goto exception; + } + JS_FreeValue(ctx, obj); + return arr; + + exception: + JS_FreeValue(ctx, obj); + JS_FreeValue(ctx, arr); + return JS_EXCEPTION; +} + +static JSValue js_array_toSpliced(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + JSValue arr, obj, ret, *arrp, *pval, *last; + JSObject *p; + int64_t i, j, len, newlen, start, add, del; + uint32_t count32; + + pval = NULL; + last = NULL; + ret = JS_EXCEPTION; + arr = JS_UNDEFINED; + + obj = JS_ToObject(ctx, this_val); + if (js_get_length64(ctx, &len, obj)) + goto exception; + + start = 0; + if (argc > 0) + if (JS_ToInt64Clamp(ctx, &start, argv[0], 0, len, len)) + goto exception; + + del = 0; + if (argc > 0) + del = len - start; + if (argc > 1) + if (JS_ToInt64Clamp(ctx, &del, argv[1], 0, del, 0)) + goto exception; + + add = 0; + if (argc > 2) + add = argc - 2; + + newlen = len + add - del; + if (newlen > UINT32_MAX) { + // Per spec: TypeError if newlen >= 2**53, RangeError below + if (newlen > MAX_SAFE_INTEGER) { + JS_ThrowTypeError(ctx, "invalid array length"); + } else { + JS_ThrowRangeError(ctx, "invalid array length"); + } + goto exception; + } + + arr = JS_NewArray(ctx); + if (JS_IsException(arr)) + goto exception; + + if (newlen <= 0) + goto done; + + p = JS_VALUE_GET_OBJ(arr); + if (expand_fast_array(ctx, p, newlen) < 0) + goto exception; + + p->u.array.count = newlen; + pval = &p->u.array.u.values[0]; + last = &p->u.array.u.values[newlen]; + + if (js_get_fast_array(ctx, obj, &arrp, &count32) && count32 == len) { + for (i = 0; i < start; i++, pval++) + *pval = js_dup(arrp[i]); + for (j = 0; j < add; j++, pval++) + *pval = js_dup(argv[2 + j]); + for (i += del; i < len; i++, pval++) + *pval = js_dup(arrp[i]); + } else { + for (i = 0; i < start; i++, pval++) + if (-1 == JS_TryGetPropertyInt64(ctx, obj, i, pval)) + goto exception; + for (j = 0; j < add; j++, pval++) + *pval = js_dup(argv[2 + j]); + for (i += del; i < len; i++, pval++) + if (-1 == JS_TryGetPropertyInt64(ctx, obj, i, pval)) + goto exception; + } + + assert(pval == last); + + if (JS_SetProperty(ctx, arr, JS_ATOM_length, js_int64(newlen)) < 0) + goto exception; + +done: + ret = arr; + arr = JS_UNDEFINED; + +exception: + while (pval != last) + *pval++ = JS_UNDEFINED; + + JS_FreeValue(ctx, arr); + JS_FreeValue(ctx, obj); + return ret; +} + +static JSValue js_array_copyWithin(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + JSValue obj; + int64_t len, from, to, final, count; + + obj = JS_ToObject(ctx, this_val); + if (js_get_length64(ctx, &len, obj)) + goto exception; + + if (JS_ToInt64Clamp(ctx, &to, argv[0], 0, len, len)) + goto exception; + + if (JS_ToInt64Clamp(ctx, &from, argv[1], 0, len, len)) + goto exception; + + final = len; + if (argc > 2 && !JS_IsUndefined(argv[2])) { + if (JS_ToInt64Clamp(ctx, &final, argv[2], 0, len, len)) + goto exception; + } + + count = min_int64(final - from, len - to); + + if (JS_CopySubArray(ctx, obj, to, from, count, + (from < to && to < from + count) ? -1 : +1)) + goto exception; + + return obj; + + exception: + JS_FreeValue(ctx, obj); + return JS_EXCEPTION; +} + +static int64_t JS_FlattenIntoArray(JSContext *ctx, JSValue target, + JSValue source, int64_t sourceLen, + int64_t targetIndex, int depth, + JSValue mapperFunction, + JSValue thisArg) +{ + JSValue element; + int64_t sourceIndex, elementLen; + int present, is_array; + + if (js_check_stack_overflow(ctx->rt, 0)) { + JS_ThrowStackOverflow(ctx); + return -1; + } + + for (sourceIndex = 0; sourceIndex < sourceLen; sourceIndex++) { + present = JS_TryGetPropertyInt64(ctx, source, sourceIndex, &element); + if (present < 0) + return -1; + if (!present) + continue; + if (!JS_IsUndefined(mapperFunction)) { + JSValue args[3] = { element, js_int64(sourceIndex), source }; + element = JS_Call(ctx, mapperFunction, thisArg, 3, args); + JS_FreeValue(ctx, args[0]); + JS_FreeValue(ctx, args[1]); + if (JS_IsException(element)) + return -1; + } + if (depth > 0) { + is_array = JS_IsArray(ctx, element); + if (is_array < 0) + goto fail; + if (is_array) { + if (js_get_length64(ctx, &elementLen, element) < 0) + goto fail; + targetIndex = JS_FlattenIntoArray(ctx, target, element, + elementLen, targetIndex, + depth - 1, + JS_UNDEFINED, JS_UNDEFINED); + if (targetIndex < 0) + goto fail; + JS_FreeValue(ctx, element); + continue; + } + } + if (targetIndex >= MAX_SAFE_INTEGER) { + JS_ThrowTypeError(ctx, "Array too long"); + goto fail; + } + if (JS_DefinePropertyValueInt64(ctx, target, targetIndex, element, + JS_PROP_C_W_E | JS_PROP_THROW) < 0) + return -1; + targetIndex++; + } + return targetIndex; + +fail: + JS_FreeValue(ctx, element); + return -1; +} + +static JSValue js_array_flatten(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv, int map) +{ + JSValue obj, arr; + JSValue mapperFunction, thisArg; + int64_t sourceLen; + int depthNum; + + arr = JS_UNDEFINED; + obj = JS_ToObject(ctx, this_val); + if (js_get_length64(ctx, &sourceLen, obj)) + goto exception; + + depthNum = 1; + mapperFunction = JS_UNDEFINED; + thisArg = JS_UNDEFINED; + if (map) { + mapperFunction = argv[0]; + if (argc > 1) { + thisArg = argv[1]; + } + if (check_function(ctx, mapperFunction)) + goto exception; + } else { + if (argc > 0 && !JS_IsUndefined(argv[0])) { + if (JS_ToInt32Sat(ctx, &depthNum, argv[0]) < 0) + goto exception; + } + } + arr = JS_ArraySpeciesCreate(ctx, obj, js_int32(0)); + if (JS_IsException(arr)) + goto exception; + if (JS_FlattenIntoArray(ctx, arr, obj, sourceLen, 0, depthNum, + mapperFunction, thisArg) < 0) + goto exception; + JS_FreeValue(ctx, obj); + return arr; + +exception: + JS_FreeValue(ctx, obj); + JS_FreeValue(ctx, arr); + return JS_EXCEPTION; +} + +/* Array sort */ + +typedef struct ValueSlot { + JSValue val; + JSString *str; + int64_t pos; +} ValueSlot; + +struct array_sort_context { + JSContext *ctx; + int exception; + int has_method; + JSValue method; +}; + +static int js_array_cmp_generic(const void *a, const void *b, void *opaque) { + struct array_sort_context *psc = opaque; + JSContext *ctx = psc->ctx; + JSValue argv[2]; + JSValue res; + ValueSlot *ap = (ValueSlot *)(void *)a; + ValueSlot *bp = (ValueSlot *)(void *)b; + int cmp; + + if (psc->exception) + return 0; + + if (psc->has_method) { + /* custom sort function is specified as returning 0 for identical + * objects: avoid method call overhead. + */ + if (!memcmp(&ap->val, &bp->val, sizeof(ap->val))) + goto cmp_same; + argv[0] = ap->val; + argv[1] = bp->val; + res = JS_Call(ctx, psc->method, JS_UNDEFINED, 2, argv); + if (JS_IsException(res)) + goto exception; + if (JS_VALUE_GET_TAG(res) == JS_TAG_INT) { + int val = JS_VALUE_GET_INT(res); + cmp = (val > 0) - (val < 0); + } else { + double val; + if (JS_ToFloat64Free(ctx, &val, res) < 0) + goto exception; + cmp = (val > 0) - (val < 0); + } + } else { + /* Not supposed to bypass ToString even for identical objects as + * tested in test262/test/built-ins/Array/prototype/sort/bug_596_1.js + */ + if (!ap->str) { + JSValue str = JS_ToString(ctx, ap->val); + if (JS_IsException(str)) + goto exception; + ap->str = JS_VALUE_GET_STRING(str); + } + if (!bp->str) { + JSValue str = JS_ToString(ctx, bp->val); + if (JS_IsException(str)) + goto exception; + bp->str = JS_VALUE_GET_STRING(str); + } + cmp = js_string_compare(ctx, ap->str, bp->str); + } + if (cmp != 0) + return cmp; +cmp_same: + /* make sort stable: compare array offsets */ + return (ap->pos > bp->pos) - (ap->pos < bp->pos); + +exception: + psc->exception = 1; + return 0; +} + +static JSValue js_array_sort(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + struct array_sort_context asc = { ctx, 0, 0, argv[0] }; + JSValue obj = JS_UNDEFINED; + ValueSlot *array = NULL; + size_t array_size = 0, pos = 0, n = 0; + int64_t i, len, undefined_count = 0; + int present; + + if (!JS_IsUndefined(asc.method)) { + if (check_function(ctx, asc.method)) + goto exception; + asc.has_method = 1; + } + obj = JS_ToObject(ctx, this_val); + if (js_get_length64(ctx, &len, obj)) + goto exception; + + /* XXX: should special case fast arrays */ + for (i = 0; i < len; i++) { + if (pos >= array_size) { + size_t new_size, slack; + ValueSlot *new_array; + new_size = (array_size + (array_size >> 1) + 31) & ~15; + new_array = js_realloc2(ctx, array, new_size * sizeof(*array), &slack); + if (new_array == NULL) + goto exception; + new_size += slack / sizeof(*new_array); + array = new_array; + array_size = new_size; + } + present = JS_TryGetPropertyInt64(ctx, obj, i, &array[pos].val); + if (present < 0) + goto exception; + if (present == 0) + continue; + if (JS_IsUndefined(array[pos].val)) { + undefined_count++; + continue; + } + array[pos].str = NULL; + array[pos].pos = i; + pos++; + } + rqsort(array, pos, sizeof(*array), js_array_cmp_generic, &asc); + if (asc.exception) + goto exception; + + /* XXX: should special case fast arrays */ + while (n < pos) { + if (array[n].str) + JS_FreeValue(ctx, JS_MKPTR(JS_TAG_STRING, array[n].str)); + if (array[n].pos == n) { + JS_FreeValue(ctx, array[n].val); + } else { + if (JS_SetPropertyInt64(ctx, obj, n, array[n].val) < 0) { + n++; + goto exception; + } + } + n++; + } + js_free(ctx, array); + for (i = n; undefined_count-- > 0; i++) { + if (JS_SetPropertyInt64(ctx, obj, i, JS_UNDEFINED) < 0) + goto fail; + } + for (; i < len; i++) { + if (JS_DeletePropertyInt64(ctx, obj, i, JS_PROP_THROW) < 0) + goto fail; + } + return obj; + +exception: + for (; n < pos; n++) { + JS_FreeValue(ctx, array[n].val); + if (array[n].str) + JS_FreeValue(ctx, JS_MKPTR(JS_TAG_STRING, array[n].str)); + } + js_free(ctx, array); +fail: + JS_FreeValue(ctx, obj); + return JS_EXCEPTION; +} + +// Note: a.toSorted() is a.slice().sort() with the twist that a.slice() +// leaves holes in sparse arrays intact whereas a.toSorted() replaces them +// with undefined, thus in effect creating a dense array. +// Does not use Array[@@species], always returns a base Array. +static JSValue js_array_toSorted(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + JSValue arr, obj, ret, *arrp, *pval; + JSObject *p; + int64_t i, len; + uint32_t count32; + int ok; + + ok = JS_IsUndefined(argv[0]) || JS_IsFunction(ctx, argv[0]); + if (!ok) + return JS_ThrowTypeError(ctx, "not a function"); + + ret = JS_EXCEPTION; + arr = JS_UNDEFINED; + obj = JS_ToObject(ctx, this_val); + if (js_get_length64(ctx, &len, obj)) + goto exception; + + if (len > UINT32_MAX) { + JS_ThrowRangeError(ctx, "invalid array length"); + goto exception; + } + + arr = JS_NewArray(ctx); + if (JS_IsException(arr)) + goto exception; + + if (len > 0) { + p = JS_VALUE_GET_OBJ(arr); + if (expand_fast_array(ctx, p, len) < 0) + goto exception; + p->u.array.count = len; + + i = 0; + pval = p->u.array.u.values; + if (js_get_fast_array(ctx, obj, &arrp, &count32) && count32 == len) { + for (; i < len; i++, pval++) + *pval = js_dup(arrp[i]); + } else { + for (; i < len; i++, pval++) { + if (-1 == JS_TryGetPropertyInt64(ctx, obj, i, pval)) { + for (; i < len; i++, pval++) + *pval = JS_UNDEFINED; + goto exception; + } + } + } + + if (JS_SetProperty(ctx, arr, JS_ATOM_length, js_int64(len)) < 0) + goto exception; + } + + ret = js_array_sort(ctx, arr, argc, argv); + if (JS_IsException(ret)) + goto exception; + JS_FreeValue(ctx, ret); + + ret = arr; + arr = JS_UNDEFINED; + +exception: + JS_FreeValue(ctx, arr); + JS_FreeValue(ctx, obj); + return ret; +} + +typedef struct JSArrayIteratorData { + JSValue obj; + JSIteratorKindEnum kind; + uint32_t idx; +} JSArrayIteratorData; + +static void js_array_iterator_finalizer(JSRuntime *rt, JSValue val) +{ + JSObject *p = JS_VALUE_GET_OBJ(val); + JSArrayIteratorData *it = p->u.array_iterator_data; + if (it) { + JS_FreeValueRT(rt, it->obj); + js_free_rt(rt, it); + } +} + +static void js_array_iterator_mark(JSRuntime *rt, JSValue val, + JS_MarkFunc *mark_func) +{ + JSObject *p = JS_VALUE_GET_OBJ(val); + JSArrayIteratorData *it = p->u.array_iterator_data; + if (it) { + JS_MarkValue(rt, it->obj, mark_func); + } +} + +static JSValue js_create_array(JSContext *ctx, int len, JSValue *tab) +{ + JSValue obj; + int i; + + obj = JS_NewArray(ctx); + if (JS_IsException(obj)) + return JS_EXCEPTION; + for(i = 0; i < len; i++) { + if (JS_CreateDataPropertyUint32(ctx, obj, i, js_dup(tab[i]), 0) < 0) { + JS_FreeValue(ctx, obj); + return JS_EXCEPTION; + } + } + return obj; +} + +static JSValue js_create_array_iterator(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv, int magic) +{ + JSValue enum_obj, arr; + JSArrayIteratorData *it; + JSIteratorKindEnum kind; + int class_id; + + kind = magic & 3; + if (magic & 4) { + /* string iterator case */ + arr = JS_ToStringCheckObject(ctx, this_val); + class_id = JS_CLASS_STRING_ITERATOR; + } else { + arr = JS_ToObject(ctx, this_val); + class_id = JS_CLASS_ARRAY_ITERATOR; + } + if (JS_IsException(arr)) + goto fail; + enum_obj = JS_NewObjectClass(ctx, class_id); + if (JS_IsException(enum_obj)) + goto fail; + it = js_malloc(ctx, sizeof(*it)); + if (!it) + goto fail1; + it->obj = arr; + it->kind = kind; + it->idx = 0; + JS_SetOpaqueInternal(enum_obj, it); + return enum_obj; + fail1: + JS_FreeValue(ctx, enum_obj); + fail: + JS_FreeValue(ctx, arr); + return JS_EXCEPTION; +} + +static JSValue js_array_iterator_next(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv, + BOOL *pdone, int magic) +{ + JSArrayIteratorData *it; + uint32_t len, idx; + JSValue val, obj; + JSObject *p; + + it = JS_GetOpaque2(ctx, this_val, JS_CLASS_ARRAY_ITERATOR); + if (!it) + goto fail1; + if (JS_IsUndefined(it->obj)) + goto done; + p = JS_VALUE_GET_OBJ(it->obj); + if (p->class_id >= JS_CLASS_UINT8C_ARRAY && + p->class_id <= JS_CLASS_FLOAT64_ARRAY) { + if (typed_array_is_oob(p)) { + JS_ThrowTypeErrorArrayBufferOOB(ctx); + goto fail1; + } + len = p->u.array.count; + } else { + if (js_get_length32(ctx, &len, it->obj)) { + fail1: + *pdone = FALSE; + return JS_EXCEPTION; + } + } + idx = it->idx; + if (idx >= len) { + JS_FreeValue(ctx, it->obj); + it->obj = JS_UNDEFINED; + done: + *pdone = TRUE; + return JS_UNDEFINED; + } + it->idx = idx + 1; + *pdone = FALSE; + if (it->kind == JS_ITERATOR_KIND_KEY) { + return js_uint32(idx); + } else { + val = JS_GetPropertyUint32(ctx, it->obj, idx); + if (JS_IsException(val)) + return JS_EXCEPTION; + if (it->kind == JS_ITERATOR_KIND_VALUE) { + return val; + } else { + JSValue args[2]; + JSValue num; + num = js_uint32(idx); + args[0] = num; + args[1] = val; + obj = js_create_array(ctx, 2, args); + JS_FreeValue(ctx, val); + JS_FreeValue(ctx, num); + return obj; + } + } +} + +typedef struct JSIteratorWrapData { + JSValue wrapped_iter; + JSValue wrapped_next; +} JSIteratorWrapData; + +static void js_iterator_wrap_finalizer(JSRuntime *rt, JSValue val) +{ + JSObject *p = JS_VALUE_GET_OBJ(val); + JSIteratorWrapData *it = p->u.iterator_wrap_data; + if (it) { + JS_FreeValueRT(rt, it->wrapped_iter); + JS_FreeValueRT(rt, it->wrapped_next); + js_free_rt(rt, it); + } +} + +static void js_iterator_wrap_mark(JSRuntime *rt, JSValue val, + JS_MarkFunc *mark_func) +{ + JSObject *p = JS_VALUE_GET_OBJ(val); + JSIteratorWrapData *it = p->u.iterator_wrap_data; + if (it) { + JS_MarkValue(rt, it->wrapped_iter, mark_func); + JS_MarkValue(rt, it->wrapped_next, mark_func); + } +} + +static JSValue js_iterator_wrap_next(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv, + BOOL *pdone, int magic) +{ + JSIteratorWrapData *it; + JSValue method, ret; + it = JS_GetOpaque2(ctx, this_val, JS_CLASS_ITERATOR_WRAP); + if (!it) + return JS_EXCEPTION; + if (magic == GEN_MAGIC_NEXT) + return JS_IteratorNext(ctx, it->wrapped_iter, it->wrapped_next, argc, argv, pdone); + method = JS_GetProperty(ctx, it->wrapped_iter, JS_ATOM_return); + if (JS_IsException(method)) + return JS_EXCEPTION; + if (JS_IsNull(method) || JS_IsUndefined(method)) { + *pdone = TRUE; + return JS_UNDEFINED; + } + ret = JS_IteratorNext2(ctx, it->wrapped_iter, method, argc, argv, pdone); + JS_FreeValue(ctx, method); + return ret; +} + +static const JSCFunctionListEntry js_iterator_wrap_proto_funcs[] = { + JS_ITERATOR_NEXT_DEF("next", 0, js_iterator_wrap_next, GEN_MAGIC_NEXT ), + JS_ITERATOR_NEXT_DEF("return", 0, js_iterator_wrap_next, GEN_MAGIC_RETURN ), +}; + +static JSValue js_iterator_constructor(JSContext *ctx, JSValue new_target, + int argc, JSValue *argv) +{ + JSObject *p; + + if (JS_TAG_OBJECT != JS_VALUE_GET_TAG(new_target)) + return JS_ThrowTypeError(ctx, "constructor requires 'new'"); + p = JS_VALUE_GET_OBJ(new_target); + if (p->class_id == JS_CLASS_C_FUNCTION) + if (p->u.cfunc.c_function.generic == js_iterator_constructor) + return JS_ThrowTypeError(ctx, "abstract class not constructable"); + return js_create_from_ctor(ctx, new_target, JS_CLASS_ITERATOR); +} + +static JSValue js_iterator_from(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + JSValue obj, method, iter; + JSIteratorWrapData *it; + int ret; + + obj = argv[0]; + if (JS_IsString(obj)) { + method = JS_GetProperty(ctx, obj, JS_ATOM_Symbol_iterator); + if (JS_IsException(method)) + return JS_EXCEPTION; + return JS_CallFree(ctx, method, obj, 0, NULL); + } + if (!JS_IsObject(obj)) + return JS_ThrowTypeError(ctx, "Iterator.from called on non-object"); + ret = JS_OrdinaryIsInstanceOf(ctx, obj, ctx->iterator_ctor); + if (ret < 0) + return JS_EXCEPTION; + if (ret) + return js_dup(obj); + method = JS_GetProperty(ctx, obj, JS_ATOM_Symbol_iterator); + if (JS_IsException(method)) + return JS_EXCEPTION; + if (JS_IsNull(method) || JS_IsUndefined(method)) { + method = JS_GetProperty(ctx, obj, JS_ATOM_next); + if (JS_IsException(method)) + return JS_EXCEPTION; + iter = JS_NewObjectClass(ctx, JS_CLASS_ITERATOR_WRAP); + if (JS_IsException(iter)) + goto fail; + it = js_malloc(ctx, sizeof(*it)); + if (!it) + goto fail; + it->wrapped_iter = js_dup(obj); + it->wrapped_next = method; + JS_SetOpaqueInternal(iter, it); + } else { + iter = JS_GetIterator2(ctx, obj, method); + JS_FreeValue(ctx, method); + if (JS_IsException(iter)) + return JS_EXCEPTION; + } + return iter; +fail: + JS_FreeValue(ctx, method); + JS_FreeValue(ctx, iter); + return JS_EXCEPTION; +} + +static JSValue js_create_iterator_helper(JSContext *ctx, JSValue iterator, + JSIteratorHelperKindEnum kind, JSValue func, int64_t count); + +static JSValue js_iterator_proto_drop(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + JSValue v; + double dlimit; + int64_t limit; + if (!JS_IsObject(this_val)) + return JS_ThrowTypeError(ctx, "Iterator.prototype.drop called on non-object"); + v = JS_ToNumber(ctx, argv[0]); + if (JS_IsException(v)) + return JS_EXCEPTION; + // Check for Infinity. + if (JS_ToFloat64(ctx, &dlimit, v)) { + JS_FreeValue(ctx, v); + return JS_EXCEPTION; + } + if (isnan(dlimit)) { + JS_FreeValue(ctx, v); + goto fail; + } + if (!isfinite(dlimit)) { + JS_FreeValue(ctx, v); + if (dlimit < 0) + goto fail; + else + limit = MAX_SAFE_INTEGER; + } else { + v = JS_ToIntegerFree(ctx, v); + if (JS_IsException(v)) + return JS_EXCEPTION; + if (JS_ToInt64Free(ctx, &limit, v)) + return JS_EXCEPTION; + } + if (limit < 0) { + fail: + return JS_ThrowRangeError(ctx, "must be positive"); + } + return js_create_iterator_helper(ctx, this_val, JS_ITERATOR_HELPER_KIND_DROP, JS_UNDEFINED, limit); +} + +static JSValue js_iterator_proto_every(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + JSValue item, method, ret, func, index_val, r; + JSValue args[2]; + int64_t idx; + BOOL done; + + if (!JS_IsObject(this_val)) + return JS_ThrowTypeError(ctx, "Iterator.prototype.every called on non-object"); + if (check_function(ctx, argv[0])) + return JS_EXCEPTION; + func = js_dup(argv[0]); + method = JS_GetProperty(ctx, this_val, JS_ATOM_next); + if (JS_IsException(method)) + goto exception; + r = JS_TRUE; + for (idx = 0; /*empty*/; idx++) { + item = JS_IteratorNext(ctx, this_val, method, 0, NULL, &done); + if (JS_IsException(item)) + goto exception; + if (done) + break; + index_val = js_int64(idx); + args[0] = item; + args[1] = index_val; + ret = JS_Call(ctx, func, JS_UNDEFINED, countof(args), args); + JS_FreeValue(ctx, item); + JS_FreeValue(ctx, index_val); + if (JS_IsException(ret)) + goto exception; + if (!JS_ToBoolFree(ctx, ret)) { + if (JS_IteratorClose(ctx, this_val, FALSE) < 0) + r = JS_EXCEPTION; + else + r = JS_FALSE; + break; + } + index_val = JS_UNDEFINED; + ret = JS_UNDEFINED; + item = JS_UNDEFINED; + } + JS_FreeValue(ctx, func); + JS_FreeValue(ctx, method); + return r; +exception: + JS_IteratorClose(ctx, this_val, TRUE); + JS_FreeValue(ctx, func); + JS_FreeValue(ctx, method); + return JS_EXCEPTION; +} + +static JSValue js_iterator_proto_filter(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + JSValue func; + if (!JS_IsObject(this_val)) + return JS_ThrowTypeError(ctx, "Iterator.prototype.filter called on non-object"); + func = argv[0]; + if (check_function(ctx, func)) + return JS_EXCEPTION; + return js_create_iterator_helper(ctx, this_val, JS_ITERATOR_HELPER_KIND_FILTER, func, 0); +} + +static JSValue js_iterator_proto_find(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + JSValue item, method, ret, func, index_val, r; + JSValue args[2]; + int64_t idx; + BOOL done; + + if (!JS_IsObject(this_val)) + return JS_ThrowTypeError(ctx, "Iterator.prototype.filter called on non-object"); + if (check_function(ctx, argv[0])) + return JS_EXCEPTION; + func = js_dup(argv[0]); + method = JS_GetProperty(ctx, this_val, JS_ATOM_next); + if (JS_IsException(method)) + goto exception; + r = JS_UNDEFINED; + for (idx = 0; /*empty*/; idx++) { + item = JS_IteratorNext(ctx, this_val, method, 0, NULL, &done); + if (JS_IsException(item)) + goto exception; + if (done) + break; + index_val = js_int64(idx); + args[0] = item; + args[1] = index_val; + ret = JS_Call(ctx, func, JS_UNDEFINED, countof(args), args); + JS_FreeValue(ctx, index_val); + if (JS_IsException(ret)) { + JS_FreeValue(ctx, item); + goto exception; + } + if (JS_ToBoolFree(ctx, ret)) { + if (JS_IteratorClose(ctx, this_val, FALSE) < 0) { + JS_FreeValue(ctx, item); + r = JS_EXCEPTION; + } else { + r = item; + } + break; + } + index_val = JS_UNDEFINED; + ret = JS_UNDEFINED; + item = JS_UNDEFINED; + } + JS_FreeValue(ctx, func); + JS_FreeValue(ctx, method); + return r; +exception: + JS_IteratorClose(ctx, this_val, TRUE); + JS_FreeValue(ctx, func); + JS_FreeValue(ctx, method); + return JS_EXCEPTION; +} + +static JSValue js_iterator_proto_flatMap(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + JSValue func; + if (!JS_IsObject(this_val)) + return JS_ThrowTypeError(ctx, "Iterator.prototype.flatMap called on non-object"); + func = argv[0]; + if (check_function(ctx, func)) + return JS_EXCEPTION; + return js_create_iterator_helper(ctx, this_val, JS_ITERATOR_HELPER_KIND_FLAT_MAP, func, 0); +} + +static JSValue js_iterator_proto_forEach(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + JSValue item, method, ret, func, index_val; + JSValue args[2]; + int64_t idx; + BOOL done; + + if (!JS_IsObject(this_val)) + return JS_ThrowTypeError(ctx, "Iterator.prototype.forEach called on non-object"); + if (check_function(ctx, argv[0])) + return JS_EXCEPTION; + func = js_dup(argv[0]); + method = JS_GetProperty(ctx, this_val, JS_ATOM_next); + if (JS_IsException(method)) + goto exception; + for (idx = 0; /*empty*/; idx++) { + item = JS_IteratorNext(ctx, this_val, method, 0, NULL, &done); + if (JS_IsException(item)) + goto exception; + if (done) + break; + index_val = js_int64(idx); + args[0] = item; + args[1] = index_val; + ret = JS_Call(ctx, func, JS_UNDEFINED, countof(args), args); + JS_FreeValue(ctx, item); + JS_FreeValue(ctx, index_val); + if (JS_IsException(ret)) + goto exception; + JS_FreeValue(ctx, ret); + index_val = JS_UNDEFINED; + ret = JS_UNDEFINED; + item = JS_UNDEFINED; + } + JS_FreeValue(ctx, func); + JS_FreeValue(ctx, method); + return JS_UNDEFINED; +exception: + JS_IteratorClose(ctx, this_val, TRUE); + JS_FreeValue(ctx, func); + JS_FreeValue(ctx, method); + return JS_EXCEPTION; +} + +static JSValue js_iterator_proto_map(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + JSValue func; + if (!JS_IsObject(this_val)) + return JS_ThrowTypeError(ctx, "Iterator.prototype.map called on non-object"); + func = argv[0]; + if (check_function(ctx, func)) + return JS_EXCEPTION; + return js_create_iterator_helper(ctx, this_val, JS_ITERATOR_HELPER_KIND_MAP, func, 0); +} + +static JSValue js_iterator_proto_reduce(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + JSValue item, method, ret, func, index_val, acc; + JSValue args[3]; + int64_t idx; + BOOL done; + + if (!JS_IsObject(this_val)) + return JS_ThrowTypeError(ctx, "Iterator.prototype.reduce called on non-object"); + if (check_function(ctx, argv[0])) + return JS_EXCEPTION; + acc = JS_UNDEFINED; + func = js_dup(argv[0]); + method = JS_GetProperty(ctx, this_val, JS_ATOM_next); + if (JS_IsException(method)) + goto exception; + if (argc > 1) { + acc = js_dup(argv[1]); + idx = 0; + } else { + acc = JS_IteratorNext(ctx, this_val, method, 0, NULL, &done); + if (JS_IsException(acc)) + goto exception; + if (done) { + JS_ThrowTypeError(ctx, "empty iterator"); + goto exception; + } + idx = 1; + } + for (/* empty */; /*empty*/; idx++) { + item = JS_IteratorNext(ctx, this_val, method, 0, NULL, &done); + if (JS_IsException(item)) + goto exception; + if (done) + break; + index_val = js_int64(idx); + args[0] = acc; + args[1] = item; + args[2] = index_val; + ret = JS_Call(ctx, func, JS_UNDEFINED, countof(args), args); + JS_FreeValue(ctx, item); + JS_FreeValue(ctx, index_val); + if (JS_IsException(ret)) + goto exception; + JS_FreeValue(ctx, acc); + acc = ret; + index_val = JS_UNDEFINED; + ret = JS_UNDEFINED; + item = JS_UNDEFINED; + } + JS_FreeValue(ctx, func); + JS_FreeValue(ctx, method); + return acc; +exception: + JS_IteratorClose(ctx, this_val, TRUE); + JS_FreeValue(ctx, acc); + JS_FreeValue(ctx, func); + JS_FreeValue(ctx, method); + return JS_EXCEPTION; +} + +static JSValue js_iterator_proto_some(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + JSValue item, method, ret, func, index_val, r; + JSValue args[2]; + int64_t idx; + BOOL done; + + if (!JS_IsObject(this_val)) + return JS_ThrowTypeError(ctx, "Iterator.prototype.some called on non-object"); + if (check_function(ctx, argv[0])) + return JS_EXCEPTION; + func = js_dup(argv[0]); + method = JS_GetProperty(ctx, this_val, JS_ATOM_next); + if (JS_IsException(method)) + goto exception; + r = JS_FALSE; + for (idx = 0; /*empty*/; idx++) { + item = JS_IteratorNext(ctx, this_val, method, 0, NULL, &done); + if (JS_IsException(item)) + goto exception; + if (done) + break; + index_val = js_int64(idx); + args[0] = item; + args[1] = index_val; + ret = JS_Call(ctx, func, JS_UNDEFINED, countof(args), args); + JS_FreeValue(ctx, item); + JS_FreeValue(ctx, index_val); + if (JS_IsException(ret)) + goto exception; + if (JS_ToBoolFree(ctx, ret)) { + if (JS_IteratorClose(ctx, this_val, FALSE) < 0) + r = JS_EXCEPTION; + else + r = JS_TRUE; + break; + } + index_val = JS_UNDEFINED; + ret = JS_UNDEFINED; + item = JS_UNDEFINED; + } + JS_FreeValue(ctx, func); + JS_FreeValue(ctx, method); + return r; +exception: + JS_IteratorClose(ctx, this_val, TRUE); + JS_FreeValue(ctx, func); + JS_FreeValue(ctx, method); + return JS_EXCEPTION; +} + +static JSValue js_iterator_proto_take(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + JSValue v; + double dlimit; + int64_t limit; + if (!JS_IsObject(this_val)) + return JS_ThrowTypeError(ctx, "Iterator.prototype.take called on non-object"); + v = JS_ToNumber(ctx, argv[0]); + if (JS_IsException(v)) + return JS_EXCEPTION; + // Check for Infinity. + if (JS_ToFloat64(ctx, &dlimit, v)) { + JS_FreeValue(ctx, v); + return JS_EXCEPTION; + } + if (isnan(dlimit)) { + JS_FreeValue(ctx, v); + goto fail; + } + if (!isfinite(dlimit)) { + JS_FreeValue(ctx, v); + if (dlimit < 0) + goto fail; + else + limit = MAX_SAFE_INTEGER; + } else { + v = JS_ToIntegerFree(ctx, v); + if (JS_IsException(v)) + return JS_EXCEPTION; + if (JS_ToInt64Free(ctx, &limit, v)) + return JS_EXCEPTION; + } + if (limit < 0) { + fail: + return JS_ThrowRangeError(ctx, "must be positive"); + } + return js_create_iterator_helper(ctx, this_val, JS_ITERATOR_HELPER_KIND_TAKE, JS_UNDEFINED, limit); +} + +static JSValue js_iterator_proto_toArray(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + JSValue item, method, result; + int64_t idx; + BOOL done; + + result = JS_UNDEFINED; + if (!JS_IsObject(this_val)) + return JS_ThrowTypeError(ctx, "Iterator.prototype.toArray called on non-object"); + method = JS_GetProperty(ctx, this_val, JS_ATOM_next); + if (JS_IsException(method)) + return JS_EXCEPTION; + result = JS_NewArray(ctx); + if (JS_IsException(result)) + goto exception; + for (idx = 0; /*empty*/; idx++) { + item = JS_IteratorNext(ctx, this_val, method, 0, NULL, &done); + if (JS_IsException(item)) + goto exception; + if (done) + break; + if (JS_DefinePropertyValueInt64(ctx, result, idx, item, + JS_PROP_C_W_E | JS_PROP_THROW) < 0) + goto exception; + } + if (JS_SetProperty(ctx, result, JS_ATOM_length, js_uint32(idx)) < 0) + goto exception; + JS_FreeValue(ctx, method); + return result; +exception: + JS_FreeValue(ctx, result); + JS_FreeValue(ctx, method); + return JS_EXCEPTION; +} + +static JSValue js_iterator_proto_iterator(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + return js_dup(this_val); +} + +static JSValue js_iterator_proto_get_toStringTag(JSContext *ctx, JSValue this_val) +{ + return JS_AtomToString(ctx, JS_ATOM_Iterator); +} + +static JSValue js_iterator_proto_set_toStringTag(JSContext *ctx, JSValue this_val, JSValue val) +{ + int res; + + if (JS_VALUE_GET_TAG(this_val) != JS_TAG_OBJECT) + return JS_ThrowTypeError(ctx, + "set Iterator.prototype[Symbol.toStringTag] called on non-object"); + if (js_same_value(ctx, this_val, ctx->class_proto[JS_CLASS_ITERATOR])) + return JS_ThrowTypeError(ctx, "Cannot assign to read only property"); + res = JS_GetOwnProperty(ctx, NULL, this_val, JS_ATOM_Symbol_toStringTag); + if (res < 0) + return JS_EXCEPTION; + if (res) { + if (JS_SetProperty(ctx, this_val, JS_ATOM_Symbol_toStringTag, js_dup(val)) < 0) + return JS_EXCEPTION; + } else { + if (JS_DefinePropertyValue(ctx, this_val, JS_ATOM_Symbol_toStringTag, js_dup(val), JS_PROP_C_W_E) < 0) + return JS_EXCEPTION; + } + return JS_UNDEFINED; +} + +typedef struct JSIteratorHelperData { + JSIteratorHelperKindEnum kind; + JSValue obj; + JSValue next; + JSValue func; // predicate (filter) or mapper (flatMap, map) + JSValue inner; // innerValue (flatMap) + int64_t count; // limit (drop, take) or counter (filter, map, flatMap) + BOOL executing; + BOOL done; +} JSIteratorHelperData; + +static void js_iterator_helper_finalizer(JSRuntime *rt, JSValue val) +{ + JSObject *p = JS_VALUE_GET_OBJ(val); + JSIteratorHelperData *it = p->u.iterator_helper_data; + if (it) { + JS_FreeValueRT(rt, it->obj); + JS_FreeValueRT(rt, it->func); + JS_FreeValueRT(rt, it->next); + JS_FreeValueRT(rt, it->inner); + js_free_rt(rt, it); + } +} + +static void js_iterator_helper_mark(JSRuntime *rt, JSValue val, + JS_MarkFunc *mark_func) +{ + JSObject *p = JS_VALUE_GET_OBJ(val); + JSIteratorHelperData *it = p->u.iterator_helper_data; + if (it) { + JS_MarkValue(rt, it->obj, mark_func); + JS_MarkValue(rt, it->func, mark_func); + JS_MarkValue(rt, it->next, mark_func); + JS_MarkValue(rt, it->inner, mark_func); + } +} + +static JSValue js_iterator_helper_next(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv, + BOOL *pdone, int magic) +{ + JSIteratorHelperData *it; + JSValue ret; + + *pdone = FALSE; + + it = JS_GetOpaque2(ctx, this_val, JS_CLASS_ITERATOR_HELPER); + if (!it) + goto fail; + if (it->executing) + return JS_ThrowTypeError(ctx, "cannot invoke a running iterator"); + if (it->done) { + *pdone = TRUE; + return JS_UNDEFINED; + } + + it->executing = TRUE; + + switch (it->kind) { + case JS_ITERATOR_HELPER_KIND_DROP: + { + JSValue item, method; + if (magic == GEN_MAGIC_NEXT) { + method = js_dup(it->next); + } else { + method = JS_GetProperty(ctx, it->obj, JS_ATOM_return); + if (JS_IsException(method)) + goto fail; + } + while (it->count > 0) { + it->count--; + item = JS_IteratorNext(ctx, it->obj, method, 0, NULL, pdone); + if (JS_IsException(item)) { + JS_FreeValue(ctx, method); + goto fail; + } + JS_FreeValue(ctx, item); + if (magic == GEN_MAGIC_RETURN) + *pdone = TRUE; + if (*pdone) { + JS_FreeValue(ctx, method); + ret = JS_UNDEFINED; + goto done; + } + } + + item = JS_IteratorNext(ctx, it->obj, method, 0, NULL, pdone); + JS_FreeValue(ctx, method); + if (JS_IsException(item)) + goto fail; + ret = item; + goto done; + } + break; + case JS_ITERATOR_HELPER_KIND_FILTER: + { + JSValue item, method, selected, index_val, args[2]; + if (magic == GEN_MAGIC_NEXT) { + method = js_dup(it->next); + } else { + method = JS_GetProperty(ctx, it->obj, JS_ATOM_return); + if (JS_IsException(method)) + goto fail; + } + filter_again: + item = JS_IteratorNext(ctx, it->obj, method, 0, NULL, pdone); + if (JS_IsException(item)) { + JS_FreeValue(ctx, method); + goto fail; + } + if (*pdone || magic == GEN_MAGIC_RETURN) { + JS_FreeValue(ctx, method); + ret = item; + goto done; + } + index_val = js_int64(it->count++); + args[0] = item; + args[1] = index_val; + selected = JS_Call(ctx, it->func, JS_UNDEFINED, countof(args), args); + JS_FreeValue(ctx, index_val); + if (JS_IsException(selected)) { + JS_FreeValue(ctx, method); + goto fail; + } + if (JS_ToBoolFree(ctx, selected)) { + JS_FreeValue(ctx, method); + ret = item; + goto done; + } + goto filter_again; + } + break; + case JS_ITERATOR_HELPER_KIND_FLAT_MAP: + { + JSValue item, method, index_val, args[2], iter; + flat_map_again: + if (JS_IsUndefined(it->inner)) { + if (magic == GEN_MAGIC_NEXT) { + method = js_dup(it->next); + } else { + method = JS_GetProperty(ctx, it->obj, JS_ATOM_return); + if (JS_IsException(method)) + goto fail; + } + item = JS_IteratorNext(ctx, it->obj, method, 0, NULL, pdone); + JS_FreeValue(ctx, method); + if (JS_IsException(item)) + goto fail; + if (*pdone || magic == GEN_MAGIC_RETURN) { + ret = item; + goto done; + } + index_val = js_int64(it->count++); + args[0] = item; + args[1] = index_val; + ret = JS_Call(ctx, it->func, JS_UNDEFINED, countof(args), args); + JS_FreeValue(ctx, item); + JS_FreeValue(ctx, index_val); + if (JS_IsException(ret)) + goto fail; + if (!JS_IsObject(ret)) { + JS_FreeValue(ctx, ret); + JS_ThrowTypeError(ctx, "not an object"); + goto fail; + } + method = JS_GetProperty(ctx, ret, JS_ATOM_Symbol_iterator); + if (JS_IsException(method)) { + JS_FreeValue(ctx, ret); + goto fail; + } + if (JS_IsNull(method) || JS_IsUndefined(method)) { + JS_FreeValue(ctx, method); + iter = ret; + } else { + iter = JS_GetIterator2(ctx, ret, method); + JS_FreeValue(ctx, method); + JS_FreeValue(ctx, ret); + if (JS_IsException(iter)) + goto fail; + } + + it->inner = iter; + } + + if (magic == GEN_MAGIC_NEXT) + method = JS_GetProperty(ctx, it->inner, JS_ATOM_next); + else + method = JS_GetProperty(ctx, it->inner, JS_ATOM_return); + if (JS_IsException(method)) { + inner_fail: + JS_IteratorClose(ctx, it->inner, FALSE); + JS_FreeValue(ctx, it->inner); + it->inner = JS_UNDEFINED; + goto fail; + } + if (magic == GEN_MAGIC_RETURN && (JS_IsUndefined(method) || JS_IsNull(method))) { + goto inner_end; + } else { + item = JS_IteratorNext(ctx, it->inner, method, 0, NULL, pdone); + JS_FreeValue(ctx, method); + if (JS_IsException(item)) + goto inner_fail; + } + if (*pdone) { + inner_end: + *pdone = FALSE; // The outer iterator must continue. + JS_IteratorClose(ctx, it->inner, FALSE); + JS_FreeValue(ctx, it->inner); + it->inner = JS_UNDEFINED; + goto flat_map_again; + } + ret = item; + goto done; + } + break; + case JS_ITERATOR_HELPER_KIND_MAP: + { + JSValue item, method, index_val, args[2]; + if (magic == GEN_MAGIC_NEXT) { + method = js_dup(it->next); + } else { + method = JS_GetProperty(ctx, it->obj, JS_ATOM_return); + if (JS_IsException(method)) + goto fail; + } + item = JS_IteratorNext(ctx, it->obj, method, 0, NULL, pdone); + JS_FreeValue(ctx, method); + if (JS_IsException(item)) + goto fail; + if (*pdone || magic == GEN_MAGIC_RETURN) { + ret = item; + goto done; + } + index_val = js_int64(it->count++); + args[0] = item; + args[1] = index_val; + ret = JS_Call(ctx, it->func, JS_UNDEFINED, countof(args), args); + JS_FreeValue(ctx, index_val); + if (JS_IsException(ret)) + goto fail; + goto done; + } + break; + case JS_ITERATOR_HELPER_KIND_TAKE: + { + JSValue item, method; + if (it->count > 0) { + if (magic == GEN_MAGIC_NEXT) { + method = js_dup(it->next); + } else { + method = JS_GetProperty(ctx, it->obj, JS_ATOM_return); + if (JS_IsException(method)) + goto fail; + } + it->count--; + item = JS_IteratorNext(ctx, it->obj, method, 0, NULL, pdone); + JS_FreeValue(ctx, method); + if (JS_IsException(item)) + goto fail; + ret = item; + goto done; + } + + *pdone = TRUE; + if (JS_IteratorClose(ctx, it->obj, FALSE)) + ret = JS_EXCEPTION; + else + ret = JS_UNDEFINED; + goto done; + } + break; + default: + abort(); + } + +done: + it->done = magic == GEN_MAGIC_NEXT ? *pdone : TRUE; + it->executing = FALSE; + return ret; +fail: + if (it) { + /* close the iterator object, preserving pending exception */ + JS_IteratorClose(ctx, it->obj, TRUE); + } + ret = JS_EXCEPTION; + goto done; +} + +static JSValue js_create_iterator_helper(JSContext *ctx, JSValue iterator, + JSIteratorHelperKindEnum kind, JSValue func, int64_t count) +{ + JSValue obj, method; + JSIteratorHelperData *it; + + method = JS_GetProperty(ctx, iterator, JS_ATOM_next); + if (JS_IsException(method)) + return JS_EXCEPTION; + obj = JS_NewObjectClass(ctx, JS_CLASS_ITERATOR_HELPER); + if (JS_IsException(obj)) { + JS_FreeValue(ctx, method); + return JS_EXCEPTION; + } + it = js_malloc(ctx, sizeof(*it)); + if (!it) { + JS_FreeValue(ctx, obj); + JS_FreeValue(ctx, method); + return JS_EXCEPTION; + } + it->kind = kind; + it->obj = js_dup(iterator); + it->func = js_dup(func); + it->next = method; + it->inner = JS_UNDEFINED; + it->count = count; + it->executing = FALSE; + it->done = FALSE; + JS_SetOpaqueInternal(obj, it); + return obj; +} + +static const JSCFunctionListEntry js_iterator_funcs[] = { + JS_CFUNC_DEF("from", 1, js_iterator_from ), +}; + +static const JSCFunctionListEntry js_iterator_proto_funcs[] = { + JS_CFUNC_DEF("drop", 1, js_iterator_proto_drop ), + JS_CFUNC_DEF("every", 1, js_iterator_proto_every ), + JS_CFUNC_DEF("filter", 1, js_iterator_proto_filter ), + JS_CFUNC_DEF("find", 1, js_iterator_proto_find ), + JS_CFUNC_DEF("flatMap", 1, js_iterator_proto_flatMap ), + JS_CFUNC_DEF("forEach", 1, js_iterator_proto_forEach ), + JS_CFUNC_DEF("map", 1, js_iterator_proto_map ), + JS_CFUNC_DEF("reduce", 1, js_iterator_proto_reduce ), + JS_CFUNC_DEF("some", 1, js_iterator_proto_some ), + JS_CFUNC_DEF("take", 1, js_iterator_proto_take ), + JS_CFUNC_DEF("toArray", 0, js_iterator_proto_toArray ), + JS_CFUNC_DEF("[Symbol.iterator]", 0, js_iterator_proto_iterator ), + JS_CGETSET_DEF("[Symbol.toStringTag]", js_iterator_proto_get_toStringTag, js_iterator_proto_set_toStringTag), +}; + +static const JSCFunctionListEntry js_iterator_helper_proto_funcs[] = { + JS_ITERATOR_NEXT_DEF("next", 0, js_iterator_helper_next, GEN_MAGIC_NEXT ), + JS_ITERATOR_NEXT_DEF("return", 0, js_iterator_helper_next, GEN_MAGIC_RETURN ), + JS_PROP_STRING_DEF("[Symbol.toStringTag]", "Iterator Helper", JS_PROP_CONFIGURABLE ), +}; + +static const JSCFunctionListEntry js_array_proto_funcs[] = { + JS_CFUNC_DEF("at", 1, js_array_at ), + JS_CFUNC_DEF("with", 2, js_array_with ), + JS_CFUNC_DEF("concat", 1, js_array_concat ), + JS_CFUNC_MAGIC_DEF("every", 1, js_array_every, special_every ), + JS_CFUNC_MAGIC_DEF("some", 1, js_array_every, special_some ), + JS_CFUNC_MAGIC_DEF("forEach", 1, js_array_every, special_forEach ), + JS_CFUNC_MAGIC_DEF("map", 1, js_array_every, special_map ), + JS_CFUNC_MAGIC_DEF("filter", 1, js_array_every, special_filter ), + JS_CFUNC_MAGIC_DEF("reduce", 1, js_array_reduce, special_reduce ), + JS_CFUNC_MAGIC_DEF("reduceRight", 1, js_array_reduce, special_reduceRight ), + JS_CFUNC_DEF("fill", 1, js_array_fill ), + JS_CFUNC_MAGIC_DEF("find", 1, js_array_find, ArrayFind ), + JS_CFUNC_MAGIC_DEF("findIndex", 1, js_array_find, ArrayFindIndex ), + JS_CFUNC_MAGIC_DEF("findLast", 1, js_array_find, ArrayFindLast ), + JS_CFUNC_MAGIC_DEF("findLastIndex", 1, js_array_find, ArrayFindLastIndex ), + JS_CFUNC_DEF("indexOf", 1, js_array_indexOf ), + JS_CFUNC_DEF("lastIndexOf", 1, js_array_lastIndexOf ), + JS_CFUNC_DEF("includes", 1, js_array_includes ), + JS_CFUNC_MAGIC_DEF("join", 1, js_array_join, 0 ), + JS_CFUNC_DEF("toString", 0, js_array_toString ), + JS_CFUNC_MAGIC_DEF("toLocaleString", 0, js_array_join, 1 ), + JS_CFUNC_MAGIC_DEF("pop", 0, js_array_pop, 0 ), + JS_CFUNC_MAGIC_DEF("push", 1, js_array_push, 0 ), + JS_CFUNC_MAGIC_DEF("shift", 0, js_array_pop, 1 ), + JS_CFUNC_MAGIC_DEF("unshift", 1, js_array_push, 1 ), + JS_CFUNC_DEF("reverse", 0, js_array_reverse ), + JS_CFUNC_DEF("toReversed", 0, js_array_toReversed ), + JS_CFUNC_DEF("sort", 1, js_array_sort ), + JS_CFUNC_DEF("toSorted", 1, js_array_toSorted ), + JS_CFUNC_MAGIC_DEF("slice", 2, js_array_slice, 0 ), + JS_CFUNC_MAGIC_DEF("splice", 2, js_array_slice, 1 ), + JS_CFUNC_DEF("toSpliced", 2, js_array_toSpliced ), + JS_CFUNC_DEF("copyWithin", 2, js_array_copyWithin ), + JS_CFUNC_MAGIC_DEF("flatMap", 1, js_array_flatten, 1 ), + JS_CFUNC_MAGIC_DEF("flat", 0, js_array_flatten, 0 ), + JS_CFUNC_MAGIC_DEF("values", 0, js_create_array_iterator, JS_ITERATOR_KIND_VALUE ), + JS_ALIAS_DEF("[Symbol.iterator]", "values" ), + JS_CFUNC_MAGIC_DEF("keys", 0, js_create_array_iterator, JS_ITERATOR_KIND_KEY ), + JS_CFUNC_MAGIC_DEF("entries", 0, js_create_array_iterator, JS_ITERATOR_KIND_KEY_AND_VALUE ), +}; + +static const JSCFunctionListEntry js_array_iterator_proto_funcs[] = { + JS_ITERATOR_NEXT_DEF("next", 0, js_array_iterator_next, 0 ), + JS_PROP_STRING_DEF("[Symbol.toStringTag]", "Array Iterator", JS_PROP_CONFIGURABLE ), +}; + +/* Number */ + +static JSValue js_number_constructor(JSContext *ctx, JSValue new_target, + int argc, JSValue *argv) +{ + JSValue val, obj; + if (argc == 0) { + val = js_int32(0); + } else { + val = JS_ToNumeric(ctx, argv[0]); + if (JS_IsException(val)) + return val; + switch(JS_VALUE_GET_TAG(val)) { + case JS_TAG_BIG_INT: + { + JSBigInt *p = JS_VALUE_GET_PTR(val); + double d; + bf_get_float64(&p->num, &d, BF_RNDN); + JS_FreeValue(ctx, val); + val = js_float64(d); + } + break; + default: + break; + } + } + if (!JS_IsUndefined(new_target)) { + obj = js_create_from_ctor(ctx, new_target, JS_CLASS_NUMBER); + if (!JS_IsException(obj)) + JS_SetObjectData(ctx, obj, val); + return obj; + } else { + return val; + } +} + +static JSValue js_number_isNaN(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + if (!JS_IsNumber(argv[0])) + return JS_FALSE; + return js_global_isNaN(ctx, this_val, argc, argv); +} + +static JSValue js_number_isFinite(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + if (!JS_IsNumber(argv[0])) + return JS_FALSE; + return js_global_isFinite(ctx, this_val, argc, argv); +} + +static JSValue js_number_isInteger(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + int ret; + ret = JS_NumberIsInteger(ctx, argv[0]); + if (ret < 0) + return JS_EXCEPTION; + else + return js_bool(ret); +} + +static JSValue js_number_isSafeInteger(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + double d; + if (!JS_IsNumber(argv[0])) + return JS_FALSE; + if (unlikely(JS_ToFloat64(ctx, &d, argv[0]))) + return JS_EXCEPTION; + return js_bool(is_safe_integer(d)); +} + +static const JSCFunctionListEntry js_number_funcs[] = { + /* global ParseInt and parseFloat should be defined already or delayed */ + JS_ALIAS_BASE_DEF("parseInt", "parseInt", 0 ), + JS_ALIAS_BASE_DEF("parseFloat", "parseFloat", 0 ), + JS_CFUNC_DEF("isNaN", 1, js_number_isNaN ), + JS_CFUNC_DEF("isFinite", 1, js_number_isFinite ), + JS_CFUNC_DEF("isInteger", 1, js_number_isInteger ), + JS_CFUNC_DEF("isSafeInteger", 1, js_number_isSafeInteger ), + JS_PROP_DOUBLE_DEF("MAX_VALUE", 1.7976931348623157e+308, 0 ), + JS_PROP_DOUBLE_DEF("MIN_VALUE", 5e-324, 0 ), + JS_PROP_DOUBLE_DEF("NaN", NAN, 0 ), + JS_PROP_DOUBLE_DEF("NEGATIVE_INFINITY", -INFINITY, 0 ), + JS_PROP_DOUBLE_DEF("POSITIVE_INFINITY", INFINITY, 0 ), + JS_PROP_DOUBLE_DEF("EPSILON", 2.220446049250313e-16, 0 ), /* ES6 */ + JS_PROP_DOUBLE_DEF("MAX_SAFE_INTEGER", 9007199254740991.0, 0 ), /* ES6 */ + JS_PROP_DOUBLE_DEF("MIN_SAFE_INTEGER", -9007199254740991.0, 0 ), /* ES6 */ +}; + +static JSValue js_thisNumberValue(JSContext *ctx, JSValue this_val) +{ + if (JS_IsNumber(this_val)) + return js_dup(this_val); + + if (JS_VALUE_GET_TAG(this_val) == JS_TAG_OBJECT) { + JSObject *p = JS_VALUE_GET_OBJ(this_val); + if (p->class_id == JS_CLASS_NUMBER) { + if (JS_IsNumber(p->u.object_data)) + return js_dup(p->u.object_data); + } + } + return JS_ThrowTypeError(ctx, "not a number"); +} + +static JSValue js_number_valueOf(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + return js_thisNumberValue(ctx, this_val); +} + +static int js_get_radix(JSContext *ctx, JSValue val) +{ + int radix; + if (JS_ToInt32Sat(ctx, &radix, val)) + return -1; + if (radix < 2 || radix > 36) { + JS_ThrowRangeError(ctx, "toString() radix argument must be between 2 and 36"); + return -1; + } + return radix; +} + +static JSValue js_number_toString(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv, int magic) +{ + char buf[72]; + JSValue val; + int base; + double d; + + val = js_thisNumberValue(ctx, this_val); + if (JS_IsException(val)) + return val; + if (magic || JS_IsUndefined(argv[0])) { + if (JS_VALUE_GET_TAG(val) == JS_TAG_INT) { + size_t len = i32toa(buf, JS_VALUE_GET_INT(val)); + return js_new_string8_len(ctx, buf, len); + } + base = 10; + } else { + base = js_get_radix(ctx, argv[0]); + if (base < 0) { + JS_FreeValue(ctx, val); + return JS_EXCEPTION; + } + } + if (JS_VALUE_GET_TAG(val) == JS_TAG_INT) { + size_t len = i32toa_radix(buf, JS_VALUE_GET_INT(val), base); + return js_new_string8_len(ctx, buf, len); + } + if (JS_ToFloat64Free(ctx, &d, val)) + return JS_EXCEPTION; + if (base != 10) + return js_dtoa_radix(ctx, d, base); + + return js_dtoa(ctx, d, 0, JS_DTOA_TOSTRING); +} + +static JSValue js_number_toFixed(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + JSValue val; + int f; + double d; + + val = js_thisNumberValue(ctx, this_val); + if (JS_IsException(val)) + return val; + if (JS_ToFloat64Free(ctx, &d, val)) + return JS_EXCEPTION; + if (JS_ToInt32Sat(ctx, &f, argv[0])) + return JS_EXCEPTION; + if (f < 0 || f > 100) { + return JS_ThrowRangeError(ctx, "toFixed() digits argument must be between 0 and 100"); + } + if (fabs(d) >= 1e21) { + // use ToString(d) + return js_dtoa(ctx, d, 0, JS_DTOA_TOSTRING); + } else { + return js_dtoa(ctx, d, f, JS_DTOA_FIXED); + } +} + +static JSValue js_number_toExponential(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + JSValue val; + double d; + int f; + + val = js_thisNumberValue(ctx, this_val); + if (JS_IsException(val)) + return val; + if (JS_ToFloat64Free(ctx, &d, val)) + return JS_EXCEPTION; + if (JS_ToInt32Sat(ctx, &f, argv[0])) + return JS_EXCEPTION; + if (!isfinite(d)) + return js_dtoa_infinite(ctx, d); + if (!JS_IsUndefined(argv[0])) { + if (f < 0 || f > 100) { + return JS_ThrowRangeError(ctx, "toExponential() argument must be between 0 and 100"); + } + f += 1; /* number of significant digits between 1 and 101 */ + } + return js_dtoa(ctx, d, f, JS_DTOA_EXPONENTIAL); +} + +static JSValue js_number_toPrecision(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + JSValue val; + int p; + double d; + + val = js_thisNumberValue(ctx, this_val); + if (JS_IsException(val)) + return val; + if (JS_ToFloat64Free(ctx, &d, val)) + return JS_EXCEPTION; + if (JS_IsUndefined(argv[0])) + return js_dtoa(ctx, d, 0, JS_DTOA_TOSTRING); + if (JS_ToInt32Sat(ctx, &p, argv[0])) + return JS_EXCEPTION; + if (!isfinite(d)) + return js_dtoa_infinite(ctx, d); + if (p < 1 || p > 100) { + return JS_ThrowRangeError(ctx, "toPrecision() argument must be between 1 and 100"); + } + return js_dtoa(ctx, d, p, JS_DTOA_PRECISION); +} + +static const JSCFunctionListEntry js_number_proto_funcs[] = { + JS_CFUNC_DEF("toExponential", 1, js_number_toExponential ), + JS_CFUNC_DEF("toFixed", 1, js_number_toFixed ), + JS_CFUNC_DEF("toPrecision", 1, js_number_toPrecision ), + JS_CFUNC_MAGIC_DEF("toString", 1, js_number_toString, 0 ), + JS_CFUNC_MAGIC_DEF("toLocaleString", 0, js_number_toString, 1 ), + JS_CFUNC_DEF("valueOf", 0, js_number_valueOf ), +}; + +static JSValue js_parseInt(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + const char *str; + int radix, flags; + JSValue ret; + size_t len; + + str = JS_ToCStringLen(ctx, &len, argv[0]); + if (!str) + return JS_EXCEPTION; + if (JS_ToInt32(ctx, &radix, argv[1])) { + JS_FreeCString(ctx, str); + return JS_EXCEPTION; + } + flags = ATOD_TRIM_SPACES; + if (radix == 0) { + flags |= ATOD_ACCEPT_HEX_PREFIX; // Only 0x and 0X are supported + radix = 10; + } + ret = js_atof(ctx, str, len, NULL, radix, flags); + JS_FreeCString(ctx, str); + return ret; +} + +static JSValue js_parseFloat(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + const char *str; + JSValue ret; + int flags; + size_t len; + + str = JS_ToCStringLen(ctx, &len, argv[0]); + if (!str) + return JS_EXCEPTION; + flags = ATOD_TRIM_SPACES | ATOD_ACCEPT_FLOAT | ATOD_ACCEPT_INFINITY; + ret = js_atof(ctx, str, len, NULL, 10, flags); + JS_FreeCString(ctx, str); + return ret; +} + +/* Boolean */ +static JSValue js_boolean_constructor(JSContext *ctx, JSValue new_target, + int argc, JSValue *argv) +{ + JSValue val, obj; + val = js_bool(JS_ToBool(ctx, argv[0])); + if (!JS_IsUndefined(new_target)) { + obj = js_create_from_ctor(ctx, new_target, JS_CLASS_BOOLEAN); + if (!JS_IsException(obj)) + JS_SetObjectData(ctx, obj, val); + return obj; + } else { + return val; + } +} + +static JSValue js_thisBooleanValue(JSContext *ctx, JSValue this_val) +{ + if (JS_VALUE_GET_TAG(this_val) == JS_TAG_BOOL) + return js_dup(this_val); + + if (JS_VALUE_GET_TAG(this_val) == JS_TAG_OBJECT) { + JSObject *p = JS_VALUE_GET_OBJ(this_val); + if (p->class_id == JS_CLASS_BOOLEAN) { + if (JS_VALUE_GET_TAG(p->u.object_data) == JS_TAG_BOOL) + return p->u.object_data; + } + } + return JS_ThrowTypeError(ctx, "not a boolean"); +} + +static JSValue js_boolean_toString(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + JSValue val = js_thisBooleanValue(ctx, this_val); + if (JS_IsException(val)) + return val; + return JS_AtomToString(ctx, JS_VALUE_GET_BOOL(val) ? + JS_ATOM_true : JS_ATOM_false); +} + +static JSValue js_boolean_valueOf(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + return js_thisBooleanValue(ctx, this_val); +} + +static const JSCFunctionListEntry js_boolean_proto_funcs[] = { + JS_CFUNC_DEF("toString", 0, js_boolean_toString ), + JS_CFUNC_DEF("valueOf", 0, js_boolean_valueOf ), +}; + +/* String */ + +static int js_string_get_own_property(JSContext *ctx, + JSPropertyDescriptor *desc, + JSValue obj, JSAtom prop) +{ + JSObject *p; + JSString *p1; + uint32_t idx, ch; + + /* This is a class exotic method: obj class_id is JS_CLASS_STRING */ + if (__JS_AtomIsTaggedInt(prop)) { + p = JS_VALUE_GET_OBJ(obj); + if (JS_VALUE_GET_TAG(p->u.object_data) == JS_TAG_STRING) { + p1 = JS_VALUE_GET_STRING(p->u.object_data); + idx = __JS_AtomToUInt32(prop); + if (idx < p1->len) { + if (desc) { + ch = string_get(p1, idx); + desc->flags = JS_PROP_ENUMERABLE; + desc->value = js_new_string_char(ctx, ch); + desc->getter = JS_UNDEFINED; + desc->setter = JS_UNDEFINED; + } + return TRUE; + } + } + } + return FALSE; +} + +static int js_string_define_own_property(JSContext *ctx, + JSValue this_obj, + JSAtom prop, JSValue val, + JSValue getter, + JSValue setter, int flags) +{ + uint32_t idx; + JSObject *p; + JSString *p1, *p2; + + if (__JS_AtomIsTaggedInt(prop)) { + idx = __JS_AtomToUInt32(prop); + p = JS_VALUE_GET_OBJ(this_obj); + if (JS_VALUE_GET_TAG(p->u.object_data) != JS_TAG_STRING) + goto def; + p1 = JS_VALUE_GET_STRING(p->u.object_data); + if (idx >= p1->len) + goto def; + if (!check_define_prop_flags(JS_PROP_ENUMERABLE, flags)) + goto fail; + /* check that the same value is configured */ + if (flags & JS_PROP_HAS_VALUE) { + if (JS_VALUE_GET_TAG(val) != JS_TAG_STRING) + goto fail; + p2 = JS_VALUE_GET_STRING(val); + if (p2->len != 1) + goto fail; + if (string_get(p1, idx) != string_get(p2, 0)) { + fail: + return JS_ThrowTypeErrorOrFalse(ctx, flags, "property is not configurable"); + } + } + return TRUE; + } else { + def: + return JS_DefineProperty(ctx, this_obj, prop, val, getter, setter, + flags | JS_PROP_NO_EXOTIC); + } +} + +static int js_string_delete_property(JSContext *ctx, + JSValue obj, JSAtom prop) +{ + uint32_t idx; + + if (__JS_AtomIsTaggedInt(prop)) { + idx = __JS_AtomToUInt32(prop); + if (idx < js_string_obj_get_length(ctx, obj)) { + return FALSE; + } + } + return TRUE; +} + +static const JSClassExoticMethods js_string_exotic_methods = { + .get_own_property = js_string_get_own_property, + .define_own_property = js_string_define_own_property, + .delete_property = js_string_delete_property, +}; + +static JSValue js_string_constructor(JSContext *ctx, JSValue new_target, + int argc, JSValue *argv) +{ + JSValue val, obj; + if (argc == 0) { + val = JS_AtomToString(ctx, JS_ATOM_empty_string); + } else { + if (JS_IsUndefined(new_target) && JS_IsSymbol(argv[0])) { + JSAtomStruct *p = JS_VALUE_GET_PTR(argv[0]); + val = JS_ConcatString3(ctx, "Symbol(", JS_AtomToString(ctx, js_get_atom_index(ctx->rt, p)), ")"); + } else { + val = JS_ToString(ctx, argv[0]); + } + if (JS_IsException(val)) + return val; + } + if (!JS_IsUndefined(new_target)) { + JSString *p1 = JS_VALUE_GET_STRING(val); + + obj = js_create_from_ctor(ctx, new_target, JS_CLASS_STRING); + if (!JS_IsException(obj)) { + JS_SetObjectData(ctx, obj, val); + JS_DefinePropertyValue(ctx, obj, JS_ATOM_length, js_int32(p1->len), 0); + } + return obj; + } else { + return val; + } +} + +static JSValue js_thisStringValue(JSContext *ctx, JSValue this_val) +{ + if (JS_VALUE_GET_TAG(this_val) == JS_TAG_STRING) + return js_dup(this_val); + + if (JS_VALUE_GET_TAG(this_val) == JS_TAG_OBJECT) { + JSObject *p = JS_VALUE_GET_OBJ(this_val); + if (p->class_id == JS_CLASS_STRING) { + if (JS_VALUE_GET_TAG(p->u.object_data) == JS_TAG_STRING) + return js_dup(p->u.object_data); + } + } + return JS_ThrowTypeError(ctx, "not a string"); +} + +static JSValue js_string_fromCharCode(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + int i; + StringBuffer b_s, *b = &b_s; + + // shortcut for single argument common case + if (argc == 1 && JS_VALUE_GET_TAG(argv[0]) == JS_TAG_INT) { + uint16_t c16 = JS_VALUE_GET_INT(argv[0]); + return js_new_string_char(ctx, c16); + } + + string_buffer_init(ctx, b, argc); + + for(i = 0; i < argc; i++) { + int32_t c; + if (JS_ToInt32(ctx, &c, argv[i]) || string_buffer_putc16(b, c & 0xffff)) { + string_buffer_free(b); + return JS_EXCEPTION; + } + } + return string_buffer_end(b); +} + +static JSValue js_string_fromCodePoint(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + double d; + int i, c; + StringBuffer b_s, *b = NULL; + + // shortcut for single argument common case + if (argc == 1 && JS_VALUE_GET_TAG(argv[0]) == JS_TAG_INT) { + c = JS_VALUE_GET_INT(argv[0]); + if (c < 0 || c > 0x10ffff) + goto range_error; + if (c <= 0xffff) { + return js_new_string_char(ctx, c); + } else { + uint16_t c16[2]; + c16[0] = get_hi_surrogate(c); + c16[1] = get_lo_surrogate(c); + return js_new_string16_len(ctx, c16, 2); + } + } + + /* XXX: could pre-compute string length if all arguments are JS_TAG_INT */ + b = &b_s; + if (string_buffer_init(ctx, b, argc)) + goto fail; + for(i = 0; i < argc; i++) { + if (JS_VALUE_GET_TAG(argv[i]) == JS_TAG_INT) { + c = JS_VALUE_GET_INT(argv[i]); + if (c < 0 || c > 0x10ffff) + goto range_error; + } else { + if (JS_ToFloat64(ctx, &d, argv[i])) + goto fail; + if (!(d >= 0 && d <= 0x10ffff) || (c = (int)d) != d) + goto range_error; + } + if (string_buffer_putc(b, c)) + goto fail; + } + return string_buffer_end(b); + + range_error: + JS_ThrowRangeError(ctx, "invalid code point"); + fail: + if (b) string_buffer_free(b); + return JS_EXCEPTION; +} + +static JSValue js_string_raw(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + // raw(temp,...a) + JSValue cooked, val, raw; + StringBuffer b_s, *b = &b_s; + int64_t i, n; + + string_buffer_init(ctx, b, 0); + raw = JS_UNDEFINED; + cooked = JS_ToObject(ctx, argv[0]); + if (JS_IsException(cooked)) + goto exception; + raw = JS_ToObjectFree(ctx, JS_GetProperty(ctx, cooked, JS_ATOM_raw)); + if (JS_IsException(raw)) + goto exception; + if (js_get_length64(ctx, &n, raw) < 0) + goto exception; + + for (i = 0; i < n; i++) { + val = JS_ToStringFree(ctx, JS_GetPropertyInt64(ctx, raw, i)); + if (JS_IsException(val)) + goto exception; + string_buffer_concat_value_free(b, val); + if (i < n - 1 && i + 1 < argc) { + if (string_buffer_concat_value(b, argv[i + 1])) + goto exception; + } + } + JS_FreeValue(ctx, cooked); + JS_FreeValue(ctx, raw); + return string_buffer_end(b); + +exception: + JS_FreeValue(ctx, cooked); + JS_FreeValue(ctx, raw); + string_buffer_free(b); + return JS_EXCEPTION; +} + +/* only used in test262 */ +JSValue js_string_codePointRange(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + uint32_t start, end, i, n; + StringBuffer b_s, *b = &b_s; + + if (JS_ToUint32(ctx, &start, argv[0]) || + JS_ToUint32(ctx, &end, argv[1])) + return JS_EXCEPTION; + end = min_uint32(end, 0x10ffff + 1); + + if (start > end) { + start = end; + } + n = end - start; + if (end > 0x10000) { + n += end - max_uint32(start, 0x10000); + } + if (string_buffer_init2(ctx, b, n, end >= 0x100)) + return JS_EXCEPTION; + for(i = start; i < end; i++) { + string_buffer_putc(b, i); + } + return string_buffer_end(b); +} + +static JSValue js_string_at(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + JSValue val, ret; + JSString *p; + int idx, c; + + val = JS_ToStringCheckObject(ctx, this_val); + if (JS_IsException(val)) + return val; + p = JS_VALUE_GET_STRING(val); + if (JS_ToInt32Sat(ctx, &idx, argv[0])) { + JS_FreeValue(ctx, val); + return JS_EXCEPTION; + } + if (idx < 0) + idx = p->len + idx; + if (idx < 0 || idx >= p->len) { + ret = JS_UNDEFINED; + } else { + c = string_get(p, idx); + ret = js_new_string_char(ctx, c); + } + JS_FreeValue(ctx, val); + return ret; +} + +static JSValue js_string_charCodeAt(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + JSValue val, ret; + JSString *p; + int idx, c; + + val = JS_ToStringCheckObject(ctx, this_val); + if (JS_IsException(val)) + return val; + p = JS_VALUE_GET_STRING(val); + if (JS_ToInt32Sat(ctx, &idx, argv[0])) { + JS_FreeValue(ctx, val); + return JS_EXCEPTION; + } + if (idx < 0 || idx >= p->len) { + ret = JS_NAN; + } else { + c = string_get(p, idx); + ret = js_int32(c); + } + JS_FreeValue(ctx, val); + return ret; +} + +static JSValue js_string_charAt(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + JSValue val, ret; + JSString *p; + int idx, c; + + val = JS_ToStringCheckObject(ctx, this_val); + if (JS_IsException(val)) + return val; + p = JS_VALUE_GET_STRING(val); + if (JS_ToInt32Sat(ctx, &idx, argv[0])) { + JS_FreeValue(ctx, val); + return JS_EXCEPTION; + } + if (idx < 0 || idx >= p->len) { + ret = JS_AtomToString(ctx, JS_ATOM_empty_string); + } else { + c = string_get(p, idx); + ret = js_new_string_char(ctx, c); + } + JS_FreeValue(ctx, val); + return ret; +} + +static JSValue js_string_codePointAt(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + JSValue val, ret; + JSString *p; + int idx, c; + + val = JS_ToStringCheckObject(ctx, this_val); + if (JS_IsException(val)) + return val; + p = JS_VALUE_GET_STRING(val); + if (JS_ToInt32Sat(ctx, &idx, argv[0])) { + JS_FreeValue(ctx, val); + return JS_EXCEPTION; + } + if (idx < 0 || idx >= p->len) { + ret = JS_UNDEFINED; + } else { + c = string_getc(p, &idx); + ret = js_int32(c); + } + JS_FreeValue(ctx, val); + return ret; +} + +static JSValue js_string_concat(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + JSValue r; + int i; + + /* XXX: Use more efficient method */ + /* XXX: This method is OK if r has a single refcount */ + /* XXX: should use string_buffer? */ + r = JS_ToStringCheckObject(ctx, this_val); + for (i = 0; i < argc; i++) { + if (JS_IsException(r)) + break; + r = JS_ConcatString(ctx, r, js_dup(argv[i])); + } + return r; +} + +static int string_cmp(JSString *p1, JSString *p2, int x1, int x2, int len) +{ + int i, c1, c2; + for (i = 0; i < len; i++) { + if ((c1 = string_get(p1, x1 + i)) != (c2 = string_get(p2, x2 + i))) + return c1 - c2; + } + return 0; +} + +static int string_indexof_char(JSString *p, int c, int from) +{ + /* assuming 0 <= from <= p->len */ + int i, len = p->len; + if (p->is_wide_char) { + for (i = from; i < len; i++) { + if (p->u.str16[i] == c) + return i; + } + } else { + if ((c & ~0xff) == 0) { + for (i = from; i < len; i++) { + if (p->u.str8[i] == (uint8_t)c) + return i; + } + } + } + return -1; +} + +static int string_indexof(JSString *p1, JSString *p2, int from) +{ + /* assuming 0 <= from <= p1->len */ + int c, i, j, len1 = p1->len, len2 = p2->len; + if (len2 == 0) + return from; + for (i = from, c = string_get(p2, 0); i + len2 <= len1; i = j + 1) { + j = string_indexof_char(p1, c, i); + if (j < 0 || j + len2 > len1) + break; + if (!string_cmp(p1, p2, j + 1, 1, len2 - 1)) + return j; + } + return -1; +} + +static int64_t string_advance_index(JSString *p, int64_t index, BOOL unicode) +{ + if (!unicode || index >= p->len || !p->is_wide_char) { + index++; + } else { + int index32 = (int)index; + string_getc(p, &index32); + index = index32; + } + return index; +} + +static JSValue js_string_isWellFormed(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + JSValue str; + JSValue ret; + JSString *p; + uint32_t c, i, n; + + ret = JS_TRUE; + str = JS_ToStringCheckObject(ctx, this_val); + if (JS_IsException(str)) + return JS_EXCEPTION; + + p = JS_VALUE_GET_STRING(str); + if (!p->is_wide_char || p->len == 0) + goto done; // by definition well-formed + + for (i = 0, n = p->len; i < n; i++) { + c = p->u.str16[i]; + if (!is_surrogate(c)) + continue; + if (is_lo_surrogate(c) || i + 1 == n) + break; + c = p->u.str16[++i]; + if (!is_lo_surrogate(c)) + break; + } + + if (i < n) + ret = JS_FALSE; + +done: + JS_FreeValue(ctx, str); + return ret; +} + +static JSValue js_string_toWellFormed(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + JSValue str; + JSValue ret; + JSString *p; + uint32_t c, i, n; + + str = JS_ToStringCheckObject(ctx, this_val); + if (JS_IsException(str)) + return JS_EXCEPTION; + + p = JS_VALUE_GET_STRING(str); + if (!p->is_wide_char || p->len == 0) + return str; // by definition well-formed + + // TODO(bnoordhuis) don't clone when input is well-formed + ret = js_new_string16_len(ctx, p->u.str16, p->len); + JS_FreeValue(ctx, str); + if (JS_IsException(ret)) + return JS_EXCEPTION; + + p = JS_VALUE_GET_STRING(ret); + for (i = 0, n = p->len; i < n; i++) { + c = p->u.str16[i]; + if (!is_surrogate(c)) + continue; + if (is_lo_surrogate(c) || i + 1 == n) { + p->u.str16[i] = 0xFFFD; + continue; + } + c = p->u.str16[++i]; + if (!is_lo_surrogate(c)) + p->u.str16[--i] = 0xFFFD; + } + + return ret; +} + +static JSValue js_string_indexOf(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv, int lastIndexOf) +{ + JSValue str, v; + int i, len, v_len, pos, start, stop, ret, inc; + JSString *p; + JSString *p1; + + str = JS_ToStringCheckObject(ctx, this_val); + if (JS_IsException(str)) + return str; + v = JS_ToString(ctx, argv[0]); + if (JS_IsException(v)) + goto fail; + p = JS_VALUE_GET_STRING(str); + p1 = JS_VALUE_GET_STRING(v); + len = p->len; + v_len = p1->len; + if (lastIndexOf) { + pos = len - v_len; + if (argc > 1) { + double d; + if (JS_ToFloat64(ctx, &d, argv[1])) + goto fail; + if (!isnan(d)) { + if (d <= 0) + pos = 0; + else if (d < pos) + pos = d; + } + } + start = pos; + stop = 0; + inc = -1; + } else { + pos = 0; + if (argc > 1) { + if (JS_ToInt32Clamp(ctx, &pos, argv[1], 0, len, 0)) + goto fail; + } + start = pos; + stop = len - v_len; + inc = 1; + } + ret = -1; + if (len >= v_len && inc * (stop - start) >= 0) { + for (i = start;; i += inc) { + if (!string_cmp(p, p1, i, 0, v_len)) { + ret = i; + break; + } + if (i == stop) + break; + } + } + JS_FreeValue(ctx, str); + JS_FreeValue(ctx, v); + return js_int32(ret); + +fail: + JS_FreeValue(ctx, str); + JS_FreeValue(ctx, v); + return JS_EXCEPTION; +} + +/* return < 0 if exception or TRUE/FALSE */ +static int js_is_regexp(JSContext *ctx, JSValue obj); + +static JSValue js_string_includes(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv, int magic) +{ + JSValue str, v = JS_UNDEFINED; + int i, len, v_len, pos, start, stop, ret; + JSString *p; + JSString *p1; + + str = JS_ToStringCheckObject(ctx, this_val); + if (JS_IsException(str)) + return str; + ret = js_is_regexp(ctx, argv[0]); + if (ret) { + if (ret > 0) + JS_ThrowTypeError(ctx, "regexp not supported"); + goto fail; + } + v = JS_ToString(ctx, argv[0]); + if (JS_IsException(v)) + goto fail; + p = JS_VALUE_GET_STRING(str); + p1 = JS_VALUE_GET_STRING(v); + len = p->len; + v_len = p1->len; + pos = (magic == 2) ? len : 0; + if (argc > 1 && !JS_IsUndefined(argv[1])) { + if (JS_ToInt32Clamp(ctx, &pos, argv[1], 0, len, 0)) + goto fail; + } + len -= v_len; + ret = 0; + if (magic == 0) { + start = pos; + stop = len; + } else { + if (magic == 1) { + if (pos > len) + goto done; + } else { + pos -= v_len; + } + start = stop = pos; + } + if (start >= 0 && start <= stop) { + for (i = start;; i++) { + if (!string_cmp(p, p1, i, 0, v_len)) { + ret = 1; + break; + } + if (i == stop) + break; + } + } + done: + JS_FreeValue(ctx, str); + JS_FreeValue(ctx, v); + return js_bool(ret); + +fail: + JS_FreeValue(ctx, str); + JS_FreeValue(ctx, v); + return JS_EXCEPTION; +} + +static int check_regexp_g_flag(JSContext *ctx, JSValue regexp) +{ + int ret; + JSValue flags; + + ret = js_is_regexp(ctx, regexp); + if (ret < 0) + return -1; + if (ret) { + flags = JS_GetProperty(ctx, regexp, JS_ATOM_flags); + if (JS_IsException(flags)) + return -1; + if (JS_IsUndefined(flags) || JS_IsNull(flags)) { + JS_ThrowTypeError(ctx, "cannot convert to object"); + return -1; + } + flags = JS_ToStringFree(ctx, flags); + if (JS_IsException(flags)) + return -1; + ret = string_indexof_char(JS_VALUE_GET_STRING(flags), 'g', 0); + JS_FreeValue(ctx, flags); + if (ret < 0) { + JS_ThrowTypeError(ctx, "regexp must have the 'g' flag"); + return -1; + } + } + return 0; +} + +static JSValue js_string_match(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv, int atom) +{ + // match(rx), search(rx), matchAll(rx) + // atom is JS_ATOM_Symbol_match, JS_ATOM_Symbol_search, or JS_ATOM_Symbol_matchAll + JSValue O = this_val, regexp = argv[0], args[2]; + JSValue matcher, S, rx, result, str; + int args_len; + + if (JS_IsUndefined(O) || JS_IsNull(O)) + return JS_ThrowTypeError(ctx, "cannot convert to object"); + + if (!JS_IsUndefined(regexp) && !JS_IsNull(regexp)) { + matcher = JS_GetProperty(ctx, regexp, atom); + if (JS_IsException(matcher)) + return JS_EXCEPTION; + if (atom == JS_ATOM_Symbol_matchAll) { + if (check_regexp_g_flag(ctx, regexp) < 0) { + JS_FreeValue(ctx, matcher); + return JS_EXCEPTION; + } + } + if (!JS_IsUndefined(matcher) && !JS_IsNull(matcher)) { + return JS_CallFree(ctx, matcher, regexp, 1, &O); + } + } + S = JS_ToString(ctx, O); + if (JS_IsException(S)) + return JS_EXCEPTION; + args_len = 1; + args[0] = regexp; + str = JS_UNDEFINED; + if (atom == JS_ATOM_Symbol_matchAll) { + str = js_new_string8(ctx, "g"); + if (JS_IsException(str)) + goto fail; + args[args_len++] = str; + } + rx = JS_CallConstructor(ctx, ctx->regexp_ctor, args_len, args); + JS_FreeValue(ctx, str); + if (JS_IsException(rx)) { + fail: + JS_FreeValue(ctx, S); + return JS_EXCEPTION; + } + result = JS_InvokeFree(ctx, rx, atom, 1, &S); + JS_FreeValue(ctx, S); + return result; +} + +static JSValue js_string___GetSubstitution(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + // GetSubstitution(matched, str, position, captures, namedCaptures, rep) + JSValue matched, str, captures, namedCaptures, rep; + JSValue capture, name, s; + uint32_t position, len, matched_len, captures_len; + int i, j, j0, k, k1; + int c, c1; + StringBuffer b_s, *b = &b_s; + JSString *sp, *rp; + + matched = argv[0]; + str = argv[1]; + captures = argv[3]; + namedCaptures = argv[4]; + rep = argv[5]; + + if (!JS_IsString(rep) || !JS_IsString(str)) + return JS_ThrowTypeError(ctx, "not a string"); + + sp = JS_VALUE_GET_STRING(str); + rp = JS_VALUE_GET_STRING(rep); + + string_buffer_init(ctx, b, 0); + + captures_len = 0; + if (!JS_IsUndefined(captures)) { + if (js_get_length32(ctx, &captures_len, captures)) + goto exception; + } + if (js_get_length32(ctx, &matched_len, matched)) + goto exception; + if (JS_ToUint32(ctx, &position, argv[2]) < 0) + goto exception; + + len = rp->len; + i = 0; + for(;;) { + j = string_indexof_char(rp, '$', i); + if (j < 0 || j + 1 >= len) + break; + string_buffer_concat(b, rp, i, j); + j0 = j++; + c = string_get(rp, j++); + if (c == '$') { + string_buffer_putc8(b, '$'); + } else if (c == '&') { + if (string_buffer_concat_value(b, matched)) + goto exception; + } else if (c == '`') { + string_buffer_concat(b, sp, 0, position); + } else if (c == '\'') { + string_buffer_concat(b, sp, position + matched_len, sp->len); + } else if (c >= '0' && c <= '9') { + k = c - '0'; + if (j < len) { + c1 = string_get(rp, j); + if (c1 >= '0' && c1 <= '9') { + /* This behavior is specified in ES6 and refined in ECMA 2019 */ + /* ECMA 2019 does not have the extra test, but + Test262 S15.5.4.11_A3_T1..3 require this behavior */ + k1 = k * 10 + c1 - '0'; + if (k1 >= 1 && k1 < captures_len) { + k = k1; + j++; + } + } + } + if (k >= 1 && k < captures_len) { + s = JS_GetPropertyInt64(ctx, captures, k); + if (JS_IsException(s)) + goto exception; + if (!JS_IsUndefined(s)) { + if (string_buffer_concat_value_free(b, s)) + goto exception; + } + } else { + goto norep; + } + } else if (c == '<' && !JS_IsUndefined(namedCaptures)) { + k = string_indexof_char(rp, '>', j); + if (k < 0) + goto norep; + name = js_sub_string(ctx, rp, j, k); + if (JS_IsException(name)) + goto exception; + capture = JS_GetPropertyValue(ctx, namedCaptures, name); + if (JS_IsException(capture)) + goto exception; + if (!JS_IsUndefined(capture)) { + if (string_buffer_concat_value_free(b, capture)) + goto exception; + } + j = k + 1; + } else { + norep: + string_buffer_concat(b, rp, j0, j); + } + i = j; + } + string_buffer_concat(b, rp, i, rp->len); + return string_buffer_end(b); +exception: + string_buffer_free(b); + return JS_EXCEPTION; +} + +static JSValue js_string_replace(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv, + int is_replaceAll) +{ + // replace(rx, rep) + JSValue O = this_val, searchValue = argv[0], replaceValue = argv[1]; + JSValue args[6]; + JSValue str, search_str, replaceValue_str, repl_str; + JSString *sp, *searchp; + StringBuffer b_s, *b = &b_s; + int pos, functionalReplace, endOfLastMatch; + BOOL is_first; + + if (JS_IsUndefined(O) || JS_IsNull(O)) + return JS_ThrowTypeError(ctx, "cannot convert to object"); + + search_str = JS_UNDEFINED; + replaceValue_str = JS_UNDEFINED; + repl_str = JS_UNDEFINED; + + if (!JS_IsUndefined(searchValue) && !JS_IsNull(searchValue)) { + JSValue replacer; + if (is_replaceAll) { + if (check_regexp_g_flag(ctx, searchValue) < 0) + return JS_EXCEPTION; + } + replacer = JS_GetProperty(ctx, searchValue, JS_ATOM_Symbol_replace); + if (JS_IsException(replacer)) + return JS_EXCEPTION; + if (!JS_IsUndefined(replacer) && !JS_IsNull(replacer)) { + args[0] = O; + args[1] = replaceValue; + return JS_CallFree(ctx, replacer, searchValue, 2, args); + } + } + string_buffer_init(ctx, b, 0); + + str = JS_ToString(ctx, O); + if (JS_IsException(str)) + goto exception; + search_str = JS_ToString(ctx, searchValue); + if (JS_IsException(search_str)) + goto exception; + functionalReplace = JS_IsFunction(ctx, replaceValue); + if (!functionalReplace) { + replaceValue_str = JS_ToString(ctx, replaceValue); + if (JS_IsException(replaceValue_str)) + goto exception; + } + + sp = JS_VALUE_GET_STRING(str); + searchp = JS_VALUE_GET_STRING(search_str); + endOfLastMatch = 0; + is_first = TRUE; + for(;;) { + if (unlikely(searchp->len == 0)) { + if (is_first) + pos = 0; + else if (endOfLastMatch >= sp->len) + pos = -1; + else + pos = endOfLastMatch + 1; + } else { + pos = string_indexof(sp, searchp, endOfLastMatch); + } + if (pos < 0) { + if (is_first) { + string_buffer_free(b); + JS_FreeValue(ctx, search_str); + JS_FreeValue(ctx, replaceValue_str); + return str; + } else { + break; + } + } + if (functionalReplace) { + args[0] = search_str; + args[1] = js_int32(pos); + args[2] = str; + repl_str = JS_ToStringFree(ctx, JS_Call(ctx, replaceValue, JS_UNDEFINED, 3, args)); + } else { + args[0] = search_str; + args[1] = str; + args[2] = js_int32(pos); + args[3] = JS_UNDEFINED; + args[4] = JS_UNDEFINED; + args[5] = replaceValue_str; + repl_str = js_string___GetSubstitution(ctx, JS_UNDEFINED, 6, args); + } + if (JS_IsException(repl_str)) + goto exception; + + string_buffer_concat(b, sp, endOfLastMatch, pos); + string_buffer_concat_value_free(b, repl_str); + endOfLastMatch = pos + searchp->len; + is_first = FALSE; + if (!is_replaceAll) + break; + } + string_buffer_concat(b, sp, endOfLastMatch, sp->len); + JS_FreeValue(ctx, search_str); + JS_FreeValue(ctx, replaceValue_str); + JS_FreeValue(ctx, str); + return string_buffer_end(b); + +exception: + string_buffer_free(b); + JS_FreeValue(ctx, search_str); + JS_FreeValue(ctx, replaceValue_str); + JS_FreeValue(ctx, str); + return JS_EXCEPTION; +} + +static JSValue js_string_split(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + // split(sep, limit) + JSValue O = this_val, separator = argv[0], limit = argv[1]; + JSValue args[2]; + JSValue S, A, R, T; + uint32_t lim, lengthA; + int64_t p, q, s, r, e; + JSString *sp, *rp; + + if (JS_IsUndefined(O) || JS_IsNull(O)) + return JS_ThrowTypeError(ctx, "cannot convert to object"); + + S = JS_UNDEFINED; + A = JS_UNDEFINED; + R = JS_UNDEFINED; + + if (!JS_IsUndefined(separator) && !JS_IsNull(separator)) { + JSValue splitter; + splitter = JS_GetProperty(ctx, separator, JS_ATOM_Symbol_split); + if (JS_IsException(splitter)) + return JS_EXCEPTION; + if (!JS_IsUndefined(splitter) && !JS_IsNull(splitter)) { + args[0] = O; + args[1] = limit; + return JS_CallFree(ctx, splitter, separator, 2, args); + } + } + S = JS_ToString(ctx, O); + if (JS_IsException(S)) + goto exception; + A = JS_NewArray(ctx); + if (JS_IsException(A)) + goto exception; + lengthA = 0; + if (JS_IsUndefined(limit)) { + lim = 0xffffffff; + } else { + if (JS_ToUint32(ctx, &lim, limit) < 0) + goto exception; + } + sp = JS_VALUE_GET_STRING(S); + s = sp->len; + R = JS_ToString(ctx, separator); + if (JS_IsException(R)) + goto exception; + rp = JS_VALUE_GET_STRING(R); + r = rp->len; + p = 0; + if (lim == 0) + goto done; + if (JS_IsUndefined(separator)) + goto add_tail; + if (s == 0) { + if (r != 0) + goto add_tail; + goto done; + } + q = p; + for (q = p; (q += !r) <= s - r - !r; q = p = e + r) { + e = string_indexof(sp, rp, q); + if (e < 0) + break; + T = js_sub_string(ctx, sp, p, e); + if (JS_IsException(T)) + goto exception; + if (JS_CreateDataPropertyUint32(ctx, A, lengthA++, T, 0) < 0) + goto exception; + if (lengthA == lim) + goto done; + } +add_tail: + T = js_sub_string(ctx, sp, p, s); + if (JS_IsException(T)) + goto exception; + if (JS_CreateDataPropertyUint32(ctx, A, lengthA++, T,0 ) < 0) + goto exception; +done: + JS_FreeValue(ctx, S); + JS_FreeValue(ctx, R); + return A; + +exception: + JS_FreeValue(ctx, A); + JS_FreeValue(ctx, S); + JS_FreeValue(ctx, R); + return JS_EXCEPTION; +} + +static JSValue js_string_substring(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + JSValue str, ret; + int a, b, start, end; + JSString *p; + + str = JS_ToStringCheckObject(ctx, this_val); + if (JS_IsException(str)) + return str; + p = JS_VALUE_GET_STRING(str); + if (JS_ToInt32Clamp(ctx, &a, argv[0], 0, p->len, 0)) { + JS_FreeValue(ctx, str); + return JS_EXCEPTION; + } + b = p->len; + if (!JS_IsUndefined(argv[1])) { + if (JS_ToInt32Clamp(ctx, &b, argv[1], 0, p->len, 0)) { + JS_FreeValue(ctx, str); + return JS_EXCEPTION; + } + } + if (a < b) { + start = a; + end = b; + } else { + start = b; + end = a; + } + ret = js_sub_string(ctx, p, start, end); + JS_FreeValue(ctx, str); + return ret; +} + +static JSValue js_string_substr(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + JSValue str, ret; + int a, len, n; + JSString *p; + + str = JS_ToStringCheckObject(ctx, this_val); + if (JS_IsException(str)) + return str; + p = JS_VALUE_GET_STRING(str); + len = p->len; + if (JS_ToInt32Clamp(ctx, &a, argv[0], 0, len, len)) { + JS_FreeValue(ctx, str); + return JS_EXCEPTION; + } + n = len - a; + if (!JS_IsUndefined(argv[1])) { + if (JS_ToInt32Clamp(ctx, &n, argv[1], 0, len - a, 0)) { + JS_FreeValue(ctx, str); + return JS_EXCEPTION; + } + } + ret = js_sub_string(ctx, p, a, a + n); + JS_FreeValue(ctx, str); + return ret; +} + +static JSValue js_string_slice(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + JSValue str, ret; + int len, start, end; + JSString *p; + + str = JS_ToStringCheckObject(ctx, this_val); + if (JS_IsException(str)) + return str; + p = JS_VALUE_GET_STRING(str); + len = p->len; + if (JS_ToInt32Clamp(ctx, &start, argv[0], 0, len, len)) { + JS_FreeValue(ctx, str); + return JS_EXCEPTION; + } + end = len; + if (!JS_IsUndefined(argv[1])) { + if (JS_ToInt32Clamp(ctx, &end, argv[1], 0, len, len)) { + JS_FreeValue(ctx, str); + return JS_EXCEPTION; + } + } + ret = js_sub_string(ctx, p, start, max_int(end, start)); + JS_FreeValue(ctx, str); + return ret; +} + +static JSValue js_string_pad(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv, int padEnd) +{ + JSValue str, v = JS_UNDEFINED; + StringBuffer b_s, *b = &b_s; + JSString *p, *p1 = NULL; + int n, len, c = ' '; + + str = JS_ToStringCheckObject(ctx, this_val); + if (JS_IsException(str)) + goto fail1; + if (JS_ToInt32Sat(ctx, &n, argv[0])) + goto fail2; + p = JS_VALUE_GET_STRING(str); + len = p->len; + if (len >= n) + return str; + if (argc > 1 && !JS_IsUndefined(argv[1])) { + v = JS_ToString(ctx, argv[1]); + if (JS_IsException(v)) + goto fail2; + p1 = JS_VALUE_GET_STRING(v); + if (p1->len == 0) { + JS_FreeValue(ctx, v); + return str; + } + if (p1->len == 1) { + c = string_get(p1, 0); + p1 = NULL; + } + } + if (n > JS_STRING_LEN_MAX) { + JS_ThrowRangeError(ctx, "invalid string length"); + goto fail2; + } + if (string_buffer_init(ctx, b, n)) + goto fail3; + n -= len; + if (padEnd) { + if (string_buffer_concat(b, p, 0, len)) + goto fail; + } + if (p1) { + while (n > 0) { + int chunk = min_int(n, p1->len); + if (string_buffer_concat(b, p1, 0, chunk)) + goto fail; + n -= chunk; + } + } else { + if (string_buffer_fill(b, c, n)) + goto fail; + } + if (!padEnd) { + if (string_buffer_concat(b, p, 0, len)) + goto fail; + } + JS_FreeValue(ctx, v); + JS_FreeValue(ctx, str); + return string_buffer_end(b); + +fail: + string_buffer_free(b); +fail3: + JS_FreeValue(ctx, v); +fail2: + JS_FreeValue(ctx, str); +fail1: + return JS_EXCEPTION; +} + +static JSValue js_string_repeat(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + JSValue str; + StringBuffer b_s, *b = &b_s; + JSString *p; + int64_t val; + int n, len; + + str = JS_ToStringCheckObject(ctx, this_val); + if (JS_IsException(str)) + goto fail; + if (JS_ToInt64Sat(ctx, &val, argv[0])) + goto fail; + if (val < 0 || val > 2147483647) { + JS_ThrowRangeError(ctx, "invalid repeat count"); + goto fail; + } + n = val; + p = JS_VALUE_GET_STRING(str); + len = p->len; + if (len == 0 || n == 1) + return str; + if (val * len > JS_STRING_LEN_MAX) { + JS_ThrowRangeError(ctx, "invalid string length"); + goto fail; + } + if (string_buffer_init2(ctx, b, n * len, p->is_wide_char)) + goto fail; + if (len == 1) { + string_buffer_fill(b, string_get(p, 0), n); + } else { + while (n-- > 0) { + string_buffer_concat(b, p, 0, len); + } + } + JS_FreeValue(ctx, str); + return string_buffer_end(b); + +fail: + JS_FreeValue(ctx, str); + return JS_EXCEPTION; +} + +static JSValue js_string_trim(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv, int magic) +{ + JSValue str, ret; + int a, b, len; + JSString *p; + + str = JS_ToStringCheckObject(ctx, this_val); + if (JS_IsException(str)) + return str; + p = JS_VALUE_GET_STRING(str); + a = 0; + b = len = p->len; + if (magic & 1) { + while (a < len && lre_is_space(string_get(p, a))) + a++; + } + if (magic & 2) { + while (b > a && lre_is_space(string_get(p, b - 1))) + b--; + } + ret = js_sub_string(ctx, p, a, b); + JS_FreeValue(ctx, str); + return ret; +} + +/* return 0 if before the first char */ +static int string_prevc(JSString *p, int *pidx) +{ + int idx, c, c1; + + idx = *pidx; + if (idx <= 0) + return 0; + idx--; + if (p->is_wide_char) { + c = p->u.str16[idx]; + if (is_lo_surrogate(c) && idx > 0) { + c1 = p->u.str16[idx - 1]; + if (is_hi_surrogate(c1)) { + c = from_surrogate(c1, c); + idx--; + } + } + } else { + c = p->u.str8[idx]; + } + *pidx = idx; + return c; +} + +static BOOL test_final_sigma(JSString *p, int sigma_pos) +{ + int k, c1; + + /* before C: skip case ignorable chars and check there is + a cased letter */ + k = sigma_pos; + for(;;) { + c1 = string_prevc(p, &k); + if (!lre_is_case_ignorable(c1)) + break; + } + if (!lre_is_cased(c1)) + return FALSE; + + /* after C: skip case ignorable chars and check there is + no cased letter */ + k = sigma_pos + 1; + for(;;) { + if (k >= p->len) + return TRUE; + c1 = string_getc(p, &k); + if (!lre_is_case_ignorable(c1)) + break; + } + return !lre_is_cased(c1); +} + +static int to_utf32_buf(JSContext *ctx, JSString *p, uint32_t **pbuf) +{ + uint32_t *b; + int i, j, n; + + j = -1; + n = p->len; + b = js_malloc(ctx, max_int(1, n) * sizeof(*b)); + if (b) + for (i = j = 0; i < n;) + b[j++] = string_getc(p, &i); + *pbuf = b; + return j; +} + +static JSValue js_string_localeCompare(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + int i, n, an, bn, cmp; + uint32_t *as, *bs, *ts; + JSValue a, b, ret; + + ret = JS_EXCEPTION; + as = NULL; + bs = NULL; + + a = JS_ToStringCheckObject(ctx, this_val); + if (JS_IsException(a)) + return JS_EXCEPTION; + + b = JS_ToString(ctx, argv[0]); + if (JS_IsException(b)) + goto exception; + + an = to_utf32_buf(ctx, JS_VALUE_GET_STRING(a), &as); + if (an == -1) + goto exception; + + bn = to_utf32_buf(ctx, JS_VALUE_GET_STRING(b), &bs); + if (bn == -1) + goto exception; + + // TODO(bnoordhuis) skip normalization when input is latin1 + an = unicode_normalize(&ts, as, an, UNICODE_NFC, ctx, + (DynBufReallocFunc *)js_realloc); + if (an == -1) + goto exception; + js_free(ctx, as); + as = ts; + + // TODO(bnoordhuis) skip normalization when input is latin1 + bn = unicode_normalize(&ts, bs, bn, UNICODE_NFC, ctx, + (DynBufReallocFunc *)js_realloc); + if (bn == -1) + goto exception; + js_free(ctx, bs); + bs = ts; + + n = min_int(an, bn); + for (i = 0; i < n; i++) + if (as[i] != bs[i]) + break; + if (i < n) + cmp = compare_u32(as[i], bs[i]); + else + cmp = compare_u32(an, bn); + ret = js_int32(cmp); + +exception: + JS_FreeValue(ctx, a); + JS_FreeValue(ctx, b); + js_free(ctx, as); + js_free(ctx, bs); + return ret; +} + +static JSValue js_string_toLowerCase(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv, int to_lower) +{ + JSValue val; + StringBuffer b_s, *b = &b_s; + JSString *p; + int i, c, j, l; + uint32_t res[LRE_CC_RES_LEN_MAX]; + + val = JS_ToStringCheckObject(ctx, this_val); + if (JS_IsException(val)) + return val; + p = JS_VALUE_GET_STRING(val); + if (p->len == 0) + return val; + if (string_buffer_init(ctx, b, p->len)) + goto fail; + for(i = 0; i < p->len;) { + c = string_getc(p, &i); + if (c == 0x3a3 && to_lower && test_final_sigma(p, i - 1)) { + res[0] = 0x3c2; /* final sigma */ + l = 1; + } else { + l = lre_case_conv(res, c, to_lower); + } + for(j = 0; j < l; j++) { + if (string_buffer_putc(b, res[j])) + goto fail; + } + } + JS_FreeValue(ctx, val); + return string_buffer_end(b); + fail: + JS_FreeValue(ctx, val); + string_buffer_free(b); + return JS_EXCEPTION; +} + +/* return (-1, NULL) if exception, otherwise (len, buf) */ +static int JS_ToUTF32String(JSContext *ctx, uint32_t **pbuf, JSValue val1) +{ + JSValue val; + int len; + + val = JS_ToString(ctx, val1); + if (JS_IsException(val)) + return -1; + len = to_utf32_buf(ctx, JS_VALUE_GET_STRING(val), pbuf); + JS_FreeValue(ctx, val); + return len; +} + +static JSValue JS_NewUTF32String(JSContext *ctx, const uint32_t *buf, int len) +{ + int i; + StringBuffer b_s, *b = &b_s; + if (string_buffer_init(ctx, b, len)) + return JS_EXCEPTION; + for(i = 0; i < len; i++) { + if (string_buffer_putc(b, buf[i])) + goto fail; + } + return string_buffer_end(b); + fail: + string_buffer_free(b); + return JS_EXCEPTION; +} + +static JSValue js_string_normalize(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + const char *form, *p; + size_t form_len; + int is_compat, buf_len, out_len; + UnicodeNormalizationEnum n_type; + JSValue val; + uint32_t *buf, *out_buf; + + val = JS_ToStringCheckObject(ctx, this_val); + if (JS_IsException(val)) + return val; + buf_len = JS_ToUTF32String(ctx, &buf, val); + JS_FreeValue(ctx, val); + if (buf_len < 0) + return JS_EXCEPTION; + + if (argc == 0 || JS_IsUndefined(argv[0])) { + n_type = UNICODE_NFC; + } else { + form = JS_ToCStringLen(ctx, &form_len, argv[0]); + if (!form) + goto fail1; + p = form; + if (p[0] != 'N' || p[1] != 'F') + goto bad_form; + p += 2; + is_compat = FALSE; + if (*p == 'K') { + is_compat = TRUE; + p++; + } + if (*p == 'C' || *p == 'D') { + n_type = UNICODE_NFC + is_compat * 2 + (*p - 'C'); + if ((p + 1 - form) != form_len) + goto bad_form; + } else { + bad_form: + JS_FreeCString(ctx, form); + JS_ThrowRangeError(ctx, "bad normalization form"); + fail1: + js_free(ctx, buf); + return JS_EXCEPTION; + } + JS_FreeCString(ctx, form); + } + + out_len = unicode_normalize(&out_buf, buf, buf_len, n_type, + ctx->rt, (DynBufReallocFunc *)js_realloc_rt); + js_free(ctx, buf); + if (out_len < 0) + return JS_EXCEPTION; + val = JS_NewUTF32String(ctx, out_buf, out_len); + js_free(ctx, out_buf); + return val; +} + +/* also used for String.prototype.valueOf */ +static JSValue js_string_toString(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + return js_thisStringValue(ctx, this_val); +} + +/* String Iterator */ + +static JSValue js_string_iterator_next(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv, + BOOL *pdone, int magic) +{ + JSArrayIteratorData *it; + uint32_t idx, c, start; + JSString *p; + + it = JS_GetOpaque2(ctx, this_val, JS_CLASS_STRING_ITERATOR); + if (!it) { + *pdone = FALSE; + return JS_EXCEPTION; + } + if (JS_IsUndefined(it->obj)) + goto done; + p = JS_VALUE_GET_STRING(it->obj); + idx = it->idx; + if (idx >= p->len) { + JS_FreeValue(ctx, it->obj); + it->obj = JS_UNDEFINED; + done: + *pdone = TRUE; + return JS_UNDEFINED; + } + + start = idx; + c = string_getc(p, (int *)&idx); + it->idx = idx; + *pdone = FALSE; + if (c <= 0xffff) { + return js_new_string_char(ctx, c); + } else { + return js_new_string16_len(ctx, p->u.str16 + start, 2); + } +} + +/* ES6 Annex B 2.3.2 etc. */ +enum { + magic_string_anchor, + magic_string_big, + magic_string_blink, + magic_string_bold, + magic_string_fixed, + magic_string_fontcolor, + magic_string_fontsize, + magic_string_italics, + magic_string_link, + magic_string_small, + magic_string_strike, + magic_string_sub, + magic_string_sup, +}; + +static JSValue js_string_CreateHTML(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv, int magic) +{ + JSValue str; + const JSString *p; + StringBuffer b_s, *b = &b_s; + static struct { const char *tag, *attr; } const defs[] = { + { "a", "name" }, { "big", NULL }, { "blink", NULL }, { "b", NULL }, + { "tt", NULL }, { "font", "color" }, { "font", "size" }, { "i", NULL }, + { "a", "href" }, { "small", NULL }, { "strike", NULL }, + { "sub", NULL }, { "sup", NULL }, + }; + + str = JS_ToStringCheckObject(ctx, this_val); + if (JS_IsException(str)) + return JS_EXCEPTION; + string_buffer_init(ctx, b, 7); + string_buffer_putc8(b, '<'); + string_buffer_puts8(b, defs[magic].tag); + if (defs[magic].attr) { + // r += " " + attr + "=\"" + value + "\""; + JSValue value; + int i; + + string_buffer_putc8(b, ' '); + string_buffer_puts8(b, defs[magic].attr); + string_buffer_puts8(b, "=\""); + value = JS_ToStringCheckObject(ctx, argv[0]); + if (JS_IsException(value)) { + JS_FreeValue(ctx, str); + string_buffer_free(b); + return JS_EXCEPTION; + } + p = JS_VALUE_GET_STRING(value); + for (i = 0; i < p->len; i++) { + int c = string_get(p, i); + if (c == '"') { + string_buffer_puts8(b, """); + } else { + string_buffer_putc16(b, c); + } + } + JS_FreeValue(ctx, value); + string_buffer_putc8(b, '\"'); + } + // return r + ">" + str + "</" + tag + ">"; + string_buffer_putc8(b, '>'); + string_buffer_concat_value_free(b, str); + string_buffer_puts8(b, "</"); + string_buffer_puts8(b, defs[magic].tag); + string_buffer_putc8(b, '>'); + return string_buffer_end(b); +} + +static const JSCFunctionListEntry js_string_funcs[] = { + JS_CFUNC_DEF("fromCharCode", 1, js_string_fromCharCode ), + JS_CFUNC_DEF("fromCodePoint", 1, js_string_fromCodePoint ), + JS_CFUNC_DEF("raw", 1, js_string_raw ), +}; + +static const JSCFunctionListEntry js_string_proto_funcs[] = { + JS_PROP_INT32_DEF("length", 0, JS_PROP_CONFIGURABLE ), + JS_CFUNC_DEF("at", 1, js_string_at ), + JS_CFUNC_DEF("charCodeAt", 1, js_string_charCodeAt ), + JS_CFUNC_DEF("charAt", 1, js_string_charAt ), + JS_CFUNC_DEF("concat", 1, js_string_concat ), + JS_CFUNC_DEF("codePointAt", 1, js_string_codePointAt ), + JS_CFUNC_DEF("isWellFormed", 0, js_string_isWellFormed ), + JS_CFUNC_DEF("toWellFormed", 0, js_string_toWellFormed ), + JS_CFUNC_MAGIC_DEF("indexOf", 1, js_string_indexOf, 0 ), + JS_CFUNC_MAGIC_DEF("lastIndexOf", 1, js_string_indexOf, 1 ), + JS_CFUNC_MAGIC_DEF("includes", 1, js_string_includes, 0 ), + JS_CFUNC_MAGIC_DEF("endsWith", 1, js_string_includes, 2 ), + JS_CFUNC_MAGIC_DEF("startsWith", 1, js_string_includes, 1 ), + JS_CFUNC_MAGIC_DEF("match", 1, js_string_match, JS_ATOM_Symbol_match ), + JS_CFUNC_MAGIC_DEF("matchAll", 1, js_string_match, JS_ATOM_Symbol_matchAll ), + JS_CFUNC_MAGIC_DEF("search", 1, js_string_match, JS_ATOM_Symbol_search ), + JS_CFUNC_DEF("split", 2, js_string_split ), + JS_CFUNC_DEF("substring", 2, js_string_substring ), + JS_CFUNC_DEF("substr", 2, js_string_substr ), + JS_CFUNC_DEF("slice", 2, js_string_slice ), + JS_CFUNC_DEF("repeat", 1, js_string_repeat ), + JS_CFUNC_MAGIC_DEF("replace", 2, js_string_replace, 0 ), + JS_CFUNC_MAGIC_DEF("replaceAll", 2, js_string_replace, 1 ), + JS_CFUNC_MAGIC_DEF("padEnd", 1, js_string_pad, 1 ), + JS_CFUNC_MAGIC_DEF("padStart", 1, js_string_pad, 0 ), + JS_CFUNC_MAGIC_DEF("trim", 0, js_string_trim, 3 ), + JS_CFUNC_MAGIC_DEF("trimEnd", 0, js_string_trim, 2 ), + JS_ALIAS_DEF("trimRight", "trimEnd" ), + JS_CFUNC_MAGIC_DEF("trimStart", 0, js_string_trim, 1 ), + JS_ALIAS_DEF("trimLeft", "trimStart" ), + JS_CFUNC_DEF("toString", 0, js_string_toString ), + JS_CFUNC_DEF("valueOf", 0, js_string_toString ), + JS_CFUNC_DEF("localeCompare", 1, js_string_localeCompare ), + JS_CFUNC_DEF("normalize", 0, js_string_normalize ), + JS_CFUNC_MAGIC_DEF("toLowerCase", 0, js_string_toLowerCase, 1 ), + JS_CFUNC_MAGIC_DEF("toUpperCase", 0, js_string_toLowerCase, 0 ), + JS_CFUNC_MAGIC_DEF("toLocaleLowerCase", 0, js_string_toLowerCase, 1 ), + JS_CFUNC_MAGIC_DEF("toLocaleUpperCase", 0, js_string_toLowerCase, 0 ), + JS_CFUNC_MAGIC_DEF("[Symbol.iterator]", 0, js_create_array_iterator, JS_ITERATOR_KIND_VALUE | 4 ), + /* ES6 Annex B 2.3.2 etc. */ + JS_CFUNC_MAGIC_DEF("anchor", 1, js_string_CreateHTML, magic_string_anchor ), + JS_CFUNC_MAGIC_DEF("big", 0, js_string_CreateHTML, magic_string_big ), + JS_CFUNC_MAGIC_DEF("blink", 0, js_string_CreateHTML, magic_string_blink ), + JS_CFUNC_MAGIC_DEF("bold", 0, js_string_CreateHTML, magic_string_bold ), + JS_CFUNC_MAGIC_DEF("fixed", 0, js_string_CreateHTML, magic_string_fixed ), + JS_CFUNC_MAGIC_DEF("fontcolor", 1, js_string_CreateHTML, magic_string_fontcolor ), + JS_CFUNC_MAGIC_DEF("fontsize", 1, js_string_CreateHTML, magic_string_fontsize ), + JS_CFUNC_MAGIC_DEF("italics", 0, js_string_CreateHTML, magic_string_italics ), + JS_CFUNC_MAGIC_DEF("link", 1, js_string_CreateHTML, magic_string_link ), + JS_CFUNC_MAGIC_DEF("small", 0, js_string_CreateHTML, magic_string_small ), + JS_CFUNC_MAGIC_DEF("strike", 0, js_string_CreateHTML, magic_string_strike ), + JS_CFUNC_MAGIC_DEF("sub", 0, js_string_CreateHTML, magic_string_sub ), + JS_CFUNC_MAGIC_DEF("sup", 0, js_string_CreateHTML, magic_string_sup ), +}; + +static const JSCFunctionListEntry js_string_iterator_proto_funcs[] = { + JS_ITERATOR_NEXT_DEF("next", 0, js_string_iterator_next, 0 ), + JS_PROP_STRING_DEF("[Symbol.toStringTag]", "String Iterator", JS_PROP_CONFIGURABLE ), +}; + + +/* Math */ + +/* precondition: a and b are not NaN */ +static double js_fmin(double a, double b) +{ + if (a == 0 && b == 0) { + JSFloat64Union a1, b1; + a1.d = a; + b1.d = b; + a1.u64 |= b1.u64; + return a1.d; + } else { + return fmin(a, b); + } +} + +/* precondition: a and b are not NaN */ +static double js_fmax(double a, double b) +{ + if (a == 0 && b == 0) { + JSFloat64Union a1, b1; + a1.d = a; + b1.d = b; + a1.u64 &= b1.u64; + return a1.d; + } else { + return fmax(a, b); + } +} + +static JSValue js_math_min_max(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv, int magic) +{ + BOOL is_max = magic; + double r, a; + int i; + uint32_t tag; + + if (unlikely(argc == 0)) { + return js_float64(is_max ? NEG_INF : INF); + } + + tag = JS_VALUE_GET_TAG(argv[0]); + if (tag == JS_TAG_INT) { + int a1, r1 = JS_VALUE_GET_INT(argv[0]); + for(i = 1; i < argc; i++) { + tag = JS_VALUE_GET_TAG(argv[i]); + if (tag != JS_TAG_INT) { + r = r1; + goto generic_case; + } + a1 = JS_VALUE_GET_INT(argv[i]); + if (is_max) + r1 = max_int(r1, a1); + else + r1 = min_int(r1, a1); + + } + return js_int32(r1); + } else { + if (JS_ToFloat64(ctx, &r, argv[0])) + return JS_EXCEPTION; + i = 1; + generic_case: + while (i < argc) { + if (JS_ToFloat64(ctx, &a, argv[i])) + return JS_EXCEPTION; + if (!isnan(r)) { + if (isnan(a)) { + r = a; + } else { + if (is_max) + r = js_fmax(r, a); + else + r = js_fmin(r, a); + } + } + i++; + } + return js_number(r); + } +} + +static double js_math_sign(double a) +{ + if (isnan(a) || a == 0.0) + return a; + if (a < 0) + return -1; + else + return 1; +} + +static double js_math_round(double a) +{ + JSFloat64Union u; + uint64_t frac_mask, one; + unsigned int e, s; + + u.d = a; + e = (u.u64 >> 52) & 0x7ff; + if (e < 1023) { + /* abs(a) < 1 */ + if (e == (1023 - 1) && u.u64 != 0xbfe0000000000000) { + /* abs(a) > 0.5 or a = 0.5: return +/-1.0 */ + u.u64 = (u.u64 & ((uint64_t)1 << 63)) | ((uint64_t)1023 << 52); + } else { + /* return +/-0.0 */ + u.u64 &= (uint64_t)1 << 63; + } + } else if (e < (1023 + 52)) { + s = u.u64 >> 63; + one = (uint64_t)1 << (52 - (e - 1023)); + frac_mask = one - 1; + u.u64 += (one >> 1) - s; + u.u64 &= ~frac_mask; /* truncate to an integer */ + } + /* otherwise: abs(a) >= 2^52, or NaN, +/-Infinity: no change */ + return u.d; +} + +static JSValue js_math_hypot(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + double r, a; + int i; + + r = 0; + if (argc > 0) { + if (JS_ToFloat64(ctx, &r, argv[0])) + return JS_EXCEPTION; + if (argc == 1) { + r = fabs(r); + } else { + /* use the built-in function to minimize precision loss */ + for (i = 1; i < argc; i++) { + if (JS_ToFloat64(ctx, &a, argv[i])) + return JS_EXCEPTION; + r = hypot(r, a); + } + } + } + return js_float64(r); +} + +static double js_math_f16round(double a) +{ + return fromfp16(tofp16(a)); +} + +static double js_math_fround(double a) +{ + return (float)a; +} + +static JSValue js_math_imul(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + uint32_t a, b, c; + int32_t d; + + if (JS_ToUint32(ctx, &a, argv[0])) + return JS_EXCEPTION; + if (JS_ToUint32(ctx, &b, argv[1])) + return JS_EXCEPTION; + c = a * b; + memcpy(&d, &c, sizeof(d)); + return js_int32(d); +} + +static JSValue js_math_clz32(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + uint32_t a, r; + + if (JS_ToUint32(ctx, &a, argv[0])) + return JS_EXCEPTION; + if (a == 0) + r = 32; + else + r = clz32(a); + return js_int32(r); +} + +/* xorshift* random number generator by Marsaglia */ +static uint64_t xorshift64star(uint64_t *pstate) +{ + uint64_t x; + x = *pstate; + x ^= x >> 12; + x ^= x << 25; + x ^= x >> 27; + *pstate = x; + return x * 0x2545F4914F6CDD1D; +} + +static void js_random_init(JSContext *ctx) +{ + ctx->random_state = js__gettimeofday_us(); + /* the state must be non zero */ + if (ctx->random_state == 0) + ctx->random_state = 1; +} + +static JSValue js_math_random(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + JSFloat64Union u; + uint64_t v; + + v = xorshift64star(&ctx->random_state); + /* 1.0 <= u.d < 2 */ + u.u64 = ((uint64_t)0x3ff << 52) | (v >> 12); + return js_float64(u.d - 1.0); +} + +/* use local wrappers for math functions to + - avoid initializing data with dynamic library entry points. + - avoid some overhead if the call can be inlined at compile or link time. + */ +static double js_math_fabs(double d) { return fabs(d); } +static double js_math_floor(double d) { return floor(d); } +static double js_math_ceil(double d) { return ceil(d); } +static double js_math_sqrt(double d) { return sqrt(d); } +static double js_math_acos(double d) { return acos(d); } +static double js_math_asin(double d) { return asin(d); } +static double js_math_atan(double d) { return atan(d); } +static double js_math_atan2(double a, double b) { return atan2(a, b); } +static double js_math_cos(double d) { return cos(d); } +static double js_math_exp(double d) { return exp(d); } +static double js_math_log(double d) { return log(d); } +static double js_math_sin(double d) { return sin(d); } +static double js_math_tan(double d) { return tan(d); } +static double js_math_trunc(double d) { return trunc(d); } +static double js_math_cosh(double d) { return cosh(d); } +static double js_math_sinh(double d) { return sinh(d); } +static double js_math_tanh(double d) { return tanh(d); } +static double js_math_acosh(double d) { return acosh(d); } +static double js_math_asinh(double d) { return asinh(d); } +static double js_math_atanh(double d) { return atanh(d); } +static double js_math_expm1(double d) { return expm1(d); } +static double js_math_log1p(double d) { return log1p(d); } +static double js_math_log2(double d) { return log2(d); } +static double js_math_log10(double d) { return log10(d); } +static double js_math_cbrt(double d) { return cbrt(d); } + +static const JSCFunctionListEntry js_math_funcs[] = { + JS_CFUNC_MAGIC_DEF("min", 2, js_math_min_max, 0 ), + JS_CFUNC_MAGIC_DEF("max", 2, js_math_min_max, 1 ), + JS_CFUNC_SPECIAL_DEF("abs", 1, f_f, js_math_fabs ), + JS_CFUNC_SPECIAL_DEF("floor", 1, f_f, js_math_floor ), + JS_CFUNC_SPECIAL_DEF("ceil", 1, f_f, js_math_ceil ), + JS_CFUNC_SPECIAL_DEF("round", 1, f_f, js_math_round ), + JS_CFUNC_SPECIAL_DEF("sqrt", 1, f_f, js_math_sqrt ), + + JS_CFUNC_SPECIAL_DEF("acos", 1, f_f, js_math_acos ), + JS_CFUNC_SPECIAL_DEF("asin", 1, f_f, js_math_asin ), + JS_CFUNC_SPECIAL_DEF("atan", 1, f_f, js_math_atan ), + JS_CFUNC_SPECIAL_DEF("atan2", 2, f_f_f, js_math_atan2 ), + JS_CFUNC_SPECIAL_DEF("cos", 1, f_f, js_math_cos ), + JS_CFUNC_SPECIAL_DEF("exp", 1, f_f, js_math_exp ), + JS_CFUNC_SPECIAL_DEF("log", 1, f_f, js_math_log ), + JS_CFUNC_SPECIAL_DEF("pow", 2, f_f_f, js_math_pow ), + JS_CFUNC_SPECIAL_DEF("sin", 1, f_f, js_math_sin ), + JS_CFUNC_SPECIAL_DEF("tan", 1, f_f, js_math_tan ), + /* ES6 */ + JS_CFUNC_SPECIAL_DEF("trunc", 1, f_f, js_math_trunc ), + JS_CFUNC_SPECIAL_DEF("sign", 1, f_f, js_math_sign ), + JS_CFUNC_SPECIAL_DEF("cosh", 1, f_f, js_math_cosh ), + JS_CFUNC_SPECIAL_DEF("sinh", 1, f_f, js_math_sinh ), + JS_CFUNC_SPECIAL_DEF("tanh", 1, f_f, js_math_tanh ), + JS_CFUNC_SPECIAL_DEF("acosh", 1, f_f, js_math_acosh ), + JS_CFUNC_SPECIAL_DEF("asinh", 1, f_f, js_math_asinh ), + JS_CFUNC_SPECIAL_DEF("atanh", 1, f_f, js_math_atanh ), + JS_CFUNC_SPECIAL_DEF("expm1", 1, f_f, js_math_expm1 ), + JS_CFUNC_SPECIAL_DEF("log1p", 1, f_f, js_math_log1p ), + JS_CFUNC_SPECIAL_DEF("log2", 1, f_f, js_math_log2 ), + JS_CFUNC_SPECIAL_DEF("log10", 1, f_f, js_math_log10 ), + JS_CFUNC_SPECIAL_DEF("cbrt", 1, f_f, js_math_cbrt ), + JS_CFUNC_DEF("hypot", 2, js_math_hypot ), + JS_CFUNC_DEF("random", 0, js_math_random ), + JS_CFUNC_SPECIAL_DEF("f16round", 1, f_f, js_math_f16round ), + JS_CFUNC_SPECIAL_DEF("fround", 1, f_f, js_math_fround ), + JS_CFUNC_DEF("imul", 2, js_math_imul ), + JS_CFUNC_DEF("clz32", 1, js_math_clz32 ), + JS_PROP_STRING_DEF("[Symbol.toStringTag]", "Math", JS_PROP_CONFIGURABLE ), + JS_PROP_DOUBLE_DEF("E", 2.718281828459045, 0 ), + JS_PROP_DOUBLE_DEF("LN10", 2.302585092994046, 0 ), + JS_PROP_DOUBLE_DEF("LN2", 0.6931471805599453, 0 ), + JS_PROP_DOUBLE_DEF("LOG2E", 1.4426950408889634, 0 ), + JS_PROP_DOUBLE_DEF("LOG10E", 0.4342944819032518, 0 ), + JS_PROP_DOUBLE_DEF("PI", 3.141592653589793, 0 ), + JS_PROP_DOUBLE_DEF("SQRT1_2", 0.7071067811865476, 0 ), + JS_PROP_DOUBLE_DEF("SQRT2", 1.4142135623730951, 0 ), +}; + +static const JSCFunctionListEntry js_math_obj[] = { + JS_OBJECT_DEF("Math", js_math_funcs, countof(js_math_funcs), JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE ), +}; + +/* Date */ + +/* OS dependent. d = argv[0] is in ms from 1970. Return the difference + between UTC time and local time 'd' in minutes */ +static int getTimezoneOffset(int64_t time) { +#if defined(_WIN32) + TIME_ZONE_INFORMATION time_zone_info; + GetTimeZoneInformation(&time_zone_info); + return (int)time_zone_info.Bias / 60; +#else + time_t ti; + struct tm tm; + + time /= 1000; /* convert to seconds */ + if (sizeof(time_t) == 4) { + /* on 32-bit systems, we need to clamp the time value to the + range of `time_t`. This is better than truncating values to + 32 bits and hopefully provides the same result as 64-bit + implementation of localtime_r. + */ + if ((time_t)-1 < 0) { + if (time < INT32_MIN) { + time = INT32_MIN; + } else if (time > INT32_MAX) { + time = INT32_MAX; + } + } else { + if (time < 0) { + time = 0; + } else if (time > UINT32_MAX) { + time = UINT32_MAX; + } + } + } + ti = time; + localtime_r(&ti, &tm); +#ifdef NO_TM_GMTOFF + struct tm gmt; + gmtime_r(&ti, &gmt); + + /* disable DST adjustment on the local tm struct */ + tm.tm_isdst = 0; + + return (int)difftime(mktime(&gmt), mktime(&tm)) / 60; +#else + return -tm.tm_gmtoff / 60; +#endif /* NO_TM_GMTOFF */ +#endif +} + +/* RegExp */ + +static void js_regexp_finalizer(JSRuntime *rt, JSValue val) +{ + JSObject *p = JS_VALUE_GET_OBJ(val); + JSRegExp *re = &p->u.regexp; + JS_FreeValueRT(rt, JS_MKPTR(JS_TAG_STRING, re->bytecode)); + JS_FreeValueRT(rt, JS_MKPTR(JS_TAG_STRING, re->pattern)); +} + +/* create a string containing the RegExp bytecode */ +static JSValue js_compile_regexp(JSContext *ctx, JSValue pattern, + JSValue flags) +{ + const char *str; + int re_flags, mask; + uint8_t *re_bytecode_buf; + size_t i, len; + int re_bytecode_len; + JSValue ret; + char error_msg[64]; + + re_flags = 0; + if (!JS_IsUndefined(flags)) { + str = JS_ToCStringLen(ctx, &len, flags); + if (!str) + return JS_EXCEPTION; + /* XXX: re_flags = LRE_FLAG_OCTAL unless strict mode? */ + for (i = 0; i < len; i++) { + switch(str[i]) { + case 'd': + mask = LRE_FLAG_INDICES; + break; + case 'g': + mask = LRE_FLAG_GLOBAL; + break; + case 'i': + mask = LRE_FLAG_IGNORECASE; + break; + case 'm': + mask = LRE_FLAG_MULTILINE; + break; + case 's': + mask = LRE_FLAG_DOTALL; + break; + case 'u': + mask = LRE_FLAG_UNICODE; + break; + case 'v': + mask = LRE_FLAG_UNICODE_SETS; + break; + case 'y': + mask = LRE_FLAG_STICKY; + break; + default: + goto bad_flags; + } + if ((re_flags & mask) != 0) { + bad_flags: + JS_FreeCString(ctx, str); + return JS_ThrowSyntaxError(ctx, "invalid regular expression flags"); + } + re_flags |= mask; + } + JS_FreeCString(ctx, str); + } + + if (re_flags & LRE_FLAG_UNICODE) + if (re_flags & LRE_FLAG_UNICODE_SETS) + return JS_ThrowSyntaxError(ctx, "invalid regular expression flags"); + + str = JS_ToCStringLen2(ctx, &len, pattern, !(re_flags & LRE_FLAG_UNICODE)); + if (!str) + return JS_EXCEPTION; + re_bytecode_buf = lre_compile(&re_bytecode_len, error_msg, + sizeof(error_msg), str, len, re_flags, ctx); + JS_FreeCString(ctx, str); + if (!re_bytecode_buf) { + JS_ThrowSyntaxError(ctx, "%s", error_msg); + return JS_EXCEPTION; + } + + ret = js_new_string8_len(ctx, (char *)re_bytecode_buf, re_bytecode_len); + js_free(ctx, re_bytecode_buf); + return ret; +} + +/* create a RegExp object from a string containing the RegExp bytecode + and the source pattern */ +static JSValue js_regexp_constructor_internal(JSContext *ctx, JSValue ctor, + JSValue pattern, JSValue bc) +{ + JSValue obj; + JSObject *p; + JSRegExp *re; + + /* sanity check */ + if (JS_VALUE_GET_TAG(bc) != JS_TAG_STRING || + JS_VALUE_GET_TAG(pattern) != JS_TAG_STRING) { + JS_ThrowTypeError(ctx, "string expected"); + fail: + JS_FreeValue(ctx, bc); + JS_FreeValue(ctx, pattern); + return JS_EXCEPTION; + } + + obj = js_create_from_ctor(ctx, ctor, JS_CLASS_REGEXP); + if (JS_IsException(obj)) + goto fail; + p = JS_VALUE_GET_OBJ(obj); + re = &p->u.regexp; + re->pattern = JS_VALUE_GET_STRING(pattern); + re->bytecode = JS_VALUE_GET_STRING(bc); + JS_DefinePropertyValue(ctx, obj, JS_ATOM_lastIndex, js_int32(0), + JS_PROP_WRITABLE); + return obj; +} + +static JSRegExp *js_get_regexp(JSContext *ctx, JSValue obj, BOOL throw_error) +{ + if (JS_VALUE_GET_TAG(obj) == JS_TAG_OBJECT) { + JSObject *p = JS_VALUE_GET_OBJ(obj); + if (p->class_id == JS_CLASS_REGEXP) + return &p->u.regexp; + } + if (throw_error) { + JS_ThrowTypeErrorInvalidClass(ctx, JS_CLASS_REGEXP); + } + return NULL; +} + +/* return < 0 if exception or TRUE/FALSE */ +static int js_is_regexp(JSContext *ctx, JSValue obj) +{ + JSValue m; + + if (!JS_IsObject(obj)) + return FALSE; + m = JS_GetProperty(ctx, obj, JS_ATOM_Symbol_match); + if (JS_IsException(m)) + return -1; + if (!JS_IsUndefined(m)) + return JS_ToBoolFree(ctx, m); + return js_get_regexp(ctx, obj, FALSE) != NULL; +} + +static JSValue js_regexp_constructor(JSContext *ctx, JSValue new_target, + int argc, JSValue *argv) +{ + JSValue pattern, flags, bc, val; + JSValue pat, flags1; + JSRegExp *re; + int pat_is_regexp; + + pat = argv[0]; + flags1 = argv[1]; + pat_is_regexp = js_is_regexp(ctx, pat); + if (pat_is_regexp < 0) + return JS_EXCEPTION; + if (JS_IsUndefined(new_target)) { + /* called as a function */ + new_target = JS_GetActiveFunction(ctx); + if (pat_is_regexp && JS_IsUndefined(flags1)) { + JSValue ctor; + BOOL res; + ctor = JS_GetProperty(ctx, pat, JS_ATOM_constructor); + if (JS_IsException(ctor)) + return ctor; + res = js_same_value(ctx, ctor, new_target); + JS_FreeValue(ctx, ctor); + if (res) + return js_dup(pat); + } + } + re = js_get_regexp(ctx, pat, FALSE); + if (re) { + pattern = js_dup(JS_MKPTR(JS_TAG_STRING, re->pattern)); + if (JS_IsUndefined(flags1)) { + bc = js_dup(JS_MKPTR(JS_TAG_STRING, re->bytecode)); + goto no_compilation; + } else { + flags = JS_ToString(ctx, flags1); + if (JS_IsException(flags)) + goto fail; + } + } else { + flags = JS_UNDEFINED; + if (pat_is_regexp) { + pattern = JS_GetProperty(ctx, pat, JS_ATOM_source); + if (JS_IsException(pattern)) + goto fail; + if (JS_IsUndefined(flags1)) { + flags = JS_GetProperty(ctx, pat, JS_ATOM_flags); + if (JS_IsException(flags)) + goto fail; + } else { + flags = js_dup(flags1); + } + } else { + pattern = js_dup(pat); + flags = js_dup(flags1); + } + if (JS_IsUndefined(pattern)) { + pattern = JS_AtomToString(ctx, JS_ATOM_empty_string); + } else { + val = pattern; + pattern = JS_ToString(ctx, val); + JS_FreeValue(ctx, val); + if (JS_IsException(pattern)) + goto fail; + } + } + bc = js_compile_regexp(ctx, pattern, flags); + if (JS_IsException(bc)) + goto fail; + JS_FreeValue(ctx, flags); + no_compilation: + return js_regexp_constructor_internal(ctx, new_target, pattern, bc); + fail: + JS_FreeValue(ctx, pattern); + JS_FreeValue(ctx, flags); + return JS_EXCEPTION; +} + +static JSValue js_regexp_compile(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + JSRegExp *re1, *re; + JSValue pattern1, flags1; + JSValue bc, pattern; + + re = js_get_regexp(ctx, this_val, TRUE); + if (!re) + return JS_EXCEPTION; + pattern1 = argv[0]; + flags1 = argv[1]; + re1 = js_get_regexp(ctx, pattern1, FALSE); + if (re1) { + if (!JS_IsUndefined(flags1)) + return JS_ThrowTypeError(ctx, "flags must be undefined"); + pattern = js_dup(JS_MKPTR(JS_TAG_STRING, re1->pattern)); + bc = js_dup(JS_MKPTR(JS_TAG_STRING, re1->bytecode)); + } else { + bc = JS_UNDEFINED; + if (JS_IsUndefined(pattern1)) + pattern = JS_AtomToString(ctx, JS_ATOM_empty_string); + else + pattern = JS_ToString(ctx, pattern1); + if (JS_IsException(pattern)) + goto fail; + bc = js_compile_regexp(ctx, pattern, flags1); + if (JS_IsException(bc)) + goto fail; + } + JS_FreeValue(ctx, JS_MKPTR(JS_TAG_STRING, re->pattern)); + JS_FreeValue(ctx, JS_MKPTR(JS_TAG_STRING, re->bytecode)); + re->pattern = JS_VALUE_GET_STRING(pattern); + re->bytecode = JS_VALUE_GET_STRING(bc); + if (JS_SetProperty(ctx, this_val, JS_ATOM_lastIndex, + js_int32(0)) < 0) + return JS_EXCEPTION; + return js_dup(this_val); + fail: + JS_FreeValue(ctx, pattern); + JS_FreeValue(ctx, bc); + return JS_EXCEPTION; +} + +static JSValue js_regexp_get_source(JSContext *ctx, JSValue this_val) +{ + JSRegExp *re; + JSString *p; + StringBuffer b_s, *b = &b_s; + int i, n, c, c2, bra; + + if (JS_VALUE_GET_TAG(this_val) != JS_TAG_OBJECT) + return JS_ThrowTypeErrorNotAnObject(ctx); + + if (js_same_value(ctx, this_val, ctx->class_proto[JS_CLASS_REGEXP])) + goto empty_regex; + + re = js_get_regexp(ctx, this_val, TRUE); + if (!re) + return JS_EXCEPTION; + + p = re->pattern; + + if (p->len == 0) { + empty_regex: + return js_new_string8(ctx, "(?:)"); + } + string_buffer_init2(ctx, b, p->len, p->is_wide_char); + + /* Escape '/' and newline sequences as needed */ + bra = 0; + for (i = 0, n = p->len; i < n;) { + c2 = -1; + switch (c = string_get(p, i++)) { + case '\\': + if (i < n) + c2 = string_get(p, i++); + break; + case ']': + bra = 0; + break; + case '[': + if (!bra) { + if (i < n && string_get(p, i) == ']') + c2 = string_get(p, i++); + bra = 1; + } + break; + case '\n': + c = '\\'; + c2 = 'n'; + break; + case '\r': + c = '\\'; + c2 = 'r'; + break; + case '/': + if (!bra) { + c = '\\'; + c2 = '/'; + } + break; + } + string_buffer_putc16(b, c); + if (c2 >= 0) + string_buffer_putc16(b, c2); + } + return string_buffer_end(b); +} + +static JSValue js_regexp_get_flag(JSContext *ctx, JSValue this_val, int mask) +{ + JSRegExp *re; + int flags; + + if (JS_VALUE_GET_TAG(this_val) != JS_TAG_OBJECT) + return JS_ThrowTypeErrorNotAnObject(ctx); + + re = js_get_regexp(ctx, this_val, FALSE); + if (!re) { + if (js_same_value(ctx, this_val, ctx->class_proto[JS_CLASS_REGEXP])) + return JS_UNDEFINED; + else + return JS_ThrowTypeErrorInvalidClass(ctx, JS_CLASS_REGEXP); + } + + flags = lre_get_flags(re->bytecode->u.str8); + return js_bool(flags & mask); +} + +static JSValue js_regexp_get_flags(JSContext *ctx, JSValue this_val) +{ + char str[8], *p = str; + int res; + + if (JS_VALUE_GET_TAG(this_val) != JS_TAG_OBJECT) + return JS_ThrowTypeErrorNotAnObject(ctx); + + res = JS_ToBoolFree(ctx, JS_GetPropertyStr(ctx, this_val, "hasIndices")); + if (res < 0) + goto exception; + if (res) + *p++ = 'd'; + res = JS_ToBoolFree(ctx, JS_GetProperty(ctx, this_val, JS_ATOM_global)); + if (res < 0) + goto exception; + if (res) + *p++ = 'g'; + res = JS_ToBoolFree(ctx, JS_GetPropertyStr(ctx, this_val, "ignoreCase")); + if (res < 0) + goto exception; + if (res) + *p++ = 'i'; + res = JS_ToBoolFree(ctx, JS_GetPropertyStr(ctx, this_val, "multiline")); + if (res < 0) + goto exception; + if (res) + *p++ = 'm'; + res = JS_ToBoolFree(ctx, JS_GetPropertyStr(ctx, this_val, "dotAll")); + if (res < 0) + goto exception; + if (res) + *p++ = 's'; + res = JS_ToBoolFree(ctx, JS_GetProperty(ctx, this_val, JS_ATOM_unicode)); + if (res < 0) + goto exception; + if (res) + *p++ = 'u'; + res = JS_ToBoolFree(ctx, JS_GetPropertyStr(ctx, this_val, "unicodeSets")); + if (res < 0) + goto exception; + if (res) + *p++ = 'v'; + res = JS_ToBoolFree(ctx, JS_GetPropertyStr(ctx, this_val, "sticky")); + if (res < 0) + goto exception; + if (res) + *p++ = 'y'; + if (p == str) + return JS_AtomToString(ctx, JS_ATOM_empty_string); + return js_new_string8_len(ctx, str, p - str); + +exception: + return JS_EXCEPTION; +} + +static JSValue js_regexp_toString(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + JSValue pattern, flags; + StringBuffer b_s, *b = &b_s; + + if (!JS_IsObject(this_val)) + return JS_ThrowTypeErrorNotAnObject(ctx); + + string_buffer_init(ctx, b, 0); + string_buffer_putc8(b, '/'); + pattern = JS_GetProperty(ctx, this_val, JS_ATOM_source); + if (string_buffer_concat_value_free(b, pattern)) + goto fail; + string_buffer_putc8(b, '/'); + flags = JS_GetProperty(ctx, this_val, JS_ATOM_flags); + if (string_buffer_concat_value_free(b, flags)) + goto fail; + return string_buffer_end(b); + +fail: + string_buffer_free(b); + return JS_EXCEPTION; +} + +BOOL lre_check_stack_overflow(void *opaque, size_t alloca_size) +{ + JSContext *ctx = opaque; + return js_check_stack_overflow(ctx->rt, alloca_size); +} + +void *lre_realloc(void *opaque, void *ptr, size_t size) +{ + JSContext *ctx = opaque; + /* No JS exception is raised here */ + return js_realloc_rt(ctx->rt, ptr, size); +} + +static JSValue js_regexp_escape(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + StringBuffer b_s, *b = &b_s; + JSString *p; + uint32_t c, i; + char s[16]; + + if (!JS_IsString(argv[0])) + return JS_ThrowTypeError(ctx, "not a string"); + p = JS_VALUE_GET_STRING(argv[0]); + string_buffer_init2(ctx, b, 0, p->is_wide_char); + for (i = 0; i < p->len; i++) { + c = p->is_wide_char ? (uint32_t)p->u.str16[i] : (uint32_t)p->u.str8[i]; + if (c < 33) { + if (c >= 9 && c <= 13) { + string_buffer_putc8(b, '\\'); + string_buffer_putc8(b, "tnvfr"[c - 9]); + } else { + goto hex2; + } + } else if (c < 128) { + if ((c >= '0' && c <= '9') + || (c >= 'A' && c <= 'Z') + || (c >= 'a' && c <= 'z')) { + if (i == 0) + goto hex2; + } else if (strchr(",-=<>#&!%:;@~'`\"", c)) { + goto hex2; + } else if (c != '_') { + string_buffer_putc8(b, '\\'); + } + string_buffer_putc8(b, c); + } else if (c < 256) { + hex2: + snprintf(s, sizeof(s), "\\x%02x", c); + string_buffer_puts8(b, s); + } else if (is_surrogate(c) || lre_is_white_space(c) || c == 0xFEFF) { + snprintf(s, sizeof(s), "\\u%04x", c); + string_buffer_puts8(b, s); + } else { + string_buffer_putc16(b, c); + } + } + return string_buffer_end(b); +} + +static JSValue js_regexp_exec(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + JSRegExp *re = js_get_regexp(ctx, this_val, TRUE); + JSString *str; + JSValue t, ret, str_val, obj, val, groups; + JSValue indices, indices_groups; + uint8_t *re_bytecode; + uint8_t **capture, *str_buf; + int rc, capture_count, shift, i, re_flags; + int64_t last_index; + const char *group_name_ptr; + + if (!re) + return JS_EXCEPTION; + + str_val = JS_ToString(ctx, argv[0]); + if (JS_IsException(str_val)) + return JS_EXCEPTION; + + ret = JS_EXCEPTION; + obj = JS_NULL; + groups = JS_UNDEFINED; + indices = JS_UNDEFINED; + indices_groups = JS_UNDEFINED; + capture = NULL; + + val = JS_GetProperty(ctx, this_val, JS_ATOM_lastIndex); + if (JS_IsException(val) || JS_ToLengthFree(ctx, &last_index, val)) + goto fail; + + re_bytecode = re->bytecode->u.str8; + re_flags = lre_get_flags(re_bytecode); + if ((re_flags & (LRE_FLAG_GLOBAL | LRE_FLAG_STICKY)) == 0) { + last_index = 0; + } + str = JS_VALUE_GET_STRING(str_val); + capture_count = lre_get_capture_count(re_bytecode); + if (capture_count > 0) { + capture = js_malloc(ctx, sizeof(capture[0]) * capture_count * 2); + if (!capture) + goto fail; + } + shift = str->is_wide_char; + str_buf = str->u.str8; + if (last_index > str->len) { + rc = 2; + } else { + rc = lre_exec(capture, re_bytecode, + str_buf, last_index, str->len, + shift, ctx); + } + if (rc != 1) { + if (rc >= 0) { + if (rc == 2 || (re_flags & (LRE_FLAG_GLOBAL | LRE_FLAG_STICKY))) { + if (JS_SetProperty(ctx, this_val, JS_ATOM_lastIndex, + js_int32(0)) < 0) + goto fail; + } + } else { + JS_ThrowInternalError(ctx, "out of memory in regexp execution"); + goto fail; + } + } else { + int prop_flags; + if (re_flags & (LRE_FLAG_GLOBAL | LRE_FLAG_STICKY)) { + if (JS_SetProperty(ctx, this_val, JS_ATOM_lastIndex, + js_int32((capture[1] - str_buf) >> shift)) < 0) + goto fail; + } + obj = JS_NewArray(ctx); + if (JS_IsException(obj)) + goto fail; + prop_flags = JS_PROP_C_W_E | JS_PROP_THROW; + group_name_ptr = lre_get_groupnames(re_bytecode); + if (group_name_ptr) { + groups = JS_NewObjectProto(ctx, JS_NULL); + if (JS_IsException(groups)) + goto fail; + } + if (re_flags & LRE_FLAG_INDICES) { + indices = JS_NewArray(ctx); + if (JS_IsException(indices)) + goto fail; + if (group_name_ptr) { + indices_groups = JS_NewObjectProto(ctx, JS_NULL); + if (JS_IsException(indices_groups)) + goto fail; + } + } + + for(i = 0; i < capture_count; i++) { + const char *name = NULL; + uint8_t **match = &capture[2 * i]; + int start = -1; + int end = -1; + + if (group_name_ptr && i > 0) { + if (*group_name_ptr) name = group_name_ptr; + group_name_ptr += strlen(group_name_ptr) + 1; + } + + if (match[0] && match[1]) { + start = (match[0] - str_buf) >> shift; + end = (match[1] - str_buf) >> shift; + } + + if (!JS_IsUndefined(indices)) { + JSValue val = JS_UNDEFINED; + if (start != -1) { + val = JS_NewArray(ctx); + if (JS_IsException(val)) + goto fail; + if (JS_DefinePropertyValueUint32(ctx, val, 0, + js_int32(start), + prop_flags) < 0) { + JS_FreeValue(ctx, val); + goto fail; + } + if (JS_DefinePropertyValueUint32(ctx, val, 1, + js_int32(end), + prop_flags) < 0) { + JS_FreeValue(ctx, val); + goto fail; + } + } + if (name && !JS_IsUndefined(indices_groups)) { + val = js_dup(val); + if (JS_DefinePropertyValueStr(ctx, indices_groups, + name, val, prop_flags) < 0) { + JS_FreeValue(ctx, val); + goto fail; + } + } + if (JS_DefinePropertyValueUint32(ctx, indices, i, val, + prop_flags) < 0) { + goto fail; + } + } + + JSValue val = JS_UNDEFINED; + if (start != -1) { + val = js_sub_string(ctx, str, start, end); + if (JS_IsException(val)) + goto fail; + } + + if (name) { + if (JS_DefinePropertyValueStr(ctx, groups, name, + js_dup(val), + prop_flags) < 0) { + JS_FreeValue(ctx, val); + goto fail; + } + } + + if (JS_DefinePropertyValueUint32(ctx, obj, i, val, prop_flags) < 0) + goto fail; + } + + t = groups, groups = JS_UNDEFINED; + if (JS_DefinePropertyValue(ctx, obj, JS_ATOM_groups, + t, prop_flags) < 0) { + goto fail; + } + + t = js_int32((capture[0] - str_buf) >> shift); + if (JS_DefinePropertyValue(ctx, obj, JS_ATOM_index, t, prop_flags) < 0) + goto fail; + + t = str_val, str_val = JS_UNDEFINED; + if (JS_DefinePropertyValue(ctx, obj, JS_ATOM_input, t, prop_flags) < 0) + goto fail; + + if (!JS_IsUndefined(indices)) { + t = indices_groups, indices_groups = JS_UNDEFINED; + if (JS_DefinePropertyValue(ctx, indices, JS_ATOM_groups, + t, prop_flags) < 0) { + goto fail; + } + t = indices, indices = JS_UNDEFINED; + if (JS_DefinePropertyValue(ctx, obj, JS_ATOM_indices, + t, prop_flags) < 0) { + goto fail; + } + } + } + ret = obj; + obj = JS_UNDEFINED; +fail: + JS_FreeValue(ctx, indices_groups); + JS_FreeValue(ctx, indices); + JS_FreeValue(ctx, str_val); + JS_FreeValue(ctx, groups); + JS_FreeValue(ctx, obj); + js_free(ctx, capture); + return ret; +} + +/* delete portions of a string that match a given regex */ +static JSValue JS_RegExpDelete(JSContext *ctx, JSValue this_val, JSValue arg) +{ + JSRegExp *re = js_get_regexp(ctx, this_val, TRUE); + JSString *str; + JSValue str_val, val; + uint8_t *re_bytecode; + int ret; + uint8_t **capture, *str_buf; + int capture_count, shift, re_flags; + int next_src_pos, start, end; + int64_t last_index; + StringBuffer b_s, *b = &b_s; + + if (!re) + return JS_EXCEPTION; + + string_buffer_init(ctx, b, 0); + + capture = NULL; + str_val = JS_ToString(ctx, arg); + if (JS_IsException(str_val)) + goto fail; + str = JS_VALUE_GET_STRING(str_val); + re_bytecode = re->bytecode->u.str8; + re_flags = lre_get_flags(re_bytecode); + if ((re_flags & (LRE_FLAG_GLOBAL | LRE_FLAG_STICKY)) == 0) { + last_index = 0; + } else { + val = JS_GetProperty(ctx, this_val, JS_ATOM_lastIndex); + if (JS_IsException(val) || JS_ToLengthFree(ctx, &last_index, val)) + goto fail; + } + capture_count = lre_get_capture_count(re_bytecode); + if (capture_count > 0) { + capture = js_malloc(ctx, sizeof(capture[0]) * capture_count * 2); + if (!capture) + goto fail; + } + shift = str->is_wide_char; + str_buf = str->u.str8; + next_src_pos = 0; + for (;;) { + if (last_index > str->len) + break; + + ret = lre_exec(capture, re_bytecode, + str_buf, last_index, str->len, shift, ctx); + if (ret != 1) { + if (ret >= 0) { + if (ret == 2 || (re_flags & (LRE_FLAG_GLOBAL | LRE_FLAG_STICKY))) { + if (JS_SetProperty(ctx, this_val, JS_ATOM_lastIndex, + js_int32(0)) < 0) + goto fail; + } + } else { + JS_ThrowInternalError(ctx, "out of memory in regexp execution"); + goto fail; + } + break; + } + start = (capture[0] - str_buf) >> shift; + end = (capture[1] - str_buf) >> shift; + last_index = end; + if (next_src_pos < start) { + if (string_buffer_concat(b, str, next_src_pos, start)) + goto fail; + } + next_src_pos = end; + if (!(re_flags & LRE_FLAG_GLOBAL)) { + if (JS_SetProperty(ctx, this_val, JS_ATOM_lastIndex, + js_int32(end)) < 0) + goto fail; + break; + } + if (end == start) { + if (!(re_flags & LRE_FLAG_UNICODE) || (unsigned)end >= str->len || !str->is_wide_char) { + end++; + } else { + string_getc(str, &end); + } + } + last_index = end; + } + if (string_buffer_concat(b, str, next_src_pos, str->len)) + goto fail; + JS_FreeValue(ctx, str_val); + js_free(ctx, capture); + return string_buffer_end(b); +fail: + JS_FreeValue(ctx, str_val); + js_free(ctx, capture); + string_buffer_free(b); + return JS_EXCEPTION; +} + +static JSValue JS_RegExpExec(JSContext *ctx, JSValue r, JSValue s) +{ + JSValue method, ret; + + method = JS_GetProperty(ctx, r, JS_ATOM_exec); + if (JS_IsException(method)) + return method; + if (JS_IsFunction(ctx, method)) { + ret = JS_CallFree(ctx, method, r, 1, &s); + if (JS_IsException(ret)) + return ret; + if (!JS_IsObject(ret) && !JS_IsNull(ret)) { + JS_FreeValue(ctx, ret); + return JS_ThrowTypeError(ctx, "RegExp exec method must return an object or null"); + } + return ret; + } + JS_FreeValue(ctx, method); + return js_regexp_exec(ctx, r, 1, &s); +} + +static JSValue js_regexp_test(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + JSValue val; + BOOL ret; + + val = JS_RegExpExec(ctx, this_val, argv[0]); + if (JS_IsException(val)) + return JS_EXCEPTION; + ret = !JS_IsNull(val); + JS_FreeValue(ctx, val); + return js_bool(ret); +} + +static JSValue js_regexp_Symbol_match(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + // [Symbol.match](str) + JSValue rx = this_val; + JSValue A, S, flags, result, matchStr; + int global, n, fullUnicode, isEmpty; + JSString *p; + + if (!JS_IsObject(rx)) + return JS_ThrowTypeErrorNotAnObject(ctx); + + A = JS_UNDEFINED; + flags = JS_UNDEFINED; + result = JS_UNDEFINED; + matchStr = JS_UNDEFINED; + S = JS_ToString(ctx, argv[0]); + if (JS_IsException(S)) + goto exception; + + flags = JS_GetProperty(ctx, rx, JS_ATOM_flags); + if (JS_IsException(flags)) + goto exception; + flags = JS_ToStringFree(ctx, flags); + if (JS_IsException(flags)) + goto exception; + p = JS_VALUE_GET_STRING(flags); + + // TODO(bnoordhuis) query 'u' flag the same way? + global = (-1 != string_indexof_char(p, 'g', 0)); + if (!global) { + A = JS_RegExpExec(ctx, rx, S); + } else { + fullUnicode = JS_ToBoolFree(ctx, JS_GetProperty(ctx, rx, JS_ATOM_unicode)); + if (fullUnicode < 0) + goto exception; + + if (JS_SetProperty(ctx, rx, JS_ATOM_lastIndex, js_int32(0)) < 0) + goto exception; + A = JS_NewArray(ctx); + if (JS_IsException(A)) + goto exception; + n = 0; + for(;;) { + JS_FreeValue(ctx, result); + result = JS_RegExpExec(ctx, rx, S); + if (JS_IsException(result)) + goto exception; + if (JS_IsNull(result)) + break; + matchStr = JS_ToStringFree(ctx, JS_GetPropertyInt64(ctx, result, 0)); + if (JS_IsException(matchStr)) + goto exception; + isEmpty = JS_IsEmptyString(matchStr); + if (JS_SetPropertyInt64(ctx, A, n++, matchStr) < 0) + goto exception; + if (isEmpty) { + int64_t thisIndex, nextIndex; + if (JS_ToLengthFree(ctx, &thisIndex, + JS_GetProperty(ctx, rx, JS_ATOM_lastIndex)) < 0) + goto exception; + p = JS_VALUE_GET_STRING(S); + nextIndex = string_advance_index(p, thisIndex, fullUnicode); + if (JS_SetProperty(ctx, rx, JS_ATOM_lastIndex, js_int64(nextIndex)) < 0) + goto exception; + } + } + if (n == 0) { + JS_FreeValue(ctx, A); + A = JS_NULL; + } + } + JS_FreeValue(ctx, result); + JS_FreeValue(ctx, flags); + JS_FreeValue(ctx, S); + return A; + +exception: + JS_FreeValue(ctx, A); + JS_FreeValue(ctx, result); + JS_FreeValue(ctx, flags); + JS_FreeValue(ctx, S); + return JS_EXCEPTION; +} + +typedef struct JSRegExpStringIteratorData { + JSValue iterating_regexp; + JSValue iterated_string; + BOOL global; + BOOL unicode; + BOOL done; +} JSRegExpStringIteratorData; + +static void js_regexp_string_iterator_finalizer(JSRuntime *rt, JSValue val) +{ + JSObject *p = JS_VALUE_GET_OBJ(val); + JSRegExpStringIteratorData *it = p->u.regexp_string_iterator_data; + if (it) { + JS_FreeValueRT(rt, it->iterating_regexp); + JS_FreeValueRT(rt, it->iterated_string); + js_free_rt(rt, it); + } +} + +static void js_regexp_string_iterator_mark(JSRuntime *rt, JSValue val, + JS_MarkFunc *mark_func) +{ + JSObject *p = JS_VALUE_GET_OBJ(val); + JSRegExpStringIteratorData *it = p->u.regexp_string_iterator_data; + if (it) { + JS_MarkValue(rt, it->iterating_regexp, mark_func); + JS_MarkValue(rt, it->iterated_string, mark_func); + } +} + +static JSValue js_regexp_string_iterator_next(JSContext *ctx, + JSValue this_val, + int argc, JSValue *argv, + BOOL *pdone, int magic) +{ + JSRegExpStringIteratorData *it; + JSValue R, S; + JSValue matchStr = JS_UNDEFINED, match = JS_UNDEFINED; + JSString *sp; + + it = JS_GetOpaque2(ctx, this_val, JS_CLASS_REGEXP_STRING_ITERATOR); + if (!it) + goto exception; + if (it->done) { + *pdone = TRUE; + return JS_UNDEFINED; + } + R = it->iterating_regexp; + S = it->iterated_string; + match = JS_RegExpExec(ctx, R, S); + if (JS_IsException(match)) + goto exception; + if (JS_IsNull(match)) { + it->done = TRUE; + *pdone = TRUE; + return JS_UNDEFINED; + } else if (it->global) { + matchStr = JS_ToStringFree(ctx, JS_GetPropertyInt64(ctx, match, 0)); + if (JS_IsException(matchStr)) + goto exception; + if (JS_IsEmptyString(matchStr)) { + int64_t thisIndex, nextIndex; + if (JS_ToLengthFree(ctx, &thisIndex, + JS_GetProperty(ctx, R, JS_ATOM_lastIndex)) < 0) + goto exception; + sp = JS_VALUE_GET_STRING(S); + nextIndex = string_advance_index(sp, thisIndex, it->unicode); + if (JS_SetProperty(ctx, R, JS_ATOM_lastIndex, js_int64(nextIndex)) < 0) + goto exception; + } + JS_FreeValue(ctx, matchStr); + } else { + it->done = TRUE; + } + *pdone = FALSE; + return match; + exception: + JS_FreeValue(ctx, match); + JS_FreeValue(ctx, matchStr); + *pdone = FALSE; + return JS_EXCEPTION; +} + +static JSValue js_regexp_Symbol_matchAll(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + // [Symbol.matchAll](str) + JSValue R = this_val; + JSValue S, C, flags, matcher, iter; + JSValue args[2]; + JSString *strp; + int64_t lastIndex; + JSRegExpStringIteratorData *it; + + if (!JS_IsObject(R)) + return JS_ThrowTypeErrorNotAnObject(ctx); + + C = JS_UNDEFINED; + flags = JS_UNDEFINED; + matcher = JS_UNDEFINED; + iter = JS_UNDEFINED; + + S = JS_ToString(ctx, argv[0]); + if (JS_IsException(S)) + goto exception; + C = JS_SpeciesConstructor(ctx, R, ctx->regexp_ctor); + if (JS_IsException(C)) + goto exception; + flags = JS_ToStringFree(ctx, JS_GetProperty(ctx, R, JS_ATOM_flags)); + if (JS_IsException(flags)) + goto exception; + args[0] = R; + args[1] = flags; + matcher = JS_CallConstructor(ctx, C, 2, args); + if (JS_IsException(matcher)) + goto exception; + if (JS_ToLengthFree(ctx, &lastIndex, + JS_GetProperty(ctx, R, JS_ATOM_lastIndex))) + goto exception; + if (JS_SetProperty(ctx, matcher, JS_ATOM_lastIndex, js_int64(lastIndex)) < 0) + goto exception; + + iter = JS_NewObjectClass(ctx, JS_CLASS_REGEXP_STRING_ITERATOR); + if (JS_IsException(iter)) + goto exception; + it = js_malloc(ctx, sizeof(*it)); + if (!it) + goto exception; + it->iterating_regexp = matcher; + it->iterated_string = S; + strp = JS_VALUE_GET_STRING(flags); + it->global = string_indexof_char(strp, 'g', 0) >= 0; + it->unicode = string_indexof_char(strp, 'u', 0) >= 0; + it->done = FALSE; + JS_SetOpaqueInternal(iter, it); + + JS_FreeValue(ctx, C); + JS_FreeValue(ctx, flags); + return iter; + exception: + JS_FreeValue(ctx, S); + JS_FreeValue(ctx, C); + JS_FreeValue(ctx, flags); + JS_FreeValue(ctx, matcher); + JS_FreeValue(ctx, iter); + return JS_EXCEPTION; +} + +typedef struct ValueBuffer { + JSContext *ctx; + JSValue *arr; + JSValue def[4]; + int len; + int size; + int error_status; +} ValueBuffer; + +static int value_buffer_init(JSContext *ctx, ValueBuffer *b) +{ + b->ctx = ctx; + b->len = 0; + b->size = 4; + b->error_status = 0; + b->arr = b->def; + return 0; +} + +static void value_buffer_free(ValueBuffer *b) +{ + while (b->len > 0) + JS_FreeValue(b->ctx, b->arr[--b->len]); + if (b->arr != b->def) + js_free(b->ctx, b->arr); + b->arr = b->def; + b->size = 4; +} + +static int value_buffer_append(ValueBuffer *b, JSValue val) +{ + if (b->error_status) + return -1; + + if (b->len >= b->size) { + int new_size = (b->len + (b->len >> 1) + 31) & ~16; + size_t slack; + JSValue *new_arr; + + if (b->arr == b->def) { + new_arr = js_realloc2(b->ctx, NULL, sizeof(*b->arr) * new_size, &slack); + if (new_arr) + memcpy(new_arr, b->def, sizeof b->def); + } else { + new_arr = js_realloc2(b->ctx, b->arr, sizeof(*b->arr) * new_size, &slack); + } + if (!new_arr) { + value_buffer_free(b); + JS_FreeValue(b->ctx, val); + b->error_status = -1; + return -1; + } + new_size += slack / sizeof(*new_arr); + b->arr = new_arr; + b->size = new_size; + } + b->arr[b->len++] = val; + return 0; +} + +static int js_is_standard_regexp(JSContext *ctx, JSValue rx) +{ + JSValue val; + int res; + + val = JS_GetProperty(ctx, rx, JS_ATOM_constructor); + if (JS_IsException(val)) + return -1; + // rx.constructor === RegExp + res = js_same_value(ctx, val, ctx->regexp_ctor); + JS_FreeValue(ctx, val); + if (res) { + val = JS_GetProperty(ctx, rx, JS_ATOM_exec); + if (JS_IsException(val)) + return -1; + // rx.exec === RE_exec + res = JS_IsCFunction(ctx, val, js_regexp_exec, 0); + JS_FreeValue(ctx, val); + } + return res; +} + +static JSValue js_regexp_Symbol_replace(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + // [Symbol.replace](str, rep) + JSValue rx = this_val, rep = argv[1]; + JSValue args[6]; + JSValue flags, str, rep_val, matched, tab, rep_str, namedCaptures, res; + JSString *p, *sp, *rp; + StringBuffer b_s, *b = &b_s; + ValueBuffer v_b, *results = &v_b; + int nextSourcePosition, n, j, functionalReplace, is_global, fullUnicode; + uint32_t nCaptures; + int64_t position; + + if (!JS_IsObject(rx)) + return JS_ThrowTypeErrorNotAnObject(ctx); + + string_buffer_init(ctx, b, 0); + value_buffer_init(ctx, results); + + rep_val = JS_UNDEFINED; + matched = JS_UNDEFINED; + tab = JS_UNDEFINED; + flags = JS_UNDEFINED; + rep_str = JS_UNDEFINED; + namedCaptures = JS_UNDEFINED; + + str = JS_ToString(ctx, argv[0]); + if (JS_IsException(str)) + goto exception; + + sp = JS_VALUE_GET_STRING(str); + rp = NULL; + functionalReplace = JS_IsFunction(ctx, rep); + if (!functionalReplace) { + rep_val = JS_ToString(ctx, rep); + if (JS_IsException(rep_val)) + goto exception; + rp = JS_VALUE_GET_STRING(rep_val); + } + + flags = JS_GetProperty(ctx, rx, JS_ATOM_flags); + if (JS_IsException(flags)) + goto exception; + flags = JS_ToStringFree(ctx, flags); + if (JS_IsException(flags)) + goto exception; + p = JS_VALUE_GET_STRING(flags); + + // TODO(bnoordhuis) query 'u' flag the same way? + fullUnicode = 0; + is_global = (-1 != string_indexof_char(p, 'g', 0)); + if (is_global) { + fullUnicode = JS_ToBoolFree(ctx, JS_GetProperty(ctx, rx, JS_ATOM_unicode)); + if (fullUnicode < 0) + goto exception; + if (JS_SetProperty(ctx, rx, JS_ATOM_lastIndex, js_int32(0)) < 0) + goto exception; + } + + if (rp && rp->len == 0 && is_global && js_is_standard_regexp(ctx, rx)) { + /* use faster version for simple cases */ + res = JS_RegExpDelete(ctx, rx, str); + goto done; + } + for(;;) { + JSValue result; + result = JS_RegExpExec(ctx, rx, str); + if (JS_IsException(result)) + goto exception; + if (JS_IsNull(result)) + break; + if (value_buffer_append(results, result) < 0) + goto exception; + if (!is_global) + break; + JS_FreeValue(ctx, matched); + matched = JS_ToStringFree(ctx, JS_GetPropertyInt64(ctx, result, 0)); + if (JS_IsException(matched)) + goto exception; + if (JS_IsEmptyString(matched)) { + /* always advance of at least one char */ + int64_t thisIndex, nextIndex; + if (JS_ToLengthFree(ctx, &thisIndex, JS_GetProperty(ctx, rx, JS_ATOM_lastIndex)) < 0) + goto exception; + nextIndex = string_advance_index(sp, thisIndex, fullUnicode); + if (JS_SetProperty(ctx, rx, JS_ATOM_lastIndex, js_int64(nextIndex)) < 0) + goto exception; + } + } + nextSourcePosition = 0; + for(j = 0; j < results->len; j++) { + JSValue result; + result = results->arr[j]; + if (js_get_length32(ctx, &nCaptures, result) < 0) + goto exception; + JS_FreeValue(ctx, matched); + matched = JS_ToStringFree(ctx, JS_GetPropertyInt64(ctx, result, 0)); + if (JS_IsException(matched)) + goto exception; + if (JS_ToLengthFree(ctx, &position, JS_GetProperty(ctx, result, JS_ATOM_index))) + goto exception; + if (position > sp->len) + position = sp->len; + else if (position < 0) + position = 0; + /* ignore substition if going backward (can happen + with custom regexp object) */ + JS_FreeValue(ctx, tab); + tab = JS_NewArray(ctx); + if (JS_IsException(tab)) + goto exception; + if (JS_DefinePropertyValueInt64(ctx, tab, 0, js_dup(matched), + JS_PROP_C_W_E | JS_PROP_THROW) < 0) + goto exception; + for(n = 1; n < nCaptures; n++) { + JSValue capN; + capN = JS_GetPropertyInt64(ctx, result, n); + if (JS_IsException(capN)) + goto exception; + if (!JS_IsUndefined(capN)) { + capN = JS_ToStringFree(ctx, capN); + if (JS_IsException(capN)) + goto exception; + } + if (JS_DefinePropertyValueInt64(ctx, tab, n, capN, + JS_PROP_C_W_E | JS_PROP_THROW) < 0) + goto exception; + } + JS_FreeValue(ctx, namedCaptures); + namedCaptures = JS_GetProperty(ctx, result, JS_ATOM_groups); + if (JS_IsException(namedCaptures)) + goto exception; + if (functionalReplace) { + if (JS_DefinePropertyValueInt64(ctx, tab, n++, js_int32(position), JS_PROP_C_W_E | JS_PROP_THROW) < 0) + goto exception; + if (JS_DefinePropertyValueInt64(ctx, tab, n++, js_dup(str), JS_PROP_C_W_E | JS_PROP_THROW) < 0) + goto exception; + if (!JS_IsUndefined(namedCaptures)) { + if (JS_DefinePropertyValueInt64(ctx, tab, n++, js_dup(namedCaptures), JS_PROP_C_W_E | JS_PROP_THROW) < 0) + goto exception; + } + args[0] = JS_UNDEFINED; + args[1] = tab; + JS_FreeValue(ctx, rep_str); + rep_str = JS_ToStringFree(ctx, js_function_apply(ctx, rep, 2, args, 0)); + } else { + JSValue namedCaptures1; + if (!JS_IsUndefined(namedCaptures)) { + namedCaptures1 = JS_ToObject(ctx, namedCaptures); + if (JS_IsException(namedCaptures1)) + goto exception; + } else { + namedCaptures1 = JS_UNDEFINED; + } + args[0] = matched; + args[1] = str; + args[2] = js_int32(position); + args[3] = tab; + args[4] = namedCaptures1; + args[5] = rep_val; + JS_FreeValue(ctx, rep_str); + rep_str = js_string___GetSubstitution(ctx, JS_UNDEFINED, 6, args); + JS_FreeValue(ctx, namedCaptures1); + } + if (JS_IsException(rep_str)) + goto exception; + if (position >= nextSourcePosition) { + string_buffer_concat(b, sp, nextSourcePosition, position); + string_buffer_concat_value(b, rep_str); + nextSourcePosition = position + JS_VALUE_GET_STRING(matched)->len; + } + } + string_buffer_concat(b, sp, nextSourcePosition, sp->len); + res = string_buffer_end(b); + goto done1; + +exception: + res = JS_EXCEPTION; +done: + string_buffer_free(b); +done1: + value_buffer_free(results); + JS_FreeValue(ctx, rep_val); + JS_FreeValue(ctx, matched); + JS_FreeValue(ctx, flags); + JS_FreeValue(ctx, tab); + JS_FreeValue(ctx, rep_str); + JS_FreeValue(ctx, namedCaptures); + JS_FreeValue(ctx, str); + return res; +} + +static JSValue js_regexp_Symbol_search(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + JSValue rx = this_val; + JSValue str, previousLastIndex, currentLastIndex, result, index; + + if (!JS_IsObject(rx)) + return JS_ThrowTypeErrorNotAnObject(ctx); + + result = JS_UNDEFINED; + currentLastIndex = JS_UNDEFINED; + previousLastIndex = JS_UNDEFINED; + str = JS_ToString(ctx, argv[0]); + if (JS_IsException(str)) + goto exception; + + previousLastIndex = JS_GetProperty(ctx, rx, JS_ATOM_lastIndex); + if (JS_IsException(previousLastIndex)) + goto exception; + + if (!js_same_value(ctx, previousLastIndex, js_int32(0))) { + if (JS_SetProperty(ctx, rx, JS_ATOM_lastIndex, js_int32(0)) < 0) { + goto exception; + } + } + result = JS_RegExpExec(ctx, rx, str); + if (JS_IsException(result)) + goto exception; + currentLastIndex = JS_GetProperty(ctx, rx, JS_ATOM_lastIndex); + if (JS_IsException(currentLastIndex)) + goto exception; + if (js_same_value(ctx, currentLastIndex, previousLastIndex)) { + JS_FreeValue(ctx, previousLastIndex); + } else { + if (JS_SetProperty(ctx, rx, JS_ATOM_lastIndex, previousLastIndex) < 0) { + previousLastIndex = JS_UNDEFINED; + goto exception; + } + } + JS_FreeValue(ctx, str); + JS_FreeValue(ctx, currentLastIndex); + + if (JS_IsNull(result)) { + return js_int32(-1); + } else { + index = JS_GetProperty(ctx, result, JS_ATOM_index); + JS_FreeValue(ctx, result); + return index; + } + +exception: + JS_FreeValue(ctx, result); + JS_FreeValue(ctx, str); + JS_FreeValue(ctx, currentLastIndex); + JS_FreeValue(ctx, previousLastIndex); + return JS_EXCEPTION; +} + +static JSValue js_regexp_Symbol_split(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + // [Symbol.split](str, limit) + JSValue rx = this_val; + JSValue args[2]; + JSValue str, ctor, splitter, A, flags, z, sub; + JSString *strp; + uint32_t lim, size, p, q; + int unicodeMatching; + int64_t lengthA, e, numberOfCaptures, i; + + if (!JS_IsObject(rx)) + return JS_ThrowTypeErrorNotAnObject(ctx); + + ctor = JS_UNDEFINED; + splitter = JS_UNDEFINED; + A = JS_UNDEFINED; + flags = JS_UNDEFINED; + z = JS_UNDEFINED; + str = JS_ToString(ctx, argv[0]); + if (JS_IsException(str)) + goto exception; + ctor = JS_SpeciesConstructor(ctx, rx, ctx->regexp_ctor); + if (JS_IsException(ctor)) + goto exception; + flags = JS_ToStringFree(ctx, JS_GetProperty(ctx, rx, JS_ATOM_flags)); + if (JS_IsException(flags)) + goto exception; + strp = JS_VALUE_GET_STRING(flags); + unicodeMatching = string_indexof_char(strp, 'u', 0) >= 0; + if (string_indexof_char(strp, 'y', 0) < 0) { + flags = JS_ConcatString3(ctx, "", flags, "y"); + if (JS_IsException(flags)) + goto exception; + } + args[0] = rx; + args[1] = flags; + splitter = JS_CallConstructor(ctx, ctor, 2, args); + if (JS_IsException(splitter)) + goto exception; + A = JS_NewArray(ctx); + if (JS_IsException(A)) + goto exception; + lengthA = 0; + if (JS_IsUndefined(argv[1])) { + lim = 0xffffffff; + } else { + if (JS_ToUint32(ctx, &lim, argv[1]) < 0) + goto exception; + if (lim == 0) + goto done; + } + strp = JS_VALUE_GET_STRING(str); + p = q = 0; + size = strp->len; + if (size == 0) { + z = JS_RegExpExec(ctx, splitter, str); + if (JS_IsException(z)) + goto exception; + if (JS_IsNull(z)) + goto add_tail; + goto done; + } + while (q < size) { + if (JS_SetProperty(ctx, splitter, JS_ATOM_lastIndex, js_int32(q)) < 0) + goto exception; + JS_FreeValue(ctx, z); + z = JS_RegExpExec(ctx, splitter, str); + if (JS_IsException(z)) + goto exception; + if (JS_IsNull(z)) { + q = string_advance_index(strp, q, unicodeMatching); + } else { + if (JS_ToLengthFree(ctx, &e, JS_GetProperty(ctx, splitter, JS_ATOM_lastIndex))) + goto exception; + if (e > size) + e = size; + if (e == p) { + q = string_advance_index(strp, q, unicodeMatching); + } else { + sub = js_sub_string(ctx, strp, p, q); + if (JS_IsException(sub)) + goto exception; + if (JS_DefinePropertyValueInt64(ctx, A, lengthA++, sub, + JS_PROP_C_W_E | JS_PROP_THROW) < 0) + goto exception; + if (lengthA == lim) + goto done; + p = e; + if (js_get_length64(ctx, &numberOfCaptures, z)) + goto exception; + for(i = 1; i < numberOfCaptures; i++) { + sub = JS_GetPropertyInt64(ctx, z, i); + if (JS_IsException(sub)) + goto exception; + if (!JS_IsUndefined(sub)) { + sub = JS_ToStringFree(ctx, sub); + if (JS_IsException(sub)) + goto exception; + } + if (JS_DefinePropertyValueInt64(ctx, A, lengthA++, sub, JS_PROP_C_W_E | JS_PROP_THROW) < 0) + goto exception; + if (lengthA == lim) + goto done; + } + q = p; + } + } + } +add_tail: + if (p > size) + p = size; + sub = js_sub_string(ctx, strp, p, size); + if (JS_IsException(sub)) + goto exception; + if (JS_DefinePropertyValueInt64(ctx, A, lengthA++, sub, JS_PROP_C_W_E | JS_PROP_THROW) < 0) + goto exception; + goto done; +exception: + JS_FreeValue(ctx, A); + A = JS_EXCEPTION; +done: + JS_FreeValue(ctx, str); + JS_FreeValue(ctx, ctor); + JS_FreeValue(ctx, splitter); + JS_FreeValue(ctx, flags); + JS_FreeValue(ctx, z); + return A; +} + +static const JSCFunctionListEntry js_regexp_funcs[] = { + JS_CFUNC_DEF("escape", 1, js_regexp_escape ), + JS_CGETSET_DEF("[Symbol.species]", js_get_this, NULL ), +}; + +static const JSCFunctionListEntry js_regexp_proto_funcs[] = { + JS_CGETSET_DEF("flags", js_regexp_get_flags, NULL ), + JS_CGETSET_DEF("source", js_regexp_get_source, NULL ), + JS_CGETSET_MAGIC_DEF("global", js_regexp_get_flag, NULL, LRE_FLAG_GLOBAL ), + JS_CGETSET_MAGIC_DEF("ignoreCase", js_regexp_get_flag, NULL, LRE_FLAG_IGNORECASE ), + JS_CGETSET_MAGIC_DEF("multiline", js_regexp_get_flag, NULL, LRE_FLAG_MULTILINE ), + JS_CGETSET_MAGIC_DEF("dotAll", js_regexp_get_flag, NULL, LRE_FLAG_DOTALL ), + JS_CGETSET_MAGIC_DEF("unicode", js_regexp_get_flag, NULL, LRE_FLAG_UNICODE ), + JS_CGETSET_MAGIC_DEF("unicodeSets", js_regexp_get_flag, NULL, LRE_FLAG_UNICODE_SETS ), + JS_CGETSET_MAGIC_DEF("sticky", js_regexp_get_flag, NULL, LRE_FLAG_STICKY ), + JS_CGETSET_MAGIC_DEF("hasIndices", js_regexp_get_flag, NULL, LRE_FLAG_INDICES ), + JS_CFUNC_DEF("exec", 1, js_regexp_exec ), + JS_CFUNC_DEF("compile", 2, js_regexp_compile ), + JS_CFUNC_DEF("test", 1, js_regexp_test ), + JS_CFUNC_DEF("toString", 0, js_regexp_toString ), + JS_CFUNC_DEF("[Symbol.replace]", 2, js_regexp_Symbol_replace ), + JS_CFUNC_DEF("[Symbol.match]", 1, js_regexp_Symbol_match ), + JS_CFUNC_DEF("[Symbol.matchAll]", 1, js_regexp_Symbol_matchAll ), + JS_CFUNC_DEF("[Symbol.search]", 1, js_regexp_Symbol_search ), + JS_CFUNC_DEF("[Symbol.split]", 2, js_regexp_Symbol_split ), +}; + +static const JSCFunctionListEntry js_regexp_string_iterator_proto_funcs[] = { + JS_ITERATOR_NEXT_DEF("next", 0, js_regexp_string_iterator_next, 0 ), + JS_PROP_STRING_DEF("[Symbol.toStringTag]", "RegExp String Iterator", JS_PROP_CONFIGURABLE ), +}; + +void JS_AddIntrinsicRegExpCompiler(JSContext *ctx) +{ + ctx->compile_regexp = js_compile_regexp; +} + +void JS_AddIntrinsicRegExp(JSContext *ctx) +{ + JSValue obj; + + JS_AddIntrinsicRegExpCompiler(ctx); + + ctx->class_proto[JS_CLASS_REGEXP] = JS_NewObject(ctx); + JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_REGEXP], js_regexp_proto_funcs, + countof(js_regexp_proto_funcs)); + obj = JS_NewGlobalCConstructor(ctx, "RegExp", js_regexp_constructor, 2, + ctx->class_proto[JS_CLASS_REGEXP]); + ctx->regexp_ctor = js_dup(obj); + JS_SetPropertyFunctionList(ctx, obj, js_regexp_funcs, countof(js_regexp_funcs)); + + ctx->class_proto[JS_CLASS_REGEXP_STRING_ITERATOR] = + JS_NewObjectProto(ctx, ctx->class_proto[JS_CLASS_ITERATOR]); + JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_REGEXP_STRING_ITERATOR], + js_regexp_string_iterator_proto_funcs, + countof(js_regexp_string_iterator_proto_funcs)); +} + +/* JSON */ + +static JSValue json_parse_value(JSParseState *s) +{ + JSContext *ctx = s->ctx; + JSValue val = JS_NULL; + int ret; + + switch(s->token.val) { + case '{': + { + JSValue prop_val; + JSAtom prop_name; + + if (json_next_token(s)) + goto fail; + val = JS_NewObject(ctx); + if (JS_IsException(val)) + goto fail; + if (s->token.val != '}') { + for(;;) { + if (s->token.val == TOK_STRING) { + prop_name = JS_ValueToAtom(ctx, s->token.u.str.str); + if (prop_name == JS_ATOM_NULL) + goto fail; + } else { + json_parse_error(s, s->token.ptr, "Expected property name or '}'"); + goto fail; + } + if (json_next_token(s)) + goto fail1; + if (s->token.val != ':') { + json_parse_error(s, s->token.ptr, "Expected ':' after property name"); + goto fail1; + } + if (json_next_token(s)) + goto fail1; + prop_val = json_parse_value(s); + if (JS_IsException(prop_val)) { + fail1: + JS_FreeAtom(ctx, prop_name); + goto fail; + } + ret = JS_DefinePropertyValue(ctx, val, prop_name, + prop_val, JS_PROP_C_W_E); + JS_FreeAtom(ctx, prop_name); + if (ret < 0) + goto fail; + + if (s->token.val == '}') + break; + if (s->token.val != ',') { + json_parse_error(s, s->token.ptr, "Expected ',' or '}' after property value"); + goto fail; + } + if (json_next_token(s)) + goto fail; + } + } + if (json_next_token(s)) + goto fail; + } + break; + case '[': + { + JSValue el; + uint32_t idx; + + if (json_next_token(s)) + goto fail; + val = JS_NewArray(ctx); + if (JS_IsException(val)) + goto fail; + if (s->token.val != ']') { + for(idx = 0;; idx++) { + el = json_parse_value(s); + if (JS_IsException(el)) + goto fail; + ret = JS_DefinePropertyValueUint32(ctx, val, idx, el, JS_PROP_C_W_E); + if (ret < 0) + goto fail; + if (s->token.val == ']') + break; + if (s->token.val != ',') { + json_parse_error(s, s->token.ptr, "Expected ',' or ']' after array element"); + goto fail; + } + if (json_next_token(s)) + goto fail; + } + } + if (json_next_token(s)) + goto fail; + } + break; + case TOK_STRING: + val = js_dup(s->token.u.str.str); + if (json_next_token(s)) + goto fail; + break; + case TOK_NUMBER: + val = s->token.u.num.val; + if (json_next_token(s)) + goto fail; + break; + case TOK_IDENT: + if (s->token.u.ident.atom == JS_ATOM_false || + s->token.u.ident.atom == JS_ATOM_true) { + val = js_bool(s->token.u.ident.atom == JS_ATOM_true); + } else if (s->token.u.ident.atom == JS_ATOM_null) { + val = JS_NULL; + } else { + goto def_token; + } + if (json_next_token(s)) + goto fail; + break; + default: + def_token: + if (s->token.val == TOK_EOF) { + js_parse_error(s, "Unexpected end of JSON input"); + } else { + js_parse_error(s, "unexpected token: '%.*s'", + (int)(s->buf_ptr - s->token.ptr), s->token.ptr); + } + goto fail; + } + return val; + fail: + JS_FreeValue(ctx, val); + return JS_EXCEPTION; +} + +/* 'buf' must be zero terminated i.e. buf[buf_len] = '\0'. */ +JSValue JS_ParseJSON(JSContext *ctx, const char *buf, size_t buf_len, const char *filename) +{ + JSParseState s1, *s = &s1; + JSValue val = JS_UNDEFINED; + + js_parse_init(ctx, s, buf, buf_len, filename); + if (json_next_token(s)) + goto fail; + val = json_parse_value(s); + if (JS_IsException(val)) + goto fail; + if (s->token.val != TOK_EOF) { + if (js_parse_error(s, "unexpected data at the end")) + goto fail; + } + return val; + fail: + JS_FreeValue(ctx, val); + free_token(s, &s->token); + return JS_EXCEPTION; +} + +static JSValue internalize_json_property(JSContext *ctx, JSValue holder, + JSAtom name, JSValue reviver) +{ + JSValue val, new_el, name_val, res; + JSValue args[2]; + int ret, is_array; + uint32_t i, len = 0; + JSAtom prop; + JSPropertyEnum *atoms = NULL; + + if (js_check_stack_overflow(ctx->rt, 0)) { + return JS_ThrowStackOverflow(ctx); + } + + val = JS_GetProperty(ctx, holder, name); + if (JS_IsException(val)) + return val; + if (JS_IsObject(val)) { + is_array = JS_IsArray(ctx, val); + if (is_array < 0) + goto fail; + if (is_array) { + if (js_get_length32(ctx, &len, val)) + goto fail; + } else { + ret = JS_GetOwnPropertyNamesInternal(ctx, &atoms, &len, JS_VALUE_GET_OBJ(val), JS_GPN_ENUM_ONLY | JS_GPN_STRING_MASK); + if (ret < 0) + goto fail; + } + for(i = 0; i < len; i++) { + if (is_array) { + prop = JS_NewAtomUInt32(ctx, i); + if (prop == JS_ATOM_NULL) + goto fail; + } else { + prop = JS_DupAtom(ctx, atoms[i].atom); + } + new_el = internalize_json_property(ctx, val, prop, reviver); + if (JS_IsException(new_el)) { + JS_FreeAtom(ctx, prop); + goto fail; + } + if (JS_IsUndefined(new_el)) { + ret = JS_DeleteProperty(ctx, val, prop, 0); + } else { + ret = JS_DefinePropertyValue(ctx, val, prop, new_el, JS_PROP_C_W_E); + } + JS_FreeAtom(ctx, prop); + if (ret < 0) + goto fail; + } + } + js_free_prop_enum(ctx, atoms, len); + atoms = NULL; + name_val = JS_AtomToValue(ctx, name); + if (JS_IsException(name_val)) + goto fail; + args[0] = name_val; + args[1] = val; + res = JS_Call(ctx, reviver, holder, 2, args); + JS_FreeValue(ctx, name_val); + JS_FreeValue(ctx, val); + return res; + fail: + js_free_prop_enum(ctx, atoms, len); + JS_FreeValue(ctx, val); + return JS_EXCEPTION; +} + +static JSValue js_json_parse(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + JSValue obj, root; + JSValue reviver; + const char *str; + size_t len; + + str = JS_ToCStringLen(ctx, &len, argv[0]); + if (!str) + return JS_EXCEPTION; + obj = JS_ParseJSON(ctx, str, len, "<input>"); + JS_FreeCString(ctx, str); + if (JS_IsException(obj)) + return obj; + if (argc > 1 && JS_IsFunction(ctx, argv[1])) { + reviver = argv[1]; + root = JS_NewObject(ctx); + if (JS_IsException(root)) { + JS_FreeValue(ctx, obj); + return JS_EXCEPTION; + } + if (JS_DefinePropertyValue(ctx, root, JS_ATOM_empty_string, obj, + JS_PROP_C_W_E) < 0) { + JS_FreeValue(ctx, root); + return JS_EXCEPTION; + } + obj = internalize_json_property(ctx, root, JS_ATOM_empty_string, + reviver); + JS_FreeValue(ctx, root); + } + return obj; +} + +typedef struct JSONStringifyContext { + JSValue replacer_func; + JSValue stack; + JSValue property_list; + JSValue gap; + JSValue empty; + StringBuffer *b; +} JSONStringifyContext; + +static JSValue JS_ToQuotedStringFree(JSContext *ctx, JSValue val) { + JSValue r = JS_ToQuotedString(ctx, val); + JS_FreeValue(ctx, val); + return r; +} + +static JSValue js_json_check(JSContext *ctx, JSONStringifyContext *jsc, + JSValue holder, JSValue val, JSValue key) +{ + JSValue v; + JSValue args[2]; + + if (JS_IsObject(val) || JS_IsBigInt(ctx, val)) { + JSValue f = JS_GetProperty(ctx, val, JS_ATOM_toJSON); + if (JS_IsException(f)) + goto exception; + if (JS_IsFunction(ctx, f)) { + v = JS_CallFree(ctx, f, val, 1, &key); + JS_FreeValue(ctx, val); + val = v; + if (JS_IsException(val)) + goto exception; + } else { + JS_FreeValue(ctx, f); + } + } + + if (!JS_IsUndefined(jsc->replacer_func)) { + args[0] = key; + args[1] = val; + v = JS_Call(ctx, jsc->replacer_func, holder, 2, args); + JS_FreeValue(ctx, val); + val = v; + if (JS_IsException(val)) + goto exception; + } + + switch (JS_VALUE_GET_NORM_TAG(val)) { + case JS_TAG_OBJECT: + if (JS_IsFunction(ctx, val)) + break; + case JS_TAG_STRING: + case JS_TAG_INT: + case JS_TAG_FLOAT64: + case JS_TAG_BOOL: + case JS_TAG_NULL: + case JS_TAG_BIG_INT: + case JS_TAG_EXCEPTION: + return val; + default: + break; + } + JS_FreeValue(ctx, val); + return JS_UNDEFINED; + +exception: + JS_FreeValue(ctx, val); + return JS_EXCEPTION; +} + +static int js_json_to_str(JSContext *ctx, JSONStringifyContext *jsc, + JSValue holder, JSValue val, + JSValue indent) +{ + JSValue indent1, sep, sep1, tab, v, prop; + JSObject *p; + int64_t i, len; + int cl, ret; + BOOL has_content; + + indent1 = JS_UNDEFINED; + sep = JS_UNDEFINED; + sep1 = JS_UNDEFINED; + tab = JS_UNDEFINED; + prop = JS_UNDEFINED; + + if (JS_IsObject(val)) { + p = JS_VALUE_GET_OBJ(val); + cl = p->class_id; + if (cl == JS_CLASS_STRING) { + val = JS_ToStringFree(ctx, val); + if (JS_IsException(val)) + goto exception; + goto concat_primitive; + } else if (cl == JS_CLASS_NUMBER) { + val = JS_ToNumberFree(ctx, val); + if (JS_IsException(val)) + goto exception; + goto concat_primitive; + } else if (cl == JS_CLASS_BOOLEAN || cl == JS_CLASS_BIG_INT) { + set_value(ctx, &val, js_dup(p->u.object_data)); + goto concat_primitive; + } + v = js_array_includes(ctx, jsc->stack, 1, &val); + if (JS_IsException(v)) + goto exception; + if (JS_ToBoolFree(ctx, v)) { + JS_ThrowTypeError(ctx, "circular reference"); + goto exception; + } + indent1 = JS_ConcatString(ctx, js_dup(indent), js_dup(jsc->gap)); + if (JS_IsException(indent1)) + goto exception; + if (!JS_IsEmptyString(jsc->gap)) { + sep = JS_ConcatString3(ctx, "\n", js_dup(indent1), ""); + if (JS_IsException(sep)) + goto exception; + sep1 = js_new_string8(ctx, " "); + if (JS_IsException(sep1)) + goto exception; + } else { + sep = js_dup(jsc->empty); + sep1 = js_dup(jsc->empty); + } + v = js_array_push(ctx, jsc->stack, 1, &val, 0); + if (check_exception_free(ctx, v)) + goto exception; + ret = JS_IsArray(ctx, val); + if (ret < 0) + goto exception; + if (ret) { + if (js_get_length64(ctx, &len, val)) + goto exception; + string_buffer_putc8(jsc->b, '['); + for(i = 0; i < len; i++) { + if (i > 0) + string_buffer_putc8(jsc->b, ','); + string_buffer_concat_value(jsc->b, sep); + v = JS_GetPropertyInt64(ctx, val, i); + if (JS_IsException(v)) + goto exception; + /* XXX: could do this string conversion only when needed */ + prop = JS_ToStringFree(ctx, js_int64(i)); + if (JS_IsException(prop)) + goto exception; + v = js_json_check(ctx, jsc, val, v, prop); + JS_FreeValue(ctx, prop); + prop = JS_UNDEFINED; + if (JS_IsException(v)) + goto exception; + if (JS_IsUndefined(v)) + v = JS_NULL; + if (js_json_to_str(ctx, jsc, val, v, indent1)) + goto exception; + } + if (len > 0 && !JS_IsEmptyString(jsc->gap)) { + string_buffer_putc8(jsc->b, '\n'); + string_buffer_concat_value(jsc->b, indent); + } + string_buffer_putc8(jsc->b, ']'); + } else { + if (!JS_IsUndefined(jsc->property_list)) + tab = js_dup(jsc->property_list); + else + tab = js_object_keys(ctx, JS_UNDEFINED, 1, &val, JS_ITERATOR_KIND_KEY); + if (JS_IsException(tab)) + goto exception; + if (js_get_length64(ctx, &len, tab)) + goto exception; + string_buffer_putc8(jsc->b, '{'); + has_content = FALSE; + for(i = 0; i < len; i++) { + JS_FreeValue(ctx, prop); + prop = JS_GetPropertyInt64(ctx, tab, i); + if (JS_IsException(prop)) + goto exception; + v = JS_GetPropertyValue(ctx, val, js_dup(prop)); + if (JS_IsException(v)) + goto exception; + v = js_json_check(ctx, jsc, val, v, prop); + if (JS_IsException(v)) + goto exception; + if (!JS_IsUndefined(v)) { + if (has_content) + string_buffer_putc8(jsc->b, ','); + prop = JS_ToQuotedStringFree(ctx, prop); + if (JS_IsException(prop)) { + JS_FreeValue(ctx, v); + goto exception; + } + string_buffer_concat_value(jsc->b, sep); + string_buffer_concat_value(jsc->b, prop); + string_buffer_putc8(jsc->b, ':'); + string_buffer_concat_value(jsc->b, sep1); + if (js_json_to_str(ctx, jsc, val, v, indent1)) + goto exception; + has_content = TRUE; + } + } + if (has_content && JS_VALUE_GET_STRING(jsc->gap)->len != 0) { + string_buffer_putc8(jsc->b, '\n'); + string_buffer_concat_value(jsc->b, indent); + } + string_buffer_putc8(jsc->b, '}'); + } + if (check_exception_free(ctx, js_array_pop(ctx, jsc->stack, 0, NULL, 0))) + goto exception; + JS_FreeValue(ctx, val); + JS_FreeValue(ctx, tab); + JS_FreeValue(ctx, sep); + JS_FreeValue(ctx, sep1); + JS_FreeValue(ctx, indent1); + JS_FreeValue(ctx, prop); + return 0; + } + concat_primitive: + switch (JS_VALUE_GET_NORM_TAG(val)) { + case JS_TAG_STRING: + val = JS_ToQuotedStringFree(ctx, val); + if (JS_IsException(val)) + goto exception; + goto concat_value; + case JS_TAG_FLOAT64: + if (!isfinite(JS_VALUE_GET_FLOAT64(val))) { + val = JS_NULL; + } + goto concat_value; + case JS_TAG_INT: + case JS_TAG_BOOL: + case JS_TAG_NULL: + concat_value: + return string_buffer_concat_value_free(jsc->b, val); + case JS_TAG_BIG_INT: + JS_ThrowTypeError(ctx, "BigInt are forbidden in JSON.stringify"); + goto exception; + default: + JS_FreeValue(ctx, val); + return 0; + } + +exception: + JS_FreeValue(ctx, val); + JS_FreeValue(ctx, tab); + JS_FreeValue(ctx, sep); + JS_FreeValue(ctx, sep1); + JS_FreeValue(ctx, indent1); + JS_FreeValue(ctx, prop); + return -1; +} + +JSValue JS_JSONStringify(JSContext *ctx, JSValue obj, + JSValue replacer, JSValue space0) +{ + StringBuffer b_s; + JSONStringifyContext jsc_s, *jsc = &jsc_s; + JSValue val, v, space, ret, wrapper; + int res; + int64_t i, j, n; + + jsc->replacer_func = JS_UNDEFINED; + jsc->stack = JS_UNDEFINED; + jsc->property_list = JS_UNDEFINED; + jsc->gap = JS_UNDEFINED; + jsc->b = &b_s; + jsc->empty = JS_AtomToString(ctx, JS_ATOM_empty_string); + ret = JS_UNDEFINED; + wrapper = JS_UNDEFINED; + + string_buffer_init(ctx, jsc->b, 0); + jsc->stack = JS_NewArray(ctx); + if (JS_IsException(jsc->stack)) + goto exception; + if (JS_IsFunction(ctx, replacer)) { + jsc->replacer_func = replacer; + } else { + res = JS_IsArray(ctx, replacer); + if (res < 0) + goto exception; + if (res) { + /* XXX: enumeration is not fully correct */ + jsc->property_list = JS_NewArray(ctx); + if (JS_IsException(jsc->property_list)) + goto exception; + if (js_get_length64(ctx, &n, replacer)) + goto exception; + for (i = j = 0; i < n; i++) { + JSValue present; + v = JS_GetPropertyInt64(ctx, replacer, i); + if (JS_IsException(v)) + goto exception; + if (JS_IsObject(v)) { + JSObject *p = JS_VALUE_GET_OBJ(v); + if (p->class_id == JS_CLASS_STRING || + p->class_id == JS_CLASS_NUMBER) { + v = JS_ToStringFree(ctx, v); + if (JS_IsException(v)) + goto exception; + } else { + JS_FreeValue(ctx, v); + continue; + } + } else if (JS_IsNumber(v)) { + v = JS_ToStringFree(ctx, v); + if (JS_IsException(v)) + goto exception; + } else if (!JS_IsString(v)) { + JS_FreeValue(ctx, v); + continue; + } + present = js_array_includes(ctx, jsc->property_list, + 1, &v); + if (JS_IsException(present)) { + JS_FreeValue(ctx, v); + goto exception; + } + if (!JS_ToBoolFree(ctx, present)) { + JS_SetPropertyInt64(ctx, jsc->property_list, j++, v); + } else { + JS_FreeValue(ctx, v); + } + } + } + } + space = js_dup(space0); + if (JS_IsObject(space)) { + JSObject *p = JS_VALUE_GET_OBJ(space); + if (p->class_id == JS_CLASS_NUMBER) { + space = JS_ToNumberFree(ctx, space); + } else if (p->class_id == JS_CLASS_STRING) { + space = JS_ToStringFree(ctx, space); + } + if (JS_IsException(space)) { + JS_FreeValue(ctx, space); + goto exception; + } + } + if (JS_IsNumber(space)) { + int n; + if (JS_ToInt32Clamp(ctx, &n, space, 0, 10, 0)) + goto exception; + jsc->gap = JS_NewStringLen(ctx, " ", n); + } else if (JS_IsString(space)) { + JSString *p = JS_VALUE_GET_STRING(space); + jsc->gap = js_sub_string(ctx, p, 0, min_int(p->len, 10)); + } else { + jsc->gap = js_dup(jsc->empty); + } + JS_FreeValue(ctx, space); + if (JS_IsException(jsc->gap)) + goto exception; + wrapper = JS_NewObject(ctx); + if (JS_IsException(wrapper)) + goto exception; + if (JS_DefinePropertyValue(ctx, wrapper, JS_ATOM_empty_string, + js_dup(obj), JS_PROP_C_W_E) < 0) + goto exception; + val = js_dup(obj); + + val = js_json_check(ctx, jsc, wrapper, val, jsc->empty); + if (JS_IsException(val)) + goto exception; + if (JS_IsUndefined(val)) { + ret = JS_UNDEFINED; + goto done1; + } + if (js_json_to_str(ctx, jsc, wrapper, val, jsc->empty)) + goto exception; + + ret = string_buffer_end(jsc->b); + goto done; + +exception: + ret = JS_EXCEPTION; +done1: + string_buffer_free(jsc->b); +done: + JS_FreeValue(ctx, wrapper); + JS_FreeValue(ctx, jsc->empty); + JS_FreeValue(ctx, jsc->gap); + JS_FreeValue(ctx, jsc->property_list); + JS_FreeValue(ctx, jsc->stack); + return ret; +} + +static JSValue js_json_stringify(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + // stringify(val, replacer, space) + return JS_JSONStringify(ctx, argv[0], argv[1], argv[2]); +} + +static const JSCFunctionListEntry js_json_funcs[] = { + JS_CFUNC_DEF("parse", 2, js_json_parse ), + JS_CFUNC_DEF("stringify", 3, js_json_stringify ), + JS_PROP_STRING_DEF("[Symbol.toStringTag]", "JSON", JS_PROP_CONFIGURABLE ), +}; + +static const JSCFunctionListEntry js_json_obj[] = { + JS_OBJECT_DEF("JSON", js_json_funcs, countof(js_json_funcs), JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE ), +}; + +void JS_AddIntrinsicJSON(JSContext *ctx) +{ + /* add JSON as autoinit object */ + JS_SetPropertyFunctionList(ctx, ctx->global_obj, js_json_obj, countof(js_json_obj)); +} + +/* Reflect */ + +static JSValue js_reflect_apply(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + return js_function_apply(ctx, argv[0], max_int(0, argc - 1), argv + 1, 2); +} + +static JSValue js_reflect_construct(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + JSValue func, array_arg, new_target; + JSValue *tab, ret; + uint32_t len; + + func = argv[0]; + array_arg = argv[1]; + if (argc > 2) { + new_target = argv[2]; + if (!JS_IsConstructor(ctx, new_target)) + return JS_ThrowTypeError(ctx, "not a constructor"); + } else { + new_target = func; + } + tab = build_arg_list(ctx, &len, array_arg); + if (!tab) + return JS_EXCEPTION; + ret = JS_CallConstructor2(ctx, func, new_target, len, tab); + free_arg_list(ctx, tab, len); + return ret; +} + +static JSValue js_reflect_deleteProperty(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + JSValue obj; + JSAtom atom; + int ret; + + obj = argv[0]; + if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT) + return JS_ThrowTypeErrorNotAnObject(ctx); + atom = JS_ValueToAtom(ctx, argv[1]); + if (unlikely(atom == JS_ATOM_NULL)) + return JS_EXCEPTION; + ret = JS_DeleteProperty(ctx, obj, atom, 0); + JS_FreeAtom(ctx, atom); + if (ret < 0) + return JS_EXCEPTION; + else + return js_bool(ret); +} + +static JSValue js_reflect_get(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + JSValue obj, prop, receiver; + JSAtom atom; + JSValue ret; + + obj = argv[0]; + prop = argv[1]; + if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT) + return JS_ThrowTypeErrorNotAnObject(ctx); + if (argc > 2) + receiver = argv[2]; + else + receiver = obj; + atom = JS_ValueToAtom(ctx, prop); + if (unlikely(atom == JS_ATOM_NULL)) + return JS_EXCEPTION; + ret = JS_GetPropertyInternal(ctx, obj, atom, receiver, FALSE); + JS_FreeAtom(ctx, atom); + return ret; +} + +static JSValue js_reflect_has(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + JSValue obj, prop; + JSAtom atom; + int ret; + + obj = argv[0]; + prop = argv[1]; + if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT) + return JS_ThrowTypeErrorNotAnObject(ctx); + atom = JS_ValueToAtom(ctx, prop); + if (unlikely(atom == JS_ATOM_NULL)) + return JS_EXCEPTION; + ret = JS_HasProperty(ctx, obj, atom); + JS_FreeAtom(ctx, atom); + if (ret < 0) + return JS_EXCEPTION; + else + return js_bool(ret); +} + +static JSValue js_reflect_set(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + JSValue obj, prop, val, receiver; + int ret; + JSAtom atom; + + obj = argv[0]; + prop = argv[1]; + val = argv[2]; + if (argc > 3) + receiver = argv[3]; + else + receiver = obj; + if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT) + return JS_ThrowTypeErrorNotAnObject(ctx); + atom = JS_ValueToAtom(ctx, prop); + if (unlikely(atom == JS_ATOM_NULL)) + return JS_EXCEPTION; + ret = JS_SetPropertyInternal2(ctx, obj, atom, js_dup(val), receiver, + 0, NULL); + JS_FreeAtom(ctx, atom); + if (ret < 0) + return JS_EXCEPTION; + else + return js_bool(ret); +} + +static JSValue js_reflect_setPrototypeOf(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + int ret; + ret = JS_SetPrototypeInternal(ctx, argv[0], argv[1], FALSE); + if (ret < 0) + return JS_EXCEPTION; + else + return js_bool(ret); +} + +static JSValue js_reflect_ownKeys(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + if (JS_VALUE_GET_TAG(argv[0]) != JS_TAG_OBJECT) + return JS_ThrowTypeErrorNotAnObject(ctx); + return JS_GetOwnPropertyNames2(ctx, argv[0], + JS_GPN_STRING_MASK | JS_GPN_SYMBOL_MASK, + JS_ITERATOR_KIND_KEY); +} + +static const JSCFunctionListEntry js_reflect_funcs[] = { + JS_CFUNC_DEF("apply", 3, js_reflect_apply ), + JS_CFUNC_DEF("construct", 2, js_reflect_construct ), + JS_CFUNC_MAGIC_DEF("defineProperty", 3, js_object_defineProperty, 1 ), + JS_CFUNC_DEF("deleteProperty", 2, js_reflect_deleteProperty ), + JS_CFUNC_DEF("get", 2, js_reflect_get ), + JS_CFUNC_MAGIC_DEF("getOwnPropertyDescriptor", 2, js_object_getOwnPropertyDescriptor, 1 ), + JS_CFUNC_MAGIC_DEF("getPrototypeOf", 1, js_object_getPrototypeOf, 1 ), + JS_CFUNC_DEF("has", 2, js_reflect_has ), + JS_CFUNC_MAGIC_DEF("isExtensible", 1, js_object_isExtensible, 1 ), + JS_CFUNC_DEF("ownKeys", 1, js_reflect_ownKeys ), + JS_CFUNC_MAGIC_DEF("preventExtensions", 1, js_object_preventExtensions, 1 ), + JS_CFUNC_DEF("set", 3, js_reflect_set ), + JS_CFUNC_DEF("setPrototypeOf", 2, js_reflect_setPrototypeOf ), + JS_PROP_STRING_DEF("[Symbol.toStringTag]", "Reflect", JS_PROP_CONFIGURABLE ), +}; + +static const JSCFunctionListEntry js_reflect_obj[] = { + JS_OBJECT_DEF("Reflect", js_reflect_funcs, countof(js_reflect_funcs), JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE ), +}; + +/* Proxy */ + +static void js_proxy_finalizer(JSRuntime *rt, JSValue val) +{ + JSProxyData *s = JS_GetOpaque(val, JS_CLASS_PROXY); + if (s) { + JS_FreeValueRT(rt, s->target); + JS_FreeValueRT(rt, s->handler); + js_free_rt(rt, s); + } +} + +static void js_proxy_mark(JSRuntime *rt, JSValue val, + JS_MarkFunc *mark_func) +{ + JSProxyData *s = JS_GetOpaque(val, JS_CLASS_PROXY); + if (s) { + JS_MarkValue(rt, s->target, mark_func); + JS_MarkValue(rt, s->handler, mark_func); + } +} + +static JSValue JS_ThrowTypeErrorRevokedProxy(JSContext *ctx) +{ + return JS_ThrowTypeError(ctx, "revoked proxy"); +} + +static JSProxyData *get_proxy_method(JSContext *ctx, JSValue *pmethod, + JSValue obj, JSAtom name) +{ + JSProxyData *s = JS_GetOpaque(obj, JS_CLASS_PROXY); + JSValue method; + + /* safer to test recursion in all proxy methods */ + if (js_check_stack_overflow(ctx->rt, 0)) { + JS_ThrowStackOverflow(ctx); + return NULL; + } + + /* 's' should never be NULL */ + if (s->is_revoked) { + JS_ThrowTypeErrorRevokedProxy(ctx); + return NULL; + } + method = JS_GetProperty(ctx, s->handler, name); + if (JS_IsException(method)) + return NULL; + if (JS_IsNull(method)) + method = JS_UNDEFINED; + *pmethod = method; + return s; +} + +static JSValue js_proxy_getPrototypeOf(JSContext *ctx, JSValue obj) +{ + JSProxyData *s; + JSValue method, ret, proto1; + int res; + + s = get_proxy_method(ctx, &method, obj, JS_ATOM_getPrototypeOf); + if (!s) + return JS_EXCEPTION; + if (JS_IsUndefined(method)) + return JS_GetPrototype(ctx, s->target); + ret = JS_CallFree(ctx, method, s->handler, 1, &s->target); + if (JS_IsException(ret)) + return ret; + if (JS_VALUE_GET_TAG(ret) != JS_TAG_NULL && + JS_VALUE_GET_TAG(ret) != JS_TAG_OBJECT) { + goto fail; + } + res = JS_IsExtensible(ctx, s->target); + if (res < 0) { + JS_FreeValue(ctx, ret); + return JS_EXCEPTION; + } + if (!res) { + /* check invariant */ + proto1 = JS_GetPrototype(ctx, s->target); + if (JS_IsException(proto1)) { + JS_FreeValue(ctx, ret); + return JS_EXCEPTION; + } + if (JS_VALUE_GET_OBJ(proto1) != JS_VALUE_GET_OBJ(ret)) { + JS_FreeValue(ctx, proto1); + fail: + JS_FreeValue(ctx, ret); + return JS_ThrowTypeError(ctx, "proxy: inconsistent prototype"); + } + JS_FreeValue(ctx, proto1); + } + return ret; +} + +static int js_proxy_setPrototypeOf(JSContext *ctx, JSValue obj, + JSValue proto_val, BOOL throw_flag) +{ + JSProxyData *s; + JSValue method, ret, proto1; + JSValue args[2]; + BOOL res; + int res2; + + s = get_proxy_method(ctx, &method, obj, JS_ATOM_setPrototypeOf); + if (!s) + return -1; + if (JS_IsUndefined(method)) + return JS_SetPrototypeInternal(ctx, s->target, proto_val, throw_flag); + args[0] = s->target; + args[1] = proto_val; + ret = JS_CallFree(ctx, method, s->handler, 2, args); + if (JS_IsException(ret)) + return -1; + res = JS_ToBoolFree(ctx, ret); + if (!res) { + if (throw_flag) { + JS_ThrowTypeError(ctx, "proxy: bad prototype"); + return -1; + } else { + return FALSE; + } + } + res2 = JS_IsExtensible(ctx, s->target); + if (res2 < 0) + return -1; + if (!res2) { + proto1 = JS_GetPrototype(ctx, s->target); + if (JS_IsException(proto1)) + return -1; + if (JS_VALUE_GET_OBJ(proto_val) != JS_VALUE_GET_OBJ(proto1)) { + JS_FreeValue(ctx, proto1); + JS_ThrowTypeError(ctx, "proxy: inconsistent prototype"); + return -1; + } + JS_FreeValue(ctx, proto1); + } + return TRUE; +} + +static int js_proxy_isExtensible(JSContext *ctx, JSValue obj) +{ + JSProxyData *s; + JSValue method, ret; + BOOL res; + int res2; + + s = get_proxy_method(ctx, &method, obj, JS_ATOM_isExtensible); + if (!s) + return -1; + if (JS_IsUndefined(method)) + return JS_IsExtensible(ctx, s->target); + ret = JS_CallFree(ctx, method, s->handler, 1, &s->target); + if (JS_IsException(ret)) + return -1; + res = JS_ToBoolFree(ctx, ret); + res2 = JS_IsExtensible(ctx, s->target); + if (res2 < 0) + return res2; + if (res != res2) { + JS_ThrowTypeError(ctx, "proxy: inconsistent isExtensible"); + return -1; + } + return res; +} + +static int js_proxy_preventExtensions(JSContext *ctx, JSValue obj) +{ + JSProxyData *s; + JSValue method, ret; + BOOL res; + int res2; + + s = get_proxy_method(ctx, &method, obj, JS_ATOM_preventExtensions); + if (!s) + return -1; + if (JS_IsUndefined(method)) + return JS_PreventExtensions(ctx, s->target); + ret = JS_CallFree(ctx, method, s->handler, 1, &s->target); + if (JS_IsException(ret)) + return -1; + res = JS_ToBoolFree(ctx, ret); + if (res) { + res2 = JS_IsExtensible(ctx, s->target); + if (res2 < 0) + return res2; + if (res2) { + JS_ThrowTypeError(ctx, "proxy: inconsistent preventExtensions"); + return -1; + } + } + return res; +} + +static int js_proxy_has(JSContext *ctx, JSValue obj, JSAtom atom) +{ + JSProxyData *s; + JSValue method, ret1, atom_val; + int ret, res; + JSObject *p; + JSValue args[2]; + BOOL res2; + + s = get_proxy_method(ctx, &method, obj, JS_ATOM_has); + if (!s) + return -1; + if (JS_IsUndefined(method)) + return JS_HasProperty(ctx, s->target, atom); + atom_val = JS_AtomToValue(ctx, atom); + if (JS_IsException(atom_val)) { + JS_FreeValue(ctx, method); + return -1; + } + args[0] = s->target; + args[1] = atom_val; + ret1 = JS_CallFree(ctx, method, s->handler, 2, args); + JS_FreeValue(ctx, atom_val); + if (JS_IsException(ret1)) + return -1; + ret = JS_ToBoolFree(ctx, ret1); + if (!ret) { + JSPropertyDescriptor desc; + p = JS_VALUE_GET_OBJ(s->target); + res = JS_GetOwnPropertyInternal(ctx, &desc, p, atom); + if (res < 0) + return -1; + if (res) { + res2 = !(desc.flags & JS_PROP_CONFIGURABLE); + js_free_desc(ctx, &desc); + if (res2 || !p->extensible) { + JS_ThrowTypeError(ctx, "proxy: inconsistent has"); + return -1; + } + } + } + return ret; +} + +static JSValue js_proxy_get(JSContext *ctx, JSValue obj, JSAtom atom, + JSValue receiver) +{ + JSProxyData *s; + JSValue method, ret, atom_val; + int res; + JSValue args[3]; + JSPropertyDescriptor desc; + + s = get_proxy_method(ctx, &method, obj, JS_ATOM_get); + if (!s) + return JS_EXCEPTION; + /* Note: recursion is possible thru the prototype of s->target */ + if (JS_IsUndefined(method)) + return JS_GetPropertyInternal(ctx, s->target, atom, receiver, FALSE); + atom_val = JS_AtomToValue(ctx, atom); + if (JS_IsException(atom_val)) { + JS_FreeValue(ctx, method); + return JS_EXCEPTION; + } + args[0] = s->target; + args[1] = atom_val; + args[2] = receiver; + ret = JS_CallFree(ctx, method, s->handler, 3, args); + JS_FreeValue(ctx, atom_val); + if (JS_IsException(ret)) + return JS_EXCEPTION; + res = JS_GetOwnPropertyInternal(ctx, &desc, JS_VALUE_GET_OBJ(s->target), atom); + if (res < 0) { + JS_FreeValue(ctx, ret); + return JS_EXCEPTION; + } + if (res) { + if ((desc.flags & (JS_PROP_GETSET | JS_PROP_CONFIGURABLE | JS_PROP_WRITABLE)) == 0) { + if (!js_same_value(ctx, desc.value, ret)) { + goto fail; + } + } else if ((desc.flags & (JS_PROP_GETSET | JS_PROP_CONFIGURABLE)) == JS_PROP_GETSET) { + if (JS_IsUndefined(desc.getter) && !JS_IsUndefined(ret)) { + fail: + js_free_desc(ctx, &desc); + JS_FreeValue(ctx, ret); + return JS_ThrowTypeError(ctx, "proxy: inconsistent get"); + } + } + js_free_desc(ctx, &desc); + } + return ret; +} + +static int js_proxy_set(JSContext *ctx, JSValue obj, JSAtom atom, + JSValue value, JSValue receiver, int flags) +{ + JSProxyData *s; + JSValue method, ret1, atom_val; + int ret, res; + JSValue args[4]; + + s = get_proxy_method(ctx, &method, obj, JS_ATOM_set); + if (!s) + return -1; + if (JS_IsUndefined(method)) { + return JS_SetPropertyInternal2(ctx, s->target, atom, + js_dup(value), receiver, + flags, NULL); + } + atom_val = JS_AtomToValue(ctx, atom); + if (JS_IsException(atom_val)) { + JS_FreeValue(ctx, method); + return -1; + } + args[0] = s->target; + args[1] = atom_val; + args[2] = value; + args[3] = receiver; + ret1 = JS_CallFree(ctx, method, s->handler, 4, args); + JS_FreeValue(ctx, atom_val); + if (JS_IsException(ret1)) + return -1; + ret = JS_ToBoolFree(ctx, ret1); + if (ret) { + JSPropertyDescriptor desc; + res = JS_GetOwnPropertyInternal(ctx, &desc, JS_VALUE_GET_OBJ(s->target), atom); + if (res < 0) + return -1; + if (res) { + if ((desc.flags & (JS_PROP_GETSET | JS_PROP_CONFIGURABLE | JS_PROP_WRITABLE)) == 0) { + if (!js_same_value(ctx, desc.value, value)) { + goto fail; + } + } else if ((desc.flags & (JS_PROP_GETSET | JS_PROP_CONFIGURABLE)) == JS_PROP_GETSET && JS_IsUndefined(desc.setter)) { + fail: + js_free_desc(ctx, &desc); + JS_ThrowTypeError(ctx, "proxy: inconsistent set"); + return -1; + } + js_free_desc(ctx, &desc); + } + } else { + if ((flags & JS_PROP_THROW) || + ((flags & JS_PROP_THROW_STRICT) && is_strict_mode(ctx))) { + JS_ThrowTypeError(ctx, "proxy: cannot set property"); + return -1; + } + } + return ret; +} + +static JSValue js_create_desc(JSContext *ctx, JSValue val, + JSValue getter, JSValue setter, + int flags) +{ + JSValue ret; + ret = JS_NewObject(ctx); + if (JS_IsException(ret)) + return ret; + if (flags & JS_PROP_HAS_GET) { + JS_DefinePropertyValue(ctx, ret, JS_ATOM_get, js_dup(getter), + JS_PROP_C_W_E); + } + if (flags & JS_PROP_HAS_SET) { + JS_DefinePropertyValue(ctx, ret, JS_ATOM_set, js_dup(setter), + JS_PROP_C_W_E); + } + if (flags & JS_PROP_HAS_VALUE) { + JS_DefinePropertyValue(ctx, ret, JS_ATOM_value, js_dup(val), + JS_PROP_C_W_E); + } + if (flags & JS_PROP_HAS_WRITABLE) { + JS_DefinePropertyValue(ctx, ret, JS_ATOM_writable, + js_bool(flags & JS_PROP_WRITABLE), + JS_PROP_C_W_E); + } + if (flags & JS_PROP_HAS_ENUMERABLE) { + JS_DefinePropertyValue(ctx, ret, JS_ATOM_enumerable, + js_bool(flags & JS_PROP_ENUMERABLE), + JS_PROP_C_W_E); + } + if (flags & JS_PROP_HAS_CONFIGURABLE) { + JS_DefinePropertyValue(ctx, ret, JS_ATOM_configurable, + js_bool(flags & JS_PROP_CONFIGURABLE), + JS_PROP_C_W_E); + } + return ret; +} + +static int js_proxy_get_own_property(JSContext *ctx, JSPropertyDescriptor *pdesc, + JSValue obj, JSAtom prop) +{ + JSProxyData *s; + JSValue method, trap_result_obj, prop_val; + int res, target_desc_ret, ret; + JSObject *p; + JSValue args[2]; + JSPropertyDescriptor result_desc, target_desc; + + s = get_proxy_method(ctx, &method, obj, JS_ATOM_getOwnPropertyDescriptor); + if (!s) + return -1; + p = JS_VALUE_GET_OBJ(s->target); + if (JS_IsUndefined(method)) { + return JS_GetOwnPropertyInternal(ctx, pdesc, p, prop); + } + prop_val = JS_AtomToValue(ctx, prop); + if (JS_IsException(prop_val)) { + JS_FreeValue(ctx, method); + return -1; + } + args[0] = s->target; + args[1] = prop_val; + trap_result_obj = JS_CallFree(ctx, method, s->handler, 2, args); + JS_FreeValue(ctx, prop_val); + if (JS_IsException(trap_result_obj)) + return -1; + if (!JS_IsObject(trap_result_obj) && !JS_IsUndefined(trap_result_obj)) { + JS_FreeValue(ctx, trap_result_obj); + goto fail; + } + target_desc_ret = JS_GetOwnPropertyInternal(ctx, &target_desc, p, prop); + if (target_desc_ret < 0) { + JS_FreeValue(ctx, trap_result_obj); + return -1; + } + if (target_desc_ret) + js_free_desc(ctx, &target_desc); + if (JS_IsUndefined(trap_result_obj)) { + if (target_desc_ret) { + if (!(target_desc.flags & JS_PROP_CONFIGURABLE) || !p->extensible) + goto fail; + } + ret = FALSE; + } else { + int flags1, extensible_target; + extensible_target = JS_IsExtensible(ctx, s->target); + if (extensible_target < 0) { + JS_FreeValue(ctx, trap_result_obj); + return -1; + } + res = js_obj_to_desc(ctx, &result_desc, trap_result_obj); + JS_FreeValue(ctx, trap_result_obj); + if (res < 0) + return -1; + + if (target_desc_ret) { + /* convert result_desc.flags to defineProperty flags */ + flags1 = result_desc.flags | JS_PROP_HAS_CONFIGURABLE | JS_PROP_HAS_ENUMERABLE; + if (result_desc.flags & JS_PROP_GETSET) + flags1 |= JS_PROP_HAS_GET | JS_PROP_HAS_SET; + else + flags1 |= JS_PROP_HAS_VALUE | JS_PROP_HAS_WRITABLE; + /* XXX: not complete check: need to compare value & + getter/setter as in defineproperty */ + if (!check_define_prop_flags(target_desc.flags, flags1)) + goto fail1; + } else { + if (!extensible_target) + goto fail1; + } + if (!(result_desc.flags & JS_PROP_CONFIGURABLE)) { + if (!target_desc_ret || (target_desc.flags & JS_PROP_CONFIGURABLE)) + goto fail1; + if ((result_desc.flags & + (JS_PROP_GETSET | JS_PROP_WRITABLE)) == 0 && + target_desc_ret && + (target_desc.flags & JS_PROP_WRITABLE) != 0) { + /* proxy-missing-checks */ + fail1: + js_free_desc(ctx, &result_desc); + fail: + JS_ThrowTypeError(ctx, "proxy: inconsistent getOwnPropertyDescriptor"); + return -1; + } + } + ret = TRUE; + if (pdesc) { + *pdesc = result_desc; + } else { + js_free_desc(ctx, &result_desc); + } + } + return ret; +} + +static int js_proxy_define_own_property(JSContext *ctx, JSValue obj, + JSAtom prop, JSValue val, + JSValue getter, JSValue setter, + int flags) +{ + JSProxyData *s; + JSValue method, ret1, prop_val, desc_val; + int res, ret; + JSObject *p; + JSValue args[3]; + JSPropertyDescriptor desc; + BOOL setting_not_configurable; + + s = get_proxy_method(ctx, &method, obj, JS_ATOM_defineProperty); + if (!s) + return -1; + if (JS_IsUndefined(method)) { + return JS_DefineProperty(ctx, s->target, prop, val, getter, setter, flags); + } + prop_val = JS_AtomToValue(ctx, prop); + if (JS_IsException(prop_val)) { + JS_FreeValue(ctx, method); + return -1; + } + desc_val = js_create_desc(ctx, val, getter, setter, flags); + if (JS_IsException(desc_val)) { + JS_FreeValue(ctx, prop_val); + JS_FreeValue(ctx, method); + return -1; + } + args[0] = s->target; + args[1] = prop_val; + args[2] = desc_val; + ret1 = JS_CallFree(ctx, method, s->handler, 3, args); + JS_FreeValue(ctx, prop_val); + JS_FreeValue(ctx, desc_val); + if (JS_IsException(ret1)) + return -1; + ret = JS_ToBoolFree(ctx, ret1); + if (!ret) { + if (flags & JS_PROP_THROW) { + JS_ThrowTypeError(ctx, "proxy: defineProperty exception"); + return -1; + } else { + return 0; + } + } + p = JS_VALUE_GET_OBJ(s->target); + res = JS_GetOwnPropertyInternal(ctx, &desc, p, prop); + if (res < 0) + return -1; + setting_not_configurable = ((flags & (JS_PROP_HAS_CONFIGURABLE | + JS_PROP_CONFIGURABLE)) == + JS_PROP_HAS_CONFIGURABLE); + if (!res) { + if (!p->extensible || setting_not_configurable) + goto fail; + } else { + if (!check_define_prop_flags(desc.flags, flags) || + ((desc.flags & JS_PROP_CONFIGURABLE) && setting_not_configurable)) { + goto fail1; + } + if (flags & (JS_PROP_HAS_GET | JS_PROP_HAS_SET)) { + if ((desc.flags & (JS_PROP_GETSET | JS_PROP_CONFIGURABLE)) == + JS_PROP_GETSET) { + if ((flags & JS_PROP_HAS_GET) && + !js_same_value(ctx, getter, desc.getter)) { + goto fail1; + } + if ((flags & JS_PROP_HAS_SET) && + !js_same_value(ctx, setter, desc.setter)) { + goto fail1; + } + } + } else if (flags & JS_PROP_HAS_VALUE) { + if ((desc.flags & (JS_PROP_CONFIGURABLE | JS_PROP_WRITABLE)) == + JS_PROP_WRITABLE && !(flags & JS_PROP_WRITABLE)) { + /* missing-proxy-check feature */ + goto fail1; + } else if ((desc.flags & (JS_PROP_CONFIGURABLE | JS_PROP_WRITABLE)) == 0 && + !js_same_value(ctx, val, desc.value)) { + goto fail1; + } + } + if (flags & JS_PROP_HAS_WRITABLE) { + if ((desc.flags & (JS_PROP_GETSET | JS_PROP_CONFIGURABLE | + JS_PROP_WRITABLE)) == JS_PROP_WRITABLE) { + /* proxy-missing-checks */ + fail1: + js_free_desc(ctx, &desc); + fail: + JS_ThrowTypeError(ctx, "proxy: inconsistent defineProperty"); + return -1; + } + } + js_free_desc(ctx, &desc); + } + return 1; +} + +static int js_proxy_delete_property(JSContext *ctx, JSValue obj, + JSAtom atom) +{ + JSProxyData *s; + JSValue method, ret, atom_val; + int res, res2, is_extensible; + JSValue args[2]; + + s = get_proxy_method(ctx, &method, obj, JS_ATOM_deleteProperty); + if (!s) + return -1; + if (JS_IsUndefined(method)) { + return JS_DeleteProperty(ctx, s->target, atom, 0); + } + atom_val = JS_AtomToValue(ctx, atom);; + if (JS_IsException(atom_val)) { + JS_FreeValue(ctx, method); + return -1; + } + args[0] = s->target; + args[1] = atom_val; + ret = JS_CallFree(ctx, method, s->handler, 2, args); + JS_FreeValue(ctx, atom_val); + if (JS_IsException(ret)) + return -1; + res = JS_ToBoolFree(ctx, ret); + if (res) { + JSPropertyDescriptor desc; + res2 = JS_GetOwnPropertyInternal(ctx, &desc, JS_VALUE_GET_OBJ(s->target), atom); + if (res2 < 0) + return -1; + if (res2) { + if (!(desc.flags & JS_PROP_CONFIGURABLE)) + goto fail; + is_extensible = JS_IsExtensible(ctx, s->target); + if (is_extensible < 0) + goto fail1; + if (!is_extensible) { + /* proxy-missing-checks */ + fail: + JS_ThrowTypeError(ctx, "proxy: inconsistent deleteProperty"); + fail1: + js_free_desc(ctx, &desc); + return -1; + } + js_free_desc(ctx, &desc); + } + } + return res; +} + +/* return the index of the property or -1 if not found */ +static int find_prop_key(const JSPropertyEnum *tab, int n, JSAtom atom) +{ + int i; + for(i = 0; i < n; i++) { + if (tab[i].atom == atom) + return i; + } + return -1; +} + +static int js_proxy_get_own_property_names(JSContext *ctx, + JSPropertyEnum **ptab, + uint32_t *plen, + JSValue obj) +{ + JSProxyData *s; + JSValue method, prop_array, val; + uint32_t len, i, len2; + JSPropertyEnum *tab, *tab2; + JSAtom atom; + JSPropertyDescriptor desc; + int res, is_extensible, idx; + + s = get_proxy_method(ctx, &method, obj, JS_ATOM_ownKeys); + if (!s) + return -1; + if (JS_IsUndefined(method)) { + return JS_GetOwnPropertyNamesInternal(ctx, ptab, plen, + JS_VALUE_GET_OBJ(s->target), + JS_GPN_STRING_MASK | JS_GPN_SYMBOL_MASK); + } + prop_array = JS_CallFree(ctx, method, s->handler, 1, &s->target); + if (JS_IsException(prop_array)) + return -1; + tab = NULL; + len = 0; + tab2 = NULL; + len2 = 0; + if (js_get_length32(ctx, &len, prop_array)) + goto fail; + if (len > 0) { + tab = js_mallocz(ctx, sizeof(tab[0]) * len); + if (!tab) + goto fail; + } + for(i = 0; i < len; i++) { + val = JS_GetPropertyUint32(ctx, prop_array, i); + if (JS_IsException(val)) + goto fail; + if (!JS_IsString(val) && !JS_IsSymbol(val)) { + JS_FreeValue(ctx, val); + JS_ThrowTypeError(ctx, "proxy: properties must be strings or symbols"); + goto fail; + } + atom = JS_ValueToAtom(ctx, val); + JS_FreeValue(ctx, val); + if (atom == JS_ATOM_NULL) + goto fail; + tab[i].atom = atom; + tab[i].is_enumerable = FALSE; /* XXX: redundant? */ + } + + /* check duplicate properties (XXX: inefficient, could store the + * properties an a temporary object to use the hash) */ + for(i = 1; i < len; i++) { + if (find_prop_key(tab, i, tab[i].atom) >= 0) { + JS_ThrowTypeError(ctx, "proxy: duplicate property"); + goto fail; + } + } + + is_extensible = JS_IsExtensible(ctx, s->target); + if (is_extensible < 0) + goto fail; + + /* check if there are non configurable properties */ + if (s->is_revoked) { + JS_ThrowTypeErrorRevokedProxy(ctx); + goto fail; + } + if (JS_GetOwnPropertyNamesInternal(ctx, &tab2, &len2, JS_VALUE_GET_OBJ(s->target), + JS_GPN_STRING_MASK | JS_GPN_SYMBOL_MASK)) + goto fail; + for(i = 0; i < len2; i++) { + if (s->is_revoked) { + JS_ThrowTypeErrorRevokedProxy(ctx); + goto fail; + } + res = JS_GetOwnPropertyInternal(ctx, &desc, JS_VALUE_GET_OBJ(s->target), + tab2[i].atom); + if (res < 0) + goto fail; + if (res) { /* safety, property should be found */ + js_free_desc(ctx, &desc); + if (!(desc.flags & JS_PROP_CONFIGURABLE) || !is_extensible) { + idx = find_prop_key(tab, len, tab2[i].atom); + if (idx < 0) { + JS_ThrowTypeError(ctx, "proxy: target property must be present in proxy ownKeys"); + goto fail; + } + /* mark the property as found */ + if (!is_extensible) + tab[idx].is_enumerable = TRUE; + } + } + } + if (!is_extensible) { + /* check that all property in 'tab' were checked */ + for(i = 0; i < len; i++) { + if (!tab[i].is_enumerable) { + JS_ThrowTypeError(ctx, "proxy: property not present in target were returned by non extensible proxy"); + goto fail; + } + } + } + + js_free_prop_enum(ctx, tab2, len2); + JS_FreeValue(ctx, prop_array); + *ptab = tab; + *plen = len; + return 0; + fail: + js_free_prop_enum(ctx, tab2, len2); + js_free_prop_enum(ctx, tab, len); + JS_FreeValue(ctx, prop_array); + return -1; +} + +static JSValue js_proxy_call_constructor(JSContext *ctx, JSValue func_obj, + JSValue new_target, + int argc, JSValue *argv) +{ + JSProxyData *s; + JSValue method, arg_array, ret; + JSValue args[3]; + + s = get_proxy_method(ctx, &method, func_obj, JS_ATOM_construct); + if (!s) + return JS_EXCEPTION; + if (!JS_IsConstructor(ctx, s->target)) + return JS_ThrowTypeError(ctx, "not a constructor"); + if (JS_IsUndefined(method)) + return JS_CallConstructor2(ctx, s->target, new_target, argc, argv); + arg_array = js_create_array(ctx, argc, argv); + if (JS_IsException(arg_array)) { + ret = JS_EXCEPTION; + goto fail; + } + args[0] = s->target; + args[1] = arg_array; + args[2] = new_target; + ret = JS_Call(ctx, method, s->handler, 3, args); + if (!JS_IsException(ret) && JS_VALUE_GET_TAG(ret) != JS_TAG_OBJECT) { + JS_FreeValue(ctx, ret); + ret = JS_ThrowTypeErrorNotAnObject(ctx); + } + fail: + JS_FreeValue(ctx, method); + JS_FreeValue(ctx, arg_array); + return ret; +} + +static JSValue js_proxy_call(JSContext *ctx, JSValue func_obj, + JSValue this_obj, + int argc, JSValue *argv, int flags) +{ + JSProxyData *s; + JSValue method, arg_array, ret; + JSValue args[3]; + + if (flags & JS_CALL_FLAG_CONSTRUCTOR) + return js_proxy_call_constructor(ctx, func_obj, this_obj, argc, argv); + + s = get_proxy_method(ctx, &method, func_obj, JS_ATOM_apply); + if (!s) + return JS_EXCEPTION; + if (!s->is_func) { + JS_FreeValue(ctx, method); + return JS_ThrowTypeError(ctx, "not a function"); + } + if (JS_IsUndefined(method)) + return JS_Call(ctx, s->target, this_obj, argc, argv); + arg_array = js_create_array(ctx, argc, argv); + if (JS_IsException(arg_array)) { + ret = JS_EXCEPTION; + goto fail; + } + args[0] = s->target; + args[1] = this_obj; + args[2] = arg_array; + ret = JS_Call(ctx, method, s->handler, 3, args); + fail: + JS_FreeValue(ctx, method); + JS_FreeValue(ctx, arg_array); + return ret; +} + +static int js_proxy_isArray(JSContext *ctx, JSValue obj) +{ + JSProxyData *s = JS_GetOpaque(obj, JS_CLASS_PROXY); + if (!s) + return FALSE; + + if (js_check_stack_overflow(ctx->rt, 0)) { + JS_ThrowStackOverflow(ctx); + return -1; + } + + if (s->is_revoked) { + JS_ThrowTypeErrorRevokedProxy(ctx); + return -1; + } + return JS_IsArray(ctx, s->target); +} + +static const JSClassExoticMethods js_proxy_exotic_methods = { + .get_own_property = js_proxy_get_own_property, + .define_own_property = js_proxy_define_own_property, + .delete_property = js_proxy_delete_property, + .get_own_property_names = js_proxy_get_own_property_names, + .has_property = js_proxy_has, + .get_property = js_proxy_get, + .set_property = js_proxy_set, +}; + +static JSValue js_proxy_constructor(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + JSValue target, handler; + JSValue obj; + JSProxyData *s; + + target = argv[0]; + handler = argv[1]; + if (JS_VALUE_GET_TAG(target) != JS_TAG_OBJECT || + JS_VALUE_GET_TAG(handler) != JS_TAG_OBJECT) + return JS_ThrowTypeErrorNotAnObject(ctx); + + obj = JS_NewObjectProtoClass(ctx, JS_NULL, JS_CLASS_PROXY); + if (JS_IsException(obj)) + return obj; + s = js_malloc(ctx, sizeof(JSProxyData)); + if (!s) { + JS_FreeValue(ctx, obj); + return JS_EXCEPTION; + } + s->target = js_dup(target); + s->handler = js_dup(handler); + s->is_func = JS_IsFunction(ctx, target); + s->is_revoked = FALSE; + JS_SetOpaqueInternal(obj, s); + JS_SetConstructorBit(ctx, obj, JS_IsConstructor(ctx, target)); + return obj; +} + +static JSValue js_proxy_revoke(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv, int magic, + JSValue *func_data) +{ + JSProxyData *s = JS_GetOpaque(func_data[0], JS_CLASS_PROXY); + if (s) { + /* We do not free the handler and target in case they are + referenced as constants in the C call stack */ + s->is_revoked = TRUE; + JS_FreeValue(ctx, func_data[0]); + func_data[0] = JS_NULL; + } + return JS_UNDEFINED; +} + +static JSValue js_proxy_revoke_constructor(JSContext *ctx, + JSValue proxy_obj) +{ + return JS_NewCFunctionData(ctx, js_proxy_revoke, 0, 0, 1, &proxy_obj); +} + +static JSValue js_proxy_revocable(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + JSValue proxy_obj, revoke_obj = JS_UNDEFINED, obj; + + proxy_obj = js_proxy_constructor(ctx, JS_UNDEFINED, argc, argv); + if (JS_IsException(proxy_obj)) + goto fail; + revoke_obj = js_proxy_revoke_constructor(ctx, proxy_obj); + if (JS_IsException(revoke_obj)) + goto fail; + obj = JS_NewObject(ctx); + if (JS_IsException(obj)) + goto fail; + // XXX: exceptions? + JS_DefinePropertyValue(ctx, obj, JS_ATOM_proxy, proxy_obj, JS_PROP_C_W_E); + JS_DefinePropertyValue(ctx, obj, JS_ATOM_revoke, revoke_obj, JS_PROP_C_W_E); + return obj; + fail: + JS_FreeValue(ctx, proxy_obj); + JS_FreeValue(ctx, revoke_obj); + return JS_EXCEPTION; +} + +static const JSCFunctionListEntry js_proxy_funcs[] = { + JS_CFUNC_DEF("revocable", 2, js_proxy_revocable ), +}; + +static const JSClassShortDef js_proxy_class_def[] = { + { JS_ATOM_Object, js_proxy_finalizer, js_proxy_mark }, /* JS_CLASS_PROXY */ +}; + +void JS_AddIntrinsicProxy(JSContext *ctx) +{ + JSRuntime *rt = ctx->rt; + JSValue obj1; + + if (!JS_IsRegisteredClass(rt, JS_CLASS_PROXY)) { + init_class_range(rt, js_proxy_class_def, JS_CLASS_PROXY, + countof(js_proxy_class_def)); + rt->class_array[JS_CLASS_PROXY].exotic = &js_proxy_exotic_methods; + rt->class_array[JS_CLASS_PROXY].call = js_proxy_call; + } + + obj1 = JS_NewCFunction2(ctx, js_proxy_constructor, "Proxy", 2, + JS_CFUNC_constructor, 0); + JS_SetConstructorBit(ctx, obj1, TRUE); + JS_SetPropertyFunctionList(ctx, obj1, js_proxy_funcs, + countof(js_proxy_funcs)); + JS_DefinePropertyValueStr(ctx, ctx->global_obj, "Proxy", + obj1, JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE); +} + +/* Symbol */ + +static JSValue js_symbol_constructor(JSContext *ctx, JSValue new_target, + int argc, JSValue *argv) +{ + JSValue str; + JSString *p; + + if (!JS_IsUndefined(new_target)) + return JS_ThrowTypeError(ctx, "not a constructor"); + if (argc == 0 || JS_IsUndefined(argv[0])) { + p = NULL; + } else { + str = JS_ToString(ctx, argv[0]); + if (JS_IsException(str)) + return JS_EXCEPTION; + p = JS_VALUE_GET_STRING(str); + } + return JS_NewSymbolInternal(ctx, p, JS_ATOM_TYPE_SYMBOL); +} + +static JSValue js_thisSymbolValue(JSContext *ctx, JSValue this_val) +{ + if (JS_VALUE_GET_TAG(this_val) == JS_TAG_SYMBOL) + return js_dup(this_val); + + if (JS_VALUE_GET_TAG(this_val) == JS_TAG_OBJECT) { + JSObject *p = JS_VALUE_GET_OBJ(this_val); + if (p->class_id == JS_CLASS_SYMBOL) { + if (JS_VALUE_GET_TAG(p->u.object_data) == JS_TAG_SYMBOL) + return js_dup(p->u.object_data); + } + } + return JS_ThrowTypeError(ctx, "not a symbol"); +} + +static JSValue js_symbol_toString(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + JSValue val, ret; + val = js_thisSymbolValue(ctx, this_val); + if (JS_IsException(val)) + return val; + /* XXX: use JS_ToStringInternal() with a flags */ + ret = js_string_constructor(ctx, JS_UNDEFINED, 1, &val); + JS_FreeValue(ctx, val); + return ret; +} + +static JSValue js_symbol_valueOf(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + return js_thisSymbolValue(ctx, this_val); +} + +static JSValue js_symbol_get_description(JSContext *ctx, JSValue this_val) +{ + JSValue val, ret; + JSAtomStruct *p; + + val = js_thisSymbolValue(ctx, this_val); + if (JS_IsException(val)) + return val; + p = JS_VALUE_GET_PTR(val); + if (p->len == 0 && p->is_wide_char != 0) { + ret = JS_UNDEFINED; + } else { + ret = JS_AtomToString(ctx, js_get_atom_index(ctx->rt, p)); + } + JS_FreeValue(ctx, val); + return ret; +} + +static const JSCFunctionListEntry js_symbol_proto_funcs[] = { + JS_CFUNC_DEF("toString", 0, js_symbol_toString ), + JS_CFUNC_DEF("valueOf", 0, js_symbol_valueOf ), + // XXX: should have writable: false + JS_CFUNC_DEF("[Symbol.toPrimitive]", 1, js_symbol_valueOf ), + JS_PROP_STRING_DEF("[Symbol.toStringTag]", "Symbol", JS_PROP_CONFIGURABLE ), + JS_CGETSET_DEF("description", js_symbol_get_description, NULL ), +}; + +static JSValue js_symbol_for(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + JSValue str; + + str = JS_ToString(ctx, argv[0]); + if (JS_IsException(str)) + return JS_EXCEPTION; + return JS_NewSymbolInternal(ctx, JS_VALUE_GET_STRING(str), JS_ATOM_TYPE_GLOBAL_SYMBOL); +} + +static JSValue js_symbol_keyFor(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + JSAtomStruct *p; + + if (!JS_IsSymbol(argv[0])) + return JS_ThrowTypeError(ctx, "not a symbol"); + p = JS_VALUE_GET_PTR(argv[0]); + if (p->atom_type != JS_ATOM_TYPE_GLOBAL_SYMBOL) + return JS_UNDEFINED; + return js_dup(JS_MKPTR(JS_TAG_STRING, p)); +} + +static const JSCFunctionListEntry js_symbol_funcs[] = { + JS_CFUNC_DEF("for", 1, js_symbol_for ), + JS_CFUNC_DEF("keyFor", 1, js_symbol_keyFor ), +}; + +/* Set/Map/WeakSet/WeakMap */ + +typedef struct JSMapRecord { + int ref_count; /* used during enumeration to avoid freeing the record */ + BOOL empty; /* TRUE if the record is deleted */ + struct JSMapState *map; + struct list_head link; + struct list_head hash_link; + JSValue key; + JSValue value; +} JSMapRecord; + +typedef struct JSMapState { + BOOL is_weak; /* TRUE if WeakSet/WeakMap */ + struct list_head records; /* list of JSMapRecord.link */ + uint32_t record_count; + struct list_head *hash_table; + uint32_t hash_size; /* must be a power of two */ + uint32_t record_count_threshold; /* count at which a hash table + resize is needed */ +} JSMapState; + +#define MAGIC_SET (1 << 0) +#define MAGIC_WEAK (1 << 1) + +static JSValue js_map_constructor(JSContext *ctx, JSValue new_target, + int argc, JSValue *argv, int magic) +{ + JSMapState *s; + JSValue obj, adder = JS_UNDEFINED, iter = JS_UNDEFINED, next_method = JS_UNDEFINED; + JSValue arr; + BOOL is_set, is_weak; + + is_set = magic & MAGIC_SET; + is_weak = ((magic & MAGIC_WEAK) != 0); + obj = js_create_from_ctor(ctx, new_target, JS_CLASS_MAP + magic); + if (JS_IsException(obj)) + return JS_EXCEPTION; + s = js_mallocz(ctx, sizeof(*s)); + if (!s) + goto fail; + init_list_head(&s->records); + s->is_weak = is_weak; + JS_SetOpaqueInternal(obj, s); + s->hash_size = 1; + s->hash_table = js_malloc(ctx, sizeof(s->hash_table[0]) * s->hash_size); + if (!s->hash_table) + goto fail; + init_list_head(&s->hash_table[0]); + s->record_count_threshold = 4; + + arr = JS_UNDEFINED; + if (argc > 0) + arr = argv[0]; + if (!JS_IsUndefined(arr) && !JS_IsNull(arr)) { + JSValue item, ret; + BOOL done; + + adder = JS_GetProperty(ctx, obj, is_set ? JS_ATOM_add : JS_ATOM_set); + if (JS_IsException(adder)) + goto fail; + if (!JS_IsFunction(ctx, adder)) { + JS_ThrowTypeError(ctx, "set/add is not a function"); + goto fail; + } + + iter = JS_GetIterator(ctx, arr, FALSE); + if (JS_IsException(iter)) + goto fail; + next_method = JS_GetProperty(ctx, iter, JS_ATOM_next); + if (JS_IsException(next_method)) + goto fail; + + for(;;) { + item = JS_IteratorNext(ctx, iter, next_method, 0, NULL, &done); + if (JS_IsException(item)) + goto fail; + if (done) { + JS_FreeValue(ctx, item); + break; + } + if (is_set) { + ret = JS_Call(ctx, adder, obj, 1, &item); + if (JS_IsException(ret)) { + JS_FreeValue(ctx, item); + goto fail; + } + } else { + JSValue key, value; + JSValue args[2]; + key = JS_UNDEFINED; + value = JS_UNDEFINED; + if (!JS_IsObject(item)) { + JS_ThrowTypeErrorNotAnObject(ctx); + goto fail1; + } + key = JS_GetPropertyUint32(ctx, item, 0); + if (JS_IsException(key)) + goto fail1; + value = JS_GetPropertyUint32(ctx, item, 1); + if (JS_IsException(value)) + goto fail1; + args[0] = key; + args[1] = value; + ret = JS_Call(ctx, adder, obj, 2, args); + if (JS_IsException(ret)) { + fail1: + JS_FreeValue(ctx, item); + JS_FreeValue(ctx, key); + JS_FreeValue(ctx, value); + goto fail; + } + JS_FreeValue(ctx, key); + JS_FreeValue(ctx, value); + } + JS_FreeValue(ctx, ret); + JS_FreeValue(ctx, item); + } + JS_FreeValue(ctx, next_method); + JS_FreeValue(ctx, iter); + JS_FreeValue(ctx, adder); + } + return obj; + fail: + if (JS_IsObject(iter)) { + /* close the iterator object, preserving pending exception */ + JS_IteratorClose(ctx, iter, TRUE); + } + JS_FreeValue(ctx, next_method); + JS_FreeValue(ctx, iter); + JS_FreeValue(ctx, adder); + JS_FreeValue(ctx, obj); + return JS_EXCEPTION; +} + +/* XXX: could normalize strings to speed up comparison */ +static JSValue map_normalize_key(JSContext *ctx, JSValue key) +{ + uint32_t tag = JS_VALUE_GET_TAG(key); + /* convert -0.0 to +0.0 */ + if (JS_TAG_IS_FLOAT64(tag) && JS_VALUE_GET_FLOAT64(key) == 0.0) { + key = js_int32(0); + } + return key; +} + +/* XXX: better hash ? */ +static uint32_t map_hash_key(JSContext *ctx, JSValue key) +{ + uint32_t tag = JS_VALUE_GET_NORM_TAG(key); + uint32_t h; + double d; + JSFloat64Union u; + bf_t *a; + + switch(tag) { + case JS_TAG_BOOL: + h = JS_VALUE_GET_INT(key); + break; + case JS_TAG_STRING: + h = hash_string(JS_VALUE_GET_STRING(key), 0); + break; + case JS_TAG_OBJECT: + case JS_TAG_SYMBOL: + h = (uintptr_t)JS_VALUE_GET_PTR(key) * 3163; + break; + case JS_TAG_INT: + d = JS_VALUE_GET_INT(key); + goto hash_float64; + case JS_TAG_BIG_INT: + a = JS_GetBigInt(key); + h = hash_string8((void *)a->tab, a->len * sizeof(*a->tab), 0); + break; + case JS_TAG_FLOAT64: + d = JS_VALUE_GET_FLOAT64(key); + /* normalize the NaN */ + if (isnan(d)) + d = JS_FLOAT64_NAN; + hash_float64: + u.d = d; + h = (u.u32[0] ^ u.u32[1]) * 3163; + return h ^= JS_TAG_FLOAT64; + default: + h = 0; + break; + } + h ^= tag; + return h; +} + +static JSMapRecord *map_find_record(JSContext *ctx, JSMapState *s, + JSValue key) +{ + struct list_head *el; + JSMapRecord *mr; + uint32_t h; + h = map_hash_key(ctx, key) & (s->hash_size - 1); + list_for_each(el, &s->hash_table[h]) { + mr = list_entry(el, JSMapRecord, hash_link); + if (js_same_value_zero(ctx, mr->key, key)) + return mr; + } + return NULL; +} + +static void map_hash_resize(JSContext *ctx, JSMapState *s) +{ + uint32_t new_hash_size, i, h; + size_t slack; + struct list_head *new_hash_table, *el; + JSMapRecord *mr; + + /* XXX: no reporting of memory allocation failure */ + if (s->hash_size == 1) + new_hash_size = 4; + else + new_hash_size = s->hash_size * 2; + new_hash_table = js_realloc2(ctx, s->hash_table, + sizeof(new_hash_table[0]) * new_hash_size, &slack); + if (!new_hash_table) + return; + new_hash_size += slack / sizeof(*new_hash_table); + + for(i = 0; i < new_hash_size; i++) + init_list_head(&new_hash_table[i]); + + list_for_each(el, &s->records) { + mr = list_entry(el, JSMapRecord, link); + if (!mr->empty) { + h = map_hash_key(ctx, mr->key) & (new_hash_size - 1); + list_add_tail(&mr->hash_link, &new_hash_table[h]); + } + } + s->hash_table = new_hash_table; + s->hash_size = new_hash_size; + s->record_count_threshold = new_hash_size * 2; +} + +static JSWeakRefRecord **get_first_weak_ref(JSValue key) +{ + switch (JS_VALUE_GET_TAG(key)) { + case JS_TAG_OBJECT: + { + JSObject *p = JS_VALUE_GET_OBJ(key); + return &p->first_weak_ref; + } + break; + case JS_TAG_SYMBOL: + { + JSAtomStruct *p = JS_VALUE_GET_PTR(key); + return &p->first_weak_ref; + } + break; + default: + abort(); + } + return NULL; // pacify compiler +} + +static JSMapRecord *map_add_record(JSContext *ctx, JSMapState *s, + JSValue key) +{ + uint32_t h; + JSMapRecord *mr; + + mr = js_malloc(ctx, sizeof(*mr)); + if (!mr) + return NULL; + mr->ref_count = 1; + mr->map = s; + mr->empty = FALSE; + if (s->is_weak) { + JSWeakRefRecord *wr = js_malloc(ctx, sizeof(*wr)); + if (!wr) { + js_free(ctx, mr); + return NULL; + } + wr->kind = JS_WEAK_REF_KIND_MAP; + wr->u.map_record = mr; + insert_weakref_record(key, wr); + } else { + js_dup(key); + } + mr->key = key; + h = map_hash_key(ctx, key) & (s->hash_size - 1); + list_add_tail(&mr->hash_link, &s->hash_table[h]); + list_add_tail(&mr->link, &s->records); + s->record_count++; + if (s->record_count >= s->record_count_threshold) { + map_hash_resize(ctx, s); + } + return mr; +} + +/* Remove the weak reference from the object weak + reference list. we don't use a doubly linked list to + save space, assuming a given object has few weak + references to it */ +static void delete_map_weak_ref(JSRuntime *rt, JSMapRecord *mr) +{ + JSWeakRefRecord **pwr, *wr; + + pwr = get_first_weak_ref(mr->key); + for(;;) { + wr = *pwr; + assert(wr != NULL); + if (wr->kind == JS_WEAK_REF_KIND_MAP && wr->u.map_record == mr) + break; + pwr = &wr->next_weak_ref; + } + *pwr = wr->next_weak_ref; + js_free_rt(rt, wr); +} + +static void map_delete_record(JSRuntime *rt, JSMapState *s, JSMapRecord *mr) +{ + if (mr->empty) + return; + list_del(&mr->hash_link); + if (s->is_weak) { + delete_map_weak_ref(rt, mr); + } else { + JS_FreeValueRT(rt, mr->key); + } + JS_FreeValueRT(rt, mr->value); + if (--mr->ref_count == 0) { + list_del(&mr->link); + js_free_rt(rt, mr); + } else { + /* keep a zombie record for iterators */ + mr->empty = TRUE; + mr->key = JS_UNDEFINED; + mr->value = JS_UNDEFINED; + } + s->record_count--; +} + +static void map_decref_record(JSRuntime *rt, JSMapRecord *mr) +{ + if (--mr->ref_count == 0) { + /* the record can be safely removed */ + assert(mr->empty); + list_del(&mr->link); + js_free_rt(rt, mr); + } +} + +static JSValue js_map_set(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv, int magic) +{ + JSMapState *s = JS_GetOpaque2(ctx, this_val, JS_CLASS_MAP + magic); + JSMapRecord *mr; + JSValue key, value; + int is_set; + + if (!s) + return JS_EXCEPTION; + is_set = (magic & MAGIC_SET); + key = map_normalize_key(ctx, argv[0]); + if (s->is_weak && !is_valid_weakref_target(key)) + return JS_ThrowTypeError(ctx, "invalid value used as %s key", is_set ? "WeakSet" : "WeakMap"); + if (is_set) + value = JS_UNDEFINED; + else + value = argv[1]; + mr = map_find_record(ctx, s, key); + if (mr) { + JS_FreeValue(ctx, mr->value); + } else { + mr = map_add_record(ctx, s, key); + if (!mr) + return JS_EXCEPTION; + } + mr->value = js_dup(value); + return js_dup(this_val); +} + +static JSValue js_map_get(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv, int magic) +{ + JSMapState *s = JS_GetOpaque2(ctx, this_val, JS_CLASS_MAP + magic); + JSMapRecord *mr; + JSValue key; + + if (!s) + return JS_EXCEPTION; + key = map_normalize_key(ctx, argv[0]); + mr = map_find_record(ctx, s, key); + if (!mr) + return JS_UNDEFINED; + else + return js_dup(mr->value); +} + +static JSValue js_map_has(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv, int magic) +{ + JSMapState *s = JS_GetOpaque2(ctx, this_val, JS_CLASS_MAP + magic); + JSMapRecord *mr; + JSValue key; + + if (!s) + return JS_EXCEPTION; + key = map_normalize_key(ctx, argv[0]); + mr = map_find_record(ctx, s, key); + return js_bool(mr != NULL); +} + +static JSValue js_map_delete(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv, int magic) +{ + JSMapState *s = JS_GetOpaque2(ctx, this_val, JS_CLASS_MAP + magic); + JSMapRecord *mr; + JSValue key; + + if (!s) + return JS_EXCEPTION; + key = map_normalize_key(ctx, argv[0]); + mr = map_find_record(ctx, s, key); + if (!mr) + return JS_FALSE; + map_delete_record(ctx->rt, s, mr); + return JS_TRUE; +} + +static JSValue js_map_clear(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv, int magic) +{ + JSMapState *s = JS_GetOpaque2(ctx, this_val, JS_CLASS_MAP + magic); + struct list_head *el, *el1; + JSMapRecord *mr; + + if (!s) + return JS_EXCEPTION; + list_for_each_safe(el, el1, &s->records) { + mr = list_entry(el, JSMapRecord, link); + map_delete_record(ctx->rt, s, mr); + } + return JS_UNDEFINED; +} + +static JSValue js_map_get_size(JSContext *ctx, JSValue this_val, int magic) +{ + JSMapState *s = JS_GetOpaque2(ctx, this_val, JS_CLASS_MAP + magic); + if (!s) + return JS_EXCEPTION; + return js_uint32(s->record_count); +} + +static JSValue js_map_forEach(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv, int magic) +{ + JSMapState *s = JS_GetOpaque2(ctx, this_val, JS_CLASS_MAP + magic); + JSValue func, this_arg; + JSValue ret, args[3]; + struct list_head *el; + JSMapRecord *mr; + + if (!s) + return JS_EXCEPTION; + func = argv[0]; + if (argc > 1) + this_arg = argv[1]; + else + this_arg = JS_UNDEFINED; + if (check_function(ctx, func)) + return JS_EXCEPTION; + /* Note: the list can be modified while traversing it, but the + current element is locked */ + el = s->records.next; + while (el != &s->records) { + mr = list_entry(el, JSMapRecord, link); + if (!mr->empty) { + mr->ref_count++; + /* must duplicate in case the record is deleted */ + args[1] = js_dup(mr->key); + if (magic) + args[0] = args[1]; + else + args[0] = js_dup(mr->value); + args[2] = this_val; + ret = JS_Call(ctx, func, this_arg, 3, args); + JS_FreeValue(ctx, args[0]); + if (!magic) + JS_FreeValue(ctx, args[1]); + el = el->next; + map_decref_record(ctx->rt, mr); + if (JS_IsException(ret)) + return ret; + JS_FreeValue(ctx, ret); + } else { + el = el->next; + } + } + return JS_UNDEFINED; +} + +static JSValue js_map_groupBy(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + JSValue cb, res, iter, next, groups, k, v, prop; + JSValue args[2]; + int64_t idx; + BOOL done; + + // "is function?" check must be observed before argv[0] is accessed + cb = argv[1]; + if (check_function(ctx, cb)) + return JS_EXCEPTION; + + iter = JS_GetIterator(ctx, argv[0], /*is_async*/FALSE); + if (JS_IsException(iter)) + return JS_EXCEPTION; + + k = JS_UNDEFINED; + v = JS_UNDEFINED; + prop = JS_UNDEFINED; + groups = JS_UNDEFINED; + + next = JS_GetProperty(ctx, iter, JS_ATOM_next); + if (JS_IsException(next)) + goto exception; + + groups = js_map_constructor(ctx, JS_UNDEFINED, 0, NULL, 0); + if (JS_IsException(groups)) + goto exception; + + for (idx = 0; ; idx++) { + v = JS_IteratorNext(ctx, iter, next, 0, NULL, &done); + if (JS_IsException(v)) + goto exception; + if (done) + break; // v is JS_UNDEFINED + + args[0] = v; + args[1] = js_int64(idx); + k = JS_Call(ctx, cb, ctx->global_obj, 2, args); + if (JS_IsException(k)) + goto exception; + + prop = js_map_get(ctx, groups, 1, &k, 0); + if (JS_IsException(prop)) + goto exception; + + if (JS_IsUndefined(prop)) { + prop = JS_NewArray(ctx); + if (JS_IsException(prop)) + goto exception; + args[0] = k; + args[1] = prop; + res = js_map_set(ctx, groups, 2, args, 0); + if (JS_IsException(res)) + goto exception; + JS_FreeValue(ctx, res); + } + + res = js_array_push(ctx, prop, 1, &v, /*unshift*/0); + if (JS_IsException(res)) + goto exception; + // res is an int64 + + JS_FreeValue(ctx, prop); + JS_FreeValue(ctx, k); + JS_FreeValue(ctx, v); + prop = JS_UNDEFINED; + k = JS_UNDEFINED; + v = JS_UNDEFINED; + } + + JS_FreeValue(ctx, iter); + JS_FreeValue(ctx, next); + return groups; + +exception: + JS_FreeValue(ctx, prop); + JS_FreeValue(ctx, k); + JS_FreeValue(ctx, v); + JS_FreeValue(ctx, groups); + JS_FreeValue(ctx, iter); + JS_FreeValue(ctx, next); + return JS_EXCEPTION; +} + +static void js_map_finalizer(JSRuntime *rt, JSValue val) +{ + JSObject *p; + JSMapState *s; + struct list_head *el, *el1; + JSMapRecord *mr; + + p = JS_VALUE_GET_OBJ(val); + s = p->u.map_state; + if (s) { + /* if the object is deleted we are sure that no iterator is + using it */ + list_for_each_safe(el, el1, &s->records) { + mr = list_entry(el, JSMapRecord, link); + if (!mr->empty) { + if (s->is_weak) + delete_map_weak_ref(rt, mr); + else + JS_FreeValueRT(rt, mr->key); + JS_FreeValueRT(rt, mr->value); + } + js_free_rt(rt, mr); + } + js_free_rt(rt, s->hash_table); + js_free_rt(rt, s); + } +} + +static void js_map_mark(JSRuntime *rt, JSValue val, JS_MarkFunc *mark_func) +{ + JSObject *p = JS_VALUE_GET_OBJ(val); + JSMapState *s; + struct list_head *el; + JSMapRecord *mr; + + s = p->u.map_state; + if (s) { + list_for_each(el, &s->records) { + mr = list_entry(el, JSMapRecord, link); + if (!s->is_weak) + JS_MarkValue(rt, mr->key, mark_func); + JS_MarkValue(rt, mr->value, mark_func); + } + } +} + +/* Map Iterator */ + +typedef struct JSMapIteratorData { + JSValue obj; + JSIteratorKindEnum kind; + JSMapRecord *cur_record; +} JSMapIteratorData; + +static void js_map_iterator_finalizer(JSRuntime *rt, JSValue val) +{ + JSObject *p; + JSMapIteratorData *it; + + p = JS_VALUE_GET_OBJ(val); + it = p->u.map_iterator_data; + if (it) { + /* During the GC sweep phase the Map finalizer may be + called before the Map iterator finalizer */ + if (JS_IsLiveObject(rt, it->obj) && it->cur_record) { + map_decref_record(rt, it->cur_record); + } + JS_FreeValueRT(rt, it->obj); + js_free_rt(rt, it); + } +} + +static void js_map_iterator_mark(JSRuntime *rt, JSValue val, + JS_MarkFunc *mark_func) +{ + JSObject *p = JS_VALUE_GET_OBJ(val); + JSMapIteratorData *it; + it = p->u.map_iterator_data; + if (it) { + /* the record is already marked by the object */ + JS_MarkValue(rt, it->obj, mark_func); + } +} + +static JSValue js_create_map_iterator(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv, int magic) +{ + JSIteratorKindEnum kind; + JSMapState *s; + JSMapIteratorData *it; + JSValue enum_obj; + + kind = magic >> 2; + magic &= 3; + s = JS_GetOpaque2(ctx, this_val, JS_CLASS_MAP + magic); + if (!s) + return JS_EXCEPTION; + enum_obj = JS_NewObjectClass(ctx, JS_CLASS_MAP_ITERATOR + magic); + if (JS_IsException(enum_obj)) + goto fail; + it = js_malloc(ctx, sizeof(*it)); + if (!it) { + JS_FreeValue(ctx, enum_obj); + goto fail; + } + it->obj = js_dup(this_val); + it->kind = kind; + it->cur_record = NULL; + JS_SetOpaqueInternal(enum_obj, it); + return enum_obj; + fail: + return JS_EXCEPTION; +} + +static JSValue js_map_iterator_next(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv, + BOOL *pdone, int magic) +{ + JSMapIteratorData *it; + JSMapState *s; + JSMapRecord *mr; + struct list_head *el; + + it = JS_GetOpaque2(ctx, this_val, JS_CLASS_MAP_ITERATOR + magic); + if (!it) { + *pdone = FALSE; + return JS_EXCEPTION; + } + if (JS_IsUndefined(it->obj)) + goto done; + s = JS_GetOpaque(it->obj, JS_CLASS_MAP + magic); + assert(s != NULL); + if (!it->cur_record) { + el = s->records.next; + } else { + mr = it->cur_record; + el = mr->link.next; + map_decref_record(ctx->rt, mr); /* the record can be freed here */ + } + for(;;) { + if (el == &s->records) { + /* no more record */ + it->cur_record = NULL; + JS_FreeValue(ctx, it->obj); + it->obj = JS_UNDEFINED; + done: + /* end of enumeration */ + *pdone = TRUE; + return JS_UNDEFINED; + } + mr = list_entry(el, JSMapRecord, link); + if (!mr->empty) + break; + /* get the next record */ + el = mr->link.next; + } + + /* lock the record so that it won't be freed */ + mr->ref_count++; + it->cur_record = mr; + *pdone = FALSE; + + if (it->kind == JS_ITERATOR_KIND_KEY) { + return js_dup(mr->key); + } else { + JSValue args[2]; + args[0] = mr->key; + if (magic) + args[1] = mr->key; + else + args[1] = mr->value; + if (it->kind == JS_ITERATOR_KIND_VALUE) { + return js_dup(args[1]); + } else { + return js_create_array(ctx, 2, args); + } + } +} + +static JSValue js_map_read(BCReaderState *s, int magic) +{ + JSContext *ctx = s->ctx; + JSValue obj, rv, argv[2]; + uint32_t i, prop_count; + + argv[0] = JS_UNDEFINED; + argv[1] = JS_UNDEFINED; + obj = js_map_constructor(ctx, JS_UNDEFINED, 0, NULL, magic); + if (JS_IsException(obj)) + return JS_EXCEPTION; + if (BC_add_object_ref(s, obj)) + goto fail; + if (bc_get_leb128(s, &prop_count)) + goto fail; + for(i = 0; i < prop_count; i++) { + argv[0] = JS_ReadObjectRec(s); + if (JS_IsException(argv[0])) + goto fail; + if (!(magic & MAGIC_SET)) { + argv[1] = JS_ReadObjectRec(s); + if (JS_IsException(argv[1])) + goto fail; + } + rv = js_map_set(ctx, obj, countof(argv), argv, magic); + if (JS_IsException(rv)) + goto fail; + JS_FreeValue(ctx, rv); + JS_FreeValue(ctx, argv[0]); + JS_FreeValue(ctx, argv[1]); + argv[0] = JS_UNDEFINED; + argv[1] = JS_UNDEFINED; + } + return obj; + fail: + JS_FreeValue(ctx, obj); + JS_FreeValue(ctx, argv[0]); + JS_FreeValue(ctx, argv[1]); + return JS_EXCEPTION; +} + +static int js_map_write(BCWriterState *s, struct JSMapState *map_state, + int magic) +{ + struct list_head *el; + JSMapRecord *mr; + + bc_put_leb128(s, map_state ? map_state->record_count : 0); + if (map_state) { + list_for_each(el, &map_state->records) { + mr = list_entry(el, JSMapRecord, link); + if (JS_WriteObjectRec(s, mr->key)) + return -1; + // mr->value is always JS_UNDEFINED for sets + if (!(magic & MAGIC_SET)) + if (JS_WriteObjectRec(s, mr->value)) + return -1; + } + } + + return 0; +} + +static JSValue JS_ReadMap(BCReaderState *s) +{ + return js_map_read(s, 0); +} + +static JSValue JS_ReadSet(BCReaderState *s) +{ + return js_map_read(s, MAGIC_SET); +} + +static int JS_WriteMap(BCWriterState *s, struct JSMapState *map_state) +{ + return js_map_write(s, map_state, 0); +} + +static int JS_WriteSet(BCWriterState *s, struct JSMapState *map_state) +{ + return js_map_write(s, map_state, MAGIC_SET); +} + +static int js_setlike_get_size(JSContext *ctx, JSValue setlike, int64_t *pout) +{ + JSMapState *s; + JSValue v; + double d; + + s = JS_GetOpaque(setlike, JS_CLASS_SET); + if (s) { + *pout = s->record_count; + } else { + v = JS_GetProperty(ctx, setlike, JS_ATOM_size); + if (JS_IsException(v)) + return -1; + if (JS_IsUndefined(v)) { + JS_ThrowTypeError(ctx, ".size is undefined"); + return -1; + } + if (JS_ToFloat64Free(ctx, &d, v) < 0) + return -1; + if (isnan(d)) { + JS_ThrowTypeError(ctx, ".size is not a number"); + return -1; + } + *pout = d; + } + return 0; +} + +static int js_setlike_get_has(JSContext *ctx, JSValue setlike, JSValue *pout) +{ + JSValue v; + + v = JS_GetProperty(ctx, setlike, JS_ATOM_has); + if (JS_IsException(v)) + return -1; + if (JS_IsUndefined(v)) { + JS_ThrowTypeError(ctx, ".has is undefined"); + return -1; + } + if (!JS_IsFunction(ctx, v)) { + JS_ThrowTypeError(ctx, ".has is not a function"); + JS_FreeValue(ctx, v); + return -1; + } + *pout = v; + return 0; +} + +static int js_setlike_get_keys(JSContext *ctx, JSValue setlike, JSValue *pout) +{ + JSValue v; + + v = JS_GetProperty(ctx, setlike, JS_ATOM_keys); + if (JS_IsException(v)) + return -1; + if (JS_IsUndefined(v)) { + JS_ThrowTypeError(ctx, ".keys is undefined"); + return -1; + } + if (!JS_IsFunction(ctx, v)) { + JS_ThrowTypeError(ctx, ".keys is not a function"); + JS_FreeValue(ctx, v); + return -1; + } + *pout = v; + return 0; +} + +static JSValue js_set_isDisjointFrom(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + JSValue item, iter, keys, has, next, rv, rval; + BOOL done, found; + JSMapState *s; + int64_t size; + int ok; + + has = JS_UNDEFINED; + iter = JS_UNDEFINED; + keys = JS_UNDEFINED; + next = JS_UNDEFINED; + rval = JS_EXCEPTION; + s = JS_GetOpaque2(ctx, this_val, JS_CLASS_SET); + if (!s) + goto exception; + // order matters! + if (js_setlike_get_size(ctx, argv[0], &size) < 0) + goto exception; + if (js_setlike_get_has(ctx, argv[0], &has) < 0) + goto exception; + if (js_setlike_get_keys(ctx, argv[0], &keys) < 0) + goto exception; + if (s->record_count > size) { + iter = JS_Call(ctx, keys, argv[0], 0, NULL); + if (JS_IsException(iter)) + goto exception; + next = JS_GetProperty(ctx, iter, JS_ATOM_next); + if (JS_IsException(next)) + goto exception; + found = FALSE; + do { + item = JS_IteratorNext(ctx, iter, next, 0, NULL, &done); + if (JS_IsException(item)) + goto exception; + if (done) // item is JS_UNDEFINED + break; + item = map_normalize_key(ctx, item); + found = (NULL != map_find_record(ctx, s, item)); + JS_FreeValue(ctx, item); + } while (!found); + } else { + iter = js_create_map_iterator(ctx, this_val, 0, NULL, MAGIC_SET); + if (JS_IsException(iter)) + goto exception; + found = FALSE; + do { + item = js_map_iterator_next(ctx, iter, 0, NULL, &done, MAGIC_SET); + if (JS_IsException(item)) + goto exception; + if (done) // item is JS_UNDEFINED + break; + rv = JS_Call(ctx, has, argv[0], 1, &item); + JS_FreeValue(ctx, item); + ok = JS_ToBoolFree(ctx, rv); // returns -1 if rv is JS_EXCEPTION + if (ok < 0) + goto exception; + found = (ok > 0); + } while (!found); + } + rval = !found ? JS_TRUE : JS_FALSE; +exception: + JS_FreeValue(ctx, has); + JS_FreeValue(ctx, keys); + JS_FreeValue(ctx, iter); + JS_FreeValue(ctx, next); + return rval; +} + +static JSValue js_set_isSubsetOf(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + JSValue item, iter, keys, has, next, rv, rval; + BOOL done, found; + JSMapState *s; + int64_t size; + int ok; + + has = JS_UNDEFINED; + iter = JS_UNDEFINED; + keys = JS_UNDEFINED; + next = JS_UNDEFINED; + rval = JS_EXCEPTION; + s = JS_GetOpaque2(ctx, this_val, JS_CLASS_SET); + if (!s) + goto exception; + // order matters! + if (js_setlike_get_size(ctx, argv[0], &size) < 0) + goto exception; + if (js_setlike_get_has(ctx, argv[0], &has) < 0) + goto exception; + if (js_setlike_get_keys(ctx, argv[0], &keys) < 0) + goto exception; + found = FALSE; + if (s->record_count > size) + goto fini; + iter = js_create_map_iterator(ctx, this_val, 0, NULL, MAGIC_SET); + if (JS_IsException(iter)) + goto exception; + found = TRUE; + do { + item = js_map_iterator_next(ctx, iter, 0, NULL, &done, MAGIC_SET); + if (JS_IsException(item)) + goto exception; + if (done) // item is JS_UNDEFINED + break; + rv = JS_Call(ctx, has, argv[0], 1, &item); + JS_FreeValue(ctx, item); + ok = JS_ToBoolFree(ctx, rv); // returns -1 if rv is JS_EXCEPTION + if (ok < 0) + goto exception; + found = (ok > 0); + } while (found); +fini: + rval = found ? JS_TRUE : JS_FALSE; +exception: + JS_FreeValue(ctx, has); + JS_FreeValue(ctx, keys); + JS_FreeValue(ctx, iter); + JS_FreeValue(ctx, next); + return rval; +} + +static JSValue js_set_isSupersetOf(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + JSValue item, iter, keys, has, next, rval; + BOOL done, found; + JSMapState *s; + int64_t size; + + has = JS_UNDEFINED; + iter = JS_UNDEFINED; + keys = JS_UNDEFINED; + next = JS_UNDEFINED; + rval = JS_EXCEPTION; + s = JS_GetOpaque2(ctx, this_val, JS_CLASS_SET); + if (!s) + goto exception; + // order matters! + if (js_setlike_get_size(ctx, argv[0], &size) < 0) + goto exception; + if (js_setlike_get_has(ctx, argv[0], &has) < 0) + goto exception; + if (js_setlike_get_keys(ctx, argv[0], &keys) < 0) + goto exception; + found = FALSE; + if (s->record_count < size) + goto fini; + iter = JS_Call(ctx, keys, argv[0], 0, NULL); + if (JS_IsException(iter)) + goto exception; + next = JS_GetProperty(ctx, iter, JS_ATOM_next); + if (JS_IsException(next)) + goto exception; + found = TRUE; + do { + item = JS_IteratorNext(ctx, iter, next, 0, NULL, &done); + if (JS_IsException(item)) + goto exception; + if (done) // item is JS_UNDEFINED + break; + item = map_normalize_key(ctx, item); + found = (NULL != map_find_record(ctx, s, item)); + JS_FreeValue(ctx, item); + } while (found); +fini: + rval = found ? JS_TRUE : JS_FALSE; +exception: + JS_FreeValue(ctx, has); + JS_FreeValue(ctx, keys); + JS_FreeValue(ctx, iter); + JS_FreeValue(ctx, next); + return rval; +} + +static JSValue js_set_intersection(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + JSValue newset, item, iter, keys, has, next, rv; + JSMapState *s, *t; + JSMapRecord *mr; + int64_t size; + BOOL done; + int ok; + + has = JS_UNDEFINED; + iter = JS_UNDEFINED; + keys = JS_UNDEFINED; + next = JS_UNDEFINED; + newset = JS_UNDEFINED; + s = JS_GetOpaque2(ctx, this_val, JS_CLASS_SET); + if (!s) + goto exception; + // order matters! + if (js_setlike_get_size(ctx, argv[0], &size) < 0) + goto exception; + if (js_setlike_get_has(ctx, argv[0], &has) < 0) + goto exception; + if (js_setlike_get_keys(ctx, argv[0], &keys) < 0) + goto exception; + if (s->record_count > size) { + iter = JS_Call(ctx, keys, argv[0], 0, NULL); + if (JS_IsException(iter)) + goto exception; + next = JS_GetProperty(ctx, iter, JS_ATOM_next); + if (JS_IsException(next)) + goto exception; + newset = js_map_constructor(ctx, JS_UNDEFINED, 0, NULL, MAGIC_SET); + if (JS_IsException(newset)) + goto exception; + t = JS_GetOpaque(newset, JS_CLASS_SET); + for (;;) { + item = JS_IteratorNext(ctx, iter, next, 0, NULL, &done); + if (JS_IsException(item)) + goto exception; + if (done) // item is JS_UNDEFINED + break; + item = map_normalize_key(ctx, item); + if (!map_find_record(ctx, s, item)) { + JS_FreeValue(ctx, item); + } else if (map_find_record(ctx, t, item)) { + JS_FreeValue(ctx, item); // no duplicates + } else if ((mr = map_add_record(ctx, t, item))) { + mr->value = JS_UNDEFINED; + } else { + JS_FreeValue(ctx, item); + goto exception; + } + } + } else { + iter = js_create_map_iterator(ctx, this_val, 0, NULL, MAGIC_SET); + if (JS_IsException(iter)) + goto exception; + newset = js_map_constructor(ctx, JS_UNDEFINED, 0, NULL, MAGIC_SET); + if (JS_IsException(newset)) + goto exception; + t = JS_GetOpaque(newset, JS_CLASS_SET); + for (;;) { + item = js_map_iterator_next(ctx, iter, 0, NULL, &done, MAGIC_SET); + if (JS_IsException(item)) + goto exception; + if (done) // item is JS_UNDEFINED + break; + rv = JS_Call(ctx, has, argv[0], 1, &item); + ok = JS_ToBoolFree(ctx, rv); // returns -1 if rv is JS_EXCEPTION + if (ok > 0) { + item = map_normalize_key(ctx, item); + if (map_find_record(ctx, t, item)) { + JS_FreeValue(ctx, item); // no duplicates + } else if ((mr = map_add_record(ctx, t, item))) { + mr->value = JS_UNDEFINED; + } else { + JS_FreeValue(ctx, item); + goto exception; + } + } else { + JS_FreeValue(ctx, item); + if (ok < 0) + goto exception; + } + } + } + goto fini; +exception: + JS_FreeValue(ctx, newset); + newset = JS_EXCEPTION; +fini: + JS_FreeValue(ctx, has); + JS_FreeValue(ctx, keys); + JS_FreeValue(ctx, iter); + JS_FreeValue(ctx, next); + return newset; +} + +static JSValue js_set_difference(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + JSValue newset, item, iter, keys, has, next, rv; + JSMapState *s, *t; + JSMapRecord *mr; + int64_t size; + BOOL done; + int ok; + + has = JS_UNDEFINED; + iter = JS_UNDEFINED; + keys = JS_UNDEFINED; + next = JS_UNDEFINED; + newset = JS_UNDEFINED; + s = JS_GetOpaque2(ctx, this_val, JS_CLASS_SET); + if (!s) + goto exception; + // order matters! + if (js_setlike_get_size(ctx, argv[0], &size) < 0) + goto exception; + if (js_setlike_get_has(ctx, argv[0], &has) < 0) + goto exception; + if (js_setlike_get_keys(ctx, argv[0], &keys) < 0) + goto exception; + if (s->record_count > size) { + iter = JS_Call(ctx, keys, argv[0], 0, NULL); + if (JS_IsException(iter)) + goto exception; + next = JS_GetProperty(ctx, iter, JS_ATOM_next); + if (JS_IsException(next)) + goto exception; + newset = js_map_constructor(ctx, JS_UNDEFINED, 1, &this_val, MAGIC_SET); + if (JS_IsException(newset)) + goto exception; + t = JS_GetOpaque(newset, JS_CLASS_SET); + for (;;) { + item = JS_IteratorNext(ctx, iter, next, 0, NULL, &done); + if (JS_IsException(item)) + goto exception; + if (done) // item is JS_UNDEFINED + break; + item = map_normalize_key(ctx, item); + mr = map_find_record(ctx, t, item); + if (mr) + map_delete_record(ctx->rt, t, mr); + JS_FreeValue(ctx, item); + } + } else { + iter = js_create_map_iterator(ctx, this_val, 0, NULL, MAGIC_SET); + if (JS_IsException(iter)) + goto exception; + newset = js_map_constructor(ctx, JS_UNDEFINED, 0, NULL, MAGIC_SET); + if (JS_IsException(newset)) + goto exception; + t = JS_GetOpaque(newset, JS_CLASS_SET); + for (;;) { + item = js_map_iterator_next(ctx, iter, 0, NULL, &done, MAGIC_SET); + if (JS_IsException(item)) + goto exception; + if (done) // item is JS_UNDEFINED + break; + rv = JS_Call(ctx, has, argv[0], 1, &item); + ok = JS_ToBoolFree(ctx, rv); // returns -1 if rv is JS_EXCEPTION + if (ok == 0) { + item = map_normalize_key(ctx, item); + if (map_find_record(ctx, t, item)) { + JS_FreeValue(ctx, item); // no duplicates + } else if ((mr = map_add_record(ctx, t, item))) { + mr->value = JS_UNDEFINED; + } else { + JS_FreeValue(ctx, item); + goto exception; + } + } else { + JS_FreeValue(ctx, item); + if (ok < 0) + goto exception; + } + } + } + goto fini; +exception: + JS_FreeValue(ctx, newset); + newset = JS_EXCEPTION; +fini: + JS_FreeValue(ctx, has); + JS_FreeValue(ctx, keys); + JS_FreeValue(ctx, iter); + JS_FreeValue(ctx, next); + return newset; +} + +static JSValue js_set_symmetricDifference(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + JSValue newset, item, iter, next, rv; + struct list_head *el; + JSMapState *s, *t; + JSMapRecord *mr; + int64_t size; + BOOL done, present; + + s = JS_GetOpaque2(ctx, this_val, JS_CLASS_SET); + if (!s) + return JS_EXCEPTION; + // order matters! they're JS-observable side effects + if (js_setlike_get_size(ctx, argv[0], &size) < 0) + return JS_EXCEPTION; + if (js_setlike_get_has(ctx, argv[0], &rv) < 0) + return JS_EXCEPTION; + JS_FreeValue(ctx, rv); + newset = js_map_constructor(ctx, JS_UNDEFINED, 0, NULL, MAGIC_SET); + if (JS_IsException(newset)) + return JS_EXCEPTION; + t = JS_GetOpaque(newset, JS_CLASS_SET); + iter = JS_UNDEFINED; + next = JS_UNDEFINED; + // can't clone this_val using js_map_constructor(), + // test262 mandates we don't call the .add method + list_for_each(el, &s->records) { + mr = list_entry(el, JSMapRecord, link); + if (mr->empty) + continue; + mr = map_add_record(ctx, t, js_dup(mr->key)); + if (!mr) + goto exception; + mr->value = JS_UNDEFINED; + } + iter = JS_GetProperty(ctx, argv[0], JS_ATOM_keys); + if (JS_IsException(iter)) + goto exception; + iter = JS_CallFree(ctx, iter, argv[0], 0, NULL); + if (JS_IsException(iter)) + goto exception; + next = JS_GetProperty(ctx, iter, JS_ATOM_next); + if (JS_IsException(next)) + goto exception; + for (;;) { + item = JS_IteratorNext(ctx, iter, next, 0, NULL, &done); + if (JS_IsException(item)) + goto exception; + if (done) // item is JS_UNDEFINED + break; + // note the subtlety here: due to mutating iterators, it's + // possible for keys to disappear during iteration; test262 + // still expects us to maintain insertion order though, so + // we first check |this|, then |new|; |new| is a copy of |this| + // - if item exists in |this|, delete (if it exists) from |new| + // - if item misses in |this| and |new|, add to |new| + // - if item exists in |new| but misses in |this|, *don't* add it, + // mutating iterator erased it + item = map_normalize_key(ctx, item); + present = (NULL != map_find_record(ctx, s, item)); + mr = map_find_record(ctx, t, item); + if (present) { + if (mr) + map_delete_record(ctx->rt, t, mr); + JS_FreeValue(ctx, item); + } else if (mr) { + JS_FreeValue(ctx, item); + } else { + mr = map_add_record(ctx, t, item); + if (!mr) { + JS_FreeValue(ctx, item); + goto exception; + } + mr->value = JS_UNDEFINED; + } + } + goto fini; +exception: + JS_FreeValue(ctx, newset); + newset = JS_EXCEPTION; +fini: + JS_FreeValue(ctx, next); + JS_FreeValue(ctx, iter); + return newset; +} + +static JSValue js_set_union(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + JSValue newset, item, iter, next, rv; + struct list_head *el; + JSMapState *s, *t; + JSMapRecord *mr; + int64_t size; + BOOL done; + + s = JS_GetOpaque2(ctx, this_val, JS_CLASS_SET); + if (!s) + return JS_EXCEPTION; + // order matters! they're JS-observable side effects + if (js_setlike_get_size(ctx, argv[0], &size) < 0) + return JS_EXCEPTION; + if (js_setlike_get_has(ctx, argv[0], &rv) < 0) + return JS_EXCEPTION; + JS_FreeValue(ctx, rv); + newset = js_map_constructor(ctx, JS_UNDEFINED, 0, NULL, MAGIC_SET); + if (JS_IsException(newset)) + return JS_EXCEPTION; + t = JS_GetOpaque(newset, JS_CLASS_SET); + iter = JS_UNDEFINED; + next = JS_UNDEFINED; + list_for_each(el, &s->records) { + mr = list_entry(el, JSMapRecord, link); + if (mr->empty) + continue; + mr = map_add_record(ctx, t, js_dup(mr->key)); + if (!mr) + goto exception; + mr->value = JS_UNDEFINED; + } + iter = JS_GetProperty(ctx, argv[0], JS_ATOM_keys); + if (JS_IsException(iter)) + goto exception; + iter = JS_CallFree(ctx, iter, argv[0], 0, NULL); + if (JS_IsException(iter)) + goto exception; + next = JS_GetProperty(ctx, iter, JS_ATOM_next); + if (JS_IsException(next)) + goto exception; + for (;;) { + item = JS_IteratorNext(ctx, iter, next, 0, NULL, &done); + if (JS_IsException(item)) + goto exception; + if (done) // item is JS_UNDEFINED + break; + rv = js_map_set(ctx, newset, 1, &item, MAGIC_SET); + JS_FreeValue(ctx, item); + if (JS_IsException(rv)) + goto exception; + JS_FreeValue(ctx, rv); + } + goto fini; +exception: + JS_FreeValue(ctx, newset); + newset = JS_EXCEPTION; +fini: + JS_FreeValue(ctx, next); + JS_FreeValue(ctx, iter); + return newset; +} + +static const JSCFunctionListEntry js_map_funcs[] = { + JS_CFUNC_DEF("groupBy", 2, js_map_groupBy ), + JS_CGETSET_DEF("[Symbol.species]", js_get_this, NULL ), +}; + +static const JSCFunctionListEntry js_map_proto_funcs[] = { + JS_CFUNC_MAGIC_DEF("set", 2, js_map_set, 0 ), + JS_CFUNC_MAGIC_DEF("get", 1, js_map_get, 0 ), + JS_CFUNC_MAGIC_DEF("has", 1, js_map_has, 0 ), + JS_CFUNC_MAGIC_DEF("delete", 1, js_map_delete, 0 ), + JS_CFUNC_MAGIC_DEF("clear", 0, js_map_clear, 0 ), + JS_CGETSET_MAGIC_DEF("size", js_map_get_size, NULL, 0), + JS_CFUNC_MAGIC_DEF("forEach", 1, js_map_forEach, 0 ), + JS_CFUNC_MAGIC_DEF("values", 0, js_create_map_iterator, (JS_ITERATOR_KIND_VALUE << 2) | 0 ), + JS_CFUNC_MAGIC_DEF("keys", 0, js_create_map_iterator, (JS_ITERATOR_KIND_KEY << 2) | 0 ), + JS_CFUNC_MAGIC_DEF("entries", 0, js_create_map_iterator, (JS_ITERATOR_KIND_KEY_AND_VALUE << 2) | 0 ), + JS_ALIAS_DEF("[Symbol.iterator]", "entries" ), + JS_PROP_STRING_DEF("[Symbol.toStringTag]", "Map", JS_PROP_CONFIGURABLE ), +}; + +static const JSCFunctionListEntry js_map_iterator_proto_funcs[] = { + JS_ITERATOR_NEXT_DEF("next", 0, js_map_iterator_next, 0 ), + JS_PROP_STRING_DEF("[Symbol.toStringTag]", "Map Iterator", JS_PROP_CONFIGURABLE ), +}; + +static const JSCFunctionListEntry js_set_proto_funcs[] = { + JS_CFUNC_MAGIC_DEF("add", 1, js_map_set, MAGIC_SET ), + JS_CFUNC_MAGIC_DEF("has", 1, js_map_has, MAGIC_SET ), + JS_CFUNC_MAGIC_DEF("delete", 1, js_map_delete, MAGIC_SET ), + JS_CFUNC_MAGIC_DEF("clear", 0, js_map_clear, MAGIC_SET ), + JS_CGETSET_MAGIC_DEF("size", js_map_get_size, NULL, MAGIC_SET ), + JS_CFUNC_MAGIC_DEF("forEach", 1, js_map_forEach, MAGIC_SET ), + JS_CFUNC_DEF("isDisjointFrom", 1, js_set_isDisjointFrom ), + JS_CFUNC_DEF("isSubsetOf", 1, js_set_isSubsetOf ), + JS_CFUNC_DEF("isSupersetOf", 1, js_set_isSupersetOf ), + JS_CFUNC_DEF("intersection", 1, js_set_intersection ), + JS_CFUNC_DEF("difference", 1, js_set_difference ), + JS_CFUNC_DEF("symmetricDifference", 1, js_set_symmetricDifference ), + JS_CFUNC_DEF("union", 1, js_set_union ), + JS_CFUNC_MAGIC_DEF("values", 0, js_create_map_iterator, (JS_ITERATOR_KIND_KEY << 2) | MAGIC_SET ), + JS_ALIAS_DEF("keys", "values" ), + JS_ALIAS_DEF("[Symbol.iterator]", "values" ), + JS_CFUNC_MAGIC_DEF("entries", 0, js_create_map_iterator, (JS_ITERATOR_KIND_KEY_AND_VALUE << 2) | MAGIC_SET ), + JS_PROP_STRING_DEF("[Symbol.toStringTag]", "Set", JS_PROP_CONFIGURABLE ), +}; + +static const JSCFunctionListEntry js_set_iterator_proto_funcs[] = { + JS_ITERATOR_NEXT_DEF("next", 0, js_map_iterator_next, MAGIC_SET ), + JS_PROP_STRING_DEF("[Symbol.toStringTag]", "Set Iterator", JS_PROP_CONFIGURABLE ), +}; + +static const JSCFunctionListEntry js_weak_map_proto_funcs[] = { + JS_CFUNC_MAGIC_DEF("set", 2, js_map_set, MAGIC_WEAK ), + JS_CFUNC_MAGIC_DEF("get", 1, js_map_get, MAGIC_WEAK ), + JS_CFUNC_MAGIC_DEF("has", 1, js_map_has, MAGIC_WEAK ), + JS_CFUNC_MAGIC_DEF("delete", 1, js_map_delete, MAGIC_WEAK ), + JS_PROP_STRING_DEF("[Symbol.toStringTag]", "WeakMap", JS_PROP_CONFIGURABLE ), +}; + +static const JSCFunctionListEntry js_weak_set_proto_funcs[] = { + JS_CFUNC_MAGIC_DEF("add", 1, js_map_set, MAGIC_SET | MAGIC_WEAK ), + JS_CFUNC_MAGIC_DEF("has", 1, js_map_has, MAGIC_SET | MAGIC_WEAK ), + JS_CFUNC_MAGIC_DEF("delete", 1, js_map_delete, MAGIC_SET | MAGIC_WEAK ), + JS_PROP_STRING_DEF("[Symbol.toStringTag]", "WeakSet", JS_PROP_CONFIGURABLE ), +}; + +static const JSCFunctionListEntry * const js_map_proto_funcs_ptr[6] = { + js_map_proto_funcs, + js_set_proto_funcs, + js_weak_map_proto_funcs, + js_weak_set_proto_funcs, + js_map_iterator_proto_funcs, + js_set_iterator_proto_funcs, +}; + +static const uint8_t js_map_proto_funcs_count[6] = { + countof(js_map_proto_funcs), + countof(js_set_proto_funcs), + countof(js_weak_map_proto_funcs), + countof(js_weak_set_proto_funcs), + countof(js_map_iterator_proto_funcs), + countof(js_set_iterator_proto_funcs), +}; + +void JS_AddIntrinsicMapSet(JSContext *ctx) +{ + int i; + JSValue obj1; + char buf[ATOM_GET_STR_BUF_SIZE]; + + for(i = 0; i < 4; i++) { + const char *name = JS_AtomGetStr(ctx, buf, sizeof(buf), + JS_ATOM_Map + i); + ctx->class_proto[JS_CLASS_MAP + i] = JS_NewObject(ctx); + JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_MAP + i], + js_map_proto_funcs_ptr[i], + js_map_proto_funcs_count[i]); + obj1 = JS_NewCFunctionMagic(ctx, js_map_constructor, name, 0, + JS_CFUNC_constructor_magic, i); + if (i < 2) { + JS_SetPropertyFunctionList(ctx, obj1, js_map_funcs, + countof(js_map_funcs)); + } + JS_NewGlobalCConstructor2(ctx, obj1, name, ctx->class_proto[JS_CLASS_MAP + i]); + } + + for(i = 0; i < 2; i++) { + ctx->class_proto[JS_CLASS_MAP_ITERATOR + i] = + JS_NewObjectProto(ctx, ctx->class_proto[JS_CLASS_ITERATOR]); + JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_MAP_ITERATOR + i], + js_map_proto_funcs_ptr[i + 4], + js_map_proto_funcs_count[i + 4]); + } +} + +/* Generator */ +static const JSCFunctionListEntry js_generator_function_proto_funcs[] = { + JS_PROP_STRING_DEF("[Symbol.toStringTag]", "GeneratorFunction", JS_PROP_CONFIGURABLE), +}; + +static const JSCFunctionListEntry js_generator_proto_funcs[] = { + JS_ITERATOR_NEXT_DEF("next", 1, js_generator_next, GEN_MAGIC_NEXT ), + JS_ITERATOR_NEXT_DEF("return", 1, js_generator_next, GEN_MAGIC_RETURN ), + JS_ITERATOR_NEXT_DEF("throw", 1, js_generator_next, GEN_MAGIC_THROW ), + JS_PROP_STRING_DEF("[Symbol.toStringTag]", "Generator", JS_PROP_CONFIGURABLE), +}; + +/* Promise */ + +typedef struct JSPromiseData { + JSPromiseStateEnum promise_state; + /* 0=fulfill, 1=reject, list of JSPromiseReactionData.link */ + struct list_head promise_reactions[2]; + BOOL is_handled; /* Note: only useful to debug */ + JSValue promise_result; +} JSPromiseData; + +typedef struct JSPromiseFunctionDataResolved { + int ref_count; + BOOL already_resolved; +} JSPromiseFunctionDataResolved; + +typedef struct JSPromiseFunctionData { + JSValue promise; + JSPromiseFunctionDataResolved *presolved; +} JSPromiseFunctionData; + +typedef struct JSPromiseReactionData { + struct list_head link; /* not used in promise_reaction_job */ + JSValue resolving_funcs[2]; + JSValue handler; +} JSPromiseReactionData; + + JSPromiseStateEnum JS_PromiseState(JSContext *ctx, JSValue promise) +{ + JSPromiseData *s = JS_GetOpaque(promise, JS_CLASS_PROMISE); + if (!s) + return -1; + return s->promise_state; +} + +JSValue JS_PromiseResult(JSContext *ctx, JSValue promise) +{ + JSPromiseData *s = JS_GetOpaque(promise, JS_CLASS_PROMISE); + if (!s) + return JS_UNDEFINED; + return JS_DupValue(ctx, s->promise_result); +} + +static int js_create_resolving_functions(JSContext *ctx, JSValue *args, + JSValue promise); + +static void promise_reaction_data_free(JSRuntime *rt, + JSPromiseReactionData *rd) +{ + JS_FreeValueRT(rt, rd->resolving_funcs[0]); + JS_FreeValueRT(rt, rd->resolving_funcs[1]); + JS_FreeValueRT(rt, rd->handler); + js_free_rt(rt, rd); +} + +#ifdef DUMP_PROMISE +#define promise_trace(ctx, ...) \ + do { \ + if (check_dump_flag(ctx->rt, DUMP_PROMISE)) \ + printf(__VA_ARGS__); \ + } while (0) +#else +#define promise_trace(...) +#endif + +static JSValue promise_reaction_job(JSContext *ctx, int argc, + JSValue *argv) +{ + JSValue handler, arg, func; + JSValue res, res2; + BOOL is_reject; + + assert(argc == 5); + handler = argv[2]; + is_reject = JS_ToBool(ctx, argv[3]); + arg = argv[4]; + + promise_trace(ctx, "promise_reaction_job: is_reject=%d\n", is_reject); + + if (JS_IsUndefined(handler)) { + if (is_reject) { + res = JS_Throw(ctx, js_dup(arg)); + } else { + res = js_dup(arg); + } + } else { + res = JS_Call(ctx, handler, JS_UNDEFINED, 1, &arg); + } + is_reject = JS_IsException(res); + if (is_reject) + res = JS_GetException(ctx); + func = argv[is_reject]; + /* as an extension, we support undefined as value to avoid + creating a dummy promise in the 'await' implementation of async + functions */ + if (!JS_IsUndefined(func)) { + res2 = JS_Call(ctx, func, JS_UNDEFINED, + 1, &res); + } else { + res2 = JS_UNDEFINED; + } + JS_FreeValue(ctx, res); + + return res2; +} + +void JS_SetHostPromiseRejectionTracker(JSRuntime *rt, + JSHostPromiseRejectionTracker *cb, + void *opaque) +{ + rt->host_promise_rejection_tracker = cb; + rt->host_promise_rejection_tracker_opaque = opaque; +} + +static void fulfill_or_reject_promise(JSContext *ctx, JSValue promise, + JSValue value, BOOL is_reject) +{ + JSPromiseData *s = JS_GetOpaque(promise, JS_CLASS_PROMISE); + struct list_head *el, *el1; + JSPromiseReactionData *rd; + JSValue args[5]; + + if (!s || s->promise_state != JS_PROMISE_PENDING) + return; /* should never happen */ + set_value(ctx, &s->promise_result, js_dup(value)); + s->promise_state = JS_PROMISE_FULFILLED + is_reject; + + promise_trace(ctx, "fulfill_or_reject_promise: is_reject=%d\n", is_reject); + + if (s->promise_state == JS_PROMISE_REJECTED && !s->is_handled) { + JSRuntime *rt = ctx->rt; + if (rt->host_promise_rejection_tracker) { + rt->host_promise_rejection_tracker(ctx, promise, value, FALSE, + rt->host_promise_rejection_tracker_opaque); + } + } + + list_for_each_safe(el, el1, &s->promise_reactions[is_reject]) { + rd = list_entry(el, JSPromiseReactionData, link); + args[0] = rd->resolving_funcs[0]; + args[1] = rd->resolving_funcs[1]; + args[2] = rd->handler; + args[3] = js_bool(is_reject); + args[4] = value; + JS_EnqueueJob(ctx, promise_reaction_job, 5, args); + list_del(&rd->link); + promise_reaction_data_free(ctx->rt, rd); + } + + list_for_each_safe(el, el1, &s->promise_reactions[1 - is_reject]) { + rd = list_entry(el, JSPromiseReactionData, link); + list_del(&rd->link); + promise_reaction_data_free(ctx->rt, rd); + } +} + +static void reject_promise(JSContext *ctx, JSValue promise, + JSValue value) +{ + fulfill_or_reject_promise(ctx, promise, value, TRUE); +} + +static JSValue js_promise_resolve_thenable_job(JSContext *ctx, + int argc, JSValue *argv) +{ + JSValue promise, thenable, then; + JSValue args[2], res; + + promise_trace(ctx, "js_promise_resolve_thenable_job\n"); + + assert(argc == 3); + promise = argv[0]; + thenable = argv[1]; + then = argv[2]; + if (js_create_resolving_functions(ctx, args, promise) < 0) + return JS_EXCEPTION; + res = JS_Call(ctx, then, thenable, 2, args); + if (JS_IsException(res)) { + JSValue error = JS_GetException(ctx); + res = JS_Call(ctx, args[1], JS_UNDEFINED, 1, &error); + JS_FreeValue(ctx, error); + } + JS_FreeValue(ctx, args[0]); + JS_FreeValue(ctx, args[1]); + return res; +} + +static void js_promise_resolve_function_free_resolved(JSRuntime *rt, + JSPromiseFunctionDataResolved *sr) +{ + if (--sr->ref_count == 0) { + js_free_rt(rt, sr); + } +} + +static int js_create_resolving_functions(JSContext *ctx, + JSValue *resolving_funcs, + JSValue promise) + +{ + JSValue obj; + JSPromiseFunctionData *s; + JSPromiseFunctionDataResolved *sr; + int i, ret; + + sr = js_malloc(ctx, sizeof(*sr)); + if (!sr) + return -1; + sr->ref_count = 1; + sr->already_resolved = FALSE; /* must be shared between the two functions */ + ret = 0; + for(i = 0; i < 2; i++) { + obj = JS_NewObjectProtoClass(ctx, ctx->function_proto, + JS_CLASS_PROMISE_RESOLVE_FUNCTION + i); + if (JS_IsException(obj)) + goto fail; + s = js_malloc(ctx, sizeof(*s)); + if (!s) { + JS_FreeValue(ctx, obj); + fail: + + if (i != 0) + JS_FreeValue(ctx, resolving_funcs[0]); + ret = -1; + break; + } + sr->ref_count++; + s->presolved = sr; + s->promise = js_dup(promise); + JS_SetOpaqueInternal(obj, s); + js_function_set_properties(ctx, obj, JS_ATOM_empty_string, 1); + resolving_funcs[i] = obj; + } + js_promise_resolve_function_free_resolved(ctx->rt, sr); + return ret; +} + +static void js_promise_resolve_function_finalizer(JSRuntime *rt, JSValue val) +{ + JSPromiseFunctionData *s = JS_VALUE_GET_OBJ(val)->u.promise_function_data; + if (s) { + js_promise_resolve_function_free_resolved(rt, s->presolved); + JS_FreeValueRT(rt, s->promise); + js_free_rt(rt, s); + } +} + +static void js_promise_resolve_function_mark(JSRuntime *rt, JSValue val, + JS_MarkFunc *mark_func) +{ + JSPromiseFunctionData *s = JS_VALUE_GET_OBJ(val)->u.promise_function_data; + if (s) { + JS_MarkValue(rt, s->promise, mark_func); + } +} + +static JSValue js_promise_resolve_function_call(JSContext *ctx, + JSValue func_obj, + JSValue this_val, + int argc, JSValue *argv, + int flags) +{ + JSObject *p = JS_VALUE_GET_OBJ(func_obj); + JSPromiseFunctionData *s; + JSValue resolution, args[3]; + JSValue then; + BOOL is_reject; + + s = p->u.promise_function_data; + if (!s || s->presolved->already_resolved) + return JS_UNDEFINED; + s->presolved->already_resolved = TRUE; + is_reject = p->class_id - JS_CLASS_PROMISE_RESOLVE_FUNCTION; + if (argc > 0) + resolution = argv[0]; + else + resolution = JS_UNDEFINED; +#ifdef DUMP_PROMISE + if (check_dump_flag(ctx->rt, DUMP_PROMISE)) { + printf("js_promise_resolving_function_call: is_reject=%d resolution=", is_reject); + JS_DumpValue(ctx->rt, resolution); + printf("\n"); + } +#endif + if (is_reject || !JS_IsObject(resolution)) { + goto done; + } else if (js_same_value(ctx, resolution, s->promise)) { + JS_ThrowTypeError(ctx, "promise self resolution"); + goto fail_reject; + } + then = JS_GetProperty(ctx, resolution, JS_ATOM_then); + if (JS_IsException(then)) { + JSValue error; + fail_reject: + error = JS_GetException(ctx); + reject_promise(ctx, s->promise, error); + JS_FreeValue(ctx, error); + } else if (!JS_IsFunction(ctx, then)) { + JS_FreeValue(ctx, then); + done: + fulfill_or_reject_promise(ctx, s->promise, resolution, is_reject); + } else { + args[0] = s->promise; + args[1] = resolution; + args[2] = then; + JS_EnqueueJob(ctx, js_promise_resolve_thenable_job, 3, args); + JS_FreeValue(ctx, then); + } + return JS_UNDEFINED; +} + +static void js_promise_finalizer(JSRuntime *rt, JSValue val) +{ + JSPromiseData *s = JS_GetOpaque(val, JS_CLASS_PROMISE); + struct list_head *el, *el1; + int i; + + if (!s) + return; + for(i = 0; i < 2; i++) { + list_for_each_safe(el, el1, &s->promise_reactions[i]) { + JSPromiseReactionData *rd = + list_entry(el, JSPromiseReactionData, link); + promise_reaction_data_free(rt, rd); + } + } + JS_FreeValueRT(rt, s->promise_result); + js_free_rt(rt, s); +} + +static void js_promise_mark(JSRuntime *rt, JSValue val, + JS_MarkFunc *mark_func) +{ + JSPromiseData *s = JS_GetOpaque(val, JS_CLASS_PROMISE); + struct list_head *el; + int i; + + if (!s) + return; + for(i = 0; i < 2; i++) { + list_for_each(el, &s->promise_reactions[i]) { + JSPromiseReactionData *rd = + list_entry(el, JSPromiseReactionData, link); + JS_MarkValue(rt, rd->resolving_funcs[0], mark_func); + JS_MarkValue(rt, rd->resolving_funcs[1], mark_func); + JS_MarkValue(rt, rd->handler, mark_func); + } + } + JS_MarkValue(rt, s->promise_result, mark_func); +} + +static JSValue js_promise_constructor(JSContext *ctx, JSValue new_target, + int argc, JSValue *argv) +{ + JSValue executor; + JSValue obj; + JSPromiseData *s; + JSValue args[2], ret; + int i; + + executor = argv[0]; + if (check_function(ctx, executor)) + return JS_EXCEPTION; + obj = js_create_from_ctor(ctx, new_target, JS_CLASS_PROMISE); + if (JS_IsException(obj)) + return JS_EXCEPTION; + s = js_mallocz(ctx, sizeof(*s)); + if (!s) + goto fail; + s->promise_state = JS_PROMISE_PENDING; + s->is_handled = FALSE; + for(i = 0; i < 2; i++) + init_list_head(&s->promise_reactions[i]); + s->promise_result = JS_UNDEFINED; + JS_SetOpaqueInternal(obj, s); + if (js_create_resolving_functions(ctx, args, obj)) + goto fail; + ret = JS_Call(ctx, executor, JS_UNDEFINED, 2, args); + if (JS_IsException(ret)) { + JSValue ret2, error; + error = JS_GetException(ctx); + ret2 = JS_Call(ctx, args[1], JS_UNDEFINED, 1, &error); + JS_FreeValue(ctx, error); + if (JS_IsException(ret2)) + goto fail1; + JS_FreeValue(ctx, ret2); + } + JS_FreeValue(ctx, ret); + JS_FreeValue(ctx, args[0]); + JS_FreeValue(ctx, args[1]); + return obj; + fail1: + JS_FreeValue(ctx, args[0]); + JS_FreeValue(ctx, args[1]); + fail: + JS_FreeValue(ctx, obj); + return JS_EXCEPTION; +} + +static JSValue js_promise_executor(JSContext *ctx, + JSValue this_val, + int argc, JSValue *argv, + int magic, JSValue *func_data) +{ + int i; + + for(i = 0; i < 2; i++) { + if (!JS_IsUndefined(func_data[i])) + return JS_ThrowTypeError(ctx, "resolving function already set"); + func_data[i] = js_dup(argv[i]); + } + return JS_UNDEFINED; +} + +static JSValue js_promise_executor_new(JSContext *ctx) +{ + JSValue func_data[2]; + + func_data[0] = JS_UNDEFINED; + func_data[1] = JS_UNDEFINED; + return JS_NewCFunctionData(ctx, js_promise_executor, 2, + 0, 2, func_data); +} + +static JSValue js_new_promise_capability(JSContext *ctx, + JSValue *resolving_funcs, + JSValue ctor) +{ + JSValue executor, result_promise; + JSCFunctionDataRecord *s; + int i; + + executor = js_promise_executor_new(ctx); + if (JS_IsException(executor)) + return executor; + + if (JS_IsUndefined(ctor)) { + result_promise = js_promise_constructor(ctx, ctor, 1, + &executor); + } else { + result_promise = JS_CallConstructor(ctx, ctor, 1, + &executor); + } + if (JS_IsException(result_promise)) + goto fail; + s = JS_GetOpaque(executor, JS_CLASS_C_FUNCTION_DATA); + for(i = 0; i < 2; i++) { + if (check_function(ctx, s->data[i])) + goto fail; + } + for(i = 0; i < 2; i++) + resolving_funcs[i] = js_dup(s->data[i]); + JS_FreeValue(ctx, executor); + return result_promise; + fail: + JS_FreeValue(ctx, executor); + JS_FreeValue(ctx, result_promise); + return JS_EXCEPTION; +} + +JSValue JS_NewPromiseCapability(JSContext *ctx, JSValue *resolving_funcs) +{ + return js_new_promise_capability(ctx, resolving_funcs, JS_UNDEFINED); +} + +static JSValue js_promise_resolve(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv, int magic) +{ + JSValue result_promise, resolving_funcs[2], ret; + BOOL is_reject = magic; + + if (!JS_IsObject(this_val)) + return JS_ThrowTypeErrorNotAnObject(ctx); + if (!is_reject && JS_GetOpaque(argv[0], JS_CLASS_PROMISE)) { + JSValue ctor; + BOOL is_same; + ctor = JS_GetProperty(ctx, argv[0], JS_ATOM_constructor); + if (JS_IsException(ctor)) + return ctor; + is_same = js_same_value(ctx, ctor, this_val); + JS_FreeValue(ctx, ctor); + if (is_same) + return js_dup(argv[0]); + } + result_promise = js_new_promise_capability(ctx, resolving_funcs, this_val); + if (JS_IsException(result_promise)) + return result_promise; + ret = JS_Call(ctx, resolving_funcs[is_reject], JS_UNDEFINED, 1, argv); + JS_FreeValue(ctx, resolving_funcs[0]); + JS_FreeValue(ctx, resolving_funcs[1]); + if (JS_IsException(ret)) { + JS_FreeValue(ctx, result_promise); + return ret; + } + JS_FreeValue(ctx, ret); + return result_promise; +} + +static JSValue js_promise_withResolvers(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + JSValue result_promise, resolving_funcs[2], obj; + if (!JS_IsObject(this_val)) + return JS_ThrowTypeErrorNotAnObject(ctx); + result_promise = js_new_promise_capability(ctx, resolving_funcs, this_val); + if (JS_IsException(result_promise)) + return JS_EXCEPTION; + obj = JS_NewObject(ctx); + if (JS_IsException(obj)) { + JS_FreeValue(ctx, resolving_funcs[0]); + JS_FreeValue(ctx, resolving_funcs[1]); + JS_FreeValue(ctx, result_promise); + return JS_EXCEPTION; + } + JS_DefinePropertyValue(ctx, obj, JS_ATOM_promise, result_promise, JS_PROP_C_W_E); + JS_DefinePropertyValue(ctx, obj, JS_ATOM_resolve, resolving_funcs[0], JS_PROP_C_W_E); + JS_DefinePropertyValue(ctx, obj, JS_ATOM_reject, resolving_funcs[1], JS_PROP_C_W_E); + return obj; +} + +static JSValue js_promise_try(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + JSValue result_promise, resolving_funcs[2], ret, ret2; + BOOL is_reject = 0; + + if (!JS_IsObject(this_val)) + return JS_ThrowTypeErrorNotAnObject(ctx); + result_promise = js_new_promise_capability(ctx, resolving_funcs, this_val); + if (JS_IsException(result_promise)) + return result_promise; + ret = JS_Call(ctx, argv[0], JS_UNDEFINED, argc - 1, argv + 1); + if (JS_IsException(ret)) { + is_reject = 1; + ret = JS_GetException(ctx); + } + ret2 = JS_Call(ctx, resolving_funcs[is_reject], JS_UNDEFINED, 1, &ret); + JS_FreeValue(ctx, resolving_funcs[0]); + JS_FreeValue(ctx, resolving_funcs[1]); + JS_FreeValue(ctx, ret); + if (JS_IsException(ret2)) { + JS_FreeValue(ctx, result_promise); + return ret2; + } + JS_FreeValue(ctx, ret2); + return result_promise; +} + +static __exception int remainingElementsCount_add(JSContext *ctx, + JSValue resolve_element_env, + int addend) +{ + JSValue val; + int remainingElementsCount; + + val = JS_GetPropertyUint32(ctx, resolve_element_env, 0); + if (JS_IsException(val)) + return -1; + if (JS_ToInt32Free(ctx, &remainingElementsCount, val)) + return -1; + remainingElementsCount += addend; + if (JS_SetPropertyUint32(ctx, resolve_element_env, 0, + js_int32(remainingElementsCount)) < 0) + return -1; + return (remainingElementsCount == 0); +} + +#define PROMISE_MAGIC_all 0 +#define PROMISE_MAGIC_allSettled 1 +#define PROMISE_MAGIC_any 2 + +static JSValue js_promise_all_resolve_element(JSContext *ctx, + JSValue this_val, + int argc, JSValue *argv, + int magic, + JSValue *func_data) +{ + int resolve_type = magic & 3; + int is_reject = magic & 4; + BOOL alreadyCalled = JS_ToBool(ctx, func_data[0]); + JSValue values = func_data[2]; + JSValue resolve = func_data[3]; + JSValue resolve_element_env = func_data[4]; + JSValue ret, obj; + int is_zero, index; + + if (JS_ToInt32(ctx, &index, func_data[1])) + return JS_EXCEPTION; + if (alreadyCalled) + return JS_UNDEFINED; + func_data[0] = js_bool(TRUE); + + if (resolve_type == PROMISE_MAGIC_allSettled) { + JSValue str; + + obj = JS_NewObject(ctx); + if (JS_IsException(obj)) + return JS_EXCEPTION; + str = js_new_string8(ctx, is_reject ? "rejected" : "fulfilled"); + if (JS_IsException(str)) + goto fail1; + if (JS_DefinePropertyValue(ctx, obj, JS_ATOM_status, + str, + JS_PROP_C_W_E) < 0) + goto fail1; + if (JS_DefinePropertyValue(ctx, obj, + is_reject ? JS_ATOM_reason : JS_ATOM_value, + js_dup(argv[0]), + JS_PROP_C_W_E) < 0) { + fail1: + JS_FreeValue(ctx, obj); + return JS_EXCEPTION; + } + } else { + obj = js_dup(argv[0]); + } + if (JS_DefinePropertyValueUint32(ctx, values, index, + obj, JS_PROP_C_W_E) < 0) + return JS_EXCEPTION; + + is_zero = remainingElementsCount_add(ctx, resolve_element_env, -1); + if (is_zero < 0) + return JS_EXCEPTION; + if (is_zero) { + if (resolve_type == PROMISE_MAGIC_any) { + JSValue error; + error = js_aggregate_error_constructor(ctx, values); + if (JS_IsException(error)) + return JS_EXCEPTION; + ret = JS_Call(ctx, resolve, JS_UNDEFINED, 1, &error); + JS_FreeValue(ctx, error); + } else { + ret = JS_Call(ctx, resolve, JS_UNDEFINED, 1, &values); + } + if (JS_IsException(ret)) + return ret; + JS_FreeValue(ctx, ret); + } + return JS_UNDEFINED; +} + +/* magic = 0: Promise.all 1: Promise.allSettled */ +static JSValue js_promise_all(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv, int magic) +{ + JSValue result_promise, resolving_funcs[2], item, next_promise, ret; + JSValue next_method = JS_UNDEFINED, values = JS_UNDEFINED; + JSValue resolve_element_env = JS_UNDEFINED, resolve_element, reject_element; + JSValue promise_resolve = JS_UNDEFINED, iter = JS_UNDEFINED; + JSValue then_args[2], resolve_element_data[5]; + BOOL done; + int index, is_zero, is_promise_any = (magic == PROMISE_MAGIC_any); + + if (!JS_IsObject(this_val)) + return JS_ThrowTypeErrorNotAnObject(ctx); + result_promise = js_new_promise_capability(ctx, resolving_funcs, this_val); + if (JS_IsException(result_promise)) + return result_promise; + promise_resolve = JS_GetProperty(ctx, this_val, JS_ATOM_resolve); + if (JS_IsException(promise_resolve) || + check_function(ctx, promise_resolve)) + goto fail_reject; + iter = JS_GetIterator(ctx, argv[0], FALSE); + if (JS_IsException(iter)) { + JSValue error; + fail_reject: + error = JS_GetException(ctx); + ret = JS_Call(ctx, resolving_funcs[1], JS_UNDEFINED, 1, + &error); + JS_FreeValue(ctx, error); + if (JS_IsException(ret)) + goto fail; + JS_FreeValue(ctx, ret); + } else { + next_method = JS_GetProperty(ctx, iter, JS_ATOM_next); + if (JS_IsException(next_method)) + goto fail_reject; + values = JS_NewArray(ctx); + if (JS_IsException(values)) + goto fail_reject; + resolve_element_env = JS_NewArray(ctx); + if (JS_IsException(resolve_element_env)) + goto fail_reject; + /* remainingElementsCount field */ + if (JS_DefinePropertyValueUint32(ctx, resolve_element_env, 0, + js_int32(1), + JS_PROP_CONFIGURABLE | JS_PROP_ENUMERABLE | JS_PROP_WRITABLE) < 0) + goto fail_reject; + + index = 0; + for(;;) { + /* XXX: conformance: should close the iterator if error on 'done' + access, but not on 'value' access */ + item = JS_IteratorNext(ctx, iter, next_method, 0, NULL, &done); + if (JS_IsException(item)) + goto fail_reject; + if (done) + break; + next_promise = JS_Call(ctx, promise_resolve, + this_val, 1, &item); + JS_FreeValue(ctx, item); + if (JS_IsException(next_promise)) { + fail_reject1: + JS_IteratorClose(ctx, iter, TRUE); + goto fail_reject; + } + resolve_element_data[0] = js_bool(FALSE); + resolve_element_data[1] = js_int32(index); + resolve_element_data[2] = values; + resolve_element_data[3] = resolving_funcs[is_promise_any]; + resolve_element_data[4] = resolve_element_env; + resolve_element = + JS_NewCFunctionData(ctx, js_promise_all_resolve_element, 1, + magic, 5, resolve_element_data); + if (JS_IsException(resolve_element)) { + JS_FreeValue(ctx, next_promise); + goto fail_reject1; + } + + if (magic == PROMISE_MAGIC_allSettled) { + reject_element = + JS_NewCFunctionData(ctx, js_promise_all_resolve_element, 1, + magic | 4, 5, resolve_element_data); + if (JS_IsException(reject_element)) { + JS_FreeValue(ctx, next_promise); + goto fail_reject1; + } + } else if (magic == PROMISE_MAGIC_any) { + if (JS_DefinePropertyValueUint32(ctx, values, index, + JS_UNDEFINED, JS_PROP_C_W_E) < 0) + goto fail_reject1; + reject_element = resolve_element; + resolve_element = js_dup(resolving_funcs[0]); + } else { + reject_element = js_dup(resolving_funcs[1]); + } + + if (remainingElementsCount_add(ctx, resolve_element_env, 1) < 0) { + JS_FreeValue(ctx, next_promise); + JS_FreeValue(ctx, resolve_element); + JS_FreeValue(ctx, reject_element); + goto fail_reject1; + } + + then_args[0] = resolve_element; + then_args[1] = reject_element; + ret = JS_InvokeFree(ctx, next_promise, JS_ATOM_then, 2, then_args); + JS_FreeValue(ctx, resolve_element); + JS_FreeValue(ctx, reject_element); + if (check_exception_free(ctx, ret)) + goto fail_reject1; + index++; + } + + is_zero = remainingElementsCount_add(ctx, resolve_element_env, -1); + if (is_zero < 0) + goto fail_reject; + if (is_zero) { + if (magic == PROMISE_MAGIC_any) { + JSValue error; + error = js_aggregate_error_constructor(ctx, values); + if (JS_IsException(error)) + goto fail_reject; + JS_FreeValue(ctx, values); + values = error; + } + ret = JS_Call(ctx, resolving_funcs[is_promise_any], JS_UNDEFINED, + 1, &values); + if (check_exception_free(ctx, ret)) + goto fail_reject; + } + } + done: + JS_FreeValue(ctx, promise_resolve); + JS_FreeValue(ctx, resolve_element_env); + JS_FreeValue(ctx, values); + JS_FreeValue(ctx, next_method); + JS_FreeValue(ctx, iter); + JS_FreeValue(ctx, resolving_funcs[0]); + JS_FreeValue(ctx, resolving_funcs[1]); + return result_promise; + fail: + JS_FreeValue(ctx, result_promise); + result_promise = JS_EXCEPTION; + goto done; +} + +static JSValue js_promise_race(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + JSValue result_promise, resolving_funcs[2], item, next_promise, ret; + JSValue next_method = JS_UNDEFINED, iter = JS_UNDEFINED; + JSValue promise_resolve = JS_UNDEFINED; + BOOL done; + + if (!JS_IsObject(this_val)) + return JS_ThrowTypeErrorNotAnObject(ctx); + result_promise = js_new_promise_capability(ctx, resolving_funcs, this_val); + if (JS_IsException(result_promise)) + return result_promise; + promise_resolve = JS_GetProperty(ctx, this_val, JS_ATOM_resolve); + if (JS_IsException(promise_resolve) || + check_function(ctx, promise_resolve)) + goto fail_reject; + iter = JS_GetIterator(ctx, argv[0], FALSE); + if (JS_IsException(iter)) { + JSValue error; + fail_reject: + error = JS_GetException(ctx); + ret = JS_Call(ctx, resolving_funcs[1], JS_UNDEFINED, 1, + &error); + JS_FreeValue(ctx, error); + if (JS_IsException(ret)) + goto fail; + JS_FreeValue(ctx, ret); + } else { + next_method = JS_GetProperty(ctx, iter, JS_ATOM_next); + if (JS_IsException(next_method)) + goto fail_reject; + + for(;;) { + /* XXX: conformance: should close the iterator if error on 'done' + access, but not on 'value' access */ + item = JS_IteratorNext(ctx, iter, next_method, 0, NULL, &done); + if (JS_IsException(item)) + goto fail_reject; + if (done) + break; + next_promise = JS_Call(ctx, promise_resolve, + this_val, 1, &item); + JS_FreeValue(ctx, item); + if (JS_IsException(next_promise)) { + fail_reject1: + JS_IteratorClose(ctx, iter, TRUE); + goto fail_reject; + } + ret = JS_InvokeFree(ctx, next_promise, JS_ATOM_then, 2, + resolving_funcs); + if (check_exception_free(ctx, ret)) + goto fail_reject1; + } + } + done: + JS_FreeValue(ctx, promise_resolve); + JS_FreeValue(ctx, next_method); + JS_FreeValue(ctx, iter); + JS_FreeValue(ctx, resolving_funcs[0]); + JS_FreeValue(ctx, resolving_funcs[1]); + return result_promise; + fail: + //JS_FreeValue(ctx, next_method); // why not??? + JS_FreeValue(ctx, result_promise); + result_promise = JS_EXCEPTION; + goto done; +} + +static __exception int perform_promise_then(JSContext *ctx, + JSValue promise, + JSValue *resolve_reject, + JSValue *cap_resolving_funcs) +{ + JSPromiseData *s = JS_GetOpaque(promise, JS_CLASS_PROMISE); + JSPromiseReactionData *rd_array[2], *rd; + int i, j; + + rd_array[0] = NULL; + rd_array[1] = NULL; + for(i = 0; i < 2; i++) { + JSValue handler; + rd = js_mallocz(ctx, sizeof(*rd)); + if (!rd) { + if (i == 1) + promise_reaction_data_free(ctx->rt, rd_array[0]); + return -1; + } + for(j = 0; j < 2; j++) + rd->resolving_funcs[j] = js_dup(cap_resolving_funcs[j]); + handler = resolve_reject[i]; + if (!JS_IsFunction(ctx, handler)) + handler = JS_UNDEFINED; + rd->handler = js_dup(handler); + rd_array[i] = rd; + } + + if (s->promise_state == JS_PROMISE_PENDING) { + for(i = 0; i < 2; i++) + list_add_tail(&rd_array[i]->link, &s->promise_reactions[i]); + } else { + JSValue args[5]; + if (s->promise_state == JS_PROMISE_REJECTED && !s->is_handled) { + JSRuntime *rt = ctx->rt; + if (rt->host_promise_rejection_tracker) { + rt->host_promise_rejection_tracker(ctx, promise, s->promise_result, + TRUE, rt->host_promise_rejection_tracker_opaque); + } + } + i = s->promise_state - JS_PROMISE_FULFILLED; + rd = rd_array[i]; + args[0] = rd->resolving_funcs[0]; + args[1] = rd->resolving_funcs[1]; + args[2] = rd->handler; + args[3] = js_bool(i); + args[4] = s->promise_result; + JS_EnqueueJob(ctx, promise_reaction_job, 5, args); + for(i = 0; i < 2; i++) + promise_reaction_data_free(ctx->rt, rd_array[i]); + } + s->is_handled = TRUE; + return 0; +} + +static JSValue js_promise_then(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + JSValue ctor, result_promise, resolving_funcs[2]; + JSPromiseData *s; + int i, ret; + + s = JS_GetOpaque2(ctx, this_val, JS_CLASS_PROMISE); + if (!s) + return JS_EXCEPTION; + + ctor = JS_SpeciesConstructor(ctx, this_val, JS_UNDEFINED); + if (JS_IsException(ctor)) + return ctor; + result_promise = js_new_promise_capability(ctx, resolving_funcs, ctor); + JS_FreeValue(ctx, ctor); + if (JS_IsException(result_promise)) + return result_promise; + ret = perform_promise_then(ctx, this_val, argv, + resolving_funcs); + for(i = 0; i < 2; i++) + JS_FreeValue(ctx, resolving_funcs[i]); + if (ret) { + JS_FreeValue(ctx, result_promise); + return JS_EXCEPTION; + } + return result_promise; +} + +static JSValue js_promise_catch(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + JSValue args[2]; + args[0] = JS_UNDEFINED; + args[1] = argv[0]; + return JS_Invoke(ctx, this_val, JS_ATOM_then, 2, args); +} + +static JSValue js_promise_finally_value_thunk(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv, + int magic, JSValue *func_data) +{ + return js_dup(func_data[0]); +} + +static JSValue js_promise_finally_thrower(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv, + int magic, JSValue *func_data) +{ + return JS_Throw(ctx, js_dup(func_data[0])); +} + +static JSValue js_promise_then_finally_func(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv, + int magic, JSValue *func_data) +{ + JSValue ctor = func_data[0]; + JSValue onFinally = func_data[1]; + JSValue res, promise, ret, then_func; + + res = JS_Call(ctx, onFinally, JS_UNDEFINED, 0, NULL); + if (JS_IsException(res)) + return res; + promise = js_promise_resolve(ctx, ctor, 1, &res, 0); + JS_FreeValue(ctx, res); + if (JS_IsException(promise)) + return promise; + if (magic == 0) { + then_func = JS_NewCFunctionData(ctx, js_promise_finally_value_thunk, 0, + 0, 1, argv); + } else { + then_func = JS_NewCFunctionData(ctx, js_promise_finally_thrower, 0, + 0, 1, argv); + } + if (JS_IsException(then_func)) { + JS_FreeValue(ctx, promise); + return then_func; + } + ret = JS_InvokeFree(ctx, promise, JS_ATOM_then, 1, &then_func); + JS_FreeValue(ctx, then_func); + return ret; +} + +static JSValue js_promise_finally(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + JSValue onFinally = argv[0]; + JSValue ctor, ret; + JSValue then_funcs[2]; + JSValue func_data[2]; + int i; + + ctor = JS_SpeciesConstructor(ctx, this_val, JS_UNDEFINED); + if (JS_IsException(ctor)) + return ctor; + if (!JS_IsFunction(ctx, onFinally)) { + then_funcs[0] = js_dup(onFinally); + then_funcs[1] = js_dup(onFinally); + } else { + func_data[0] = ctor; + func_data[1] = onFinally; + for(i = 0; i < 2; i++) { + then_funcs[i] = JS_NewCFunctionData(ctx, js_promise_then_finally_func, 1, i, 2, func_data); + if (JS_IsException(then_funcs[i])) { + if (i == 1) + JS_FreeValue(ctx, then_funcs[0]); + JS_FreeValue(ctx, ctor); + return JS_EXCEPTION; + } + } + } + JS_FreeValue(ctx, ctor); + ret = JS_Invoke(ctx, this_val, JS_ATOM_then, 2, then_funcs); + JS_FreeValue(ctx, then_funcs[0]); + JS_FreeValue(ctx, then_funcs[1]); + return ret; +} + +static const JSCFunctionListEntry js_promise_funcs[] = { + JS_CFUNC_MAGIC_DEF("resolve", 1, js_promise_resolve, 0 ), + JS_CFUNC_MAGIC_DEF("reject", 1, js_promise_resolve, 1 ), + JS_CFUNC_MAGIC_DEF("all", 1, js_promise_all, PROMISE_MAGIC_all ), + JS_CFUNC_MAGIC_DEF("allSettled", 1, js_promise_all, PROMISE_MAGIC_allSettled ), + JS_CFUNC_MAGIC_DEF("any", 1, js_promise_all, PROMISE_MAGIC_any ), + JS_CFUNC_DEF("try", 1, js_promise_try ), + JS_CFUNC_DEF("race", 1, js_promise_race ), + JS_CFUNC_DEF("withResolvers", 0, js_promise_withResolvers ), + JS_CGETSET_DEF("[Symbol.species]", js_get_this, NULL), +}; + +static const JSCFunctionListEntry js_promise_proto_funcs[] = { + JS_CFUNC_DEF("then", 2, js_promise_then ), + JS_CFUNC_DEF("catch", 1, js_promise_catch ), + JS_CFUNC_DEF("finally", 1, js_promise_finally ), + JS_PROP_STRING_DEF("[Symbol.toStringTag]", "Promise", JS_PROP_CONFIGURABLE ), +}; + +/* AsyncFunction */ +static const JSCFunctionListEntry js_async_function_proto_funcs[] = { + JS_PROP_STRING_DEF("[Symbol.toStringTag]", "AsyncFunction", JS_PROP_CONFIGURABLE ), +}; + +static JSValue js_async_from_sync_iterator_unwrap(JSContext *ctx, + JSValue this_val, + int argc, JSValue *argv, + int magic, JSValue *func_data) +{ + return js_create_iterator_result(ctx, js_dup(argv[0]), + JS_ToBool(ctx, func_data[0])); +} + +static JSValue js_async_from_sync_iterator_unwrap_func_create(JSContext *ctx, + BOOL done) +{ + JSValue func_data[1]; + + func_data[0] = js_bool(done); + return JS_NewCFunctionData(ctx, js_async_from_sync_iterator_unwrap, + 1, 0, 1, func_data); +} + +/* AsyncIteratorPrototype */ + +static const JSCFunctionListEntry js_async_iterator_proto_funcs[] = { + JS_CFUNC_DEF("[Symbol.asyncIterator]", 0, js_iterator_proto_iterator ), +}; + +/* AsyncFromSyncIteratorPrototype */ + +typedef struct JSAsyncFromSyncIteratorData { + JSValue sync_iter; + JSValue next_method; +} JSAsyncFromSyncIteratorData; + +static void js_async_from_sync_iterator_finalizer(JSRuntime *rt, JSValue val) +{ + JSAsyncFromSyncIteratorData *s = + JS_GetOpaque(val, JS_CLASS_ASYNC_FROM_SYNC_ITERATOR); + if (s) { + JS_FreeValueRT(rt, s->sync_iter); + JS_FreeValueRT(rt, s->next_method); + js_free_rt(rt, s); + } +} + +static void js_async_from_sync_iterator_mark(JSRuntime *rt, JSValue val, + JS_MarkFunc *mark_func) +{ + JSAsyncFromSyncIteratorData *s = + JS_GetOpaque(val, JS_CLASS_ASYNC_FROM_SYNC_ITERATOR); + if (s) { + JS_MarkValue(rt, s->sync_iter, mark_func); + JS_MarkValue(rt, s->next_method, mark_func); + } +} + +static JSValue JS_CreateAsyncFromSyncIterator(JSContext *ctx, + JSValue sync_iter) +{ + JSValue async_iter, next_method; + JSAsyncFromSyncIteratorData *s; + + next_method = JS_GetProperty(ctx, sync_iter, JS_ATOM_next); + if (JS_IsException(next_method)) + return JS_EXCEPTION; + async_iter = JS_NewObjectClass(ctx, JS_CLASS_ASYNC_FROM_SYNC_ITERATOR); + if (JS_IsException(async_iter)) { + JS_FreeValue(ctx, next_method); + return async_iter; + } + s = js_mallocz(ctx, sizeof(*s)); + if (!s) { + JS_FreeValue(ctx, async_iter); + JS_FreeValue(ctx, next_method); + return JS_EXCEPTION; + } + s->sync_iter = js_dup(sync_iter); + s->next_method = next_method; + JS_SetOpaqueInternal(async_iter, s); + return async_iter; +} + +static JSValue js_async_from_sync_iterator_next(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv, + int magic) +{ + JSValue promise, resolving_funcs[2], value, err, method; + JSAsyncFromSyncIteratorData *s; + int done; + int is_reject; + + promise = JS_NewPromiseCapability(ctx, resolving_funcs); + if (JS_IsException(promise)) + return JS_EXCEPTION; + s = JS_GetOpaque(this_val, JS_CLASS_ASYNC_FROM_SYNC_ITERATOR); + if (!s) { + JS_ThrowTypeError(ctx, "not an Async-from-Sync Iterator"); + goto reject; + } + + if (magic == GEN_MAGIC_NEXT) { + method = js_dup(s->next_method); + } else { + method = JS_GetProperty(ctx, s->sync_iter, + magic == GEN_MAGIC_RETURN ? JS_ATOM_return : + JS_ATOM_throw); + if (JS_IsException(method)) + goto reject; + if (JS_IsUndefined(method) || JS_IsNull(method)) { + if (magic == GEN_MAGIC_RETURN) { + err = js_create_iterator_result(ctx, js_dup(argv[0]), TRUE); + is_reject = 0; + } else { + err = JS_MakeError(ctx, JS_TYPE_ERROR, "throw is not a method", + TRUE); + is_reject = 1; + } + goto done_resolve; + } + } + value = JS_IteratorNext2(ctx, s->sync_iter, method, + argc >= 1 ? 1 : 0, argv, &done); + JS_FreeValue(ctx, method); + if (JS_IsException(value)) + goto reject; + if (done == 2) { + JSValue obj = value; + value = JS_IteratorGetCompleteValue(ctx, obj, &done); + JS_FreeValue(ctx, obj); + if (JS_IsException(value)) + goto reject; + } + + if (JS_IsException(value)) { + JSValue res2; + reject: + err = JS_GetException(ctx); + is_reject = 1; + done_resolve: + res2 = JS_Call(ctx, resolving_funcs[is_reject], JS_UNDEFINED, + 1, &err); + JS_FreeValue(ctx, err); + JS_FreeValue(ctx, res2); + JS_FreeValue(ctx, resolving_funcs[0]); + JS_FreeValue(ctx, resolving_funcs[1]); + return promise; + } + { + JSValue value_wrapper_promise, resolve_reject[2]; + int res; + + value_wrapper_promise = js_promise_resolve(ctx, ctx->promise_ctor, + 1, &value, 0); + if (JS_IsException(value_wrapper_promise)) { + JS_FreeValue(ctx, value); + goto reject; + } + + resolve_reject[0] = + js_async_from_sync_iterator_unwrap_func_create(ctx, done); + if (JS_IsException(resolve_reject[0])) { + JS_FreeValue(ctx, value_wrapper_promise); + goto fail; + } + JS_FreeValue(ctx, value); + resolve_reject[1] = JS_UNDEFINED; + + res = perform_promise_then(ctx, value_wrapper_promise, + resolve_reject, + resolving_funcs); + JS_FreeValue(ctx, resolve_reject[0]); + JS_FreeValue(ctx, value_wrapper_promise); + JS_FreeValue(ctx, resolving_funcs[0]); + JS_FreeValue(ctx, resolving_funcs[1]); + if (res) { + JS_FreeValue(ctx, promise); + return JS_EXCEPTION; + } + } + return promise; + fail: + JS_FreeValue(ctx, value); + JS_FreeValue(ctx, resolving_funcs[0]); + JS_FreeValue(ctx, resolving_funcs[1]); + JS_FreeValue(ctx, promise); + return JS_EXCEPTION; +} + +static const JSCFunctionListEntry js_async_from_sync_iterator_proto_funcs[] = { + JS_CFUNC_MAGIC_DEF("next", 1, js_async_from_sync_iterator_next, GEN_MAGIC_NEXT ), + JS_CFUNC_MAGIC_DEF("return", 1, js_async_from_sync_iterator_next, GEN_MAGIC_RETURN ), + JS_CFUNC_MAGIC_DEF("throw", 1, js_async_from_sync_iterator_next, GEN_MAGIC_THROW ), +}; + +/* AsyncGeneratorFunction */ + +static const JSCFunctionListEntry js_async_generator_function_proto_funcs[] = { + JS_PROP_STRING_DEF("[Symbol.toStringTag]", "AsyncGeneratorFunction", JS_PROP_CONFIGURABLE ), +}; + +/* AsyncGenerator prototype */ + +static const JSCFunctionListEntry js_async_generator_proto_funcs[] = { + JS_CFUNC_MAGIC_DEF("next", 1, js_async_generator_next, GEN_MAGIC_NEXT ), + JS_CFUNC_MAGIC_DEF("return", 1, js_async_generator_next, GEN_MAGIC_RETURN ), + JS_CFUNC_MAGIC_DEF("throw", 1, js_async_generator_next, GEN_MAGIC_THROW ), + JS_PROP_STRING_DEF("[Symbol.toStringTag]", "AsyncGenerator", JS_PROP_CONFIGURABLE ), +}; + +static JSClassShortDef const js_async_class_def[] = { + { JS_ATOM_Promise, js_promise_finalizer, js_promise_mark }, /* JS_CLASS_PROMISE */ + { JS_ATOM_PromiseResolveFunction, js_promise_resolve_function_finalizer, js_promise_resolve_function_mark }, /* JS_CLASS_PROMISE_RESOLVE_FUNCTION */ + { JS_ATOM_PromiseRejectFunction, js_promise_resolve_function_finalizer, js_promise_resolve_function_mark }, /* JS_CLASS_PROMISE_REJECT_FUNCTION */ + { JS_ATOM_AsyncFunction, js_bytecode_function_finalizer, js_bytecode_function_mark }, /* JS_CLASS_ASYNC_FUNCTION */ + { JS_ATOM_AsyncFunctionResolve, js_async_function_resolve_finalizer, js_async_function_resolve_mark }, /* JS_CLASS_ASYNC_FUNCTION_RESOLVE */ + { JS_ATOM_AsyncFunctionReject, js_async_function_resolve_finalizer, js_async_function_resolve_mark }, /* JS_CLASS_ASYNC_FUNCTION_REJECT */ + { JS_ATOM_empty_string, js_async_from_sync_iterator_finalizer, js_async_from_sync_iterator_mark }, /* JS_CLASS_ASYNC_FROM_SYNC_ITERATOR */ + { JS_ATOM_AsyncGeneratorFunction, js_bytecode_function_finalizer, js_bytecode_function_mark }, /* JS_CLASS_ASYNC_GENERATOR_FUNCTION */ + { JS_ATOM_AsyncGenerator, js_async_generator_finalizer, js_async_generator_mark }, /* JS_CLASS_ASYNC_GENERATOR */ +}; + +void JS_AddIntrinsicPromise(JSContext *ctx) +{ + JSRuntime *rt = ctx->rt; + JSValue obj1; + + if (!JS_IsRegisteredClass(rt, JS_CLASS_PROMISE)) { + init_class_range(rt, js_async_class_def, JS_CLASS_PROMISE, + countof(js_async_class_def)); + rt->class_array[JS_CLASS_PROMISE_RESOLVE_FUNCTION].call = js_promise_resolve_function_call; + rt->class_array[JS_CLASS_PROMISE_REJECT_FUNCTION].call = js_promise_resolve_function_call; + rt->class_array[JS_CLASS_ASYNC_FUNCTION].call = js_async_function_call; + rt->class_array[JS_CLASS_ASYNC_FUNCTION_RESOLVE].call = js_async_function_resolve_call; + rt->class_array[JS_CLASS_ASYNC_FUNCTION_REJECT].call = js_async_function_resolve_call; + rt->class_array[JS_CLASS_ASYNC_GENERATOR_FUNCTION].call = js_async_generator_function_call; + } + + /* Promise */ + ctx->class_proto[JS_CLASS_PROMISE] = JS_NewObject(ctx); + JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_PROMISE], + js_promise_proto_funcs, + countof(js_promise_proto_funcs)); + obj1 = JS_NewCFunction2(ctx, js_promise_constructor, "Promise", 1, + JS_CFUNC_constructor, 0); + ctx->promise_ctor = js_dup(obj1); + JS_SetPropertyFunctionList(ctx, obj1, + js_promise_funcs, + countof(js_promise_funcs)); + JS_NewGlobalCConstructor2(ctx, obj1, "Promise", + ctx->class_proto[JS_CLASS_PROMISE]); + + /* Used to squelch a -Wcast-function-type warning. */ + JSCFunctionType ft; + + /* AsyncFunction */ + ctx->class_proto[JS_CLASS_ASYNC_FUNCTION] = JS_NewObjectProto(ctx, ctx->function_proto); + ft.generic_magic = js_function_constructor; + obj1 = JS_NewCFunction3(ctx, ft.generic, + "AsyncFunction", 1, + JS_CFUNC_constructor_or_func_magic, JS_FUNC_ASYNC, + ctx->function_ctor); + JS_SetPropertyFunctionList(ctx, + ctx->class_proto[JS_CLASS_ASYNC_FUNCTION], + js_async_function_proto_funcs, + countof(js_async_function_proto_funcs)); + JS_SetConstructor2(ctx, obj1, ctx->class_proto[JS_CLASS_ASYNC_FUNCTION], + 0, JS_PROP_CONFIGURABLE); + JS_FreeValue(ctx, obj1); + + /* AsyncIteratorPrototype */ + ctx->async_iterator_proto = JS_NewObject(ctx); + JS_SetPropertyFunctionList(ctx, ctx->async_iterator_proto, + js_async_iterator_proto_funcs, + countof(js_async_iterator_proto_funcs)); + + /* AsyncFromSyncIteratorPrototype */ + ctx->class_proto[JS_CLASS_ASYNC_FROM_SYNC_ITERATOR] = + JS_NewObjectProto(ctx, ctx->async_iterator_proto); + JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_ASYNC_FROM_SYNC_ITERATOR], + js_async_from_sync_iterator_proto_funcs, + countof(js_async_from_sync_iterator_proto_funcs)); + + /* AsyncGeneratorPrototype */ + ctx->class_proto[JS_CLASS_ASYNC_GENERATOR] = + JS_NewObjectProto(ctx, ctx->async_iterator_proto); + JS_SetPropertyFunctionList(ctx, + ctx->class_proto[JS_CLASS_ASYNC_GENERATOR], + js_async_generator_proto_funcs, + countof(js_async_generator_proto_funcs)); + + /* AsyncGeneratorFunction */ + ctx->class_proto[JS_CLASS_ASYNC_GENERATOR_FUNCTION] = + JS_NewObjectProto(ctx, ctx->function_proto); + ft.generic_magic = js_function_constructor; + obj1 = JS_NewCFunction3(ctx, ft.generic, + "AsyncGeneratorFunction", 1, + JS_CFUNC_constructor_or_func_magic, + JS_FUNC_ASYNC_GENERATOR, + ctx->function_ctor); + JS_SetPropertyFunctionList(ctx, + ctx->class_proto[JS_CLASS_ASYNC_GENERATOR_FUNCTION], + js_async_generator_function_proto_funcs, + countof(js_async_generator_function_proto_funcs)); + JS_SetConstructor2(ctx, ctx->class_proto[JS_CLASS_ASYNC_GENERATOR_FUNCTION], + ctx->class_proto[JS_CLASS_ASYNC_GENERATOR], + JS_PROP_CONFIGURABLE, JS_PROP_CONFIGURABLE); + JS_SetConstructor2(ctx, obj1, ctx->class_proto[JS_CLASS_ASYNC_GENERATOR_FUNCTION], + 0, JS_PROP_CONFIGURABLE); + JS_FreeValue(ctx, obj1); +} + +/* URI handling */ + +static int string_get_hex(JSString *p, int k, int n) { + int c = 0, h; + while (n-- > 0) { + if ((h = from_hex(string_get(p, k++))) < 0) + return -1; + c = (c << 4) | h; + } + return c; +} + +static int isURIReserved(int c) { + return c < 0x100 && memchr(";/?:@&=+$,#", c, sizeof(";/?:@&=+$,#") - 1) != NULL; +} + +static int __attribute__((format(printf, 2, 3))) js_throw_URIError(JSContext *ctx, const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + JS_ThrowError(ctx, JS_URI_ERROR, fmt, ap); + va_end(ap); + return -1; +} + +static int hex_decode(JSContext *ctx, JSString *p, int k) { + int c; + + if (k >= p->len || string_get(p, k) != '%') + return js_throw_URIError(ctx, "expecting %%"); + if (k + 2 >= p->len || (c = string_get_hex(p, k + 1, 2)) < 0) + return js_throw_URIError(ctx, "expecting hex digit"); + + return c; +} + +static JSValue js_global_decodeURI(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv, int isComponent) +{ + JSValue str; + StringBuffer b_s, *b = &b_s; + JSString *p; + int k, c, c1, n, c_min; + + str = JS_ToString(ctx, argv[0]); + if (JS_IsException(str)) + return str; + + string_buffer_init(ctx, b, 0); + + p = JS_VALUE_GET_STRING(str); + for (k = 0; k < p->len;) { + c = string_get(p, k); + if (c == '%') { + c = hex_decode(ctx, p, k); + if (c < 0) + goto fail; + k += 3; + if (c < 0x80) { + if (!isComponent && isURIReserved(c)) { + c = '%'; + k -= 2; + } + } else { + /* Decode URI-encoded UTF-8 sequence */ + if (c >= 0xc0 && c <= 0xdf) { + n = 1; + c_min = 0x80; + c &= 0x1f; + } else if (c >= 0xe0 && c <= 0xef) { + n = 2; + c_min = 0x800; + c &= 0xf; + } else if (c >= 0xf0 && c <= 0xf7) { + n = 3; + c_min = 0x10000; + c &= 0x7; + } else { + n = 0; + c_min = 1; + c = 0; + } + while (n-- > 0) { + c1 = hex_decode(ctx, p, k); + if (c1 < 0) + goto fail; + k += 3; + if ((c1 & 0xc0) != 0x80) { + c = 0; + break; + } + c = (c << 6) | (c1 & 0x3f); + } + if (c < c_min || c > 0x10FFFF || is_surrogate(c)) { + js_throw_URIError(ctx, "malformed UTF-8"); + goto fail; + } + } + } else { + k++; + } + string_buffer_putc(b, c); + } + JS_FreeValue(ctx, str); + return string_buffer_end(b); + +fail: + JS_FreeValue(ctx, str); + string_buffer_free(b); + return JS_EXCEPTION; +} + +static int isUnescaped(int c) { + static char const unescaped_chars[] = + "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "abcdefghijklmnopqrstuvwxyz" + "0123456789" + "@*_+-./"; + return c < 0x100 && + memchr(unescaped_chars, c, sizeof(unescaped_chars) - 1); +} + +static int isURIUnescaped(int c, int isComponent) { + return c < 0x100 && + ((c >= 0x61 && c <= 0x7a) || + (c >= 0x41 && c <= 0x5a) || + (c >= 0x30 && c <= 0x39) || + memchr("-_.!~*'()", c, sizeof("-_.!~*'()") - 1) != NULL || + (!isComponent && isURIReserved(c))); +} + +static int encodeURI_hex(StringBuffer *b, int c) { + uint8_t buf[6]; + int n = 0; + const char *hex = "0123456789ABCDEF"; + + buf[n++] = '%'; + if (c >= 256) { + buf[n++] = 'u'; + buf[n++] = hex[(c >> 12) & 15]; + buf[n++] = hex[(c >> 8) & 15]; + } + buf[n++] = hex[(c >> 4) & 15]; + buf[n++] = hex[(c >> 0) & 15]; + return string_buffer_write8(b, buf, n); +} + +static JSValue js_global_encodeURI(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv, + int isComponent) +{ + JSValue str; + StringBuffer b_s, *b = &b_s; + JSString *p; + int k, c, c1; + + str = JS_ToString(ctx, argv[0]); + if (JS_IsException(str)) + return str; + + p = JS_VALUE_GET_STRING(str); + string_buffer_init(ctx, b, p->len); + for (k = 0; k < p->len;) { + c = string_get(p, k); + k++; + if (isURIUnescaped(c, isComponent)) { + string_buffer_putc16(b, c); + } else { + if (is_lo_surrogate(c)) { + js_throw_URIError(ctx, "invalid character"); + goto fail; + } else if (is_hi_surrogate(c)) { + if (k >= p->len) { + js_throw_URIError(ctx, "expecting surrogate pair"); + goto fail; + } + c1 = string_get(p, k); + k++; + if (!is_lo_surrogate(c1)) { + js_throw_URIError(ctx, "expecting surrogate pair"); + goto fail; + } + c = from_surrogate(c, c1); + } + if (c < 0x80) { + encodeURI_hex(b, c); + } else { + /* XXX: use C UTF-8 conversion ? */ + if (c < 0x800) { + encodeURI_hex(b, (c >> 6) | 0xc0); + } else { + if (c < 0x10000) { + encodeURI_hex(b, (c >> 12) | 0xe0); + } else { + encodeURI_hex(b, (c >> 18) | 0xf0); + encodeURI_hex(b, ((c >> 12) & 0x3f) | 0x80); + } + encodeURI_hex(b, ((c >> 6) & 0x3f) | 0x80); + } + encodeURI_hex(b, (c & 0x3f) | 0x80); + } + } + } + JS_FreeValue(ctx, str); + return string_buffer_end(b); + +fail: + JS_FreeValue(ctx, str); + string_buffer_free(b); + return JS_EXCEPTION; +} + +static JSValue js_global_escape(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + JSValue str; + StringBuffer b_s, *b = &b_s; + JSString *p; + int i, len, c; + + str = JS_ToString(ctx, argv[0]); + if (JS_IsException(str)) + return str; + + p = JS_VALUE_GET_STRING(str); + string_buffer_init(ctx, b, p->len); + for (i = 0, len = p->len; i < len; i++) { + c = string_get(p, i); + if (isUnescaped(c)) { + string_buffer_putc16(b, c); + } else { + encodeURI_hex(b, c); + } + } + JS_FreeValue(ctx, str); + return string_buffer_end(b); +} + +static JSValue js_global_unescape(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + JSValue str; + StringBuffer b_s, *b = &b_s; + JSString *p; + int i, len, c, n; + + str = JS_ToString(ctx, argv[0]); + if (JS_IsException(str)) + return str; + + string_buffer_init(ctx, b, 0); + p = JS_VALUE_GET_STRING(str); + for (i = 0, len = p->len; i < len; i++) { + c = string_get(p, i); + if (c == '%') { + if (i + 6 <= len + && string_get(p, i + 1) == 'u' + && (n = string_get_hex(p, i + 2, 4)) >= 0) { + c = n; + i += 6 - 1; + } else + if (i + 3 <= len + && (n = string_get_hex(p, i + 1, 2)) >= 0) { + c = n; + i += 3 - 1; + } + } + string_buffer_putc16(b, c); + } + JS_FreeValue(ctx, str); + return string_buffer_end(b); +} + +/* global object */ + +static const JSCFunctionListEntry js_global_funcs[] = { + JS_CFUNC_DEF("parseInt", 2, js_parseInt ), + JS_CFUNC_DEF("parseFloat", 1, js_parseFloat ), + JS_CFUNC_DEF("isNaN", 1, js_global_isNaN ), + JS_CFUNC_DEF("isFinite", 1, js_global_isFinite ), + JS_CFUNC_DEF("queueMicrotask", 1, js_global_queueMicrotask ), + + JS_CFUNC_MAGIC_DEF("decodeURI", 1, js_global_decodeURI, 0 ), + JS_CFUNC_MAGIC_DEF("decodeURIComponent", 1, js_global_decodeURI, 1 ), + JS_CFUNC_MAGIC_DEF("encodeURI", 1, js_global_encodeURI, 0 ), + JS_CFUNC_MAGIC_DEF("encodeURIComponent", 1, js_global_encodeURI, 1 ), + JS_CFUNC_DEF("escape", 1, js_global_escape ), + JS_CFUNC_DEF("unescape", 1, js_global_unescape ), + JS_PROP_DOUBLE_DEF("Infinity", 1.0 / 0.0, 0 ), + JS_PROP_DOUBLE_DEF("NaN", NAN, 0 ), + JS_PROP_UNDEFINED_DEF("undefined", 0 ), + JS_PROP_STRING_DEF("[Symbol.toStringTag]", "global", JS_PROP_CONFIGURABLE ), +}; + +/* Date */ + +static int64_t math_mod(int64_t a, int64_t b) { + /* return positive modulo */ + int64_t m = a % b; + return m + (m < 0) * b; +} + +static int64_t floor_div_int64(int64_t a, int64_t b) { + /* integer division rounding toward -Infinity */ + int64_t m = a % b; + return (a - (m + (m < 0) * b)) / b; +} + +static JSValue js_Date_parse(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv); + +static __exception int JS_ThisTimeValue(JSContext *ctx, double *valp, JSValue this_val) +{ + if (JS_VALUE_GET_TAG(this_val) == JS_TAG_OBJECT) { + JSObject *p = JS_VALUE_GET_OBJ(this_val); + if (p->class_id == JS_CLASS_DATE && JS_IsNumber(p->u.object_data)) + return JS_ToFloat64(ctx, valp, p->u.object_data); + } + JS_ThrowTypeError(ctx, "not a Date object"); + return -1; +} + +static JSValue JS_SetThisTimeValue(JSContext *ctx, JSValue this_val, double v) +{ + if (JS_VALUE_GET_TAG(this_val) == JS_TAG_OBJECT) { + JSObject *p = JS_VALUE_GET_OBJ(this_val); + if (p->class_id == JS_CLASS_DATE) { + JS_FreeValue(ctx, p->u.object_data); + p->u.object_data = js_float64(v); + return js_dup(p->u.object_data); + } + } + return JS_ThrowTypeError(ctx, "not a Date object"); +} + +static int64_t days_from_year(int64_t y) { + return 365 * (y - 1970) + floor_div_int64(y - 1969, 4) - + floor_div_int64(y - 1901, 100) + floor_div_int64(y - 1601, 400); +} + +static int64_t days_in_year(int64_t y) { + return 365 + !(y % 4) - !(y % 100) + !(y % 400); +} + +/* return the year, update days */ +static int64_t year_from_days(int64_t *days) { + int64_t y, d1, nd, d = *days; + y = floor_div_int64(d * 10000, 3652425) + 1970; + /* the initial approximation is very good, so only a few + iterations are necessary */ + for(;;) { + d1 = d - days_from_year(y); + if (d1 < 0) { + y--; + d1 += days_in_year(y); + } else { + nd = days_in_year(y); + if (d1 < nd) + break; + d1 -= nd; + y++; + } + } + *days = d1; + return y; +} + +static int const month_days[] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; +static char const month_names[] = "JanFebMarAprMayJunJulAugSepOctNovDec"; +static char const day_names[] = "SunMonTueWedThuFriSat"; + +static __exception int get_date_fields(JSContext *ctx, JSValue obj, + double fields[minimum_length(9)], + int is_local, int force) +{ + double dval; + int64_t d, days, wd, y, i, md, h, m, s, ms, tz = 0; + + if (JS_ThisTimeValue(ctx, &dval, obj)) + return -1; + + if (isnan(dval)) { + if (!force) + return FALSE; /* NaN */ + d = 0; /* initialize all fields to 0 */ + } else { + d = dval; /* assuming -8.64e15 <= dval <= -8.64e15 */ + if (is_local) { + tz = -getTimezoneOffset(d); + d += tz * 60000; + } + } + + /* result is >= 0, we can use % */ + h = math_mod(d, 86400000); + days = (d - h) / 86400000; + ms = h % 1000; + h = (h - ms) / 1000; + s = h % 60; + h = (h - s) / 60; + m = h % 60; + h = (h - m) / 60; + wd = math_mod(days + 4, 7); /* week day */ + y = year_from_days(&days); + + for(i = 0; i < 11; i++) { + md = month_days[i]; + if (i == 1) + md += days_in_year(y) - 365; + if (days < md) + break; + days -= md; + } + fields[0] = y; + fields[1] = i; + fields[2] = days + 1; + fields[3] = h; + fields[4] = m; + fields[5] = s; + fields[6] = ms; + fields[7] = wd; + fields[8] = tz; + return TRUE; +} + +static double time_clip(double t) { + if (t >= -8.64e15 && t <= 8.64e15) + return trunc(t) + 0.0; /* convert -0 to +0 */ + else + return NAN; +} + +/* The spec mandates the use of 'double' and it specifies the order + of the operations */ +static double set_date_fields(double fields[minimum_length(7)], int is_local) { + double y, m, dt, ym, mn, day, h, s, milli, time, tv; + int yi, mi, i; + int64_t days; + volatile double temp; /* enforce evaluation order */ + + /* emulate 21.4.1.15 MakeDay ( year, month, date ) */ + y = fields[0]; + m = fields[1]; + dt = fields[2]; + ym = y + floor(m / 12); + mn = fmod(m, 12); + if (mn < 0) + mn += 12; + if (ym < -271821 || ym > 275760) + return NAN; + + yi = ym; + mi = mn; + days = days_from_year(yi); + for(i = 0; i < mi; i++) { + days += month_days[i]; + if (i == 1) + days += days_in_year(yi) - 365; + } + day = days + dt - 1; + + /* emulate 21.4.1.14 MakeTime ( hour, min, sec, ms ) */ + h = fields[3]; + m = fields[4]; + s = fields[5]; + milli = fields[6]; + /* Use a volatile intermediary variable to ensure order of evaluation + * as specified in ECMA. This fixes a test262 error on + * test262/test/built-ins/Date/UTC/fp-evaluation-order.js. + * Without the volatile qualifier, the compile can generate code + * that performs the computation in a different order or with instructions + * that produce a different result such as FMA (float multiply and add). + */ + time = h * 3600000; + time += (temp = m * 60000); + time += (temp = s * 1000); + time += milli; + + /* emulate 21.4.1.16 MakeDate ( day, time ) */ + tv = (temp = day * 86400000) + time; /* prevent generation of FMA */ + if (!isfinite(tv)) + return NAN; + + /* adjust for local time and clip */ + if (is_local) { + int64_t ti = tv < INT64_MIN ? INT64_MIN : tv >= 0x1p63 ? INT64_MAX : (int64_t)tv; + tv += getTimezoneOffset(ti) * 60000; + } + return time_clip(tv); +} + +static JSValue get_date_field(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv, int magic) +{ + // get_date_field(obj, n, is_local) + double fields[9]; + int res, n, is_local; + + is_local = magic & 0x0F; + n = (magic >> 4) & 0x0F; + res = get_date_fields(ctx, this_val, fields, is_local, 0); + if (res < 0) + return JS_EXCEPTION; + if (!res) + return JS_NAN; + + if (magic & 0x100) { // getYear + fields[0] -= 1900; + } + return js_number(fields[n]); +} + +static JSValue set_date_field(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv, int magic) +{ + // _field(obj, first_field, end_field, args, is_local) + double fields[9]; + int res, first_field, end_field, is_local, i, n; + double d, a; + + d = NAN; + first_field = (magic >> 8) & 0x0F; + end_field = (magic >> 4) & 0x0F; + is_local = magic & 0x0F; + + res = get_date_fields(ctx, this_val, fields, is_local, first_field == 0); + if (res < 0) + return JS_EXCEPTION; + + // Argument coercion is observable and must be done unconditionally. + n = min_int(argc, end_field - first_field); + for(i = 0; i < n; i++) { + if (JS_ToFloat64(ctx, &a, argv[i])) + return JS_EXCEPTION; + if (!isfinite(a)) + res = FALSE; + fields[first_field + i] = trunc(a); + } + + if (res && argc > 0) + d = set_date_fields(fields, is_local); + + return JS_SetThisTimeValue(ctx, this_val, d); +} + +/* fmt: + 0: toUTCString: "Tue, 02 Jan 2018 23:04:46 GMT" + 1: toString: "Wed Jan 03 2018 00:05:22 GMT+0100 (CET)" + 2: toISOString: "2018-01-02T23:02:56.927Z" + 3: toLocaleString: "1/2/2018, 11:40:40 PM" + part: 1=date, 2=time 3=all + XXX: should use a variant of strftime(). + */ +static JSValue get_date_string(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv, int magic) +{ + // _string(obj, fmt, part) + char buf[64]; + double fields[9]; + int res, fmt, part, pos; + int y, mon, d, h, m, s, ms, wd, tz; + + fmt = (magic >> 4) & 0x0F; + part = magic & 0x0F; + + res = get_date_fields(ctx, this_val, fields, fmt & 1, 0); + if (res < 0) + return JS_EXCEPTION; + if (!res) { + if (fmt == 2) + return JS_ThrowRangeError(ctx, "Date value is NaN"); + else + return js_new_string8(ctx, "Invalid Date"); + } + + y = fields[0]; + mon = fields[1]; + d = fields[2]; + h = fields[3]; + m = fields[4]; + s = fields[5]; + ms = fields[6]; + wd = fields[7]; + tz = fields[8]; + + pos = 0; + + if (part & 1) { /* date part */ + switch(fmt) { + case 0: + pos += snprintf(buf + pos, sizeof(buf) - pos, + "%.3s, %02d %.3s %0*d ", + day_names + wd * 3, d, + month_names + mon * 3, 4 + (y < 0), y); + break; + case 1: + pos += snprintf(buf + pos, sizeof(buf) - pos, + "%.3s %.3s %02d %0*d", + day_names + wd * 3, + month_names + mon * 3, d, 4 + (y < 0), y); + if (part == 3) { + buf[pos++] = ' '; + } + break; + case 2: + if (y >= 0 && y <= 9999) { + pos += snprintf(buf + pos, sizeof(buf) - pos, + "%04d", y); + } else { + pos += snprintf(buf + pos, sizeof(buf) - pos, + "%+07d", y); + } + pos += snprintf(buf + pos, sizeof(buf) - pos, + "-%02d-%02dT", mon + 1, d); + break; + case 3: + pos += snprintf(buf + pos, sizeof(buf) - pos, + "%02d/%02d/%0*d", mon + 1, d, 4 + (y < 0), y); + if (part == 3) { + buf[pos++] = ','; + buf[pos++] = ' '; + } + break; + } + } + if (part & 2) { /* time part */ + switch(fmt) { + case 0: + pos += snprintf(buf + pos, sizeof(buf) - pos, + "%02d:%02d:%02d GMT", h, m, s); + break; + case 1: + pos += snprintf(buf + pos, sizeof(buf) - pos, + "%02d:%02d:%02d GMT", h, m, s); + if (tz < 0) { + buf[pos++] = '-'; + tz = -tz; + } else { + buf[pos++] = '+'; + } + /* tz is >= 0, can use % */ + pos += snprintf(buf + pos, sizeof(buf) - pos, + "%02d%02d", tz / 60, tz % 60); + /* XXX: tack the time zone code? */ + break; + case 2: + pos += snprintf(buf + pos, sizeof(buf) - pos, + "%02d:%02d:%02d.%03dZ", h, m, s, ms); + break; + case 3: + pos += snprintf(buf + pos, sizeof(buf) - pos, + "%02d:%02d:%02d %cM", (h + 11) % 12 + 1, m, s, + (h < 12) ? 'A' : 'P'); + break; + } + } + if (!pos) { + // XXX: should throw exception? + return JS_AtomToString(ctx, JS_ATOM_empty_string); + } + return js_new_string8_len(ctx, buf, pos); +} + +/* OS dependent: return the UTC time in ms since 1970. */ +static int64_t date_now(void) { + return js__gettimeofday_us() / 1000; +} + +static JSValue js_date_constructor(JSContext *ctx, JSValue new_target, + int argc, JSValue *argv) +{ + // Date(y, mon, d, h, m, s, ms) + JSValue rv; + int i, n; + double a, val; + + if (JS_IsUndefined(new_target)) { + /* invoked as function */ + argc = 0; + } + n = argc; + if (n == 0) { + val = date_now(); + } else if (n == 1) { + JSValue v, dv; + if (JS_VALUE_GET_TAG(argv[0]) == JS_TAG_OBJECT) { + JSObject *p = JS_VALUE_GET_OBJ(argv[0]); + if (p->class_id == JS_CLASS_DATE && JS_IsNumber(p->u.object_data)) { + if (JS_ToFloat64(ctx, &val, p->u.object_data)) + return JS_EXCEPTION; + val = time_clip(val); + goto has_val; + } + } + v = JS_ToPrimitive(ctx, argv[0], HINT_NONE); + if (JS_IsString(v)) { + dv = js_Date_parse(ctx, JS_UNDEFINED, 1, &v); + JS_FreeValue(ctx, v); + if (JS_IsException(dv)) + return JS_EXCEPTION; + if (JS_ToFloat64Free(ctx, &val, dv)) + return JS_EXCEPTION; + } else { + if (JS_ToFloat64Free(ctx, &val, v)) + return JS_EXCEPTION; + } + val = time_clip(val); + } else { + double fields[] = { 0, 0, 1, 0, 0, 0, 0 }; + if (n > 7) + n = 7; + for(i = 0; i < n; i++) { + if (JS_ToFloat64(ctx, &a, argv[i])) + return JS_EXCEPTION; + if (!isfinite(a)) + break; + fields[i] = trunc(a); + if (i == 0 && fields[0] >= 0 && fields[0] < 100) + fields[0] += 1900; + } + val = (i == n) ? set_date_fields(fields, 1) : NAN; + } +has_val: + rv = js_create_from_ctor(ctx, new_target, JS_CLASS_DATE); + if (!JS_IsException(rv)) + JS_SetObjectData(ctx, rv, js_float64(val)); + if (!JS_IsException(rv) && JS_IsUndefined(new_target)) { + /* invoked as a function, return (new Date()).toString(); */ + JSValue s; + s = get_date_string(ctx, rv, 0, NULL, 0x13); + JS_FreeValue(ctx, rv); + rv = s; + } + return rv; +} + +static JSValue js_Date_UTC(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + // UTC(y, mon, d, h, m, s, ms) + double fields[] = { 0, 0, 1, 0, 0, 0, 0 }; + int i, n; + double a; + + n = argc; + if (n == 0) + return JS_NAN; + if (n > 7) + n = 7; + for(i = 0; i < n; i++) { + if (JS_ToFloat64(ctx, &a, argv[i])) + return JS_EXCEPTION; + if (!isfinite(a)) + return JS_NAN; + fields[i] = trunc(a); + if (i == 0 && fields[0] >= 0 && fields[0] < 100) + fields[0] += 1900; + } + return js_float64(set_date_fields(fields, 0)); +} + +/* Date string parsing */ + +static BOOL string_skip_char(const uint8_t *sp, int *pp, int c) { + if (sp[*pp] == c) { + *pp += 1; + return TRUE; + } else { + return FALSE; + } +} + +/* skip spaces, update offset, return next char */ +static int string_skip_spaces(const uint8_t *sp, int *pp) { + int c; + while ((c = sp[*pp]) == ' ') + *pp += 1; + return c; +} + +/* skip dashes dots and commas */ +static int string_skip_separators(const uint8_t *sp, int *pp) { + int c; + while ((c = sp[*pp]) == '-' || c == '/' || c == '.' || c == ',') + *pp += 1; + return c; +} + +/* skip a word, stop on spaces, digits and separators, update offset */ +static int string_skip_until(const uint8_t *sp, int *pp, const char *stoplist) { + int c; + while (!strchr(stoplist, c = sp[*pp])) + *pp += 1; + return c; +} + +/* parse a numeric field (max_digits = 0 -> no maximum) */ +static BOOL string_get_digits(const uint8_t *sp, int *pp, int *pval, + int min_digits, int max_digits) +{ + int v = 0; + int c, p = *pp, p_start; + + p_start = p; + while ((c = sp[p]) >= '0' && c <= '9') { + v = v * 10 + c - '0'; + p++; + if (p - p_start == max_digits) + break; + } + if (p - p_start < min_digits) + return FALSE; + *pval = v; + *pp = p; + return TRUE; +} + +static BOOL string_get_milliseconds(const uint8_t *sp, int *pp, int *pval) { + /* parse optional fractional part as milliseconds and truncate. */ + /* spec does not indicate which rounding should be used */ + int mul = 100, ms = 0, c, p_start, p = *pp; + + c = sp[p]; + if (c == '.' || c == ',') { + p++; + p_start = p; + while ((c = sp[p]) >= '0' && c <= '9') { + ms += (c - '0') * mul; + mul /= 10; + p++; + if (p - p_start == 9) + break; + } + if (p > p_start) { + /* only consume the separator if digits are present */ + *pval = ms; + *pp = p; + } + } + return TRUE; +} + +static BOOL string_get_tzoffset(const uint8_t *sp, int *pp, int *tzp, BOOL strict) { + int tz = 0, sgn, hh, mm, p = *pp; + + sgn = sp[p++]; + if (sgn == '+' || sgn == '-') { + int n = p; + if (!string_get_digits(sp, &p, &hh, 1, 9)) + return FALSE; + n = p - n; + if (strict && n != 2 && n != 4) + return FALSE; + while (n > 4) { + n -= 2; + hh /= 100; + } + if (n > 2) { + mm = hh % 100; + hh = hh / 100; + } else { + mm = 0; + if (string_skip_char(sp, &p, ':') /* optional separator */ + && !string_get_digits(sp, &p, &mm, 2, 2)) + return FALSE; + } + if (hh > 23 || mm > 59) + return FALSE; + tz = hh * 60 + mm; + if (sgn != '+') + tz = -tz; + } else + if (sgn != 'Z') { + return FALSE; + } + *pp = p; + *tzp = tz; + return TRUE; +} + +static BOOL string_match(const uint8_t *sp, int *pp, const char *s) { + int p = *pp; + while (*s != '\0') { + if (to_upper_ascii(sp[p]) != to_upper_ascii(*s++)) + return FALSE; + p++; + } + *pp = p; + return TRUE; +} + +static int find_abbrev(const uint8_t *sp, int p, const char *list, int count) { + int n, i; + + for (n = 0; n < count; n++) { + for (i = 0;; i++) { + if (to_upper_ascii(sp[p + i]) != to_upper_ascii(list[n * 3 + i])) + break; + if (i == 2) + return n; + } + } + return -1; +} + +static BOOL string_get_month(const uint8_t *sp, int *pp, int *pval) { + int n; + + n = find_abbrev(sp, *pp, month_names, 12); + if (n < 0) + return FALSE; + + *pval = n + 1; + *pp += 3; + return TRUE; +} + +/* parse toISOString format */ +static BOOL js_date_parse_isostring(const uint8_t *sp, int fields[9], BOOL *is_local) { + int sgn, i, p = 0; + + /* initialize fields to the beginning of the Epoch */ + for (i = 0; i < 9; i++) { + fields[i] = (i == 2); + } + *is_local = FALSE; + + /* year is either yyyy digits or [+-]yyyyyy */ + sgn = sp[p]; + if (sgn == '-' || sgn == '+') { + p++; + if (!string_get_digits(sp, &p, &fields[0], 6, 6)) + return FALSE; + if (sgn == '-') { + if (fields[0] == 0) + return FALSE; // reject -000000 + fields[0] = -fields[0]; + } + } else { + if (!string_get_digits(sp, &p, &fields[0], 4, 4)) + return FALSE; + } + if (string_skip_char(sp, &p, '-')) { + if (!string_get_digits(sp, &p, &fields[1], 2, 2)) /* month */ + return FALSE; + if (fields[1] < 1) + return FALSE; + fields[1] -= 1; + if (string_skip_char(sp, &p, '-')) { + if (!string_get_digits(sp, &p, &fields[2], 2, 2)) /* day */ + return FALSE; + if (fields[2] < 1) + return FALSE; + } + } + if (string_skip_char(sp, &p, 'T')) { + *is_local = TRUE; + if (!string_get_digits(sp, &p, &fields[3], 2, 2) /* hour */ + || !string_skip_char(sp, &p, ':') + || !string_get_digits(sp, &p, &fields[4], 2, 2)) { /* minute */ + fields[3] = 100; // reject unconditionally + return TRUE; + } + if (string_skip_char(sp, &p, ':')) { + if (!string_get_digits(sp, &p, &fields[5], 2, 2)) /* second */ + return FALSE; + string_get_milliseconds(sp, &p, &fields[6]); + } + } + /* parse the time zone offset if present: [+-]HH:mm or [+-]HHmm */ + if (sp[p]) { + *is_local = FALSE; + if (!string_get_tzoffset(sp, &p, &fields[8], TRUE)) + return FALSE; + } + /* error if extraneous characters */ + return sp[p] == '\0'; +} + +static struct { + char name[6]; + int16_t offset; +} const js_tzabbr[] = { + { "GMT", 0 }, // Greenwich Mean Time + { "UTC", 0 }, // Coordinated Universal Time + { "UT", 0 }, // Universal Time + { "Z", 0 }, // Zulu Time + { "EDT", -4 * 60 }, // Eastern Daylight Time + { "EST", -5 * 60 }, // Eastern Standard Time + { "CDT", -5 * 60 }, // Central Daylight Time + { "CST", -6 * 60 }, // Central Standard Time + { "MDT", -6 * 60 }, // Mountain Daylight Time + { "MST", -7 * 60 }, // Mountain Standard Time + { "PDT", -7 * 60 }, // Pacific Daylight Time + { "PST", -8 * 60 }, // Pacific Standard Time + { "WET", +0 * 60 }, // Western European Time + { "WEST", +1 * 60 }, // Western European Summer Time + { "CET", +1 * 60 }, // Central European Time + { "CEST", +2 * 60 }, // Central European Summer Time + { "EET", +2 * 60 }, // Eastern European Time + { "EEST", +3 * 60 }, // Eastern European Summer Time +}; + +static BOOL string_get_tzabbr(const uint8_t *sp, int *pp, int *offset) { + size_t i; + + for (i = 0; i < countof(js_tzabbr); i++) { + if (string_match(sp, pp, js_tzabbr[i].name)) { + *offset = js_tzabbr[i].offset; + return TRUE; + } + } + return FALSE; +} + +/* parse toString, toUTCString and other formats */ +static BOOL js_date_parse_otherstring(const uint8_t *sp, + int fields[minimum_length(9)], + BOOL *is_local) { + int c, i, val, p = 0, p_start; + int num[3]; + BOOL has_year = FALSE; + BOOL has_mon = FALSE; + BOOL has_time = FALSE; + int num_index = 0; + + /* initialize fields to the beginning of 2001-01-01 */ + fields[0] = 2001; + fields[1] = 1; + fields[2] = 1; + for (i = 3; i < 9; i++) { + fields[i] = 0; + } + *is_local = TRUE; + + while (string_skip_spaces(sp, &p)) { + p_start = p; + if ((c = sp[p]) == '+' || c == '-') { + if (has_time && string_get_tzoffset(sp, &p, &fields[8], FALSE)) { + *is_local = FALSE; + } else { + p++; + if (string_get_digits(sp, &p, &val, 1, 9)) { + if (c == '-') { + if (val == 0) + return FALSE; + val = -val; + } + fields[0] = val; + has_year = TRUE; + } + } + } else + if (string_get_digits(sp, &p, &val, 1, 9)) { + if (string_skip_char(sp, &p, ':')) { + /* time part */ + fields[3] = val; + if (!string_get_digits(sp, &p, &fields[4], 1, 2)) + return FALSE; + if (string_skip_char(sp, &p, ':')) { + if (!string_get_digits(sp, &p, &fields[5], 1, 2)) + return FALSE; + string_get_milliseconds(sp, &p, &fields[6]); + } else + if (sp[p] != '\0' && sp[p] != ' ') + return FALSE; + has_time = TRUE; + } else { + if (p - p_start > 2) { + fields[0] = val; + has_year = TRUE; + } else + if (val < 1 || val > 31) { + fields[0] = val + (val < 100) * 1900 + (val < 50) * 100; + has_year = TRUE; + } else { + if (num_index == 3) + return FALSE; + num[num_index++] = val; + } + } + } else + if (string_get_month(sp, &p, &fields[1])) { + has_mon = TRUE; + string_skip_until(sp, &p, "0123456789 -/("); + } else + if (has_time && string_match(sp, &p, "PM")) { + /* hours greater than 12 will be incremented and + rejected by the range check in js_Date_parse */ + /* 00:00 PM will be silently converted as 12:00 */ + if (fields[3] != 12) + fields[3] += 12; + continue; + } else + if (has_time && string_match(sp, &p, "AM")) { + /* 00:00 AM will be silently accepted */ + if (fields[3] > 12) + return FALSE; + if (fields[3] == 12) + fields[3] -= 12; + continue; + } else + if (string_get_tzabbr(sp, &p, &fields[8])) { + *is_local = FALSE; + continue; + } else + if (c == '(') { /* skip parenthesized phrase */ + int level = 0; + while ((c = sp[p]) != '\0') { + p++; + level += (c == '('); + level -= (c == ')'); + if (!level) + break; + } + if (level > 0) + return FALSE; + } else + if (c == ')') { + return FALSE; + } else { + if (has_year + has_mon + has_time + num_index) + return FALSE; + /* skip a word */ + string_skip_until(sp, &p, " -/("); + } + string_skip_separators(sp, &p); + } + if (num_index + has_year + has_mon > 3) + return FALSE; + + switch (num_index) { + case 0: + if (!has_year) + return FALSE; + break; + case 1: + if (has_mon) + fields[2] = num[0]; + else + fields[1] = num[0]; + break; + case 2: + if (has_year) { + fields[1] = num[0]; + fields[2] = num[1]; + } else + if (has_mon) { + fields[0] = num[1] + (num[1] < 100) * 1900 + (num[1] < 50) * 100; + fields[2] = num[0]; + } else { + fields[1] = num[0]; + fields[2] = num[1]; + } + break; + case 3: + fields[0] = num[2] + (num[2] < 100) * 1900 + (num[2] < 50) * 100; + fields[1] = num[0]; + fields[2] = num[1]; + break; + default: + return FALSE; + } + if (fields[1] < 1 || fields[2] < 1) + return FALSE; + fields[1] -= 1; + return TRUE; +} + +static JSValue js_Date_parse(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + JSValue s, rv; + int fields[9]; + double fields1[9]; + double d; + int i, c; + JSString *sp; + uint8_t buf[128]; + BOOL is_local; + + rv = JS_NAN; + + s = JS_ToString(ctx, argv[0]); + if (JS_IsException(s)) + return JS_EXCEPTION; + + sp = JS_VALUE_GET_STRING(s); + /* convert the string as a byte array */ + for (i = 0; i < sp->len && i < (int)countof(buf) - 1; i++) { + c = string_get(sp, i); + if (c > 255) + c = (c == 0x2212) ? '-' : 'x'; + buf[i] = c; + } + buf[i] = '\0'; + if (js_date_parse_isostring(buf, fields, &is_local) + || js_date_parse_otherstring(buf, fields, &is_local)) { + static int const field_max[6] = { 0, 11, 31, 24, 59, 59 }; + BOOL valid = TRUE; + /* check field maximum values */ + for (i = 1; i < 6; i++) { + if (fields[i] > field_max[i]) + valid = FALSE; + } + /* special case 24:00:00.000 */ + if (fields[3] == 24 && (fields[4] | fields[5] | fields[6])) + valid = FALSE; + if (valid) { + for(i = 0; i < 7; i++) + fields1[i] = fields[i]; + d = set_date_fields(fields1, is_local) - fields[8] * 60000; + rv = JS_NewFloat64(ctx, d); + } + } + JS_FreeValue(ctx, s); + return rv; +} + +static JSValue js_Date_now(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + // now() + return js_int64(date_now()); +} + +static JSValue js_date_Symbol_toPrimitive(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + // Symbol_toPrimitive(hint) + JSValue obj = this_val; + JSAtom hint = JS_ATOM_NULL; + int hint_num; + + if (!JS_IsObject(obj)) + return JS_ThrowTypeErrorNotAnObject(ctx); + + if (JS_IsString(argv[0])) { + hint = JS_ValueToAtom(ctx, argv[0]); + if (hint == JS_ATOM_NULL) + return JS_EXCEPTION; + JS_FreeAtom(ctx, hint); + } + switch (hint) { + case JS_ATOM_number: + case JS_ATOM_integer: + hint_num = HINT_NUMBER; + break; + case JS_ATOM_string: + case JS_ATOM_default: + hint_num = HINT_STRING; + break; + default: + return JS_ThrowTypeError(ctx, "invalid hint"); + } + return JS_ToPrimitive(ctx, obj, hint_num | HINT_FORCE_ORDINARY); +} + +static JSValue js_date_getTimezoneOffset(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + // getTimezoneOffset() + double v; + + if (JS_ThisTimeValue(ctx, &v, this_val)) + return JS_EXCEPTION; + if (isnan(v)) + return JS_NAN; + else + /* assuming -8.64e15 <= v <= -8.64e15 */ + return js_int64(getTimezoneOffset((int64_t)trunc(v))); +} + +static JSValue js_date_getTime(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + // getTime() + double v; + + if (JS_ThisTimeValue(ctx, &v, this_val)) + return JS_EXCEPTION; + return js_float64(v); +} + +static JSValue js_date_setTime(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + // setTime(v) + double v; + + if (JS_ThisTimeValue(ctx, &v, this_val) || JS_ToFloat64(ctx, &v, argv[0])) + return JS_EXCEPTION; + return JS_SetThisTimeValue(ctx, this_val, time_clip(v)); +} + +static JSValue js_date_setYear(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + // setYear(y) + double y; + JSValue args[1]; + + if (JS_ThisTimeValue(ctx, &y, this_val) || JS_ToFloat64(ctx, &y, argv[0])) + return JS_EXCEPTION; + y = +y; + if (isfinite(y)) { + y = trunc(y); + if (y >= 0 && y < 100) + y += 1900; + } + args[0] = js_float64(y); + return set_date_field(ctx, this_val, 1, args, 0x011); +} + +static JSValue js_date_toJSON(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + // toJSON(key) + JSValue obj, tv, method, rv; + double d; + + rv = JS_EXCEPTION; + tv = JS_UNDEFINED; + + obj = JS_ToObject(ctx, this_val); + tv = JS_ToPrimitive(ctx, obj, HINT_NUMBER); + if (JS_IsException(tv)) + goto exception; + if (JS_IsNumber(tv)) { + if (JS_ToFloat64(ctx, &d, tv) < 0) + goto exception; + if (!isfinite(d)) { + rv = JS_NULL; + goto done; + } + } + method = JS_GetPropertyStr(ctx, obj, "toISOString"); + if (JS_IsException(method)) + goto exception; + if (!JS_IsFunction(ctx, method)) { + JS_ThrowTypeError(ctx, "object needs toISOString method"); + JS_FreeValue(ctx, method); + goto exception; + } + rv = JS_CallFree(ctx, method, obj, 0, NULL); +exception: +done: + JS_FreeValue(ctx, obj); + JS_FreeValue(ctx, tv); + return rv; +} + +static const JSCFunctionListEntry js_date_funcs[] = { + JS_CFUNC_DEF("now", 0, js_Date_now ), + JS_CFUNC_DEF("parse", 1, js_Date_parse ), + JS_CFUNC_DEF("UTC", 7, js_Date_UTC ), +}; + +static const JSCFunctionListEntry js_date_proto_funcs[] = { + JS_CFUNC_DEF("valueOf", 0, js_date_getTime ), + JS_CFUNC_MAGIC_DEF("toString", 0, get_date_string, 0x13 ), + JS_CFUNC_DEF("[Symbol.toPrimitive]", 1, js_date_Symbol_toPrimitive ), + JS_CFUNC_MAGIC_DEF("toUTCString", 0, get_date_string, 0x03 ), + JS_ALIAS_DEF("toGMTString", "toUTCString" ), + JS_CFUNC_MAGIC_DEF("toISOString", 0, get_date_string, 0x23 ), + JS_CFUNC_MAGIC_DEF("toDateString", 0, get_date_string, 0x11 ), + JS_CFUNC_MAGIC_DEF("toTimeString", 0, get_date_string, 0x12 ), + JS_CFUNC_MAGIC_DEF("toLocaleString", 0, get_date_string, 0x33 ), + JS_CFUNC_MAGIC_DEF("toLocaleDateString", 0, get_date_string, 0x31 ), + JS_CFUNC_MAGIC_DEF("toLocaleTimeString", 0, get_date_string, 0x32 ), + JS_CFUNC_DEF("getTimezoneOffset", 0, js_date_getTimezoneOffset ), + JS_CFUNC_DEF("getTime", 0, js_date_getTime ), + JS_CFUNC_MAGIC_DEF("getYear", 0, get_date_field, 0x101 ), + JS_CFUNC_MAGIC_DEF("getFullYear", 0, get_date_field, 0x01 ), + JS_CFUNC_MAGIC_DEF("getUTCFullYear", 0, get_date_field, 0x00 ), + JS_CFUNC_MAGIC_DEF("getMonth", 0, get_date_field, 0x11 ), + JS_CFUNC_MAGIC_DEF("getUTCMonth", 0, get_date_field, 0x10 ), + JS_CFUNC_MAGIC_DEF("getDate", 0, get_date_field, 0x21 ), + JS_CFUNC_MAGIC_DEF("getUTCDate", 0, get_date_field, 0x20 ), + JS_CFUNC_MAGIC_DEF("getHours", 0, get_date_field, 0x31 ), + JS_CFUNC_MAGIC_DEF("getUTCHours", 0, get_date_field, 0x30 ), + JS_CFUNC_MAGIC_DEF("getMinutes", 0, get_date_field, 0x41 ), + JS_CFUNC_MAGIC_DEF("getUTCMinutes", 0, get_date_field, 0x40 ), + JS_CFUNC_MAGIC_DEF("getSeconds", 0, get_date_field, 0x51 ), + JS_CFUNC_MAGIC_DEF("getUTCSeconds", 0, get_date_field, 0x50 ), + JS_CFUNC_MAGIC_DEF("getMilliseconds", 0, get_date_field, 0x61 ), + JS_CFUNC_MAGIC_DEF("getUTCMilliseconds", 0, get_date_field, 0x60 ), + JS_CFUNC_MAGIC_DEF("getDay", 0, get_date_field, 0x71 ), + JS_CFUNC_MAGIC_DEF("getUTCDay", 0, get_date_field, 0x70 ), + JS_CFUNC_DEF("setTime", 1, js_date_setTime ), + JS_CFUNC_MAGIC_DEF("setMilliseconds", 1, set_date_field, 0x671 ), + JS_CFUNC_MAGIC_DEF("setUTCMilliseconds", 1, set_date_field, 0x670 ), + JS_CFUNC_MAGIC_DEF("setSeconds", 2, set_date_field, 0x571 ), + JS_CFUNC_MAGIC_DEF("setUTCSeconds", 2, set_date_field, 0x570 ), + JS_CFUNC_MAGIC_DEF("setMinutes", 3, set_date_field, 0x471 ), + JS_CFUNC_MAGIC_DEF("setUTCMinutes", 3, set_date_field, 0x470 ), + JS_CFUNC_MAGIC_DEF("setHours", 4, set_date_field, 0x371 ), + JS_CFUNC_MAGIC_DEF("setUTCHours", 4, set_date_field, 0x370 ), + JS_CFUNC_MAGIC_DEF("setDate", 1, set_date_field, 0x231 ), + JS_CFUNC_MAGIC_DEF("setUTCDate", 1, set_date_field, 0x230 ), + JS_CFUNC_MAGIC_DEF("setMonth", 2, set_date_field, 0x131 ), + JS_CFUNC_MAGIC_DEF("setUTCMonth", 2, set_date_field, 0x130 ), + JS_CFUNC_DEF("setYear", 1, js_date_setYear ), + JS_CFUNC_MAGIC_DEF("setFullYear", 3, set_date_field, 0x031 ), + JS_CFUNC_MAGIC_DEF("setUTCFullYear", 3, set_date_field, 0x030 ), + JS_CFUNC_DEF("toJSON", 1, js_date_toJSON ), +}; + +JSValue JS_NewDate(JSContext *ctx, double epoch_ms) +{ + JSValue obj = js_create_from_ctor(ctx, JS_UNDEFINED, JS_CLASS_DATE); + if (JS_IsException(obj)) + return JS_EXCEPTION; + JS_SetObjectData(ctx, obj, js_float64(time_clip(epoch_ms))); + return obj; +} + +void JS_AddIntrinsicDate(JSContext *ctx) +{ + JSValue obj; + + /* Date */ + ctx->class_proto[JS_CLASS_DATE] = JS_NewObject(ctx); + JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_DATE], js_date_proto_funcs, + countof(js_date_proto_funcs)); + obj = JS_NewGlobalCConstructor(ctx, "Date", js_date_constructor, 7, + ctx->class_proto[JS_CLASS_DATE]); + JS_SetPropertyFunctionList(ctx, obj, js_date_funcs, countof(js_date_funcs)); +} + +/* eval */ + +void JS_AddIntrinsicEval(JSContext *ctx) +{ + ctx->eval_internal = __JS_EvalInternal; +} + +/* BigInt */ + +static JSValue JS_ToBigIntCtorFree(JSContext *ctx, JSValue val) +{ + uint32_t tag; + + redo: + tag = JS_VALUE_GET_NORM_TAG(val); + switch(tag) { + case JS_TAG_INT: + case JS_TAG_BOOL: + val = JS_NewBigInt64(ctx, JS_VALUE_GET_INT(val)); + break; + case JS_TAG_BIG_INT: + break; + case JS_TAG_FLOAT64: + { + bf_t *a, a_s; + + a = JS_ToBigInt1(ctx, &a_s, val); + if (!bf_is_finite(a)) { + JS_FreeValue(ctx, val); + val = JS_ThrowRangeError(ctx, "cannot convert NaN or Infinity to BigInt"); + } else { + JSValue val1 = JS_NewBigInt(ctx); + bf_t *r; + int ret; + if (JS_IsException(val1)) { + JS_FreeValue(ctx, val); + return JS_EXCEPTION; + } + r = JS_GetBigInt(val1); + ret = bf_set(r, a); + ret |= bf_rint(r, BF_RNDZ); + JS_FreeValue(ctx, val); + if (ret & BF_ST_MEM_ERROR) { + JS_FreeValue(ctx, val1); + val = JS_ThrowOutOfMemory(ctx); + } else if (ret & BF_ST_INEXACT) { + JS_FreeValue(ctx, val1); + val = JS_ThrowRangeError(ctx, "cannot convert to BigInt: not an integer"); + } else { + val = JS_CompactBigInt(ctx, val1); + } + } + if (a == &a_s) + bf_delete(a); + } + break; + case JS_TAG_STRING: + val = JS_StringToBigIntErr(ctx, val); + break; + case JS_TAG_OBJECT: + val = JS_ToPrimitiveFree(ctx, val, HINT_NUMBER); + if (JS_IsException(val)) + break; + goto redo; + case JS_TAG_NULL: + case JS_TAG_UNDEFINED: + default: + JS_FreeValue(ctx, val); + return JS_ThrowTypeError(ctx, "cannot convert to BigInt"); + } + return val; +} + +static JSValue js_bigint_constructor(JSContext *ctx, + JSValue new_target, + int argc, JSValue *argv) +{ + if (!JS_IsUndefined(new_target)) + return JS_ThrowTypeError(ctx, "not a constructor"); + return JS_ToBigIntCtorFree(ctx, js_dup(argv[0])); +} + +static JSValue js_thisBigIntValue(JSContext *ctx, JSValue this_val) +{ + if (JS_IsBigInt(ctx, this_val)) + return js_dup(this_val); + + if (JS_VALUE_GET_TAG(this_val) == JS_TAG_OBJECT) { + JSObject *p = JS_VALUE_GET_OBJ(this_val); + if (p->class_id == JS_CLASS_BIG_INT) { + if (JS_IsBigInt(ctx, p->u.object_data)) + return js_dup(p->u.object_data); + } + } + return JS_ThrowTypeError(ctx, "not a BigInt"); +} + +static JSValue js_bigint_toString(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + JSValue val; + int base; + JSValue ret; + + val = js_thisBigIntValue(ctx, this_val); + if (JS_IsException(val)) + return val; + if (argc == 0 || JS_IsUndefined(argv[0])) { + base = 10; + } else { + base = js_get_radix(ctx, argv[0]); + if (base < 0) + goto fail; + } + ret = js_bigint_to_string1(ctx, val, base); + JS_FreeValue(ctx, val); + return ret; + fail: + JS_FreeValue(ctx, val); + return JS_EXCEPTION; +} + +static JSValue js_bigint_valueOf(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + return js_thisBigIntValue(ctx, this_val); +} + +static JSValue js_bigint_asUintN(JSContext *ctx, + JSValue this_val, + int argc, JSValue *argv, int asIntN) +{ + uint64_t bits; + bf_t a_s, *a = &a_s, *r, mask_s, *mask = &mask_s; + JSValue res; + + if (JS_ToIndex(ctx, &bits, argv[0])) + return JS_EXCEPTION; + res = JS_NewBigInt(ctx); + if (JS_IsException(res)) + return JS_EXCEPTION; + a = JS_ToBigInt(ctx, &a_s, argv[1]); + if (!a) { + JS_FreeValue(ctx, res); + return JS_EXCEPTION; + } + /* XXX: optimize */ + r = JS_GetBigInt(res); + bf_init(ctx->bf_ctx, mask); + bf_set_ui(mask, 1); + bf_mul_2exp(mask, bits, BF_PREC_INF, BF_RNDZ); + bf_add_si(mask, mask, -1, BF_PREC_INF, BF_RNDZ); + bf_logic_and(r, a, mask); + if (asIntN && bits != 0) { + bf_set_ui(mask, 1); + bf_mul_2exp(mask, bits - 1, BF_PREC_INF, BF_RNDZ); + if (bf_cmpu(r, mask) >= 0) { + bf_set_ui(mask, 1); + bf_mul_2exp(mask, bits, BF_PREC_INF, BF_RNDZ); + bf_sub(r, r, mask, BF_PREC_INF, BF_RNDZ); + } + } + bf_delete(mask); + JS_FreeBigInt(ctx, a, &a_s); + return JS_CompactBigInt(ctx, res); +} + +static const JSCFunctionListEntry js_bigint_funcs[] = { + JS_CFUNC_MAGIC_DEF("asUintN", 2, js_bigint_asUintN, 0 ), + JS_CFUNC_MAGIC_DEF("asIntN", 2, js_bigint_asUintN, 1 ), +}; + +static const JSCFunctionListEntry js_bigint_proto_funcs[] = { + JS_CFUNC_DEF("toString", 0, js_bigint_toString ), + JS_CFUNC_DEF("valueOf", 0, js_bigint_valueOf ), + JS_PROP_STRING_DEF("[Symbol.toStringTag]", "BigInt", JS_PROP_CONFIGURABLE ), +}; + +void JS_AddIntrinsicBigInt(JSContext *ctx) +{ + JSValue obj1; + + ctx->class_proto[JS_CLASS_BIG_INT] = JS_NewObject(ctx); + JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_BIG_INT], + js_bigint_proto_funcs, + countof(js_bigint_proto_funcs)); + obj1 = JS_NewGlobalCConstructor(ctx, "BigInt", js_bigint_constructor, 1, + ctx->class_proto[JS_CLASS_BIG_INT]); + JS_SetPropertyFunctionList(ctx, obj1, js_bigint_funcs, + countof(js_bigint_funcs)); +} + +static const char * const native_error_name[JS_NATIVE_ERROR_COUNT] = { + "EvalError", "RangeError", "ReferenceError", + "SyntaxError", "TypeError", "URIError", + "InternalError", "AggregateError", +}; + +/* Minimum amount of objects to be able to compile code and display + error messages. No JSAtom should be allocated by this function. */ +static void JS_AddIntrinsicBasicObjects(JSContext *ctx) +{ + JSValue proto; + int i; + + ctx->class_proto[JS_CLASS_OBJECT] = JS_NewObjectProto(ctx, JS_NULL); + ctx->function_proto = JS_NewCFunction3(ctx, js_function_proto, "", 0, + JS_CFUNC_generic, 0, + ctx->class_proto[JS_CLASS_OBJECT]); + ctx->class_proto[JS_CLASS_BYTECODE_FUNCTION] = js_dup(ctx->function_proto); + ctx->class_proto[JS_CLASS_ERROR] = JS_NewObject(ctx); + JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_ERROR], + js_error_proto_funcs, + countof(js_error_proto_funcs)); + + for(i = 0; i < JS_NATIVE_ERROR_COUNT; i++) { + proto = JS_NewObjectProto(ctx, ctx->class_proto[JS_CLASS_ERROR]); + JS_DefinePropertyValue(ctx, proto, JS_ATOM_name, + JS_NewAtomString(ctx, native_error_name[i]), + JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE); + JS_DefinePropertyValue(ctx, proto, JS_ATOM_message, + JS_AtomToString(ctx, JS_ATOM_empty_string), + JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE); + ctx->native_error_proto[i] = proto; + } + + /* the array prototype is an array */ + ctx->class_proto[JS_CLASS_ARRAY] = + JS_NewObjectProtoClass(ctx, ctx->class_proto[JS_CLASS_OBJECT], + JS_CLASS_ARRAY); + + ctx->array_shape = js_new_shape2(ctx, get_proto_obj(ctx->class_proto[JS_CLASS_ARRAY]), + JS_PROP_INITIAL_HASH_SIZE, 1); + add_shape_property(ctx, &ctx->array_shape, NULL, + JS_ATOM_length, JS_PROP_WRITABLE | JS_PROP_LENGTH); + + /* XXX: could test it on first context creation to ensure that no + new atoms are created in JS_AddIntrinsicBasicObjects(). It is + necessary to avoid useless renumbering of atoms after + JS_EvalBinary() if it is done just after + JS_AddIntrinsicBasicObjects(). */ + // assert(ctx->rt->atom_count == JS_ATOM_END); +} + +void JS_AddIntrinsicBaseObjects(JSContext *ctx) +{ + int i; + JSValue obj, number_obj; + JSValue obj1; + + ctx->throw_type_error = JS_NewCFunction(ctx, js_throw_type_error, NULL, 0); + + /* add caller and arguments properties to throw a TypeError */ + obj1 = JS_NewCFunction(ctx, js_function_proto_caller, NULL, 0); + JS_DefineProperty(ctx, ctx->function_proto, JS_ATOM_caller, JS_UNDEFINED, + obj1, ctx->throw_type_error, + JS_PROP_HAS_GET | JS_PROP_HAS_SET | + JS_PROP_HAS_CONFIGURABLE | JS_PROP_CONFIGURABLE); + JS_DefineProperty(ctx, ctx->function_proto, JS_ATOM_arguments, JS_UNDEFINED, + obj1, ctx->throw_type_error, + JS_PROP_HAS_GET | JS_PROP_HAS_SET | + JS_PROP_HAS_CONFIGURABLE | JS_PROP_CONFIGURABLE); + JS_FreeValue(ctx, obj1); + JS_FreeValue(ctx, js_object_seal(ctx, JS_UNDEFINED, 1, &ctx->throw_type_error, 1)); + + ctx->global_obj = JS_NewObject(ctx); + ctx->global_var_obj = JS_NewObjectProto(ctx, JS_NULL); + + /* Object */ + obj = JS_NewGlobalCConstructor(ctx, "Object", js_object_constructor, 1, + ctx->class_proto[JS_CLASS_OBJECT]); + JS_SetPropertyFunctionList(ctx, obj, js_object_funcs, countof(js_object_funcs)); + JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_OBJECT], + js_object_proto_funcs, countof(js_object_proto_funcs)); + + /* Function */ + JS_SetPropertyFunctionList(ctx, ctx->function_proto, js_function_proto_funcs, countof(js_function_proto_funcs)); + ctx->function_ctor = JS_NewCFunctionMagic(ctx, js_function_constructor, + "Function", 1, JS_CFUNC_constructor_or_func_magic, + JS_FUNC_NORMAL); + JS_NewGlobalCConstructor2(ctx, js_dup(ctx->function_ctor), "Function", + ctx->function_proto); + + /* Error */ + ctx->error_ctor = JS_NewCFunctionMagic(ctx, js_error_constructor, + "Error", 1, JS_CFUNC_constructor_or_func_magic, -1); + JS_NewGlobalCConstructor2(ctx, js_dup(ctx->error_ctor), + "Error", ctx->class_proto[JS_CLASS_ERROR]); + JS_SetPropertyFunctionList(ctx, ctx->error_ctor, js_error_funcs, countof(js_error_funcs)); + + /* Used to squelch a -Wcast-function-type warning. */ + JSCFunctionType ft = { .generic_magic = js_error_constructor }; + for(i = 0; i < JS_NATIVE_ERROR_COUNT; i++) { + JSValue func_obj; + int n_args; + n_args = 1 + (i == JS_AGGREGATE_ERROR); + func_obj = JS_NewCFunction3(ctx, ft.generic, + native_error_name[i], n_args, + JS_CFUNC_constructor_or_func_magic, i, + ctx->error_ctor); + JS_NewGlobalCConstructor2(ctx, func_obj, native_error_name[i], + ctx->native_error_proto[i]); + } + + /* CallSite */ + _JS_AddIntrinsicCallSite(ctx); + + /* Iterator */ + ctx->class_proto[JS_CLASS_ITERATOR] = JS_NewObject(ctx); + JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_ITERATOR], + js_iterator_proto_funcs, + countof(js_iterator_proto_funcs)); + obj = JS_NewGlobalCConstructor(ctx, "Iterator", js_iterator_constructor, 0, + ctx->class_proto[JS_CLASS_ITERATOR]); + ctx->iterator_ctor = js_dup(obj); + JS_SetPropertyFunctionList(ctx, obj, + js_iterator_funcs, + countof(js_iterator_funcs)); + + ctx->class_proto[JS_CLASS_ITERATOR_HELPER] = JS_NewObjectProto(ctx, ctx->class_proto[JS_CLASS_ITERATOR]); + JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_ITERATOR_HELPER], + js_iterator_helper_proto_funcs, + countof(js_iterator_helper_proto_funcs)); + + ctx->class_proto[JS_CLASS_ITERATOR_WRAP] = JS_NewObjectProto(ctx, ctx->class_proto[JS_CLASS_ITERATOR]); + JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_ITERATOR_WRAP], + js_iterator_wrap_proto_funcs, + countof(js_iterator_wrap_proto_funcs)); + + /* Array */ + JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_ARRAY], + js_array_proto_funcs, + countof(js_array_proto_funcs)); + + obj = JS_NewGlobalCConstructor(ctx, "Array", js_array_constructor, 1, + ctx->class_proto[JS_CLASS_ARRAY]); + ctx->array_ctor = js_dup(obj); + JS_SetPropertyFunctionList(ctx, obj, js_array_funcs, + countof(js_array_funcs)); + + /* XXX: create auto_initializer */ + { + /* initialize Array.prototype[Symbol.unscopables] */ + static const char unscopables[] = + "copyWithin" "\0" + "entries" "\0" + "fill" "\0" + "find" "\0" + "findIndex" "\0" + "findLast" "\0" + "findLastIndex" "\0" + "flat" "\0" + "flatMap" "\0" + "includes" "\0" + "keys" "\0" + "toReversed" "\0" + "toSorted" "\0" + "toSpliced" "\0" + "values" "\0"; + const char *p = unscopables; + obj1 = JS_NewObjectProto(ctx, JS_NULL); + for(p = unscopables; *p; p += strlen(p) + 1) { + JS_DefinePropertyValueStr(ctx, obj1, p, JS_TRUE, JS_PROP_C_W_E); + } + JS_DefinePropertyValue(ctx, ctx->class_proto[JS_CLASS_ARRAY], + JS_ATOM_Symbol_unscopables, obj1, + JS_PROP_CONFIGURABLE); + } + + /* needed to initialize arguments[Symbol.iterator] */ + ctx->array_proto_values = + JS_GetProperty(ctx, ctx->class_proto[JS_CLASS_ARRAY], JS_ATOM_values); + + ctx->class_proto[JS_CLASS_ARRAY_ITERATOR] = JS_NewObjectProto(ctx, ctx->class_proto[JS_CLASS_ITERATOR]); + JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_ARRAY_ITERATOR], + js_array_iterator_proto_funcs, + countof(js_array_iterator_proto_funcs)); + + /* parseFloat and parseInteger must be defined before Number + because of the Number.parseFloat and Number.parseInteger + aliases */ + JS_SetPropertyFunctionList(ctx, ctx->global_obj, js_global_funcs, + countof(js_global_funcs)); + + /* Number */ + ctx->class_proto[JS_CLASS_NUMBER] = JS_NewObjectProtoClass(ctx, ctx->class_proto[JS_CLASS_OBJECT], + JS_CLASS_NUMBER); + JS_SetObjectData(ctx, ctx->class_proto[JS_CLASS_NUMBER], js_int32(0)); + JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_NUMBER], + js_number_proto_funcs, + countof(js_number_proto_funcs)); + number_obj = JS_NewGlobalCConstructor(ctx, "Number", js_number_constructor, 1, + ctx->class_proto[JS_CLASS_NUMBER]); + JS_SetPropertyFunctionList(ctx, number_obj, js_number_funcs, countof(js_number_funcs)); + + /* Boolean */ + ctx->class_proto[JS_CLASS_BOOLEAN] = JS_NewObjectProtoClass(ctx, ctx->class_proto[JS_CLASS_OBJECT], + JS_CLASS_BOOLEAN); + JS_SetObjectData(ctx, ctx->class_proto[JS_CLASS_BOOLEAN], js_bool(FALSE)); + JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_BOOLEAN], js_boolean_proto_funcs, + countof(js_boolean_proto_funcs)); + JS_NewGlobalCConstructor(ctx, "Boolean", js_boolean_constructor, 1, + ctx->class_proto[JS_CLASS_BOOLEAN]); + + /* String */ + ctx->class_proto[JS_CLASS_STRING] = JS_NewObjectProtoClass(ctx, ctx->class_proto[JS_CLASS_OBJECT], + JS_CLASS_STRING); + JS_SetObjectData(ctx, ctx->class_proto[JS_CLASS_STRING], JS_AtomToString(ctx, JS_ATOM_empty_string)); + obj = JS_NewGlobalCConstructor(ctx, "String", js_string_constructor, 1, + ctx->class_proto[JS_CLASS_STRING]); + JS_SetPropertyFunctionList(ctx, obj, js_string_funcs, + countof(js_string_funcs)); + JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_STRING], js_string_proto_funcs, + countof(js_string_proto_funcs)); + + ctx->class_proto[JS_CLASS_STRING_ITERATOR] = JS_NewObjectProto(ctx, ctx->class_proto[JS_CLASS_ITERATOR]); + JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_STRING_ITERATOR], + js_string_iterator_proto_funcs, + countof(js_string_iterator_proto_funcs)); + + /* Math: create as autoinit object */ + js_random_init(ctx); + JS_SetPropertyFunctionList(ctx, ctx->global_obj, js_math_obj, countof(js_math_obj)); + + /* ES6 Reflect: create as autoinit object */ + JS_SetPropertyFunctionList(ctx, ctx->global_obj, js_reflect_obj, countof(js_reflect_obj)); + + /* ES6 Symbol */ + ctx->class_proto[JS_CLASS_SYMBOL] = JS_NewObject(ctx); + JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_SYMBOL], js_symbol_proto_funcs, + countof(js_symbol_proto_funcs)); + obj = JS_NewGlobalCConstructor(ctx, "Symbol", js_symbol_constructor, 0, + ctx->class_proto[JS_CLASS_SYMBOL]); + JS_SetPropertyFunctionList(ctx, obj, js_symbol_funcs, + countof(js_symbol_funcs)); + for(i = JS_ATOM_Symbol_toPrimitive; i < JS_ATOM_END; i++) { + char buf[ATOM_GET_STR_BUF_SIZE]; + const char *str, *p; + str = JS_AtomGetStr(ctx, buf, sizeof(buf), i); + /* skip "Symbol." */ + p = strchr(str, '.'); + if (p) + str = p + 1; + JS_DefinePropertyValueStr(ctx, obj, str, JS_AtomToValue(ctx, i), 0); + } + + /* ES6 Generator */ + ctx->class_proto[JS_CLASS_GENERATOR] = JS_NewObjectProto(ctx, ctx->class_proto[JS_CLASS_ITERATOR]); + JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_GENERATOR], + js_generator_proto_funcs, + countof(js_generator_proto_funcs)); + + ctx->class_proto[JS_CLASS_GENERATOR_FUNCTION] = JS_NewObjectProto(ctx, ctx->function_proto); + obj1 = JS_NewCFunctionMagic(ctx, js_function_constructor, + "GeneratorFunction", 1, + JS_CFUNC_constructor_or_func_magic, JS_FUNC_GENERATOR); + JS_SetPropertyFunctionList(ctx, + ctx->class_proto[JS_CLASS_GENERATOR_FUNCTION], + js_generator_function_proto_funcs, + countof(js_generator_function_proto_funcs)); + JS_SetConstructor2(ctx, ctx->class_proto[JS_CLASS_GENERATOR_FUNCTION], + ctx->class_proto[JS_CLASS_GENERATOR], + JS_PROP_CONFIGURABLE, JS_PROP_CONFIGURABLE); + JS_SetConstructor2(ctx, obj1, ctx->class_proto[JS_CLASS_GENERATOR_FUNCTION], + 0, JS_PROP_CONFIGURABLE); + JS_FreeValue(ctx, obj1); + + /* global properties */ + ctx->eval_obj = JS_NewCFunction(ctx, js_global_eval, "eval", 1); + JS_DefinePropertyValue(ctx, ctx->global_obj, JS_ATOM_eval, + js_dup(ctx->eval_obj), + JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE); + + JS_DefinePropertyValue(ctx, ctx->global_obj, JS_ATOM_globalThis, + js_dup(ctx->global_obj), + JS_PROP_CONFIGURABLE | JS_PROP_WRITABLE); +} + +/* Typed Arrays */ + +static uint8_t const typed_array_size_log2[JS_TYPED_ARRAY_COUNT] = { + 0, 0, 0, 1, 1, 2, 2, + 3, 3, // BigInt64Array, BigUint64Array + 1, 2, 3 // Float16Array, Float32Array, Float64Array +}; + +static JSValue js_array_buffer_constructor3(JSContext *ctx, + JSValue new_target, + uint64_t len, uint64_t *max_len, + JSClassID class_id, + uint8_t *buf, + JSFreeArrayBufferDataFunc *free_func, + void *opaque, BOOL alloc_flag) +{ + JSRuntime *rt = ctx->rt; + JSValue obj; + JSArrayBuffer *abuf = NULL; + uint64_t sab_alloc_len; + + if (!alloc_flag && buf && max_len && free_func != js_array_buffer_free) { + // not observable from JS land, only through C API misuse; + // JS code cannot create externally managed buffers directly + return JS_ThrowInternalError(ctx, + "resizable ArrayBuffers not supported " + "for externally managed buffers"); + } + obj = js_create_from_ctor(ctx, new_target, class_id); + if (JS_IsException(obj)) + return obj; + /* XXX: we are currently limited to 2 GB */ + if (len > INT32_MAX) { + JS_ThrowRangeError(ctx, "invalid array buffer length"); + goto fail; + } + if (max_len && *max_len > INT32_MAX) { + JS_ThrowRangeError(ctx, "invalid max array buffer length"); + goto fail; + } + abuf = js_malloc(ctx, sizeof(*abuf)); + if (!abuf) + goto fail; + abuf->byte_length = len; + abuf->max_byte_length = max_len ? *max_len : -1; + if (alloc_flag) { + if (class_id == JS_CLASS_SHARED_ARRAY_BUFFER && + rt->sab_funcs.sab_alloc) { + // TOOD(bnoordhuis) resizing backing memory for SABs atomically + // is hard so we cheat and allocate |maxByteLength| bytes upfront + sab_alloc_len = max_len ? *max_len : len; + abuf->data = rt->sab_funcs.sab_alloc(rt->sab_funcs.sab_opaque, + max_int(sab_alloc_len, 1)); + if (!abuf->data) + goto fail; + memset(abuf->data, 0, sab_alloc_len); + } else { + /* the allocation must be done after the object creation */ + abuf->data = js_mallocz(ctx, max_int(len, 1)); + if (!abuf->data) + goto fail; + } + } else { + if (class_id == JS_CLASS_SHARED_ARRAY_BUFFER && + rt->sab_funcs.sab_dup) { + rt->sab_funcs.sab_dup(rt->sab_funcs.sab_opaque, buf); + } + abuf->data = buf; + } + init_list_head(&abuf->array_list); + abuf->detached = FALSE; + abuf->shared = (class_id == JS_CLASS_SHARED_ARRAY_BUFFER); + abuf->opaque = opaque; + abuf->free_func = free_func; + if (alloc_flag && buf) + memcpy(abuf->data, buf, len); + JS_SetOpaqueInternal(obj, abuf); + return obj; + fail: + JS_FreeValue(ctx, obj); + js_free(ctx, abuf); + return JS_EXCEPTION; +} + +static void js_array_buffer_free(JSRuntime *rt, void *opaque, void *ptr) +{ + js_free_rt(rt, ptr); +} + +static JSValue js_array_buffer_constructor2(JSContext *ctx, + JSValue new_target, + uint64_t len, uint64_t *max_len, + JSClassID class_id) +{ + return js_array_buffer_constructor3(ctx, new_target, len, max_len, + class_id, NULL, js_array_buffer_free, + NULL, TRUE); +} + +static JSValue js_array_buffer_constructor1(JSContext *ctx, + JSValue new_target, + uint64_t len, uint64_t *max_len) +{ + return js_array_buffer_constructor2(ctx, new_target, len, max_len, + JS_CLASS_ARRAY_BUFFER); +} + +JSValue JS_NewArrayBuffer(JSContext *ctx, uint8_t *buf, size_t len, + JSFreeArrayBufferDataFunc *free_func, void *opaque, + BOOL is_shared) +{ + JSClassID class_id = + is_shared ? JS_CLASS_SHARED_ARRAY_BUFFER : JS_CLASS_ARRAY_BUFFER; + return js_array_buffer_constructor3(ctx, JS_UNDEFINED, len, NULL, class_id, + buf, free_func, opaque, FALSE); +} + +JS_BOOL JS_IsArrayBuffer(JSValue obj) { + return JS_GetClassID(obj) == JS_CLASS_ARRAY_BUFFER; +} + +/* create a new ArrayBuffer of length 'len' and copy 'buf' to it */ +JSValue JS_NewArrayBufferCopy(JSContext *ctx, const uint8_t *buf, size_t len) +{ + return js_array_buffer_constructor3(ctx, JS_UNDEFINED, len, NULL, + JS_CLASS_ARRAY_BUFFER, + (uint8_t *)buf, + js_array_buffer_free, NULL, + TRUE); +} + +static JSValue js_array_buffer_constructor0(JSContext *ctx, JSValue new_target, + int argc, JSValue *argv, + JSClassID class_id) +{ + uint64_t len, max_len, *pmax_len = NULL; + JSValue obj, val; + int64_t i; + + if (JS_ToIndex(ctx, &len, argv[0])) + return JS_EXCEPTION; + if (argc < 2) + goto next; + if (!JS_IsObject(argv[1])) + goto next; + obj = JS_ToObject(ctx, argv[1]); + if (JS_IsException(obj)) + return JS_EXCEPTION; + val = JS_GetProperty(ctx, obj, JS_ATOM_maxByteLength); + JS_FreeValue(ctx, obj); + if (JS_IsException(val)) + return JS_EXCEPTION; + if (JS_IsUndefined(val)) + goto next; + if (JS_ToInt64Free(ctx, &i, val)) + return JS_EXCEPTION; + // don't have to check i < 0 because len >= 0 + if (len > i || i > MAX_SAFE_INTEGER) + return JS_ThrowRangeError(ctx, "invalid array buffer max length"); + max_len = i; + pmax_len = &max_len; +next: + return js_array_buffer_constructor2(ctx, new_target, len, pmax_len, + class_id); +} + +static JSValue js_array_buffer_constructor(JSContext *ctx, JSValue new_target, + int argc, JSValue *argv) +{ + return js_array_buffer_constructor0(ctx, new_target, argc, argv, + JS_CLASS_ARRAY_BUFFER); +} + +static JSValue js_shared_array_buffer_constructor(JSContext *ctx, + JSValue new_target, + int argc, JSValue *argv) +{ + return js_array_buffer_constructor0(ctx, new_target, argc, argv, + JS_CLASS_SHARED_ARRAY_BUFFER); +} + +/* also used for SharedArrayBuffer */ +static void js_array_buffer_finalizer(JSRuntime *rt, JSValue val) +{ + JSObject *p = JS_VALUE_GET_OBJ(val); + JSArrayBuffer *abuf = p->u.array_buffer; + struct list_head *el, *el1; + + if (abuf) { + /* The ArrayBuffer finalizer may be called before the typed + array finalizers using it, so abuf->array_list is not + necessarily empty. */ + list_for_each_safe(el, el1, &abuf->array_list) { + JSTypedArray *ta; + JSObject *p1; + + ta = list_entry(el, JSTypedArray, link); + ta->link.prev = NULL; + ta->link.next = NULL; + p1 = ta->obj; + /* Note: the typed array length and offset fields are not modified */ + if (p1->class_id != JS_CLASS_DATAVIEW) { + p1->u.array.count = 0; + p1->u.array.u.ptr = NULL; + } + } + if (abuf->shared && rt->sab_funcs.sab_free) { + rt->sab_funcs.sab_free(rt->sab_funcs.sab_opaque, abuf->data); + } else { + if (abuf->free_func) + abuf->free_func(rt, abuf->opaque, abuf->data); + } + js_free_rt(rt, abuf); + } +} + +static JSValue js_array_buffer_isView(JSContext *ctx, + JSValue this_val, + int argc, JSValue *argv) +{ + JSObject *p; + BOOL res; + res = FALSE; + if (JS_VALUE_GET_TAG(argv[0]) == JS_TAG_OBJECT) { + p = JS_VALUE_GET_OBJ(argv[0]); + if (p->class_id >= JS_CLASS_UINT8C_ARRAY && + p->class_id <= JS_CLASS_DATAVIEW) { + res = TRUE; + } + } + return js_bool(res); +} + +static const JSCFunctionListEntry js_array_buffer_funcs[] = { + JS_CFUNC_DEF("isView", 1, js_array_buffer_isView ), + JS_CGETSET_DEF("[Symbol.species]", js_get_this, NULL ), +}; + +static JSValue JS_ThrowTypeErrorDetachedArrayBuffer(JSContext *ctx) +{ + return JS_ThrowTypeError(ctx, "ArrayBuffer is detached"); +} + +static JSValue JS_ThrowTypeErrorArrayBufferOOB(JSContext *ctx) +{ + return JS_ThrowTypeError(ctx, "ArrayBuffer is detached or resized"); +} + +// #sec-get-arraybuffer.prototype.detached +static JSValue js_array_buffer_get_detached(JSContext *ctx, + JSValue this_val) +{ + JSArrayBuffer *abuf = JS_GetOpaque2(ctx, this_val, JS_CLASS_ARRAY_BUFFER); + if (!abuf) + return JS_EXCEPTION; + if (abuf->shared) + return JS_ThrowTypeError(ctx, "detached called on SharedArrayBuffer"); + return js_bool(abuf->detached); +} + +static JSValue js_array_buffer_get_byteLength(JSContext *ctx, + JSValue this_val, + int class_id) +{ + JSArrayBuffer *abuf = JS_GetOpaque2(ctx, this_val, class_id); + if (!abuf) + return JS_EXCEPTION; + /* return 0 if detached */ + return js_uint32(abuf->byte_length); +} + +static JSValue js_array_buffer_get_maxByteLength(JSContext *ctx, + JSValue this_val, + int class_id) +{ + JSArrayBuffer *abuf = JS_GetOpaque2(ctx, this_val, class_id); + if (!abuf) + return JS_EXCEPTION; + if (array_buffer_is_resizable(abuf)) + return js_uint32(abuf->max_byte_length); + return js_uint32(abuf->byte_length); +} + +static JSValue js_array_buffer_get_resizable(JSContext *ctx, JSValue this_val, + int class_id) +{ + JSArrayBuffer *abuf = JS_GetOpaque2(ctx, this_val, class_id); + if (!abuf) + return JS_EXCEPTION; + return js_bool(array_buffer_is_resizable(abuf)); +} + +void JS_DetachArrayBuffer(JSContext *ctx, JSValue obj) +{ + JSArrayBuffer *abuf = JS_GetOpaque(obj, JS_CLASS_ARRAY_BUFFER); + struct list_head *el; + + if (!abuf || abuf->detached) + return; + if (abuf->free_func) + abuf->free_func(ctx->rt, abuf->opaque, abuf->data); + abuf->data = NULL; + abuf->byte_length = 0; + abuf->detached = TRUE; + + list_for_each(el, &abuf->array_list) { + JSTypedArray *ta; + JSObject *p; + + ta = list_entry(el, JSTypedArray, link); + p = ta->obj; + /* Note: the typed array length and offset fields are not modified */ + if (p->class_id != JS_CLASS_DATAVIEW) { + p->u.array.count = 0; + p->u.array.u.ptr = NULL; + } + } +} + +/* get an ArrayBuffer or SharedArrayBuffer */ +static JSArrayBuffer *js_get_array_buffer(JSContext *ctx, JSValue obj) +{ + JSObject *p; + if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT) + goto fail; + p = JS_VALUE_GET_OBJ(obj); + if (p->class_id != JS_CLASS_ARRAY_BUFFER && + p->class_id != JS_CLASS_SHARED_ARRAY_BUFFER) { + fail: + JS_ThrowTypeErrorInvalidClass(ctx, JS_CLASS_ARRAY_BUFFER); + return NULL; + } + return p->u.array_buffer; +} + +/* return NULL if exception. WARNING: any JS call can detach the + buffer and render the returned pointer invalid */ +uint8_t *JS_GetArrayBuffer(JSContext *ctx, size_t *psize, JSValue obj) +{ + JSArrayBuffer *abuf = js_get_array_buffer(ctx, obj); + if (!abuf) + goto fail; + if (abuf->detached) { + JS_ThrowTypeErrorDetachedArrayBuffer(ctx); + goto fail; + } + *psize = abuf->byte_length; + return abuf->data; + fail: + *psize = 0; + return NULL; +} + +static BOOL array_buffer_is_resizable(const JSArrayBuffer *abuf) +{ + return abuf->max_byte_length >= 0; +} + +// ES #sec-arraybuffer.prototype.transfer +static JSValue js_array_buffer_transfer(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv, int magic) +{ + BOOL transfer_to_fixed_length = magic & 1; + JSArrayBuffer *abuf; + uint64_t new_len, old_len, max_len, *pmax_len; + uint8_t *bs, *new_bs; + + abuf = JS_GetOpaque2(ctx, this_val, JS_CLASS_ARRAY_BUFFER); + if (!abuf) + return JS_EXCEPTION; + if (abuf->shared) + return JS_ThrowTypeError(ctx, "cannot transfer a SharedArrayBuffer"); + if (argc < 1 || JS_IsUndefined(argv[0])) + new_len = abuf->byte_length; + else if (JS_ToIndex(ctx, &new_len, argv[0])) + return JS_EXCEPTION; + if (abuf->detached) + return JS_ThrowTypeErrorDetachedArrayBuffer(ctx); + pmax_len = NULL; + if (!transfer_to_fixed_length) { + if (array_buffer_is_resizable(abuf)) { // carry over maxByteLength + max_len = abuf->max_byte_length; + if (new_len > max_len) + return JS_ThrowTypeError(ctx, "invalid array buffer length"); + // TODO(bnoordhuis) support externally managed RABs + if (abuf->free_func == js_array_buffer_free) + pmax_len = &max_len; + } + } + /* create an empty AB */ + if (new_len == 0) { + JS_DetachArrayBuffer(ctx, this_val); + return js_array_buffer_constructor2(ctx, JS_UNDEFINED, 0, pmax_len, + JS_CLASS_ARRAY_BUFFER); + } + bs = abuf->data; + old_len = abuf->byte_length; + /* if length mismatch, realloc. Otherwise, use the same backing buffer. */ + if (new_len != old_len) { + new_bs = js_realloc(ctx, bs, new_len); + if (!new_bs) + return JS_EXCEPTION; + bs = new_bs; + if (new_len > old_len) + memset(bs + old_len, 0, new_len - old_len); + } + /* neuter the backing buffer */ + abuf->data = NULL; + abuf->byte_length = 0; + abuf->detached = TRUE; + return js_array_buffer_constructor3(ctx, JS_UNDEFINED, new_len, pmax_len, + JS_CLASS_ARRAY_BUFFER, + bs, abuf->free_func, + NULL, FALSE); +} + +static JSValue js_array_buffer_resize(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv, int class_id) +{ + uint32_t size_log2, size_elem; + struct list_head *el; + JSArrayBuffer *abuf; + JSTypedArray *ta; + JSObject *p; + uint8_t *data; + int64_t len; + + abuf = JS_GetOpaque2(ctx, this_val, class_id); + if (!abuf) + return JS_EXCEPTION; + if (JS_ToInt64(ctx, &len, argv[0])) + return JS_EXCEPTION; + if (abuf->detached) + return JS_ThrowTypeErrorDetachedArrayBuffer(ctx); + if (!array_buffer_is_resizable(abuf)) + return JS_ThrowTypeError(ctx, "array buffer is not resizable"); + // TODO(bnoordhuis) support externally managed RABs + if (abuf->free_func != js_array_buffer_free) + return JS_ThrowTypeError(ctx, "external array buffer is not resizable"); + if (len < 0 || len > abuf->max_byte_length) { + bad_length: + return JS_ThrowRangeError(ctx, "invalid array buffer length"); + } + // SABs can only grow and we don't need to realloc because + // js_array_buffer_constructor3 commits all memory upfront; + // regular RABs are resizable both ways and realloc + if (abuf->shared) { + if (len < abuf->byte_length) + goto bad_length; + // Note this is off-spec; there's supposed to be a single atomic + // |byteLength| property that's shared across SABs but we store + // it per SAB instead. That means when thread A calls sab.grow(2) + // at time t0, and thread B calls sab.grow(1) at time t1, we don't + // throw a TypeError in thread B as the spec says we should, + // instead both threads get their own view of the backing memory, + // 2 bytes big in A, and 1 byte big in B + abuf->byte_length = len; + } else { + data = js_realloc(ctx, abuf->data, max_int(len, 1)); + if (!data) + return JS_EXCEPTION; + if (len > abuf->byte_length) + memset(&data[abuf->byte_length], 0, len - abuf->byte_length); + abuf->byte_length = len; + abuf->data = data; + } + data = abuf->data; + // update lengths of all typed arrays backed by this array buffer + list_for_each(el, &abuf->array_list) { + ta = list_entry(el, JSTypedArray, link); + p = ta->obj; + if (p->class_id == JS_CLASS_DATAVIEW) + continue; + p->u.array.count = 0; + p->u.array.u.ptr = NULL; + size_log2 = typed_array_size_log2(p->class_id); + size_elem = 1 << size_log2; + if (ta->track_rab) { + if (len >= (int64_t)ta->offset + size_elem) { + p->u.array.count = (len - ta->offset) >> size_log2; + p->u.array.u.ptr = &data[ta->offset]; + } + } else { + if (len >= (int64_t)ta->offset + ta->length) { + p->u.array.count = ta->length >> size_log2; + p->u.array.u.ptr = &data[ta->offset]; + } + } + } + return JS_UNDEFINED; +} + +static JSValue js_array_buffer_slice(JSContext *ctx, + JSValue this_val, + int argc, JSValue *argv, int class_id) +{ + JSArrayBuffer *abuf, *new_abuf; + int64_t len, start, end, new_len; + JSValue ctor, new_obj; + + abuf = JS_GetOpaque2(ctx, this_val, class_id); + if (!abuf) + return JS_EXCEPTION; + if (abuf->detached) + return JS_ThrowTypeErrorDetachedArrayBuffer(ctx); + len = abuf->byte_length; + + if (JS_ToInt64Clamp(ctx, &start, argv[0], 0, len, len)) + return JS_EXCEPTION; + + end = len; + if (!JS_IsUndefined(argv[1])) { + if (JS_ToInt64Clamp(ctx, &end, argv[1], 0, len, len)) + return JS_EXCEPTION; + } + new_len = max_int64(end - start, 0); + ctor = JS_SpeciesConstructor(ctx, this_val, JS_UNDEFINED); + if (JS_IsException(ctor)) + return ctor; + if (JS_IsUndefined(ctor)) { + new_obj = js_array_buffer_constructor2(ctx, JS_UNDEFINED, new_len, + NULL, class_id); + } else { + JSValue args[1]; + args[0] = js_int64(new_len); + new_obj = JS_CallConstructor(ctx, ctor, 1, args); + JS_FreeValue(ctx, ctor); + JS_FreeValue(ctx, args[0]); + } + if (JS_IsException(new_obj)) + return new_obj; + new_abuf = JS_GetOpaque2(ctx, new_obj, class_id); + if (!new_abuf) + goto fail; + if (js_same_value(ctx, new_obj, this_val)) { + JS_ThrowTypeError(ctx, "cannot use identical ArrayBuffer"); + goto fail; + } + if (new_abuf->detached) { + JS_ThrowTypeErrorDetachedArrayBuffer(ctx); + goto fail; + } + if (new_abuf->byte_length < new_len) { + JS_ThrowTypeError(ctx, "new ArrayBuffer is too small"); + goto fail; + } + /* must test again because of side effects */ + if (abuf->detached) { + JS_ThrowTypeErrorDetachedArrayBuffer(ctx); + goto fail; + } + memcpy(new_abuf->data, abuf->data + start, new_len); + return new_obj; + fail: + JS_FreeValue(ctx, new_obj); + return JS_EXCEPTION; +} + +static const JSCFunctionListEntry js_array_buffer_proto_funcs[] = { + JS_CGETSET_MAGIC_DEF("byteLength", js_array_buffer_get_byteLength, NULL, JS_CLASS_ARRAY_BUFFER ), + JS_CGETSET_MAGIC_DEF("maxByteLength", js_array_buffer_get_maxByteLength, NULL, JS_CLASS_ARRAY_BUFFER ), + JS_CGETSET_MAGIC_DEF("resizable", js_array_buffer_get_resizable, NULL, JS_CLASS_ARRAY_BUFFER ), + JS_CGETSET_DEF("detached", js_array_buffer_get_detached, NULL ), + JS_CFUNC_MAGIC_DEF("resize", 1, js_array_buffer_resize, JS_CLASS_ARRAY_BUFFER ), + JS_CFUNC_MAGIC_DEF("slice", 2, js_array_buffer_slice, JS_CLASS_ARRAY_BUFFER ), + JS_CFUNC_MAGIC_DEF("transfer", 0, js_array_buffer_transfer, 0 ), + JS_CFUNC_MAGIC_DEF("transferToFixedLength", 0, js_array_buffer_transfer, 1 ), + JS_PROP_STRING_DEF("[Symbol.toStringTag]", "ArrayBuffer", JS_PROP_CONFIGURABLE ), +}; + +/* SharedArrayBuffer */ + +static const JSCFunctionListEntry js_shared_array_buffer_funcs[] = { + JS_CGETSET_DEF("[Symbol.species]", js_get_this, NULL ), +}; + +static const JSCFunctionListEntry js_shared_array_buffer_proto_funcs[] = { + JS_CGETSET_MAGIC_DEF("byteLength", js_array_buffer_get_byteLength, NULL, JS_CLASS_SHARED_ARRAY_BUFFER ), + JS_CGETSET_MAGIC_DEF("maxByteLength", js_array_buffer_get_maxByteLength, NULL, JS_CLASS_SHARED_ARRAY_BUFFER ), + JS_CGETSET_MAGIC_DEF("growable", js_array_buffer_get_resizable, NULL, JS_CLASS_SHARED_ARRAY_BUFFER ), + JS_CFUNC_MAGIC_DEF("grow", 1, js_array_buffer_resize, JS_CLASS_SHARED_ARRAY_BUFFER ), + JS_CFUNC_MAGIC_DEF("slice", 2, js_array_buffer_slice, JS_CLASS_SHARED_ARRAY_BUFFER ), + JS_PROP_STRING_DEF("[Symbol.toStringTag]", "SharedArrayBuffer", JS_PROP_CONFIGURABLE ), +}; + +// is the typed array detached or out of bounds relative to its RAB? +// |p| must be a typed array, *not* a DataView +static BOOL typed_array_is_oob(JSObject *p) +{ + JSArrayBuffer *abuf; + JSTypedArray *ta; + int len, size_elem; + int64_t end; + + assert(p->class_id >= JS_CLASS_UINT8C_ARRAY); + assert(p->class_id <= JS_CLASS_FLOAT64_ARRAY); + + ta = p->u.typed_array; + abuf = ta->buffer->u.array_buffer; + if (abuf->detached) + return TRUE; + len = abuf->byte_length; + if (ta->offset > len) + return TRUE; + if (ta->track_rab) + return FALSE; + if (len < (int64_t)ta->offset + ta->length) + return TRUE; + size_elem = 1 << typed_array_size_log2(p->class_id); + end = (int64_t)ta->offset + (int64_t)p->u.array.count * size_elem; + return end > len; +} + +/* WARNING: 'p' must be a typed array. Works even if the array buffer + is detached */ +static uint32_t typed_array_get_length(JSContext *ctx, JSObject *p) +{ + JSTypedArray *ta = p->u.typed_array; + int size_log2 = typed_array_size_log2(p->class_id); + return ta->length >> size_log2; +} + +static int validate_typed_array(JSContext *ctx, JSValue this_val) +{ + JSObject *p; + p = get_typed_array(ctx, this_val); + if (!p) + return -1; + if (typed_array_is_oob(p)) { + JS_ThrowTypeErrorArrayBufferOOB(ctx); + return -1; + } + return 0; +} + +static JSValue js_typed_array_get_length(JSContext *ctx, JSValue this_val) +{ + JSObject *p; + p = get_typed_array(ctx, this_val); + if (!p) + return JS_EXCEPTION; + return js_int32(p->u.array.count); +} + +static JSValue js_typed_array_get_buffer(JSContext *ctx, JSValue this_val) +{ + JSObject *p; + JSTypedArray *ta; + p = get_typed_array(ctx, this_val); + if (!p) + return JS_EXCEPTION; + ta = p->u.typed_array; + return js_dup(JS_MKPTR(JS_TAG_OBJECT, ta->buffer)); +} + +static JSValue js_typed_array_get_byteLength(JSContext *ctx, JSValue this_val) +{ + uint32_t size_log2; + JSTypedArray *ta; + JSObject *p; + + p = get_typed_array(ctx, this_val); + if (!p) + return JS_EXCEPTION; + if (typed_array_is_oob(p)) + return js_int32(0); + ta = p->u.typed_array; + if (!ta->track_rab) + return js_uint32(ta->length); + size_log2 = typed_array_size_log2(p->class_id); + return js_int64((int64_t)p->u.array.count << size_log2); +} + +static JSValue js_typed_array_get_byteOffset(JSContext *ctx, JSValue this_val) +{ + JSObject *p; + JSTypedArray *ta; + p = get_typed_array(ctx, this_val); + if (!p) + return JS_EXCEPTION; + if (typed_array_is_oob(p)) + return js_int32(0); + ta = p->u.typed_array; + return js_uint32(ta->offset); +} + +/* Return the buffer associated to the typed array or an exception if + it is not a typed array or if the buffer is detached. pbyte_offset, + pbyte_length or pbytes_per_element can be NULL. */ +JSValue JS_GetTypedArrayBuffer(JSContext *ctx, JSValue obj, + size_t *pbyte_offset, + size_t *pbyte_length, + size_t *pbytes_per_element) +{ + JSObject *p; + JSTypedArray *ta; + p = get_typed_array(ctx, obj); + if (!p) + return JS_EXCEPTION; + if (typed_array_is_oob(p)) + return JS_ThrowTypeErrorArrayBufferOOB(ctx); + ta = p->u.typed_array; + if (pbyte_offset) + *pbyte_offset = ta->offset; + if (pbyte_length) + *pbyte_length = ta->length; + if (pbytes_per_element) { + *pbytes_per_element = 1 << typed_array_size_log2(p->class_id); + } + return js_dup(JS_MKPTR(JS_TAG_OBJECT, ta->buffer)); +} + +/* return NULL if exception. WARNING: any JS call can detach the + buffer and render the returned pointer invalid */ +uint8_t *JS_GetUint8Array(JSContext *ctx, size_t *psize, JSValue obj) +{ + JSObject *p; + JSTypedArray *ta; + JSArrayBuffer *abuf; + p = get_typed_array(ctx, obj); + if (!p) + goto fail; + if (typed_array_is_oob(p)) { + JS_ThrowTypeErrorArrayBufferOOB(ctx); + goto fail; + } + if (p->class_id != JS_CLASS_UINT8_ARRAY && p->class_id != JS_CLASS_UINT8C_ARRAY) { + JS_ThrowTypeError(ctx, "not a Uint8Array"); + goto fail; + } + ta = p->u.typed_array; + abuf = ta->buffer->u.array_buffer; + + *psize = ta->length; + return abuf->data + ta->offset; + fail: + *psize = 0; + return NULL; +} + +static JSValue js_typed_array_get_toStringTag(JSContext *ctx, + JSValue this_val) +{ + JSObject *p; + if (JS_VALUE_GET_TAG(this_val) != JS_TAG_OBJECT) + return JS_UNDEFINED; + p = JS_VALUE_GET_OBJ(this_val); + if (!(p->class_id >= JS_CLASS_UINT8C_ARRAY && + p->class_id <= JS_CLASS_FLOAT64_ARRAY)) + return JS_UNDEFINED; + return JS_AtomToString(ctx, ctx->rt->class_array[p->class_id].class_name); +} + +static JSValue js_typed_array_set_internal(JSContext *ctx, + JSValue dst, + JSValue src, + JSValue off) +{ + JSObject *p; + JSObject *src_p; + uint32_t i; + int64_t dst_len, src_len, offset; + JSValue val, src_obj = JS_UNDEFINED; + + p = get_typed_array(ctx, dst); + if (!p) + goto fail; + if (JS_ToInt64Sat(ctx, &offset, off)) + goto fail; + if (offset < 0) + goto range_error; + if (typed_array_is_oob(p)) { + detached: + JS_ThrowTypeErrorArrayBufferOOB(ctx); + goto fail; + } + dst_len = p->u.array.count; + src_obj = JS_ToObject(ctx, src); + if (JS_IsException(src_obj)) + goto fail; + src_p = JS_VALUE_GET_OBJ(src_obj); + if (src_p->class_id >= JS_CLASS_UINT8C_ARRAY && + src_p->class_id <= JS_CLASS_FLOAT64_ARRAY) { + JSTypedArray *dest_ta = p->u.typed_array; + JSArrayBuffer *dest_abuf = dest_ta->buffer->u.array_buffer; + JSTypedArray *src_ta = src_p->u.typed_array; + JSArrayBuffer *src_abuf = src_ta->buffer->u.array_buffer; + int shift = typed_array_size_log2(p->class_id); + + if (typed_array_is_oob(src_p)) + goto detached; + + src_len = src_p->u.array.count; + if (offset > dst_len - src_len) + goto range_error; + + /* copying between typed objects */ + if (src_p->class_id == p->class_id) { + /* same type, use memmove */ + memmove(dest_abuf->data + dest_ta->offset + (offset << shift), + src_abuf->data + src_ta->offset, src_len << shift); + goto done; + } + if (dest_abuf->data == src_abuf->data) { + /* copying between the same buffer using different types of mappings + would require a temporary buffer */ + } + /* otherwise, default behavior is slow but correct */ + } else { + // can change |dst| as a side effect; per spec, + // perform the range check against its old length + if (js_get_length64(ctx, &src_len, src_obj)) + goto fail; + if (offset > dst_len - src_len) { + range_error: + JS_ThrowRangeError(ctx, "invalid array length"); + goto fail; + } + } + for(i = 0; i < src_len; i++) { + val = JS_GetPropertyUint32(ctx, src_obj, i); + if (JS_IsException(val)) + goto fail; + // Per spec: detaching the TA mid-iteration is allowed and should + // not throw an exception. Because iteration over the source array is + // observable, we cannot bail out early when the TA is first detached. + if (typed_array_is_oob(p)) { + JS_FreeValue(ctx, val); + } else if (JS_SetPropertyUint32(ctx, dst, offset + i, val) < 0) { + goto fail; + } + } +done: + JS_FreeValue(ctx, src_obj); + return JS_UNDEFINED; +fail: + JS_FreeValue(ctx, src_obj); + return JS_EXCEPTION; +} + +static JSValue js_typed_array_at(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + JSObject *p; + int64_t idx, len; + + p = get_typed_array(ctx, this_val); + if (!p) + return JS_EXCEPTION; + if (typed_array_is_oob(p)) + return JS_ThrowTypeErrorArrayBufferOOB(ctx); + len = p->u.array.count; + + // note: can change p->u.array.count + if (JS_ToInt64Sat(ctx, &idx, argv[0])) + return JS_EXCEPTION; + + if (idx < 0) + idx = len + idx; + + if (idx < 0 || idx >= p->u.array.count) + return JS_UNDEFINED; + + switch (p->class_id) { + case JS_CLASS_INT8_ARRAY: + return js_int32(p->u.array.u.int8_ptr[idx]); + case JS_CLASS_UINT8C_ARRAY: + case JS_CLASS_UINT8_ARRAY: + return js_int32(p->u.array.u.uint8_ptr[idx]); + case JS_CLASS_INT16_ARRAY: + return js_int32(p->u.array.u.int16_ptr[idx]); + case JS_CLASS_UINT16_ARRAY: + return js_int32(p->u.array.u.uint16_ptr[idx]); + case JS_CLASS_INT32_ARRAY: + return js_int32(p->u.array.u.int32_ptr[idx]); + case JS_CLASS_UINT32_ARRAY: + return js_uint32(p->u.array.u.uint32_ptr[idx]); + case JS_CLASS_FLOAT16_ARRAY: + return js_float64(fromfp16(p->u.array.u.fp16_ptr[idx])); + case JS_CLASS_FLOAT32_ARRAY: + return js_float64(p->u.array.u.float_ptr[idx]); + case JS_CLASS_FLOAT64_ARRAY: + return js_float64(p->u.array.u.double_ptr[idx]); + case JS_CLASS_BIG_INT64_ARRAY: + return JS_NewBigInt64(ctx, p->u.array.u.int64_ptr[idx]); + case JS_CLASS_BIG_UINT64_ARRAY: + return JS_NewBigUint64(ctx, p->u.array.u.uint64_ptr[idx]); + } + + abort(); /* unreachable */ + return JS_UNDEFINED; +} + +static JSValue js_typed_array_with(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + JSValue arr, val; + JSObject *p; + int64_t idx; + uint32_t len, oldlen, newlen; + + p = get_typed_array(ctx, this_val); + if (!p) + return JS_EXCEPTION; + + oldlen = p->u.array.count; + if (JS_ToInt64Sat(ctx, &idx, argv[0])) + return JS_EXCEPTION; + + val = JS_ToPrimitive(ctx, argv[1], HINT_NUMBER); + if (JS_IsException(val)) + return JS_EXCEPTION; + + newlen = p->u.array.count; + if (idx < 0) + idx = newlen + idx; + if (idx < 0 || idx >= newlen) { + JS_FreeValue(ctx, val); + return JS_ThrowRangeError(ctx, "invalid array index"); + } + + len = min_uint32(oldlen, newlen); + arr = js_typed_array_constructor_ta(ctx, JS_UNDEFINED, this_val, + p->class_id, len); + if (JS_IsException(arr)) { + JS_FreeValue(ctx, val); + return JS_EXCEPTION; + } + if (idx < len && JS_SetPropertyInt64(ctx, arr, idx, val) < 0) { + JS_FreeValue(ctx, arr); + return JS_EXCEPTION; + } + return arr; +} + +static JSValue js_typed_array_set(JSContext *ctx, + JSValue this_val, + int argc, JSValue *argv) +{ + JSValue offset = JS_UNDEFINED; + if (argc > 1) { + offset = argv[1]; + } + return js_typed_array_set_internal(ctx, this_val, argv[0], offset); +} + +static JSValue js_create_typed_array_iterator(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv, int magic) +{ + if (validate_typed_array(ctx, this_val)) + return JS_EXCEPTION; + return js_create_array_iterator(ctx, this_val, argc, argv, magic); +} + +static JSValue js_typed_array_create(JSContext *ctx, JSValue ctor, + int argc, JSValue *argv) +{ + JSValue ret; + int new_len; + int64_t len; + + ret = JS_CallConstructor(ctx, ctor, argc, argv); + if (JS_IsException(ret)) + return ret; + /* validate the typed array */ + new_len = js_typed_array_get_length_unsafe(ctx, ret); + if (new_len < 0) + goto fail; + if (argc == 1) { + /* ensure that it is large enough */ + if (JS_ToLengthFree(ctx, &len, js_dup(argv[0]))) + goto fail; + if (new_len < len) { + JS_ThrowTypeError(ctx, "TypedArray length is too small"); + fail: + JS_FreeValue(ctx, ret); + return JS_EXCEPTION; + } + } + return ret; +} + +static JSValue js_typed_array___speciesCreate(JSContext *ctx, + JSValue this_val, + int argc, JSValue *argv) +{ + JSValue obj; + JSObject *p; + JSValue ctor, ret; + int argc1; + + obj = argv[0]; + p = get_typed_array(ctx, obj); + if (!p) + return JS_EXCEPTION; + ctor = JS_SpeciesConstructor(ctx, obj, JS_UNDEFINED); + if (JS_IsException(ctor)) + return ctor; + argc1 = max_int(argc - 1, 0); + if (JS_IsUndefined(ctor)) { + ret = js_typed_array_constructor(ctx, JS_UNDEFINED, argc1, argv + 1, + p->class_id); + } else { + ret = js_typed_array_create(ctx, ctor, argc1, argv + 1); + JS_FreeValue(ctx, ctor); + } + return ret; +} + +static JSValue js_typed_array_from(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + // from(items, mapfn = void 0, this_arg = void 0) + JSValue items = argv[0], mapfn, this_arg; + JSValue args[2]; + JSValue stack[2]; + JSValue iter, arr, r, v, v2; + int64_t k, len; + int done, mapping; + + mapping = FALSE; + mapfn = JS_UNDEFINED; + this_arg = JS_UNDEFINED; + r = JS_UNDEFINED; + arr = JS_UNDEFINED; + stack[0] = JS_UNDEFINED; + stack[1] = JS_UNDEFINED; + + if (argc > 1) { + mapfn = argv[1]; + if (!JS_IsUndefined(mapfn)) { + if (check_function(ctx, mapfn)) + goto exception; + mapping = 1; + if (argc > 2) + this_arg = argv[2]; + } + } + iter = JS_GetProperty(ctx, items, JS_ATOM_Symbol_iterator); + if (JS_IsException(iter)) + goto exception; + if (!JS_IsUndefined(iter)) { + JS_FreeValue(ctx, iter); + arr = JS_NewArray(ctx); + if (JS_IsException(arr)) + goto exception; + stack[0] = js_dup(items); + if (js_for_of_start(ctx, &stack[1], FALSE)) + goto exception; + for (k = 0;; k++) { + v = JS_IteratorNext(ctx, stack[0], stack[1], 0, NULL, &done); + if (JS_IsException(v)) + goto exception_close; + if (done) + break; + if (JS_DefinePropertyValueInt64(ctx, arr, k, v, JS_PROP_C_W_E | JS_PROP_THROW) < 0) + goto exception_close; + } + } else { + arr = JS_ToObject(ctx, items); + if (JS_IsException(arr)) + goto exception; + } + if (js_get_length64(ctx, &len, arr) < 0) + goto exception; + v = js_int64(len); + args[0] = v; + r = js_typed_array_create(ctx, this_val, 1, args); + JS_FreeValue(ctx, v); + if (JS_IsException(r)) + goto exception; + for(k = 0; k < len; k++) { + v = JS_GetPropertyInt64(ctx, arr, k); + if (JS_IsException(v)) + goto exception; + if (mapping) { + args[0] = v; + args[1] = js_int32(k); + v2 = JS_Call(ctx, mapfn, this_arg, 2, args); + JS_FreeValue(ctx, v); + v = v2; + if (JS_IsException(v)) + goto exception; + } + if (JS_SetPropertyInt64(ctx, r, k, v) < 0) + goto exception; + } + goto done; + + exception_close: + if (!JS_IsUndefined(stack[0])) + JS_IteratorClose(ctx, stack[0], TRUE); + exception: + JS_FreeValue(ctx, r); + r = JS_EXCEPTION; + done: + JS_FreeValue(ctx, arr); + JS_FreeValue(ctx, stack[0]); + JS_FreeValue(ctx, stack[1]); + return r; +} + +static JSValue js_typed_array_of(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + JSValue obj; + JSValue args[1]; + int i; + + args[0] = js_int32(argc); + obj = js_typed_array_create(ctx, this_val, 1, args); + if (JS_IsException(obj)) + return obj; + + for(i = 0; i < argc; i++) { + if (JS_SetPropertyUint32(ctx, obj, i, js_dup(argv[i])) < 0) { + JS_FreeValue(ctx, obj); + return JS_EXCEPTION; + } + } + return obj; +} + +static JSValue js_typed_array_copyWithin(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + JSObject *p; + int len, to, from, final, count, shift, space; + + p = get_typed_array(ctx, this_val); + if (!p) + return JS_EXCEPTION; + if (typed_array_is_oob(p)) + return JS_ThrowTypeErrorArrayBufferOOB(ctx); + len = p->u.array.count; + + if (JS_ToInt32Clamp(ctx, &to, argv[0], 0, len, len)) + return JS_EXCEPTION; + + if (JS_ToInt32Clamp(ctx, &from, argv[1], 0, len, len)) + return JS_EXCEPTION; + + final = len; + if (argc > 2 && !JS_IsUndefined(argv[2])) { + if (JS_ToInt32Clamp(ctx, &final, argv[2], 0, len, len)) + return JS_EXCEPTION; + } + + if (typed_array_is_oob(p)) + return JS_ThrowTypeErrorArrayBufferOOB(ctx); + + // RAB may have been resized by evil .valueOf method + space = p->u.array.count - max_int(to, from); + count = min_int(final - from, len - to); + count = min_int(count, space); + if (count > 0) { + shift = typed_array_size_log2(p->class_id); + memmove(p->u.array.u.uint8_ptr + (to << shift), + p->u.array.u.uint8_ptr + (from << shift), + count << shift); + } + return js_dup(this_val); +} + +static JSValue js_typed_array_fill(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + JSObject *p; + int len, k, final, shift; + uint64_t v64; + + p = get_typed_array(ctx, this_val); + if (!p) + return JS_EXCEPTION; + if (typed_array_is_oob(p)) + return JS_ThrowTypeErrorArrayBufferOOB(ctx); + len = p->u.array.count; + + if (p->class_id == JS_CLASS_UINT8C_ARRAY) { + int32_t v; + if (JS_ToUint8ClampFree(ctx, &v, js_dup(argv[0]))) + return JS_EXCEPTION; + v64 = v; + } else if (p->class_id <= JS_CLASS_UINT32_ARRAY) { + uint32_t v; + if (JS_ToUint32(ctx, &v, argv[0])) + return JS_EXCEPTION; + v64 = v; + } else + if (p->class_id <= JS_CLASS_BIG_UINT64_ARRAY) { + if (JS_ToBigInt64(ctx, (int64_t *)&v64, argv[0])) + return JS_EXCEPTION; + } else { + double d; + if (JS_ToFloat64(ctx, &d, argv[0])) + return JS_EXCEPTION; + if (p->class_id == JS_CLASS_FLOAT16_ARRAY) { + v64 = tofp16(d); + } else if (p->class_id == JS_CLASS_FLOAT32_ARRAY) { + union { + float f; + uint32_t u32; + } u; + u.f = d; + v64 = u.u32; + } else { + JSFloat64Union u; + u.d = d; + v64 = u.u64; + } + } + + k = 0; + if (argc > 1) { + if (JS_ToInt32Clamp(ctx, &k, argv[1], 0, len, len)) + return JS_EXCEPTION; + } + + final = len; + if (argc > 2 && !JS_IsUndefined(argv[2])) { + if (JS_ToInt32Clamp(ctx, &final, argv[2], 0, len, len)) + return JS_EXCEPTION; + } + + if (typed_array_is_oob(p)) + return JS_ThrowTypeErrorArrayBufferOOB(ctx); + + // RAB may have been resized by evil .valueOf method + final = min_int(final, p->u.array.count); + shift = typed_array_size_log2(p->class_id); + switch(shift) { + case 0: + if (k < final) { + memset(p->u.array.u.uint8_ptr + k, v64, final - k); + } + break; + case 1: + for(; k < final; k++) { + p->u.array.u.uint16_ptr[k] = v64; + } + break; + case 2: + for(; k < final; k++) { + p->u.array.u.uint32_ptr[k] = v64; + } + break; + case 3: + for(; k < final; k++) { + p->u.array.u.uint64_ptr[k] = v64; + } + break; + default: + abort(); + } + return js_dup(this_val); +} + +static JSValue js_typed_array_find(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv, int mode) +{ + JSValue func, this_arg; + JSValue args[3]; + JSValue val, index_val, res; + int len, k, end; + int dir; + + val = JS_UNDEFINED; + len = js_typed_array_get_length_unsafe(ctx, this_val); + if (len < 0) + goto exception; + + func = argv[0]; + if (check_function(ctx, func)) + goto exception; + + this_arg = JS_UNDEFINED; + if (argc > 1) + this_arg = argv[1]; + + k = 0; + dir = 1; + end = len; + if (mode == ArrayFindLast || mode == ArrayFindLastIndex) { + k = len - 1; + dir = -1; + end = -1; + } + + for(; k != end; k += dir) { + index_val = js_int32(k); + val = JS_GetPropertyValue(ctx, this_val, index_val); + if (JS_IsException(val)) + goto exception; + args[0] = val; + args[1] = index_val; + args[2] = this_val; + res = JS_Call(ctx, func, this_arg, 3, args); + if (JS_IsException(res)) + goto exception; + if (JS_ToBoolFree(ctx, res)) { + if (mode == ArrayFindIndex || mode == ArrayFindLastIndex) { + JS_FreeValue(ctx, val); + return index_val; + } else { + return val; + } + } + JS_FreeValue(ctx, val); + } + if (mode == ArrayFindIndex || mode == ArrayFindLastIndex) + return js_int32(-1); + else + return JS_UNDEFINED; + +exception: + JS_FreeValue(ctx, val); + return JS_EXCEPTION; +} + +#define special_indexOf 0 +#define special_lastIndexOf 1 +#define special_includes -1 + +static JSValue js_typed_array_indexOf(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv, int special) +{ + JSObject *p; + int len, tag, is_int, is_bigint, k, stop, inc, res = -1; + int64_t v64; + double d; + float f; + uint16_t hf; + BOOL oob; + + p = get_typed_array(ctx, this_val); + if (!p) + return JS_EXCEPTION; + if (typed_array_is_oob(p)) + return JS_ThrowTypeErrorArrayBufferOOB(ctx); + len = p->u.array.count; + + if (len == 0) + goto done; + + oob = FALSE; + if (special == special_lastIndexOf) { + k = len - 1; + if (argc > 1) { + if (JS_ToFloat64(ctx, &d, argv[1])) + goto exception; + if (isnan(d)) { + k = 0; + } else { + if (d >= 0) { + if (d < k) { + k = d; + } + } else { + d += len; + if (d < 0) + goto done; + k = d; + } + } + } + stop = -1; + inc = -1; + } else { + k = 0; + if (argc > 1) { + if (JS_ToInt32Sat(ctx, &k, argv[1])) + goto exception; + if (k < 0) { + k += len; + if (k < 0) + k = 0; + } else if (k > len) { + k = len; + oob = TRUE; + } + } + stop = len; + inc = 1; + } + + /* if the array was detached, no need to go further (but no + exception is raised) */ + if (typed_array_is_oob(p) || len > p->u.array.count) { + /* "includes" scans all the properties, so "undefined" can match */ + if (special == special_includes && JS_IsUndefined(argv[0]) && len > 0) + res = oob ? -1 : 0; + goto done; + } + + // RAB may have been resized by evil .valueOf method + len = min_int(len, p->u.array.count); + if (len == 0) + goto done; + k = min_int(k, len); + stop = min_int(stop, len); + + is_bigint = 0; + is_int = 0; /* avoid warning */ + v64 = 0; /* avoid warning */ + tag = JS_VALUE_GET_NORM_TAG(argv[0]); + if (tag == JS_TAG_INT) { + is_int = 1; + v64 = JS_VALUE_GET_INT(argv[0]); + d = v64; + } else + if (tag == JS_TAG_FLOAT64) { + d = JS_VALUE_GET_FLOAT64(argv[0]); + if (d >= INT64_MIN && d < 0x1p63) { + v64 = d; + is_int = (v64 == d); + } + } else + if (tag == JS_TAG_BIG_INT) { + JSBigInt *p1 = JS_VALUE_GET_PTR(argv[0]); + + if (p->class_id == JS_CLASS_BIG_INT64_ARRAY) { + if (bf_get_int64(&v64, &p1->num, 0) != 0) + goto done; + } else if (p->class_id == JS_CLASS_BIG_UINT64_ARRAY) { + if (bf_get_uint64((uint64_t *)&v64, &p1->num) != 0) + goto done; + } else { + goto done; + } + d = 0; + is_bigint = 1; + } else { + goto done; + } + + switch (p->class_id) { + case JS_CLASS_INT8_ARRAY: + if (is_int && (int8_t)v64 == v64) + goto scan8; + break; + case JS_CLASS_UINT8C_ARRAY: + case JS_CLASS_UINT8_ARRAY: + if (is_int && (uint8_t)v64 == v64) { + const uint8_t *pv, *pp; + uint16_t v; + scan8: + pv = p->u.array.u.uint8_ptr; + v = v64; + if (inc > 0) { + pp = NULL; + if (pv) + pp = memchr(pv + k, v, len - k); + if (pp) + res = pp - pv; + } else { + for (; k != stop; k += inc) { + if (pv[k] == v) { + res = k; + break; + } + } + } + } + break; + case JS_CLASS_INT16_ARRAY: + if (is_int && (int16_t)v64 == v64) + goto scan16; + break; + case JS_CLASS_UINT16_ARRAY: + if (is_int && (uint16_t)v64 == v64) { + const uint16_t *pv; + uint16_t v; + scan16: + pv = p->u.array.u.uint16_ptr; + v = v64; + for (; k != stop; k += inc) { + if (pv[k] == v) { + res = k; + break; + } + } + } + break; + case JS_CLASS_INT32_ARRAY: + if (is_int && (int32_t)v64 == v64) + goto scan32; + break; + case JS_CLASS_UINT32_ARRAY: + if (is_int && (uint32_t)v64 == v64) { + const uint32_t *pv; + uint32_t v; + scan32: + pv = p->u.array.u.uint32_ptr; + v = v64; + for (; k != stop; k += inc) { + if (pv[k] == v) { + res = k; + break; + } + } + } + break; + case JS_CLASS_FLOAT16_ARRAY: + if (is_bigint) + break; + if (isnan(d)) { + const uint16_t *pv = p->u.array.u.fp16_ptr; + /* special case: indexOf returns -1, includes finds NaN */ + if (special != special_includes) + goto done; + for (; k != stop; k += inc) { + if (isfp16nan(pv[k])) { + res = k; + break; + } + } + } else if (d == 0) { + // special case: includes also finds negative zero + const uint16_t *pv = p->u.array.u.fp16_ptr; + for (; k != stop; k += inc) { + if (isfp16zero(pv[k])) { + res = k; + break; + } + } + } else if (hf = tofp16(d), d == fromfp16(hf)) { + const uint16_t *pv = p->u.array.u.fp16_ptr; + for (; k != stop; k += inc) { + if (pv[k] == hf) { + res = k; + break; + } + } + } + break; + case JS_CLASS_FLOAT32_ARRAY: + if (is_bigint) + break; + if (isnan(d)) { + const float *pv = p->u.array.u.float_ptr; + /* special case: indexOf returns -1, includes finds NaN */ + if (special != special_includes) + goto done; + for (; k != stop; k += inc) { + if (isnan(pv[k])) { + res = k; + break; + } + } + } else if ((f = (float)d) == d) { + const float *pv = p->u.array.u.float_ptr; + for (; k != stop; k += inc) { + if (pv[k] == f) { + res = k; + break; + } + } + } + break; + case JS_CLASS_FLOAT64_ARRAY: + if (is_bigint) + break; + if (isnan(d)) { + const double *pv = p->u.array.u.double_ptr; + /* special case: indexOf returns -1, includes finds NaN */ + if (special != special_includes) + goto done; + for (; k != stop; k += inc) { + if (isnan(pv[k])) { + res = k; + break; + } + } + } else { + const double *pv = p->u.array.u.double_ptr; + for (; k != stop; k += inc) { + if (pv[k] == d) { + res = k; + break; + } + } + } + break; + case JS_CLASS_BIG_INT64_ARRAY: + if (is_bigint) { + goto scan64; + } + break; + case JS_CLASS_BIG_UINT64_ARRAY: + if (is_bigint) { + const uint64_t *pv; + uint64_t v; + scan64: + pv = p->u.array.u.uint64_ptr; + v = v64; + for (; k != stop; k += inc) { + if (pv[k] == v) { + res = k; + break; + } + } + } + break; + } + +done: + if (special == special_includes) + return js_bool(res >= 0); + else + return js_int32(res); + +exception: + return JS_EXCEPTION; +} + +static JSValue js_typed_array_join(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv, int toLocaleString) +{ + JSValue sep = JS_UNDEFINED, el; + StringBuffer b_s, *b = &b_s; + JSString *s = NULL; + JSObject *p; + int i, len, oldlen, newlen; + int c; + + p = get_typed_array(ctx, this_val); + if (!p) + return JS_EXCEPTION; + if (typed_array_is_oob(p)) + return JS_ThrowTypeErrorArrayBufferOOB(ctx); + len = oldlen = newlen = p->u.array.count; + + c = ','; /* default separator */ + if (!toLocaleString && argc > 0 && !JS_IsUndefined(argv[0])) { + sep = JS_ToString(ctx, argv[0]); + if (JS_IsException(sep)) + goto exception; + s = JS_VALUE_GET_STRING(sep); + if (s->len == 1 && !s->is_wide_char) + c = s->u.str8[0]; + else + c = -1; + // ToString(sep) can detach or resize the arraybuffer as a side effect + newlen = p->u.array.count; + len = min_int(len, newlen); + } + string_buffer_init(ctx, b, 0); + + /* XXX: optimize with direct access */ + for(i = 0; i < len; i++) { + if (i > 0) { + if (c >= 0) { + if (string_buffer_putc8(b, c)) + goto fail; + } else { + if (string_buffer_concat(b, s, 0, s->len)) + goto fail; + } + } + el = JS_GetPropertyUint32(ctx, this_val, i); + /* Can return undefined for example if the typed array is detached */ + if (!JS_IsNull(el) && !JS_IsUndefined(el)) { + if (JS_IsException(el)) + goto fail; + if (toLocaleString) { + el = JS_ToLocaleStringFree(ctx, el); + } + if (string_buffer_concat_value_free(b, el)) + goto fail; + } + } + + // add extra separators in case RAB was resized by evil .valueOf method + i = max_int(1, newlen); + for(/*empty*/; i < oldlen; i++) { + if (c >= 0) { + if (string_buffer_putc8(b, c)) + goto fail; + } else { + if (string_buffer_concat(b, s, 0, s->len)) + goto fail; + } + } + + JS_FreeValue(ctx, sep); + return string_buffer_end(b); + +fail: + string_buffer_free(b); + JS_FreeValue(ctx, sep); +exception: + return JS_EXCEPTION; +} + +static JSValue js_typed_array_reverse(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + JSObject *p; + int len; + + len = js_typed_array_get_length_unsafe(ctx, this_val); + if (len < 0) + return JS_EXCEPTION; + if (len > 0) { + p = JS_VALUE_GET_OBJ(this_val); + switch (typed_array_size_log2(p->class_id)) { + case 0: + { + uint8_t *p1 = p->u.array.u.uint8_ptr; + uint8_t *p2 = p1 + len - 1; + while (p1 < p2) { + uint8_t v = *p1; + *p1++ = *p2; + *p2-- = v; + } + } + break; + case 1: + { + uint16_t *p1 = p->u.array.u.uint16_ptr; + uint16_t *p2 = p1 + len - 1; + while (p1 < p2) { + uint16_t v = *p1; + *p1++ = *p2; + *p2-- = v; + } + } + break; + case 2: + { + uint32_t *p1 = p->u.array.u.uint32_ptr; + uint32_t *p2 = p1 + len - 1; + while (p1 < p2) { + uint32_t v = *p1; + *p1++ = *p2; + *p2-- = v; + } + } + break; + case 3: + { + uint64_t *p1 = p->u.array.u.uint64_ptr; + uint64_t *p2 = p1 + len - 1; + while (p1 < p2) { + uint64_t v = *p1; + *p1++ = *p2; + *p2-- = v; + } + } + break; + default: + abort(); + } + } + return js_dup(this_val); +} + +static JSValue js_typed_array_toReversed(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + JSValue arr, ret; + JSObject *p; + + p = get_typed_array(ctx, this_val); + if (!p) + return JS_EXCEPTION; + arr = js_typed_array_constructor_ta(ctx, JS_UNDEFINED, this_val, + p->class_id, p->u.array.count); + if (JS_IsException(arr)) + return JS_EXCEPTION; + ret = js_typed_array_reverse(ctx, arr, argc, argv); + JS_FreeValue(ctx, arr); + return ret; +} + +static JSValue js_typed_array_slice(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + JSValue args[2]; + JSValue arr, val; + JSObject *p, *p1; + int n, len, start, final, count, shift, space; + + arr = JS_UNDEFINED; + p = get_typed_array(ctx, this_val); + if (!p) + return JS_EXCEPTION; + if (typed_array_is_oob(p)) + return JS_ThrowTypeErrorArrayBufferOOB(ctx); + len = p->u.array.count; + + if (JS_ToInt32Clamp(ctx, &start, argv[0], 0, len, len)) + goto exception; + final = len; + if (!JS_IsUndefined(argv[1])) { + if (JS_ToInt32Clamp(ctx, &final, argv[1], 0, len, len)) + goto exception; + } + count = max_int(final - start, 0); + + args[0] = this_val; + args[1] = js_int32(count); + arr = js_typed_array___speciesCreate(ctx, JS_UNDEFINED, 2, args); + if (JS_IsException(arr)) + goto exception; + + if (count > 0) { + if (validate_typed_array(ctx, this_val) + || validate_typed_array(ctx, arr)) + goto exception; + + if (len != p->u.array.count) + goto slow_path; + + p1 = get_typed_array(ctx, arr); + if (p1 != NULL && p->class_id == p1->class_id && + typed_array_get_length(ctx, p1) >= count && + typed_array_get_length(ctx, p) >= start + count) { + shift = typed_array_size_log2(p->class_id); + memmove(p1->u.array.u.uint8_ptr, + p->u.array.u.uint8_ptr + (start << shift), + count << shift); + } else { + slow_path: + space = max_int(0, p->u.array.count - start); + count = min_int(count, space); + for (n = 0; n < count; n++) { + val = JS_GetPropertyValue(ctx, this_val, js_int32(start + n)); + if (JS_IsException(val)) + goto exception; + if (JS_SetPropertyValue(ctx, arr, js_int32(n), val, + JS_PROP_THROW) < 0) + goto exception; + } + } + } + return arr; + + exception: + JS_FreeValue(ctx, arr); + return JS_EXCEPTION; +} + +static JSValue js_typed_array_subarray(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + JSArrayBuffer *abuf; + JSTypedArray *ta; + JSValue args[4]; + JSValue arr, byteOffset, ta_buffer; + JSObject *p; + int len, start, final, count, shift, offset; + + p = get_typed_array(ctx, this_val); + if (!p) + goto exception; + len = p->u.array.count; + if (JS_ToInt32Clamp(ctx, &start, argv[0], 0, len, len)) + goto exception; + final = len; + if (!JS_IsUndefined(argv[1])) { + if (JS_ToInt32Clamp(ctx, &final, argv[1], 0, len, len)) + goto exception; + } + count = max_int(final - start, 0); + byteOffset = js_typed_array_get_byteOffset(ctx, this_val); + if (JS_IsException(byteOffset)) + goto exception; + ta = p->u.typed_array; + abuf = ta->buffer->u.array_buffer; + if (ta->offset > abuf->byte_length) + goto range_error; + if (ta->offset == abuf->byte_length && count > 0) { + range_error: + JS_ThrowRangeError(ctx, "invalid offset"); + goto exception; + } + shift = typed_array_size_log2(p->class_id); + offset = JS_VALUE_GET_INT(byteOffset) + (start << shift); + JS_FreeValue(ctx, byteOffset); + ta_buffer = js_typed_array_get_buffer(ctx, this_val); + if (JS_IsException(ta_buffer)) + goto exception; + args[0] = this_val; + args[1] = ta_buffer; + args[2] = js_int32(offset); + args[3] = js_int32(count); + // result is length-tracking if source TA is and no explicit count is given + if (ta->track_rab && JS_IsUndefined(argv[1])) + args[3] = JS_UNDEFINED; + arr = js_typed_array___speciesCreate(ctx, JS_UNDEFINED, 4, args); + JS_FreeValue(ctx, ta_buffer); + return arr; + exception: + return JS_EXCEPTION; +} + +/* TypedArray.prototype.sort */ + +static int js_cmp_doubles(double x, double y) +{ + if (isnan(x)) return isnan(y) ? 0 : +1; + if (isnan(y)) return -1; + if (x < y) return -1; + if (x > y) return 1; + if (x != 0) return 0; + if (signbit(x)) return signbit(y) ? 0 : -1; + else return signbit(y) ? 1 : 0; +} + +static int js_TA_cmp_int8(const void *a, const void *b, void *opaque) { + return *(const int8_t *)a - *(const int8_t *)b; +} + +static int js_TA_cmp_uint8(const void *a, const void *b, void *opaque) { + return *(const uint8_t *)a - *(const uint8_t *)b; +} + +static int js_TA_cmp_int16(const void *a, const void *b, void *opaque) { + return *(const int16_t *)a - *(const int16_t *)b; +} + +static int js_TA_cmp_uint16(const void *a, const void *b, void *opaque) { + return *(const uint16_t *)a - *(const uint16_t *)b; +} + +static int js_TA_cmp_int32(const void *a, const void *b, void *opaque) { + int32_t x = *(const int32_t *)a; + int32_t y = *(const int32_t *)b; + return (y < x) - (y > x); +} + +static int js_TA_cmp_uint32(const void *a, const void *b, void *opaque) { + uint32_t x = *(const uint32_t *)a; + uint32_t y = *(const uint32_t *)b; + return (y < x) - (y > x); +} + +static int js_TA_cmp_int64(const void *a, const void *b, void *opaque) { + int64_t x = *(const int64_t *)a; + int64_t y = *(const int64_t *)b; + return (y < x) - (y > x); +} + +static int js_TA_cmp_uint64(const void *a, const void *b, void *opaque) { + uint64_t x = *(const uint64_t *)a; + uint64_t y = *(const uint64_t *)b; + return (y < x) - (y > x); +} + +static int js_TA_cmp_float16(const void *a, const void *b, void *opaque) { + return js_cmp_doubles(fromfp16(*(const uint16_t *)a), + fromfp16(*(const uint16_t *)b)); +} + +static int js_TA_cmp_float32(const void *a, const void *b, void *opaque) { + return js_cmp_doubles(*(const float *)a, *(const float *)b); +} + +static int js_TA_cmp_float64(const void *a, const void *b, void *opaque) { + return js_cmp_doubles(*(const double *)a, *(const double *)b); +} + +static JSValue js_TA_get_int8(JSContext *ctx, const void *a) { + return js_int32(*(const int8_t *)a); +} + +static JSValue js_TA_get_uint8(JSContext *ctx, const void *a) { + return js_int32(*(const uint8_t *)a); +} + +static JSValue js_TA_get_int16(JSContext *ctx, const void *a) { + return js_int32(*(const int16_t *)a); +} + +static JSValue js_TA_get_uint16(JSContext *ctx, const void *a) { + return js_int32(*(const uint16_t *)a); +} + +static JSValue js_TA_get_int32(JSContext *ctx, const void *a) { + return js_int32(*(const int32_t *)a); +} + +static JSValue js_TA_get_uint32(JSContext *ctx, const void *a) { + return js_uint32(*(const uint32_t *)a); +} + +static JSValue js_TA_get_int64(JSContext *ctx, const void *a) { + return JS_NewBigInt64(ctx, *(int64_t *)a); +} + +static JSValue js_TA_get_uint64(JSContext *ctx, const void *a) { + return JS_NewBigUint64(ctx, *(uint64_t *)a); +} + +static JSValue js_TA_get_float16(JSContext *ctx, const void *a) { + return js_float64(fromfp16(*(const uint16_t *)a)); +} + +static JSValue js_TA_get_float32(JSContext *ctx, const void *a) { + return js_float64(*(const float *)a); +} + +static JSValue js_TA_get_float64(JSContext *ctx, const void *a) { + return js_float64(*(const double *)a); +} + +struct TA_sort_context { + JSContext *ctx; + int exception; + JSValue arr; + JSValue cmp; + JSValue (*getfun)(JSContext *ctx, const void *a); + int elt_size; +}; + +static int js_TA_cmp_generic(const void *a, const void *b, void *opaque) { + struct TA_sort_context *psc = opaque; + JSContext *ctx = psc->ctx; + uint32_t a_idx, b_idx; + JSValue argv[2]; + JSValue res; + JSObject *p; + int cmp; + + p = JS_VALUE_GET_OBJ(psc->arr); + if (typed_array_is_oob(p)) + return 0; + + cmp = 0; + if (!psc->exception) { + a_idx = *(uint32_t *)a; + b_idx = *(uint32_t *)b; + if (a_idx >= p->u.array.count || b_idx >= p->u.array.count) + return 0; + argv[0] = psc->getfun(ctx, (char *)p->u.array.u.ptr + + a_idx * (size_t)psc->elt_size); + argv[1] = psc->getfun(ctx, (char *)p->u.array.u.ptr + + b_idx * (size_t)(psc->elt_size)); + res = JS_Call(ctx, psc->cmp, JS_UNDEFINED, 2, argv); + if (JS_IsException(res)) { + psc->exception = 1; + goto done; + } + if (JS_VALUE_GET_TAG(res) == JS_TAG_INT) { + int val = JS_VALUE_GET_INT(res); + cmp = (val > 0) - (val < 0); + } else { + double val; + if (JS_ToFloat64Free(ctx, &val, res) < 0) { + psc->exception = 1; + goto done; + } else { + cmp = (val > 0) - (val < 0); + } + } + if (cmp == 0) { + /* make sort stable: compare array offsets */ + cmp = (a_idx > b_idx) - (a_idx < b_idx); + } + done: + JS_FreeValue(ctx, argv[0]); + JS_FreeValue(ctx, argv[1]); + } + return cmp; +} + +static JSValue js_typed_array_sort(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + JSObject *p; + int len; + size_t elt_size; + struct TA_sort_context tsc; + int (*cmpfun)(const void *a, const void *b, void *opaque); + + p = get_typed_array(ctx, this_val); + if (!p) + return JS_EXCEPTION; + if (typed_array_is_oob(p)) + return JS_ThrowTypeErrorArrayBufferOOB(ctx); + + tsc.ctx = ctx; + tsc.exception = 0; + tsc.arr = this_val; + tsc.cmp = argv[0]; + + if (!JS_IsUndefined(tsc.cmp) && check_function(ctx, tsc.cmp)) + return JS_EXCEPTION; + + len = p->u.array.count; + if (len > 1) { + switch (p->class_id) { + case JS_CLASS_INT8_ARRAY: + tsc.getfun = js_TA_get_int8; + cmpfun = js_TA_cmp_int8; + break; + case JS_CLASS_UINT8C_ARRAY: + case JS_CLASS_UINT8_ARRAY: + tsc.getfun = js_TA_get_uint8; + cmpfun = js_TA_cmp_uint8; + break; + case JS_CLASS_INT16_ARRAY: + tsc.getfun = js_TA_get_int16; + cmpfun = js_TA_cmp_int16; + break; + case JS_CLASS_UINT16_ARRAY: + tsc.getfun = js_TA_get_uint16; + cmpfun = js_TA_cmp_uint16; + break; + case JS_CLASS_INT32_ARRAY: + tsc.getfun = js_TA_get_int32; + cmpfun = js_TA_cmp_int32; + break; + case JS_CLASS_UINT32_ARRAY: + tsc.getfun = js_TA_get_uint32; + cmpfun = js_TA_cmp_uint32; + break; + case JS_CLASS_BIG_INT64_ARRAY: + tsc.getfun = js_TA_get_int64; + cmpfun = js_TA_cmp_int64; + break; + case JS_CLASS_BIG_UINT64_ARRAY: + tsc.getfun = js_TA_get_uint64; + cmpfun = js_TA_cmp_uint64; + break; + case JS_CLASS_FLOAT16_ARRAY: + tsc.getfun = js_TA_get_float16; + cmpfun = js_TA_cmp_float16; + break; + case JS_CLASS_FLOAT32_ARRAY: + tsc.getfun = js_TA_get_float32; + cmpfun = js_TA_cmp_float32; + break; + case JS_CLASS_FLOAT64_ARRAY: + tsc.getfun = js_TA_get_float64; + cmpfun = js_TA_cmp_float64; + break; + default: + abort(); + } + elt_size = 1 << typed_array_size_log2(p->class_id); + if (!JS_IsUndefined(tsc.cmp)) { + uint32_t *array_idx; + void *array_tmp; + size_t i, j; + + /* XXX: a stable sort would use less memory */ + array_idx = js_malloc(ctx, len * sizeof(array_idx[0])); + if (!array_idx) + return JS_EXCEPTION; + for(i = 0; i < len; i++) + array_idx[i] = i; + tsc.elt_size = elt_size; + rqsort(array_idx, len, sizeof(array_idx[0]), + js_TA_cmp_generic, &tsc); + if (tsc.exception) + goto fail; + // per spec: typed array can be detached mid-iteration + if (typed_array_is_oob(p)) + goto done; + len = min_int(len, p->u.array.count); + if (len == 0) + goto done; + array_tmp = js_malloc(ctx, len * elt_size); + if (!array_tmp) { + fail: + js_free(ctx, array_idx); + return JS_EXCEPTION; + } + memcpy(array_tmp, p->u.array.u.ptr, len * elt_size); + switch(elt_size) { + case 1: + for(i = 0; i < len; i++) { + j = array_idx[i]; + p->u.array.u.uint8_ptr[i] = ((uint8_t *)array_tmp)[j]; + } + break; + case 2: + for(i = 0; i < len; i++) { + j = array_idx[i]; + p->u.array.u.uint16_ptr[i] = ((uint16_t *)array_tmp)[j]; + } + break; + case 4: + for(i = 0; i < len; i++) { + j = array_idx[i]; + p->u.array.u.uint32_ptr[i] = ((uint32_t *)array_tmp)[j]; + } + break; + case 8: + for(i = 0; i < len; i++) { + j = array_idx[i]; + p->u.array.u.uint64_ptr[i] = ((uint64_t *)array_tmp)[j]; + } + break; + default: + abort(); + } + js_free(ctx, array_tmp); + done: + js_free(ctx, array_idx); + } else { + rqsort(p->u.array.u.ptr, len, elt_size, cmpfun, &tsc); + if (tsc.exception) + return JS_EXCEPTION; + } + } + return js_dup(this_val); +} + +static JSValue js_typed_array_toSorted(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + JSValue arr, ret; + JSObject *p; + + p = get_typed_array(ctx, this_val); + if (!p) + return JS_EXCEPTION; + arr = js_typed_array_constructor_ta(ctx, JS_UNDEFINED, this_val, + p->class_id, p->u.array.count); + if (JS_IsException(arr)) + return JS_EXCEPTION; + ret = js_typed_array_sort(ctx, arr, argc, argv); + JS_FreeValue(ctx, arr); + return ret; +} + +static const JSCFunctionListEntry js_typed_array_base_funcs[] = { + JS_CFUNC_DEF("from", 1, js_typed_array_from ), + JS_CFUNC_DEF("of", 0, js_typed_array_of ), + JS_CGETSET_DEF("[Symbol.species]", js_get_this, NULL ), +}; + +static const JSCFunctionListEntry js_typed_array_base_proto_funcs[] = { + JS_CGETSET_DEF("length", js_typed_array_get_length, NULL ), + JS_CFUNC_DEF("at", 1, js_typed_array_at ), + JS_CFUNC_DEF("with", 2, js_typed_array_with ), + JS_CGETSET_DEF("buffer", js_typed_array_get_buffer, NULL ), + JS_CGETSET_DEF("byteLength", js_typed_array_get_byteLength, NULL ), + JS_CGETSET_DEF("byteOffset", js_typed_array_get_byteOffset, NULL ), + JS_CFUNC_DEF("set", 1, js_typed_array_set ), + JS_CFUNC_MAGIC_DEF("values", 0, js_create_typed_array_iterator, JS_ITERATOR_KIND_VALUE ), + JS_ALIAS_DEF("[Symbol.iterator]", "values" ), + JS_CFUNC_MAGIC_DEF("keys", 0, js_create_typed_array_iterator, JS_ITERATOR_KIND_KEY ), + JS_CFUNC_MAGIC_DEF("entries", 0, js_create_typed_array_iterator, JS_ITERATOR_KIND_KEY_AND_VALUE ), + JS_CGETSET_DEF("[Symbol.toStringTag]", js_typed_array_get_toStringTag, NULL ), + JS_CFUNC_DEF("copyWithin", 2, js_typed_array_copyWithin ), + JS_CFUNC_MAGIC_DEF("every", 1, js_array_every, special_every | special_TA ), + JS_CFUNC_MAGIC_DEF("some", 1, js_array_every, special_some | special_TA ), + JS_CFUNC_MAGIC_DEF("forEach", 1, js_array_every, special_forEach | special_TA ), + JS_CFUNC_MAGIC_DEF("map", 1, js_array_every, special_map | special_TA ), + JS_CFUNC_MAGIC_DEF("filter", 1, js_array_every, special_filter | special_TA ), + JS_CFUNC_MAGIC_DEF("reduce", 1, js_array_reduce, special_reduce | special_TA ), + JS_CFUNC_MAGIC_DEF("reduceRight", 1, js_array_reduce, special_reduceRight | special_TA ), + JS_CFUNC_DEF("fill", 1, js_typed_array_fill ), + JS_CFUNC_MAGIC_DEF("find", 1, js_typed_array_find, ArrayFind ), + JS_CFUNC_MAGIC_DEF("findIndex", 1, js_typed_array_find, ArrayFindIndex ), + JS_CFUNC_MAGIC_DEF("findLast", 1, js_typed_array_find, ArrayFindLast ), + JS_CFUNC_MAGIC_DEF("findLastIndex", 1, js_typed_array_find, ArrayFindLastIndex ), + JS_CFUNC_DEF("reverse", 0, js_typed_array_reverse ), + JS_CFUNC_DEF("toReversed", 0, js_typed_array_toReversed ), + JS_CFUNC_DEF("slice", 2, js_typed_array_slice ), + JS_CFUNC_DEF("subarray", 2, js_typed_array_subarray ), + JS_CFUNC_DEF("sort", 1, js_typed_array_sort ), + JS_CFUNC_DEF("toSorted", 1, js_typed_array_toSorted ), + JS_CFUNC_MAGIC_DEF("join", 1, js_typed_array_join, 0 ), + JS_CFUNC_MAGIC_DEF("toLocaleString", 0, js_typed_array_join, 1 ), + JS_CFUNC_MAGIC_DEF("indexOf", 1, js_typed_array_indexOf, special_indexOf ), + JS_CFUNC_MAGIC_DEF("lastIndexOf", 1, js_typed_array_indexOf, special_lastIndexOf ), + JS_CFUNC_MAGIC_DEF("includes", 1, js_typed_array_indexOf, special_includes ), + //JS_ALIAS_BASE_DEF("toString", "toString", 2 /* Array.prototype. */), @@@ +}; + +static JSValue js_typed_array_base_constructor(JSContext *ctx, + JSValue this_val, + int argc, JSValue *argv) +{ + return JS_ThrowTypeError(ctx, "cannot be called"); +} + +/* 'obj' must be an allocated typed array object */ +static int typed_array_init(JSContext *ctx, JSValue obj, JSValue buffer, + uint64_t offset, uint64_t len, BOOL track_rab) +{ + JSTypedArray *ta; + JSObject *p, *pbuffer; + JSArrayBuffer *abuf; + int size_log2; + + p = JS_VALUE_GET_OBJ(obj); + size_log2 = typed_array_size_log2(p->class_id); + ta = js_malloc(ctx, sizeof(*ta)); + if (!ta) { + JS_FreeValue(ctx, buffer); + return -1; + } + pbuffer = JS_VALUE_GET_OBJ(buffer); + abuf = pbuffer->u.array_buffer; + ta->obj = p; + ta->buffer = pbuffer; + ta->offset = offset; + ta->length = len << size_log2; + ta->track_rab = track_rab; + list_add_tail(&ta->link, &abuf->array_list); + p->u.typed_array = ta; + p->u.array.count = len; + p->u.array.u.ptr = abuf->data + offset; + return 0; +} + + +static JSValue js_array_from_iterator(JSContext *ctx, uint32_t *plen, + JSValue obj, JSValue method) +{ + JSValue arr, iter, next_method = JS_UNDEFINED, val; + BOOL done; + uint32_t k; + + *plen = 0; + arr = JS_NewArray(ctx); + if (JS_IsException(arr)) + return arr; + iter = JS_GetIterator2(ctx, obj, method); + if (JS_IsException(iter)) + goto fail; + next_method = JS_GetProperty(ctx, iter, JS_ATOM_next); + if (JS_IsException(next_method)) + goto fail; + k = 0; + for(;;) { + val = JS_IteratorNext(ctx, iter, next_method, 0, NULL, &done); + if (JS_IsException(val)) + goto fail; + if (done) { + JS_FreeValue(ctx, val); + break; + } + if (JS_CreateDataPropertyUint32(ctx, arr, k, val, JS_PROP_THROW) < 0) + goto fail; + k++; + } + JS_FreeValue(ctx, next_method); + JS_FreeValue(ctx, iter); + *plen = k; + return arr; + fail: + JS_FreeValue(ctx, next_method); + JS_FreeValue(ctx, iter); + JS_FreeValue(ctx, arr); + return JS_EXCEPTION; +} + +static JSValue js_typed_array_constructor_obj(JSContext *ctx, + JSValue new_target, + JSValue obj, + int classid) +{ + JSValue iter, ret, arr = JS_UNDEFINED, val, buffer; + uint32_t i; + int size_log2; + int64_t len; + + size_log2 = typed_array_size_log2(classid); + ret = js_create_from_ctor(ctx, new_target, classid); + if (JS_IsException(ret)) + return JS_EXCEPTION; + + iter = JS_GetProperty(ctx, obj, JS_ATOM_Symbol_iterator); + if (JS_IsException(iter)) + goto fail; + if (!JS_IsUndefined(iter) && !JS_IsNull(iter)) { + uint32_t len1; + arr = js_array_from_iterator(ctx, &len1, obj, iter); + JS_FreeValue(ctx, iter); + if (JS_IsException(arr)) + goto fail; + len = len1; + } else { + if (js_get_length64(ctx, &len, obj)) + goto fail; + arr = js_dup(obj); + } + + buffer = js_array_buffer_constructor1(ctx, JS_UNDEFINED, + len << size_log2, + NULL); + if (JS_IsException(buffer)) + goto fail; + if (typed_array_init(ctx, ret, buffer, 0, len, /*track_rab*/FALSE)) + goto fail; + + for(i = 0; i < len; i++) { + val = JS_GetPropertyUint32(ctx, arr, i); + if (JS_IsException(val)) + goto fail; + if (JS_SetPropertyUint32(ctx, ret, i, val) < 0) + goto fail; + } + JS_FreeValue(ctx, arr); + return ret; + fail: + JS_FreeValue(ctx, arr); + JS_FreeValue(ctx, ret); + return JS_EXCEPTION; +} + +static JSValue js_typed_array_constructor_ta(JSContext *ctx, + JSValue new_target, + JSValue src_obj, + int classid, uint32_t len) +{ + JSObject *p, *src_buffer; + JSTypedArray *ta; + JSValue obj, buffer; + uint32_t i; + int size_log2; + JSArrayBuffer *src_abuf, *abuf; + + obj = js_create_from_ctor(ctx, new_target, classid); + if (JS_IsException(obj)) + return obj; + p = JS_VALUE_GET_OBJ(src_obj); + if (typed_array_is_oob(p)) { + JS_ThrowTypeErrorArrayBufferOOB(ctx); + goto fail; + } + ta = p->u.typed_array; + src_buffer = ta->buffer; + src_abuf = src_buffer->u.array_buffer; + size_log2 = typed_array_size_log2(classid); + buffer = js_array_buffer_constructor1(ctx, JS_UNDEFINED, + (uint64_t)len << size_log2, + NULL); + if (JS_IsException(buffer)) + goto fail; + /* necessary because it could have been detached */ + if (typed_array_is_oob(p)) { + JS_FreeValue(ctx, buffer); + JS_ThrowTypeErrorArrayBufferOOB(ctx); + goto fail; + } + abuf = JS_GetOpaque(buffer, JS_CLASS_ARRAY_BUFFER); + if (typed_array_init(ctx, obj, buffer, 0, len, /*track_rab*/FALSE)) + goto fail; + if (p->class_id == classid) { + /* same type: copy the content */ + memcpy(abuf->data, src_abuf->data + ta->offset, abuf->byte_length); + } else { + for(i = 0; i < len; i++) { + JSValue val; + val = JS_GetPropertyUint32(ctx, src_obj, i); + if (JS_IsException(val)) + goto fail; + if (JS_SetPropertyUint32(ctx, obj, i, val) < 0) + goto fail; + } + } + return obj; + fail: + JS_FreeValue(ctx, obj); + return JS_EXCEPTION; +} + +static JSValue js_typed_array_constructor(JSContext *ctx, + JSValue new_target, + int argc, JSValue *argv, + int classid) +{ + BOOL track_rab = FALSE; + JSValue buffer, obj; + JSArrayBuffer *abuf; + int size_log2; + uint64_t len, offset; + + size_log2 = typed_array_size_log2(classid); + if (JS_VALUE_GET_TAG(argv[0]) != JS_TAG_OBJECT) { + if (JS_ToIndex(ctx, &len, argv[0])) + return JS_EXCEPTION; + buffer = js_array_buffer_constructor1(ctx, JS_UNDEFINED, + len << size_log2, + NULL); + if (JS_IsException(buffer)) + return JS_EXCEPTION; + offset = 0; + } else { + JSObject *p = JS_VALUE_GET_OBJ(argv[0]); + if (p->class_id == JS_CLASS_ARRAY_BUFFER || + p->class_id == JS_CLASS_SHARED_ARRAY_BUFFER) { + abuf = p->u.array_buffer; + if (JS_ToIndex(ctx, &offset, argv[1])) + return JS_EXCEPTION; + if (abuf->detached) + return JS_ThrowTypeErrorDetachedArrayBuffer(ctx); + if ((offset & ((1 << size_log2) - 1)) != 0 || + offset > abuf->byte_length) + return JS_ThrowRangeError(ctx, "invalid offset"); + if (JS_IsUndefined(argv[2])) { + track_rab = array_buffer_is_resizable(abuf); + if (!track_rab) + if ((abuf->byte_length & ((1 << size_log2) - 1)) != 0) + goto invalid_length; + len = (abuf->byte_length - offset) >> size_log2; + } else { + if (JS_ToIndex(ctx, &len, argv[2])) + return JS_EXCEPTION; + if (abuf->detached) + return JS_ThrowTypeErrorDetachedArrayBuffer(ctx); + if ((offset + (len << size_log2)) > abuf->byte_length) { + invalid_length: + return JS_ThrowRangeError(ctx, "invalid length"); + } + } + buffer = js_dup(argv[0]); + } else { + if (p->class_id >= JS_CLASS_UINT8C_ARRAY && + p->class_id <= JS_CLASS_FLOAT64_ARRAY) { + return js_typed_array_constructor_ta(ctx, new_target, argv[0], + classid, p->u.array.count); + } else { + return js_typed_array_constructor_obj(ctx, new_target, argv[0], classid); + } + } + } + + obj = js_create_from_ctor(ctx, new_target, classid); + if (JS_IsException(obj)) { + JS_FreeValue(ctx, buffer); + return JS_EXCEPTION; + } + if (typed_array_init(ctx, obj, buffer, offset, len, track_rab)) { + JS_FreeValue(ctx, obj); + return JS_EXCEPTION; + } + return obj; +} + +static void js_typed_array_finalizer(JSRuntime *rt, JSValue val) +{ + JSObject *p = JS_VALUE_GET_OBJ(val); + JSTypedArray *ta = p->u.typed_array; + if (ta) { + /* during the GC the finalizers are called in an arbitrary + order so the ArrayBuffer finalizer may have been called */ + if (ta->link.next) { + list_del(&ta->link); + } + JS_FreeValueRT(rt, JS_MKPTR(JS_TAG_OBJECT, ta->buffer)); + js_free_rt(rt, ta); + } +} + +static void js_typed_array_mark(JSRuntime *rt, JSValue val, + JS_MarkFunc *mark_func) +{ + JSObject *p = JS_VALUE_GET_OBJ(val); + JSTypedArray *ta = p->u.typed_array; + if (ta) { + JS_MarkValue(rt, JS_MKPTR(JS_TAG_OBJECT, ta->buffer), mark_func); + } +} + +static JSValue js_dataview_constructor(JSContext *ctx, + JSValue new_target, + int argc, JSValue *argv) +{ + BOOL recompute_len = FALSE; + BOOL track_rab = FALSE; + JSArrayBuffer *abuf; + uint64_t offset; + uint32_t len; + JSValue buffer; + JSValue obj; + JSTypedArray *ta; + JSObject *p; + + buffer = argv[0]; + abuf = js_get_array_buffer(ctx, buffer); + if (!abuf) + return JS_EXCEPTION; + offset = 0; + if (argc > 1) { + if (JS_ToIndex(ctx, &offset, argv[1])) + return JS_EXCEPTION; + } + if (abuf->detached) + return JS_ThrowTypeErrorDetachedArrayBuffer(ctx); + if (offset > abuf->byte_length) + return JS_ThrowRangeError(ctx, "invalid byteOffset"); + len = abuf->byte_length - offset; + if (argc > 2 && !JS_IsUndefined(argv[2])) { + uint64_t l; + if (JS_ToIndex(ctx, &l, argv[2])) + return JS_EXCEPTION; + if (l > len) + return JS_ThrowRangeError(ctx, "invalid byteLength"); + len = l; + } else { + recompute_len = TRUE; + track_rab = array_buffer_is_resizable(abuf); + } + + obj = js_create_from_ctor(ctx, new_target, JS_CLASS_DATAVIEW); + if (JS_IsException(obj)) + return JS_EXCEPTION; + if (abuf->detached) { + /* could have been detached in js_create_from_ctor() */ + JS_ThrowTypeErrorDetachedArrayBuffer(ctx); + goto fail; + } + // RAB could have been resized in js_create_from_ctor() + if (offset > abuf->byte_length) { + goto out_of_bound; + } else if (recompute_len) { + len = abuf->byte_length - offset; + } else if (offset + len > abuf->byte_length) { + out_of_bound: + JS_ThrowRangeError(ctx, "invalid byteOffset or byteLength"); + goto fail; + } + ta = js_malloc(ctx, sizeof(*ta)); + if (!ta) { + fail: + JS_FreeValue(ctx, obj); + return JS_EXCEPTION; + } + p = JS_VALUE_GET_OBJ(obj); + ta->obj = p; + ta->buffer = JS_VALUE_GET_OBJ(js_dup(buffer)); + ta->offset = offset; + ta->length = len; + ta->track_rab = track_rab; + list_add_tail(&ta->link, &abuf->array_list); + p->u.typed_array = ta; + return obj; +} + +// is the DataView out of bounds relative to its parent arraybuffer? +static BOOL dataview_is_oob(JSObject *p) +{ + JSArrayBuffer *abuf; + JSTypedArray *ta; + + assert(p->class_id == JS_CLASS_DATAVIEW); + ta = p->u.typed_array; + abuf = ta->buffer->u.array_buffer; + if (abuf->detached) + return TRUE; + if (ta->offset > abuf->byte_length) + return TRUE; + if (ta->track_rab) + return FALSE; + return (int64_t)ta->offset + ta->length > abuf->byte_length; +} + +static JSObject *get_dataview(JSContext *ctx, JSValue this_val) +{ + JSObject *p; + if (JS_VALUE_GET_TAG(this_val) != JS_TAG_OBJECT) + goto fail; + p = JS_VALUE_GET_OBJ(this_val); + if (p->class_id != JS_CLASS_DATAVIEW) { + fail: + JS_ThrowTypeError(ctx, "not a DataView"); + return NULL; + } + return p; +} + +static JSValue js_dataview_get_buffer(JSContext *ctx, JSValue this_val) +{ + JSObject *p; + JSTypedArray *ta; + p = get_dataview(ctx, this_val); + if (!p) + return JS_EXCEPTION; + ta = p->u.typed_array; + return js_dup(JS_MKPTR(JS_TAG_OBJECT, ta->buffer)); +} + +static JSValue js_dataview_get_byteLength(JSContext *ctx, JSValue this_val) +{ + JSArrayBuffer *abuf; + JSTypedArray *ta; + JSObject *p; + + p = get_dataview(ctx, this_val); + if (!p) + return JS_EXCEPTION; + if (dataview_is_oob(p)) + return JS_ThrowTypeErrorArrayBufferOOB(ctx); + ta = p->u.typed_array; + if (ta->track_rab) { + abuf = ta->buffer->u.array_buffer; + return js_uint32(abuf->byte_length - ta->offset); + } + return js_uint32(ta->length); +} + +static JSValue js_dataview_get_byteOffset(JSContext *ctx, JSValue this_val) +{ + JSTypedArray *ta; + JSObject *p; + + p = get_dataview(ctx, this_val); + if (!p) + return JS_EXCEPTION; + if (dataview_is_oob(p)) + return JS_ThrowTypeErrorArrayBufferOOB(ctx); + ta = p->u.typed_array; + return js_uint32(ta->offset); +} + +static JSValue js_dataview_getValue(JSContext *ctx, + JSValue this_obj, + int argc, JSValue *argv, int class_id) +{ + JSTypedArray *ta; + JSArrayBuffer *abuf; + BOOL littleEndian, is_swap; + int size; + uint8_t *ptr; + uint32_t v; + uint64_t pos; + + ta = JS_GetOpaque2(ctx, this_obj, JS_CLASS_DATAVIEW); + if (!ta) + return JS_EXCEPTION; + size = 1 << typed_array_size_log2(class_id); + if (JS_ToIndex(ctx, &pos, argv[0])) + return JS_EXCEPTION; + littleEndian = argc > 1 && JS_ToBool(ctx, argv[1]); + is_swap = littleEndian ^ !is_be(); + abuf = ta->buffer->u.array_buffer; + if (abuf->detached) + return JS_ThrowTypeErrorDetachedArrayBuffer(ctx); + // order matters: this check should come before the next one + if ((pos + size) > ta->length) + return JS_ThrowRangeError(ctx, "out of bound"); + // test262 expects a TypeError for this and V8, in its infinite wisdom, + // throws a "detached array buffer" exception, but IMO that doesn't make + // sense because the buffer is not in fact detached, it's still there + if ((int64_t)ta->offset + ta->length > abuf->byte_length) + return JS_ThrowTypeError(ctx, "out of bound"); + ptr = abuf->data + ta->offset + pos; + + switch(class_id) { + case JS_CLASS_INT8_ARRAY: + return js_int32(*(int8_t *)ptr); + case JS_CLASS_UINT8_ARRAY: + return js_int32(*(uint8_t *)ptr); + case JS_CLASS_INT16_ARRAY: + v = get_u16(ptr); + if (is_swap) + v = bswap16(v); + return js_int32((int16_t)v); + case JS_CLASS_UINT16_ARRAY: + v = get_u16(ptr); + if (is_swap) + v = bswap16(v); + return js_int32(v); + case JS_CLASS_INT32_ARRAY: + v = get_u32(ptr); + if (is_swap) + v = bswap32(v); + return js_int32(v); + case JS_CLASS_UINT32_ARRAY: + v = get_u32(ptr); + if (is_swap) + v = bswap32(v); + return js_uint32(v); + case JS_CLASS_BIG_INT64_ARRAY: + { + uint64_t v; + v = get_u64(ptr); + if (is_swap) + v = bswap64(v); + return JS_NewBigInt64(ctx, v); + } + break; + case JS_CLASS_BIG_UINT64_ARRAY: + { + uint64_t v; + v = get_u64(ptr); + if (is_swap) + v = bswap64(v); + return JS_NewBigUint64(ctx, v); + } + break; + case JS_CLASS_FLOAT16_ARRAY: + { + uint16_t v; + v = get_u16(ptr); + if (is_swap) + v = bswap16(v); + return js_float64(fromfp16(v)); + } + case JS_CLASS_FLOAT32_ARRAY: + { + union { + float f; + uint32_t i; + } u; + v = get_u32(ptr); + if (is_swap) + v = bswap32(v); + u.i = v; + return js_float64(u.f); + } + case JS_CLASS_FLOAT64_ARRAY: + { + union { + double f; + uint64_t i; + } u; + u.i = get_u64(ptr); + if (is_swap) + u.i = bswap64(u.i); + return js_float64(u.f); + } + default: + abort(); + } + return JS_EXCEPTION; // pacify compiler +} + +static JSValue js_dataview_setValue(JSContext *ctx, + JSValue this_obj, + int argc, JSValue *argv, int class_id) +{ + JSTypedArray *ta; + JSArrayBuffer *abuf; + BOOL littleEndian, is_swap; + int size; + uint8_t *ptr; + uint64_t v64; + uint32_t v; + uint64_t pos; + JSValue val; + + ta = JS_GetOpaque2(ctx, this_obj, JS_CLASS_DATAVIEW); + if (!ta) + return JS_EXCEPTION; + size = 1 << typed_array_size_log2(class_id); + if (JS_ToIndex(ctx, &pos, argv[0])) + return JS_EXCEPTION; + val = argv[1]; + v = 0; /* avoid warning */ + v64 = 0; /* avoid warning */ + if (class_id <= JS_CLASS_UINT32_ARRAY) { + if (JS_ToUint32(ctx, &v, val)) + return JS_EXCEPTION; + } else + if (class_id <= JS_CLASS_BIG_UINT64_ARRAY) { + if (JS_ToBigInt64(ctx, (int64_t *)&v64, val)) + return JS_EXCEPTION; + } else { + double d; + if (JS_ToFloat64(ctx, &d, val)) + return JS_EXCEPTION; + if (class_id == JS_CLASS_FLOAT16_ARRAY) { + v = tofp16(d); + } else if (class_id == JS_CLASS_FLOAT32_ARRAY) { + union { + float f; + uint32_t i; + } u; + u.f = d; + v = u.i; + } else { + JSFloat64Union u; + u.d = d; + v64 = u.u64; + } + } + littleEndian = argc > 2 && JS_ToBool(ctx, argv[2]); + is_swap = littleEndian ^ !is_be(); + abuf = ta->buffer->u.array_buffer; + if (abuf->detached) + return JS_ThrowTypeErrorDetachedArrayBuffer(ctx); + // order matters: this check should come before the next one + if ((pos + size) > ta->length) + return JS_ThrowRangeError(ctx, "out of bound"); + // test262 expects a TypeError for this and V8, in its infinite wisdom, + // throws a "detached array buffer" exception, but IMO that doesn't make + // sense because the buffer is not in fact detached, it's still there + if ((int64_t)ta->offset + ta->length > abuf->byte_length) + return JS_ThrowTypeError(ctx, "out of bound"); + ptr = abuf->data + ta->offset + pos; + + switch(class_id) { + case JS_CLASS_INT8_ARRAY: + case JS_CLASS_UINT8_ARRAY: + *ptr = v; + break; + case JS_CLASS_INT16_ARRAY: + case JS_CLASS_UINT16_ARRAY: + case JS_CLASS_FLOAT16_ARRAY: + if (is_swap) + v = bswap16(v); + put_u16(ptr, v); + break; + case JS_CLASS_INT32_ARRAY: + case JS_CLASS_UINT32_ARRAY: + case JS_CLASS_FLOAT32_ARRAY: + if (is_swap) + v = bswap32(v); + put_u32(ptr, v); + break; + case JS_CLASS_BIG_INT64_ARRAY: + case JS_CLASS_BIG_UINT64_ARRAY: + case JS_CLASS_FLOAT64_ARRAY: + if (is_swap) + v64 = bswap64(v64); + put_u64(ptr, v64); + break; + default: + abort(); + } + return JS_UNDEFINED; +} + +static const JSCFunctionListEntry js_dataview_proto_funcs[] = { + JS_CGETSET_DEF("buffer", js_dataview_get_buffer, NULL ), + JS_CGETSET_DEF("byteLength", js_dataview_get_byteLength, NULL ), + JS_CGETSET_DEF("byteOffset", js_dataview_get_byteOffset, NULL ), + JS_CFUNC_MAGIC_DEF("getInt8", 1, js_dataview_getValue, JS_CLASS_INT8_ARRAY ), + JS_CFUNC_MAGIC_DEF("getUint8", 1, js_dataview_getValue, JS_CLASS_UINT8_ARRAY ), + JS_CFUNC_MAGIC_DEF("getInt16", 1, js_dataview_getValue, JS_CLASS_INT16_ARRAY ), + JS_CFUNC_MAGIC_DEF("getUint16", 1, js_dataview_getValue, JS_CLASS_UINT16_ARRAY ), + JS_CFUNC_MAGIC_DEF("getInt32", 1, js_dataview_getValue, JS_CLASS_INT32_ARRAY ), + JS_CFUNC_MAGIC_DEF("getUint32", 1, js_dataview_getValue, JS_CLASS_UINT32_ARRAY ), + JS_CFUNC_MAGIC_DEF("getBigInt64", 1, js_dataview_getValue, JS_CLASS_BIG_INT64_ARRAY ), + JS_CFUNC_MAGIC_DEF("getBigUint64", 1, js_dataview_getValue, JS_CLASS_BIG_UINT64_ARRAY ), + JS_CFUNC_MAGIC_DEF("getFloat16", 1, js_dataview_getValue, JS_CLASS_FLOAT16_ARRAY ), + JS_CFUNC_MAGIC_DEF("getFloat32", 1, js_dataview_getValue, JS_CLASS_FLOAT32_ARRAY ), + JS_CFUNC_MAGIC_DEF("getFloat64", 1, js_dataview_getValue, JS_CLASS_FLOAT64_ARRAY ), + JS_CFUNC_MAGIC_DEF("setInt8", 2, js_dataview_setValue, JS_CLASS_INT8_ARRAY ), + JS_CFUNC_MAGIC_DEF("setUint8", 2, js_dataview_setValue, JS_CLASS_UINT8_ARRAY ), + JS_CFUNC_MAGIC_DEF("setInt16", 2, js_dataview_setValue, JS_CLASS_INT16_ARRAY ), + JS_CFUNC_MAGIC_DEF("setUint16", 2, js_dataview_setValue, JS_CLASS_UINT16_ARRAY ), + JS_CFUNC_MAGIC_DEF("setInt32", 2, js_dataview_setValue, JS_CLASS_INT32_ARRAY ), + JS_CFUNC_MAGIC_DEF("setUint32", 2, js_dataview_setValue, JS_CLASS_UINT32_ARRAY ), + JS_CFUNC_MAGIC_DEF("setBigInt64", 2, js_dataview_setValue, JS_CLASS_BIG_INT64_ARRAY ), + JS_CFUNC_MAGIC_DEF("setBigUint64", 2, js_dataview_setValue, JS_CLASS_BIG_UINT64_ARRAY ), + JS_CFUNC_MAGIC_DEF("setFloat16", 2, js_dataview_setValue, JS_CLASS_FLOAT16_ARRAY ), + JS_CFUNC_MAGIC_DEF("setFloat32", 2, js_dataview_setValue, JS_CLASS_FLOAT32_ARRAY ), + JS_CFUNC_MAGIC_DEF("setFloat64", 2, js_dataview_setValue, JS_CLASS_FLOAT64_ARRAY ), + JS_PROP_STRING_DEF("[Symbol.toStringTag]", "DataView", JS_PROP_CONFIGURABLE ), +}; + +static JSValue js_new_uint8array(JSContext *ctx, JSValue buffer) +{ + if (JS_IsException(buffer)) + return JS_EXCEPTION; + JSValue obj = js_create_from_ctor(ctx, JS_UNDEFINED, JS_CLASS_UINT8_ARRAY); + if (JS_IsException(obj)) { + JS_FreeValue(ctx, buffer); + return JS_EXCEPTION; + } + JSArrayBuffer *abuf = js_get_array_buffer(ctx, buffer); + assert(abuf != NULL); + if (typed_array_init(ctx, obj, buffer, 0, abuf->byte_length, /*track_rab*/FALSE)) { + // 'buffer' is freed on error above. + JS_FreeValue(ctx, obj); + return JS_EXCEPTION; + } + return obj; +} + +JSValue JS_NewUint8Array(JSContext *ctx, uint8_t *buf, size_t len, + JSFreeArrayBufferDataFunc *free_func, void *opaque, + JS_BOOL is_shared) +{ + JSClassID class_id = + is_shared ? JS_CLASS_SHARED_ARRAY_BUFFER : JS_CLASS_ARRAY_BUFFER; + JSValue buffer = js_array_buffer_constructor3(ctx, JS_UNDEFINED, len, NULL, + class_id, buf, free_func, + opaque, FALSE); + return js_new_uint8array(ctx, buffer); +} + +JSValue JS_NewUint8ArrayCopy(JSContext *ctx, const uint8_t *buf, size_t len) +{ + JSValue buffer = js_array_buffer_constructor3(ctx, JS_UNDEFINED, len, NULL, + JS_CLASS_ARRAY_BUFFER, + (uint8_t *)buf, + js_array_buffer_free, NULL, + TRUE); + return js_new_uint8array(ctx, buffer); +} + +JS_BOOL JS_IsUint8Array(JSValue obj) { + return JS_GetClassID(obj) == JS_CLASS_UINT8_ARRAY; +} + +/* Atomics */ +#ifdef CONFIG_ATOMICS + +typedef enum AtomicsOpEnum { + ATOMICS_OP_ADD, + ATOMICS_OP_AND, + ATOMICS_OP_OR, + ATOMICS_OP_SUB, + ATOMICS_OP_XOR, + ATOMICS_OP_EXCHANGE, + ATOMICS_OP_COMPARE_EXCHANGE, + ATOMICS_OP_LOAD, +} AtomicsOpEnum; + +static void *js_atomics_get_ptr(JSContext *ctx, + JSArrayBuffer **pabuf, + int *psize_log2, JSClassID *pclass_id, + JSValue obj, JSValue idx_val, + int is_waitable) +{ + JSObject *p; + JSTypedArray *ta; + JSArrayBuffer *abuf; + void *ptr; + uint64_t idx; + BOOL err; + int size_log2; + + if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT) + goto fail; + p = JS_VALUE_GET_OBJ(obj); + if (is_waitable) + err = (p->class_id != JS_CLASS_INT32_ARRAY && + p->class_id != JS_CLASS_BIG_INT64_ARRAY); + else + err = !(p->class_id >= JS_CLASS_INT8_ARRAY && + p->class_id <= JS_CLASS_BIG_UINT64_ARRAY); + if (err) { + fail: + JS_ThrowTypeError(ctx, "integer TypedArray expected"); + return NULL; + } + ta = p->u.typed_array; + abuf = ta->buffer->u.array_buffer; + if (!abuf->shared) { + if (is_waitable == 2) { + JS_ThrowTypeError(ctx, "not a SharedArrayBuffer TypedArray"); + return NULL; + } + if (abuf->detached) { + JS_ThrowTypeErrorDetachedArrayBuffer(ctx); + return NULL; + } + } + if (JS_ToIndex(ctx, &idx, idx_val)) { + return NULL; + } + /* if the array buffer is detached, p->u.array.count = 0 */ + if (idx >= p->u.array.count) { + JS_ThrowRangeError(ctx, "out-of-bound access"); + return NULL; + } + size_log2 = typed_array_size_log2(p->class_id); + ptr = p->u.array.u.uint8_ptr + ((uintptr_t)idx << size_log2); + if (pabuf) + *pabuf = abuf; + if (psize_log2) + *psize_log2 = size_log2; + if (pclass_id) + *pclass_id = p->class_id; + return ptr; +} + +static JSValue js_atomics_op(JSContext *ctx, + JSValue this_obj, + int argc, JSValue *argv, int op) +{ + int size_log2; + uint64_t v, a, rep_val; + void *ptr; + JSValue ret; + JSClassID class_id; + JSArrayBuffer *abuf; + + ptr = js_atomics_get_ptr(ctx, &abuf, &size_log2, &class_id, + argv[0], argv[1], 0); + if (!ptr) + return JS_EXCEPTION; + rep_val = 0; + if (op == ATOMICS_OP_LOAD) { + v = 0; + } else { + if (size_log2 == 3) { + int64_t v64; + if (JS_ToBigInt64(ctx, &v64, argv[2])) + return JS_EXCEPTION; + v = v64; + if (op == ATOMICS_OP_COMPARE_EXCHANGE) { + if (JS_ToBigInt64(ctx, &v64, argv[3])) + return JS_EXCEPTION; + rep_val = v64; + } + } else { + uint32_t v32; + if (JS_ToUint32(ctx, &v32, argv[2])) + return JS_EXCEPTION; + v = v32; + if (op == ATOMICS_OP_COMPARE_EXCHANGE) { + if (JS_ToUint32(ctx, &v32, argv[3])) + return JS_EXCEPTION; + rep_val = v32; + } + } + if (abuf->detached) + return JS_ThrowTypeErrorDetachedArrayBuffer(ctx); + } + + switch(op | (size_log2 << 3)) { + +#define OP(op_name, func_name) \ + case ATOMICS_OP_ ## op_name | (0 << 3): \ + a = func_name((_Atomic uint8_t *)ptr, v); \ + break; \ + case ATOMICS_OP_ ## op_name | (1 << 3): \ + a = func_name((_Atomic uint16_t *)ptr, v); \ + break; \ + case ATOMICS_OP_ ## op_name | (2 << 3): \ + a = func_name((_Atomic uint32_t *)ptr, v); \ + break; \ + case ATOMICS_OP_ ## op_name | (3 << 3): \ + a = func_name((_Atomic uint64_t *)ptr, v); \ + break; + OP(ADD, atomic_fetch_add) + OP(AND, atomic_fetch_and) + OP(OR, atomic_fetch_or) + OP(SUB, atomic_fetch_sub) + OP(XOR, atomic_fetch_xor) + OP(EXCHANGE, atomic_exchange) +#undef OP + + case ATOMICS_OP_LOAD | (0 << 3): + a = atomic_load((_Atomic uint8_t *)ptr); + break; + case ATOMICS_OP_LOAD | (1 << 3): + a = atomic_load((_Atomic uint16_t *)ptr); + break; + case ATOMICS_OP_LOAD | (2 << 3): + a = atomic_load((_Atomic uint32_t *)ptr); + break; + case ATOMICS_OP_LOAD | (3 << 3): + a = atomic_load((_Atomic uint64_t *)ptr); + break; + case ATOMICS_OP_COMPARE_EXCHANGE | (0 << 3): + { + uint8_t v1 = v; + atomic_compare_exchange_strong((_Atomic uint8_t *)ptr, &v1, rep_val); + a = v1; + } + break; + case ATOMICS_OP_COMPARE_EXCHANGE | (1 << 3): + { + uint16_t v1 = v; + atomic_compare_exchange_strong((_Atomic uint16_t *)ptr, &v1, rep_val); + a = v1; + } + break; + case ATOMICS_OP_COMPARE_EXCHANGE | (2 << 3): + { + uint32_t v1 = v; + atomic_compare_exchange_strong((_Atomic uint32_t *)ptr, &v1, rep_val); + a = v1; + } + break; + case ATOMICS_OP_COMPARE_EXCHANGE | (3 << 3): + { + uint64_t v1 = v; + atomic_compare_exchange_strong((_Atomic uint64_t *)ptr, &v1, rep_val); + a = v1; + } + break; + default: + abort(); + } + + switch(class_id) { + case JS_CLASS_INT8_ARRAY: + a = (int8_t)a; + goto done; + case JS_CLASS_UINT8_ARRAY: + a = (uint8_t)a; + goto done; + case JS_CLASS_INT16_ARRAY: + a = (int16_t)a; + goto done; + case JS_CLASS_UINT16_ARRAY: + a = (uint16_t)a; + goto done; + case JS_CLASS_INT32_ARRAY: + done: + ret = js_int32(a); + break; + case JS_CLASS_UINT32_ARRAY: + ret = js_uint32(a); + break; + case JS_CLASS_BIG_INT64_ARRAY: + ret = JS_NewBigInt64(ctx, a); + break; + case JS_CLASS_BIG_UINT64_ARRAY: + ret = JS_NewBigUint64(ctx, a); + break; + default: + abort(); + } + return ret; +} + +static JSValue js_atomics_store(JSContext *ctx, + JSValue this_obj, + int argc, JSValue *argv) +{ + int size_log2; + void *ptr; + JSValue ret; + JSArrayBuffer *abuf; + + ptr = js_atomics_get_ptr(ctx, &abuf, &size_log2, NULL, + argv[0], argv[1], 0); + if (!ptr) + return JS_EXCEPTION; + if (size_log2 == 3) { + int64_t v64; + ret = JS_ToBigIntValueFree(ctx, js_dup(argv[2])); + if (JS_IsException(ret)) + return ret; + if (JS_ToBigInt64(ctx, &v64, ret)) { + JS_FreeValue(ctx, ret); + return JS_EXCEPTION; + } + if (abuf->detached) + return JS_ThrowTypeErrorDetachedArrayBuffer(ctx); + atomic_store((_Atomic uint64_t *)ptr, v64); + } else { + uint32_t v; + /* XXX: spec, would be simpler to return the written value */ + ret = JS_ToIntegerFree(ctx, js_dup(argv[2])); + if (JS_IsException(ret)) + return ret; + if (JS_ToUint32(ctx, &v, ret)) { + JS_FreeValue(ctx, ret); + return JS_EXCEPTION; + } + if (abuf->detached) + return JS_ThrowTypeErrorDetachedArrayBuffer(ctx); + switch(size_log2) { + case 0: + atomic_store((_Atomic uint8_t *)ptr, v); + break; + case 1: + atomic_store((_Atomic uint16_t *)ptr, v); + break; + case 2: + atomic_store((_Atomic uint32_t *)ptr, v); + break; + default: + abort(); + } + } + return ret; +} + +static JSValue js_atomics_isLockFree(JSContext *ctx, + JSValue this_obj, + int argc, JSValue *argv) +{ + int v, ret; + if (JS_ToInt32Sat(ctx, &v, argv[0])) + return JS_EXCEPTION; + ret = (v == 1 || v == 2 || v == 4 || v == 8); + return js_bool(ret); +} + +typedef struct JSAtomicsWaiter { + struct list_head link; + BOOL linked; + js_cond_t cond; + int32_t *ptr; +} JSAtomicsWaiter; + +static js_once_t js_atomics_once = JS_ONCE_INIT; +static js_mutex_t js_atomics_mutex; +static struct list_head js_atomics_waiter_list = + LIST_HEAD_INIT(js_atomics_waiter_list); + +// no-op: Atomics.pause() is not allowed to block or yield to another +// thread, only to hint the CPU that it should back off for a bit; +// the amount of work we do here is a good enough substitute +static JSValue js_atomics_pause(JSContext *ctx, JSValue this_obj, + int argc, JSValue *argv) +{ + double d; + + if (argc > 0) { + switch (JS_VALUE_GET_TAG(argv[0])) { + case JS_TAG_FLOAT64: // accepted if and only if fraction == 0.0 + d = JS_VALUE_GET_FLOAT64(argv[0]); + if (isfinite(d)) + if (0 == modf(d, &d)) + break; + // fallthru + default: + return JS_ThrowTypeError(ctx, "not an integral number"); + case JS_TAG_UNDEFINED: + case JS_TAG_INT: + break; + } + } + return JS_UNDEFINED; +} + +static JSValue js_atomics_wait(JSContext *ctx, + JSValue this_obj, + int argc, JSValue *argv) +{ + int64_t v; + int32_t v32; + void *ptr; + int64_t timeout; + JSAtomicsWaiter waiter_s, *waiter; + int ret, size_log2, res; + double d; + + ptr = js_atomics_get_ptr(ctx, NULL, &size_log2, NULL, + argv[0], argv[1], 2); + if (!ptr) + return JS_EXCEPTION; + if (size_log2 == 3) { + if (JS_ToBigInt64(ctx, &v, argv[2])) + return JS_EXCEPTION; + } else { + if (JS_ToInt32(ctx, &v32, argv[2])) + return JS_EXCEPTION; + v = v32; + } + if (JS_ToFloat64(ctx, &d, argv[3])) + return JS_EXCEPTION; + if (isnan(d) || d >= 0x1p63) + timeout = INT64_MAX; + else if (d < 0) + timeout = 0; + else + timeout = (int64_t)d; + if (!ctx->rt->can_block) + return JS_ThrowTypeError(ctx, "cannot block in this thread"); + + /* XXX: inefficient if large number of waiters, should hash on + 'ptr' value */ + js_mutex_lock(&js_atomics_mutex); + if (size_log2 == 3) { + res = *(int64_t *)ptr != v; + } else { + res = *(int32_t *)ptr != v; + } + if (res) { + js_mutex_unlock(&js_atomics_mutex); + return JS_AtomToString(ctx, JS_ATOM_not_equal); + } + + waiter = &waiter_s; + waiter->ptr = ptr; + js_cond_init(&waiter->cond); + waiter->linked = TRUE; + list_add_tail(&waiter->link, &js_atomics_waiter_list); + + if (timeout == INT64_MAX) { + js_cond_wait(&waiter->cond, &js_atomics_mutex); + ret = 0; + } else { + ret = js_cond_timedwait(&waiter->cond, &js_atomics_mutex, timeout * 1e6 /* to ns */); + } + if (waiter->linked) + list_del(&waiter->link); + js_mutex_unlock(&js_atomics_mutex); + js_cond_destroy(&waiter->cond); + if (ret == -1) { + return JS_AtomToString(ctx, JS_ATOM_timed_out); + } else { + return JS_AtomToString(ctx, JS_ATOM_ok); + } +} + +static JSValue js_atomics_notify(JSContext *ctx, + JSValue this_obj, + int argc, JSValue *argv) +{ + struct list_head *el, *el1, waiter_list; + int32_t count, n; + void *ptr; + JSAtomicsWaiter *waiter; + JSArrayBuffer *abuf; + + ptr = js_atomics_get_ptr(ctx, &abuf, NULL, NULL, argv[0], argv[1], 1); + if (!ptr) + return JS_EXCEPTION; + + if (JS_IsUndefined(argv[2])) { + count = INT32_MAX; + } else { + if (JS_ToInt32Clamp(ctx, &count, argv[2], 0, INT32_MAX, 0)) + return JS_EXCEPTION; + } + if (abuf->detached) + return JS_ThrowTypeErrorDetachedArrayBuffer(ctx); + + n = 0; + if (abuf->shared && count > 0) { + js_mutex_lock(&js_atomics_mutex); + init_list_head(&waiter_list); + list_for_each_safe(el, el1, &js_atomics_waiter_list) { + waiter = list_entry(el, JSAtomicsWaiter, link); + if (waiter->ptr == ptr) { + list_del(&waiter->link); + waiter->linked = FALSE; + list_add_tail(&waiter->link, &waiter_list); + n++; + if (n >= count) + break; + } + } + list_for_each(el, &waiter_list) { + waiter = list_entry(el, JSAtomicsWaiter, link); + js_cond_signal(&waiter->cond); + } + js_mutex_unlock(&js_atomics_mutex); + } + return js_int32(n); +} + +static const JSCFunctionListEntry js_atomics_funcs[] = { + JS_CFUNC_MAGIC_DEF("add", 3, js_atomics_op, ATOMICS_OP_ADD ), + JS_CFUNC_MAGIC_DEF("and", 3, js_atomics_op, ATOMICS_OP_AND ), + JS_CFUNC_MAGIC_DEF("or", 3, js_atomics_op, ATOMICS_OP_OR ), + JS_CFUNC_MAGIC_DEF("sub", 3, js_atomics_op, ATOMICS_OP_SUB ), + JS_CFUNC_MAGIC_DEF("xor", 3, js_atomics_op, ATOMICS_OP_XOR ), + JS_CFUNC_MAGIC_DEF("exchange", 3, js_atomics_op, ATOMICS_OP_EXCHANGE ), + JS_CFUNC_MAGIC_DEF("compareExchange", 4, js_atomics_op, ATOMICS_OP_COMPARE_EXCHANGE ), + JS_CFUNC_MAGIC_DEF("load", 2, js_atomics_op, ATOMICS_OP_LOAD ), + JS_CFUNC_DEF("store", 3, js_atomics_store ), + JS_CFUNC_DEF("isLockFree", 1, js_atomics_isLockFree ), + JS_CFUNC_DEF("pause", 0, js_atomics_pause ), + JS_CFUNC_DEF("wait", 4, js_atomics_wait ), + JS_CFUNC_DEF("notify", 3, js_atomics_notify ), + JS_PROP_STRING_DEF("[Symbol.toStringTag]", "Atomics", JS_PROP_CONFIGURABLE ), +}; + +static const JSCFunctionListEntry js_atomics_obj[] = { + JS_OBJECT_DEF("Atomics", js_atomics_funcs, countof(js_atomics_funcs), JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE ), +}; + +static void js__atomics_init(void) { + js_mutex_init(&js_atomics_mutex); +} + +/* TODO(saghul) make this public and not dependent on typed arrays? */ +void JS_AddIntrinsicAtomics(JSContext *ctx) +{ + js_once(&js_atomics_once, js__atomics_init); + + /* add Atomics as autoinit object */ + JS_SetPropertyFunctionList(ctx, ctx->global_obj, js_atomics_obj, countof(js_atomics_obj)); +} + +#endif /* CONFIG_ATOMICS */ + +void JS_AddIntrinsicTypedArrays(JSContext *ctx) +{ + JSValue typed_array_base_proto, typed_array_base_func; + JSValue array_buffer_func, shared_array_buffer_func; + int i; + + ctx->class_proto[JS_CLASS_ARRAY_BUFFER] = JS_NewObject(ctx); + JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_ARRAY_BUFFER], + js_array_buffer_proto_funcs, + countof(js_array_buffer_proto_funcs)); + + array_buffer_func = JS_NewGlobalCConstructorOnly(ctx, "ArrayBuffer", + js_array_buffer_constructor, 1, + ctx->class_proto[JS_CLASS_ARRAY_BUFFER]); + JS_SetPropertyFunctionList(ctx, array_buffer_func, + js_array_buffer_funcs, + countof(js_array_buffer_funcs)); + + ctx->class_proto[JS_CLASS_SHARED_ARRAY_BUFFER] = JS_NewObject(ctx); + JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_SHARED_ARRAY_BUFFER], + js_shared_array_buffer_proto_funcs, + countof(js_shared_array_buffer_proto_funcs)); + + shared_array_buffer_func = JS_NewGlobalCConstructorOnly(ctx, "SharedArrayBuffer", + js_shared_array_buffer_constructor, 1, + ctx->class_proto[JS_CLASS_SHARED_ARRAY_BUFFER]); + JS_SetPropertyFunctionList(ctx, shared_array_buffer_func, + js_shared_array_buffer_funcs, + countof(js_shared_array_buffer_funcs)); + + typed_array_base_proto = JS_NewObject(ctx); + JS_SetPropertyFunctionList(ctx, typed_array_base_proto, + js_typed_array_base_proto_funcs, + countof(js_typed_array_base_proto_funcs)); + + /* TypedArray.prototype.toString must be the same object as Array.prototype.toString */ + JSValue obj = JS_GetProperty(ctx, ctx->class_proto[JS_CLASS_ARRAY], JS_ATOM_toString); + /* XXX: should use alias method in JSCFunctionListEntry */ //@@@ + JS_DefinePropertyValue(ctx, typed_array_base_proto, JS_ATOM_toString, obj, + JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE); + + typed_array_base_func = JS_NewCFunction(ctx, js_typed_array_base_constructor, + "TypedArray", 0); + JS_SetPropertyFunctionList(ctx, typed_array_base_func, + js_typed_array_base_funcs, + countof(js_typed_array_base_funcs)); + JS_SetConstructor(ctx, typed_array_base_func, typed_array_base_proto); + + /* Used to squelch a -Wcast-function-type warning. */ + JSCFunctionType ft = { .generic_magic = js_typed_array_constructor }; + for(i = JS_CLASS_UINT8C_ARRAY; i < JS_CLASS_UINT8C_ARRAY + JS_TYPED_ARRAY_COUNT; i++) { + JSValue func_obj; + char buf[ATOM_GET_STR_BUF_SIZE]; + const char *name; + + ctx->class_proto[i] = JS_NewObjectProto(ctx, typed_array_base_proto); + JS_DefinePropertyValueStr(ctx, ctx->class_proto[i], + "BYTES_PER_ELEMENT", + js_int32(1 << typed_array_size_log2(i)), + 0); + name = JS_AtomGetStr(ctx, buf, sizeof(buf), + JS_ATOM_Uint8ClampedArray + i - JS_CLASS_UINT8C_ARRAY); + func_obj = JS_NewCFunction3(ctx, ft.generic, + name, 3, JS_CFUNC_constructor_magic, i, + typed_array_base_func); + JS_NewGlobalCConstructor2(ctx, func_obj, name, ctx->class_proto[i]); + JS_DefinePropertyValueStr(ctx, func_obj, + "BYTES_PER_ELEMENT", + js_int32(1 << typed_array_size_log2(i)), + 0); + } + JS_FreeValue(ctx, typed_array_base_proto); + JS_FreeValue(ctx, typed_array_base_func); + + /* DataView */ + ctx->class_proto[JS_CLASS_DATAVIEW] = JS_NewObject(ctx); + JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_DATAVIEW], + js_dataview_proto_funcs, + countof(js_dataview_proto_funcs)); + JS_NewGlobalCConstructorOnly(ctx, "DataView", + js_dataview_constructor, 1, + ctx->class_proto[JS_CLASS_DATAVIEW]); + /* Atomics */ +#ifdef CONFIG_ATOMICS + JS_AddIntrinsicAtomics(ctx); +#endif +} + +/* Performance */ + +static double js__now_ms(void) +{ + return js__hrtime_ns() / 1e6; +} + +static JSValue js_perf_now(JSContext *ctx, JSValue this_val, int argc, JSValue *argv) +{ + return js_float64(js__now_ms() - ctx->time_origin); +} + +static const JSCFunctionListEntry js_perf_proto_funcs[] = { + JS_CFUNC_DEF2("now", 0, js_perf_now, JS_PROP_ENUMERABLE), +}; + +void JS_AddPerformance(JSContext *ctx) +{ + ctx->time_origin = js__now_ms(); + + JSValue performance = JS_NewObject(ctx); + JS_SetPropertyFunctionList(ctx, performance, js_perf_proto_funcs, countof(js_perf_proto_funcs)); + JS_DefinePropertyValueStr(ctx, performance, "timeOrigin", + js_float64(ctx->time_origin), + JS_PROP_ENUMERABLE); + JS_DefinePropertyValueStr(ctx, ctx->global_obj, "performance", + js_dup(performance), + JS_PROP_WRITABLE | JS_PROP_ENUMERABLE | JS_PROP_CONFIGURABLE); + JS_FreeValue(ctx, performance); +} + +/* Equality comparisons and sameness */ +int JS_IsEqual(JSContext *ctx, JSValue op1, JSValue op2) +{ + JSValue sp[2] = { js_dup(op1), js_dup(op2) }; + if (js_eq_slow(ctx, endof(sp), 0)) + return -1; + return JS_VALUE_GET_BOOL(sp[0]); +} + +JS_BOOL JS_IsStrictEqual(JSContext *ctx, JSValue op1, JSValue op2) +{ + return js_strict_eq2(ctx, js_dup(op1), js_dup(op2), JS_EQ_STRICT); +} + +JS_BOOL JS_IsSameValue(JSContext *ctx, JSValue op1, JSValue op2) +{ + return js_same_value(ctx, op1, op2); +} + +JS_BOOL JS_IsSameValueZero(JSContext *ctx, JSValue op1, JSValue op2) +{ + return js_same_value_zero(ctx, op1, op2); +} + +/* WeakRef */ + +typedef struct JSWeakRefData { + JSValue target; + JSValue obj; +} JSWeakRefData; + +static JSWeakRefData js_weakref_sentinel; + +static void js_weakref_finalizer(JSRuntime *rt, JSValue val) +{ + JSWeakRefData *wrd = JS_GetOpaque(val, JS_CLASS_WEAK_REF); + if (!wrd || wrd == &js_weakref_sentinel) + return; + + /* Delete weak ref */ + JSWeakRefRecord **pwr, *wr; + + pwr = get_first_weak_ref(wrd->target); + for(;;) { + wr = *pwr; + assert(wr != NULL); + if (wr->kind == JS_WEAK_REF_KIND_WEAK_REF && wr->u.weak_ref_data == wrd) + break; + pwr = &wr->next_weak_ref; + } + *pwr = wr->next_weak_ref; + js_free_rt(rt, wrd); + js_free_rt(rt, wr); +} + +static JSValue js_weakref_constructor(JSContext *ctx, JSValue new_target, int argc, JSValue *argv) +{ + if (JS_IsUndefined(new_target)) + return JS_ThrowTypeError(ctx, "constructor requires 'new'"); + JSValue arg = argv[0]; + if (!is_valid_weakref_target(arg)) + return JS_ThrowTypeError(ctx, "invalid target"); + // TODO(saghul): short-circuit if the refcount is 1? + JSValue obj = js_create_from_ctor(ctx, new_target, JS_CLASS_WEAK_REF); + if (JS_IsException(obj)) + return JS_EXCEPTION; + JSWeakRefData *wrd = js_malloc(ctx, sizeof(*wrd)); + if (!wrd) { + JS_FreeValue(ctx, obj); + return JS_EXCEPTION; + } + JSWeakRefRecord *wr = js_malloc(ctx, sizeof(*wr)); + if (!wr) { + JS_FreeValue(ctx, obj); + js_free(ctx, wrd); + return JS_EXCEPTION; + } + wrd->target = arg; + wrd->obj = obj; + wr->kind = JS_WEAK_REF_KIND_WEAK_REF; + wr->u.weak_ref_data = wrd; + insert_weakref_record(arg, wr); + + JS_SetOpaqueInternal(obj, wrd); + return obj; +} + +static JSValue js_weakref_deref(JSContext *ctx, JSValue this_val, int argc, JSValue *argv) +{ + JSWeakRefData *wrd = JS_GetOpaque2(ctx, this_val, JS_CLASS_WEAK_REF); + if (!wrd) + return JS_EXCEPTION; + if (wrd == &js_weakref_sentinel) + return JS_UNDEFINED; + return js_dup(wrd->target); +} + +static const JSCFunctionListEntry js_weakref_proto_funcs[] = { + JS_CFUNC_DEF("deref", 0, js_weakref_deref ), + JS_PROP_STRING_DEF("[Symbol.toStringTag]", "WeakRef", JS_PROP_CONFIGURABLE ), +}; + +static const JSClassShortDef js_weakref_class_def[] = { + { JS_ATOM_WeakRef, js_weakref_finalizer, NULL }, /* JS_CLASS_WEAK_REF */ +}; + +typedef struct JSFinRecEntry { + struct list_head link; + JSValue obj; + JSValue target; + JSValue held_val; + JSValue token; +} JSFinRecEntry; + +typedef struct JSFinalizationRegistryData { + struct list_head entries; + JSContext *ctx; + JSValue cb; +} JSFinalizationRegistryData; + +static void delete_finrec_weakref(JSRuntime *rt, JSFinRecEntry *fre) +{ + JSWeakRefRecord **pwr, *wr; + + pwr = get_first_weak_ref(fre->target); + for(;;) { + wr = *pwr; + assert(wr != NULL); + if (wr->kind == JS_WEAK_REF_KIND_FINALIZATION_REGISTRY_ENTRY && wr->u.fin_rec_entry == fre) + break; + pwr = &wr->next_weak_ref; + } + *pwr = wr->next_weak_ref; + js_free_rt(rt, wr); +} + +static void js_finrec_finalizer(JSRuntime *rt, JSValue val) +{ + JSFinalizationRegistryData *frd = JS_GetOpaque(val, JS_CLASS_FINALIZATION_REGISTRY); + if (frd) { + struct list_head *el, *el1; + /* first pass to remove the weak ref entries and avoid having them modified + by freeing a token / held value. */ + list_for_each_safe(el, el1, &frd->entries) { + JSFinRecEntry *fre = list_entry(el, JSFinRecEntry, link); + delete_finrec_weakref(rt, fre); + } + /* second pass to actually free all objects. */ + list_for_each_safe(el, el1, &frd->entries) { + JSFinRecEntry *fre = list_entry(el, JSFinRecEntry, link); + list_del(&fre->link); + JS_FreeValueRT(rt, fre->held_val); + JS_FreeValueRT(rt, fre->token); + js_free_rt(rt, fre); + } + JS_FreeValueRT(rt, frd->cb); + js_free_rt(rt, frd); + } +} + +static void js_finrec_mark(JSRuntime *rt, JSValue val, JS_MarkFunc *mark_func) +{ + JSFinalizationRegistryData *frd = JS_GetOpaque(val, JS_CLASS_FINALIZATION_REGISTRY); + if (frd) { + JS_MarkValue(rt, frd->cb, mark_func); + struct list_head *el; + list_for_each(el, &frd->entries) { + JSFinRecEntry *fre = list_entry(el, JSFinRecEntry, link); + JS_MarkValue(rt, fre->held_val, mark_func); + JS_MarkValue(rt, fre->token, mark_func); + } + } +} + +static JSValue js_finrec_constructor(JSContext *ctx, JSValue new_target, int argc, JSValue *argv) +{ + if (JS_IsUndefined(new_target)) + return JS_ThrowTypeError(ctx, "constructor requires 'new'"); + JSValue cb = argv[0]; + if (!JS_IsFunction(ctx, cb)) + return JS_ThrowTypeError(ctx, "argument must be a function"); + + JSValue obj = js_create_from_ctor(ctx, new_target, JS_CLASS_FINALIZATION_REGISTRY); + if (JS_IsException(obj)) + return JS_EXCEPTION; + JSFinalizationRegistryData *frd = js_malloc(ctx, sizeof(*frd)); + if (!frd) { + JS_FreeValue(ctx, obj); + return JS_EXCEPTION; + } + init_list_head(&frd->entries); + frd->ctx = ctx; + frd->cb = js_dup(cb); + JS_SetOpaqueInternal(obj, frd); + return obj; +} + +static JSValue js_finrec_register(JSContext *ctx, JSValue this_val, int argc, JSValue *argv) +{ + JSFinalizationRegistryData *frd = JS_GetOpaque2(ctx, this_val, JS_CLASS_FINALIZATION_REGISTRY); + if (!frd) + return JS_EXCEPTION; + + JSValue target = argv[0]; + JSValue held_val = argv[1]; + // The function length needs to return 2, so the 3rd argument won't be initialized. + JSValue token = argc > 2 ? argv[2] : JS_UNDEFINED; + + if (!is_valid_weakref_target(target)) + return JS_ThrowTypeError(ctx, "invalid target"); + if (js_same_value(ctx, target, this_val)) + return JS_UNDEFINED; + if (!JS_IsUndefined(held_val) && js_same_value(ctx, target, held_val)) + return JS_ThrowTypeError(ctx, "held value cannot be the target"); + if (!JS_IsUndefined(token) && !is_valid_weakref_target(token)) + return JS_ThrowTypeError(ctx, "invalid unregister token"); + + JSFinRecEntry *fre = js_malloc(ctx, sizeof(*fre)); + if (!fre) + return JS_EXCEPTION; + JSWeakRefRecord *wr = js_malloc(ctx, sizeof(*wr)); + if (!wr) { + js_free(ctx, fre); + return JS_EXCEPTION; + } + fre->obj = this_val; + fre->target = target; + fre->held_val = js_dup(held_val); + fre->token = js_dup(token); + list_add_tail(&fre->link, &frd->entries); + wr->kind = JS_WEAK_REF_KIND_FINALIZATION_REGISTRY_ENTRY; + wr->u.fin_rec_entry = fre; + insert_weakref_record(target, wr); + + return JS_UNDEFINED; +} + +static JSValue js_finrec_unregister(JSContext *ctx, JSValue this_val, int argc, JSValue *argv) +{ + JSFinalizationRegistryData *frd = JS_GetOpaque2(ctx, this_val, JS_CLASS_FINALIZATION_REGISTRY); + if (!frd) + return JS_EXCEPTION; + + JSValue token = argv[0]; + if (!is_valid_weakref_target(token)) + return JS_ThrowTypeError(ctx, "invalid unregister token"); + + struct list_head *el, *el1; + BOOL removed = FALSE; + list_for_each_safe(el, el1, &frd->entries) { + JSFinRecEntry *fre = list_entry(el, JSFinRecEntry, link); + if (js_same_value(ctx, fre->token, token)) { + list_del(&fre->link); + delete_finrec_weakref(ctx->rt, fre); + JS_FreeValue(ctx, fre->held_val); + JS_FreeValue(ctx, fre->token); + js_free(ctx, fre); + removed = TRUE; + } + } + + return js_bool(removed); +} + +static const JSCFunctionListEntry js_finrec_proto_funcs[] = { + JS_CFUNC_DEF("register", 2, js_finrec_register ), + JS_CFUNC_DEF("unregister", 1, js_finrec_unregister ), + JS_PROP_STRING_DEF("[Symbol.toStringTag]", "FinalizationRegistry", JS_PROP_CONFIGURABLE ), +}; + +static const JSClassShortDef js_finrec_class_def[] = { + { JS_ATOM_FinalizationRegistry, js_finrec_finalizer, js_finrec_mark }, /* JS_CLASS_FINALIZATION_REGISTRY */ +}; + +static JSValue js_finrec_job(JSContext *ctx, int argc, JSValue *argv) +{ + return JS_Call(ctx, argv[0], JS_UNDEFINED, 1, &argv[1]); +} + +void JS_AddIntrinsicWeakRef(JSContext *ctx) +{ + JSRuntime *rt = ctx->rt; + + /* WeakRef */ + if (!JS_IsRegisteredClass(rt, JS_CLASS_WEAK_REF)) { + init_class_range(rt, js_weakref_class_def, JS_CLASS_WEAK_REF, + countof(js_weakref_class_def)); + } + ctx->class_proto[JS_CLASS_WEAK_REF] = JS_NewObject(ctx); + JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_WEAK_REF], + js_weakref_proto_funcs, + countof(js_weakref_proto_funcs)); + JS_NewGlobalCConstructor(ctx, "WeakRef", js_weakref_constructor, 1, ctx->class_proto[JS_CLASS_WEAK_REF]); + + /* FinalizationRegistry */ + if (!JS_IsRegisteredClass(rt, JS_CLASS_FINALIZATION_REGISTRY)) { + init_class_range(rt, js_finrec_class_def, JS_CLASS_FINALIZATION_REGISTRY, + countof(js_finrec_class_def)); + } + ctx->class_proto[JS_CLASS_FINALIZATION_REGISTRY] = JS_NewObject(ctx); + JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_FINALIZATION_REGISTRY], + js_finrec_proto_funcs, + countof(js_finrec_proto_funcs)); + JS_NewGlobalCConstructor(ctx, "FinalizationRegistry", js_finrec_constructor, 1, ctx->class_proto[JS_CLASS_FINALIZATION_REGISTRY]); +} + +static void reset_weak_ref(JSRuntime *rt, JSWeakRefRecord **first_weak_ref) +{ + JSWeakRefRecord *wr, *wr_next; + JSWeakRefData *wrd; + JSMapRecord *mr; + JSMapState *s; + JSFinRecEntry *fre; + + /* first pass to remove the records from the WeakMap/WeakSet + lists */ + for(wr = *first_weak_ref; wr != NULL; wr = wr->next_weak_ref) { + switch(wr->kind) { + case JS_WEAK_REF_KIND_MAP: + mr = wr->u.map_record; + s = mr->map; + assert(s->is_weak); + assert(!mr->empty); /* no iterator on WeakMap/WeakSet */ + list_del(&mr->hash_link); + list_del(&mr->link); + break; + case JS_WEAK_REF_KIND_WEAK_REF: + wrd = wr->u.weak_ref_data; + wrd->target = JS_UNDEFINED; + break; + case JS_WEAK_REF_KIND_FINALIZATION_REGISTRY_ENTRY: + fre = wr->u.fin_rec_entry; + list_del(&fre->link); + break; + default: + abort(); + } + } + + /* second pass to free the values to avoid modifying the weak + reference list while traversing it. */ + for(wr = *first_weak_ref; wr != NULL; wr = wr_next) { + wr_next = wr->next_weak_ref; + switch(wr->kind) { + case JS_WEAK_REF_KIND_MAP: + mr = wr->u.map_record; + JS_FreeValueRT(rt, mr->value); + js_free_rt(rt, mr); + break; + case JS_WEAK_REF_KIND_WEAK_REF: + wrd = wr->u.weak_ref_data; + JS_SetOpaqueInternal(wrd->obj, &js_weakref_sentinel); + js_free_rt(rt, wrd); + break; + case JS_WEAK_REF_KIND_FINALIZATION_REGISTRY_ENTRY: { + fre = wr->u.fin_rec_entry; + JSFinalizationRegistryData *frd = JS_GetOpaque(fre->obj, JS_CLASS_FINALIZATION_REGISTRY); + assert(frd != NULL); + /** + * During the GC sweep phase the held object might be collected first. + */ + if (!rt->in_free && (!JS_IsObject(fre->held_val) || JS_IsLiveObject(rt, fre->held_val))) { + JSValue args[2]; + args[0] = frd->cb; + args[1] = fre->held_val; + JS_EnqueueJob(frd->ctx, js_finrec_job, 2, args); + } + JS_FreeValueRT(rt, fre->held_val); + JS_FreeValueRT(rt, fre->token); + js_free_rt(rt, fre); + break; + } + default: + abort(); + } + js_free_rt(rt, wr); + } + + *first_weak_ref = NULL; /* fail safe */ +} + +static BOOL is_valid_weakref_target(JSValue val) +{ + switch (JS_VALUE_GET_TAG(val)) { + case JS_TAG_OBJECT: + break; + case JS_TAG_SYMBOL: { + // Per spec: prohibit symbols registered with Symbol.for() + JSAtomStruct *p = JS_VALUE_GET_PTR(val); + if (p->atom_type != JS_ATOM_TYPE_GLOBAL_SYMBOL) + break; + // fallthru + } + default: + return FALSE; + } + + return TRUE; +} + +static void insert_weakref_record(JSValue target, struct JSWeakRefRecord *wr) +{ + JSWeakRefRecord **pwr = get_first_weak_ref(target); + /* Add the weak reference */ + wr->next_weak_ref = *pwr; + *pwr = wr; +} + +/* Poly IC */ + +JSInlineCache *init_ic(JSContext *ctx) +{ + JSInlineCache *ic; + ic = js_malloc(ctx, sizeof(JSInlineCache)); + if (unlikely(!ic)) + goto fail; + ic->count = 0; + ic->hash_bits = 2; + ic->capacity = 1 << ic->hash_bits; + ic->hash = js_mallocz(ctx, sizeof(ic->hash[0]) * ic->capacity); + if (unlikely(!ic->hash)) + goto fail; + ic->cache = NULL; + return ic; +fail: + js_free(ctx, ic); + return NULL; +} + +int rebuild_ic(JSContext *ctx, JSInlineCache *ic) +{ + uint32_t i, count; + JSInlineCacheHashSlot *ch; + if (ic->count == 0) + goto end; + count = 0; + ic->cache = js_mallocz(ctx, sizeof(JSInlineCacheRingSlot) * ic->count); + if (unlikely(!ic->cache)) + goto fail; + for (i = 0; i < ic->capacity; i++) { + for (ch = ic->hash[i]; ch != NULL; ch = ch->next) { + ch->index = count++; + ic->cache[ch->index].atom = JS_DupAtom(ctx, ch->atom); + ic->cache[ch->index].index = 0; + } + } +end: + return 0; +fail: + return -1; +} + +int resize_ic_hash(JSContext *ctx, JSInlineCache *ic) +{ + uint32_t new_capacity, i, h; + JSInlineCacheHashSlot *ch, *ch_next; + JSInlineCacheHashSlot **new_hash; + new_capacity = 1 << (ic->hash_bits + 1); + new_hash = js_mallocz(ctx, sizeof(ic->hash[0]) * new_capacity); + if (unlikely(!new_hash)) + goto fail; + ic->hash_bits += 1; + for (i = 0; i < ic->capacity; i++) { + for (ch = ic->hash[i]; ch != NULL; ch = ch_next) { + h = get_index_hash(ch->atom, ic->hash_bits); + ch_next = ch->next; + ch->next = new_hash[h]; + new_hash[h] = ch; + } + } + js_free(ctx, ic->hash); + ic->hash = new_hash; + ic->capacity = new_capacity; + return 0; +fail: + return -1; +} + +int free_ic(JSRuntime* rt, JSInlineCache *ic) +{ + uint32_t i; + JSInlineCacheHashSlot *ch, *ch_next; + JSShape **shape, *(*shapes)[IC_CACHE_ITEM_CAPACITY]; + if (ic->cache) { + for (i = 0; i < ic->count; i++) { + shapes = &ic->cache[i].shape; + JS_FreeAtomRT(rt, ic->cache[i].atom); + for (shape = *shapes; shape != endof(*shapes); shape++) + js_free_shape_null(rt, *shape); + } + } + for (i = 0; i < ic->capacity; i++) { + for (ch = ic->hash[i]; ch != NULL; ch = ch_next) { + ch_next = ch->next; + JS_FreeAtomRT(rt, ch->atom); + js_free_rt(rt, ch); + } + } + if (ic->count > 0) + js_free_rt(rt, ic->cache); + js_free_rt(rt, ic->hash); + js_free_rt(rt, ic); + return 0; +} + +static void add_ic_slot(JSContext *ctx, JSInlineCacheUpdate *icu, + JSAtom atom, JSObject *object, uint32_t prop_offset) +{ + int32_t i; + uint32_t h; + JSInlineCacheHashSlot *ch; + JSInlineCacheRingSlot *cr; + JSInlineCache *ic; + JSShape *sh; + + if (!icu) + return; + ic = icu->ic; + if (!ic) + return; + sh = object->shape; + if (!sh->is_hashed) + return; + cr = NULL; + h = get_index_hash(atom, ic->hash_bits); + for (ch = ic->hash[h]; ch != NULL; ch = ch->next) { + if (ch->atom == atom) { + cr = ic->cache + ch->index; + break; + } + } + assert(cr != NULL); + i = cr->index; + do { + if (sh == cr->shape[i]) { + cr->prop_offset[i] = prop_offset; + goto end; + } + i = (i + 1) % countof(cr->shape); + } while (i != cr->index); + js_free_shape_null(ctx->rt, cr->shape[i]); + cr->shape[i] = js_dup_shape(sh); + cr->prop_offset[i] = prop_offset; +end: + icu->offset = ch->index; +} + +/* CallSite */ + +static void js_callsite_finalizer(JSRuntime *rt, JSValue val) +{ + JSCallSiteData *csd = JS_GetOpaque(val, JS_CLASS_CALL_SITE); + if (csd) { + JS_FreeValueRT(rt, csd->filename); + JS_FreeValueRT(rt, csd->func); + JS_FreeValueRT(rt, csd->func_name); + js_free_rt(rt, csd); + } +} + +static void js_callsite_mark(JSRuntime *rt, JSValue val, JS_MarkFunc *mark_func) +{ + JSCallSiteData *csd = JS_GetOpaque(val, JS_CLASS_CALL_SITE); + if (csd) { + JS_MarkValue(rt, csd->filename, mark_func); + JS_MarkValue(rt, csd->func, mark_func); + JS_MarkValue(rt, csd->func_name, mark_func); + } +} + +static JSValue js_new_callsite(JSContext *ctx, JSCallSiteData *csd) { + JSValue obj = js_create_from_ctor(ctx, JS_UNDEFINED, JS_CLASS_CALL_SITE); + if (JS_IsException(obj)) + return JS_EXCEPTION; + + JSCallSiteData *csd1 = js_malloc(ctx, sizeof(*csd)); + if (!csd1) { + JS_FreeValue(ctx, obj); + return JS_EXCEPTION; + } + + memcpy(csd1, csd, sizeof(*csd)); + + JS_SetOpaqueInternal(obj, csd1); + + return obj; +} + +static void js_new_callsite_data(JSContext *ctx, JSCallSiteData *csd, JSStackFrame *sf) +{ + const char *func_name_str; + JSObject *p; + + csd->func = js_dup(sf->cur_func); + /* func_name_str is UTF-8 encoded if needed */ + func_name_str = get_func_name(ctx, sf->cur_func); + if (!func_name_str || func_name_str[0] == '\0') + csd->func_name = JS_NULL; + else + csd->func_name = JS_NewString(ctx, func_name_str); + JS_FreeCString(ctx, func_name_str); + if (JS_IsException(csd->func_name)) + csd->func_name = JS_NULL; + + p = JS_VALUE_GET_OBJ(sf->cur_func); + if (js_class_has_bytecode(p->class_id)) { + JSFunctionBytecode *b = p->u.func.function_bytecode; + int line_num1, col_num1; + line_num1 = find_line_num(ctx, b, + sf->cur_pc - b->byte_code_buf - 1, + &col_num1); + csd->native = FALSE; + csd->line_num = line_num1; + csd->col_num = col_num1; + csd->filename = JS_AtomToString(ctx, b->filename); + if (JS_IsException(csd->filename)) { + csd->filename = JS_NULL; + JS_FreeValue(ctx, JS_GetException(ctx)); // Clear exception. + } + } else { + csd->native = TRUE; + csd->line_num = -1; + csd->col_num = -1; + csd->filename = JS_NULL; + } +} + +static void js_new_callsite_data2(JSContext *ctx, JSCallSiteData *csd, const char *filename, int line_num, int col_num) +{ + csd->func = JS_NULL; + csd->func_name = JS_NULL; + csd->native = FALSE; + csd->line_num = line_num; + csd->col_num = col_num; + /* filename is UTF-8 encoded if needed (original argument to __JS_EvalInternal()) */ + csd->filename = JS_NewString(ctx, filename); + if (JS_IsException(csd->filename)) { + csd->filename = JS_NULL; + JS_FreeValue(ctx, JS_GetException(ctx)); // Clear exception. + } +} + +static JSValue js_callsite_getfield(JSContext *ctx, JSValue this_val, int argc, JSValue *argv, int magic) +{ + JSCallSiteData *csd = JS_GetOpaque2(ctx, this_val, JS_CLASS_CALL_SITE); + if (!csd) + return JS_EXCEPTION; + JSValue *field = (void *)((char *)csd + magic); + return js_dup(*field); +} + +static JSValue js_callsite_isnative(JSContext *ctx, JSValue this_val, int argc, JSValue *argv) +{ + JSCallSiteData *csd = JS_GetOpaque2(ctx, this_val, JS_CLASS_CALL_SITE); + if (!csd) + return JS_EXCEPTION; + return JS_NewBool(ctx, csd->native); +} + +static JSValue js_callsite_getnumber(JSContext *ctx, JSValue this_val, int argc, JSValue *argv, int magic) +{ + JSCallSiteData *csd = JS_GetOpaque2(ctx, this_val, JS_CLASS_CALL_SITE); + if (!csd) + return JS_EXCEPTION; + int *field = (void *)((char *)csd + magic); + return JS_NewInt32(ctx, *field); +} + +static const JSCFunctionListEntry js_callsite_proto_funcs[] = { + JS_CFUNC_DEF("isNative", 0, js_callsite_isnative), + JS_CFUNC_MAGIC_DEF("getFileName", 0, js_callsite_getfield, offsetof(JSCallSiteData, filename)), + JS_CFUNC_MAGIC_DEF("getFunction", 0, js_callsite_getfield, offsetof(JSCallSiteData, func)), + JS_CFUNC_MAGIC_DEF("getFunctionName", 0, js_callsite_getfield, offsetof(JSCallSiteData, func_name)), + JS_CFUNC_MAGIC_DEF("getColumnNumber", 0, js_callsite_getnumber, offsetof(JSCallSiteData, col_num)), + JS_CFUNC_MAGIC_DEF("getLineNumber", 0, js_callsite_getnumber, offsetof(JSCallSiteData, line_num)), + JS_PROP_STRING_DEF("[Symbol.toStringTag]", "CallSite", JS_PROP_CONFIGURABLE ), +}; + +static const JSClassShortDef js_callsite_class_def[] = { + { JS_ATOM_CallSite, js_callsite_finalizer, js_callsite_mark }, /* JS_CLASS_CALL_SITE */ +}; + +static void _JS_AddIntrinsicCallSite(JSContext *ctx) +{ + JSRuntime *rt = ctx->rt; + + if (!JS_IsRegisteredClass(rt, JS_CLASS_CALL_SITE)) { + init_class_range(rt, js_callsite_class_def, JS_CLASS_CALL_SITE, + countof(js_callsite_class_def)); + } + ctx->class_proto[JS_CLASS_CALL_SITE] = JS_NewObject(ctx); + JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_CALL_SITE], + js_callsite_proto_funcs, + countof(js_callsite_proto_funcs)); +} + +BOOL JS_DetectModule(const char *input, size_t input_len) +{ + JSRuntime *rt; + JSContext *ctx; + JSValue val; + BOOL is_module; + + is_module = TRUE; + rt = JS_NewRuntime(); + if (!rt) + return FALSE; + ctx = JS_NewContextRaw(rt); + if (!ctx) { + JS_FreeRuntime(rt); + return FALSE; + } + JS_AddIntrinsicRegExp(ctx); // otherwise regexp literals don't parse + val = __JS_EvalInternal(ctx, JS_UNDEFINED, input, input_len, "<unnamed>", + JS_EVAL_TYPE_MODULE|JS_EVAL_FLAG_COMPILE_ONLY, -1); + if (JS_IsException(val)) { + const char *msg = JS_ToCString(ctx, rt->current_exception); + // gruesome hack to recognize exceptions from import statements; + // necessary because we don't pass in a module loader + is_module = !!strstr(msg, "ReferenceError: could not load module"); + JS_FreeCString(ctx, msg); + } + JS_FreeValue(ctx, val); + JS_FreeContext(ctx); + JS_FreeRuntime(rt); + return is_module; +} + +uintptr_t js_std_cmd(int cmd, ...) { + JSRuntime *rt; + uintptr_t rv; + va_list ap; + + rv = 0; + va_start(ap, cmd); + switch (cmd) { + case 0: // GetOpaque + rt = va_arg(ap, JSRuntime *); + rv = (uintptr_t)rt->libc_opaque; + break; + case 1: // SetOpaque + rt = va_arg(ap, JSRuntime *); + rt->libc_opaque = va_arg(ap, void *); + break; + default: + rv = -1; + } + va_end(ap); + + return rv; +} + +#undef malloc +#undef free +#undef realloc diff --git a/third_party/quickjs/quickjs.h b/third_party/quickjs/quickjs.h new file mode 100644 index 0000000000..925de6bfab --- /dev/null +++ b/third_party/quickjs/quickjs.h @@ -0,0 +1,1045 @@ +/* + * QuickJS Javascript Engine + * + * Copyright (c) 2017-2021 Fabrice Bellard + * Copyright (c) 2017-2021 Charlie Gordon + * Copyright (c) 2023 Ben Noordhuis + * Copyright (c) 2023 Saúl Ibarra Corretgé + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#ifndef QUICKJS_H +#define QUICKJS_H + +#include <stdio.h> +#include <stdint.h> +#include <string.h> +#include <math.h> + +#ifdef __cplusplus +extern "C" { +#endif + +#if defined(__GNUC__) || defined(__clang__) +#define js_force_inline inline __attribute__((always_inline)) +#define __js_printf_like(f, a) __attribute__((format(printf, f, a))) +#define JS_EXTERN __attribute__((visibility("default"))) +#else +#define js_force_inline inline +#define __js_printf_like(a, b) +#define JS_EXTERN /* nothing */ +#endif + +#define JS_BOOL int + +typedef struct JSRuntime JSRuntime; +typedef struct JSContext JSContext; +typedef struct JSObject JSObject; +typedef struct JSClass JSClass; +typedef uint32_t JSClassID; +typedef uint32_t JSAtom; + +/* Unless documented otherwise, C string pointers (`char *` or `const char *`) + are assumed to verify these constraints: + - unless a length is passed separately, the string has a null terminator + - string contents is either pure ASCII or is UTF-8 encoded. + */ + +/* Overridable purely for testing purposes; don't touch. */ +#ifndef JS_NAN_BOXING +#if INTPTR_MAX < INT64_MAX +#define JS_NAN_BOXING 1 /* Use NAN boxing for 32bit builds. */ +#endif +#endif + +enum { + /* all tags with a reference count are negative */ + JS_TAG_FIRST = -9, /* first negative tag */ + JS_TAG_BIG_INT = -9, + JS_TAG_SYMBOL = -8, + JS_TAG_STRING = -7, + JS_TAG_MODULE = -3, /* used internally */ + JS_TAG_FUNCTION_BYTECODE = -2, /* used internally */ + JS_TAG_OBJECT = -1, + + JS_TAG_INT = 0, + JS_TAG_BOOL = 1, + JS_TAG_NULL = 2, + JS_TAG_UNDEFINED = 3, + JS_TAG_UNINITIALIZED = 4, + JS_TAG_CATCH_OFFSET = 5, + JS_TAG_EXCEPTION = 6, + JS_TAG_FLOAT64 = 7, + /* any larger tag is FLOAT64 if JS_NAN_BOXING */ +}; + +#define JS_FLOAT64_NAN NAN +#define JSValueConst JSValue /* For backwards compatibility. */ + +#if defined(JS_NAN_BOXING) && JS_NAN_BOXING + +typedef uint64_t JSValue; + +#define JS_VALUE_GET_TAG(v) (int)((v) >> 32) +#define JS_VALUE_GET_INT(v) (int)(v) +#define JS_VALUE_GET_BOOL(v) (int)(v) +#define JS_VALUE_GET_PTR(v) (void *)(intptr_t)(v) + +#define JS_MKVAL(tag, val) (((uint64_t)(tag) << 32) | (uint32_t)(val)) +#define JS_MKPTR(tag, ptr) (((uint64_t)(tag) << 32) | (uintptr_t)(ptr)) + +#define JS_FLOAT64_TAG_ADDEND (0x7ff80000 - JS_TAG_FIRST + 1) /* quiet NaN encoding */ + +static inline double JS_VALUE_GET_FLOAT64(JSValue v) +{ + union { + JSValue v; + double d; + } u; + u.v = v; + u.v += (uint64_t)JS_FLOAT64_TAG_ADDEND << 32; + return u.d; +} + +#define JS_NAN (0x7ff8000000000000 - ((uint64_t)JS_FLOAT64_TAG_ADDEND << 32)) + +static inline JSValue __JS_NewFloat64(double d) +{ + union { + double d; + uint64_t u64; + } u; + JSValue v; + u.d = d; + /* normalize NaN */ + if ((u.u64 & 0x7fffffffffffffff) > 0x7ff0000000000000) + v = JS_NAN; + else + v = u.u64 - ((uint64_t)JS_FLOAT64_TAG_ADDEND << 32); + return v; +} + +#define JS_TAG_IS_FLOAT64(tag) ((unsigned)((tag) - JS_TAG_FIRST) >= (JS_TAG_FLOAT64 - JS_TAG_FIRST)) + +/* same as JS_VALUE_GET_TAG, but return JS_TAG_FLOAT64 with NaN boxing */ +static inline int JS_VALUE_GET_NORM_TAG(JSValue v) +{ + uint32_t tag; + tag = JS_VALUE_GET_TAG(v); + if (JS_TAG_IS_FLOAT64(tag)) + return JS_TAG_FLOAT64; + else + return tag; +} + +static inline JS_BOOL JS_VALUE_IS_NAN(JSValue v) +{ + uint32_t tag; + tag = JS_VALUE_GET_TAG(v); + return tag == (JS_NAN >> 32); +} + +#else /* !JS_NAN_BOXING */ + +typedef union JSValueUnion { + int32_t int32; + double float64; + void *ptr; +} JSValueUnion; + +typedef struct JSValue { + JSValueUnion u; + int64_t tag; +} JSValue; + +#define JS_VALUE_GET_TAG(v) ((int32_t)(v).tag) +/* same as JS_VALUE_GET_TAG, but return JS_TAG_FLOAT64 with NaN boxing */ +#define JS_VALUE_GET_NORM_TAG(v) JS_VALUE_GET_TAG(v) +#define JS_VALUE_GET_INT(v) ((v).u.int32) +#define JS_VALUE_GET_BOOL(v) ((v).u.int32) +#define JS_VALUE_GET_FLOAT64(v) ((v).u.float64) +#define JS_VALUE_GET_PTR(v) ((v).u.ptr) + +/* msvc doesn't understand designated initializers without /std:c++20 */ +#ifdef __cplusplus +static inline JSValue JS_MKPTR(int64_t tag, void *ptr) +{ + JSValue v; + v.u.ptr = ptr; + v.tag = tag; + return v; +} +static inline JSValue JS_MKVAL(int64_t tag, int32_t int32) +{ + JSValue v; + v.u.int32 = int32; + v.tag = tag; + return v; +} +static inline JSValue JS_MKNAN(void) +{ + JSValue v; + v.u.float64 = JS_FLOAT64_NAN; + v.tag = JS_TAG_FLOAT64; + return v; +} +/* provide as macros for consistency and backward compat reasons */ +#define JS_MKPTR(tag, ptr) JS_MKPTR(tag, ptr) +#define JS_MKVAL(tag, val) JS_MKVAL(tag, val) +#define JS_NAN JS_MKNAN() /* alas, not a constant expression */ +#else +#define JS_MKPTR(tag, p) (JSValue){ (JSValueUnion){ .ptr = p }, tag } +#define JS_MKVAL(tag, val) (JSValue){ (JSValueUnion){ .int32 = val }, tag } +#define JS_NAN (JSValue){ (JSValueUnion){ .float64 = JS_FLOAT64_NAN }, JS_TAG_FLOAT64 } +#endif + +#define JS_TAG_IS_FLOAT64(tag) ((unsigned)(tag) == JS_TAG_FLOAT64) + +static inline JSValue __JS_NewFloat64(double d) +{ + JSValue v; + v.tag = JS_TAG_FLOAT64; + v.u.float64 = d; + return v; +} + +static inline JS_BOOL JS_VALUE_IS_NAN(JSValue v) +{ + union { + double d; + uint64_t u64; + } u; + if (v.tag != JS_TAG_FLOAT64) + return 0; + u.d = v.u.float64; + return (u.u64 & 0x7fffffffffffffff) > 0x7ff0000000000000; +} + +#endif /* !JS_NAN_BOXING */ + +#define JS_VALUE_IS_BOTH_INT(v1, v2) ((JS_VALUE_GET_TAG(v1) | JS_VALUE_GET_TAG(v2)) == 0) +#define JS_VALUE_IS_BOTH_FLOAT(v1, v2) (JS_TAG_IS_FLOAT64(JS_VALUE_GET_TAG(v1)) && JS_TAG_IS_FLOAT64(JS_VALUE_GET_TAG(v2))) + +#define JS_VALUE_GET_OBJ(v) ((JSObject *)JS_VALUE_GET_PTR(v)) +#define JS_VALUE_HAS_REF_COUNT(v) ((unsigned)JS_VALUE_GET_TAG(v) >= (unsigned)JS_TAG_FIRST) + +/* special values */ +#define JS_NULL JS_MKVAL(JS_TAG_NULL, 0) +#define JS_UNDEFINED JS_MKVAL(JS_TAG_UNDEFINED, 0) +#define JS_FALSE JS_MKVAL(JS_TAG_BOOL, 0) +#define JS_TRUE JS_MKVAL(JS_TAG_BOOL, 1) +#define JS_EXCEPTION JS_MKVAL(JS_TAG_EXCEPTION, 0) +#define JS_UNINITIALIZED JS_MKVAL(JS_TAG_UNINITIALIZED, 0) + +/* flags for object properties */ +#define JS_PROP_CONFIGURABLE (1 << 0) +#define JS_PROP_WRITABLE (1 << 1) +#define JS_PROP_ENUMERABLE (1 << 2) +#define JS_PROP_C_W_E (JS_PROP_CONFIGURABLE | JS_PROP_WRITABLE | JS_PROP_ENUMERABLE) +#define JS_PROP_LENGTH (1 << 3) /* used internally in Arrays */ +#define JS_PROP_TMASK (3 << 4) /* mask for NORMAL, GETSET, VARREF, AUTOINIT */ +#define JS_PROP_NORMAL (0 << 4) +#define JS_PROP_GETSET (1 << 4) +#define JS_PROP_VARREF (2 << 4) /* used internally */ +#define JS_PROP_AUTOINIT (3 << 4) /* used internally */ + +/* flags for JS_DefineProperty */ +#define JS_PROP_HAS_SHIFT 8 +#define JS_PROP_HAS_CONFIGURABLE (1 << 8) +#define JS_PROP_HAS_WRITABLE (1 << 9) +#define JS_PROP_HAS_ENUMERABLE (1 << 10) +#define JS_PROP_HAS_GET (1 << 11) +#define JS_PROP_HAS_SET (1 << 12) +#define JS_PROP_HAS_VALUE (1 << 13) + +/* throw an exception if false would be returned + (JS_DefineProperty/JS_SetProperty) */ +#define JS_PROP_THROW (1 << 14) +/* throw an exception if false would be returned in strict mode + (JS_SetProperty) */ +#define JS_PROP_THROW_STRICT (1 << 15) + +#define JS_PROP_NO_ADD (1 << 16) /* internal use */ +#define JS_PROP_NO_EXOTIC (1 << 17) /* internal use */ +#define JS_PROP_DEFINE_PROPERTY (1 << 18) /* internal use */ +#define JS_PROP_REFLECT_DEFINE_PROPERTY (1 << 19) /* internal use */ + +#if defined(__wasi__) +#define JS_DEFAULT_STACK_SIZE 0 +#else +#define JS_DEFAULT_STACK_SIZE (256 * 1024) +#endif + +/* JS_Eval() flags */ +#define JS_EVAL_TYPE_GLOBAL (0 << 0) /* global code (default) */ +#define JS_EVAL_TYPE_MODULE (1 << 0) /* module code */ +#define JS_EVAL_TYPE_DIRECT (2 << 0) /* direct call (internal use) */ +#define JS_EVAL_TYPE_INDIRECT (3 << 0) /* indirect call (internal use) */ +#define JS_EVAL_TYPE_MASK (3 << 0) + +#define JS_EVAL_FLAG_STRICT (1 << 3) /* force 'strict' mode */ +#define JS_EVAL_FLAG_UNUSED (1 << 4) /* unused */ +/* compile but do not run. The result is an object with a + JS_TAG_FUNCTION_BYTECODE or JS_TAG_MODULE tag. It can be executed + with JS_EvalFunction(). */ +#define JS_EVAL_FLAG_COMPILE_ONLY (1 << 5) +/* don't include the stack frames before this eval in the Error() backtraces */ +#define JS_EVAL_FLAG_BACKTRACE_BARRIER (1 << 6) +/* allow top-level await in normal script. JS_Eval() returns a + promise. Only allowed with JS_EVAL_TYPE_GLOBAL */ +#define JS_EVAL_FLAG_ASYNC (1 << 7) + +typedef JSValue JSCFunction(JSContext *ctx, JSValue this_val, int argc, JSValue *argv); +typedef JSValue JSCFunctionMagic(JSContext *ctx, JSValue this_val, int argc, JSValue *argv, int magic); +typedef JSValue JSCFunctionData(JSContext *ctx, JSValue this_val, int argc, JSValue *argv, int magic, JSValue *func_data); + +typedef struct JSMallocFunctions { + void *(*js_calloc)(void *opaque, size_t count, size_t size); + void *(*js_malloc)(void *opaque, size_t size); + void (*js_free)(void *opaque, void *ptr); + void *(*js_realloc)(void *opaque, void *ptr, size_t size); + size_t (*js_malloc_usable_size)(const void *ptr); +} JSMallocFunctions; + +// Finalizers run in LIFO order at the very end of JS_FreeRuntime. +// Intended for cleanup of associated resources; the runtime itself +// is no longer usable. +typedef void JSRuntimeFinalizer(JSRuntime *rt, void *arg); + +typedef struct JSGCObjectHeader JSGCObjectHeader; + +JS_EXTERN JSRuntime *JS_NewRuntime(void); +/* info lifetime must exceed that of rt */ +JS_EXTERN void JS_SetRuntimeInfo(JSRuntime *rt, const char *info); +/* use 0 to disable memory limit */ +JS_EXTERN void JS_SetMemoryLimit(JSRuntime *rt, size_t limit); +JS_EXTERN void JS_SetDumpFlags(JSRuntime *rt, uint64_t flags); +JS_EXTERN size_t JS_GetGCThreshold(JSRuntime *rt); +JS_EXTERN void JS_SetGCThreshold(JSRuntime *rt, size_t gc_threshold); +/* use 0 to disable maximum stack size check */ +JS_EXTERN void JS_SetMaxStackSize(JSRuntime *rt, size_t stack_size); +/* should be called when changing thread to update the stack top value + used to check stack overflow. */ +JS_EXTERN void JS_UpdateStackTop(JSRuntime *rt); +JS_EXTERN JSRuntime *JS_NewRuntime2(const JSMallocFunctions *mf, void *opaque); +JS_EXTERN void JS_FreeRuntime(JSRuntime *rt); +JS_EXTERN void *JS_GetRuntimeOpaque(JSRuntime *rt); +JS_EXTERN void JS_SetRuntimeOpaque(JSRuntime *rt, void *opaque); +JS_EXTERN int JS_AddRuntimeFinalizer(JSRuntime *rt, + JSRuntimeFinalizer *finalizer, void *arg); +typedef void JS_MarkFunc(JSRuntime *rt, JSGCObjectHeader *gp); +JS_EXTERN void JS_MarkValue(JSRuntime *rt, JSValue val, JS_MarkFunc *mark_func); +JS_EXTERN void JS_RunGC(JSRuntime *rt); +JS_EXTERN JS_BOOL JS_IsLiveObject(JSRuntime *rt, JSValue obj); + +JS_EXTERN JSContext *JS_NewContext(JSRuntime *rt); +JS_EXTERN void JS_FreeContext(JSContext *s); +JS_EXTERN JSContext *JS_DupContext(JSContext *ctx); +JS_EXTERN void *JS_GetContextOpaque(JSContext *ctx); +JS_EXTERN void JS_SetContextOpaque(JSContext *ctx, void *opaque); +JS_EXTERN JSRuntime *JS_GetRuntime(JSContext *ctx); +JS_EXTERN void JS_SetClassProto(JSContext *ctx, JSClassID class_id, JSValue obj); +JS_EXTERN JSValue JS_GetClassProto(JSContext *ctx, JSClassID class_id); +JS_EXTERN JSValue JS_GetFunctionProto(JSContext *ctx); + +/* the following functions are used to select the intrinsic object to + save memory */ +JS_EXTERN JSContext *JS_NewContextRaw(JSRuntime *rt); +JS_EXTERN void JS_AddIntrinsicBaseObjects(JSContext *ctx); +JS_EXTERN void JS_AddIntrinsicDate(JSContext *ctx); +JS_EXTERN void JS_AddIntrinsicEval(JSContext *ctx); +JS_EXTERN void JS_AddIntrinsicRegExpCompiler(JSContext *ctx); +JS_EXTERN void JS_AddIntrinsicRegExp(JSContext *ctx); +JS_EXTERN void JS_AddIntrinsicJSON(JSContext *ctx); +JS_EXTERN void JS_AddIntrinsicProxy(JSContext *ctx); +JS_EXTERN void JS_AddIntrinsicMapSet(JSContext *ctx); +JS_EXTERN void JS_AddIntrinsicTypedArrays(JSContext *ctx); +JS_EXTERN void JS_AddIntrinsicPromise(JSContext *ctx); +JS_EXTERN void JS_AddIntrinsicBigInt(JSContext *ctx); +JS_EXTERN void JS_AddIntrinsicWeakRef(JSContext *ctx); +JS_EXTERN void JS_AddPerformance(JSContext *ctx); + +/* for equality comparisons and sameness */ +JS_EXTERN int JS_IsEqual(JSContext *ctx, JSValue op1, JSValue op2); +JS_EXTERN JS_BOOL JS_IsStrictEqual(JSContext *ctx, JSValue op1, JSValue op2); +JS_EXTERN JS_BOOL JS_IsSameValue(JSContext *ctx, JSValue op1, JSValue op2); +/* Similar to same-value equality, but +0 and -0 are considered equal. */ +JS_EXTERN JS_BOOL JS_IsSameValueZero(JSContext *ctx, JSValue op1, JSValue op2); + +/* Only used for running 262 tests. TODO(saghul) add build time flag. */ +JS_EXTERN JSValue js_string_codePointRange(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv); + +JS_EXTERN void *js_calloc_rt(JSRuntime *rt, size_t count, size_t size); +JS_EXTERN void *js_malloc_rt(JSRuntime *rt, size_t size); +JS_EXTERN void js_free_rt(JSRuntime *rt, void *ptr); +JS_EXTERN void *js_realloc_rt(JSRuntime *rt, void *ptr, size_t size); +JS_EXTERN size_t js_malloc_usable_size_rt(JSRuntime *rt, const void *ptr); +JS_EXTERN void *js_mallocz_rt(JSRuntime *rt, size_t size); + +JS_EXTERN void *js_calloc(JSContext *ctx, size_t count, size_t size); +JS_EXTERN void *js_malloc(JSContext *ctx, size_t size); +JS_EXTERN void js_free(JSContext *ctx, void *ptr); +JS_EXTERN void *js_realloc(JSContext *ctx, void *ptr, size_t size); +JS_EXTERN size_t js_malloc_usable_size(JSContext *ctx, const void *ptr); +JS_EXTERN void *js_realloc2(JSContext *ctx, void *ptr, size_t size, size_t *pslack); +JS_EXTERN void *js_mallocz(JSContext *ctx, size_t size); +JS_EXTERN char *js_strdup(JSContext *ctx, const char *str); +JS_EXTERN char *js_strndup(JSContext *ctx, const char *s, size_t n); + +typedef struct JSMemoryUsage { + int64_t malloc_size, malloc_limit, memory_used_size; + int64_t malloc_count; + int64_t memory_used_count; + int64_t atom_count, atom_size; + int64_t str_count, str_size; + int64_t obj_count, obj_size; + int64_t prop_count, prop_size; + int64_t shape_count, shape_size; + int64_t js_func_count, js_func_size, js_func_code_size; + int64_t js_func_pc2line_count, js_func_pc2line_size; + int64_t c_func_count, array_count; + int64_t fast_array_count, fast_array_elements; + int64_t binary_object_count, binary_object_size; +} JSMemoryUsage; + +JS_EXTERN void JS_ComputeMemoryUsage(JSRuntime *rt, JSMemoryUsage *s); +JS_EXTERN void JS_DumpMemoryUsage(FILE *fp, const JSMemoryUsage *s, JSRuntime *rt); + +/* atom support */ +#define JS_ATOM_NULL 0 + +JS_EXTERN JSAtom JS_NewAtomLen(JSContext *ctx, const char *str, size_t len); +JS_EXTERN JSAtom JS_NewAtom(JSContext *ctx, const char *str); +JS_EXTERN JSAtom JS_NewAtomUInt32(JSContext *ctx, uint32_t n); +JS_EXTERN JSAtom JS_DupAtom(JSContext *ctx, JSAtom v); +JS_EXTERN void JS_FreeAtom(JSContext *ctx, JSAtom v); +JS_EXTERN void JS_FreeAtomRT(JSRuntime *rt, JSAtom v); +JS_EXTERN JSValue JS_AtomToValue(JSContext *ctx, JSAtom atom); +JS_EXTERN JSValue JS_AtomToString(JSContext *ctx, JSAtom atom); +JS_EXTERN const char *JS_AtomToCString(JSContext *ctx, JSAtom atom); +JS_EXTERN JSAtom JS_ValueToAtom(JSContext *ctx, JSValue val); + +/* object class support */ + +typedef struct JSPropertyEnum { + JS_BOOL is_enumerable; + JSAtom atom; +} JSPropertyEnum; + +typedef struct JSPropertyDescriptor { + int flags; + JSValue value; + JSValue getter; + JSValue setter; +} JSPropertyDescriptor; + +typedef struct JSClassExoticMethods { + /* Return -1 if exception (can only happen in case of Proxy object), + FALSE if the property does not exists, TRUE if it exists. If 1 is + returned, the property descriptor 'desc' is filled if != NULL. */ + int (*get_own_property)(JSContext *ctx, JSPropertyDescriptor *desc, + JSValue obj, JSAtom prop); + /* '*ptab' should hold the '*plen' property keys. Return 0 if OK, + -1 if exception. The 'is_enumerable' field is ignored. + */ + int (*get_own_property_names)(JSContext *ctx, JSPropertyEnum **ptab, + uint32_t *plen, + JSValue obj); + /* return < 0 if exception, or TRUE/FALSE */ + int (*delete_property)(JSContext *ctx, JSValue obj, JSAtom prop); + /* return < 0 if exception or TRUE/FALSE */ + int (*define_own_property)(JSContext *ctx, JSValue this_obj, + JSAtom prop, JSValue val, + JSValue getter, JSValue setter, + int flags); + /* The following methods can be emulated with the previous ones, + so they are usually not needed */ + /* return < 0 if exception or TRUE/FALSE */ + int (*has_property)(JSContext *ctx, JSValue obj, JSAtom atom); + JSValue (*get_property)(JSContext *ctx, JSValue obj, JSAtom atom, + JSValue receiver); + /* return < 0 if exception or TRUE/FALSE */ + int (*set_property)(JSContext *ctx, JSValue obj, JSAtom atom, + JSValue value, JSValue receiver, int flags); +} JSClassExoticMethods; + +typedef void JSClassFinalizer(JSRuntime *rt, JSValue val); +typedef void JSClassGCMark(JSRuntime *rt, JSValue val, + JS_MarkFunc *mark_func); +#define JS_CALL_FLAG_CONSTRUCTOR (1 << 0) +typedef JSValue JSClassCall(JSContext *ctx, JSValue func_obj, + JSValue this_val, int argc, JSValue *argv, + int flags); + +typedef struct JSClassDef { + const char *class_name; /* pure ASCII only! */ + JSClassFinalizer *finalizer; + JSClassGCMark *gc_mark; + /* if call != NULL, the object is a function. If (flags & + JS_CALL_FLAG_CONSTRUCTOR) != 0, the function is called as a + constructor. In this case, 'this_val' is new.target. A + constructor call only happens if the object constructor bit is + set (see JS_SetConstructorBit()). */ + JSClassCall *call; + /* XXX: suppress this indirection ? It is here only to save memory + because only a few classes need these methods */ + JSClassExoticMethods *exotic; +} JSClassDef; + +#define JS_INVALID_CLASS_ID 0 +JS_EXTERN JSClassID JS_NewClassID(JSRuntime *rt, JSClassID *pclass_id); +/* Returns the class ID if `v` is an object, otherwise returns JS_INVALID_CLASS_ID. */ +JS_EXTERN JSClassID JS_GetClassID(JSValue v); +JS_EXTERN int JS_NewClass(JSRuntime *rt, JSClassID class_id, const JSClassDef *class_def); +JS_EXTERN int JS_IsRegisteredClass(JSRuntime *rt, JSClassID class_id); + +/* value handling */ + +static js_force_inline JSValue JS_NewBool(JSContext *ctx, JS_BOOL val) +{ + (void)&ctx; + return JS_MKVAL(JS_TAG_BOOL, (val != 0)); +} + +static js_force_inline JSValue JS_NewInt32(JSContext *ctx, int32_t val) +{ + (void)&ctx; + return JS_MKVAL(JS_TAG_INT, val); +} + +static js_force_inline JSValue JS_NewFloat64(JSContext *ctx, double val) +{ + (void)&ctx; + return __JS_NewFloat64(val); +} + +static js_force_inline JSValue JS_NewCatchOffset(JSContext *ctx, int32_t val) +{ + (void)&ctx; + return JS_MKVAL(JS_TAG_CATCH_OFFSET, val); +} + +static js_force_inline JSValue JS_NewInt64(JSContext *ctx, int64_t val) +{ + JSValue v; + if (val >= INT32_MIN && val <= INT32_MAX) { + v = JS_NewInt32(ctx, (int32_t)val); + } else { + v = JS_NewFloat64(ctx, (double)val); + } + return v; +} + +static js_force_inline JSValue JS_NewUint32(JSContext *ctx, uint32_t val) +{ + JSValue v; + if (val <= INT32_MAX) { + v = JS_NewInt32(ctx, (int32_t)val); + } else { + v = JS_NewFloat64(ctx, (double)val); + } + return v; +} + +JS_EXTERN JSValue JS_NewNumber(JSContext *ctx, double d); +JS_EXTERN JSValue JS_NewBigInt64(JSContext *ctx, int64_t v); +JS_EXTERN JSValue JS_NewBigUint64(JSContext *ctx, uint64_t v); + +static inline JS_BOOL JS_IsNumber(JSValue v) +{ + int tag = JS_VALUE_GET_TAG(v); + return tag == JS_TAG_INT || JS_TAG_IS_FLOAT64(tag); +} + +static inline JS_BOOL JS_IsBigInt(JSContext *ctx, JSValue v) +{ + (void)&ctx; + return JS_VALUE_GET_TAG(v) == JS_TAG_BIG_INT; +} + +static inline JS_BOOL JS_IsBool(JSValue v) +{ + return JS_VALUE_GET_TAG(v) == JS_TAG_BOOL; +} + +static inline JS_BOOL JS_IsNull(JSValue v) +{ + return JS_VALUE_GET_TAG(v) == JS_TAG_NULL; +} + +static inline JS_BOOL JS_IsUndefined(JSValue v) +{ + return JS_VALUE_GET_TAG(v) == JS_TAG_UNDEFINED; +} + +static inline JS_BOOL JS_IsException(JSValue v) +{ + return JS_VALUE_GET_TAG(v) == JS_TAG_EXCEPTION; +} + +static inline JS_BOOL JS_IsUninitialized(JSValue v) +{ + return JS_VALUE_GET_TAG(v) == JS_TAG_UNINITIALIZED; +} + +static inline JS_BOOL JS_IsString(JSValue v) +{ + return JS_VALUE_GET_TAG(v) == JS_TAG_STRING; +} + +static inline JS_BOOL JS_IsSymbol(JSValue v) +{ + return JS_VALUE_GET_TAG(v) == JS_TAG_SYMBOL; +} + +static inline JS_BOOL JS_IsObject(JSValue v) +{ + return JS_VALUE_GET_TAG(v) == JS_TAG_OBJECT; +} + +JS_EXTERN JSValue JS_Throw(JSContext *ctx, JSValue obj); +JS_EXTERN JSValue JS_GetException(JSContext *ctx); +JS_BOOL JS_HasException(JSContext *ctx); +JS_EXTERN JS_BOOL JS_IsError(JSContext *ctx, JSValue val); +JS_EXTERN void JS_ResetUncatchableError(JSContext *ctx); +JS_EXTERN JSValue JS_NewError(JSContext *ctx); +JS_EXTERN JSValue __js_printf_like(2, 3) JS_ThrowPlainError(JSContext *ctx, const char *fmt, ...); +JS_EXTERN JSValue __js_printf_like(2, 3) JS_ThrowSyntaxError(JSContext *ctx, const char *fmt, ...); +JS_EXTERN JSValue __js_printf_like(2, 3) JS_ThrowTypeError(JSContext *ctx, const char *fmt, ...); +JS_EXTERN JSValue __js_printf_like(2, 3) JS_ThrowReferenceError(JSContext *ctx, const char *fmt, ...); +JS_EXTERN JSValue __js_printf_like(2, 3) JS_ThrowRangeError(JSContext *ctx, const char *fmt, ...); +JS_EXTERN JSValue __js_printf_like(2, 3) JS_ThrowInternalError(JSContext *ctx, const char *fmt, ...); +JS_EXTERN JSValue JS_ThrowOutOfMemory(JSContext *ctx); +JS_EXTERN void JS_FreeValue(JSContext *ctx, JSValue v); +JS_EXTERN void JS_FreeValueRT(JSRuntime *rt, JSValue v); +JS_EXTERN JSValue JS_DupValue(JSContext *ctx, JSValue v); +JS_EXTERN JSValue JS_DupValueRT(JSRuntime *rt, JSValue v); +JS_EXTERN int JS_ToBool(JSContext *ctx, JSValue val); /* return -1 for JS_EXCEPTION */ +JS_EXTERN int JS_ToInt32(JSContext *ctx, int32_t *pres, JSValue val); +static inline int JS_ToUint32(JSContext *ctx, uint32_t *pres, JSValue val) +{ + return JS_ToInt32(ctx, (int32_t*)pres, val); +} +JS_EXTERN int JS_ToInt64(JSContext *ctx, int64_t *pres, JSValue val); +JS_EXTERN int JS_ToIndex(JSContext *ctx, uint64_t *plen, JSValue val); +JS_EXTERN int JS_ToFloat64(JSContext *ctx, double *pres, JSValue val); +/* return an exception if 'val' is a Number */ +JS_EXTERN int JS_ToBigInt64(JSContext *ctx, int64_t *pres, JSValue val); +JS_EXTERN int JS_ToBigUint64(JSContext *ctx, uint64_t *pres, JSValue val); +/* same as JS_ToInt64() but allow BigInt */ +JS_EXTERN int JS_ToInt64Ext(JSContext *ctx, int64_t *pres, JSValue val); + +JS_EXTERN JSValue JS_NewStringLen(JSContext *ctx, const char *str1, size_t len1); +static inline JSValue JS_NewString(JSContext *ctx, const char *str) { + return JS_NewStringLen(ctx, str, strlen(str)); +} +JS_EXTERN JSValue JS_NewAtomString(JSContext *ctx, const char *str); +JS_EXTERN JSValue JS_ToString(JSContext *ctx, JSValue val); +JS_EXTERN JSValue JS_ToPropertyKey(JSContext *ctx, JSValue val); +JS_EXTERN const char *JS_ToCStringLen2(JSContext *ctx, size_t *plen, JSValue val1, JS_BOOL cesu8); +static inline const char *JS_ToCStringLen(JSContext *ctx, size_t *plen, JSValue val1) +{ + return JS_ToCStringLen2(ctx, plen, val1, 0); +} +static inline const char *JS_ToCString(JSContext *ctx, JSValue val1) +{ + return JS_ToCStringLen2(ctx, NULL, val1, 0); +} +JS_EXTERN void JS_FreeCString(JSContext *ctx, const char *ptr); + +JS_EXTERN JSValue JS_NewObjectProtoClass(JSContext *ctx, JSValue proto, JSClassID class_id); +JS_EXTERN JSValue JS_NewObjectClass(JSContext *ctx, int class_id); +JS_EXTERN JSValue JS_NewObjectProto(JSContext *ctx, JSValue proto); +JS_EXTERN JSValue JS_NewObject(JSContext *ctx); + +JS_EXTERN JS_BOOL JS_IsFunction(JSContext* ctx, JSValue val); +JS_EXTERN JS_BOOL JS_IsConstructor(JSContext* ctx, JSValue val); +JS_EXTERN JS_BOOL JS_SetConstructorBit(JSContext *ctx, JSValue func_obj, JS_BOOL val); + +JS_EXTERN JSValue JS_NewArray(JSContext *ctx); +JS_EXTERN int JS_IsArray(JSContext *ctx, JSValue val); + +JS_EXTERN JSValue JS_NewDate(JSContext *ctx, double epoch_ms); + +JS_EXTERN JSValue JS_GetProperty(JSContext *ctx, JSValue this_obj, JSAtom prop); +JS_EXTERN JSValue JS_GetPropertyUint32(JSContext *ctx, JSValue this_obj, + uint32_t idx); +JS_EXTERN JSValue JS_GetPropertyInt64(JSContext *ctx, JSValue this_obj, + int64_t idx); +JS_EXTERN JSValue JS_GetPropertyStr(JSContext *ctx, JSValue this_obj, + const char *prop); + +JS_EXTERN int JS_SetProperty(JSContext *ctx, JSValue this_obj, + JSAtom prop, JSValue val); +JS_EXTERN int JS_SetPropertyUint32(JSContext *ctx, JSValue this_obj, + uint32_t idx, JSValue val); +JS_EXTERN int JS_SetPropertyInt64(JSContext *ctx, JSValue this_obj, + int64_t idx, JSValue val); +JS_EXTERN int JS_SetPropertyStr(JSContext *ctx, JSValue this_obj, + const char *prop, JSValue val); +JS_EXTERN int JS_HasProperty(JSContext *ctx, JSValue this_obj, JSAtom prop); +JS_EXTERN int JS_IsExtensible(JSContext *ctx, JSValue obj); +JS_EXTERN int JS_PreventExtensions(JSContext *ctx, JSValue obj); +JS_EXTERN int JS_DeleteProperty(JSContext *ctx, JSValue obj, JSAtom prop, int flags); +JS_EXTERN int JS_SetPrototype(JSContext *ctx, JSValue obj, JSValue proto_val); +JS_EXTERN JSValue JS_GetPrototype(JSContext *ctx, JSValue val); +JS_EXTERN int JS_GetLength(JSContext *ctx, JSValue obj, int64_t *pres); +JS_EXTERN int JS_SetLength(JSContext *ctx, JSValue obj, int64_t len); + +#define JS_GPN_STRING_MASK (1 << 0) +#define JS_GPN_SYMBOL_MASK (1 << 1) +#define JS_GPN_PRIVATE_MASK (1 << 2) +/* only include the enumerable properties */ +#define JS_GPN_ENUM_ONLY (1 << 4) +/* set theJSPropertyEnum.is_enumerable field */ +#define JS_GPN_SET_ENUM (1 << 5) + +JS_EXTERN int JS_GetOwnPropertyNames(JSContext *ctx, JSPropertyEnum **ptab, + uint32_t *plen, JSValue obj, int flags); +JS_EXTERN int JS_GetOwnProperty(JSContext *ctx, JSPropertyDescriptor *desc, + JSValue obj, JSAtom prop); +JS_EXTERN void JS_FreePropertyEnum(JSContext *ctx, JSPropertyEnum *tab, + uint32_t len); + +JS_EXTERN JSValue JS_Call(JSContext *ctx, JSValue func_obj, JSValue this_obj, + int argc, JSValue *argv); +JS_EXTERN JSValue JS_Invoke(JSContext *ctx, JSValue this_val, JSAtom atom, + int argc, JSValue *argv); +JS_EXTERN JSValue JS_CallConstructor(JSContext *ctx, JSValue func_obj, + int argc, JSValue *argv); +JS_EXTERN JSValue JS_CallConstructor2(JSContext *ctx, JSValue func_obj, + JSValue new_target, + int argc, JSValue *argv); +/* Try to detect if the input is a module. Returns TRUE if parsing the input + * as a module produces no syntax errors. It's a naive approach that is not + * wholly infallible: non-strict classic scripts may _parse_ okay as a module + * but not _execute_ as one (different runtime semantics.) Use with caution. + * |input| can be either ASCII or UTF-8 encoded source code. + */ +JS_EXTERN JS_BOOL JS_DetectModule(const char *input, size_t input_len); +/* 'input' must be zero terminated i.e. input[input_len] = '\0'. */ +JS_EXTERN JSValue JS_Eval(JSContext *ctx, const char *input, size_t input_len, + const char *filename, int eval_flags); +/* same as JS_Eval() but with an explicit 'this_obj' parameter */ +JS_EXTERN JSValue JS_EvalThis(JSContext *ctx, JSValue this_obj, + const char *input, size_t input_len, + const char *filename, int eval_flags); +JS_EXTERN JSValue JS_GetGlobalObject(JSContext *ctx); +JS_EXTERN int JS_IsInstanceOf(JSContext *ctx, JSValue val, JSValue obj); +JS_EXTERN int JS_DefineProperty(JSContext *ctx, JSValue this_obj, + JSAtom prop, JSValue val, + JSValue getter, JSValue setter, int flags); +JS_EXTERN int JS_DefinePropertyValue(JSContext *ctx, JSValue this_obj, + JSAtom prop, JSValue val, int flags); +JS_EXTERN int JS_DefinePropertyValueUint32(JSContext *ctx, JSValue this_obj, + uint32_t idx, JSValue val, int flags); +JS_EXTERN int JS_DefinePropertyValueStr(JSContext *ctx, JSValue this_obj, + const char *prop, JSValue val, int flags); +JS_EXTERN int JS_DefinePropertyGetSet(JSContext *ctx, JSValue this_obj, + JSAtom prop, JSValue getter, JSValue setter, + int flags); +JS_EXTERN JS_BOOL JS_SetOpaque(JSValue obj, void *opaque); +JS_EXTERN void *JS_GetOpaque(JSValue obj, JSClassID class_id); +JS_EXTERN void *JS_GetOpaque2(JSContext *ctx, JSValue obj, JSClassID class_id); +JS_EXTERN void *JS_GetAnyOpaque(JSValue obj, JSClassID *class_id); + +/* 'buf' must be zero terminated i.e. buf[buf_len] = '\0'. */ +JS_EXTERN JSValue JS_ParseJSON(JSContext *ctx, const char *buf, size_t buf_len, + const char *filename); +JS_EXTERN JSValue JS_JSONStringify(JSContext *ctx, JSValue obj, + JSValue replacer, JSValue space0); + +typedef void JSFreeArrayBufferDataFunc(JSRuntime *rt, void *opaque, void *ptr); +JS_EXTERN JSValue JS_NewArrayBuffer(JSContext *ctx, uint8_t *buf, size_t len, + JSFreeArrayBufferDataFunc *free_func, void *opaque, + JS_BOOL is_shared); +JS_EXTERN JSValue JS_NewArrayBufferCopy(JSContext *ctx, const uint8_t *buf, size_t len); +JS_EXTERN void JS_DetachArrayBuffer(JSContext *ctx, JSValue obj); +JS_EXTERN uint8_t *JS_GetArrayBuffer(JSContext *ctx, size_t *psize, JSValue obj); +JS_EXTERN JS_BOOL JS_IsArrayBuffer(JSValue obj); +JS_EXTERN uint8_t *JS_GetUint8Array(JSContext *ctx, size_t *psize, JSValue obj); +JS_EXTERN JSValue JS_GetTypedArrayBuffer(JSContext *ctx, JSValue obj, + size_t *pbyte_offset, + size_t *pbyte_length, + size_t *pbytes_per_element); +JS_EXTERN JSValue JS_NewUint8Array(JSContext *ctx, uint8_t *buf, size_t len, + JSFreeArrayBufferDataFunc *free_func, void *opaque, + JS_BOOL is_shared); +JS_EXTERN JS_BOOL JS_IsUint8Array(JSValue obj); +JS_EXTERN JSValue JS_NewUint8ArrayCopy(JSContext *ctx, const uint8_t *buf, size_t len); +typedef struct { + void *(*sab_alloc)(void *opaque, size_t size); + void (*sab_free)(void *opaque, void *ptr); + void (*sab_dup)(void *opaque, void *ptr); + void *sab_opaque; +} JSSharedArrayBufferFunctions; +JS_EXTERN void JS_SetSharedArrayBufferFunctions(JSRuntime *rt, const JSSharedArrayBufferFunctions *sf); + +typedef enum JSPromiseStateEnum { + JS_PROMISE_PENDING, + JS_PROMISE_FULFILLED, + JS_PROMISE_REJECTED, +} JSPromiseStateEnum; + +JS_EXTERN JSValue JS_NewPromiseCapability(JSContext *ctx, JSValue *resolving_funcs); +JS_EXTERN JSPromiseStateEnum JS_PromiseState(JSContext *ctx, JSValue promise); +JS_EXTERN JSValue JS_PromiseResult(JSContext *ctx, JSValue promise); + +JS_EXTERN JSValue JS_NewSymbol(JSContext *ctx, const char *description, JS_BOOL is_global); + +/* is_handled = TRUE means that the rejection is handled */ +typedef void JSHostPromiseRejectionTracker(JSContext *ctx, JSValue promise, + JSValue reason, + JS_BOOL is_handled, void *opaque); +JS_EXTERN void JS_SetHostPromiseRejectionTracker(JSRuntime *rt, JSHostPromiseRejectionTracker *cb, void *opaque); + +/* return != 0 if the JS code needs to be interrupted */ +typedef int JSInterruptHandler(JSRuntime *rt, void *opaque); +JS_EXTERN void JS_SetInterruptHandler(JSRuntime *rt, JSInterruptHandler *cb, void *opaque); +/* if can_block is TRUE, Atomics.wait() can be used */ +JS_EXTERN void JS_SetCanBlock(JSRuntime *rt, JS_BOOL can_block); +/* set the [IsHTMLDDA] internal slot */ +JS_EXTERN void JS_SetIsHTMLDDA(JSContext *ctx, JSValue obj); + +typedef struct JSModuleDef JSModuleDef; + +/* return the module specifier (allocated with js_malloc()) or NULL if + exception */ +typedef char *JSModuleNormalizeFunc(JSContext *ctx, + const char *module_base_name, + const char *module_name, void *opaque); +typedef JSModuleDef *JSModuleLoaderFunc(JSContext *ctx, + const char *module_name, void *opaque); + +/* module_normalize = NULL is allowed and invokes the default module + filename normalizer */ +JS_EXTERN void JS_SetModuleLoaderFunc(JSRuntime *rt, + JSModuleNormalizeFunc *module_normalize, + JSModuleLoaderFunc *module_loader, void *opaque); +/* return the import.meta object of a module */ +JS_EXTERN JSValue JS_GetImportMeta(JSContext *ctx, JSModuleDef *m); +JS_EXTERN JSAtom JS_GetModuleName(JSContext *ctx, JSModuleDef *m); +JSValue JS_GetModuleNamespace(JSContext *ctx, JSModuleDef *m); + +/* JS Job support */ + +typedef JSValue JSJobFunc(JSContext *ctx, int argc, JSValue *argv); +JS_EXTERN int JS_EnqueueJob(JSContext *ctx, JSJobFunc *job_func, int argc, JSValue *argv); + +JS_EXTERN JS_BOOL JS_IsJobPending(JSRuntime *rt); +JS_EXTERN int JS_ExecutePendingJob(JSRuntime *rt, JSContext **pctx); + +/* Structure to retrieve (de)serialized SharedArrayBuffer objects. */ +typedef struct JSSABTab { + uint8_t **tab; + size_t len; +} JSSABTab; + +/* Object Writer/Reader (currently only used to handle precompiled code) */ +#define JS_WRITE_OBJ_BYTECODE (1 << 0) /* allow function/module */ +#define JS_WRITE_OBJ_BSWAP (0) /* byte swapped output (obsolete, handled transparently) */ +#define JS_WRITE_OBJ_SAB (1 << 2) /* allow SharedArrayBuffer */ +#define JS_WRITE_OBJ_REFERENCE (1 << 3) /* allow object references to encode arbitrary object graph */ +#define JS_WRITE_OBJ_STRIP_SOURCE (1 << 4) /* do not write source code information */ +#define JS_WRITE_OBJ_STRIP_DEBUG (1 << 5) /* do not write debug information */ +JS_EXTERN uint8_t *JS_WriteObject(JSContext *ctx, size_t *psize, JSValue obj, int flags); +JS_EXTERN uint8_t *JS_WriteObject2(JSContext *ctx, size_t *psize, JSValue obj, + int flags, JSSABTab *psab_tab); + +#define JS_READ_OBJ_BYTECODE (1 << 0) /* allow function/module */ +#define JS_READ_OBJ_ROM_DATA (0) /* avoid duplicating 'buf' data (obsolete, broken by ICs) */ +#define JS_READ_OBJ_SAB (1 << 2) /* allow SharedArrayBuffer */ +#define JS_READ_OBJ_REFERENCE (1 << 3) /* allow object references */ +JS_EXTERN JSValue JS_ReadObject(JSContext *ctx, const uint8_t *buf, size_t buf_len, int flags); +JS_EXTERN JSValue JS_ReadObject2(JSContext *ctx, const uint8_t *buf, size_t buf_len, + int flags, JSSABTab *psab_tab); +/* instantiate and evaluate a bytecode function. Only used when + reading a script or module with JS_ReadObject() */ +JS_EXTERN JSValue JS_EvalFunction(JSContext *ctx, JSValue fun_obj); +/* load the dependencies of the module 'obj'. Useful when JS_ReadObject() + returns a module. */ +JS_EXTERN int JS_ResolveModule(JSContext *ctx, JSValue obj); + +/* only exported for os.Worker() */ +JS_EXTERN JSAtom JS_GetScriptOrModuleName(JSContext *ctx, int n_stack_levels); +/* only exported for os.Worker() */ +JS_EXTERN JSValue JS_LoadModule(JSContext *ctx, const char *basename, + const char *filename); + +/* C function definition */ +typedef enum JSCFunctionEnum { /* XXX: should rename for namespace isolation */ + JS_CFUNC_generic, + JS_CFUNC_generic_magic, + JS_CFUNC_constructor, + JS_CFUNC_constructor_magic, + JS_CFUNC_constructor_or_func, + JS_CFUNC_constructor_or_func_magic, + JS_CFUNC_f_f, + JS_CFUNC_f_f_f, + JS_CFUNC_getter, + JS_CFUNC_setter, + JS_CFUNC_getter_magic, + JS_CFUNC_setter_magic, + JS_CFUNC_iterator_next, +} JSCFunctionEnum; + +typedef union JSCFunctionType { + JSCFunction *generic; + JSValue (*generic_magic)(JSContext *ctx, JSValue this_val, int argc, JSValue *argv, int magic); + JSCFunction *constructor; + JSValue (*constructor_magic)(JSContext *ctx, JSValue new_target, int argc, JSValue *argv, int magic); + JSCFunction *constructor_or_func; + double (*f_f)(double); + double (*f_f_f)(double, double); + JSValue (*getter)(JSContext *ctx, JSValue this_val); + JSValue (*setter)(JSContext *ctx, JSValue this_val, JSValue val); + JSValue (*getter_magic)(JSContext *ctx, JSValue this_val, int magic); + JSValue (*setter_magic)(JSContext *ctx, JSValue this_val, JSValue val, int magic); + JSValue (*iterator_next)(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv, int *pdone, int magic); +} JSCFunctionType; + +JS_EXTERN JSValue JS_NewCFunction2(JSContext *ctx, JSCFunction *func, + const char *name, + int length, JSCFunctionEnum cproto, int magic); +JS_EXTERN JSValue JS_NewCFunctionData(JSContext *ctx, JSCFunctionData *func, + int length, int magic, int data_len, + JSValue *data); + +static inline JSValue JS_NewCFunction(JSContext *ctx, JSCFunction *func, const char *name, + int length) +{ + return JS_NewCFunction2(ctx, func, name, length, JS_CFUNC_generic, 0); +} + +static inline JSValue JS_NewCFunctionMagic(JSContext *ctx, JSCFunctionMagic *func, + const char *name, + int length, JSCFunctionEnum cproto, int magic) +{ + /* Used to squelch a -Wcast-function-type warning. */ + JSCFunctionType ft; + ft.generic_magic = func; + return JS_NewCFunction2(ctx, ft.generic, name, length, cproto, magic); +} +JS_EXTERN void JS_SetConstructor(JSContext *ctx, JSValue func_obj, + JSValue proto); + +/* C property definition */ + +typedef struct JSCFunctionListEntry { + const char *name; /* pure ASCII or UTF-8 encoded */ + uint8_t prop_flags; + uint8_t def_type; + int16_t magic; + union { + struct { + uint8_t length; /* XXX: should move outside union */ + uint8_t cproto; /* XXX: should move outside union */ + JSCFunctionType cfunc; + } func; + struct { + JSCFunctionType get; + JSCFunctionType set; + } getset; + struct { + const char *name; + int base; + } alias; + struct { + const struct JSCFunctionListEntry *tab; + int len; + } prop_list; + const char *str; /* pure ASCII or UTF-8 encoded */ + int32_t i32; + int64_t i64; + double f64; + } u; +} JSCFunctionListEntry; + +#define JS_DEF_CFUNC 0 +#define JS_DEF_CGETSET 1 +#define JS_DEF_CGETSET_MAGIC 2 +#define JS_DEF_PROP_STRING 3 +#define JS_DEF_PROP_INT32 4 +#define JS_DEF_PROP_INT64 5 +#define JS_DEF_PROP_DOUBLE 6 +#define JS_DEF_PROP_UNDEFINED 7 +#define JS_DEF_OBJECT 8 +#define JS_DEF_ALIAS 9 + +/* Note: c++ does not like nested designators */ +#define JS_CFUNC_DEF(name, length, func1) { name, JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE, JS_DEF_CFUNC, 0, { .func = { length, JS_CFUNC_generic, { .generic = func1 } } } } +#define JS_CFUNC_DEF2(name, length, func1, prop_flags) { name, prop_flags, JS_DEF_CFUNC, 0, { .func = { length, JS_CFUNC_generic, { .generic = func1 } } } } +#define JS_CFUNC_MAGIC_DEF(name, length, func1, magic) { name, JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE, JS_DEF_CFUNC, magic, { .func = { length, JS_CFUNC_generic_magic, { .generic_magic = func1 } } } } +#define JS_CFUNC_SPECIAL_DEF(name, length, cproto, func1) { name, JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE, JS_DEF_CFUNC, 0, { .func = { length, JS_CFUNC_ ## cproto, { .cproto = func1 } } } } +#define JS_ITERATOR_NEXT_DEF(name, length, func1, magic) { name, JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE, JS_DEF_CFUNC, magic, { .func = { length, JS_CFUNC_iterator_next, { .iterator_next = func1 } } } } +#define JS_CGETSET_DEF(name, fgetter, fsetter) { name, JS_PROP_CONFIGURABLE, JS_DEF_CGETSET, 0, { .getset = { .get = { .getter = fgetter }, .set = { .setter = fsetter } } } } +#define JS_CGETSET_DEF2(name, fgetter, fsetter, prop_flags) { name, prop_flags, JS_DEF_CGETSET, 0, { .getset = { .get = { .getter = fgetter }, .set = { .setter = fsetter } } } } +#define JS_CGETSET_MAGIC_DEF(name, fgetter, fsetter, magic) { name, JS_PROP_CONFIGURABLE, JS_DEF_CGETSET_MAGIC, magic, { .getset = { .get = { .getter_magic = fgetter }, .set = { .setter_magic = fsetter } } } } +#define JS_PROP_STRING_DEF(name, cstr, prop_flags) { name, prop_flags, JS_DEF_PROP_STRING, 0, { .str = cstr } } +#define JS_PROP_INT32_DEF(name, val, prop_flags) { name, prop_flags, JS_DEF_PROP_INT32, 0, { .i32 = val } } +#define JS_PROP_INT64_DEF(name, val, prop_flags) { name, prop_flags, JS_DEF_PROP_INT64, 0, { .i64 = val } } +#define JS_PROP_DOUBLE_DEF(name, val, prop_flags) { name, prop_flags, JS_DEF_PROP_DOUBLE, 0, { .f64 = val } } +#define JS_PROP_UNDEFINED_DEF(name, prop_flags) { name, prop_flags, JS_DEF_PROP_UNDEFINED, 0, { .i32 = 0 } } +#define JS_OBJECT_DEF(name, tab, len, prop_flags) { name, prop_flags, JS_DEF_OBJECT, 0, { .prop_list = { tab, len } } } +#define JS_ALIAS_DEF(name, from) { name, JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE, JS_DEF_ALIAS, 0, { .alias = { from, -1 } } } +#define JS_ALIAS_BASE_DEF(name, from, base) { name, JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE, JS_DEF_ALIAS, 0, { .alias = { from, base } } } + +JS_EXTERN void JS_SetPropertyFunctionList(JSContext *ctx, JSValue obj, + const JSCFunctionListEntry *tab, + int len); + +/* C module definition */ + +typedef int JSModuleInitFunc(JSContext *ctx, JSModuleDef *m); + +JS_EXTERN JSModuleDef *JS_NewCModule(JSContext *ctx, const char *name_str, + JSModuleInitFunc *func); +/* can only be called before the module is instantiated */ +JS_EXTERN int JS_AddModuleExport(JSContext *ctx, JSModuleDef *m, const char *name_str); +JS_EXTERN int JS_AddModuleExportList(JSContext *ctx, JSModuleDef *m, + const JSCFunctionListEntry *tab, int len); +/* can only be called after the module is instantiated */ +JS_EXTERN int JS_SetModuleExport(JSContext *ctx, JSModuleDef *m, const char *export_name, + JSValue val); +JS_EXTERN int JS_SetModuleExportList(JSContext *ctx, JSModuleDef *m, + const JSCFunctionListEntry *tab, int len); + +/* Version */ + +#define QJS_VERSION_MAJOR 0 +#define QJS_VERSION_MINOR 7 +#define QJS_VERSION_PATCH 0 +#define QJS_VERSION_SUFFIX "" + +JS_EXTERN const char* JS_GetVersion(void); + +/* Integration point for quickjs-libc.c, not for public use. */ +JS_EXTERN uintptr_t js_std_cmd(int cmd, ...); + +#undef JS_EXTERN +#undef js_force_inline +#undef __js_printf_like + +#ifdef __cplusplus +} /* extern "C" { */ +#endif + +#endif /* QUICKJS_H */ diff --git a/third_party/quickjs/repl.c b/third_party/quickjs/repl.c new file mode 100644 index 0000000000..80bf563d83 --- /dev/null +++ b/third_party/quickjs/repl.c @@ -0,0 +1,2929 @@ +/* File generated automatically by the QuickJS-ng compiler. */ + +#include <inttypes.h> + +const uint32_t qjsc_repl_size = 23360; + +const uint8_t qjsc_repl[23360] = { + 0x13, 0x96, 0x04, 0x01, 0x0e, 0x72, 0x65, 0x70, + 0x6c, 0x2e, 0x6a, 0x73, 0x01, 0x0e, 0x71, 0x6a, + 0x73, 0x3a, 0x73, 0x74, 0x64, 0x01, 0x0c, 0x71, + 0x6a, 0x73, 0x3a, 0x6f, 0x73, 0x01, 0x12, 0x71, + 0x6a, 0x73, 0x3a, 0x62, 0x6a, 0x73, 0x6f, 0x6e, + 0x01, 0x06, 0x73, 0x74, 0x64, 0x01, 0x04, 0x6f, + 0x73, 0x01, 0x0a, 0x62, 0x6a, 0x73, 0x6f, 0x6e, + 0x01, 0x02, 0x67, 0x01, 0x10, 0x69, 0x73, 0x46, + 0x69, 0x6e, 0x69, 0x74, 0x65, 0x01, 0x0a, 0x69, + 0x73, 0x4e, 0x61, 0x4e, 0x01, 0x10, 0x49, 0x6e, + 0x66, 0x69, 0x6e, 0x69, 0x74, 0x79, 0x01, 0x0e, + 0x63, 0x6f, 0x6e, 0x73, 0x6f, 0x6c, 0x65, 0x01, + 0x0c, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x73, 0x01, + 0x0c, 0x74, 0x68, 0x65, 0x6d, 0x65, 0x73, 0x01, + 0x0c, 0x73, 0x74, 0x79, 0x6c, 0x65, 0x73, 0x01, + 0x08, 0x75, 0x74, 0x66, 0x38, 0x01, 0x12, 0x73, + 0x68, 0x6f, 0x77, 0x5f, 0x74, 0x69, 0x6d, 0x65, + 0x01, 0x16, 0x73, 0x68, 0x6f, 0x77, 0x5f, 0x63, + 0x6f, 0x6c, 0x6f, 0x72, 0x73, 0x01, 0x16, 0x73, + 0x68, 0x6f, 0x77, 0x5f, 0x68, 0x69, 0x64, 0x64, + 0x65, 0x6e, 0x01, 0x14, 0x73, 0x68, 0x6f, 0x77, + 0x5f, 0x64, 0x65, 0x70, 0x74, 0x68, 0x01, 0x10, + 0x68, 0x65, 0x78, 0x5f, 0x6d, 0x6f, 0x64, 0x65, + 0x01, 0x14, 0x75, 0x73, 0x65, 0x5f, 0x73, 0x74, + 0x72, 0x69, 0x63, 0x74, 0x01, 0x0e, 0x68, 0x69, + 0x73, 0x74, 0x6f, 0x72, 0x79, 0x01, 0x1a, 0x68, + 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x5f, 0x69, + 0x6e, 0x64, 0x65, 0x78, 0x01, 0x14, 0x63, 0x6c, + 0x69, 0x70, 0x5f, 0x62, 0x6f, 0x61, 0x72, 0x64, + 0x01, 0x0c, 0x70, 0x73, 0x74, 0x61, 0x74, 0x65, + 0x01, 0x0c, 0x70, 0x72, 0x6f, 0x6d, 0x70, 0x74, + 0x01, 0x08, 0x70, 0x6c, 0x65, 0x6e, 0x01, 0x06, + 0x70, 0x73, 0x31, 0x01, 0x06, 0x70, 0x73, 0x32, + 0x01, 0x1e, 0x65, 0x76, 0x61, 0x6c, 0x5f, 0x73, + 0x74, 0x61, 0x72, 0x74, 0x5f, 0x74, 0x69, 0x6d, + 0x65, 0x01, 0x12, 0x65, 0x76, 0x61, 0x6c, 0x5f, + 0x74, 0x69, 0x6d, 0x65, 0x01, 0x0a, 0x6d, 0x65, + 0x78, 0x70, 0x72, 0x01, 0x0a, 0x6c, 0x65, 0x76, + 0x65, 0x6c, 0x01, 0x06, 0x63, 0x6d, 0x64, 0x01, + 0x14, 0x63, 0x75, 0x72, 0x73, 0x6f, 0x72, 0x5f, + 0x70, 0x6f, 0x73, 0x01, 0x10, 0x6c, 0x61, 0x73, + 0x74, 0x5f, 0x63, 0x6d, 0x64, 0x01, 0x1e, 0x6c, + 0x61, 0x73, 0x74, 0x5f, 0x63, 0x75, 0x72, 0x73, + 0x6f, 0x72, 0x5f, 0x70, 0x6f, 0x73, 0x01, 0x10, + 0x74, 0x68, 0x69, 0x73, 0x5f, 0x66, 0x75, 0x6e, + 0x01, 0x10, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x66, + 0x75, 0x6e, 0x01, 0x14, 0x71, 0x75, 0x6f, 0x74, + 0x65, 0x5f, 0x66, 0x6c, 0x61, 0x67, 0x01, 0x14, + 0x75, 0x74, 0x66, 0x38, 0x5f, 0x73, 0x74, 0x61, + 0x74, 0x65, 0x01, 0x10, 0x75, 0x74, 0x66, 0x38, + 0x5f, 0x76, 0x61, 0x6c, 0x01, 0x0e, 0x74, 0x65, + 0x72, 0x6d, 0x5f, 0x66, 0x64, 0x01, 0x1a, 0x74, + 0x65, 0x72, 0x6d, 0x5f, 0x72, 0x65, 0x61, 0x64, + 0x5f, 0x62, 0x75, 0x66, 0x01, 0x14, 0x74, 0x65, + 0x72, 0x6d, 0x5f, 0x77, 0x69, 0x64, 0x74, 0x68, + 0x01, 0x1a, 0x74, 0x65, 0x72, 0x6d, 0x5f, 0x63, + 0x75, 0x72, 0x73, 0x6f, 0x72, 0x5f, 0x78, 0x01, + 0x10, 0x74, 0x65, 0x72, 0x6d, 0x49, 0x6e, 0x69, + 0x74, 0x01, 0x1c, 0x73, 0x69, 0x67, 0x69, 0x6e, + 0x74, 0x5f, 0x68, 0x61, 0x6e, 0x64, 0x6c, 0x65, + 0x72, 0x01, 0x22, 0x74, 0x65, 0x72, 0x6d, 0x5f, + 0x72, 0x65, 0x61, 0x64, 0x5f, 0x68, 0x61, 0x6e, + 0x64, 0x6c, 0x65, 0x72, 0x01, 0x16, 0x68, 0x61, + 0x6e, 0x64, 0x6c, 0x65, 0x5f, 0x62, 0x79, 0x74, + 0x65, 0x01, 0x10, 0x69, 0x73, 0x5f, 0x61, 0x6c, + 0x70, 0x68, 0x61, 0x01, 0x10, 0x69, 0x73, 0x5f, + 0x64, 0x69, 0x67, 0x69, 0x74, 0x01, 0x0e, 0x69, + 0x73, 0x5f, 0x77, 0x6f, 0x72, 0x64, 0x01, 0x14, + 0x75, 0x63, 0x73, 0x5f, 0x6c, 0x65, 0x6e, 0x67, + 0x74, 0x68, 0x01, 0x2a, 0x69, 0x73, 0x5f, 0x74, + 0x72, 0x61, 0x69, 0x6c, 0x69, 0x6e, 0x67, 0x5f, + 0x73, 0x75, 0x72, 0x72, 0x6f, 0x67, 0x61, 0x74, + 0x65, 0x01, 0x16, 0x69, 0x73, 0x5f, 0x62, 0x61, + 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x64, 0x01, 0x20, + 0x70, 0x72, 0x69, 0x6e, 0x74, 0x5f, 0x63, 0x6f, + 0x6c, 0x6f, 0x72, 0x5f, 0x74, 0x65, 0x78, 0x74, + 0x01, 0x12, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x5f, + 0x63, 0x73, 0x69, 0x01, 0x16, 0x6d, 0x6f, 0x76, + 0x65, 0x5f, 0x63, 0x75, 0x72, 0x73, 0x6f, 0x72, + 0x01, 0x0c, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, + 0x01, 0x0c, 0x69, 0x6e, 0x73, 0x65, 0x72, 0x74, + 0x01, 0x1a, 0x71, 0x75, 0x6f, 0x74, 0x65, 0x64, + 0x5f, 0x69, 0x6e, 0x73, 0x65, 0x72, 0x74, 0x01, + 0x0a, 0x61, 0x62, 0x6f, 0x72, 0x74, 0x01, 0x0a, + 0x61, 0x6c, 0x65, 0x72, 0x74, 0x01, 0x22, 0x62, + 0x65, 0x67, 0x69, 0x6e, 0x6e, 0x69, 0x6e, 0x67, + 0x5f, 0x6f, 0x66, 0x5f, 0x6c, 0x69, 0x6e, 0x65, + 0x01, 0x16, 0x65, 0x6e, 0x64, 0x5f, 0x6f, 0x66, + 0x5f, 0x6c, 0x69, 0x6e, 0x65, 0x01, 0x18, 0x66, + 0x6f, 0x72, 0x77, 0x61, 0x72, 0x64, 0x5f, 0x63, + 0x68, 0x61, 0x72, 0x01, 0x1a, 0x62, 0x61, 0x63, + 0x6b, 0x77, 0x61, 0x72, 0x64, 0x5f, 0x63, 0x68, + 0x61, 0x72, 0x01, 0x22, 0x73, 0x6b, 0x69, 0x70, + 0x5f, 0x77, 0x6f, 0x72, 0x64, 0x5f, 0x66, 0x6f, + 0x72, 0x77, 0x61, 0x72, 0x64, 0x01, 0x24, 0x73, + 0x6b, 0x69, 0x70, 0x5f, 0x77, 0x6f, 0x72, 0x64, + 0x5f, 0x62, 0x61, 0x63, 0x6b, 0x77, 0x61, 0x72, + 0x64, 0x01, 0x18, 0x66, 0x6f, 0x72, 0x77, 0x61, + 0x72, 0x64, 0x5f, 0x77, 0x6f, 0x72, 0x64, 0x01, + 0x1a, 0x62, 0x61, 0x63, 0x6b, 0x77, 0x61, 0x72, + 0x64, 0x5f, 0x77, 0x6f, 0x72, 0x64, 0x01, 0x18, + 0x63, 0x6c, 0x65, 0x61, 0x72, 0x5f, 0x73, 0x63, + 0x72, 0x65, 0x65, 0x6e, 0x01, 0x16, 0x61, 0x63, + 0x63, 0x65, 0x70, 0x74, 0x5f, 0x6c, 0x69, 0x6e, + 0x65, 0x01, 0x16, 0x68, 0x69, 0x73, 0x74, 0x6f, + 0x72, 0x79, 0x5f, 0x61, 0x64, 0x64, 0x01, 0x20, + 0x70, 0x72, 0x65, 0x76, 0x69, 0x6f, 0x75, 0x73, + 0x5f, 0x68, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, + 0x01, 0x18, 0x6e, 0x65, 0x78, 0x74, 0x5f, 0x68, + 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x01, 0x1c, + 0x68, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x5f, + 0x73, 0x65, 0x61, 0x72, 0x63, 0x68, 0x01, 0x2e, + 0x68, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x5f, + 0x73, 0x65, 0x61, 0x72, 0x63, 0x68, 0x5f, 0x62, + 0x61, 0x63, 0x6b, 0x77, 0x61, 0x72, 0x64, 0x01, + 0x2c, 0x68, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, + 0x5f, 0x73, 0x65, 0x61, 0x72, 0x63, 0x68, 0x5f, + 0x66, 0x6f, 0x72, 0x77, 0x61, 0x72, 0x64, 0x01, + 0x1e, 0x64, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x5f, + 0x63, 0x68, 0x61, 0x72, 0x5f, 0x64, 0x69, 0x72, + 0x01, 0x16, 0x64, 0x65, 0x6c, 0x65, 0x74, 0x65, + 0x5f, 0x63, 0x68, 0x61, 0x72, 0x01, 0x12, 0x63, + 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x5f, 0x64, + 0x01, 0x28, 0x62, 0x61, 0x63, 0x6b, 0x77, 0x61, + 0x72, 0x64, 0x5f, 0x64, 0x65, 0x6c, 0x65, 0x74, + 0x65, 0x5f, 0x63, 0x68, 0x61, 0x72, 0x01, 0x1e, + 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x73, + 0x65, 0x5f, 0x63, 0x68, 0x61, 0x72, 0x73, 0x01, + 0x1e, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, + 0x73, 0x65, 0x5f, 0x77, 0x6f, 0x72, 0x64, 0x73, + 0x01, 0x16, 0x75, 0x70, 0x63, 0x61, 0x73, 0x65, + 0x5f, 0x77, 0x6f, 0x72, 0x64, 0x01, 0x1a, 0x64, + 0x6f, 0x77, 0x6e, 0x63, 0x61, 0x73, 0x65, 0x5f, + 0x77, 0x6f, 0x72, 0x64, 0x01, 0x16, 0x6b, 0x69, + 0x6c, 0x6c, 0x5f, 0x72, 0x65, 0x67, 0x69, 0x6f, + 0x6e, 0x01, 0x12, 0x6b, 0x69, 0x6c, 0x6c, 0x5f, + 0x6c, 0x69, 0x6e, 0x65, 0x01, 0x24, 0x62, 0x61, + 0x63, 0x6b, 0x77, 0x61, 0x72, 0x64, 0x5f, 0x6b, + 0x69, 0x6c, 0x6c, 0x5f, 0x6c, 0x69, 0x6e, 0x65, + 0x01, 0x12, 0x6b, 0x69, 0x6c, 0x6c, 0x5f, 0x77, + 0x6f, 0x72, 0x64, 0x01, 0x24, 0x62, 0x61, 0x63, + 0x6b, 0x77, 0x61, 0x72, 0x64, 0x5f, 0x6b, 0x69, + 0x6c, 0x6c, 0x5f, 0x77, 0x6f, 0x72, 0x64, 0x01, + 0x08, 0x79, 0x61, 0x6e, 0x6b, 0x01, 0x12, 0x63, + 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x5f, 0x63, + 0x01, 0x0a, 0x72, 0x65, 0x73, 0x65, 0x74, 0x01, + 0x20, 0x67, 0x65, 0x74, 0x5f, 0x63, 0x6f, 0x6e, + 0x74, 0x65, 0x78, 0x74, 0x5f, 0x77, 0x6f, 0x72, + 0x64, 0x01, 0x24, 0x67, 0x65, 0x74, 0x5f, 0x63, + 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x5f, 0x6f, + 0x62, 0x6a, 0x65, 0x63, 0x74, 0x01, 0x1e, 0x67, + 0x65, 0x74, 0x5f, 0x63, 0x6f, 0x6d, 0x70, 0x6c, + 0x65, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x01, 0x14, + 0x63, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x69, + 0x6f, 0x6e, 0x01, 0x10, 0x63, 0x6f, 0x6d, 0x6d, + 0x61, 0x6e, 0x64, 0x73, 0x01, 0x0c, 0x64, 0x75, + 0x70, 0x73, 0x74, 0x72, 0x01, 0x1a, 0x72, 0x65, + 0x61, 0x64, 0x6c, 0x69, 0x6e, 0x65, 0x5f, 0x6b, + 0x65, 0x79, 0x73, 0x01, 0x1c, 0x72, 0x65, 0x61, + 0x64, 0x6c, 0x69, 0x6e, 0x65, 0x5f, 0x73, 0x74, + 0x61, 0x74, 0x65, 0x01, 0x16, 0x72, 0x65, 0x61, + 0x64, 0x6c, 0x69, 0x6e, 0x65, 0x5f, 0x63, 0x62, + 0x01, 0x2a, 0x72, 0x65, 0x61, 0x64, 0x6c, 0x69, + 0x6e, 0x65, 0x5f, 0x70, 0x72, 0x69, 0x6e, 0x74, + 0x5f, 0x70, 0x72, 0x6f, 0x6d, 0x70, 0x74, 0x01, + 0x1c, 0x72, 0x65, 0x61, 0x64, 0x6c, 0x69, 0x6e, + 0x65, 0x5f, 0x73, 0x74, 0x61, 0x72, 0x74, 0x01, + 0x16, 0x68, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x5f, + 0x63, 0x68, 0x61, 0x72, 0x01, 0x14, 0x68, 0x61, + 0x6e, 0x64, 0x6c, 0x65, 0x5f, 0x6b, 0x65, 0x79, + 0x01, 0x20, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, + 0x5f, 0x74, 0x6f, 0x5f, 0x73, 0x74, 0x72, 0x69, + 0x6e, 0x67, 0x01, 0x20, 0x62, 0x69, 0x67, 0x69, + 0x6e, 0x74, 0x5f, 0x74, 0x6f, 0x5f, 0x73, 0x74, + 0x72, 0x69, 0x6e, 0x67, 0x01, 0x08, 0x75, 0x74, + 0x69, 0x6c, 0x01, 0x0a, 0x70, 0x72, 0x69, 0x6e, + 0x74, 0x01, 0x20, 0x68, 0x61, 0x6e, 0x64, 0x6c, + 0x65, 0x5f, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, + 0x69, 0x76, 0x65, 0x01, 0x08, 0x68, 0x65, 0x6c, + 0x70, 0x01, 0x08, 0x6c, 0x6f, 0x61, 0x64, 0x01, + 0x08, 0x65, 0x78, 0x69, 0x74, 0x01, 0x0e, 0x74, + 0x6f, 0x5f, 0x62, 0x6f, 0x6f, 0x6c, 0x01, 0x14, + 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x69, 0x76, + 0x65, 0x73, 0x01, 0x12, 0x63, 0x6d, 0x64, 0x5f, + 0x73, 0x74, 0x61, 0x72, 0x74, 0x01, 0x24, 0x63, + 0x6d, 0x64, 0x5f, 0x72, 0x65, 0x61, 0x64, 0x6c, + 0x69, 0x6e, 0x65, 0x5f, 0x73, 0x74, 0x61, 0x72, + 0x74, 0x01, 0x26, 0x72, 0x65, 0x61, 0x64, 0x6c, + 0x69, 0x6e, 0x65, 0x5f, 0x68, 0x61, 0x6e, 0x64, + 0x6c, 0x65, 0x5f, 0x63, 0x6d, 0x64, 0x01, 0x14, + 0x68, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x5f, 0x63, + 0x6d, 0x64, 0x01, 0x1c, 0x65, 0x76, 0x61, 0x6c, + 0x5f, 0x61, 0x6e, 0x64, 0x5f, 0x70, 0x72, 0x69, + 0x6e, 0x74, 0x01, 0x22, 0x70, 0x72, 0x69, 0x6e, + 0x74, 0x5f, 0x65, 0x76, 0x61, 0x6c, 0x5f, 0x72, + 0x65, 0x73, 0x75, 0x6c, 0x74, 0x01, 0x20, 0x70, + 0x72, 0x69, 0x6e, 0x74, 0x5f, 0x65, 0x76, 0x61, + 0x6c, 0x5f, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x01, + 0x1c, 0x68, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x5f, + 0x63, 0x6d, 0x64, 0x5f, 0x65, 0x6e, 0x64, 0x01, + 0x16, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x69, 0x7a, + 0x65, 0x5f, 0x6a, 0x73, 0x01, 0x16, 0x63, 0x6f, + 0x6e, 0x66, 0x69, 0x67, 0x5f, 0x66, 0x69, 0x6c, + 0x65, 0x01, 0x18, 0x73, 0x61, 0x76, 0x65, 0x5f, + 0x68, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x01, + 0x18, 0x6c, 0x6f, 0x61, 0x64, 0x5f, 0x68, 0x69, + 0x73, 0x74, 0x6f, 0x72, 0x79, 0x01, 0x16, 0x6c, + 0x6f, 0x61, 0x64, 0x5f, 0x63, 0x6f, 0x6e, 0x66, + 0x69, 0x67, 0x01, 0x06, 0x74, 0x61, 0x62, 0x01, + 0x0c, 0x66, 0x69, 0x6c, 0x65, 0x6e, 0x6f, 0x01, + 0x0c, 0x69, 0x73, 0x61, 0x74, 0x74, 0x79, 0x01, + 0x1a, 0x74, 0x74, 0x79, 0x47, 0x65, 0x74, 0x57, + 0x69, 0x6e, 0x53, 0x69, 0x7a, 0x65, 0x01, 0x12, + 0x74, 0x74, 0x79, 0x53, 0x65, 0x74, 0x52, 0x61, + 0x77, 0x01, 0x0c, 0x73, 0x69, 0x67, 0x6e, 0x61, + 0x6c, 0x01, 0x0c, 0x53, 0x49, 0x47, 0x49, 0x4e, + 0x54, 0x01, 0x1c, 0x73, 0x65, 0x74, 0x52, 0x65, + 0x61, 0x64, 0x48, 0x61, 0x6e, 0x64, 0x6c, 0x65, + 0x72, 0x01, 0x02, 0x6c, 0x01, 0x02, 0x69, 0x01, + 0x08, 0x72, 0x65, 0x61, 0x64, 0x01, 0x0c, 0x62, + 0x75, 0x66, 0x66, 0x65, 0x72, 0x01, 0x02, 0x63, + 0x01, 0x02, 0x41, 0x01, 0x02, 0x5a, 0x01, 0x02, + 0x61, 0x01, 0x02, 0x7a, 0x01, 0x02, 0x5f, 0x01, + 0x02, 0x24, 0x01, 0x06, 0x73, 0x74, 0x72, 0x01, + 0x06, 0x6c, 0x65, 0x6e, 0x01, 0x0e, 0x73, 0x74, + 0x72, 0x5f, 0x6c, 0x65, 0x6e, 0x01, 0x14, 0x63, + 0x68, 0x61, 0x72, 0x43, 0x6f, 0x64, 0x65, 0x41, + 0x74, 0x01, 0x02, 0x64, 0x01, 0x16, 0x63, 0x6f, + 0x64, 0x65, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x41, + 0x74, 0x01, 0x02, 0x62, 0x01, 0x04, 0x28, 0x29, + 0x01, 0x04, 0x5b, 0x5d, 0x01, 0x04, 0x7b, 0x7d, + 0x01, 0x0a, 0x73, 0x74, 0x61, 0x72, 0x74, 0x01, + 0x16, 0x73, 0x74, 0x79, 0x6c, 0x65, 0x5f, 0x6e, + 0x61, 0x6d, 0x65, 0x73, 0x01, 0x02, 0x6a, 0x01, + 0x0a, 0x73, 0x74, 0x79, 0x6c, 0x65, 0x01, 0x08, + 0x70, 0x75, 0x74, 0x73, 0x01, 0x08, 0x6e, 0x6f, + 0x6e, 0x65, 0x01, 0x12, 0x73, 0x75, 0x62, 0x73, + 0x74, 0x72, 0x69, 0x6e, 0x67, 0x01, 0x02, 0x6e, + 0x01, 0x08, 0x63, 0x6f, 0x64, 0x65, 0x01, 0x04, + 0x1b, 0x5b, 0x01, 0x0a, 0x64, 0x65, 0x6c, 0x74, + 0x61, 0x01, 0x02, 0x0a, 0x01, 0x06, 0x6d, 0x69, + 0x6e, 0x01, 0x02, 0x43, 0x01, 0x02, 0x44, 0x01, + 0x0e, 0x63, 0x6d, 0x64, 0x5f, 0x6c, 0x65, 0x6e, + 0x01, 0x14, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x73, + 0x74, 0x61, 0x74, 0x65, 0x01, 0x04, 0x20, 0x08, + 0x01, 0x06, 0x1b, 0x5b, 0x4a, 0x01, 0x06, 0x6f, + 0x75, 0x74, 0x01, 0x0a, 0x66, 0x6c, 0x75, 0x73, + 0x68, 0x01, 0x0c, 0x63, 0x68, 0x61, 0x72, 0x41, + 0x74, 0x01, 0x06, 0x70, 0x6f, 0x73, 0x01, 0x0a, + 0x63, 0x6c, 0x65, 0x61, 0x72, 0x01, 0x12, 0x74, + 0x72, 0x69, 0x6d, 0x52, 0x69, 0x67, 0x68, 0x74, + 0x01, 0x08, 0x70, 0x75, 0x73, 0x68, 0x01, 0x06, + 0x64, 0x69, 0x72, 0x01, 0x06, 0x65, 0x6e, 0x64, + 0x01, 0x04, 0x70, 0x31, 0x01, 0x04, 0x70, 0x32, + 0x01, 0x04, 0x70, 0x34, 0x01, 0x04, 0x70, 0x33, + 0x01, 0x16, 0x74, 0x6f, 0x55, 0x70, 0x70, 0x65, + 0x72, 0x43, 0x61, 0x73, 0x65, 0x01, 0x16, 0x74, + 0x6f, 0x4c, 0x6f, 0x77, 0x65, 0x72, 0x43, 0x61, + 0x73, 0x65, 0x01, 0x02, 0x73, 0x01, 0x3c, 0x0a, + 0x28, 0x50, 0x72, 0x65, 0x73, 0x73, 0x20, 0x43, + 0x74, 0x72, 0x6c, 0x2d, 0x43, 0x20, 0x61, 0x67, + 0x61, 0x69, 0x6e, 0x20, 0x74, 0x6f, 0x20, 0x71, + 0x75, 0x69, 0x74, 0x29, 0x0a, 0x01, 0x08, 0x6c, + 0x69, 0x6e, 0x65, 0x01, 0x0a, 0x73, 0x6c, 0x69, + 0x63, 0x65, 0x01, 0x08, 0x62, 0x61, 0x73, 0x65, + 0x01, 0x10, 0x62, 0x61, 0x73, 0x65, 0x5f, 0x70, + 0x6f, 0x73, 0x01, 0x06, 0x6f, 0x62, 0x6a, 0x01, + 0x02, 0x5c, 0x01, 0x02, 0x2e, 0x01, 0x12, 0x27, + 0x22, 0x60, 0x40, 0x23, 0x29, 0x5d, 0x7d, 0x5c, + 0x01, 0x0e, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x4f, + 0x66, 0x01, 0x02, 0x27, 0x01, 0x02, 0x22, 0x01, + 0x02, 0x60, 0x01, 0x02, 0x5d, 0x01, 0x02, 0x2f, + 0x01, 0x0a, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x01, + 0x0e, 0x63, 0x74, 0x78, 0x5f, 0x6f, 0x62, 0x6a, + 0x01, 0x02, 0x72, 0x01, 0x0a, 0x70, 0x61, 0x72, + 0x65, 0x6e, 0x01, 0x0a, 0x70, 0x72, 0x6f, 0x70, + 0x73, 0x01, 0x08, 0x70, 0x72, 0x6f, 0x70, 0x01, + 0x0c, 0x73, 0x79, 0x6d, 0x63, 0x6d, 0x70, 0x01, + 0x26, 0x67, 0x65, 0x74, 0x4f, 0x77, 0x6e, 0x50, + 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x79, 0x4e, + 0x61, 0x6d, 0x65, 0x73, 0x01, 0x14, 0x73, 0x74, + 0x61, 0x72, 0x74, 0x73, 0x57, 0x69, 0x74, 0x68, + 0x01, 0x08, 0x73, 0x6f, 0x72, 0x74, 0x01, 0x06, + 0x63, 0x74, 0x78, 0x01, 0x06, 0x72, 0x65, 0x73, + 0x01, 0x02, 0x74, 0x01, 0x12, 0x6d, 0x61, 0x78, + 0x5f, 0x77, 0x69, 0x64, 0x74, 0x68, 0x01, 0x06, + 0x63, 0x6f, 0x6c, 0x01, 0x0c, 0x6e, 0x5f, 0x63, + 0x6f, 0x6c, 0x73, 0x01, 0x06, 0x72, 0x6f, 0x77, + 0x01, 0x0c, 0x6e, 0x5f, 0x72, 0x6f, 0x77, 0x73, + 0x01, 0x02, 0x6d, 0x01, 0x02, 0x28, 0x01, 0x02, + 0x29, 0x01, 0x06, 0x6d, 0x61, 0x78, 0x01, 0x0a, + 0x66, 0x6c, 0x6f, 0x6f, 0x72, 0x01, 0x08, 0x63, + 0x65, 0x69, 0x6c, 0x01, 0x0c, 0x70, 0x61, 0x64, + 0x45, 0x6e, 0x64, 0x01, 0x0a, 0x63, 0x6f, 0x75, + 0x6e, 0x74, 0x01, 0x0c, 0x64, 0x65, 0x66, 0x73, + 0x74, 0x72, 0x01, 0x04, 0x63, 0x62, 0x01, 0x02, + 0x20, 0x01, 0x0e, 0x74, 0x6f, 0x46, 0x69, 0x78, + 0x65, 0x64, 0x01, 0x04, 0x63, 0x31, 0x01, 0x1a, + 0x66, 0x72, 0x6f, 0x6d, 0x43, 0x6f, 0x64, 0x65, + 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x01, 0x02, 0x1b, + 0x01, 0x02, 0x5b, 0x01, 0x02, 0x4f, 0x01, 0x02, + 0x3b, 0x01, 0x06, 0x66, 0x75, 0x6e, 0x01, 0x0a, + 0x72, 0x61, 0x64, 0x69, 0x78, 0x01, 0x04, 0x2d, + 0x30, 0x01, 0x02, 0x2d, 0x01, 0x04, 0x30, 0x78, + 0x01, 0x06, 0x76, 0x61, 0x6c, 0x01, 0x12, 0x6d, + 0x61, 0x78, 0x5f, 0x64, 0x65, 0x70, 0x74, 0x68, + 0x01, 0x14, 0x75, 0x73, 0x65, 0x5f, 0x63, 0x6f, + 0x6c, 0x6f, 0x72, 0x73, 0x01, 0x0e, 0x6f, 0x70, + 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x01, 0x16, 0x62, + 0x72, 0x65, 0x61, 0x6b, 0x4c, 0x65, 0x6e, 0x67, + 0x74, 0x68, 0x01, 0x1c, 0x6d, 0x61, 0x78, 0x41, + 0x72, 0x72, 0x61, 0x79, 0x4c, 0x65, 0x6e, 0x67, + 0x74, 0x68, 0x01, 0x1e, 0x6d, 0x61, 0x78, 0x4f, + 0x62, 0x6a, 0x65, 0x63, 0x74, 0x4c, 0x65, 0x6e, + 0x67, 0x74, 0x68, 0x01, 0x1e, 0x6d, 0x61, 0x78, + 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x4c, 0x65, + 0x6e, 0x67, 0x74, 0x68, 0x01, 0x08, 0x72, 0x65, + 0x66, 0x73, 0x01, 0x0c, 0x74, 0x6f, 0x6b, 0x65, + 0x6e, 0x73, 0x01, 0x0c, 0x6f, 0x75, 0x74, 0x70, + 0x75, 0x74, 0x01, 0x14, 0x6c, 0x61, 0x73, 0x74, + 0x5f, 0x73, 0x74, 0x79, 0x6c, 0x65, 0x01, 0x12, + 0x71, 0x75, 0x6f, 0x74, 0x65, 0x5f, 0x73, 0x74, + 0x72, 0x01, 0x14, 0x70, 0x75, 0x73, 0x68, 0x5f, + 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x01, 0x18, 0x61, + 0x70, 0x70, 0x65, 0x6e, 0x64, 0x5f, 0x74, 0x6f, + 0x6b, 0x65, 0x6e, 0x01, 0x12, 0x63, 0x6c, 0x61, + 0x73, 0x73, 0x5f, 0x74, 0x61, 0x67, 0x01, 0x12, + 0x70, 0x72, 0x69, 0x6e, 0x74, 0x5f, 0x72, 0x65, + 0x63, 0x01, 0x14, 0x6f, 0x75, 0x74, 0x70, 0x75, + 0x74, 0x5f, 0x73, 0x74, 0x72, 0x01, 0x1e, 0x6f, + 0x75, 0x74, 0x70, 0x75, 0x74, 0x5f, 0x70, 0x72, + 0x6f, 0x70, 0x6e, 0x61, 0x6d, 0x65, 0x01, 0x1a, + 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x5f, 0x70, + 0x72, 0x65, 0x74, 0x74, 0x79, 0x01, 0x10, 0x69, + 0x73, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x01, + 0x16, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x77, + 0x69, 0x64, 0x74, 0x68, 0x01, 0x1a, 0x6f, 0x75, + 0x74, 0x70, 0x75, 0x74, 0x5f, 0x73, 0x69, 0x6e, + 0x67, 0x6c, 0x65, 0x01, 0x1a, 0x6f, 0x75, 0x74, + 0x70, 0x75, 0x74, 0x5f, 0x73, 0x70, 0x61, 0x63, + 0x65, 0x73, 0x01, 0x1a, 0x6f, 0x75, 0x74, 0x70, + 0x75, 0x74, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x6e, + 0x74, 0x01, 0x06, 0x6f, 0x70, 0x74, 0x01, 0x06, + 0x64, 0x65, 0x66, 0x01, 0x10, 0x69, 0x6e, 0x63, + 0x6c, 0x75, 0x64, 0x65, 0x73, 0x01, 0x12, 0x73, + 0x74, 0x72, 0x69, 0x6e, 0x67, 0x69, 0x66, 0x79, + 0x01, 0x14, 0x72, 0x65, 0x70, 0x6c, 0x61, 0x63, + 0x65, 0x41, 0x6c, 0x6c, 0x01, 0x04, 0x5c, 0x22, + 0x01, 0x02, 0x6f, 0x01, 0x08, 0x63, 0x61, 0x6c, + 0x6c, 0x01, 0x04, 0x6e, 0x30, 0x01, 0x02, 0x6b, + 0x01, 0x06, 0x6b, 0x65, 0x79, 0x01, 0x08, 0x74, + 0x79, 0x70, 0x65, 0x01, 0x0e, 0x69, 0x73, 0x61, + 0x72, 0x72, 0x61, 0x79, 0x01, 0x0e, 0x6e, 0x6f, + 0x69, 0x6e, 0x64, 0x65, 0x78, 0x01, 0x0c, 0x6e, + 0x6f, 0x6b, 0x65, 0x79, 0x73, 0x01, 0x0a, 0x62, + 0x72, 0x61, 0x63, 0x65, 0x01, 0x06, 0x73, 0x65, + 0x70, 0x01, 0x12, 0x6f, 0x62, 0x6a, 0x5f, 0x69, + 0x6e, 0x64, 0x65, 0x78, 0x01, 0x06, 0x74, 0x61, + 0x67, 0x01, 0x08, 0x63, 0x6f, 0x6e, 0x73, 0x01, + 0x08, 0x64, 0x65, 0x73, 0x63, 0x01, 0x0c, 0x66, + 0x69, 0x65, 0x6c, 0x64, 0x73, 0x01, 0x06, 0x2e, + 0x2e, 0x2e, 0x01, 0x16, 0x5b, 0x43, 0x69, 0x72, + 0x63, 0x75, 0x6c, 0x61, 0x72, 0x20, 0x2a, 0x01, + 0x0a, 0x44, 0x61, 0x74, 0x65, 0x20, 0x01, 0x16, + 0x74, 0x6f, 0x47, 0x4d, 0x54, 0x53, 0x74, 0x72, + 0x69, 0x6e, 0x67, 0x01, 0x04, 0x3a, 0x20, 0x01, + 0x0e, 0x69, 0x73, 0x41, 0x72, 0x72, 0x61, 0x79, + 0x01, 0x06, 0x29, 0x20, 0x5b, 0x01, 0x16, 0x5b, + 0x46, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, + 0x3a, 0x20, 0x01, 0x2c, 0x5b, 0x46, 0x75, 0x6e, + 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x28, 0x61, + 0x6e, 0x6f, 0x6e, 0x79, 0x6d, 0x6f, 0x75, 0x73, + 0x29, 0x5d, 0x01, 0x04, 0x20, 0x5b, 0x01, 0x06, + 0x5d, 0x20, 0x7b, 0x01, 0x26, 0x3a, 0x20, 0x6e, + 0x75, 0x6c, 0x6c, 0x20, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x74, 0x79, 0x70, 0x65, 0x5d, 0x20, 0x7b, + 0x01, 0x04, 0x20, 0x7b, 0x01, 0x02, 0x7b, 0x01, + 0x02, 0x7d, 0x01, 0x02, 0x3c, 0x01, 0x1a, 0x20, + 0x65, 0x6d, 0x70, 0x74, 0x79, 0x20, 0x69, 0x74, + 0x65, 0x6d, 0x73, 0x3e, 0x01, 0x0e, 0x3c, 0x65, + 0x6d, 0x70, 0x74, 0x79, 0x3e, 0x01, 0x08, 0x2e, + 0x2e, 0x2e, 0x20, 0x01, 0x16, 0x20, 0x6d, 0x6f, + 0x72, 0x65, 0x20, 0x69, 0x74, 0x65, 0x6d, 0x73, + 0x01, 0x02, 0x3a, 0x01, 0x0c, 0x47, 0x65, 0x74, + 0x74, 0x65, 0x72, 0x01, 0x0c, 0x53, 0x65, 0x74, + 0x74, 0x65, 0x72, 0x01, 0x20, 0x20, 0x6d, 0x6f, + 0x72, 0x65, 0x20, 0x70, 0x72, 0x6f, 0x70, 0x65, + 0x72, 0x74, 0x69, 0x65, 0x73, 0x01, 0x06, 0x70, + 0x6f, 0x70, 0x01, 0x0c, 0x3c, 0x72, 0x65, 0x66, + 0x20, 0x2a, 0x01, 0x04, 0x3e, 0x20, 0x01, 0x0a, + 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x01, 0x10, 0x70, + 0x72, 0x6f, 0x70, 0x6e, 0x61, 0x6d, 0x65, 0x01, + 0x0a, 0x63, 0x68, 0x75, 0x6e, 0x6b, 0x01, 0x0c, + 0x72, 0x65, 0x67, 0x65, 0x78, 0x70, 0x01, 0x14, + 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x01, 0x02, 0x2c, 0x01, 0x0a, 0x6f, + 0x74, 0x68, 0x65, 0x72, 0x01, 0x08, 0x64, 0x61, + 0x74, 0x65, 0x01, 0x06, 0x4e, 0x61, 0x4e, 0x01, + 0x0e, 0x6b, 0x65, 0x79, 0x77, 0x6f, 0x72, 0x64, + 0x01, 0x02, 0x77, 0x01, 0x0a, 0x77, 0x69, 0x64, + 0x74, 0x68, 0x01, 0x0c, 0x73, 0x65, 0x70, 0x6c, + 0x65, 0x6e, 0x01, 0x08, 0x6c, 0x61, 0x73, 0x74, + 0x01, 0x04, 0x2c, 0x20, 0x01, 0x0c, 0x72, 0x65, + 0x70, 0x65, 0x61, 0x74, 0x01, 0x0c, 0x69, 0x6e, + 0x64, 0x65, 0x6e, 0x74, 0x01, 0x16, 0x61, 0x76, + 0x61, 0x69, 0x6c, 0x5f, 0x77, 0x69, 0x64, 0x74, + 0x68, 0x01, 0x0a, 0x66, 0x69, 0x72, 0x73, 0x74, + 0x01, 0x08, 0x63, 0x6f, 0x6c, 0x73, 0x01, 0x10, + 0x63, 0x6f, 0x6c, 0x77, 0x69, 0x64, 0x74, 0x68, + 0x01, 0x10, 0x65, 0x6e, 0x64, 0x73, 0x57, 0x69, + 0x74, 0x68, 0x01, 0x14, 0x73, 0x68, 0x6f, 0x77, + 0x48, 0x69, 0x64, 0x64, 0x65, 0x6e, 0x01, 0x0a, + 0x64, 0x65, 0x70, 0x74, 0x68, 0x01, 0x0e, 0x69, + 0x6e, 0x73, 0x70, 0x65, 0x63, 0x74, 0x01, 0x0e, + 0x70, 0x61, 0x72, 0x74, 0x69, 0x61, 0x6c, 0x01, + 0x02, 0x70, 0x01, 0x02, 0x3f, 0x01, 0x08, 0x74, + 0x72, 0x69, 0x6d, 0x01, 0x26, 0x55, 0x6e, 0x6b, + 0x6e, 0x6f, 0x77, 0x6e, 0x20, 0x64, 0x69, 0x72, + 0x65, 0x63, 0x74, 0x69, 0x76, 0x65, 0x3a, 0x20, + 0x01, 0x06, 0x73, 0x65, 0x6c, 0x01, 0x32, 0x2e, + 0x68, 0x65, 0x6c, 0x70, 0x20, 0x20, 0x20, 0x20, + 0x70, 0x72, 0x69, 0x6e, 0x74, 0x20, 0x74, 0x68, + 0x69, 0x73, 0x20, 0x68, 0x65, 0x6c, 0x70, 0x0a, + 0x01, 0x10, 0x2e, 0x78, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x01, 0x36, 0x68, 0x65, 0x78, 0x61, + 0x64, 0x65, 0x63, 0x69, 0x6d, 0x61, 0x6c, 0x20, + 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x20, 0x64, + 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x0a, 0x01, + 0x10, 0x2e, 0x64, 0x65, 0x63, 0x20, 0x20, 0x20, + 0x20, 0x01, 0x2e, 0x64, 0x65, 0x63, 0x69, 0x6d, + 0x61, 0x6c, 0x20, 0x6e, 0x75, 0x6d, 0x62, 0x65, + 0x72, 0x20, 0x64, 0x69, 0x73, 0x70, 0x6c, 0x61, + 0x79, 0x0a, 0x01, 0x10, 0x2e, 0x74, 0x69, 0x6d, + 0x65, 0x20, 0x20, 0x20, 0x01, 0x2c, 0x74, 0x6f, + 0x67, 0x67, 0x6c, 0x65, 0x20, 0x74, 0x69, 0x6d, + 0x69, 0x6e, 0x67, 0x20, 0x64, 0x69, 0x73, 0x70, + 0x6c, 0x61, 0x79, 0x0a, 0x01, 0x10, 0x2e, 0x73, + 0x74, 0x72, 0x69, 0x63, 0x74, 0x20, 0x01, 0x3c, + 0x74, 0x6f, 0x67, 0x67, 0x6c, 0x65, 0x20, 0x73, + 0x74, 0x72, 0x69, 0x63, 0x74, 0x20, 0x6d, 0x6f, + 0x64, 0x65, 0x20, 0x65, 0x76, 0x61, 0x6c, 0x75, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x0a, 0x01, 0x48, + 0x2e, 0x64, 0x65, 0x70, 0x74, 0x68, 0x20, 0x20, + 0x20, 0x73, 0x65, 0x74, 0x20, 0x6f, 0x62, 0x6a, + 0x65, 0x63, 0x74, 0x20, 0x64, 0x65, 0x70, 0x74, + 0x68, 0x20, 0x28, 0x63, 0x75, 0x72, 0x72, 0x65, + 0x6e, 0x74, 0x3a, 0x20, 0x01, 0x04, 0x29, 0x0a, + 0x01, 0x10, 0x2e, 0x68, 0x69, 0x64, 0x64, 0x65, + 0x6e, 0x20, 0x01, 0x42, 0x74, 0x6f, 0x67, 0x67, + 0x6c, 0x65, 0x20, 0x68, 0x69, 0x64, 0x64, 0x65, + 0x6e, 0x20, 0x70, 0x72, 0x6f, 0x70, 0x65, 0x72, + 0x74, 0x69, 0x65, 0x73, 0x20, 0x64, 0x69, 0x73, + 0x70, 0x6c, 0x61, 0x79, 0x0a, 0x01, 0x10, 0x2e, + 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x20, 0x20, 0x01, + 0x2c, 0x74, 0x6f, 0x67, 0x67, 0x6c, 0x65, 0x20, + 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x65, 0x64, 0x20, + 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x0a, 0x01, + 0x10, 0x2e, 0x64, 0x61, 0x72, 0x6b, 0x20, 0x20, + 0x20, 0x01, 0x08, 0x64, 0x61, 0x72, 0x6b, 0x01, + 0x30, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x20, + 0x64, 0x61, 0x72, 0x6b, 0x20, 0x63, 0x6f, 0x6c, + 0x6f, 0x72, 0x20, 0x74, 0x68, 0x65, 0x6d, 0x65, + 0x0a, 0x01, 0x10, 0x2e, 0x6c, 0x69, 0x67, 0x68, + 0x74, 0x20, 0x20, 0x01, 0x0a, 0x6c, 0x69, 0x67, + 0x68, 0x74, 0x01, 0x32, 0x73, 0x65, 0x6c, 0x65, + 0x63, 0x74, 0x20, 0x6c, 0x69, 0x67, 0x68, 0x74, + 0x20, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x20, 0x74, + 0x68, 0x65, 0x6d, 0x65, 0x0a, 0x01, 0x38, 0x2e, + 0x63, 0x6c, 0x65, 0x61, 0x72, 0x20, 0x20, 0x20, + 0x63, 0x6c, 0x65, 0x61, 0x72, 0x20, 0x74, 0x68, + 0x65, 0x20, 0x74, 0x65, 0x72, 0x6d, 0x69, 0x6e, + 0x61, 0x6c, 0x0a, 0x01, 0x4c, 0x2e, 0x6c, 0x6f, + 0x61, 0x64, 0x20, 0x20, 0x20, 0x20, 0x6c, 0x6f, + 0x61, 0x64, 0x20, 0x73, 0x6f, 0x75, 0x72, 0x63, + 0x65, 0x20, 0x63, 0x6f, 0x64, 0x65, 0x20, 0x66, + 0x72, 0x6f, 0x6d, 0x20, 0x61, 0x20, 0x66, 0x69, + 0x6c, 0x65, 0x0a, 0x01, 0x1c, 0x2e, 0x71, 0x75, + 0x69, 0x74, 0x20, 0x20, 0x20, 0x20, 0x65, 0x78, + 0x69, 0x74, 0x0a, 0x01, 0x02, 0x65, 0x01, 0x16, + 0x6c, 0x61, 0x73, 0x74, 0x49, 0x6e, 0x64, 0x65, + 0x78, 0x4f, 0x66, 0x01, 0x06, 0x2e, 0x6a, 0x73, + 0x01, 0x14, 0x6c, 0x6f, 0x61, 0x64, 0x53, 0x63, + 0x72, 0x69, 0x70, 0x74, 0x01, 0x1c, 0x31, 0x20, + 0x74, 0x72, 0x75, 0x65, 0x20, 0x79, 0x65, 0x73, + 0x20, 0x59, 0x65, 0x73, 0x01, 0x0c, 0x1b, 0x5b, + 0x48, 0x1b, 0x5b, 0x4a, 0x01, 0x46, 0x51, 0x75, + 0x69, 0x63, 0x6b, 0x4a, 0x53, 0x2d, 0x6e, 0x67, + 0x20, 0x2d, 0x20, 0x54, 0x79, 0x70, 0x65, 0x20, + 0x22, 0x2e, 0x68, 0x65, 0x6c, 0x70, 0x22, 0x20, + 0x66, 0x6f, 0x72, 0x20, 0x68, 0x65, 0x6c, 0x70, + 0x0a, 0x01, 0x08, 0x20, 0x20, 0x20, 0x20, 0x01, + 0x08, 0x65, 0x78, 0x70, 0x72, 0x01, 0x0c, 0x72, + 0x65, 0x73, 0x75, 0x6c, 0x74, 0x01, 0x2a, 0x22, + 0x75, 0x73, 0x65, 0x20, 0x73, 0x74, 0x72, 0x69, + 0x63, 0x74, 0x22, 0x3b, 0x20, 0x76, 0x6f, 0x69, + 0x64, 0x20, 0x30, 0x3b, 0x01, 0x06, 0x6e, 0x6f, + 0x77, 0x01, 0x14, 0x65, 0x76, 0x61, 0x6c, 0x53, + 0x63, 0x72, 0x69, 0x70, 0x74, 0x01, 0x22, 0x62, + 0x61, 0x63, 0x6b, 0x74, 0x72, 0x61, 0x63, 0x65, + 0x5f, 0x62, 0x61, 0x72, 0x72, 0x69, 0x65, 0x72, + 0x01, 0x0a, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x01, + 0x0e, 0x54, 0x68, 0x72, 0x6f, 0x77, 0x3a, 0x20, + 0x01, 0x04, 0x67, 0x63, 0x01, 0x0a, 0x73, 0x74, + 0x61, 0x74, 0x65, 0x01, 0x0e, 0x70, 0x72, 0x69, + 0x6d, 0x61, 0x72, 0x79, 0x01, 0x12, 0x63, 0x61, + 0x6e, 0x5f, 0x72, 0x65, 0x67, 0x65, 0x78, 0x01, + 0x14, 0x70, 0x75, 0x73, 0x68, 0x5f, 0x73, 0x74, + 0x61, 0x74, 0x65, 0x01, 0x14, 0x6c, 0x61, 0x73, + 0x74, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x65, 0x01, + 0x12, 0x70, 0x6f, 0x70, 0x5f, 0x73, 0x74, 0x61, + 0x74, 0x65, 0x01, 0x26, 0x70, 0x61, 0x72, 0x73, + 0x65, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, + 0x63, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x01, + 0x24, 0x70, 0x61, 0x72, 0x73, 0x65, 0x5f, 0x6c, + 0x69, 0x6e, 0x65, 0x5f, 0x63, 0x6f, 0x6d, 0x6d, + 0x65, 0x6e, 0x74, 0x01, 0x18, 0x70, 0x61, 0x72, + 0x73, 0x65, 0x5f, 0x73, 0x74, 0x72, 0x69, 0x6e, + 0x67, 0x01, 0x16, 0x70, 0x61, 0x72, 0x73, 0x65, + 0x5f, 0x72, 0x65, 0x67, 0x65, 0x78, 0x01, 0x18, + 0x70, 0x61, 0x72, 0x73, 0x65, 0x5f, 0x6e, 0x75, + 0x6d, 0x62, 0x65, 0x72, 0x01, 0x16, 0x6a, 0x73, + 0x5f, 0x6b, 0x65, 0x79, 0x77, 0x6f, 0x72, 0x64, + 0x73, 0x01, 0x16, 0x6a, 0x73, 0x5f, 0x6e, 0x6f, + 0x5f, 0x72, 0x65, 0x67, 0x65, 0x78, 0x01, 0x10, + 0x6a, 0x73, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x73, + 0x01, 0x20, 0x70, 0x61, 0x72, 0x73, 0x65, 0x5f, + 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, + 0x65, 0x72, 0x01, 0x12, 0x73, 0x65, 0x74, 0x5f, + 0x73, 0x74, 0x79, 0x6c, 0x65, 0x01, 0x0e, 0x63, + 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x01, 0x0a, + 0x64, 0x65, 0x6c, 0x69, 0x6d, 0x01, 0x04, 0x69, + 0x31, 0x01, 0x02, 0x7c, 0x01, 0x14, 0x69, 0x64, + 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, + 0x01, 0x04, 0x74, 0x6f, 0x01, 0x6a, 0x62, 0x72, + 0x65, 0x61, 0x6b, 0x7c, 0x63, 0x61, 0x73, 0x65, + 0x7c, 0x63, 0x61, 0x74, 0x63, 0x68, 0x7c, 0x63, + 0x6f, 0x6e, 0x74, 0x69, 0x6e, 0x75, 0x65, 0x7c, + 0x64, 0x65, 0x62, 0x75, 0x67, 0x67, 0x65, 0x72, + 0x7c, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, + 0x7c, 0x64, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x7c, + 0x64, 0x6f, 0x7c, 0x01, 0x5e, 0x65, 0x6c, 0x73, + 0x65, 0x7c, 0x66, 0x69, 0x6e, 0x61, 0x6c, 0x6c, + 0x79, 0x7c, 0x66, 0x6f, 0x72, 0x7c, 0x66, 0x75, + 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x7c, 0x69, + 0x66, 0x7c, 0x69, 0x6e, 0x7c, 0x69, 0x6e, 0x73, + 0x74, 0x61, 0x6e, 0x63, 0x65, 0x6f, 0x66, 0x7c, + 0x6e, 0x65, 0x77, 0x7c, 0x01, 0x5e, 0x72, 0x65, + 0x74, 0x75, 0x72, 0x6e, 0x7c, 0x73, 0x77, 0x69, + 0x74, 0x63, 0x68, 0x7c, 0x74, 0x68, 0x69, 0x73, + 0x7c, 0x74, 0x68, 0x72, 0x6f, 0x77, 0x7c, 0x74, + 0x72, 0x79, 0x7c, 0x74, 0x79, 0x70, 0x65, 0x6f, + 0x66, 0x7c, 0x77, 0x68, 0x69, 0x6c, 0x65, 0x7c, + 0x77, 0x69, 0x74, 0x68, 0x7c, 0x01, 0x5a, 0x63, + 0x6c, 0x61, 0x73, 0x73, 0x7c, 0x63, 0x6f, 0x6e, + 0x73, 0x74, 0x7c, 0x65, 0x6e, 0x75, 0x6d, 0x7c, + 0x69, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x7c, 0x65, + 0x78, 0x70, 0x6f, 0x72, 0x74, 0x7c, 0x65, 0x78, + 0x74, 0x65, 0x6e, 0x64, 0x73, 0x7c, 0x73, 0x75, + 0x70, 0x65, 0x72, 0x7c, 0x01, 0x66, 0x69, 0x6d, + 0x70, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x73, + 0x7c, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x66, 0x61, + 0x63, 0x65, 0x7c, 0x6c, 0x65, 0x74, 0x7c, 0x70, + 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x7c, 0x70, + 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x7c, 0x70, + 0x72, 0x6f, 0x74, 0x65, 0x63, 0x74, 0x65, 0x64, + 0x7c, 0x01, 0x28, 0x70, 0x75, 0x62, 0x6c, 0x69, + 0x63, 0x7c, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, + 0x7c, 0x79, 0x69, 0x65, 0x6c, 0x64, 0x7c, 0x01, + 0x4e, 0x75, 0x6e, 0x64, 0x65, 0x66, 0x69, 0x6e, + 0x65, 0x64, 0x7c, 0x6e, 0x75, 0x6c, 0x6c, 0x7c, + 0x74, 0x72, 0x75, 0x65, 0x7c, 0x66, 0x61, 0x6c, + 0x73, 0x65, 0x7c, 0x49, 0x6e, 0x66, 0x69, 0x6e, + 0x69, 0x74, 0x79, 0x7c, 0x4e, 0x61, 0x4e, 0x7c, + 0x01, 0x1e, 0x65, 0x76, 0x61, 0x6c, 0x7c, 0x61, + 0x72, 0x67, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x73, + 0x7c, 0x01, 0x0c, 0x61, 0x77, 0x61, 0x69, 0x74, + 0x7c, 0x01, 0x7a, 0x7c, 0x74, 0x68, 0x69, 0x73, + 0x7c, 0x73, 0x75, 0x70, 0x65, 0x72, 0x7c, 0x75, + 0x6e, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x64, + 0x7c, 0x6e, 0x75, 0x6c, 0x6c, 0x7c, 0x74, 0x72, + 0x75, 0x65, 0x7c, 0x66, 0x61, 0x6c, 0x73, 0x65, + 0x7c, 0x49, 0x6e, 0x66, 0x69, 0x6e, 0x69, 0x74, + 0x79, 0x7c, 0x4e, 0x61, 0x4e, 0x7c, 0x61, 0x72, + 0x67, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x7c, + 0x01, 0x14, 0x7c, 0x76, 0x6f, 0x69, 0x64, 0x7c, + 0x76, 0x61, 0x72, 0x7c, 0x01, 0x02, 0x09, 0x01, + 0x02, 0x0d, 0x01, 0x02, 0x2b, 0x01, 0x0c, 0x67, + 0x65, 0x74, 0x65, 0x6e, 0x76, 0x01, 0x08, 0x48, + 0x4f, 0x4d, 0x45, 0x01, 0x16, 0x55, 0x53, 0x45, + 0x52, 0x50, 0x52, 0x4f, 0x46, 0x49, 0x4c, 0x45, + 0x01, 0x02, 0x66, 0x01, 0x08, 0x6f, 0x70, 0x65, + 0x6e, 0x01, 0x18, 0x2e, 0x71, 0x6a, 0x73, 0x5f, + 0x68, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x01, + 0x0a, 0x63, 0x6c, 0x6f, 0x73, 0x65, 0x01, 0x10, + 0x6c, 0x6f, 0x61, 0x64, 0x46, 0x69, 0x6c, 0x65, + 0x01, 0x12, 0x43, 0x4f, 0x4c, 0x4f, 0x52, 0x46, + 0x47, 0x42, 0x47, 0x01, 0x10, 0x4e, 0x4f, 0x5f, + 0x43, 0x4f, 0x4c, 0x4f, 0x52, 0x01, 0x08, 0x1b, + 0x5b, 0x30, 0x6d, 0x01, 0x0a, 0x1b, 0x5b, 0x33, + 0x30, 0x6d, 0x01, 0x0a, 0x62, 0x6c, 0x61, 0x63, + 0x6b, 0x01, 0x0a, 0x1b, 0x5b, 0x33, 0x31, 0x6d, + 0x01, 0x06, 0x72, 0x65, 0x64, 0x01, 0x0a, 0x1b, + 0x5b, 0x33, 0x32, 0x6d, 0x01, 0x0a, 0x67, 0x72, + 0x65, 0x65, 0x6e, 0x01, 0x0a, 0x1b, 0x5b, 0x33, + 0x33, 0x6d, 0x01, 0x0c, 0x79, 0x65, 0x6c, 0x6c, + 0x6f, 0x77, 0x01, 0x0a, 0x1b, 0x5b, 0x33, 0x34, + 0x6d, 0x01, 0x08, 0x62, 0x6c, 0x75, 0x65, 0x01, + 0x0a, 0x1b, 0x5b, 0x33, 0x35, 0x6d, 0x01, 0x0e, + 0x6d, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x61, 0x01, + 0x0a, 0x1b, 0x5b, 0x33, 0x36, 0x6d, 0x01, 0x08, + 0x63, 0x79, 0x61, 0x6e, 0x01, 0x0a, 0x1b, 0x5b, + 0x33, 0x37, 0x6d, 0x01, 0x0a, 0x77, 0x68, 0x69, + 0x74, 0x65, 0x01, 0x0e, 0x1b, 0x5b, 0x33, 0x30, + 0x3b, 0x31, 0x6d, 0x01, 0x08, 0x67, 0x72, 0x61, + 0x79, 0x01, 0x08, 0x67, 0x72, 0x65, 0x79, 0x01, + 0x0e, 0x1b, 0x5b, 0x33, 0x31, 0x3b, 0x31, 0x6d, + 0x01, 0x14, 0x62, 0x72, 0x69, 0x67, 0x68, 0x74, + 0x5f, 0x72, 0x65, 0x64, 0x01, 0x0e, 0x1b, 0x5b, + 0x33, 0x32, 0x3b, 0x31, 0x6d, 0x01, 0x18, 0x62, + 0x72, 0x69, 0x67, 0x68, 0x74, 0x5f, 0x67, 0x72, + 0x65, 0x65, 0x6e, 0x01, 0x0e, 0x1b, 0x5b, 0x33, + 0x33, 0x3b, 0x31, 0x6d, 0x01, 0x1a, 0x62, 0x72, + 0x69, 0x67, 0x68, 0x74, 0x5f, 0x79, 0x65, 0x6c, + 0x6c, 0x6f, 0x77, 0x01, 0x0e, 0x1b, 0x5b, 0x33, + 0x34, 0x3b, 0x31, 0x6d, 0x01, 0x16, 0x62, 0x72, + 0x69, 0x67, 0x68, 0x74, 0x5f, 0x62, 0x6c, 0x75, + 0x65, 0x01, 0x0e, 0x1b, 0x5b, 0x33, 0x35, 0x3b, + 0x31, 0x6d, 0x01, 0x1c, 0x62, 0x72, 0x69, 0x67, + 0x68, 0x74, 0x5f, 0x6d, 0x61, 0x67, 0x65, 0x6e, + 0x74, 0x61, 0x01, 0x0e, 0x1b, 0x5b, 0x33, 0x36, + 0x3b, 0x31, 0x6d, 0x01, 0x16, 0x62, 0x72, 0x69, + 0x67, 0x68, 0x74, 0x5f, 0x63, 0x79, 0x61, 0x6e, + 0x01, 0x0e, 0x1b, 0x5b, 0x33, 0x37, 0x3b, 0x31, + 0x6d, 0x01, 0x18, 0x62, 0x72, 0x69, 0x67, 0x68, + 0x74, 0x5f, 0x77, 0x68, 0x69, 0x74, 0x65, 0x01, + 0x0c, 0x71, 0x6a, 0x73, 0x20, 0x3e, 0x20, 0x01, + 0x0c, 0x20, 0x20, 0x2e, 0x2e, 0x2e, 0x20, 0x01, + 0x02, 0x01, 0x01, 0x02, 0x02, 0x01, 0x02, 0x03, + 0x01, 0x02, 0x04, 0x01, 0x02, 0x05, 0x01, 0x02, + 0x06, 0x01, 0x02, 0x07, 0x01, 0x02, 0x08, 0x01, + 0x02, 0x0b, 0x01, 0x02, 0x0c, 0x01, 0x02, 0x0e, + 0x01, 0x02, 0x10, 0x01, 0x02, 0x11, 0x01, 0x02, + 0x12, 0x01, 0x02, 0x13, 0x01, 0x02, 0x14, 0x01, + 0x02, 0x17, 0x01, 0x02, 0x18, 0x01, 0x02, 0x19, + 0x01, 0x06, 0x1b, 0x4f, 0x41, 0x01, 0x06, 0x1b, + 0x4f, 0x42, 0x01, 0x06, 0x1b, 0x4f, 0x43, 0x01, + 0x06, 0x1b, 0x4f, 0x44, 0x01, 0x06, 0x1b, 0x4f, + 0x46, 0x01, 0x06, 0x1b, 0x4f, 0x48, 0x01, 0x0c, + 0x1b, 0x5b, 0x31, 0x3b, 0x35, 0x43, 0x01, 0x0c, + 0x1b, 0x5b, 0x31, 0x3b, 0x35, 0x44, 0x01, 0x08, + 0x1b, 0x5b, 0x31, 0x7e, 0x01, 0x08, 0x1b, 0x5b, + 0x33, 0x7e, 0x01, 0x08, 0x1b, 0x5b, 0x34, 0x7e, + 0x01, 0x08, 0x1b, 0x5b, 0x35, 0x7e, 0x01, 0x08, + 0x1b, 0x5b, 0x36, 0x7e, 0x01, 0x06, 0x1b, 0x5b, + 0x41, 0x01, 0x06, 0x1b, 0x5b, 0x42, 0x01, 0x06, + 0x1b, 0x5b, 0x43, 0x01, 0x06, 0x1b, 0x5b, 0x44, + 0x01, 0x06, 0x1b, 0x5b, 0x46, 0x01, 0x06, 0x1b, + 0x5b, 0x48, 0x01, 0x04, 0x1b, 0x7f, 0x01, 0x04, + 0x1b, 0x62, 0x01, 0x04, 0x1b, 0x64, 0x01, 0x04, + 0x1b, 0x66, 0x01, 0x04, 0x1b, 0x6b, 0x01, 0x04, + 0x1b, 0x6c, 0x01, 0x04, 0x1b, 0x74, 0x01, 0x04, + 0x1b, 0x75, 0x01, 0x02, 0x7f, 0x01, 0x02, 0x78, + 0x01, 0x06, 0x64, 0x65, 0x63, 0x01, 0x08, 0x74, + 0x69, 0x6d, 0x65, 0x01, 0x0c, 0x73, 0x74, 0x72, + 0x69, 0x63, 0x74, 0x01, 0x0c, 0x68, 0x69, 0x64, + 0x64, 0x65, 0x6e, 0x01, 0x08, 0x71, 0x75, 0x69, + 0x74, 0x0d, 0xc0, 0x03, 0x03, 0xc2, 0x03, 0xc4, + 0x03, 0xc6, 0x03, 0x00, 0x00, 0x03, 0x00, 0xfc, + 0x01, 0x00, 0x01, 0xfc, 0x01, 0x01, 0x02, 0xfc, + 0x01, 0x02, 0x00, 0x0c, 0x20, 0x02, 0x01, 0xa2, + 0x01, 0x00, 0x00, 0x00, 0x02, 0x03, 0x01, 0x0f, + 0x00, 0xc8, 0x03, 0x00, 0x0d, 0xca, 0x03, 0x01, + 0x0d, 0xcc, 0x03, 0x02, 0x0d, 0x0c, 0x43, 0x02, + 0x01, 0x00, 0x01, 0x8a, 0x01, 0x01, 0x04, 0x03, + 0x5c, 0x83, 0x0d, 0x8b, 0x01, 0xce, 0x03, 0x00, + 0x01, 0x40, 0xa6, 0x02, 0x00, 0x00, 0x40, 0xae, + 0x02, 0x00, 0x01, 0x40, 0xac, 0x02, 0x00, 0x02, + 0x40, 0xb0, 0x02, 0x00, 0x03, 0x40, 0xe2, 0x02, + 0x00, 0x04, 0x40, 0xcc, 0x02, 0x00, 0x05, 0x40, + 0xa8, 0x02, 0x00, 0x06, 0x40, 0xba, 0x02, 0x00, + 0x07, 0x40, 0xc2, 0x02, 0x00, 0x08, 0x40, 0xaa, + 0x02, 0x00, 0x09, 0x40, 0xb2, 0x02, 0x00, 0x0a, + 0x00, 0xb6, 0x02, 0x00, 0x0b, 0x40, 0xb8, 0x02, + 0x00, 0x0c, 0x40, 0xd0, 0x03, 0x00, 0x0d, 0x40, + 0xd2, 0x03, 0x00, 0x0e, 0x40, 0xd4, 0x03, 0x00, + 0x0f, 0x40, 0xd6, 0x03, 0x00, 0x10, 0x00, 0xd8, + 0x03, 0x00, 0x11, 0x40, 0xda, 0x03, 0x00, 0x12, + 0x40, 0xdc, 0x03, 0x00, 0x13, 0x40, 0xde, 0x03, + 0x00, 0x14, 0x40, 0xe0, 0x03, 0x00, 0x15, 0x40, + 0xe2, 0x03, 0x00, 0x16, 0x40, 0xe4, 0x03, 0x00, + 0x17, 0x40, 0xe6, 0x03, 0x00, 0x18, 0x40, 0xe8, + 0x03, 0x00, 0x19, 0x40, 0xea, 0x03, 0x00, 0x1a, + 0x40, 0xec, 0x03, 0x00, 0x1b, 0x40, 0xee, 0x03, + 0x00, 0x1c, 0x40, 0xf0, 0x03, 0x00, 0x1d, 0x40, + 0xf2, 0x03, 0x00, 0x1e, 0x40, 0xf4, 0x03, 0x00, + 0x1f, 0x40, 0xf6, 0x03, 0x00, 0x20, 0x40, 0xf8, + 0x03, 0x00, 0x21, 0x40, 0xfa, 0x03, 0x00, 0x22, + 0x40, 0xfc, 0x03, 0x00, 0x23, 0x40, 0xfe, 0x03, + 0x00, 0x24, 0x40, 0x80, 0x04, 0x00, 0x25, 0x40, + 0x82, 0x04, 0x00, 0x26, 0x40, 0x84, 0x04, 0x00, + 0x27, 0x40, 0x86, 0x04, 0x00, 0x28, 0x40, 0x88, + 0x04, 0x00, 0x29, 0x40, 0x8a, 0x04, 0x00, 0x2a, + 0x40, 0x8c, 0x04, 0x00, 0x2b, 0x40, 0x8e, 0x04, + 0x00, 0x2c, 0x40, 0x90, 0x04, 0x00, 0x2d, 0x40, + 0x92, 0x04, 0x00, 0x2e, 0x40, 0x94, 0x04, 0x00, + 0x2f, 0x40, 0x96, 0x04, 0x00, 0x30, 0x40, 0x98, + 0x04, 0x00, 0x31, 0x40, 0x9a, 0x04, 0x00, 0x32, + 0x40, 0x9c, 0x04, 0x00, 0x33, 0x40, 0x9e, 0x04, + 0x00, 0x34, 0x00, 0xa0, 0x04, 0x00, 0x35, 0x40, + 0xa2, 0x04, 0x00, 0x36, 0x40, 0xa4, 0x04, 0x00, + 0x37, 0x40, 0xa6, 0x04, 0x00, 0x38, 0x40, 0xa8, + 0x04, 0x00, 0x39, 0x40, 0xaa, 0x04, 0x00, 0x3a, + 0x40, 0xac, 0x04, 0x00, 0x3b, 0x40, 0xae, 0x04, + 0x00, 0x3c, 0x40, 0xb0, 0x04, 0x00, 0x3d, 0x40, + 0xb2, 0x04, 0x00, 0x3e, 0x40, 0xb4, 0x04, 0x00, + 0x3f, 0x40, 0xb6, 0x04, 0x00, 0x40, 0x40, 0xb8, + 0x04, 0x00, 0x41, 0x40, 0xba, 0x04, 0x00, 0x42, + 0x40, 0xbc, 0x04, 0x00, 0x43, 0x00, 0xbe, 0x04, + 0x00, 0x44, 0x00, 0xc0, 0x04, 0x00, 0x45, 0x40, + 0xc2, 0x04, 0x00, 0x46, 0x00, 0xc4, 0x04, 0x00, + 0x47, 0x00, 0xc6, 0x04, 0x00, 0x48, 0x00, 0xc8, + 0x04, 0x00, 0x49, 0x00, 0xca, 0x04, 0x00, 0x4a, + 0x40, 0xcc, 0x04, 0x00, 0x4b, 0x40, 0xce, 0x04, + 0x00, 0x4c, 0x00, 0xd0, 0x04, 0x00, 0x4d, 0x00, + 0xd2, 0x04, 0x00, 0x4e, 0x00, 0xd4, 0x04, 0x00, + 0x4f, 0x00, 0xd6, 0x04, 0x00, 0x50, 0x40, 0xd8, + 0x04, 0x00, 0x51, 0x00, 0xda, 0x04, 0x00, 0x52, + 0x00, 0xdc, 0x04, 0x00, 0x53, 0x40, 0xde, 0x04, + 0x00, 0x54, 0x00, 0xe0, 0x04, 0x00, 0x55, 0x00, + 0xe2, 0x04, 0x00, 0x56, 0x40, 0xe4, 0x04, 0x00, + 0x57, 0x00, 0xe6, 0x04, 0x00, 0x58, 0x00, 0xe8, + 0x04, 0x00, 0x59, 0x00, 0xea, 0x04, 0x00, 0x5a, + 0x00, 0xec, 0x04, 0x00, 0x5b, 0x00, 0xee, 0x04, + 0x00, 0x5c, 0x00, 0xf0, 0x04, 0x00, 0x5d, 0x00, + 0xf2, 0x04, 0x00, 0x5e, 0x40, 0xf4, 0x04, 0x00, + 0x5f, 0x00, 0xf6, 0x04, 0x00, 0x60, 0x00, 0xf8, + 0x04, 0x00, 0x61, 0x00, 0xfa, 0x04, 0x00, 0x62, + 0x00, 0xfc, 0x04, 0x00, 0x63, 0x00, 0xfe, 0x04, + 0x00, 0x64, 0x40, 0x80, 0x05, 0x00, 0x65, 0x00, + 0x82, 0x05, 0x00, 0x66, 0x40, 0x84, 0x05, 0x00, + 0x67, 0x40, 0x86, 0x05, 0x00, 0x68, 0x40, 0x88, + 0x05, 0x00, 0x69, 0x40, 0x8a, 0x05, 0x00, 0x6a, + 0x40, 0x8c, 0x05, 0x00, 0x6b, 0x40, 0x8e, 0x05, + 0x00, 0x6c, 0x40, 0x90, 0x05, 0x00, 0x6d, 0x40, + 0x92, 0x05, 0x00, 0x6e, 0x40, 0x94, 0x05, 0x00, + 0x6f, 0x40, 0x96, 0x05, 0x00, 0x70, 0x40, 0x98, + 0x05, 0x00, 0x71, 0x40, 0x9a, 0x05, 0x00, 0x72, + 0x40, 0x9c, 0x05, 0x00, 0x73, 0x40, 0x9e, 0x05, + 0x00, 0x74, 0x40, 0xa0, 0x05, 0x00, 0x75, 0x40, + 0xa2, 0x05, 0x00, 0x76, 0x40, 0xa4, 0x05, 0x00, + 0x77, 0x40, 0xa6, 0x05, 0x00, 0x78, 0x40, 0xa8, + 0x05, 0x00, 0x79, 0x00, 0xaa, 0x05, 0x00, 0x7a, + 0x40, 0xac, 0x05, 0x00, 0x7b, 0x40, 0xae, 0x05, + 0x00, 0x7c, 0x40, 0xb0, 0x05, 0x00, 0x7d, 0x00, + 0xb2, 0x05, 0x00, 0x7e, 0x40, 0xb4, 0x05, 0x00, + 0x7f, 0x40, 0xb6, 0x05, 0x00, 0x80, 0x01, 0x40, + 0xb8, 0x05, 0x00, 0x81, 0x01, 0x40, 0xba, 0x05, + 0x00, 0x82, 0x01, 0x40, 0xbc, 0x05, 0x00, 0x83, + 0x01, 0x40, 0xbe, 0x05, 0x00, 0x84, 0x01, 0x40, + 0xc0, 0x05, 0x00, 0x85, 0x01, 0x40, 0xc2, 0x05, + 0x00, 0x86, 0x01, 0x40, 0xc4, 0x05, 0x00, 0x87, + 0x01, 0x40, 0xc6, 0x05, 0x00, 0x88, 0x01, 0x00, + 0xc8, 0x05, 0x00, 0x89, 0x01, 0x00, 0xc8, 0x03, + 0x00, 0x0c, 0xca, 0x03, 0x01, 0x0c, 0xcc, 0x03, + 0x02, 0x0c, 0x0c, 0x43, 0x02, 0x01, 0x9e, 0x04, + 0x00, 0x01, 0x00, 0x04, 0x08, 0x00, 0x89, 0x01, + 0x01, 0xca, 0x05, 0x00, 0x00, 0x00, 0x96, 0x04, + 0x30, 0x01, 0xc8, 0x03, 0x00, 0x0c, 0x9a, 0x04, + 0x32, 0x01, 0xca, 0x03, 0x01, 0x0c, 0xa0, 0x04, + 0x35, 0x01, 0x98, 0x04, 0x31, 0x01, 0xcc, 0x02, + 0x05, 0x01, 0xa2, 0x04, 0x36, 0x01, 0x65, 0x01, + 0x00, 0x41, 0x0d, 0x00, 0x00, 0x00, 0x42, 0x66, + 0x01, 0x00, 0x00, 0x24, 0x00, 0x00, 0xe1, 0xbc, + 0x50, 0xe3, 0x65, 0x03, 0x00, 0x42, 0x67, 0x01, + 0x00, 0x00, 0xdd, 0x24, 0x01, 0x00, 0xea, 0x35, + 0x65, 0x03, 0x00, 0x41, 0x68, 0x01, 0x00, 0x00, + 0xea, 0x14, 0x65, 0x03, 0x00, 0x42, 0x68, 0x01, + 0x00, 0x00, 0xdd, 0x24, 0x01, 0x00, 0xcd, 0xea, + 0x05, 0xc5, 0xb4, 0x47, 0xe3, 0x65, 0x03, 0x00, + 0x41, 0x69, 0x01, 0x00, 0x00, 0xea, 0x0e, 0x65, + 0x03, 0x00, 0x42, 0x69, 0x01, 0x00, 0x00, 0xdd, + 0x24, 0x01, 0x00, 0x0e, 0x65, 0x03, 0x00, 0x42, + 0x6a, 0x01, 0x00, 0x00, 0x65, 0x03, 0x00, 0x41, + 0x6b, 0x01, 0x00, 0x00, 0x5e, 0x04, 0x00, 0x24, + 0x02, 0x00, 0x0e, 0x5e, 0x06, 0x00, 0x11, 0xbc, + 0x40, 0x21, 0x01, 0x00, 0x5f, 0x05, 0x00, 0x65, + 0x03, 0x00, 0x42, 0x6c, 0x01, 0x00, 0x00, 0xdd, + 0x5e, 0x07, 0x00, 0x24, 0x02, 0x00, 0x29, 0x0c, + 0x43, 0x02, 0x01, 0xa0, 0x04, 0x00, 0x00, 0x00, + 0x02, 0x01, 0x00, 0x04, 0x00, 0xa4, 0x04, 0x37, + 0x01, 0xdd, 0xb7, 0xef, 0x29, 0x0c, 0x43, 0x02, + 0x01, 0xa2, 0x04, 0x00, 0x02, 0x00, 0x06, 0x04, + 0x00, 0x28, 0x02, 0xda, 0x05, 0x00, 0x00, 0x00, + 0xdc, 0x05, 0x00, 0x01, 0x00, 0xca, 0x03, 0x01, + 0x0c, 0x96, 0x04, 0x30, 0x01, 0x98, 0x04, 0x31, + 0x01, 0xa4, 0x04, 0x37, 0x01, 0x65, 0x00, 0x00, + 0x42, 0x6f, 0x01, 0x00, 0x00, 0xde, 0xdf, 0x41, + 0x70, 0x01, 0x00, 0x00, 0xb4, 0xdf, 0xe9, 0x24, + 0x04, 0x00, 0xc9, 0xb4, 0xca, 0xc6, 0xc5, 0xa6, + 0xea, 0x0b, 0xe0, 0xdf, 0xc6, 0x47, 0xef, 0x0e, + 0x93, 0x01, 0xec, 0xf2, 0x29, 0x0c, 0x43, 0x02, + 0x01, 0xa4, 0x04, 0x01, 0x00, 0x01, 0x04, 0x04, + 0x00, 0x5f, 0x01, 0xe2, 0x05, 0x00, 0x01, 0x00, + 0xde, 0x03, 0x14, 0x01, 0x98, 0x05, 0x71, 0x01, + 0x92, 0x04, 0x2e, 0x01, 0x94, 0x04, 0x2f, 0x01, + 0xdd, 0x96, 0xea, 0x06, 0xde, 0xd1, 0xef, 0x0e, + 0x29, 0xdf, 0xb4, 0xaf, 0xea, 0x24, 0xd1, 0xbd, + 0x80, 0x00, 0xa9, 0xea, 0x1d, 0xd1, 0xbd, 0xc0, + 0x00, 0xa6, 0xea, 0x16, 0xe0, 0xba, 0x9f, 0xd1, + 0xbc, 0x3f, 0xa2, 0xa4, 0xe4, 0xdf, 0x8e, 0xe7, + 0xb4, 0xae, 0xea, 0x33, 0xde, 0xe0, 0xef, 0x0e, + 0x29, 0xd1, 0xbd, 0xc0, 0x00, 0xa9, 0xea, 0x21, + 0xd1, 0xbd, 0xf8, 0x00, 0xa6, 0xea, 0x1a, 0xb5, + 0xd1, 0xbd, 0xe0, 0x00, 0xa9, 0x9d, 0xd1, 0xbd, + 0xf0, 0x00, 0xa9, 0x9d, 0xe3, 0xd1, 0xb5, 0xba, + 0xdf, 0x9e, 0x9f, 0xb5, 0x9e, 0xa2, 0xe4, 0x29, + 0xb4, 0xe3, 0xde, 0xd1, 0xef, 0x0e, 0x29, 0x0c, + 0x43, 0x02, 0x01, 0xa6, 0x04, 0x01, 0x00, 0x01, + 0x02, 0x00, 0x00, 0x35, 0x01, 0xe2, 0x05, 0x00, + 0x01, 0x00, 0xd1, 0x97, 0x04, 0x49, 0x00, 0x00, + 0x00, 0xae, 0x11, 0xea, 0x2a, 0x0e, 0xd1, 0x04, + 0x72, 0x01, 0x00, 0x00, 0xa9, 0x11, 0xea, 0x09, + 0x0e, 0xd1, 0x04, 0x73, 0x01, 0x00, 0x00, 0xa7, + 0x11, 0xeb, 0x14, 0x0e, 0xd1, 0x04, 0x74, 0x01, + 0x00, 0x00, 0xa9, 0x11, 0xea, 0x09, 0x0e, 0xd1, + 0x04, 0x75, 0x01, 0x00, 0x00, 0xa7, 0x28, 0x0c, + 0x43, 0x02, 0x01, 0xa8, 0x04, 0x01, 0x00, 0x01, + 0x02, 0x00, 0x02, 0x19, 0x01, 0xe2, 0x05, 0x00, + 0x01, 0x00, 0x07, 0x02, 0x30, 0x07, 0x02, 0x39, + 0xd1, 0x97, 0x04, 0x49, 0x00, 0x00, 0x00, 0xae, + 0x11, 0xea, 0x0e, 0x0e, 0xd1, 0xbe, 0x00, 0xa9, + 0x11, 0xea, 0x06, 0x0e, 0xd1, 0xbe, 0x01, 0xa7, + 0x28, 0x0c, 0x43, 0x02, 0x01, 0xaa, 0x04, 0x01, + 0x00, 0x01, 0x02, 0x02, 0x00, 0x2d, 0x01, 0xe2, + 0x05, 0x00, 0x01, 0x00, 0xa6, 0x04, 0x38, 0x01, + 0xa8, 0x04, 0x39, 0x01, 0xd1, 0x97, 0x04, 0x49, + 0x00, 0x00, 0x00, 0xae, 0x11, 0xea, 0x22, 0x0e, + 0xdd, 0xd1, 0xef, 0x11, 0xeb, 0x1b, 0x0e, 0xde, + 0xd1, 0xef, 0x11, 0xeb, 0x14, 0x0e, 0xd1, 0x04, + 0x76, 0x01, 0x00, 0x00, 0xac, 0x11, 0xeb, 0x09, + 0x0e, 0xd1, 0x04, 0x77, 0x01, 0x00, 0x00, 0xac, + 0x28, 0x0c, 0x43, 0x02, 0x01, 0xac, 0x04, 0x01, + 0x04, 0x01, 0x03, 0x00, 0x00, 0x32, 0x05, 0xf0, + 0x05, 0x00, 0x01, 0x00, 0xf2, 0x05, 0x00, 0x00, + 0x00, 0xe2, 0x05, 0x00, 0x01, 0x00, 0xdc, 0x05, + 0x00, 0x02, 0x00, 0xf4, 0x05, 0x00, 0x03, 0x00, + 0xd1, 0xe9, 0xcc, 0xb4, 0xc9, 0xb4, 0xcb, 0xc7, + 0xc8, 0xa6, 0xea, 0x25, 0xd1, 0x42, 0x7b, 0x01, + 0x00, 0x00, 0xc7, 0x24, 0x01, 0x00, 0xce, 0x01, + 0x00, 0xdc, 0x00, 0x00, 0xa6, 0x11, 0xeb, 0x09, + 0x0e, 0xc6, 0x01, 0x00, 0xe0, 0x00, 0x00, 0xa9, + 0xea, 0x03, 0x93, 0x00, 0x93, 0x02, 0xec, 0xd8, + 0xc5, 0x28, 0x0c, 0x43, 0x02, 0x01, 0xae, 0x04, + 0x01, 0x01, 0x01, 0x03, 0x00, 0x00, 0x29, 0x02, + 0xe2, 0x05, 0x00, 0x01, 0x00, 0xf8, 0x05, 0x00, + 0x00, 0x00, 0xd1, 0x97, 0x04, 0x49, 0x00, 0x00, + 0x00, 0xaf, 0xea, 0x03, 0x09, 0x28, 0xd1, 0x42, + 0x7d, 0x01, 0x00, 0x00, 0xb4, 0x24, 0x01, 0x00, + 0xcd, 0x01, 0x00, 0xdc, 0x00, 0x00, 0xa9, 0x11, + 0xea, 0x09, 0x0e, 0xc5, 0x01, 0x00, 0xe0, 0x00, + 0x00, 0xa6, 0x28, 0x0c, 0x43, 0x02, 0x01, 0xb0, + 0x04, 0x02, 0x00, 0x02, 0x03, 0x00, 0x00, 0x23, + 0x02, 0xe8, 0x05, 0x00, 0x01, 0x00, 0xfc, 0x05, + 0x00, 0x01, 0x00, 0xd1, 0xd2, 0x9d, 0x11, 0x04, + 0x7f, 0x01, 0x00, 0x00, 0xae, 0xeb, 0x13, 0x11, + 0x04, 0x80, 0x01, 0x00, 0x00, 0xae, 0xeb, 0x0a, + 0x11, 0x04, 0x81, 0x01, 0x00, 0x00, 0xae, 0xea, + 0x03, 0x0a, 0x28, 0x0e, 0x09, 0x28, 0x0c, 0x43, + 0x02, 0x01, 0xb2, 0x04, 0x03, 0x03, 0x03, 0x06, + 0x03, 0x00, 0x62, 0x06, 0xf0, 0x05, 0x00, 0x01, + 0x00, 0x84, 0x06, 0x00, 0x01, 0x00, 0x86, 0x06, + 0x00, 0x01, 0x00, 0xdc, 0x05, 0x00, 0x00, 0x00, + 0x88, 0x06, 0x00, 0x01, 0x00, 0x8a, 0x06, 0x00, + 0x02, 0x00, 0xc8, 0x03, 0x00, 0x0c, 0xd8, 0x03, + 0x11, 0x01, 0xdc, 0x03, 0x13, 0x01, 0xd2, 0xca, + 0xc6, 0xd1, 0xe9, 0xa6, 0xea, 0x5a, 0xd3, 0xc6, + 0xcd, 0x47, 0xcb, 0xc6, 0x8f, 0xce, 0xd1, 0xe9, + 0xa6, 0xea, 0x08, 0xd3, 0xc6, 0x47, 0xc7, 0xac, + 0xeb, 0xf2, 0x65, 0x00, 0x00, 0x42, 0x86, 0x01, + 0x00, 0x00, 0xde, 0xdf, 0xc7, 0x47, 0x11, 0xeb, + 0x07, 0x0e, 0x04, 0x87, 0x01, 0x00, 0x00, 0x47, + 0x24, 0x01, 0x00, 0x0e, 0x65, 0x00, 0x00, 0x42, + 0x86, 0x01, 0x00, 0x00, 0xd1, 0x42, 0x88, 0x01, + 0x00, 0x00, 0xc4, 0x24, 0x02, 0x00, 0x24, 0x01, + 0x00, 0x0e, 0x65, 0x00, 0x00, 0x42, 0x86, 0x01, + 0x00, 0x00, 0xde, 0x04, 0x87, 0x01, 0x00, 0x00, + 0x47, 0x24, 0x01, 0x00, 0x0e, 0xec, 0xa2, 0x29, + 0x0c, 0x43, 0x02, 0x01, 0xb4, 0x04, 0x02, 0x00, + 0x02, 0x05, 0x01, 0x00, 0x1d, 0x02, 0x92, 0x06, + 0x00, 0x01, 0x00, 0x94, 0x06, 0x00, 0x01, 0x00, + 0xc8, 0x03, 0x00, 0x0c, 0x65, 0x00, 0x00, 0x42, + 0x86, 0x01, 0x00, 0x00, 0x04, 0x8b, 0x01, 0x00, + 0x00, 0xd1, 0xb5, 0xad, 0xea, 0x04, 0xd1, 0xec, + 0x02, 0xc0, 0x9d, 0xd2, 0x9d, 0x24, 0x01, 0x00, + 0x29, 0x0c, 0x43, 0x02, 0x01, 0xb6, 0x04, 0x01, + 0x02, 0x01, 0x04, 0x05, 0x00, 0xa1, 0x01, 0x03, + 0x98, 0x06, 0x00, 0x01, 0x00, 0xdc, 0x05, 0x00, + 0x00, 0x00, 0xda, 0x05, 0x00, 0x01, 0x00, 0x9c, + 0x04, 0x33, 0x01, 0x9a, 0x04, 0x32, 0x01, 0xc8, + 0x03, 0x00, 0x0c, 0xb6, 0x02, 0x0b, 0x01, 0xb4, + 0x04, 0x3f, 0x01, 0xd1, 0xb4, 0xa8, 0xea, 0x4d, + 0xd1, 0xb4, 0xad, 0x69, 0x97, 0x00, 0x00, 0x00, + 0xdd, 0xde, 0xb5, 0x9e, 0xac, 0xea, 0x19, 0x65, + 0x02, 0x00, 0x42, 0x86, 0x01, 0x00, 0x00, 0x04, + 0x8d, 0x01, 0x00, 0x00, 0x24, 0x01, 0x00, 0x0e, + 0xb4, 0xe1, 0xd1, 0x8e, 0xd5, 0xec, 0xda, 0xe0, + 0x42, 0x8e, 0x01, 0x00, 0x00, 0xde, 0xb5, 0x9e, + 0xdd, 0x9e, 0xd1, 0x24, 0x02, 0x00, 0xca, 0x5e, + 0x04, 0x00, 0xc6, 0x04, 0x8f, 0x01, 0x00, 0x00, + 0xf0, 0x0e, 0xd1, 0xc6, 0x9e, 0xd5, 0xdd, 0xc6, + 0x9d, 0xe1, 0xec, 0xb5, 0xd1, 0x8c, 0xd5, 0xd1, + 0xb4, 0xad, 0xea, 0x48, 0xdd, 0xb4, 0xac, 0xea, + 0x22, 0x5e, 0x04, 0x00, 0xb5, 0x04, 0x72, 0x01, + 0x00, 0x00, 0xf0, 0x0e, 0x5e, 0x04, 0x00, 0xde, + 0xb5, 0x9e, 0x04, 0x8f, 0x01, 0x00, 0x00, 0xf0, + 0x0e, 0xd1, 0x8e, 0xd5, 0xde, 0xb5, 0x9e, 0xe1, + 0xec, 0xd6, 0xe0, 0x42, 0x8e, 0x01, 0x00, 0x00, + 0xd1, 0xdd, 0x24, 0x02, 0x00, 0xca, 0x5e, 0x04, + 0x00, 0xc6, 0x04, 0x90, 0x01, 0x00, 0x00, 0xf0, + 0x0e, 0xd1, 0xc6, 0x9e, 0xd5, 0xdd, 0xc6, 0x9e, + 0xe1, 0xec, 0xb5, 0x29, 0x0c, 0x43, 0x02, 0x01, + 0xb8, 0x04, 0x00, 0x05, 0x00, 0x06, 0x0d, 0x00, + 0x9c, 0x02, 0x05, 0xdc, 0x05, 0x00, 0x00, 0x00, + 0xa2, 0x06, 0x00, 0x01, 0x00, 0xf0, 0x05, 0x00, + 0x02, 0x00, 0x84, 0x06, 0x00, 0x03, 0x00, 0xa4, + 0x06, 0x00, 0x04, 0x00, 0x84, 0x04, 0x27, 0x01, + 0x88, 0x04, 0x29, 0x01, 0xe2, 0x03, 0x16, 0x01, + 0x8a, 0x04, 0x2a, 0x01, 0xc8, 0x03, 0x00, 0x0c, + 0xb6, 0x04, 0x40, 0x01, 0xac, 0x04, 0x3b, 0x01, + 0x80, 0x04, 0x25, 0x01, 0xc0, 0x05, 0x85, 0x01, + 0x01, 0xb2, 0x04, 0x3e, 0x01, 0x9c, 0x04, 0x33, + 0x01, 0x9a, 0x04, 0x32, 0x01, 0x86, 0x04, 0x28, + 0x01, 0xdd, 0xde, 0xad, 0x69, 0xc6, 0x00, 0x00, + 0x00, 0xdf, 0x96, 0xea, 0x32, 0xde, 0x42, 0x88, + 0x01, 0x00, 0x00, 0xb4, 0xe0, 0x24, 0x02, 0x00, + 0xdd, 0x42, 0x88, 0x01, 0x00, 0x00, 0xb4, 0xe0, + 0x24, 0x02, 0x00, 0xac, 0xea, 0x19, 0x65, 0x04, + 0x00, 0x42, 0x86, 0x01, 0x00, 0x00, 0xdd, 0x42, + 0x88, 0x01, 0x00, 0x00, 0xe0, 0x24, 0x01, 0x00, + 0x24, 0x01, 0x00, 0x0e, 0xec, 0x53, 0x5e, 0x05, + 0x00, 0x5e, 0x06, 0x00, 0xde, 0x42, 0x88, 0x01, + 0x00, 0x00, 0xb4, 0xe0, 0x24, 0x02, 0x00, 0xef, + 0x8c, 0xef, 0x0e, 0xdf, 0xea, 0x2e, 0x5e, 0x07, + 0x00, 0xea, 0x0e, 0x5e, 0x07, 0x00, 0x04, 0x8d, + 0x01, 0x00, 0x00, 0x9d, 0xdd, 0x9d, 0xec, 0x02, + 0xdd, 0xcf, 0xe9, 0xdd, 0xe9, 0x9e, 0xcc, 0x5e, + 0x08, 0x00, 0xc7, 0xef, 0xc2, 0x04, 0x5e, 0x09, + 0x00, 0xc7, 0xc8, 0xc1, 0x04, 0xb6, 0x47, 0xf1, + 0x0e, 0xec, 0x0e, 0x65, 0x04, 0x00, 0x42, 0x86, + 0x01, 0x00, 0x00, 0xdd, 0x24, 0x01, 0x00, 0x0e, + 0x5e, 0x0a, 0x00, 0x5e, 0x06, 0x00, 0xdd, 0xef, + 0x9d, 0x5e, 0x0b, 0x00, 0x9c, 0x60, 0x0a, 0x00, + 0xb4, 0xac, 0xea, 0x12, 0x65, 0x04, 0x00, 0x42, + 0x86, 0x01, 0x00, 0x00, 0x04, 0x93, 0x01, 0x00, + 0x00, 0x24, 0x01, 0x00, 0x0e, 0x65, 0x04, 0x00, + 0x42, 0x86, 0x01, 0x00, 0x00, 0x04, 0x94, 0x01, + 0x00, 0x00, 0x24, 0x01, 0x00, 0x0e, 0xdd, 0xe2, + 0xdd, 0xe9, 0xe4, 0x5e, 0x0c, 0x00, 0xe0, 0xa8, + 0xea, 0x19, 0x5e, 0x05, 0x00, 0x5e, 0x06, 0x00, + 0xdd, 0x42, 0x88, 0x01, 0x00, 0x00, 0xe0, 0x5e, + 0x0c, 0x00, 0x24, 0x02, 0x00, 0xef, 0xef, 0x0e, + 0xec, 0x1f, 0x5e, 0x0c, 0x00, 0xe0, 0xa6, 0xea, + 0x18, 0x5e, 0x05, 0x00, 0x5e, 0x06, 0x00, 0xdd, + 0x42, 0x88, 0x01, 0x00, 0x00, 0x5e, 0x0c, 0x00, + 0xe0, 0x24, 0x02, 0x00, 0xef, 0x8c, 0xef, 0x0e, + 0x5e, 0x0c, 0x00, 0xe4, 0x65, 0x04, 0x00, 0x41, + 0x95, 0x01, 0x00, 0x00, 0x42, 0x96, 0x01, 0x00, + 0x00, 0x24, 0x00, 0x00, 0x29, 0x0c, 0x43, 0x02, + 0x01, 0xba, 0x04, 0x01, 0x00, 0x01, 0x04, 0x02, + 0x00, 0x22, 0x01, 0xf0, 0x05, 0x00, 0x01, 0x00, + 0x84, 0x04, 0x27, 0x01, 0x86, 0x04, 0x28, 0x01, + 0xd1, 0xea, 0x1f, 0xdd, 0x42, 0x88, 0x01, 0x00, + 0x00, 0xb4, 0xde, 0x24, 0x02, 0x00, 0xd1, 0x9d, + 0xdd, 0x42, 0x88, 0x01, 0x00, 0x00, 0xde, 0x24, + 0x01, 0x00, 0x9d, 0xe1, 0xde, 0xd1, 0xe9, 0x9d, + 0xe2, 0x29, 0x0c, 0x43, 0x02, 0x01, 0xbc, 0x04, + 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x03, 0x00, + 0x90, 0x04, 0x2d, 0x01, 0x0a, 0xe1, 0x29, 0x0c, + 0x43, 0x02, 0x01, 0xbe, 0x04, 0x00, 0x00, 0x00, + 0x01, 0x02, 0x00, 0x07, 0x00, 0x84, 0x04, 0x27, + 0x01, 0x86, 0x04, 0x28, 0x01, 0xc0, 0xe1, 0xb4, + 0xe2, 0xbc, 0xfe, 0x28, 0x0c, 0x43, 0x02, 0x01, + 0xc0, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x29, 0x0c, 0x43, 0x02, 0x01, 0xc2, + 0x04, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x03, + 0x00, 0x86, 0x04, 0x28, 0x01, 0xb4, 0xe1, 0x29, + 0x0c, 0x43, 0x02, 0x01, 0xc4, 0x04, 0x00, 0x00, + 0x00, 0x01, 0x02, 0x00, 0x04, 0x00, 0x86, 0x04, + 0x28, 0x01, 0x84, 0x04, 0x27, 0x01, 0xde, 0xe9, + 0xe1, 0x29, 0x0c, 0x43, 0x02, 0x01, 0xc6, 0x04, + 0x00, 0x00, 0x00, 0x04, 0x03, 0x00, 0x1d, 0x00, + 0x86, 0x04, 0x28, 0x01, 0x84, 0x04, 0x27, 0x01, + 0xae, 0x04, 0x3c, 0x01, 0xdd, 0xde, 0xe9, 0xa6, + 0xea, 0x17, 0xdd, 0x8f, 0xe1, 0xdf, 0xde, 0x42, + 0x97, 0x01, 0x00, 0x00, 0xdd, 0x24, 0x01, 0x00, + 0xef, 0xea, 0x06, 0xdd, 0x8f, 0xe1, 0xec, 0xee, + 0x29, 0x0c, 0x43, 0x02, 0x01, 0xc8, 0x04, 0x00, + 0x00, 0x00, 0x04, 0x03, 0x00, 0x1c, 0x00, 0x86, + 0x04, 0x28, 0x01, 0xae, 0x04, 0x3c, 0x01, 0x84, + 0x04, 0x27, 0x01, 0xdd, 0xb4, 0xa8, 0xea, 0x17, + 0xdd, 0x8e, 0xe1, 0xde, 0xdf, 0x42, 0x97, 0x01, + 0x00, 0x00, 0xdd, 0x24, 0x01, 0x00, 0xef, 0xea, + 0x06, 0xdd, 0x8e, 0xe1, 0xec, 0xee, 0x29, 0x0c, + 0x43, 0x02, 0x01, 0xca, 0x04, 0x01, 0x00, 0x01, + 0x04, 0x02, 0x00, 0x35, 0x01, 0xb0, 0x06, 0x00, + 0x01, 0x00, 0x84, 0x04, 0x27, 0x01, 0xaa, 0x04, + 0x3a, 0x01, 0xd1, 0xdd, 0xe9, 0xa6, 0xea, 0x15, + 0xde, 0xdd, 0x42, 0x97, 0x01, 0x00, 0x00, 0xd1, + 0x24, 0x01, 0x00, 0xef, 0x96, 0xea, 0x06, 0xd1, + 0x8f, 0xd5, 0xec, 0xe7, 0xd1, 0xdd, 0xe9, 0xa6, + 0xea, 0x14, 0xde, 0xdd, 0x42, 0x97, 0x01, 0x00, + 0x00, 0xd1, 0x24, 0x01, 0x00, 0xef, 0xea, 0x06, + 0xd1, 0x8f, 0xd5, 0xec, 0xe8, 0xd1, 0x28, 0x0c, + 0x43, 0x02, 0x01, 0xcc, 0x04, 0x01, 0x00, 0x01, + 0x05, 0x02, 0x00, 0x37, 0x01, 0xb0, 0x06, 0x00, + 0x01, 0x00, 0xaa, 0x04, 0x3a, 0x01, 0x84, 0x04, + 0x27, 0x01, 0xd1, 0xb4, 0xa8, 0xea, 0x17, 0xdd, + 0xde, 0x42, 0x97, 0x01, 0x00, 0x00, 0xd1, 0xb5, + 0x9e, 0x24, 0x01, 0x00, 0xef, 0x96, 0xea, 0x06, + 0xd1, 0x8e, 0xd5, 0xec, 0xe6, 0xd1, 0xb4, 0xa8, + 0xea, 0x16, 0xdd, 0xde, 0x42, 0x97, 0x01, 0x00, + 0x00, 0xd1, 0xb5, 0x9e, 0x24, 0x01, 0x00, 0xef, + 0xea, 0x06, 0xd1, 0x8e, 0xd5, 0xec, 0xe7, 0xd1, + 0x28, 0x0c, 0x43, 0x02, 0x01, 0xce, 0x04, 0x00, + 0x00, 0x00, 0x02, 0x02, 0x00, 0x05, 0x00, 0x86, + 0x04, 0x28, 0x01, 0xca, 0x04, 0x4a, 0x01, 0xde, + 0xdd, 0xef, 0xe1, 0x29, 0x0c, 0x43, 0x02, 0x01, + 0xd0, 0x04, 0x00, 0x00, 0x00, 0x02, 0x02, 0x00, + 0x05, 0x00, 0x86, 0x04, 0x28, 0x01, 0xcc, 0x04, + 0x4b, 0x01, 0xde, 0xdd, 0xef, 0xe1, 0x29, 0x0c, + 0x43, 0x02, 0x01, 0xd2, 0x04, 0x00, 0x00, 0x00, + 0x02, 0x01, 0x00, 0x0e, 0x00, 0xae, 0x05, 0x7c, + 0x01, 0xdd, 0x04, 0x99, 0x01, 0x00, 0x00, 0x48, + 0x24, 0x00, 0x00, 0x0e, 0xbc, 0xfe, 0x28, 0x0c, + 0x43, 0x02, 0x01, 0xd4, 0x04, 0x00, 0x00, 0x00, + 0x03, 0x03, 0x00, 0x17, 0x00, 0xc8, 0x03, 0x00, + 0x0c, 0xd6, 0x04, 0x50, 0x01, 0x84, 0x04, 0x27, + 0x01, 0x65, 0x00, 0x00, 0x42, 0x86, 0x01, 0x00, + 0x00, 0x04, 0x8d, 0x01, 0x00, 0x00, 0x24, 0x01, + 0x00, 0x0e, 0xde, 0xdf, 0xef, 0x0e, 0xb3, 0x28, + 0x0c, 0x43, 0x02, 0x01, 0xd6, 0x04, 0x01, 0x00, + 0x01, 0x03, 0x02, 0x00, 0x36, 0x01, 0xf0, 0x05, + 0x00, 0x01, 0x00, 0xec, 0x03, 0x1b, 0x01, 0xee, + 0x03, 0x1c, 0x01, 0xd1, 0x42, 0x9a, 0x01, 0x00, + 0x00, 0x24, 0x00, 0x00, 0xd9, 0xea, 0x27, 0xdd, + 0xe9, 0xea, 0x18, 0xdd, 0xdd, 0xe9, 0xb5, 0x9e, + 0x47, 0x96, 0xea, 0x0f, 0xdd, 0x42, 0x32, 0x00, + 0x00, 0x00, 0x8e, 0x43, 0x32, 0x00, 0x00, 0x00, + 0xec, 0xe6, 0xdd, 0x42, 0x9b, 0x01, 0x00, 0x00, + 0xd1, 0x24, 0x01, 0x00, 0x0e, 0xdd, 0xe9, 0xe2, + 0x29, 0x0c, 0x43, 0x02, 0x01, 0xd8, 0x04, 0x00, + 0x00, 0x00, 0x03, 0x04, 0x00, 0x20, 0x00, 0xee, + 0x03, 0x1c, 0x01, 0xec, 0x03, 0x1b, 0x01, 0x84, + 0x04, 0x27, 0x01, 0x86, 0x04, 0x28, 0x01, 0xdd, + 0xb4, 0xa8, 0xea, 0x1b, 0xdd, 0xde, 0xe9, 0xac, + 0xea, 0x0c, 0xde, 0x42, 0x9b, 0x01, 0x00, 0x00, + 0xdf, 0x24, 0x01, 0x00, 0x0e, 0xdd, 0x8e, 0xe1, + 0xde, 0xdd, 0x47, 0xe7, 0xe9, 0xe4, 0x29, 0x0c, + 0x43, 0x02, 0x01, 0xda, 0x04, 0x00, 0x00, 0x00, + 0x03, 0x04, 0x00, 0x12, 0x00, 0xee, 0x03, 0x1c, + 0x01, 0xec, 0x03, 0x1b, 0x01, 0x84, 0x04, 0x27, + 0x01, 0x86, 0x04, 0x28, 0x01, 0xdd, 0xde, 0xe9, + 0xb5, 0x9e, 0xa6, 0xea, 0x0a, 0xdd, 0x8f, 0xe1, + 0xde, 0xdd, 0x47, 0xe7, 0xe9, 0xe4, 0x29, 0x0c, + 0x43, 0x02, 0x01, 0xdc, 0x04, 0x01, 0x03, 0x01, + 0x05, 0x04, 0x00, 0x3d, 0x04, 0xb8, 0x06, 0x00, + 0x01, 0x00, 0xb0, 0x06, 0x00, 0x00, 0x00, 0xdc, + 0x05, 0x00, 0x01, 0x00, 0xb0, 0x01, 0x00, 0x02, + 0x00, 0x86, 0x04, 0x28, 0x01, 0xec, 0x03, 0x1b, + 0x01, 0xee, 0x03, 0x1c, 0x01, 0x84, 0x04, 0x27, + 0x01, 0xdd, 0xc9, 0xb5, 0xca, 0xc6, 0xde, 0xe9, + 0xa7, 0xea, 0x33, 0xde, 0xe9, 0xc6, 0xd1, 0x9a, + 0x9d, 0xdf, 0x9d, 0xde, 0xe9, 0x9c, 0xcb, 0xde, + 0xc7, 0x47, 0x42, 0x88, 0x01, 0x00, 0x00, 0xb4, + 0xc5, 0x24, 0x02, 0x00, 0xe0, 0x42, 0x88, 0x01, + 0x00, 0x00, 0xb4, 0xc5, 0x24, 0x02, 0x00, 0xac, + 0xea, 0x08, 0xc7, 0xe3, 0xde, 0xc7, 0x47, 0xe4, + 0x29, 0x93, 0x01, 0xec, 0xc9, 0x29, 0x0c, 0x43, + 0x02, 0x01, 0xde, 0x04, 0x00, 0x00, 0x00, 0x02, + 0x01, 0x00, 0x05, 0x00, 0xdc, 0x04, 0x53, 0x01, + 0xdd, 0xb3, 0x23, 0x01, 0x00, 0x0c, 0x43, 0x02, + 0x01, 0xe0, 0x04, 0x00, 0x00, 0x00, 0x02, 0x01, + 0x00, 0x05, 0x00, 0xdc, 0x04, 0x53, 0x01, 0xdd, + 0xb5, 0x23, 0x01, 0x00, 0x0c, 0x43, 0x02, 0x01, + 0xe2, 0x04, 0x01, 0x02, 0x01, 0x04, 0x05, 0x00, + 0x65, 0x03, 0xb8, 0x06, 0x00, 0x01, 0x00, 0x84, + 0x06, 0x00, 0x00, 0x00, 0xba, 0x06, 0x00, 0x01, + 0x00, 0x86, 0x04, 0x28, 0x01, 0xae, 0x04, 0x3c, + 0x01, 0x84, 0x04, 0x27, 0x01, 0x8e, 0x04, 0x2c, + 0x01, 0xf2, 0x04, 0x5e, 0x01, 0xdd, 0xc9, 0xd1, + 0xb4, 0xa6, 0xea, 0x15, 0x92, 0x00, 0xde, 0xdf, + 0x42, 0x97, 0x01, 0x00, 0x00, 0xc5, 0x24, 0x01, + 0x00, 0xef, 0xea, 0x05, 0x92, 0x00, 0xec, 0xef, + 0xc5, 0xb5, 0x9d, 0xca, 0xde, 0xdf, 0x42, 0x97, + 0x01, 0x00, 0x00, 0xc6, 0x24, 0x01, 0x00, 0xef, + 0xea, 0x05, 0x93, 0x01, 0xec, 0xef, 0xc5, 0xb4, + 0xa9, 0xea, 0x2f, 0xc5, 0xdf, 0xe9, 0xa6, 0xea, + 0x29, 0xe0, 0x5e, 0x04, 0x00, 0xae, 0xea, 0x09, + 0x5e, 0x04, 0x00, 0xc4, 0xd1, 0xf1, 0x0e, 0x29, + 0xdf, 0x42, 0x88, 0x01, 0x00, 0x00, 0xb4, 0xc5, + 0x24, 0x02, 0x00, 0xdf, 0x42, 0x88, 0x01, 0x00, + 0x00, 0xc6, 0x24, 0x01, 0x00, 0x9d, 0xe3, 0xc5, + 0xe1, 0x29, 0x0c, 0x43, 0x02, 0x01, 0xe4, 0x04, + 0x00, 0x00, 0x00, 0x02, 0x01, 0x00, 0x04, 0x00, + 0xe2, 0x04, 0x56, 0x01, 0xdd, 0xb5, 0xef, 0x29, + 0x0c, 0x43, 0x02, 0x01, 0xe6, 0x04, 0x00, 0x00, + 0x00, 0x03, 0x03, 0x00, 0x1f, 0x00, 0x84, 0x04, + 0x27, 0x01, 0xc8, 0x03, 0x00, 0x0c, 0xe2, 0x04, + 0x56, 0x01, 0xdd, 0xe9, 0xb4, 0xac, 0xea, 0x15, + 0x65, 0x01, 0x00, 0x42, 0x86, 0x01, 0x00, 0x00, + 0x04, 0x8d, 0x01, 0x00, 0x00, 0x24, 0x01, 0x00, + 0x0e, 0xbc, 0xfd, 0x28, 0xdf, 0xb5, 0xef, 0x0e, + 0x29, 0x0c, 0x43, 0x02, 0x01, 0xe8, 0x04, 0x00, + 0x00, 0x00, 0x02, 0x01, 0x00, 0x04, 0x00, 0xe2, + 0x04, 0x56, 0x01, 0xdd, 0xb3, 0xef, 0x29, 0x0c, + 0x43, 0x02, 0x01, 0xea, 0x04, 0x00, 0x01, 0x00, + 0x06, 0x02, 0x00, 0x51, 0x01, 0xb0, 0x06, 0x00, + 0x00, 0x00, 0x86, 0x04, 0x28, 0x01, 0x84, 0x04, + 0x27, 0x01, 0xdd, 0xc9, 0xde, 0xe9, 0xb5, 0xa8, + 0xea, 0x49, 0xc5, 0xb4, 0xa8, 0xea, 0x44, 0xc5, + 0xde, 0xe9, 0xac, 0xea, 0x03, 0x92, 0x00, 0xde, + 0x42, 0x88, 0x01, 0x00, 0x00, 0xb4, 0xc5, 0xb5, + 0x9e, 0x24, 0x02, 0x00, 0xde, 0x42, 0x88, 0x01, + 0x00, 0x00, 0xc5, 0xc5, 0xb5, 0x9d, 0x24, 0x02, + 0x00, 0x9d, 0xde, 0x42, 0x88, 0x01, 0x00, 0x00, + 0xc5, 0xb5, 0x9e, 0xc5, 0x24, 0x02, 0x00, 0x9d, + 0xde, 0x42, 0x88, 0x01, 0x00, 0x00, 0xc5, 0xb5, + 0x9d, 0x24, 0x01, 0x00, 0x9d, 0xe2, 0xc5, 0xb5, + 0x9d, 0xe1, 0x29, 0x0c, 0x43, 0x02, 0x01, 0xec, + 0x04, 0x00, 0x04, 0x00, 0x05, 0x04, 0x00, 0x55, + 0x04, 0xbc, 0x06, 0x00, 0x00, 0x00, 0xbe, 0x06, + 0x00, 0x01, 0x00, 0xc0, 0x06, 0x00, 0x02, 0x00, + 0xc2, 0x06, 0x00, 0x03, 0x00, 0xcc, 0x04, 0x4b, + 0x01, 0x86, 0x04, 0x28, 0x01, 0xca, 0x04, 0x4a, + 0x01, 0x84, 0x04, 0x27, 0x01, 0xdd, 0xde, 0xef, + 0xc9, 0xdf, 0xc5, 0xef, 0xca, 0xdf, 0xde, 0xef, + 0xcb, 0xdd, 0xc7, 0xef, 0xcc, 0xc4, 0xa6, 0xea, + 0x41, 0xc6, 0xde, 0xa7, 0xea, 0x3c, 0xde, 0xc8, + 0xa7, 0xea, 0x37, 0xc8, 0xc7, 0xa6, 0xea, 0x32, + 0xe0, 0x42, 0x88, 0x01, 0x00, 0x00, 0xb4, 0xc5, + 0x24, 0x02, 0x00, 0xe0, 0x42, 0x88, 0x01, 0x00, + 0x00, 0xc8, 0xc7, 0x24, 0x02, 0x00, 0x9d, 0xe0, + 0x42, 0x88, 0x01, 0x00, 0x00, 0xc6, 0xc8, 0x24, + 0x02, 0x00, 0x9d, 0xe0, 0x42, 0x88, 0x01, 0x00, + 0x00, 0xc4, 0x24, 0x02, 0x00, 0x9d, 0xe4, 0xc7, + 0xe2, 0x29, 0x0c, 0x43, 0x02, 0x01, 0xee, 0x04, + 0x00, 0x01, 0x00, 0x05, 0x03, 0x00, 0x30, 0x01, + 0xba, 0x06, 0x00, 0x00, 0x00, 0xca, 0x04, 0x4a, + 0x01, 0x86, 0x04, 0x28, 0x01, 0x84, 0x04, 0x27, + 0x01, 0xdd, 0xde, 0xef, 0xc9, 0xdf, 0x42, 0x88, + 0x01, 0x00, 0x00, 0xb4, 0xde, 0x24, 0x02, 0x00, + 0xdf, 0x42, 0x88, 0x01, 0x00, 0x00, 0xde, 0xc5, + 0x24, 0x02, 0x00, 0x42, 0xa2, 0x01, 0x00, 0x00, + 0x24, 0x00, 0x00, 0x9d, 0xdf, 0x42, 0x88, 0x01, + 0x00, 0x00, 0xc5, 0x24, 0x01, 0x00, 0x9d, 0xe3, + 0x29, 0x0c, 0x43, 0x02, 0x01, 0xf0, 0x04, 0x00, + 0x01, 0x00, 0x05, 0x03, 0x00, 0x30, 0x01, 0xba, + 0x06, 0x00, 0x00, 0x00, 0xca, 0x04, 0x4a, 0x01, + 0x86, 0x04, 0x28, 0x01, 0x84, 0x04, 0x27, 0x01, + 0xdd, 0xde, 0xef, 0xc9, 0xdf, 0x42, 0x88, 0x01, + 0x00, 0x00, 0xb4, 0xde, 0x24, 0x02, 0x00, 0xdf, + 0x42, 0x88, 0x01, 0x00, 0x00, 0xde, 0xc5, 0x24, + 0x02, 0x00, 0x42, 0xa3, 0x01, 0x00, 0x00, 0x24, + 0x00, 0x00, 0x9d, 0xdf, 0x42, 0x88, 0x01, 0x00, + 0x00, 0xc5, 0x24, 0x01, 0x00, 0x9d, 0xe3, 0x29, + 0x0c, 0x43, 0x02, 0x01, 0xf2, 0x04, 0x03, 0x01, + 0x03, 0x04, 0x06, 0x00, 0x5e, 0x04, 0x84, 0x06, + 0x00, 0x01, 0x00, 0xba, 0x06, 0x00, 0x01, 0x00, + 0xb8, 0x06, 0x00, 0x01, 0x00, 0xc8, 0x06, 0x00, + 0x00, 0x00, 0x84, 0x04, 0x27, 0x01, 0x8e, 0x04, + 0x2c, 0x01, 0xf2, 0x04, 0x5e, 0x01, 0xf0, 0x03, + 0x1d, 0x01, 0x86, 0x04, 0x28, 0x01, 0x8c, 0x04, + 0x2b, 0x01, 0xdd, 0x42, 0x88, 0x01, 0x00, 0x00, + 0xd1, 0xd2, 0x24, 0x02, 0x00, 0xc9, 0xde, 0xdf, + 0xaf, 0xea, 0x05, 0xc5, 0xe4, 0xec, 0x10, 0xd3, + 0xb4, 0xa6, 0xea, 0x07, 0xc5, 0xe0, 0x9d, 0xe4, + 0xec, 0x05, 0xe0, 0xc5, 0x9d, 0xe4, 0xdd, 0x42, + 0x88, 0x01, 0x00, 0x00, 0xb4, 0xd1, 0x24, 0x02, + 0x00, 0xdd, 0x42, 0x88, 0x01, 0x00, 0x00, 0xd2, + 0x24, 0x01, 0x00, 0x9d, 0xe1, 0x5e, 0x04, 0x00, + 0xd2, 0xa8, 0xea, 0x0d, 0x5e, 0x04, 0x00, 0xd2, + 0xd1, 0x9e, 0x9e, 0x5f, 0x04, 0x00, 0xec, 0x0c, + 0x5e, 0x04, 0x00, 0xd1, 0xa8, 0xea, 0x05, 0xd1, + 0x5f, 0x04, 0x00, 0xdf, 0x5f, 0x05, 0x00, 0x29, + 0x0c, 0x43, 0x02, 0x01, 0xf4, 0x04, 0x00, 0x00, + 0x00, 0x04, 0x03, 0x00, 0x07, 0x00, 0xf2, 0x04, + 0x5e, 0x01, 0x86, 0x04, 0x28, 0x01, 0x84, 0x04, + 0x27, 0x01, 0xdd, 0xde, 0xdf, 0xe9, 0xb5, 0xf1, + 0x29, 0x0c, 0x43, 0x02, 0x01, 0xf6, 0x04, 0x00, + 0x00, 0x00, 0x04, 0x02, 0x00, 0x06, 0x00, 0xf2, + 0x04, 0x5e, 0x01, 0x86, 0x04, 0x28, 0x01, 0xdd, + 0xb4, 0xde, 0xb3, 0xf1, 0x29, 0x0c, 0x43, 0x02, + 0x01, 0xf8, 0x04, 0x00, 0x00, 0x00, 0x04, 0x03, + 0x00, 0x08, 0x00, 0xf2, 0x04, 0x5e, 0x01, 0x86, + 0x04, 0x28, 0x01, 0xca, 0x04, 0x4a, 0x01, 0xdd, + 0xde, 0xdf, 0xde, 0xef, 0xb5, 0xf1, 0x29, 0x0c, + 0x43, 0x02, 0x01, 0xfa, 0x04, 0x00, 0x00, 0x00, + 0x04, 0x03, 0x00, 0x08, 0x00, 0xf2, 0x04, 0x5e, + 0x01, 0xcc, 0x04, 0x4b, 0x01, 0x86, 0x04, 0x28, + 0x01, 0xdd, 0xde, 0xdf, 0xef, 0xdf, 0xb3, 0xf1, + 0x29, 0x0c, 0x43, 0x02, 0x01, 0xfc, 0x04, 0x00, + 0x00, 0x00, 0x02, 0x02, 0x00, 0x04, 0x00, 0xba, + 0x04, 0x42, 0x01, 0xf0, 0x03, 0x1d, 0x01, 0xdd, + 0xde, 0xef, 0x29, 0x0c, 0x43, 0x02, 0x01, 0xfe, + 0x04, 0x00, 0x00, 0x00, 0x03, 0x05, 0x00, 0x32, + 0x00, 0x8e, 0x04, 0x2c, 0x01, 0xfe, 0x04, 0x64, + 0x01, 0xc8, 0x03, 0x00, 0x0c, 0xaa, 0x05, 0x7a, + 0x01, 0x94, 0x05, 0x6f, 0x01, 0xdd, 0xde, 0xae, + 0xea, 0x17, 0x65, 0x02, 0x00, 0x42, 0x86, 0x01, + 0x00, 0x00, 0x04, 0x8d, 0x01, 0x00, 0x00, 0x24, + 0x01, 0x00, 0x0e, 0xe0, 0xb4, 0xef, 0x0e, 0x29, + 0x65, 0x02, 0x00, 0x42, 0x86, 0x01, 0x00, 0x00, + 0x04, 0xa5, 0x01, 0x00, 0x00, 0x24, 0x01, 0x00, + 0x0e, 0x5e, 0x04, 0x00, 0xee, 0x0e, 0x29, 0x0c, + 0x43, 0x02, 0x01, 0x80, 0x05, 0x00, 0x00, 0x00, + 0x01, 0x02, 0x00, 0x05, 0x00, 0x84, 0x04, 0x27, + 0x01, 0x86, 0x04, 0x28, 0x01, 0xc0, 0xe1, 0xb4, + 0xe2, 0x29, 0x0c, 0x43, 0x02, 0x01, 0x82, 0x05, + 0x02, 0x01, 0x02, 0x04, 0x01, 0x00, 0x1f, 0x03, + 0xcc, 0x06, 0x00, 0x01, 0x00, 0xba, 0x06, 0x00, + 0x01, 0x00, 0xb0, 0x06, 0x00, 0x00, 0x00, 0xaa, + 0x04, 0x3a, 0x01, 0xd2, 0xc9, 0xc5, 0xb4, 0xa8, + 0xea, 0x0e, 0xdd, 0xd1, 0xc5, 0xb5, 0x9e, 0x47, + 0xef, 0xea, 0x05, 0x92, 0x00, 0xec, 0xef, 0xd1, + 0x42, 0xa7, 0x01, 0x00, 0x00, 0xc5, 0xd2, 0x25, + 0x02, 0x00, 0x0c, 0x43, 0x02, 0x01, 0x84, 0x05, + 0x02, 0x04, 0x02, 0x05, 0x07, 0x04, 0x9f, 0x02, + 0x06, 0xcc, 0x06, 0x00, 0x01, 0x00, 0xb0, 0x06, + 0x00, 0x01, 0x00, 0xe2, 0x05, 0x00, 0x00, 0x00, + 0xd0, 0x06, 0x00, 0x01, 0x00, 0xd2, 0x06, 0x00, + 0x02, 0x00, 0xd4, 0x06, 0x00, 0x03, 0x00, 0xce, + 0x03, 0x00, 0x03, 0xae, 0x05, 0x7c, 0x01, 0xaa, + 0x04, 0x3a, 0x01, 0x82, 0x05, 0x66, 0x01, 0xd2, + 0x03, 0x0e, 0x01, 0x84, 0x05, 0x67, 0x01, 0xc2, + 0x02, 0x08, 0x01, 0x07, 0x02, 0x20, 0x07, 0x34, + 0x00, 0x00, 0x01, 0x00, 0x12, 0x00, 0x00, 0x00, + 0x09, 0x06, 0x00, 0x00, 0x00, 0x05, 0x08, 0xf5, + 0xff, 0xff, 0xff, 0x0c, 0x00, 0x01, 0x20, 0x0d, + 0x00, 0x0b, 0x07, 0x1a, 0x5e, 0x5b, 0x64, 0x67, + 0x69, 0x6d, 0x73, 0x75, 0x76, 0x79, 0x5d, 0x2b, + 0x24, 0x07, 0x96, 0x01, 0x00, 0x00, 0x01, 0x00, + 0x43, 0x00, 0x00, 0x00, 0x09, 0x06, 0x00, 0x00, + 0x00, 0x05, 0x08, 0xf5, 0xff, 0xff, 0xff, 0x0c, + 0x00, 0x06, 0x1d, 0x20, 0x00, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x7f, 0x01, + 0x00, 0x00, 0x00, 0x16, 0x07, 0x00, 0x64, 0x00, + 0x64, 0x00, 0x67, 0x00, 0x67, 0x00, 0x69, 0x00, + 0x69, 0x00, 0x6d, 0x00, 0x6d, 0x00, 0x73, 0x00, + 0x73, 0x00, 0x75, 0x00, 0x76, 0x00, 0x79, 0x00, + 0x79, 0x00, 0x0b, 0x07, 0x0d, 0x00, 0x0b, 0xd2, + 0xb4, 0xa7, 0xea, 0x03, 0xdd, 0x28, 0xd1, 0xd2, + 0xb5, 0x9e, 0x47, 0xc9, 0xd2, 0xb5, 0xae, 0xea, + 0x17, 0xc5, 0x04, 0xab, 0x01, 0x00, 0x00, 0xae, + 0x11, 0xeb, 0x09, 0x0e, 0xc5, 0x04, 0xac, 0x01, + 0x00, 0x00, 0xae, 0xea, 0x03, 0xde, 0x28, 0x04, + 0xad, 0x01, 0x00, 0x00, 0x42, 0xae, 0x01, 0x00, + 0x00, 0xc5, 0x24, 0x01, 0x00, 0xb4, 0xa9, 0xea, + 0x02, 0x29, 0xc5, 0x04, 0xac, 0x01, 0x00, 0x00, + 0xae, 0x69, 0xda, 0x00, 0x00, 0x00, 0xd2, 0x8e, + 0xd6, 0xd1, 0xd2, 0xb5, 0x9e, 0x47, 0xcd, 0x11, + 0x04, 0xaf, 0x01, 0x00, 0x00, 0xae, 0xeb, 0x13, + 0x11, 0x04, 0xb0, 0x01, 0x00, 0x00, 0xae, 0xeb, + 0x0a, 0x11, 0x04, 0xb1, 0x01, 0x00, 0x00, 0xae, + 0xea, 0x07, 0x04, 0x74, 0x01, 0x00, 0x00, 0x28, + 0x11, 0x04, 0xb2, 0x01, 0x00, 0x00, 0xae, 0xea, + 0x05, 0x26, 0x00, 0x00, 0x28, 0x11, 0x04, 0xb3, + 0x01, 0x00, 0x00, 0xae, 0xea, 0x07, 0xbe, 0x00, + 0xbe, 0x01, 0x33, 0x28, 0xdf, 0xc5, 0xef, 0x69, + 0x89, 0x00, 0x00, 0x00, 0xe0, 0xd1, 0xd2, 0xf0, + 0xca, 0xd2, 0xc6, 0xe9, 0x9e, 0xcb, 0xc6, 0x04, + 0x03, 0x00, 0x00, 0x00, 0xae, 0x11, 0xeb, 0x09, + 0x0e, 0xc6, 0x04, 0x02, 0x00, 0x00, 0x00, 0xae, + 0xea, 0x03, 0x0a, 0x28, 0xc6, 0x04, 0x01, 0x00, + 0x00, 0x00, 0xae, 0xea, 0x03, 0x07, 0x28, 0xc6, + 0x04, 0x08, 0x00, 0x00, 0x00, 0xae, 0xea, 0x03, + 0xdd, 0x28, 0x5e, 0x04, 0x00, 0xc6, 0x8d, 0xef, + 0x96, 0xea, 0x03, 0xb4, 0x28, 0x5e, 0x05, 0x00, + 0xd1, 0xc7, 0xf0, 0xd0, 0xf3, 0x11, 0xeb, 0x04, + 0x0e, 0xc8, 0xf2, 0xea, 0x03, 0xc8, 0x28, 0xc8, + 0xc6, 0x47, 0xf4, 0xeb, 0x05, 0xc8, 0xc6, 0x47, + 0x28, 0xc7, 0xb7, 0xa9, 0xea, 0x24, 0xd1, 0xc7, + 0xb5, 0x9e, 0x47, 0x04, 0xb3, 0x01, 0x00, 0x00, + 0xae, 0xea, 0x17, 0xc6, 0x42, 0xb4, 0x01, 0x00, + 0x00, 0xbe, 0x02, 0xbe, 0x03, 0x33, 0x24, 0x01, + 0x00, 0xea, 0x07, 0x5e, 0x06, 0x00, 0x23, 0x00, + 0x00, 0x0e, 0x0b, 0x28, 0xdd, 0x28, 0x0c, 0x43, + 0x02, 0x01, 0x86, 0x05, 0x02, 0x0a, 0x02, 0x04, + 0x03, 0x01, 0xee, 0x01, 0x0c, 0xcc, 0x06, 0x00, + 0x01, 0x00, 0xb0, 0x06, 0x00, 0x01, 0x00, 0xc8, + 0x06, 0x00, 0x00, 0x00, 0xd4, 0x06, 0x00, 0x01, + 0x00, 0xea, 0x06, 0x00, 0x02, 0x00, 0xec, 0x06, + 0x00, 0x03, 0x00, 0xdc, 0x05, 0x00, 0x04, 0x00, + 0x88, 0x06, 0x00, 0x05, 0x00, 0xee, 0x06, 0x00, + 0x06, 0x00, 0xf0, 0x06, 0x00, 0x07, 0x00, 0xf2, + 0x06, 0x00, 0x08, 0x00, 0xf4, 0x06, 0x08, 0x00, + 0x21, 0x82, 0x05, 0x66, 0x01, 0x84, 0x05, 0x67, + 0x01, 0xa6, 0x02, 0x00, 0x01, 0x0c, 0x43, 0x02, + 0x01, 0xf4, 0x06, 0x02, 0x00, 0x02, 0x03, 0x00, + 0x00, 0x34, 0x02, 0xe8, 0x05, 0x00, 0x01, 0x00, + 0xfc, 0x05, 0x00, 0x01, 0x00, 0xd1, 0xb4, 0x47, + 0xd2, 0xb4, 0x47, 0xad, 0xea, 0x1b, 0xd1, 0xb4, + 0x47, 0x04, 0x76, 0x01, 0x00, 0x00, 0xac, 0xea, + 0x03, 0xb5, 0x28, 0xd2, 0xb4, 0x47, 0x04, 0x76, + 0x01, 0x00, 0x00, 0xac, 0xea, 0x03, 0xb3, 0x28, + 0xd1, 0xd2, 0xa6, 0xea, 0x03, 0xb3, 0x28, 0xd1, + 0xd2, 0xa8, 0xea, 0x04, 0xb5, 0x8d, 0x28, 0xb4, + 0x28, 0xdd, 0xd1, 0xd2, 0xf0, 0xc9, 0xde, 0xd1, + 0xd2, 0xc5, 0xe9, 0x9e, 0xf0, 0xcb, 0x26, 0x00, + 0x00, 0xcc, 0xb4, 0xc2, 0x04, 0xc7, 0xca, 0xc1, + 0x04, 0xbc, 0x0a, 0xa6, 0xea, 0x67, 0xc6, 0xf3, + 0xeb, 0x63, 0xc6, 0x06, 0xaf, 0xea, 0x5e, 0xdf, + 0x42, 0xbb, 0x01, 0x00, 0x00, 0xc6, 0x24, 0x01, + 0x00, 0xc2, 0x07, 0xb4, 0xc2, 0x05, 0xc1, 0x05, + 0xc1, 0x07, 0xe9, 0xa6, 0xea, 0x38, 0xc1, 0x07, + 0xc1, 0x05, 0x47, 0xc3, 0x08, 0x97, 0x04, 0x49, + 0x00, 0x00, 0x00, 0xac, 0xea, 0x24, 0xc0, 0xc1, + 0x08, 0x8d, 0x9d, 0xc1, 0x08, 0xad, 0xea, 0x1a, + 0xc1, 0x08, 0x42, 0xbc, 0x01, 0x00, 0x00, 0xc5, + 0x24, 0x01, 0x00, 0xea, 0x0d, 0xc8, 0x42, 0x9b, + 0x01, 0x00, 0x00, 0xc1, 0x08, 0x24, 0x01, 0x00, + 0x0e, 0x93, 0x05, 0xec, 0xc2, 0xdf, 0x42, 0x60, + 0x00, 0x00, 0x00, 0xc6, 0x24, 0x01, 0x00, 0xca, + 0x93, 0x04, 0xec, 0x94, 0xc8, 0xe9, 0xb5, 0xa8, + 0xea, 0x51, 0xbf, 0x00, 0xc2, 0x09, 0xbf, 0x00, + 0x0e, 0xc8, 0x42, 0xbd, 0x01, 0x00, 0x00, 0x62, + 0x09, 0x00, 0x24, 0x01, 0x00, 0x0e, 0xb5, 0xc3, + 0x05, 0xc2, 0x04, 0xc1, 0x04, 0xc8, 0xe9, 0xa6, + 0xea, 0x29, 0xc8, 0xc1, 0x04, 0x47, 0xc8, 0xc1, + 0x04, 0xb5, 0x9e, 0x47, 0xad, 0xea, 0x18, 0xc8, + 0xc1, 0x05, 0x91, 0xc2, 0x05, 0x1b, 0x11, 0xb0, + 0xeb, 0x04, 0x1b, 0x71, 0x1b, 0x1b, 0xc8, 0xc1, + 0x04, 0x47, 0x1b, 0x71, 0x1b, 0x49, 0x93, 0x04, + 0xec, 0xd2, 0xc8, 0xc1, 0x05, 0x43, 0x32, 0x00, + 0x00, 0x00, 0x0b, 0xc8, 0x4c, 0x65, 0x01, 0x00, + 0x00, 0xc5, 0xe9, 0x4c, 0x98, 0x01, 0x00, 0x00, + 0xc7, 0x4c, 0xbe, 0x01, 0x00, 0x00, 0x28, 0x0c, + 0x43, 0x02, 0x01, 0x88, 0x05, 0x00, 0x0d, 0x00, + 0x07, 0x0a, 0x00, 0x8f, 0x03, 0x0d, 0xca, 0x05, + 0x00, 0x00, 0x00, 0xfe, 0x06, 0x00, 0x01, 0x00, + 0xc8, 0x06, 0x00, 0x02, 0x00, 0xdc, 0x05, 0x00, + 0x03, 0x00, 0x88, 0x06, 0x00, 0x04, 0x00, 0xf2, + 0x05, 0x00, 0x05, 0x00, 0x80, 0x07, 0x00, 0x06, + 0x00, 0x82, 0x07, 0x00, 0x07, 0x00, 0x84, 0x07, + 0x00, 0x08, 0x00, 0x86, 0x07, 0x00, 0x09, 0x00, + 0x88, 0x07, 0x00, 0x0a, 0x00, 0x8a, 0x07, 0x00, + 0x0b, 0x00, 0x8c, 0x07, 0x00, 0x0c, 0x00, 0x86, + 0x05, 0x68, 0x01, 0x84, 0x04, 0x27, 0x01, 0x86, + 0x04, 0x28, 0x01, 0xba, 0x04, 0x42, 0x01, 0x8e, + 0x04, 0x2c, 0x01, 0x88, 0x05, 0x69, 0x01, 0xb6, + 0x02, 0x0b, 0x01, 0x9a, 0x04, 0x32, 0x01, 0xc8, + 0x03, 0x00, 0x0c, 0x94, 0x05, 0x6f, 0x01, 0xdd, + 0xde, 0xdf, 0xf0, 0xce, 0x41, 0x65, 0x01, 0x00, + 0x00, 0xcd, 0xe9, 0xb4, 0xae, 0xea, 0x02, 0x29, + 0xc5, 0xb4, 0x47, 0xcf, 0xe9, 0xc2, 0x05, 0xb5, + 0xcc, 0xc8, 0xc5, 0xe9, 0xa6, 0xea, 0x2a, 0xc5, + 0xc8, 0x47, 0xc2, 0x06, 0xb4, 0xc2, 0x04, 0xc1, + 0x04, 0xc1, 0x05, 0xa6, 0xea, 0x17, 0xc1, 0x06, + 0xc1, 0x04, 0x47, 0xc7, 0xc1, 0x04, 0x47, 0xaf, + 0xea, 0x07, 0xc1, 0x04, 0xc2, 0x05, 0xec, 0x05, + 0x93, 0x04, 0xec, 0xe4, 0x93, 0x03, 0xec, 0xd2, + 0xc6, 0x41, 0x98, 0x01, 0x00, 0x00, 0xcc, 0xc8, + 0xc1, 0x05, 0xa6, 0xea, 0x0b, 0xe0, 0xc7, 0xc8, + 0x47, 0xef, 0x0e, 0x93, 0x03, 0xec, 0xf1, 0x5e, + 0x04, 0x00, 0x5e, 0x05, 0x00, 0xae, 0xea, 0x42, + 0xc5, 0xe9, 0xb5, 0xac, 0xea, 0x3c, 0xc6, 0x41, + 0xbe, 0x01, 0x00, 0x00, 0xc5, 0xb4, 0x47, 0x47, + 0xc3, 0x0c, 0xf5, 0xea, 0x1a, 0xe0, 0x04, 0xc7, + 0x01, 0x00, 0x00, 0xef, 0x0e, 0xc1, 0x0c, 0xe9, + 0xb4, 0xac, 0xea, 0x1e, 0xe0, 0x04, 0xc8, 0x01, + 0x00, 0x00, 0xef, 0x0e, 0xec, 0x14, 0xc1, 0x0c, + 0x97, 0x04, 0x4a, 0x00, 0x00, 0x00, 0xac, 0xea, + 0x09, 0xe0, 0x04, 0xac, 0x01, 0x00, 0x00, 0xef, + 0x0e, 0x5e, 0x04, 0x00, 0x5e, 0x05, 0x00, 0xae, + 0x69, 0xdc, 0x00, 0x00, 0x00, 0xc5, 0xe9, 0xb6, + 0xa9, 0x69, 0xd3, 0x00, 0x00, 0x00, 0xb4, 0xc2, + 0x07, 0xb4, 0xcc, 0xc8, 0xc5, 0xe9, 0xa6, 0xea, + 0x18, 0x5e, 0x06, 0x00, 0x42, 0xc9, 0x01, 0x00, + 0x00, 0xc1, 0x07, 0xc5, 0xc8, 0x47, 0xe9, 0x24, + 0x02, 0x00, 0xc2, 0x07, 0x93, 0x03, 0xec, 0xe4, + 0xb6, 0x94, 0x07, 0x5e, 0x06, 0x00, 0x42, 0xc9, + 0x01, 0x00, 0x00, 0xb5, 0x5e, 0x06, 0x00, 0x42, + 0xca, 0x01, 0x00, 0x00, 0x5e, 0x07, 0x00, 0xb5, + 0x9d, 0xc1, 0x07, 0x9b, 0x24, 0x01, 0x00, 0x24, + 0x02, 0x00, 0xc2, 0x09, 0x5e, 0x06, 0x00, 0x42, + 0xcb, 0x01, 0x00, 0x00, 0xc5, 0xe9, 0xc1, 0x09, + 0x9b, 0x24, 0x01, 0x00, 0xc2, 0x0b, 0x65, 0x08, + 0x00, 0x42, 0x86, 0x01, 0x00, 0x00, 0x04, 0x8d, + 0x01, 0x00, 0x00, 0x24, 0x01, 0x00, 0x0e, 0xb4, + 0xc2, 0x0a, 0xc1, 0x0a, 0xc1, 0x0b, 0xa6, 0xea, + 0x58, 0xb4, 0xc2, 0x08, 0xc1, 0x08, 0xc1, 0x09, + 0xa6, 0xea, 0x39, 0xc1, 0x08, 0xc1, 0x0b, 0x9a, + 0xc1, 0x0a, 0x9d, 0xd0, 0xc5, 0xe9, 0xa9, 0xeb, + 0x2b, 0xc5, 0xc8, 0x47, 0xcb, 0xc1, 0x08, 0xc1, + 0x09, 0xb5, 0x9e, 0xad, 0xea, 0x0d, 0xc7, 0x42, + 0xcc, 0x01, 0x00, 0x00, 0xc1, 0x07, 0x24, 0x01, + 0x00, 0xcb, 0x65, 0x08, 0x00, 0x42, 0x86, 0x01, + 0x00, 0x00, 0xc7, 0x24, 0x01, 0x00, 0x0e, 0x93, + 0x08, 0xec, 0xc2, 0x65, 0x08, 0x00, 0x42, 0x86, + 0x01, 0x00, 0x00, 0x04, 0x8d, 0x01, 0x00, 0x00, + 0x24, 0x01, 0x00, 0x0e, 0x93, 0x0a, 0xec, 0xa3, + 0x5e, 0x09, 0x00, 0xee, 0x0e, 0x29, 0x0c, 0x43, + 0x02, 0x01, 0x8c, 0x05, 0x02, 0x01, 0x02, 0x02, + 0x00, 0x00, 0x10, 0x03, 0xf0, 0x05, 0x00, 0x01, + 0x00, 0x9a, 0x07, 0x00, 0x01, 0x00, 0xfe, 0x06, + 0x00, 0x00, 0x00, 0xc0, 0xc9, 0xd2, 0x90, 0xd6, + 0xb4, 0xa8, 0xea, 0x06, 0xd1, 0x94, 0x00, 0xec, + 0xf5, 0xc5, 0x28, 0x0c, 0x43, 0x02, 0x01, 0x94, + 0x05, 0x00, 0x00, 0x00, 0x03, 0x07, 0x00, 0x1e, + 0x00, 0xc8, 0x03, 0x00, 0x0c, 0xf4, 0x03, 0x1f, + 0x01, 0x9c, 0x04, 0x33, 0x01, 0xac, 0x04, 0x3b, + 0x01, 0x9a, 0x04, 0x32, 0x01, 0x88, 0x04, 0x29, + 0x01, 0x8a, 0x04, 0x2a, 0x01, 0x65, 0x00, 0x00, + 0x42, 0x86, 0x01, 0x00, 0x00, 0xde, 0x24, 0x01, + 0x00, 0x0e, 0xe0, 0xde, 0xef, 0x5e, 0x04, 0x00, + 0x9c, 0xe3, 0xc0, 0x5f, 0x05, 0x00, 0xb4, 0x5f, + 0x06, 0x00, 0x29, 0x0c, 0x43, 0x02, 0x01, 0x96, + 0x05, 0x02, 0x01, 0x02, 0x05, 0x11, 0x00, 0x80, + 0x01, 0x03, 0x9c, 0x07, 0x00, 0x01, 0x00, 0x9e, + 0x07, 0x00, 0x01, 0x00, 0x80, 0x07, 0x00, 0x00, + 0x00, 0x84, 0x04, 0x27, 0x01, 0x86, 0x04, 0x28, + 0x01, 0xee, 0x03, 0x1c, 0x01, 0xec, 0x03, 0x1b, + 0x01, 0x92, 0x05, 0x6e, 0x01, 0xf4, 0x03, 0x1f, + 0x01, 0xf2, 0x03, 0x1e, 0x01, 0x80, 0x04, 0x25, + 0x01, 0x8c, 0x05, 0x6b, 0x01, 0xf6, 0x03, 0x20, + 0x01, 0xfa, 0x03, 0x22, 0x01, 0xe0, 0x03, 0x15, + 0x01, 0xfe, 0x03, 0x24, 0x01, 0xf8, 0x03, 0x21, + 0x01, 0x94, 0x05, 0x6f, 0x01, 0xb8, 0x04, 0x41, + 0x01, 0x90, 0x05, 0x6d, 0x01, 0xd1, 0x11, 0xeb, + 0x03, 0x0e, 0xc0, 0xe5, 0xe9, 0xe2, 0xe0, 0xe9, + 0xe3, 0xd2, 0x5f, 0x04, 0x00, 0x5e, 0x06, 0x00, + 0x5f, 0x05, 0x00, 0x5e, 0x07, 0x00, 0xea, 0x22, + 0x5e, 0x05, 0x00, 0x5e, 0x08, 0x00, 0x04, 0xd0, + 0x01, 0x00, 0x00, 0x5e, 0x09, 0x00, 0x5e, 0x05, + 0x00, 0xe9, 0x9e, 0xf0, 0x9d, 0x60, 0x05, 0x00, + 0x5e, 0x0a, 0x00, 0x9d, 0x5f, 0x05, 0x00, 0xec, + 0x36, 0x5e, 0x0b, 0x00, 0xea, 0x20, 0x5e, 0x0c, + 0x00, 0xbd, 0xe8, 0x03, 0x9b, 0xc9, 0x5e, 0x05, + 0x00, 0xc5, 0x42, 0xd1, 0x01, 0x00, 0x00, 0xba, + 0x24, 0x01, 0x00, 0x04, 0xd0, 0x01, 0x00, 0x00, + 0x9d, 0x9d, 0x5f, 0x05, 0x00, 0x5e, 0x05, 0x00, + 0xe9, 0x5f, 0x09, 0x00, 0x5e, 0x05, 0x00, 0x5e, + 0x0d, 0x00, 0x9d, 0x5f, 0x05, 0x00, 0x5e, 0x0e, + 0x00, 0xee, 0x0e, 0x5e, 0x0f, 0x00, 0xee, 0x0e, + 0xb4, 0x5f, 0x10, 0x00, 0x29, 0x0c, 0x43, 0x02, + 0x01, 0x98, 0x05, 0x01, 0x01, 0x01, 0x03, 0x04, + 0x02, 0x86, 0x01, 0x02, 0xa4, 0x07, 0x00, 0x01, + 0x00, 0xe2, 0x05, 0x00, 0x00, 0x00, 0xae, 0x02, + 0x01, 0x01, 0x90, 0x05, 0x6d, 0x01, 0x8e, 0x05, + 0x6c, 0x01, 0x9a, 0x05, 0x72, 0x01, 0x07, 0x02, + 0x30, 0x07, 0x02, 0x39, 0xdd, 0x42, 0xd3, 0x01, + 0x00, 0x00, 0xd1, 0x24, 0x01, 0x00, 0xc9, 0xde, + 0x11, 0xb4, 0xae, 0xea, 0x14, 0xc5, 0x04, 0xd4, + 0x01, 0x00, 0x00, 0xac, 0xea, 0x06, 0xc5, 0xe3, + 0xb5, 0xe2, 0x29, 0xe0, 0xc5, 0xef, 0x0e, 0x29, + 0x11, 0xb5, 0xae, 0xea, 0x24, 0xdf, 0xc5, 0x9d, + 0xe3, 0xc5, 0x04, 0xd5, 0x01, 0x00, 0x00, 0xac, + 0xea, 0x04, 0xb6, 0xe2, 0x29, 0xc5, 0x04, 0xd6, + 0x01, 0x00, 0x00, 0xac, 0xea, 0x04, 0xb7, 0xe2, + 0x29, 0xe0, 0xdf, 0xef, 0x0e, 0xb4, 0xe2, 0x29, + 0x11, 0xb6, 0xae, 0xea, 0x26, 0xdf, 0xc5, 0x9d, + 0xe3, 0xc5, 0x04, 0xd7, 0x01, 0x00, 0x00, 0xac, + 0x11, 0xeb, 0x0e, 0x0e, 0xc5, 0xbe, 0x00, 0xa9, + 0x11, 0xea, 0x06, 0x0e, 0xc5, 0xbe, 0x01, 0xa7, + 0x96, 0xea, 0x17, 0xe0, 0xdf, 0xef, 0x0e, 0xb4, + 0xe2, 0x29, 0x11, 0xb7, 0xae, 0xea, 0x0b, 0xdf, + 0xc5, 0x9d, 0xe3, 0xe0, 0xdf, 0xef, 0x0e, 0xb4, + 0xe2, 0x29, 0x0c, 0x43, 0x02, 0x01, 0x9a, 0x05, + 0x01, 0x01, 0x01, 0x05, 0x0e, 0x00, 0xb6, 0x01, + 0x02, 0x60, 0x00, 0x01, 0x00, 0xb0, 0x07, 0x00, + 0x00, 0x00, 0x90, 0x04, 0x2d, 0x01, 0xac, 0x04, + 0x3b, 0x01, 0xba, 0x04, 0x42, 0x01, 0x8a, 0x05, + 0x6a, 0x01, 0x8c, 0x04, 0x2b, 0x01, 0x92, 0x05, + 0x6e, 0x01, 0x84, 0x04, 0x27, 0x01, 0xca, 0x03, + 0x01, 0x0c, 0x96, 0x04, 0x30, 0x01, 0xc4, 0x05, + 0x87, 0x01, 0x01, 0x8e, 0x04, 0x2c, 0x01, 0xc0, + 0x04, 0x45, 0x01, 0x86, 0x04, 0x28, 0x01, 0xb8, + 0x04, 0x41, 0x01, 0xdd, 0xea, 0x11, 0xde, 0xd1, + 0xef, 0xb5, 0xae, 0xea, 0x05, 0xdf, 0xd1, 0xef, + 0x0e, 0x09, 0xe1, 0xed, 0x80, 0x00, 0xe0, 0xd1, + 0x47, 0xcd, 0xea, 0x5a, 0xc5, 0x5f, 0x04, 0x00, + 0xc5, 0xd1, 0xef, 0x11, 0xb3, 0xae, 0xea, 0x09, + 0x5e, 0x05, 0x00, 0x5e, 0x06, 0x00, 0xef, 0x29, + 0x11, 0xbc, 0xfe, 0xae, 0xea, 0x07, 0x5e, 0x05, + 0x00, 0x07, 0xef, 0x29, 0x11, 0xbc, 0xfd, 0xae, + 0xea, 0x2b, 0x65, 0x07, 0x00, 0x42, 0x6a, 0x01, + 0x00, 0x00, 0x65, 0x07, 0x00, 0x41, 0x6b, 0x01, + 0x00, 0x00, 0x07, 0x24, 0x02, 0x00, 0x0e, 0x65, + 0x07, 0x00, 0x42, 0x6c, 0x01, 0x00, 0x00, 0x5e, + 0x08, 0x00, 0x07, 0x24, 0x02, 0x00, 0x0e, 0x5e, + 0x09, 0x00, 0xee, 0x29, 0x0e, 0x5e, 0x04, 0x00, + 0x5f, 0x0a, 0x00, 0xec, 0x20, 0xde, 0xd1, 0xef, + 0xb5, 0xae, 0xea, 0x14, 0xd1, 0x04, 0xd0, 0x01, + 0x00, 0x00, 0xa9, 0xea, 0x0b, 0xdf, 0xd1, 0xef, + 0x0e, 0xdf, 0x5f, 0x0a, 0x00, 0xec, 0x06, 0x5e, + 0x0b, 0x00, 0xee, 0x0e, 0x5e, 0x0c, 0x00, 0xb4, + 0xa6, 0xea, 0x04, 0xb4, 0xec, 0x14, 0x5e, 0x0c, + 0x00, 0x5e, 0x06, 0x00, 0xe9, 0xa8, 0xea, 0x07, + 0x5e, 0x06, 0x00, 0xe9, 0xec, 0x04, 0x5e, 0x0c, + 0x00, 0x5f, 0x0c, 0x00, 0x5e, 0x0d, 0x00, 0xee, + 0x29, 0x0c, 0x43, 0x02, 0x01, 0x9c, 0x05, 0x02, + 0x01, 0x02, 0x05, 0x02, 0x01, 0x70, 0x03, 0xe8, + 0x05, 0x00, 0x01, 0x00, 0xb2, 0x07, 0x00, 0x01, + 0x00, 0xc8, 0x06, 0x00, 0x00, 0x00, 0xd0, 0x03, + 0x0d, 0x01, 0xb6, 0x02, 0x0b, 0x01, 0x07, 0x02, + 0x30, 0xdd, 0xd1, 0xef, 0x96, 0xea, 0x0a, 0xd1, + 0x42, 0x38, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, + 0xd1, 0xb4, 0xac, 0xea, 0x15, 0xb5, 0xd1, 0x9b, + 0xb4, 0xa6, 0xea, 0x09, 0x04, 0xda, 0x01, 0x00, + 0x00, 0xc9, 0xec, 0x4c, 0xbe, 0x00, 0xc9, 0xec, + 0x47, 0xd2, 0xbc, 0x10, 0xac, 0xea, 0x37, 0xd1, + 0xde, 0x42, 0xca, 0x01, 0x00, 0x00, 0xd1, 0x24, + 0x01, 0x00, 0xae, 0xea, 0x29, 0xd1, 0xb4, 0xa6, + 0xea, 0x0c, 0xd1, 0x8c, 0xd5, 0x04, 0xdb, 0x01, + 0x00, 0x00, 0xc9, 0xec, 0x03, 0xc0, 0xc9, 0xc5, + 0x04, 0xdc, 0x01, 0x00, 0x00, 0xd1, 0x42, 0x38, + 0x00, 0x00, 0x00, 0xbc, 0x10, 0x24, 0x01, 0x00, + 0x9d, 0x9d, 0xc9, 0xec, 0x0b, 0xd1, 0x42, 0x38, + 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0xc9, 0xc5, + 0x28, 0x0c, 0x43, 0x02, 0x01, 0x9e, 0x05, 0x02, + 0x01, 0x02, 0x05, 0x00, 0x00, 0x40, 0x03, 0xe8, + 0x05, 0x00, 0x01, 0x00, 0xb2, 0x07, 0x00, 0x01, + 0x00, 0xc8, 0x06, 0x00, 0x00, 0x00, 0xd2, 0xbc, + 0x10, 0xac, 0xea, 0x29, 0xd1, 0xb4, 0xa6, 0xea, + 0x0c, 0xd1, 0x8c, 0xd5, 0x04, 0xdb, 0x01, 0x00, + 0x00, 0xc9, 0xec, 0x03, 0xc0, 0xc9, 0xc5, 0x04, + 0xdc, 0x01, 0x00, 0x00, 0xd1, 0x42, 0x38, 0x00, + 0x00, 0x00, 0xbc, 0x10, 0x24, 0x01, 0x00, 0x9d, + 0x9d, 0xc9, 0xec, 0x0b, 0xd1, 0x42, 0x38, 0x00, + 0x00, 0x00, 0x24, 0x00, 0x00, 0xc9, 0xc5, 0x04, + 0x89, 0x01, 0x00, 0x00, 0x9d, 0x28, 0x0c, 0x43, + 0x02, 0x01, 0x00, 0x04, 0x18, 0x04, 0x06, 0x12, + 0x0e, 0xea, 0x01, 0x1c, 0xba, 0x07, 0x00, 0x01, + 0x00, 0xe4, 0x03, 0x00, 0x01, 0x40, 0xbc, 0x07, + 0x00, 0x01, 0x40, 0xbe, 0x07, 0x00, 0x01, 0x40, + 0xc0, 0x07, 0x00, 0x00, 0x00, 0x86, 0x01, 0x00, + 0x01, 0x00, 0xc2, 0x07, 0x00, 0x02, 0x40, 0xc4, + 0x07, 0x00, 0x03, 0x40, 0xc6, 0x07, 0x00, 0x04, + 0x40, 0xc8, 0x07, 0x00, 0x05, 0x40, 0xca, 0x07, + 0x00, 0x06, 0x40, 0x6c, 0x00, 0x07, 0x40, 0xcc, + 0x07, 0x00, 0x08, 0x40, 0xce, 0x07, 0x00, 0x09, + 0x40, 0xd0, 0x07, 0x00, 0x0a, 0x40, 0xd2, 0x07, + 0x00, 0x0b, 0x40, 0xd4, 0x07, 0x00, 0x0c, 0x40, + 0xd6, 0x07, 0x00, 0x0d, 0x40, 0xd8, 0x07, 0x00, + 0x0e, 0x40, 0xda, 0x07, 0x00, 0x0f, 0x40, 0xdc, + 0x07, 0x00, 0x10, 0x40, 0xde, 0x07, 0x00, 0x11, + 0x40, 0xe0, 0x07, 0x00, 0x12, 0x40, 0xe2, 0x07, + 0x00, 0x13, 0x40, 0xe4, 0x07, 0x00, 0x14, 0x40, + 0xe6, 0x07, 0x00, 0x15, 0x40, 0xe8, 0x07, 0x00, + 0x16, 0x40, 0xea, 0x07, 0x00, 0x17, 0x40, 0xd4, + 0x03, 0x0f, 0x01, 0xb8, 0x02, 0x0c, 0x01, 0xa6, + 0x02, 0x00, 0x01, 0x9c, 0x05, 0x73, 0x01, 0xe8, + 0x03, 0x19, 0x01, 0x9e, 0x05, 0x74, 0x01, 0xae, + 0x02, 0x01, 0x01, 0xba, 0x02, 0x07, 0x01, 0xc2, + 0x02, 0x08, 0x01, 0xb0, 0x02, 0x03, 0x01, 0xac, + 0x02, 0x02, 0x01, 0xe2, 0x02, 0x04, 0x01, 0xa8, + 0x02, 0x06, 0x01, 0xcc, 0x02, 0x05, 0x01, 0xd8, + 0x03, 0x11, 0x01, 0xdc, 0x03, 0x13, 0x01, 0xb6, + 0x02, 0x0b, 0x01, 0x9a, 0x04, 0x32, 0x01, 0x0c, + 0x43, 0x02, 0x01, 0x86, 0x01, 0x02, 0x00, 0x02, + 0x01, 0x01, 0x00, 0x0e, 0x02, 0xec, 0x07, 0x00, + 0x01, 0x00, 0xee, 0x07, 0x00, 0x01, 0x00, 0xd4, + 0x03, 0x00, 0x00, 0xd1, 0xf4, 0xea, 0x03, 0xd2, + 0x28, 0xd1, 0xf3, 0xea, 0x03, 0xdd, 0x28, 0xd1, + 0x28, 0x0c, 0x43, 0x02, 0x01, 0xd2, 0x07, 0x01, + 0x00, 0x01, 0x04, 0x01, 0x00, 0x54, 0x01, 0xc8, + 0x06, 0x00, 0x01, 0x00, 0xb8, 0x02, 0x01, 0x00, + 0xd1, 0x42, 0xf8, 0x01, 0x00, 0x00, 0x04, 0xaf, + 0x01, 0x00, 0x00, 0x24, 0x01, 0x00, 0xea, 0x0b, + 0xdd, 0x42, 0xf9, 0x01, 0x00, 0x00, 0xd1, 0x25, + 0x01, 0x00, 0xdd, 0x42, 0xf9, 0x01, 0x00, 0x00, + 0xd1, 0x24, 0x01, 0x00, 0x42, 0xa7, 0x01, 0x00, + 0x00, 0xb5, 0xb3, 0x24, 0x02, 0x00, 0x42, 0xfa, + 0x01, 0x00, 0x00, 0x04, 0xfb, 0x01, 0x00, 0x00, + 0x04, 0xb0, 0x01, 0x00, 0x00, 0x24, 0x02, 0x00, + 0xd5, 0x04, 0xaf, 0x01, 0x00, 0x00, 0x42, 0x5d, + 0x00, 0x00, 0x00, 0xd1, 0x04, 0xaf, 0x01, 0x00, + 0x00, 0x25, 0x02, 0x00, 0x0c, 0x43, 0x02, 0x01, + 0xd4, 0x07, 0x01, 0x00, 0x01, 0x04, 0x01, 0x00, + 0x0d, 0x01, 0xc8, 0x06, 0x00, 0x01, 0x00, 0xcc, + 0x07, 0x08, 0x01, 0xdd, 0x42, 0x9b, 0x01, 0x00, + 0x00, 0xc0, 0xd1, 0x9d, 0x24, 0x01, 0x00, 0x29, + 0x0c, 0x43, 0x02, 0x01, 0xd6, 0x07, 0x01, 0x00, + 0x01, 0x04, 0x01, 0x00, 0x0c, 0x01, 0xc8, 0x06, + 0x00, 0x01, 0x00, 0xcc, 0x07, 0x08, 0x01, 0xdd, + 0xdd, 0xe9, 0xb5, 0x9e, 0x72, 0x13, 0x47, 0xd1, + 0x9d, 0x49, 0x29, 0x0c, 0x43, 0x02, 0x01, 0xd8, + 0x07, 0x01, 0x00, 0x01, 0x04, 0x01, 0x00, 0x1f, + 0x01, 0xf8, 0x07, 0x00, 0x01, 0x00, 0xa6, 0x02, + 0x02, 0x00, 0xdd, 0x41, 0x3c, 0x00, 0x00, 0x00, + 0x41, 0x38, 0x00, 0x00, 0x00, 0x42, 0xfd, 0x01, + 0x00, 0x00, 0xd1, 0x24, 0x01, 0x00, 0x42, 0xa7, + 0x01, 0x00, 0x00, 0xbc, 0x08, 0xb3, 0x25, 0x02, + 0x00, 0x0c, 0x43, 0x02, 0x01, 0xda, 0x07, 0x02, + 0x13, 0x02, 0x09, 0x1a, 0x02, 0xdc, 0x0b, 0x15, + 0xe8, 0x05, 0x00, 0x01, 0x00, 0x82, 0x04, 0x00, + 0x01, 0x00, 0x92, 0x06, 0x00, 0x00, 0x00, 0xfc, + 0x07, 0x00, 0x01, 0x00, 0xdc, 0x05, 0x00, 0x02, + 0x00, 0xfe, 0x07, 0x00, 0x03, 0x00, 0x60, 0x00, + 0x04, 0x00, 0x80, 0x08, 0x00, 0x05, 0x00, 0x82, + 0x08, 0x00, 0x06, 0x00, 0x84, 0x08, 0x00, 0x07, + 0x00, 0x86, 0x08, 0x00, 0x08, 0x00, 0x88, 0x08, + 0x00, 0x09, 0x00, 0x8a, 0x08, 0x00, 0x0a, 0x00, + 0x8c, 0x08, 0x00, 0x0b, 0x00, 0x8e, 0x08, 0x00, + 0x0c, 0x00, 0x90, 0x08, 0x00, 0x0d, 0x00, 0x92, + 0x08, 0x00, 0x0e, 0x00, 0xf2, 0x05, 0x00, 0x0f, + 0x00, 0x84, 0x06, 0x00, 0x10, 0x00, 0x94, 0x08, + 0x00, 0x11, 0x00, 0x96, 0x08, 0x00, 0x12, 0x00, + 0xd4, 0x07, 0x0c, 0x01, 0x9c, 0x05, 0x03, 0x00, + 0xe8, 0x03, 0x04, 0x00, 0x9e, 0x05, 0x05, 0x00, + 0xc8, 0x07, 0x05, 0x01, 0xd2, 0x07, 0x0b, 0x01, + 0xae, 0x02, 0x06, 0x00, 0xca, 0x07, 0x06, 0x01, + 0x6c, 0x07, 0x01, 0xcc, 0x07, 0x08, 0x01, 0xd8, + 0x07, 0x0e, 0x01, 0xba, 0x02, 0x07, 0x00, 0xb8, + 0x02, 0x01, 0x00, 0xc2, 0x02, 0x08, 0x00, 0xb0, + 0x02, 0x09, 0x00, 0xac, 0x02, 0x0a, 0x00, 0xe2, + 0x02, 0x0b, 0x00, 0xa8, 0x02, 0x0c, 0x00, 0xcc, + 0x02, 0x0d, 0x00, 0xbc, 0x07, 0x02, 0x03, 0xda, + 0x07, 0x0f, 0x01, 0xc4, 0x07, 0x03, 0x01, 0xe4, + 0x03, 0x01, 0x03, 0xa6, 0x02, 0x02, 0x00, 0xd6, + 0x07, 0x0d, 0x01, 0xc6, 0x07, 0x04, 0x01, 0x07, + 0x32, 0x5e, 0x5b, 0x61, 0x2d, 0x7a, 0x41, 0x2d, + 0x5a, 0x5f, 0x24, 0x5d, 0x5b, 0x30, 0x2d, 0x39, + 0x61, 0x2d, 0x7a, 0x41, 0x2d, 0x5a, 0x5f, 0x24, + 0x5d, 0x2a, 0x07, 0xaa, 0x01, 0x00, 0x00, 0x01, + 0x00, 0x4d, 0x00, 0x00, 0x00, 0x09, 0x06, 0x00, + 0x00, 0x00, 0x05, 0x08, 0xf5, 0xff, 0xff, 0xff, + 0x0c, 0x00, 0x06, 0x16, 0x04, 0x00, 0x24, 0x00, + 0x24, 0x00, 0x41, 0x00, 0x5a, 0x00, 0x5f, 0x00, + 0x5f, 0x00, 0x61, 0x00, 0x7a, 0x00, 0x1d, 0x18, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, + 0xff, 0xff, 0x7f, 0x01, 0x00, 0x00, 0x00, 0x16, + 0x05, 0x00, 0x24, 0x00, 0x24, 0x00, 0x30, 0x00, + 0x39, 0x00, 0x41, 0x00, 0x5a, 0x00, 0x5f, 0x00, + 0x5f, 0x00, 0x61, 0x00, 0x7a, 0x00, 0x0b, 0x0d, + 0x00, 0x0b, 0xd1, 0x97, 0xc3, 0x06, 0x11, 0x04, + 0x46, 0x00, 0x00, 0x00, 0xae, 0xeb, 0x0a, 0x11, + 0x04, 0x48, 0x00, 0x00, 0x00, 0xae, 0xea, 0x06, + 0xdd, 0xd1, 0xef, 0x0e, 0x29, 0x11, 0x04, 0x47, + 0x00, 0x00, 0x00, 0xae, 0xea, 0x11, 0xdd, 0xde, + 0xd1, 0xdf, 0xea, 0x05, 0xbc, 0x10, 0xec, 0x03, + 0xbc, 0x0a, 0xf0, 0xef, 0x0e, 0x29, 0x11, 0x04, + 0x8d, 0x00, 0x00, 0x00, 0xae, 0xea, 0x11, 0xdd, + 0xe0, 0xd1, 0xdf, 0xea, 0x05, 0xbc, 0x10, 0xec, + 0x03, 0xbc, 0x0a, 0xf0, 0xef, 0x0e, 0x29, 0x11, + 0x04, 0x49, 0x00, 0x00, 0x00, 0xae, 0xea, 0x26, + 0xd1, 0xe9, 0x5e, 0x04, 0x00, 0xa8, 0xea, 0x15, + 0xd1, 0x42, 0x88, 0x01, 0x00, 0x00, 0xb4, 0x5e, + 0x04, 0x00, 0x24, 0x02, 0x00, 0x04, 0x0c, 0x02, + 0x00, 0x00, 0x9d, 0xd5, 0xdd, 0x5e, 0x05, 0x00, + 0xd1, 0xef, 0xef, 0x0e, 0x29, 0x11, 0x04, 0x4b, + 0x00, 0x00, 0x00, 0xae, 0xea, 0x0a, 0xdd, 0x5e, + 0x06, 0x00, 0xd1, 0xef, 0xef, 0x0e, 0x29, 0x11, + 0x04, 0x4a, 0x00, 0x00, 0x00, 0xae, 0xeb, 0x0d, + 0x11, 0x04, 0x1b, 0x00, 0x00, 0x00, 0xae, 0x69, + 0x35, 0x05, 0x00, 0x00, 0xd1, 0xf3, 0xea, 0x06, + 0xdd, 0xd1, 0xef, 0x0e, 0x29, 0x5e, 0x07, 0x00, + 0x42, 0xae, 0x01, 0x00, 0x00, 0xd1, 0x24, 0x01, + 0x00, 0xcd, 0xb4, 0xa9, 0xea, 0x18, 0xdd, 0x04, + 0x0d, 0x02, 0x00, 0x00, 0x42, 0x5d, 0x00, 0x00, + 0x00, 0xc5, 0x04, 0xb2, 0x01, 0x00, 0x00, 0x24, + 0x02, 0x00, 0xef, 0x0e, 0x29, 0x5e, 0x08, 0x00, + 0x42, 0xae, 0x01, 0x00, 0x00, 0xd1, 0x24, 0x01, + 0x00, 0xcd, 0xb4, 0xa9, 0xea, 0x2c, 0xdd, 0x04, + 0x0d, 0x02, 0x00, 0x00, 0x42, 0x5d, 0x00, 0x00, + 0x00, 0x5e, 0x07, 0x00, 0xe9, 0x04, 0xb2, 0x01, + 0x00, 0x00, 0x24, 0x02, 0x00, 0xef, 0x0e, 0x5e, + 0x07, 0x00, 0x42, 0x9b, 0x01, 0x00, 0x00, 0x5e, + 0x08, 0x00, 0xc5, 0x47, 0x24, 0x01, 0x00, 0x0e, + 0x29, 0x5e, 0x09, 0x00, 0xe9, 0xc2, 0x0c, 0x5e, + 0x0a, 0x00, 0xd1, 0xef, 0xc2, 0x0d, 0x5e, 0x08, + 0x00, 0x42, 0x9b, 0x01, 0x00, 0x00, 0xd1, 0x24, + 0x01, 0x00, 0x0e, 0xd1, 0x5e, 0x0b, 0x00, 0xaa, + 0xea, 0x28, 0xdd, 0x04, 0x0e, 0x02, 0x00, 0x00, + 0x42, 0x5d, 0x00, 0x00, 0x00, 0x5e, 0x0c, 0x00, + 0x42, 0xf9, 0x01, 0x00, 0x00, 0xd1, 0x42, 0x0f, + 0x02, 0x00, 0x00, 0x24, 0x00, 0x00, 0x24, 0x01, + 0x00, 0x24, 0x01, 0x00, 0xef, 0x0e, 0xed, 0xad, + 0x01, 0xd1, 0x5e, 0x0d, 0x00, 0xaa, 0xea, 0x10, + 0xdd, 0xd1, 0x42, 0x38, 0x00, 0x00, 0x00, 0x24, + 0x00, 0x00, 0xef, 0x0e, 0xed, 0x97, 0x01, 0xd1, + 0x5e, 0x0e, 0x00, 0xaa, 0x11, 0xeb, 0x10, 0x0e, + 0xd1, 0x5e, 0x0f, 0x00, 0xaa, 0x11, 0xeb, 0x07, + 0x0e, 0xd1, 0x5e, 0x10, 0x00, 0xaa, 0xea, 0x21, + 0xdd, 0x04, 0xd5, 0x01, 0x00, 0x00, 0x42, 0x5d, + 0x00, 0x00, 0x00, 0xc1, 0x0d, 0x04, 0x10, 0x02, + 0x00, 0x00, 0xd1, 0x04, 0xb2, 0x01, 0x00, 0x00, + 0x24, 0x04, 0x00, 0xef, 0x0e, 0xed, 0x5e, 0x01, + 0xd1, 0x5e, 0x06, 0x00, 0xaa, 0xea, 0x2c, 0xdd, + 0x04, 0xd5, 0x01, 0x00, 0x00, 0x42, 0x5d, 0x00, + 0x00, 0x00, 0xc1, 0x0d, 0x04, 0x10, 0x02, 0x00, + 0x00, 0x5e, 0x05, 0x00, 0xd1, 0xef, 0x04, 0xb2, + 0x01, 0x00, 0x00, 0x24, 0x04, 0x00, 0xef, 0x0e, + 0xd1, 0xe9, 0xc2, 0x0f, 0xb5, 0xc2, 0x08, 0xed, + 0x2c, 0x01, 0x5e, 0x11, 0x00, 0x42, 0x11, 0x02, + 0x00, 0x00, 0xd1, 0x24, 0x01, 0x00, 0xea, 0x0f, + 0xdd, 0x04, 0xd5, 0x01, 0x00, 0x00, 0xef, 0x0e, + 0xb5, 0xc2, 0x07, 0xed, 0x10, 0x01, 0xc1, 0x0d, + 0x42, 0xf8, 0x01, 0x00, 0x00, 0x04, 0x94, 0x00, + 0x00, 0x00, 0x24, 0x01, 0x00, 0xea, 0x2d, 0xd1, + 0x5e, 0x12, 0x00, 0x41, 0x45, 0x00, 0x00, 0x00, + 0xaa, 0xea, 0x21, 0xdd, 0xc0, 0x42, 0x5d, 0x00, + 0x00, 0x00, 0xc1, 0x0d, 0x04, 0xc7, 0x01, 0x00, + 0x00, 0xd1, 0xe9, 0x04, 0x12, 0x02, 0x00, 0x00, + 0x24, 0x04, 0x00, 0xef, 0x0e, 0xb5, 0xc2, 0x07, + 0xed, 0xd3, 0x00, 0xc1, 0x06, 0x04, 0x1b, 0x00, + 0x00, 0x00, 0xae, 0xea, 0x32, 0xd1, 0x41, 0x37, + 0x00, 0x00, 0x00, 0xea, 0x1f, 0xdd, 0x04, 0x13, + 0x02, 0x00, 0x00, 0x42, 0x5d, 0x00, 0x00, 0x00, + 0xd1, 0x41, 0x37, 0x00, 0x00, 0x00, 0x04, 0xb2, + 0x01, 0x00, 0x00, 0x24, 0x02, 0x00, 0xef, 0x0e, + 0xed, 0xa3, 0x00, 0xdd, 0x04, 0x14, 0x02, 0x00, + 0x00, 0xef, 0x0e, 0xed, 0x98, 0x00, 0xd1, 0x41, + 0x3d, 0x00, 0x00, 0x00, 0x11, 0xea, 0x0d, 0x0e, + 0xd1, 0x41, 0x3d, 0x00, 0x00, 0x00, 0x41, 0x37, + 0x00, 0x00, 0x00, 0x11, 0xeb, 0x07, 0x0e, 0x04, + 0x93, 0x00, 0x00, 0x00, 0xc2, 0x0e, 0xc1, 0x0d, + 0x04, 0x93, 0x00, 0x00, 0x00, 0xaf, 0xea, 0x1d, + 0xdd, 0xc0, 0x42, 0x5d, 0x00, 0x00, 0x00, 0xc1, + 0x0e, 0x04, 0x15, 0x02, 0x00, 0x00, 0xc1, 0x0d, + 0x04, 0x16, 0x02, 0x00, 0x00, 0x24, 0x04, 0x00, + 0xef, 0x0e, 0xec, 0x4a, 0xd1, 0x41, 0x45, 0x00, + 0x00, 0x00, 0xf3, 0xea, 0x1a, 0xdd, 0x04, 0xd5, + 0x01, 0x00, 0x00, 0x42, 0x5d, 0x00, 0x00, 0x00, + 0xc1, 0x0e, 0x04, 0x17, 0x02, 0x00, 0x00, 0x24, + 0x02, 0x00, 0xef, 0x0e, 0xec, 0x28, 0xc1, 0x0e, + 0x04, 0x93, 0x00, 0x00, 0x00, 0xaf, 0xea, 0x16, + 0xdd, 0xc0, 0x42, 0x5d, 0x00, 0x00, 0x00, 0xc1, + 0x0e, 0x04, 0x18, 0x02, 0x00, 0x00, 0x24, 0x02, + 0x00, 0xef, 0x0e, 0xec, 0x09, 0xdd, 0x04, 0x19, + 0x02, 0x00, 0x00, 0xef, 0x0e, 0x04, 0x1a, 0x02, + 0x00, 0x00, 0xc2, 0x0a, 0x07, 0xc2, 0x04, 0xb4, + 0xc9, 0xb4, 0xca, 0xb4, 0xcc, 0xc1, 0x07, 0x69, + 0xcb, 0x00, 0x00, 0x00, 0x04, 0xb2, 0x01, 0x00, + 0x00, 0xc2, 0x0a, 0xd1, 0xe9, 0xc2, 0x0f, 0xd2, + 0x5e, 0x13, 0x00, 0xa8, 0xea, 0x12, 0xc1, 0x0f, + 0xea, 0x0e, 0xdd, 0x04, 0x0c, 0x02, 0x00, 0x00, + 0xef, 0x0e, 0xdd, 0xc1, 0x0a, 0xef, 0x29, 0xb4, + 0xcb, 0xc7, 0xc1, 0x0f, 0xa6, 0x69, 0x89, 0x00, + 0x00, 0x00, 0x93, 0x03, 0xc7, 0xd1, 0xab, 0xea, + 0x0e, 0x5e, 0x14, 0x00, 0xd1, 0xc7, 0x47, 0xd2, + 0xb5, 0x9d, 0xf0, 0x0e, 0xec, 0x43, 0xc7, 0xc2, + 0x10, 0xc7, 0xb5, 0x9d, 0xc1, 0x0f, 0xa6, 0xea, + 0x0d, 0xc7, 0xb5, 0x9d, 0xd1, 0xab, 0x96, 0xea, + 0x05, 0x93, 0x02, 0xec, 0xed, 0xc7, 0xc1, 0x10, + 0xa8, 0xea, 0x1e, 0xdd, 0x04, 0x1b, 0x02, 0x00, + 0x00, 0x42, 0x5d, 0x00, 0x00, 0x00, 0xc7, 0xc1, + 0x10, 0x9e, 0xb5, 0x9d, 0x04, 0x1c, 0x02, 0x00, + 0x00, 0x24, 0x02, 0x00, 0xef, 0x0e, 0xec, 0x09, + 0xdd, 0x04, 0x1d, 0x02, 0x00, 0x00, 0xef, 0x0e, + 0xc8, 0x5e, 0x15, 0x00, 0xa9, 0xea, 0x24, 0xc1, + 0x0f, 0xc8, 0x9e, 0xb9, 0xa8, 0xea, 0x1c, 0xdd, + 0x04, 0x1e, 0x02, 0x00, 0x00, 0x42, 0x5d, 0x00, + 0x00, 0x00, 0xc1, 0x0f, 0xc8, 0x9e, 0x04, 0x1f, + 0x02, 0x00, 0x00, 0x24, 0x02, 0x00, 0xef, 0x0e, + 0xec, 0x06, 0x93, 0x02, 0xed, 0x74, 0xff, 0xb5, + 0xc2, 0x08, 0xc7, 0xc1, 0x0f, 0xaf, 0xea, 0x0c, + 0xc1, 0x0f, 0xbd, 0xe8, 0x03, 0xa8, 0xea, 0x04, + 0xb5, 0xc2, 0x09, 0xc1, 0x09, 0x96, 0xea, 0x24, + 0x5e, 0x16, 0x00, 0xea, 0x0f, 0x5e, 0x17, 0x00, + 0x42, 0xbb, 0x01, 0x00, 0x00, 0xd1, 0x24, 0x01, + 0x00, 0xec, 0x0d, 0x5e, 0x17, 0x00, 0x42, 0x30, + 0x00, 0x00, 0x00, 0xd1, 0x24, 0x01, 0x00, 0xc3, + 0x04, 0xe9, 0xc9, 0xc1, 0x08, 0xea, 0x1e, 0xc6, + 0xc5, 0xa6, 0xea, 0x19, 0xc1, 0x04, 0xc6, 0x47, + 0x8d, 0xcf, 0xc7, 0xb4, 0xa1, 0xaf, 0x11, 0xeb, + 0x06, 0x0e, 0xc7, 0xc1, 0x0f, 0xa9, 0xeb, 0x05, + 0x93, 0x01, 0xec, 0xe4, 0xc6, 0xc5, 0xa6, 0x69, + 0x5a, 0x01, 0x00, 0x00, 0xc1, 0x0a, 0x96, 0xea, + 0x12, 0x5e, 0x18, 0x00, 0x04, 0x18, 0x02, 0x00, + 0x00, 0xef, 0x0e, 0x04, 0x1a, 0x02, 0x00, 0x00, + 0xc2, 0x0a, 0xd2, 0x5e, 0x13, 0x00, 0xa8, 0xea, + 0x13, 0xc6, 0xc5, 0xa6, 0xea, 0x0e, 0xdd, 0x04, + 0x0c, 0x02, 0x00, 0x00, 0xef, 0x0e, 0xdd, 0xc1, + 0x0a, 0xef, 0x29, 0xc6, 0xcb, 0xc7, 0xc5, 0xa6, + 0x69, 0x21, 0x01, 0x00, 0x00, 0xc1, 0x04, 0xc7, + 0x47, 0xc2, 0x05, 0x5e, 0x17, 0x00, 0x42, 0x67, + 0x00, 0x00, 0x00, 0xd1, 0xc1, 0x05, 0x24, 0x02, + 0x00, 0xc3, 0x11, 0x96, 0x6a, 0x00, 0x01, 0x00, + 0x00, 0xc1, 0x11, 0x41, 0x40, 0x00, 0x00, 0x00, + 0x96, 0xea, 0x1e, 0xdd, 0x04, 0xd5, 0x01, 0x00, + 0x00, 0x42, 0x5d, 0x00, 0x00, 0x00, 0x5e, 0x06, + 0x00, 0xc1, 0x05, 0xef, 0x04, 0xb2, 0x01, 0x00, + 0x00, 0x24, 0x02, 0x00, 0xef, 0x0e, 0xec, 0x2e, + 0xc1, 0x05, 0x8d, 0xc1, 0x05, 0xb4, 0xa1, 0xae, + 0x11, 0xeb, 0x11, 0x0e, 0xc1, 0x05, 0x42, 0xb4, + 0x01, 0x00, 0x00, 0xbe, 0x00, 0xbe, 0x01, 0x33, + 0x24, 0x01, 0x00, 0xea, 0x08, 0xdd, 0xc1, 0x05, + 0xef, 0x0e, 0xec, 0x0a, 0xdd, 0x5e, 0x05, 0x00, + 0xc1, 0x05, 0xef, 0xef, 0x0e, 0xdd, 0x04, 0x20, + 0x02, 0x00, 0x00, 0xef, 0x0e, 0x04, 0x41, 0x00, + 0x00, 0x00, 0xc1, 0x11, 0xab, 0xea, 0x12, 0x5e, + 0x14, 0x00, 0xc1, 0x11, 0x41, 0x41, 0x00, 0x00, + 0x00, 0xd2, 0xb5, 0x9d, 0xf0, 0x0e, 0xec, 0x5c, + 0x26, 0x00, 0x00, 0xc2, 0x12, 0xc1, 0x11, 0x41, + 0x42, 0x00, 0x00, 0x00, 0xea, 0x11, 0xc1, 0x12, + 0x42, 0x9b, 0x01, 0x00, 0x00, 0x04, 0x21, 0x02, + 0x00, 0x00, 0x24, 0x01, 0x00, 0x0e, 0xc1, 0x11, + 0x41, 0x43, 0x00, 0x00, 0x00, 0xea, 0x11, 0xc1, + 0x12, 0x42, 0x9b, 0x01, 0x00, 0x00, 0x04, 0x22, + 0x02, 0x00, 0x00, 0x24, 0x01, 0x00, 0x0e, 0xdd, + 0x04, 0xd5, 0x01, 0x00, 0x00, 0x42, 0x5d, 0x00, + 0x00, 0x00, 0xc1, 0x12, 0x42, 0x5c, 0x00, 0x00, + 0x00, 0x04, 0xb3, 0x01, 0x00, 0x00, 0x24, 0x01, + 0x00, 0x04, 0xb2, 0x01, 0x00, 0x00, 0x24, 0x02, + 0x00, 0xef, 0x0e, 0x93, 0x03, 0xc8, 0x5e, 0x19, + 0x00, 0xa8, 0xea, 0x22, 0xc5, 0xc8, 0x9e, 0xb9, + 0xa8, 0xea, 0x1b, 0xdd, 0x04, 0x1e, 0x02, 0x00, + 0x00, 0x42, 0x5d, 0x00, 0x00, 0x00, 0xc5, 0xc8, + 0x9e, 0x04, 0x23, 0x02, 0x00, 0x00, 0x24, 0x02, + 0x00, 0xef, 0x0e, 0xec, 0x06, 0x93, 0x02, 0xed, + 0xdd, 0xfe, 0xc1, 0x0a, 0xea, 0x06, 0xdd, 0xc1, + 0x0a, 0xef, 0x0e, 0x5e, 0x08, 0x00, 0x42, 0x24, + 0x02, 0x00, 0x00, 0xd1, 0x24, 0x01, 0x00, 0x0e, + 0x5e, 0x07, 0x00, 0x42, 0xae, 0x01, 0x00, 0x00, + 0xd1, 0x24, 0x01, 0x00, 0xcf, 0xb4, 0xa8, 0xea, + 0x35, 0x5e, 0x09, 0x00, 0xc1, 0x0c, 0x1b, 0x11, + 0xb0, 0xeb, 0x04, 0x1b, 0x71, 0x1b, 0x1b, 0x04, + 0x25, 0x02, 0x00, 0x00, 0x42, 0x5d, 0x00, 0x00, + 0x00, 0xc7, 0x04, 0x26, 0x02, 0x00, 0x00, 0x5e, + 0x09, 0x00, 0xc1, 0x0c, 0x47, 0x24, 0x03, 0x00, + 0x1b, 0x71, 0x1b, 0x49, 0x29, 0xdd, 0x5e, 0x06, + 0x00, 0xd1, 0xef, 0xef, 0x0e, 0x29, 0x0c, 0x43, + 0x02, 0x01, 0xdc, 0x07, 0x02, 0x01, 0x02, 0x03, + 0x05, 0x00, 0x3d, 0x03, 0xc8, 0x06, 0x00, 0x01, + 0x00, 0x8a, 0x06, 0x00, 0x01, 0x00, 0xce, 0x08, + 0x00, 0x00, 0x00, 0xbe, 0x07, 0x03, 0x03, 0xd0, + 0x07, 0x0a, 0x01, 0xce, 0x07, 0x09, 0x01, 0xd8, + 0x03, 0x0e, 0x00, 0xdc, 0x03, 0x0f, 0x00, 0xdd, + 0xea, 0x30, 0xde, 0xd2, 0xaf, 0xea, 0x13, 0xdf, + 0x42, 0x9b, 0x01, 0x00, 0x00, 0xe0, 0x41, 0x87, + 0x01, 0x00, 0x00, 0x24, 0x01, 0x00, 0x0e, 0xd2, + 0xe2, 0xd2, 0xea, 0x16, 0xe0, 0x5e, 0x04, 0x00, + 0xd2, 0x47, 0x47, 0xcd, 0xea, 0x0c, 0xdf, 0x42, + 0x9b, 0x01, 0x00, 0x00, 0xc5, 0x24, 0x01, 0x00, + 0x0e, 0xdf, 0x42, 0x9b, 0x01, 0x00, 0x00, 0xd1, + 0x24, 0x01, 0x00, 0x29, 0x0c, 0x43, 0x02, 0x01, + 0xde, 0x07, 0x01, 0x00, 0x01, 0x03, 0x01, 0x02, + 0x2c, 0x01, 0xc8, 0x06, 0x00, 0x01, 0x00, 0xdc, + 0x07, 0x10, 0x01, 0x07, 0x02, 0x30, 0x07, 0x02, + 0x39, 0xd1, 0xb4, 0x47, 0xbe, 0x00, 0xa9, 0xea, + 0x14, 0xd1, 0xb4, 0x47, 0xbe, 0x01, 0xa7, 0xea, + 0x0c, 0xdd, 0xd1, 0x04, 0x47, 0x00, 0x00, 0x00, + 0xf0, 0x0e, 0xec, 0x0a, 0xdd, 0xd1, 0x04, 0x28, + 0x02, 0x00, 0x00, 0xf0, 0x0e, 0xdd, 0x04, 0x10, + 0x02, 0x00, 0x00, 0xef, 0x29, 0x0c, 0x43, 0x02, + 0x01, 0xe0, 0x07, 0x01, 0x04, 0x01, 0x05, 0x03, + 0x14, 0xa1, 0x04, 0x05, 0xc8, 0x06, 0x00, 0x01, + 0x00, 0x8a, 0x06, 0x00, 0x00, 0x00, 0xd2, 0x08, + 0x00, 0x01, 0x00, 0xf2, 0x05, 0x00, 0x02, 0x00, + 0x8c, 0x07, 0x00, 0x03, 0x00, 0xbe, 0x07, 0x03, + 0x03, 0xdc, 0x07, 0x10, 0x01, 0xe2, 0x07, 0x13, + 0x01, 0x07, 0x20, 0x5e, 0x22, 0x28, 0x5b, 0x5e, + 0x5c, 0x5c, 0x22, 0x5d, 0x7c, 0x5c, 0x5c, 0x2e, + 0x29, 0x2a, 0x22, 0x07, 0x98, 0x01, 0x00, 0x00, + 0x02, 0x01, 0x44, 0x00, 0x00, 0x00, 0x09, 0x06, + 0x00, 0x00, 0x00, 0x05, 0x08, 0xf5, 0xff, 0xff, + 0xff, 0x0c, 0x00, 0x06, 0x01, 0x22, 0x0e, 0x01, + 0x01, 0x0a, 0x27, 0x00, 0x00, 0x00, 0x1a, 0x0c, + 0x01, 0x0a, 0x14, 0x00, 0x00, 0x00, 0x16, 0x03, + 0x00, 0x00, 0x00, 0x21, 0x00, 0x23, 0x00, 0x5b, + 0x00, 0x5d, 0x00, 0xff, 0xff, 0x08, 0x03, 0x00, + 0x00, 0x00, 0x01, 0x5c, 0x04, 0x0d, 0x01, 0x1b, + 0x08, 0xd4, 0xff, 0xff, 0xff, 0x01, 0x22, 0x0d, + 0x00, 0x0b, 0x07, 0x20, 0x5e, 0x27, 0x28, 0x5b, + 0x5e, 0x5c, 0x5c, 0x27, 0x5d, 0x7c, 0x5c, 0x5c, + 0x2e, 0x29, 0x2a, 0x27, 0x07, 0x98, 0x01, 0x00, + 0x00, 0x02, 0x01, 0x44, 0x00, 0x00, 0x00, 0x09, + 0x06, 0x00, 0x00, 0x00, 0x05, 0x08, 0xf5, 0xff, + 0xff, 0xff, 0x0c, 0x00, 0x06, 0x01, 0x27, 0x0e, + 0x01, 0x01, 0x0a, 0x27, 0x00, 0x00, 0x00, 0x1a, + 0x0c, 0x01, 0x0a, 0x14, 0x00, 0x00, 0x00, 0x16, + 0x03, 0x00, 0x00, 0x00, 0x26, 0x00, 0x28, 0x00, + 0x5b, 0x00, 0x5d, 0x00, 0xff, 0xff, 0x08, 0x03, + 0x00, 0x00, 0x00, 0x01, 0x5c, 0x04, 0x0d, 0x01, + 0x1b, 0x08, 0xd4, 0xff, 0xff, 0xff, 0x01, 0x27, + 0x0d, 0x00, 0x0b, 0x07, 0x16, 0x5e, 0x5c, 0x3c, + 0x5b, 0x5e, 0x5c, 0x3e, 0x5d, 0x2b, 0x5c, 0x3e, + 0x07, 0x74, 0x00, 0x00, 0x01, 0x00, 0x32, 0x00, + 0x00, 0x00, 0x09, 0x06, 0x00, 0x00, 0x00, 0x05, + 0x08, 0xf5, 0xff, 0xff, 0xff, 0x0c, 0x00, 0x06, + 0x01, 0x3c, 0x1d, 0x0c, 0x00, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x7f, 0x01, + 0x00, 0x00, 0x00, 0x16, 0x02, 0x00, 0x00, 0x00, + 0x3d, 0x00, 0x3f, 0x00, 0xff, 0xff, 0x0b, 0x01, + 0x3e, 0x0d, 0x00, 0x0b, 0x07, 0x16, 0x5e, 0x5c, + 0x5b, 0x5b, 0x5e, 0x5c, 0x5d, 0x5d, 0x2b, 0x5c, + 0x5d, 0x07, 0x74, 0x00, 0x00, 0x01, 0x00, 0x32, + 0x00, 0x00, 0x00, 0x09, 0x06, 0x00, 0x00, 0x00, + 0x05, 0x08, 0xf5, 0xff, 0xff, 0xff, 0x0c, 0x00, + 0x06, 0x01, 0x5b, 0x1d, 0x0c, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x7f, + 0x01, 0x00, 0x00, 0x00, 0x16, 0x02, 0x00, 0x00, + 0x00, 0x5c, 0x00, 0x5e, 0x00, 0xff, 0xff, 0x0b, + 0x01, 0x5d, 0x0d, 0x00, 0x0b, 0x07, 0x02, 0x30, + 0x07, 0x02, 0x31, 0x07, 0x02, 0x32, 0x07, 0x02, + 0x33, 0x07, 0x02, 0x34, 0x07, 0x02, 0x35, 0x07, + 0x02, 0x36, 0x07, 0x02, 0x37, 0x07, 0x02, 0x38, + 0x07, 0x02, 0x39, 0x07, 0x56, 0x5e, 0x5b, 0x30, + 0x2d, 0x39, 0x61, 0x2d, 0x7a, 0x5f, 0x5d, 0x2b, + 0x5b, 0x2e, 0x5d, 0x3f, 0x5b, 0x30, 0x2d, 0x39, + 0x61, 0x2d, 0x7a, 0x5f, 0x5d, 0x2a, 0x5b, 0x65, + 0x45, 0x70, 0x50, 0x5d, 0x3f, 0x5b, 0x2b, 0x2d, + 0x5d, 0x3f, 0x5b, 0x30, 0x2d, 0x39, 0x5d, 0x2a, + 0x07, 0x9e, 0x03, 0x00, 0x00, 0x01, 0x00, 0xc7, + 0x00, 0x00, 0x00, 0x09, 0x06, 0x00, 0x00, 0x00, + 0x05, 0x08, 0xf5, 0xff, 0xff, 0xff, 0x0c, 0x00, + 0x06, 0x1d, 0x10, 0x00, 0x00, 0x00, 0x01, 0x00, + 0x00, 0x00, 0xff, 0xff, 0xff, 0x7f, 0x01, 0x00, + 0x00, 0x00, 0x16, 0x03, 0x00, 0x30, 0x00, 0x39, + 0x00, 0x5f, 0x00, 0x5f, 0x00, 0x61, 0x00, 0x7a, + 0x00, 0x0b, 0x1d, 0x08, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x16, 0x01, 0x00, 0x2e, 0x00, + 0x2e, 0x00, 0x0b, 0x1d, 0x10, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x7f, + 0x01, 0x00, 0x00, 0x00, 0x16, 0x03, 0x00, 0x30, + 0x00, 0x39, 0x00, 0x5f, 0x00, 0x5f, 0x00, 0x61, + 0x00, 0x7a, 0x00, 0x0b, 0x1d, 0x14, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, + 0x00, 0x01, 0x00, 0x00, 0x00, 0x16, 0x04, 0x00, + 0x45, 0x00, 0x45, 0x00, 0x50, 0x00, 0x50, 0x00, + 0x65, 0x00, 0x65, 0x00, 0x70, 0x00, 0x70, 0x00, + 0x0b, 0x1d, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, + 0x00, 0x00, 0x16, 0x02, 0x00, 0x2b, 0x00, 0x2b, + 0x00, 0x2d, 0x00, 0x2d, 0x00, 0x0b, 0x1d, 0x08, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, + 0xff, 0xff, 0x7f, 0x01, 0x00, 0x00, 0x00, 0x16, + 0x01, 0x00, 0x30, 0x00, 0x39, 0x00, 0x0b, 0x0d, + 0x00, 0x0b, 0xdd, 0x96, 0xea, 0x05, 0xde, 0xd1, + 0xef, 0x29, 0xd1, 0xe9, 0xb4, 0xa8, 0x69, 0x13, + 0x02, 0x00, 0x00, 0x04, 0x87, 0x01, 0x00, 0x00, + 0xc9, 0xd1, 0xca, 0xb4, 0xcb, 0x07, 0xcc, 0xd1, + 0xb4, 0x47, 0x11, 0x04, 0xb0, 0x01, 0x00, 0x00, + 0xae, 0xea, 0x19, 0x04, 0x49, 0x00, 0x00, 0x00, + 0xc9, 0xd1, 0x42, 0xb4, 0x01, 0x00, 0x00, 0xbe, + 0x00, 0xbe, 0x01, 0x33, 0x24, 0x01, 0x00, 0xcc, + 0xed, 0xb3, 0x01, 0x11, 0x04, 0xaf, 0x01, 0x00, + 0x00, 0xae, 0xea, 0x19, 0x04, 0x49, 0x00, 0x00, + 0x00, 0xc9, 0xd1, 0x42, 0xb4, 0x01, 0x00, 0x00, + 0xbe, 0x02, 0xbe, 0x03, 0x33, 0x24, 0x01, 0x00, + 0xcc, 0xed, 0x92, 0x01, 0x11, 0x04, 0xb3, 0x01, + 0x00, 0x00, 0xae, 0xea, 0x0a, 0x04, 0x2a, 0x02, + 0x00, 0x00, 0xc9, 0xed, 0x80, 0x01, 0x11, 0x04, + 0x1b, 0x02, 0x00, 0x00, 0xae, 0xea, 0x1e, 0xd1, + 0x42, 0xb4, 0x01, 0x00, 0x00, 0xbe, 0x04, 0xbe, + 0x05, 0x33, 0x24, 0x01, 0x00, 0xd0, 0x69, 0x65, + 0x01, 0x00, 0x00, 0x04, 0x2b, 0x02, 0x00, 0x00, + 0xc9, 0xed, 0x5a, 0x01, 0x11, 0x04, 0xd5, 0x01, + 0x00, 0x00, 0xae, 0xea, 0x1b, 0xd1, 0x42, 0xb4, + 0x01, 0x00, 0x00, 0xbe, 0x06, 0xbe, 0x07, 0x33, + 0x24, 0x01, 0x00, 0xd0, 0xea, 0x2e, 0x04, 0x2b, + 0x02, 0x00, 0x00, 0xc9, 0xed, 0x37, 0x01, 0x11, + 0x04, 0xb2, 0x01, 0x00, 0x00, 0xae, 0xeb, 0x1c, + 0x11, 0x04, 0x1a, 0x02, 0x00, 0x00, 0xae, 0xeb, + 0x13, 0x11, 0x04, 0x2c, 0x02, 0x00, 0x00, 0xae, + 0xeb, 0x0a, 0x11, 0x04, 0xd0, 0x01, 0x00, 0x00, + 0xae, 0xea, 0x0c, 0x04, 0x2d, 0x02, 0x00, 0x00, + 0xc9, 0xb5, 0xcb, 0xed, 0x08, 0x01, 0x11, 0x04, + 0xac, 0x01, 0x00, 0x00, 0xae, 0xea, 0x0a, 0x04, + 0x2b, 0x02, 0x00, 0x00, 0xc9, 0xed, 0xf6, 0x00, + 0x11, 0xbe, 0x08, 0xae, 0xeb, 0x37, 0x11, 0xbe, + 0x09, 0xae, 0xeb, 0x31, 0x11, 0xbe, 0x0a, 0xae, + 0xeb, 0x2b, 0x11, 0xbe, 0x0b, 0xae, 0xeb, 0x25, + 0x11, 0xbe, 0x0c, 0xae, 0xeb, 0x1f, 0x11, 0xbe, + 0x0d, 0xae, 0xeb, 0x19, 0x11, 0xbe, 0x0e, 0xae, + 0xeb, 0x13, 0x11, 0xbe, 0x0f, 0xae, 0xeb, 0x0d, + 0x11, 0xbe, 0x10, 0xae, 0xeb, 0x07, 0x11, 0xbe, + 0x11, 0xae, 0xea, 0x19, 0x04, 0x47, 0x00, 0x00, + 0x00, 0xc9, 0xd1, 0x42, 0xb4, 0x01, 0x00, 0x00, + 0xbe, 0x12, 0xbe, 0x13, 0x33, 0x24, 0x01, 0x00, + 0xcc, 0xed, 0xa2, 0x00, 0x11, 0x04, 0xdb, 0x01, + 0x00, 0x00, 0xae, 0xea, 0x06, 0xb5, 0xcb, 0xed, + 0x94, 0x00, 0xdf, 0xd1, 0xef, 0xea, 0x06, 0xd1, + 0xe9, 0xb5, 0x9e, 0xcb, 0xd1, 0x42, 0xbc, 0x01, + 0x00, 0x00, 0x04, 0x9d, 0x00, 0x00, 0x00, 0x24, + 0x01, 0x00, 0xea, 0x09, 0x04, 0x2e, 0x02, 0x00, + 0x00, 0xc9, 0xec, 0x71, 0xd1, 0x42, 0xbc, 0x01, + 0x00, 0x00, 0x04, 0x99, 0x00, 0x00, 0x00, 0x24, + 0x01, 0x00, 0xea, 0x09, 0x04, 0x4b, 0x00, 0x00, + 0x00, 0xc9, 0xec, 0x59, 0xd1, 0x04, 0xea, 0x00, + 0x00, 0x00, 0xae, 0x11, 0xeb, 0x09, 0x0e, 0xd1, + 0x04, 0x2f, 0x02, 0x00, 0x00, 0xae, 0xea, 0x09, + 0x04, 0x30, 0x02, 0x00, 0x00, 0xc9, 0xec, 0x3d, + 0xd1, 0x04, 0x03, 0x00, 0x00, 0x00, 0xae, 0x11, + 0xeb, 0x09, 0x0e, 0xd1, 0x04, 0x02, 0x00, 0x00, + 0x00, 0xae, 0xea, 0x09, 0x04, 0x48, 0x00, 0x00, + 0x00, 0xc9, 0xec, 0x21, 0xd1, 0x04, 0x01, 0x00, + 0x00, 0x00, 0xae, 0xea, 0x09, 0x04, 0x01, 0x00, + 0x00, 0x00, 0xc9, 0xec, 0x10, 0xd1, 0x04, 0x46, + 0x00, 0x00, 0x00, 0xae, 0xea, 0x07, 0x04, 0x46, + 0x00, 0x00, 0x00, 0xc9, 0x0e, 0xc8, 0xea, 0x06, + 0xc8, 0xb4, 0x47, 0xe9, 0xcb, 0xc7, 0xb4, 0xa8, + 0xea, 0x0d, 0xd1, 0x42, 0xa7, 0x01, 0x00, 0x00, + 0xb4, 0xc7, 0x24, 0x02, 0x00, 0xca, 0xde, 0xc6, + 0xc5, 0xf0, 0x0e, 0xd1, 0x42, 0xa7, 0x01, 0x00, + 0x00, 0xc6, 0xe9, 0x24, 0x01, 0x00, 0xd5, 0xed, + 0xea, 0xfd, 0x29, 0x0c, 0x43, 0x02, 0x01, 0xe2, + 0x07, 0x01, 0x01, 0x01, 0x03, 0x00, 0x00, 0x19, + 0x02, 0xc8, 0x06, 0x00, 0x01, 0x00, 0xe2, 0x05, + 0x00, 0x00, 0x00, 0xd1, 0xd1, 0xe9, 0xb5, 0x9e, + 0x47, 0xcd, 0x04, 0xd5, 0x01, 0x00, 0x00, 0xae, + 0x11, 0xeb, 0x09, 0x0e, 0xc5, 0x04, 0x19, 0x02, + 0x00, 0x00, 0xae, 0x28, 0x0c, 0x43, 0x02, 0x01, + 0xe4, 0x07, 0x01, 0x04, 0x01, 0x08, 0x03, 0x00, + 0x7c, 0x05, 0xdc, 0x05, 0x00, 0x01, 0x00, 0xe2, + 0x08, 0x00, 0x00, 0x00, 0xe4, 0x08, 0x00, 0x01, + 0x00, 0xe6, 0x08, 0x00, 0x02, 0x00, 0xc8, 0x06, + 0x00, 0x03, 0x00, 0xcc, 0x07, 0x08, 0x01, 0xe2, + 0x07, 0x13, 0x01, 0xe4, 0x07, 0x14, 0x01, 0xdd, + 0xd1, 0x47, 0xe9, 0xc9, 0xdd, 0xd1, 0xb5, 0x9d, + 0x47, 0x04, 0x20, 0x02, 0x00, 0x00, 0xae, 0xea, + 0x0e, 0xd1, 0xb6, 0x9d, 0xd5, 0xc5, 0xb6, 0xdd, + 0xd1, 0x47, 0xe9, 0x9d, 0x9d, 0xc9, 0xc5, 0xca, + 0xde, 0xdd, 0xd1, 0x47, 0xef, 0xea, 0x4f, 0xb5, + 0xcb, 0xd1, 0x8f, 0xd9, 0xdd, 0xe9, 0xa6, 0xea, + 0x45, 0xc7, 0x94, 0x01, 0xdd, 0xd1, 0x47, 0xd0, + 0x04, 0xb2, 0x01, 0x00, 0x00, 0xae, 0x11, 0xeb, + 0x09, 0x0e, 0xc8, 0x04, 0x1a, 0x02, 0x00, 0x00, + 0xae, 0xeb, 0x2b, 0xec, 0x1c, 0x11, 0x7e, 0x7a, + 0x6e, 0x01, 0x00, 0x00, 0x00, 0x00, 0x81, 0x02, + 0x0e, 0x3d, 0x79, 0x31, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x81, 0x02, 0x0e, 0x3d, 0x84, 0xec, 0x06, + 0xdf, 0xd1, 0xef, 0xec, 0xe1, 0x0e, 0xc5, 0x94, + 0x01, 0xb6, 0xcb, 0xec, 0xb5, 0xd1, 0xc6, 0x26, + 0x02, 0x00, 0x28, 0x0c, 0x43, 0x02, 0x01, 0xe6, + 0x07, 0x02, 0x02, 0x02, 0x03, 0x05, 0x00, 0x67, + 0x04, 0xdc, 0x05, 0x00, 0x01, 0x00, 0xe8, 0x08, + 0x00, 0x01, 0x00, 0x8c, 0x08, 0x00, 0x00, 0x00, + 0xc8, 0x06, 0x00, 0x01, 0x00, 0xcc, 0x07, 0x08, + 0x01, 0xdc, 0x07, 0x10, 0x01, 0xde, 0x07, 0x11, + 0x01, 0xe0, 0x07, 0x12, 0x01, 0xe2, 0x07, 0x13, + 0x01, 0xc0, 0xc9, 0xd1, 0xd2, 0xa7, 0xea, 0x60, + 0xdd, 0xd1, 0x91, 0xd5, 0x47, 0xce, 0x04, 0xb2, + 0x01, 0x00, 0x00, 0xae, 0x11, 0xeb, 0x09, 0x0e, + 0xc6, 0x04, 0x1a, 0x02, 0x00, 0x00, 0xae, 0xea, + 0x11, 0xc5, 0xe9, 0xb5, 0xa8, 0xea, 0x27, 0xde, + 0x04, 0xd0, 0x01, 0x00, 0x00, 0xef, 0x0e, 0xec, + 0x1d, 0xde, 0xc5, 0xef, 0x0e, 0xdd, 0xd1, 0x47, + 0x04, 0x20, 0x02, 0x00, 0x00, 0xae, 0xea, 0x0e, + 0xdf, 0xc6, 0xef, 0x0e, 0xd1, 0x8f, 0xd5, 0xdd, + 0xd1, 0x91, 0xd5, 0x47, 0xca, 0xe0, 0xc6, 0xef, + 0x0e, 0x5e, 0x04, 0x00, 0xc6, 0xef, 0xea, 0x08, + 0x04, 0xd0, 0x01, 0x00, 0x00, 0xec, 0x06, 0x04, + 0x35, 0x02, 0x00, 0x00, 0xc9, 0xec, 0x9d, 0x29, + 0x0c, 0x43, 0x02, 0x01, 0xe8, 0x07, 0x02, 0x00, + 0x02, 0x04, 0x01, 0x00, 0x1a, 0x02, 0xc8, 0x06, + 0x00, 0x01, 0x00, 0x9a, 0x07, 0x00, 0x01, 0x00, + 0xdc, 0x07, 0x10, 0x01, 0xd2, 0xb4, 0xa8, 0xea, + 0x12, 0xd1, 0x04, 0xd0, 0x01, 0x00, 0x00, 0x42, + 0x36, 0x02, 0x00, 0x00, 0xd2, 0x24, 0x01, 0x00, + 0x9d, 0xd5, 0xdd, 0xd1, 0xef, 0x29, 0x0c, 0x43, + 0x02, 0x01, 0xea, 0x07, 0x02, 0x0c, 0x02, 0x08, + 0x0b, 0x00, 0x8f, 0x05, 0x0e, 0xee, 0x08, 0x00, + 0x01, 0x00, 0xf6, 0x01, 0x00, 0x01, 0x00, 0xf0, + 0x08, 0x00, 0x00, 0x00, 0xe8, 0x08, 0x00, 0x01, + 0x00, 0xe4, 0x08, 0x00, 0x02, 0x00, 0x8c, 0x08, + 0x00, 0x03, 0x00, 0xf2, 0x08, 0x00, 0x04, 0x00, + 0xdc, 0x05, 0x00, 0x05, 0x00, 0xe2, 0x08, 0x00, + 0x06, 0x00, 0xfe, 0x07, 0x00, 0x07, 0x00, 0x84, + 0x07, 0x00, 0x08, 0x00, 0xf4, 0x08, 0x00, 0x09, + 0x00, 0xca, 0x05, 0x00, 0x0a, 0x00, 0xf6, 0x08, + 0x00, 0x0b, 0x00, 0xc2, 0x07, 0x02, 0x01, 0xe4, + 0x07, 0x14, 0x01, 0xe6, 0x07, 0x15, 0x01, 0xcc, + 0x07, 0x08, 0x01, 0xde, 0x07, 0x11, 0x01, 0xe0, + 0x07, 0x12, 0x01, 0xe2, 0x07, 0x13, 0x01, 0xb6, + 0x02, 0x10, 0x00, 0xdc, 0x07, 0x10, 0x01, 0xe8, + 0x07, 0x16, 0x01, 0xea, 0x07, 0x17, 0x01, 0xdd, + 0xd1, 0x9e, 0xb6, 0x9e, 0xc9, 0x06, 0x11, 0xf2, + 0xeb, 0x0d, 0x7e, 0x81, 0x00, 0x0e, 0xca, 0x81, + 0x00, 0x0e, 0xcb, 0x84, 0xec, 0x07, 0x0e, 0xde, + 0xd2, 0xef, 0xec, 0xef, 0xc7, 0xc5, 0xa7, 0xea, + 0x0c, 0xdf, 0xd2, 0xc6, 0xf0, 0x0e, 0xc6, 0xc7, + 0x26, 0x02, 0x00, 0x28, 0xe0, 0xd2, 0xb5, 0x9d, + 0x47, 0x04, 0x20, 0x02, 0x00, 0x00, 0xae, 0xea, + 0x0d, 0x5e, 0x04, 0x00, 0xe0, 0xd2, 0x47, 0xef, + 0x0e, 0xd2, 0xb6, 0x9d, 0xd6, 0x5e, 0x05, 0x00, + 0xe0, 0xd2, 0x47, 0xef, 0x0e, 0x5e, 0x06, 0x00, + 0xe0, 0xd2, 0x47, 0xef, 0x96, 0xea, 0x07, 0xd2, + 0xc7, 0x26, 0x02, 0x00, 0x28, 0xd1, 0xb6, 0x9d, + 0xd5, 0xc5, 0xb6, 0x9e, 0xc9, 0xc0, 0xcc, 0xd2, + 0xb5, 0x9d, 0xc2, 0x04, 0xe0, 0xd2, 0x47, 0x42, + 0x3c, 0x02, 0x00, 0x00, 0x04, 0xd5, 0x01, 0x00, + 0x00, 0x24, 0x01, 0x00, 0x69, 0xa9, 0x01, 0x00, + 0x00, 0xb4, 0xc2, 0x07, 0x26, 0x00, 0x00, 0xc2, + 0x0a, 0xc1, 0x04, 0xc2, 0x05, 0xc1, 0x05, 0xc6, + 0xa6, 0xea, 0x5d, 0xe0, 0xc1, 0x05, 0x47, 0xb4, + 0x47, 0x04, 0xac, 0x01, 0x00, 0x00, 0xae, 0x11, + 0xeb, 0x0e, 0x0e, 0xe0, 0xc1, 0x05, 0xb5, 0x9d, + 0x47, 0x04, 0x20, 0x02, 0x00, 0x00, 0xae, 0xeb, + 0x3f, 0xec, 0x1c, 0x11, 0x7e, 0x79, 0x6e, 0x01, + 0x00, 0x00, 0x05, 0x00, 0x81, 0x02, 0x0e, 0x3d, + 0x79, 0x31, 0x02, 0x00, 0x00, 0x06, 0x00, 0x81, + 0x02, 0x0e, 0x3d, 0x84, 0xec, 0x07, 0xde, 0xc1, + 0x05, 0xef, 0xec, 0xe0, 0x0e, 0xc1, 0x0a, 0xc1, + 0x07, 0x91, 0xc2, 0x07, 0x1b, 0x11, 0xb0, 0xeb, + 0x04, 0x1b, 0x71, 0x1b, 0x1b, 0xc1, 0x06, 0x1b, + 0x71, 0x1b, 0x49, 0x93, 0x05, 0xec, 0x9f, 0x5e, + 0x07, 0x00, 0x42, 0x8e, 0x01, 0x00, 0x00, 0xc5, + 0xb7, 0x9b, 0xc1, 0x0a, 0xe9, 0xbc, 0x10, 0x24, + 0x03, 0x00, 0xc2, 0x09, 0xc1, 0x09, 0xb5, 0xa8, + 0xea, 0x73, 0x26, 0x00, 0x00, 0xc2, 0x0b, 0xb4, + 0xc2, 0x08, 0xb4, 0xc2, 0x07, 0xc1, 0x07, 0xc1, + 0x0a, 0xe9, 0xa6, 0xea, 0x3b, 0xc1, 0x0b, 0xc1, + 0x08, 0x1b, 0x11, 0xb0, 0xeb, 0x04, 0x1b, 0x71, + 0x1b, 0x1b, 0x5e, 0x07, 0x00, 0x42, 0xc9, 0x01, + 0x00, 0x00, 0xc1, 0x0b, 0xc1, 0x08, 0x47, 0x11, + 0xeb, 0x03, 0x0e, 0xb4, 0xc1, 0x0a, 0xc1, 0x07, + 0x47, 0xb6, 0x9d, 0x24, 0x02, 0x00, 0x1b, 0x71, + 0x1b, 0x49, 0xc1, 0x08, 0xb5, 0x9d, 0xc1, 0x09, + 0x9c, 0xc2, 0x08, 0x93, 0x07, 0xec, 0xbf, 0xb4, + 0xc2, 0x06, 0xb4, 0xc2, 0x08, 0xc1, 0x08, 0xc1, + 0x09, 0xa6, 0xea, 0x0f, 0xc1, 0x06, 0xc1, 0x0b, + 0xc1, 0x08, 0x47, 0x9d, 0xc2, 0x06, 0x93, 0x08, + 0xec, 0xec, 0xc1, 0x06, 0xc5, 0xa7, 0xeb, 0x05, + 0x92, 0x09, 0xec, 0x89, 0xc1, 0x09, 0xb5, 0xa8, + 0x69, 0xa5, 0x00, 0x00, 0x00, 0xb4, 0xc2, 0x06, + 0xc1, 0x09, 0xb5, 0x9e, 0xc2, 0x08, 0xc1, 0x04, + 0xc2, 0x05, 0xc1, 0x05, 0xc6, 0xa6, 0x69, 0x8b, + 0x00, 0x00, 0x00, 0xe0, 0xc1, 0x05, 0x47, 0xb4, + 0x47, 0x04, 0xac, 0x01, 0x00, 0x00, 0xae, 0x11, + 0xeb, 0x0e, 0x0e, 0xe0, 0xc1, 0x05, 0xb5, 0x9d, + 0x47, 0x04, 0x20, 0x02, 0x00, 0x00, 0xae, 0xeb, + 0x6a, 0xc1, 0x06, 0xc8, 0xe9, 0x9d, 0xc2, 0x06, + 0x5e, 0x08, 0x00, 0xc8, 0xef, 0x0e, 0x04, 0x2c, + 0x02, 0x00, 0x00, 0xcc, 0xc1, 0x08, 0xc1, 0x09, + 0xb5, 0x9e, 0xae, 0xea, 0x11, 0x5e, 0x09, 0x00, + 0x04, 0x8d, 0x01, 0x00, 0x00, 0xd1, 0xf0, 0x0e, + 0xb4, 0xc2, 0x08, 0xec, 0x2f, 0x5e, 0x09, 0x00, + 0xc0, 0xc1, 0x0b, 0xc1, 0x08, 0x91, 0xc2, 0x08, + 0x47, 0xc1, 0x06, 0x9e, 0xf0, 0x0e, 0xec, 0x1c, + 0x11, 0x7e, 0x79, 0x6e, 0x01, 0x00, 0x00, 0x05, + 0x00, 0x81, 0x02, 0x0e, 0x3d, 0x79, 0x31, 0x02, + 0x00, 0x00, 0x06, 0x00, 0x81, 0x02, 0x0e, 0x3d, + 0x84, 0xec, 0x0a, 0x5e, 0x0a, 0x00, 0xd1, 0xc1, + 0x05, 0xf0, 0xec, 0xdd, 0x0e, 0x93, 0x05, 0xed, + 0x72, 0xff, 0xc1, 0x05, 0xc2, 0x04, 0xc1, 0x04, + 0xc2, 0x05, 0xc1, 0x05, 0xc6, 0xa6, 0xea, 0x43, + 0x5e, 0x08, 0x00, 0xc8, 0xef, 0x0e, 0x04, 0x2c, + 0x02, 0x00, 0x00, 0xcc, 0x5e, 0x09, 0x00, 0x04, + 0x8d, 0x01, 0x00, 0x00, 0xd1, 0xf0, 0x0e, 0xec, + 0x1c, 0x11, 0x7e, 0x79, 0x6e, 0x01, 0x00, 0x00, + 0x05, 0x00, 0x81, 0x02, 0x0e, 0x3d, 0x79, 0x31, + 0x02, 0x00, 0x00, 0x06, 0x00, 0x81, 0x02, 0x0e, + 0x3d, 0x84, 0xec, 0x0a, 0x5e, 0x0a, 0x00, 0xd1, + 0xc1, 0x05, 0xf0, 0xec, 0xdd, 0x0e, 0x93, 0x05, + 0xec, 0xb9, 0x5e, 0x09, 0x00, 0x04, 0x8d, 0x01, + 0x00, 0x00, 0xd1, 0xb6, 0x9e, 0xd9, 0xf0, 0x0e, + 0x5e, 0x05, 0x00, 0xe0, 0xc6, 0x47, 0xef, 0x0e, + 0xc6, 0xdd, 0x26, 0x02, 0x00, 0x28, 0xbf, 0x00, + 0xca, 0xbf, 0x01, 0xc2, 0x0b, 0xbf, 0x02, 0xc2, + 0x0c, 0xbf, 0x03, 0xc2, 0x0d, 0xbf, 0x04, 0xc2, + 0x0e, 0xbf, 0x05, 0xc2, 0x0f, 0xbf, 0x06, 0xc2, + 0x10, 0xbf, 0x07, 0xc2, 0x11, 0xbf, 0x08, 0xc2, + 0x12, 0xbf, 0x09, 0xc2, 0x13, 0xbf, 0x0a, 0xc2, + 0x14, 0xbf, 0x0b, 0xc2, 0x15, 0xbf, 0x0c, 0xc2, + 0x16, 0xbf, 0x0d, 0xc2, 0x17, 0x0b, 0xc9, 0xd2, + 0x97, 0x04, 0x4a, 0x00, 0x00, 0x00, 0xae, 0xea, + 0x1c, 0xd2, 0x07, 0xaf, 0xea, 0x17, 0xd2, 0xcd, + 0x41, 0x3d, 0x02, 0x00, 0x00, 0xd6, 0xc5, 0x41, + 0x3e, 0x02, 0x00, 0x00, 0xd7, 0xc5, 0x41, 0xec, + 0x00, 0x00, 0x00, 0xd8, 0xd2, 0x97, 0x04, 0x48, + 0x00, 0x00, 0x00, 0xaf, 0xea, 0x03, 0x09, 0xd6, + 0xc6, 0xd3, 0xb6, 0xf0, 0xd7, 0xc6, 0xd4, 0x0a, + 0xf0, 0xd8, 0xc6, 0xc5, 0x41, 0xe1, 0x01, 0x00, + 0x00, 0x5e, 0x10, 0x00, 0x42, 0x8e, 0x01, 0x00, + 0x00, 0x5e, 0x11, 0x00, 0xbc, 0x50, 0x24, 0x02, + 0x00, 0xf0, 0xcb, 0xc6, 0xc5, 0x41, 0xe2, 0x01, + 0x00, 0x00, 0xbc, 0x64, 0xf0, 0xcc, 0xc6, 0xc5, + 0x41, 0xe3, 0x01, 0x00, 0x00, 0xc8, 0xbc, 0x0a, + 0x9d, 0xf0, 0xc2, 0x04, 0xc6, 0xc5, 0x41, 0xe4, + 0x01, 0x00, 0x00, 0xbc, 0x4e, 0xf0, 0xc2, 0x05, + 0x0b, 0x26, 0x01, 0x00, 0xc2, 0x06, 0x26, 0x00, + 0x00, 0xc2, 0x07, 0x26, 0x00, 0x00, 0xc2, 0x08, + 0x26, 0x00, 0x00, 0xc2, 0x09, 0x04, 0x87, 0x01, + 0x00, 0x00, 0xc2, 0x0a, 0xc1, 0x0f, 0xd1, 0xb4, + 0xf0, 0x0e, 0xc1, 0x17, 0xb4, 0xb4, 0xf0, 0x0e, + 0xc1, 0x10, 0xc0, 0xef, 0x0e, 0xc1, 0x09, 0x42, + 0x5c, 0x00, 0x00, 0x00, 0xc0, 0x25, 0x01, 0x00, + 0x0c, 0x43, 0x02, 0x01, 0xa2, 0x05, 0x01, 0x00, + 0x01, 0x07, 0x05, 0x00, 0x3c, 0x01, 0xba, 0x07, + 0x00, 0x01, 0x00, 0xc8, 0x03, 0x00, 0x0c, 0xa0, + 0x05, 0x75, 0x01, 0xe6, 0x03, 0x18, 0x01, 0xe2, + 0x03, 0x16, 0x01, 0xe4, 0x03, 0x17, 0x01, 0x65, + 0x00, 0x00, 0x42, 0x86, 0x01, 0x00, 0x00, 0xde, + 0x42, 0x3f, 0x02, 0x00, 0x00, 0xd1, 0x0b, 0xdf, + 0x4c, 0x3e, 0x02, 0x00, 0x00, 0xe0, 0x4c, 0xec, + 0x00, 0x00, 0x00, 0x5e, 0x04, 0x00, 0x4c, 0x3d, + 0x02, 0x00, 0x00, 0x24, 0x02, 0x00, 0x24, 0x01, + 0x00, 0x0e, 0x65, 0x00, 0x00, 0x42, 0x86, 0x01, + 0x00, 0x00, 0x04, 0x8d, 0x01, 0x00, 0x00, 0x24, + 0x01, 0x00, 0x29, 0x0c, 0x43, 0x02, 0x01, 0xa4, + 0x05, 0x01, 0x05, 0x01, 0x06, 0x03, 0x00, 0xb4, + 0x01, 0x06, 0xe8, 0x05, 0x00, 0x01, 0x00, 0xb0, + 0x06, 0x00, 0x00, 0x00, 0x84, 0x04, 0x00, 0x01, + 0x00, 0x80, 0x09, 0x00, 0x02, 0x00, 0xb0, 0x07, + 0x00, 0x03, 0x00, 0x82, 0x09, 0x00, 0x04, 0x00, + 0xa6, 0x05, 0x78, 0x01, 0xae, 0x05, 0x7c, 0x01, + 0xc8, 0x03, 0x00, 0x0c, 0xd1, 0x04, 0x42, 0x02, + 0x00, 0x00, 0xae, 0xea, 0x06, 0xdd, 0xee, 0x0e, + 0x0a, 0x28, 0xd1, 0xb4, 0x47, 0x04, 0xab, 0x01, + 0x00, 0x00, 0xaf, 0xea, 0x0e, 0xd1, 0xb4, 0x47, + 0x04, 0xac, 0x01, 0x00, 0x00, 0xaf, 0xea, 0x03, + 0x09, 0x28, 0xb5, 0xc9, 0xc5, 0xd1, 0xe9, 0xa6, + 0xea, 0x10, 0xd1, 0xc5, 0x47, 0x04, 0xd0, 0x01, + 0x00, 0x00, 0xaf, 0xea, 0x05, 0x93, 0x00, 0xec, + 0xec, 0xd1, 0x42, 0x88, 0x01, 0x00, 0x00, 0xb5, + 0xc5, 0x24, 0x02, 0x00, 0xca, 0xb4, 0xcb, 0xde, + 0x7d, 0xec, 0x21, 0xc2, 0x04, 0xc1, 0x04, 0x42, + 0xbc, 0x01, 0x00, 0x00, 0xc6, 0x24, 0x01, 0x00, + 0xea, 0x12, 0xde, 0xc1, 0x04, 0x47, 0xcc, 0x93, + 0x02, 0xc1, 0x04, 0xc6, 0xae, 0xea, 0x05, 0xb4, + 0xcb, 0xec, 0x05, 0x80, 0xea, 0xde, 0x0e, 0x0e, + 0xc8, 0xea, 0x1d, 0xc7, 0xb6, 0xa6, 0xea, 0x18, + 0xc8, 0xd1, 0x42, 0x88, 0x01, 0x00, 0x00, 0xc5, + 0x24, 0x01, 0x00, 0x42, 0x43, 0x02, 0x00, 0x00, + 0x24, 0x00, 0x00, 0xef, 0x0e, 0xec, 0x20, 0x65, + 0x02, 0x00, 0x42, 0x86, 0x01, 0x00, 0x00, 0x04, + 0x44, 0x02, 0x00, 0x00, 0x42, 0x5d, 0x00, 0x00, + 0x00, 0xc6, 0x04, 0x8d, 0x01, 0x00, 0x00, 0x24, + 0x02, 0x00, 0x24, 0x01, 0x00, 0x0e, 0x0a, 0x28, + 0x0c, 0x43, 0x02, 0x01, 0xa6, 0x05, 0x00, 0x01, + 0x00, 0x07, 0x09, 0x01, 0xdc, 0x01, 0x01, 0x8a, + 0x09, 0x00, 0x00, 0x00, 0xc8, 0x03, 0x00, 0x0c, + 0xe8, 0x03, 0x19, 0x01, 0xe0, 0x03, 0x15, 0x01, + 0xea, 0x03, 0x1a, 0x01, 0xe6, 0x03, 0x18, 0x01, + 0xe4, 0x03, 0x17, 0x01, 0xe2, 0x03, 0x16, 0x01, + 0xdc, 0x03, 0x13, 0x01, 0xda, 0x03, 0x12, 0x01, + 0x0c, 0x42, 0x02, 0x01, 0x00, 0x01, 0x00, 0x01, + 0x01, 0x00, 0x00, 0x0f, 0x01, 0x92, 0x06, 0x00, + 0x01, 0x00, 0xd1, 0xea, 0x07, 0x04, 0x7e, 0x00, + 0x00, 0x00, 0x28, 0x04, 0xd0, 0x01, 0x00, 0x00, + 0x28, 0xbf, 0x00, 0x4d, 0x45, 0x02, 0x00, 0x00, + 0xc9, 0x65, 0x00, 0x00, 0x42, 0x86, 0x01, 0x00, + 0x00, 0x04, 0x46, 0x02, 0x00, 0x00, 0x04, 0x47, + 0x02, 0x00, 0x00, 0x9d, 0xc5, 0xde, 0xef, 0x9d, + 0x04, 0x48, 0x02, 0x00, 0x00, 0x9d, 0x04, 0x49, + 0x02, 0x00, 0x00, 0x9d, 0xc5, 0xde, 0x96, 0xef, + 0x9d, 0x04, 0x4a, 0x02, 0x00, 0x00, 0x9d, 0x04, + 0x4b, 0x02, 0x00, 0x00, 0x9d, 0xc5, 0xdf, 0xef, + 0x9d, 0x04, 0x4c, 0x02, 0x00, 0x00, 0x9d, 0x04, + 0x4d, 0x02, 0x00, 0x00, 0x9d, 0xc5, 0xe0, 0xef, + 0x9d, 0x04, 0x4e, 0x02, 0x00, 0x00, 0x9d, 0x04, + 0x4f, 0x02, 0x00, 0x00, 0x42, 0x5d, 0x00, 0x00, + 0x00, 0x5e, 0x04, 0x00, 0x04, 0x50, 0x02, 0x00, + 0x00, 0x24, 0x02, 0x00, 0x9d, 0x04, 0x51, 0x02, + 0x00, 0x00, 0x9d, 0xc5, 0x5e, 0x05, 0x00, 0xef, + 0x9d, 0x04, 0x52, 0x02, 0x00, 0x00, 0x9d, 0x04, + 0x53, 0x02, 0x00, 0x00, 0x9d, 0xc5, 0x5e, 0x06, + 0x00, 0xef, 0x9d, 0x04, 0x54, 0x02, 0x00, 0x00, + 0x9d, 0x04, 0x55, 0x02, 0x00, 0x00, 0x9d, 0xc5, + 0x5e, 0x07, 0x00, 0x5e, 0x08, 0x00, 0x41, 0x56, + 0x02, 0x00, 0x00, 0xac, 0xef, 0x9d, 0x04, 0x57, + 0x02, 0x00, 0x00, 0x9d, 0x04, 0x58, 0x02, 0x00, + 0x00, 0x9d, 0xc5, 0x5e, 0x07, 0x00, 0x5e, 0x08, + 0x00, 0x41, 0x59, 0x02, 0x00, 0x00, 0xac, 0xef, + 0x9d, 0x04, 0x5a, 0x02, 0x00, 0x00, 0x9d, 0x04, + 0x5b, 0x02, 0x00, 0x00, 0x9d, 0x04, 0x5c, 0x02, + 0x00, 0x00, 0x9d, 0x04, 0x5d, 0x02, 0x00, 0x00, + 0x9d, 0x24, 0x01, 0x00, 0x29, 0x0c, 0x43, 0x02, + 0x01, 0xa8, 0x05, 0x01, 0x01, 0x01, 0x07, 0x01, + 0x00, 0x5f, 0x02, 0xc8, 0x06, 0x00, 0x01, 0x00, + 0xbc, 0x09, 0x04, 0x00, 0x03, 0xc8, 0x03, 0x00, + 0x0c, 0xd1, 0x42, 0x5f, 0x02, 0x00, 0x00, 0x04, + 0xac, 0x01, 0x00, 0x00, 0x24, 0x01, 0x00, 0xd1, + 0x42, 0x5f, 0x02, 0x00, 0x00, 0x04, 0xb3, 0x01, + 0x00, 0x00, 0x24, 0x01, 0x00, 0xa7, 0xea, 0x09, + 0xd1, 0x04, 0x60, 0x02, 0x00, 0x00, 0x9d, 0xd5, + 0x6c, 0x13, 0x00, 0x00, 0x00, 0x65, 0x00, 0x00, + 0x42, 0x61, 0x02, 0x00, 0x00, 0xd1, 0x24, 0x01, + 0x00, 0x0e, 0x0e, 0x29, 0xc9, 0x6c, 0x21, 0x00, + 0x00, 0x00, 0x65, 0x00, 0x00, 0x42, 0x86, 0x01, + 0x00, 0x00, 0xc0, 0x42, 0x5d, 0x00, 0x00, 0x00, + 0xc5, 0x04, 0x8d, 0x01, 0x00, 0x00, 0x24, 0x02, + 0x00, 0x24, 0x01, 0x00, 0x0e, 0x0e, 0x29, 0x2f, + 0x0c, 0x43, 0x02, 0x01, 0xaa, 0x05, 0x01, 0x00, + 0x01, 0x03, 0x02, 0x00, 0x10, 0x01, 0xbc, 0x09, + 0x00, 0x01, 0x00, 0xc4, 0x05, 0x87, 0x01, 0x01, + 0xc8, 0x03, 0x00, 0x0c, 0xdd, 0xee, 0x0e, 0x65, + 0x01, 0x00, 0x42, 0x55, 0x01, 0x00, 0x00, 0xd1, + 0x24, 0x01, 0x00, 0x29, 0x0c, 0x43, 0x02, 0x01, + 0xac, 0x05, 0x02, 0x00, 0x02, 0x03, 0x00, 0x00, + 0x14, 0x02, 0xc8, 0x06, 0x00, 0x01, 0x00, 0xee, + 0x07, 0x00, 0x01, 0x00, 0xd1, 0xea, 0x10, 0x04, + 0x62, 0x02, 0x00, 0x00, 0x42, 0xf8, 0x01, 0x00, + 0x00, 0xd1, 0x24, 0x01, 0x00, 0x28, 0xd2, 0x28, + 0x0c, 0x42, 0x02, 0x01, 0x00, 0x01, 0x00, 0x01, + 0x03, 0x02, 0x00, 0x06, 0x01, 0xc8, 0x06, 0x00, + 0x01, 0x00, 0xe8, 0x03, 0x19, 0x01, 0xac, 0x05, + 0x7b, 0x01, 0xde, 0xd1, 0x0a, 0xf0, 0xe1, 0x29, + 0x0c, 0x42, 0x02, 0x01, 0x00, 0x01, 0x00, 0x01, + 0x03, 0x02, 0x00, 0x07, 0x01, 0xc8, 0x06, 0x00, + 0x01, 0x00, 0xe8, 0x03, 0x19, 0x01, 0xac, 0x05, + 0x7b, 0x01, 0xde, 0xd1, 0x0a, 0xf0, 0x96, 0xe1, + 0x29, 0x0c, 0x42, 0x02, 0x01, 0x00, 0x01, 0x00, + 0x01, 0x03, 0x02, 0x00, 0x07, 0x01, 0xc8, 0x06, + 0x00, 0x01, 0x00, 0xe0, 0x03, 0x15, 0x01, 0xac, + 0x05, 0x7b, 0x01, 0xde, 0xd1, 0xdd, 0x96, 0xf0, + 0xe1, 0x29, 0x0c, 0x42, 0x02, 0x01, 0x00, 0x01, + 0x00, 0x01, 0x03, 0x02, 0x00, 0x07, 0x01, 0xc8, + 0x06, 0x00, 0x01, 0x00, 0xea, 0x03, 0x1a, 0x01, + 0xac, 0x05, 0x7b, 0x01, 0xde, 0xd1, 0xdd, 0x96, + 0xf0, 0xe1, 0x29, 0x0c, 0x42, 0x02, 0x01, 0x00, + 0x01, 0x00, 0x01, 0x02, 0x01, 0x00, 0x09, 0x01, + 0xc8, 0x06, 0x00, 0x01, 0x00, 0xe6, 0x03, 0x18, + 0x01, 0xd1, 0x8d, 0x11, 0xeb, 0x03, 0x0e, 0xb6, + 0xe1, 0x29, 0x0c, 0x42, 0x02, 0x01, 0x00, 0x01, + 0x00, 0x01, 0x03, 0x02, 0x00, 0x07, 0x01, 0xc8, + 0x06, 0x00, 0x01, 0x00, 0xe4, 0x03, 0x17, 0x01, + 0xac, 0x05, 0x7b, 0x01, 0xde, 0xd1, 0xdd, 0x96, + 0xf0, 0xe1, 0x29, 0x0c, 0x42, 0x02, 0x01, 0x00, + 0x01, 0x00, 0x01, 0x03, 0x02, 0x00, 0x07, 0x01, + 0xc8, 0x06, 0x00, 0x01, 0x00, 0xe2, 0x03, 0x16, + 0x01, 0xac, 0x05, 0x7b, 0x01, 0xde, 0xd1, 0xdd, + 0x96, 0xf0, 0xe1, 0x29, 0x0c, 0x42, 0x02, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x00, 0x08, + 0x00, 0xdc, 0x03, 0x13, 0x01, 0xda, 0x03, 0x12, + 0x01, 0xde, 0x41, 0x56, 0x02, 0x00, 0x00, 0xe1, + 0x29, 0x0c, 0x42, 0x02, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x01, 0x02, 0x00, 0x08, 0x00, 0xdc, 0x03, + 0x13, 0x01, 0xda, 0x03, 0x12, 0x01, 0xde, 0x41, + 0x59, 0x02, 0x00, 0x00, 0xe1, 0x29, 0x0c, 0x42, + 0x02, 0x01, 0x00, 0x00, 0x00, 0x00, 0x03, 0x01, + 0x00, 0x11, 0x00, 0xc8, 0x03, 0x00, 0x0c, 0x65, + 0x00, 0x00, 0x42, 0x86, 0x01, 0x00, 0x00, 0x04, + 0x63, 0x02, 0x00, 0x00, 0x24, 0x01, 0x00, 0x29, + 0x0c, 0x42, 0x02, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x01, 0x00, 0x04, 0x00, 0xaa, 0x05, 0x7a, + 0x01, 0xdd, 0xb4, 0xef, 0x29, 0x0c, 0x43, 0x02, + 0x01, 0xb0, 0x05, 0x00, 0x00, 0x00, 0x03, 0x02, + 0x00, 0x14, 0x00, 0xc8, 0x03, 0x00, 0x0c, 0xb2, + 0x05, 0x7e, 0x01, 0x65, 0x00, 0x00, 0x42, 0x86, + 0x01, 0x00, 0x00, 0x04, 0x64, 0x02, 0x00, 0x00, + 0x24, 0x01, 0x00, 0x0e, 0xde, 0xee, 0x29, 0x0c, + 0x43, 0x02, 0x01, 0xb2, 0x05, 0x00, 0x00, 0x00, + 0x04, 0x04, 0x00, 0x0c, 0x00, 0x96, 0x05, 0x70, + 0x01, 0x8c, 0x05, 0x6b, 0x01, 0x82, 0x04, 0x26, + 0x01, 0xb4, 0x05, 0x7f, 0x01, 0xdd, 0xde, 0x04, + 0x65, 0x02, 0x00, 0x00, 0xdf, 0xf0, 0xe0, 0xf0, + 0x29, 0x0c, 0x43, 0x02, 0x01, 0xb4, 0x05, 0x01, + 0x00, 0x01, 0x02, 0x02, 0x00, 0x0a, 0x01, 0xcc, + 0x09, 0x00, 0x01, 0x00, 0xb6, 0x05, 0x80, 0x01, + 0x01, 0xb2, 0x05, 0x7e, 0x01, 0xdd, 0xd1, 0xef, + 0x96, 0xea, 0x04, 0xde, 0xee, 0x0e, 0x29, 0x0c, + 0x43, 0x02, 0x01, 0xb6, 0x05, 0x01, 0x01, 0x01, + 0x02, 0x06, 0x00, 0x3a, 0x02, 0xcc, 0x09, 0x00, + 0x01, 0x00, 0xa4, 0x06, 0x00, 0x00, 0x00, 0x80, + 0x04, 0x25, 0x01, 0xa4, 0x05, 0x77, 0x01, 0xc0, + 0x05, 0x85, 0x01, 0x01, 0xf2, 0x03, 0x1e, 0x01, + 0x82, 0x04, 0x26, 0x01, 0xb8, 0x05, 0x81, 0x01, + 0x01, 0xd1, 0x96, 0xea, 0x03, 0x09, 0x28, 0xdd, + 0xea, 0x0d, 0xdd, 0x04, 0x8d, 0x01, 0x00, 0x00, + 0x9d, 0xd1, 0x9d, 0xd5, 0xec, 0x08, 0xde, 0xd1, + 0xef, 0xea, 0x03, 0x09, 0x28, 0xdf, 0xd1, 0xef, + 0xcd, 0xb4, 0x47, 0xe4, 0xc5, 0xb5, 0x47, 0x5f, + 0x04, 0x00, 0xe0, 0xea, 0x05, 0xd1, 0xe1, 0x09, + 0x28, 0xc0, 0xe1, 0x5e, 0x05, 0x00, 0xd1, 0xef, + 0x0e, 0x0a, 0x28, 0x0c, 0x43, 0x02, 0x01, 0xb8, + 0x05, 0x01, 0x01, 0x01, 0x06, 0x06, 0x00, 0x4a, + 0x02, 0xcc, 0x09, 0x00, 0x01, 0x00, 0xce, 0x09, + 0x00, 0x00, 0x00, 0xea, 0x03, 0x1a, 0x01, 0xfc, + 0x03, 0x23, 0x01, 0xca, 0x03, 0x01, 0x0c, 0xc8, + 0x03, 0x00, 0x0c, 0xba, 0x05, 0x82, 0x01, 0x01, + 0xbc, 0x05, 0x83, 0x01, 0x01, 0xdd, 0xea, 0x09, + 0x04, 0x68, 0x02, 0x00, 0x00, 0xd1, 0x9d, 0xd5, + 0x65, 0x02, 0x00, 0x42, 0x69, 0x02, 0x00, 0x00, + 0x24, 0x00, 0x00, 0xe2, 0x38, 0xc2, 0x00, 0x00, + 0x00, 0x42, 0x18, 0x00, 0x00, 0x00, 0x65, 0x03, + 0x00, 0x41, 0x6a, 0x02, 0x00, 0x00, 0xd1, 0x0b, + 0x0a, 0x4c, 0x6b, 0x02, 0x00, 0x00, 0x0a, 0x4c, + 0x86, 0x00, 0x00, 0x00, 0x24, 0x03, 0x00, 0xcd, + 0x42, 0x80, 0x00, 0x00, 0x00, 0x5e, 0x04, 0x00, + 0x5e, 0x05, 0x00, 0x24, 0x02, 0x00, 0x29, 0x0c, + 0x43, 0x02, 0x01, 0xba, 0x05, 0x01, 0x00, 0x01, + 0x02, 0x06, 0x00, 0x27, 0x01, 0xce, 0x09, 0x00, + 0x01, 0x00, 0xfe, 0x03, 0x24, 0x01, 0xca, 0x03, + 0x01, 0x0c, 0xfc, 0x03, 0x23, 0x01, 0xa2, 0x05, + 0x76, 0x01, 0xce, 0x03, 0x00, 0x03, 0xbe, 0x05, + 0x84, 0x01, 0x01, 0xd1, 0x41, 0x41, 0x00, 0x00, + 0x00, 0xd5, 0x65, 0x01, 0x00, 0x42, 0x69, 0x02, + 0x00, 0x00, 0x24, 0x00, 0x00, 0xdf, 0x9e, 0xe1, + 0xe0, 0xd1, 0xef, 0x0e, 0x5e, 0x04, 0x00, 0xd1, + 0x43, 0x76, 0x01, 0x00, 0x00, 0x5e, 0x05, 0x00, + 0xee, 0x29, 0x0c, 0x43, 0x02, 0x01, 0xbc, 0x05, + 0x01, 0x00, 0x01, 0x04, 0x06, 0x00, 0xa1, 0x01, + 0x01, 0xd8, 0x09, 0x00, 0x01, 0x00, 0xe2, 0x03, + 0x16, 0x01, 0xc8, 0x03, 0x00, 0x0c, 0xd8, 0x03, + 0x11, 0x01, 0xdc, 0x03, 0x13, 0x01, 0xaa, 0x02, + 0x09, 0x01, 0xbe, 0x05, 0x84, 0x01, 0x01, 0xdd, + 0xea, 0x15, 0x65, 0x01, 0x00, 0x42, 0x86, 0x01, + 0x00, 0x00, 0xdf, 0xe0, 0x41, 0x6c, 0x02, 0x00, + 0x00, 0x47, 0x24, 0x01, 0x00, 0x0e, 0xd1, 0x5e, + 0x04, 0x00, 0xaa, 0xea, 0x3b, 0x65, 0x01, 0x00, + 0x42, 0x86, 0x01, 0x00, 0x00, 0xd1, 0x24, 0x01, + 0x00, 0x0e, 0x65, 0x01, 0x00, 0x42, 0x86, 0x01, + 0x00, 0x00, 0x04, 0x8d, 0x01, 0x00, 0x00, 0x24, + 0x01, 0x00, 0x0e, 0xd1, 0x41, 0x36, 0x00, 0x00, + 0x00, 0xea, 0x44, 0x65, 0x01, 0x00, 0x42, 0x86, + 0x01, 0x00, 0x00, 0xd1, 0x41, 0x36, 0x00, 0x00, + 0x00, 0x24, 0x01, 0x00, 0x0e, 0xec, 0x30, 0x65, + 0x01, 0x00, 0x42, 0x86, 0x01, 0x00, 0x00, 0x04, + 0x6d, 0x02, 0x00, 0x00, 0x24, 0x01, 0x00, 0x0e, + 0x65, 0x01, 0x00, 0x42, 0x86, 0x01, 0x00, 0x00, + 0xd1, 0x24, 0x01, 0x00, 0x0e, 0x65, 0x01, 0x00, + 0x42, 0x86, 0x01, 0x00, 0x00, 0x04, 0x8d, 0x01, + 0x00, 0x00, 0x24, 0x01, 0x00, 0x0e, 0xdd, 0xea, + 0x13, 0x65, 0x01, 0x00, 0x42, 0x86, 0x01, 0x00, + 0x00, 0xdf, 0x41, 0x87, 0x01, 0x00, 0x00, 0x24, + 0x01, 0x00, 0x0e, 0x5e, 0x05, 0x00, 0xee, 0x29, + 0x0c, 0x43, 0x02, 0x01, 0xbe, 0x05, 0x00, 0x00, + 0x00, 0x02, 0x03, 0x00, 0x11, 0x00, 0x82, 0x04, + 0x26, 0x01, 0xc8, 0x03, 0x00, 0x0c, 0xb2, 0x05, + 0x7e, 0x01, 0xb4, 0xe1, 0x65, 0x01, 0x00, 0x42, + 0x6e, 0x02, 0x00, 0x00, 0x24, 0x00, 0x00, 0x0e, + 0xdf, 0xee, 0x29, 0x0c, 0x43, 0x02, 0x01, 0xc0, + 0x05, 0x01, 0x17, 0x01, 0x04, 0x03, 0x0a, 0x84, + 0x04, 0x18, 0xf0, 0x05, 0x00, 0x01, 0x40, 0xdc, + 0x05, 0x00, 0x00, 0x40, 0xe2, 0x05, 0x00, 0x01, + 0x40, 0x84, 0x06, 0x00, 0x02, 0x40, 0x92, 0x06, + 0x00, 0x03, 0x40, 0x8a, 0x06, 0x00, 0x04, 0x40, + 0xde, 0x09, 0x00, 0x05, 0x40, 0x82, 0x04, 0x00, + 0x06, 0x00, 0xe0, 0x09, 0x00, 0x07, 0x00, 0xe2, + 0x09, 0x00, 0x08, 0x40, 0xec, 0x06, 0x00, 0x09, + 0x40, 0xe4, 0x09, 0x00, 0x0a, 0x40, 0xe6, 0x09, + 0x00, 0x0b, 0x40, 0xe8, 0x09, 0x00, 0x0c, 0x40, + 0xea, 0x09, 0x00, 0x0d, 0x00, 0xec, 0x09, 0x00, + 0x0e, 0x00, 0xee, 0x09, 0x00, 0x0f, 0x00, 0xf0, + 0x09, 0x00, 0x10, 0x00, 0xf2, 0x09, 0x00, 0x11, + 0x00, 0xf4, 0x09, 0x00, 0x12, 0x40, 0xf6, 0x09, + 0x00, 0x13, 0x40, 0xf8, 0x09, 0x00, 0x14, 0x40, + 0xfa, 0x09, 0x00, 0x15, 0x00, 0xfc, 0x09, 0x00, + 0x16, 0x00, 0xaa, 0x04, 0x3a, 0x01, 0xb0, 0x04, + 0x3d, 0x01, 0xa8, 0x04, 0x39, 0x01, 0x0c, 0x43, + 0x02, 0x01, 0xe4, 0x09, 0x01, 0x00, 0x01, 0x02, + 0x01, 0x00, 0x05, 0x01, 0xe2, 0x05, 0x00, 0x01, + 0x00, 0xde, 0x09, 0x05, 0x01, 0xdd, 0xd1, 0x9d, + 0xe1, 0x29, 0x0c, 0x43, 0x02, 0x01, 0xe6, 0x09, + 0x01, 0x00, 0x01, 0x04, 0x01, 0x00, 0x0d, 0x01, + 0xe2, 0x05, 0x00, 0x01, 0x00, 0xde, 0x09, 0x05, + 0x01, 0xdd, 0x42, 0x88, 0x01, 0x00, 0x00, 0xdd, + 0xe9, 0xb5, 0x9e, 0x25, 0x01, 0x00, 0x0c, 0x43, + 0x02, 0x01, 0xe8, 0x09, 0x01, 0x00, 0x01, 0x05, + 0x02, 0x00, 0x14, 0x01, 0xe2, 0x05, 0x00, 0x01, + 0x00, 0xe6, 0x09, 0x0b, 0x01, 0xde, 0x09, 0x05, + 0x01, 0xdd, 0xee, 0xd5, 0xde, 0x42, 0x88, 0x01, + 0x00, 0x00, 0xb4, 0xde, 0xe9, 0xb5, 0x9e, 0x24, + 0x02, 0x00, 0xe2, 0xd1, 0x28, 0x0c, 0x43, 0x02, + 0x01, 0xea, 0x09, 0x00, 0x00, 0x00, 0x03, 0x06, + 0x00, 0x49, 0x00, 0x8a, 0x06, 0x04, 0x01, 0xe4, + 0x09, 0x0a, 0x01, 0xdc, 0x05, 0x00, 0x01, 0x92, + 0x06, 0x03, 0x01, 0xf0, 0x05, 0x00, 0x03, 0xe8, + 0x09, 0x0c, 0x01, 0x04, 0x7f, 0x02, 0x00, 0x00, + 0xe1, 0xde, 0x04, 0xb3, 0x01, 0x00, 0x00, 0xef, + 0x0e, 0xdf, 0x8f, 0xe3, 0xdf, 0xe0, 0xb5, 0x9e, + 0xa6, 0xea, 0x31, 0x5e, 0x04, 0x00, 0xdf, 0x47, + 0x04, 0x7e, 0x00, 0x00, 0x00, 0xac, 0xea, 0x1f, + 0x5e, 0x04, 0x00, 0xdf, 0xb5, 0x9d, 0x47, 0x04, + 0xb3, 0x01, 0x00, 0x00, 0xac, 0xea, 0x10, 0xdf, + 0xb6, 0x9d, 0xe3, 0x5e, 0x05, 0x00, 0x04, 0xb3, + 0x01, 0x00, 0x00, 0xef, 0x0e, 0x29, 0xdf, 0x8f, + 0xe3, 0xec, 0xca, 0x29, 0x0c, 0x43, 0x02, 0x01, + 0xec, 0x09, 0x00, 0x00, 0x00, 0x02, 0x04, 0x00, + 0x1f, 0x00, 0x8a, 0x06, 0x04, 0x01, 0xdc, 0x05, + 0x00, 0x01, 0x92, 0x06, 0x03, 0x01, 0xf0, 0x05, + 0x00, 0x03, 0x04, 0x7f, 0x02, 0x00, 0x00, 0xe1, + 0xde, 0x8f, 0xe2, 0xde, 0xdf, 0xa6, 0xea, 0x11, + 0xe0, 0xde, 0x47, 0x04, 0x8d, 0x01, 0x00, 0x00, + 0xac, 0xeb, 0x06, 0xde, 0x8f, 0xe2, 0xec, 0xec, + 0x29, 0x0c, 0x43, 0x02, 0x01, 0xee, 0x09, 0x01, + 0x00, 0x01, 0x03, 0x07, 0x00, 0x4c, 0x01, 0x80, + 0x0a, 0x00, 0x01, 0x00, 0x8a, 0x06, 0x04, 0x01, + 0xe4, 0x09, 0x0a, 0x01, 0xdc, 0x05, 0x00, 0x01, + 0x92, 0x06, 0x03, 0x01, 0xe2, 0x05, 0x01, 0x01, + 0xf0, 0x05, 0x00, 0x03, 0xe8, 0x09, 0x0c, 0x01, + 0x04, 0x49, 0x00, 0x00, 0x00, 0xe1, 0xde, 0xd1, + 0xef, 0x0e, 0xdf, 0xe0, 0xa6, 0xea, 0x3d, 0x5e, + 0x05, 0x00, 0xdf, 0x91, 0xe3, 0x47, 0x60, 0x04, + 0x00, 0x04, 0x8d, 0x01, 0x00, 0x00, 0xac, 0xea, + 0x09, 0x04, 0x6c, 0x02, 0x00, 0x00, 0xe1, 0xec, + 0xe2, 0x5e, 0x04, 0x00, 0x04, 0xab, 0x01, 0x00, + 0x00, 0xac, 0xea, 0x0b, 0xdf, 0xe0, 0xa9, 0xeb, + 0x13, 0xdf, 0x8f, 0xe3, 0xec, 0xcd, 0x5e, 0x04, + 0x00, 0xd1, 0xac, 0xea, 0xc6, 0x5e, 0x06, 0x00, + 0xee, 0x0e, 0x29, 0x29, 0x0c, 0x43, 0x02, 0x01, + 0xf0, 0x09, 0x00, 0x00, 0x00, 0x03, 0x09, 0x00, + 0xc4, 0x01, 0x00, 0x8a, 0x06, 0x04, 0x01, 0xe4, + 0x09, 0x0a, 0x01, 0xdc, 0x05, 0x00, 0x01, 0x92, + 0x06, 0x03, 0x01, 0xe2, 0x05, 0x01, 0x01, 0xf0, + 0x05, 0x00, 0x03, 0xe6, 0x09, 0x0b, 0x01, 0xe8, + 0x09, 0x0c, 0x01, 0xaa, 0x04, 0x00, 0x00, 0x04, + 0x2a, 0x02, 0x00, 0x00, 0xe1, 0xde, 0x04, 0xb3, + 0x01, 0x00, 0x00, 0xef, 0x0e, 0xdf, 0xe0, 0xa6, + 0x69, 0xb1, 0x00, 0x00, 0x00, 0x5e, 0x05, 0x00, + 0xdf, 0x91, 0xe3, 0x47, 0x60, 0x04, 0x00, 0x04, + 0x8d, 0x01, 0x00, 0x00, 0xac, 0xea, 0x09, 0x04, + 0x6c, 0x02, 0x00, 0x00, 0xe1, 0xec, 0xdf, 0x5e, + 0x04, 0x00, 0x04, 0xab, 0x01, 0x00, 0x00, 0xac, + 0xea, 0x0b, 0xdf, 0xe0, 0xa6, 0xea, 0xcf, 0xdf, + 0x8f, 0xe3, 0xec, 0xca, 0x5e, 0x06, 0x00, 0xee, + 0x04, 0xd5, 0x01, 0x00, 0x00, 0xac, 0xea, 0x13, + 0x5e, 0x04, 0x00, 0x04, 0xb2, 0x01, 0x00, 0x00, + 0xac, 0xea, 0xb3, 0x5e, 0x07, 0x00, 0xee, 0x0e, + 0xec, 0xac, 0x5e, 0x04, 0x00, 0x04, 0xd5, 0x01, + 0x00, 0x00, 0xac, 0xea, 0x2e, 0xde, 0x04, 0xd5, + 0x01, 0x00, 0x00, 0xef, 0x0e, 0x5e, 0x05, 0x00, + 0xdf, 0x47, 0x04, 0xd5, 0x01, 0x00, 0x00, 0xac, + 0x11, 0xeb, 0x0d, 0x0e, 0x5e, 0x05, 0x00, 0xdf, + 0x47, 0x04, 0xb2, 0x01, 0x00, 0x00, 0xac, 0x69, + 0x7d, 0xff, 0xff, 0xff, 0xdf, 0x8f, 0xe3, 0xed, + 0x75, 0xff, 0x5e, 0x04, 0x00, 0x04, 0xb3, 0x01, + 0x00, 0x00, 0xac, 0x69, 0x69, 0xff, 0xff, 0xff, + 0x5e, 0x07, 0x00, 0xee, 0x0e, 0xdf, 0xe0, 0xa6, + 0xea, 0x11, 0x5e, 0x08, 0x00, 0x5e, 0x05, 0x00, + 0xdf, 0x47, 0xef, 0xea, 0x06, 0xdf, 0x8f, 0xe3, + 0xec, 0xec, 0x29, 0x0c, 0x43, 0x02, 0x01, 0xf2, + 0x09, 0x00, 0x00, 0x00, 0x03, 0x05, 0x00, 0x41, + 0x00, 0x8a, 0x06, 0x04, 0x01, 0xdc, 0x05, 0x00, + 0x01, 0x92, 0x06, 0x03, 0x01, 0xaa, 0x04, 0x00, + 0x00, 0xf0, 0x05, 0x00, 0x03, 0x04, 0x47, 0x00, + 0x00, 0x00, 0xe1, 0xde, 0xdf, 0xa6, 0xea, 0x36, + 0xe0, 0x5e, 0x04, 0x00, 0xde, 0x47, 0xef, 0x11, + 0xeb, 0x25, 0x0e, 0x5e, 0x04, 0x00, 0xde, 0x47, + 0x04, 0xac, 0x01, 0x00, 0x00, 0xac, 0xea, 0x1e, + 0xde, 0xdf, 0xb5, 0x9e, 0xac, 0x11, 0xeb, 0x0f, + 0x0e, 0x5e, 0x04, 0x00, 0xde, 0xb5, 0x9d, 0x47, + 0x04, 0xac, 0x01, 0x00, 0x00, 0xad, 0xea, 0x06, + 0xde, 0x8f, 0xe2, 0xec, 0xc7, 0x29, 0x0c, 0x43, + 0x02, 0x01, 0xfa, 0x09, 0x00, 0x03, 0x00, 0x04, + 0x0a, 0x00, 0x95, 0x02, 0x03, 0xc8, 0x06, 0x00, + 0x00, 0x00, 0xe2, 0x08, 0x00, 0x01, 0x00, 0x82, + 0x0a, 0x00, 0x02, 0x00, 0xe2, 0x09, 0x08, 0x01, + 0xdc, 0x05, 0x00, 0x01, 0x92, 0x06, 0x03, 0x01, + 0xaa, 0x04, 0x00, 0x00, 0xf0, 0x05, 0x00, 0x03, + 0x84, 0x06, 0x02, 0x01, 0xf4, 0x09, 0x12, 0x01, + 0x8a, 0x06, 0x04, 0x01, 0xf6, 0x09, 0x13, 0x01, + 0xf8, 0x09, 0x14, 0x01, 0xb5, 0xe1, 0xde, 0xdf, + 0xa6, 0xea, 0x0f, 0xe0, 0x5e, 0x04, 0x00, 0xde, + 0x47, 0xef, 0xea, 0x06, 0xde, 0x8f, 0xe2, 0xec, + 0xee, 0x5e, 0x04, 0x00, 0x42, 0x88, 0x01, 0x00, + 0x00, 0x5e, 0x05, 0x00, 0xde, 0x24, 0x02, 0x00, + 0xc9, 0x04, 0x82, 0x02, 0x00, 0x00, 0xc5, 0x9d, + 0x04, 0x82, 0x02, 0x00, 0x00, 0x9d, 0xca, 0x5e, + 0x06, 0x00, 0x42, 0xae, 0x01, 0x00, 0x00, 0xc6, + 0x24, 0x01, 0x00, 0xb4, 0xa9, 0xea, 0x7c, 0x04, + 0x30, 0x02, 0x00, 0x00, 0x5f, 0x07, 0x00, 0xc5, + 0x04, 0x03, 0x00, 0x00, 0x00, 0xae, 0x11, 0xeb, + 0x09, 0x0e, 0xc5, 0x04, 0x02, 0x00, 0x00, 0x00, + 0xae, 0xea, 0x0b, 0x04, 0x48, 0x00, 0x00, 0x00, + 0x5f, 0x07, 0x00, 0xec, 0x43, 0xc5, 0x04, 0x03, + 0x00, 0x00, 0x00, 0xae, 0x11, 0xeb, 0x09, 0x0e, + 0xc5, 0x04, 0x02, 0x00, 0x00, 0x00, 0xae, 0xea, + 0x0b, 0x04, 0x48, 0x00, 0x00, 0x00, 0x5f, 0x07, + 0x00, 0xec, 0x25, 0xc5, 0x04, 0x01, 0x00, 0x00, + 0x00, 0xae, 0xea, 0x0b, 0x04, 0x01, 0x00, 0x00, + 0x00, 0x5f, 0x07, 0x00, 0xec, 0x12, 0xc5, 0x04, + 0x46, 0x00, 0x00, 0x00, 0xae, 0xea, 0x09, 0x04, + 0x46, 0x00, 0x00, 0x00, 0x5f, 0x07, 0x00, 0x5e, + 0x08, 0x00, 0x42, 0xae, 0x01, 0x00, 0x00, 0xc6, + 0x24, 0x01, 0x00, 0xb4, 0xa9, 0xea, 0x03, 0xb4, + 0xe1, 0x29, 0xde, 0xcb, 0xc7, 0xdf, 0xa6, 0xea, + 0x12, 0x5e, 0x04, 0x00, 0xc7, 0x47, 0x04, 0xd0, + 0x01, 0x00, 0x00, 0xac, 0xea, 0x05, 0x93, 0x02, + 0xec, 0xeb, 0xc7, 0xdf, 0xa6, 0xea, 0x17, 0x5e, + 0x04, 0x00, 0xc7, 0x47, 0x04, 0xc7, 0x01, 0x00, + 0x00, 0xac, 0xea, 0x0a, 0x04, 0x1b, 0x00, 0x00, + 0x00, 0x5f, 0x07, 0x00, 0x29, 0x5e, 0x09, 0x00, + 0x42, 0xae, 0x01, 0x00, 0x00, 0xc6, 0x24, 0x01, + 0x00, 0xb4, 0xa9, 0xea, 0x0a, 0x04, 0x01, 0x02, + 0x00, 0x00, 0x5f, 0x07, 0x00, 0x29, 0x04, 0x83, + 0x02, 0x00, 0x00, 0x5f, 0x07, 0x00, 0xb4, 0xe1, + 0x29, 0x0c, 0x43, 0x02, 0x01, 0xfc, 0x09, 0x02, + 0x00, 0x02, 0x03, 0x02, 0x00, 0x2b, 0x02, 0xf6, + 0x01, 0x00, 0x01, 0x00, 0x88, 0x0a, 0x00, 0x01, + 0x00, 0xec, 0x06, 0x09, 0x01, 0x8a, 0x06, 0x04, + 0x01, 0xdd, 0xe9, 0xd1, 0xa6, 0xea, 0x12, 0xdd, + 0x42, 0x9b, 0x01, 0x00, 0x00, 0x04, 0x16, 0x00, + 0x00, 0x00, 0x24, 0x01, 0x00, 0x0e, 0xec, 0xea, + 0xdd, 0xe9, 0xd2, 0xa6, 0xea, 0x0e, 0xdd, 0x42, + 0x9b, 0x01, 0x00, 0x00, 0xde, 0x24, 0x01, 0x00, + 0x0e, 0xec, 0xee, 0x29, 0xbf, 0x00, 0xc2, 0x0a, + 0xbf, 0x01, 0xc2, 0x0b, 0xbf, 0x02, 0xc2, 0x0c, + 0xbf, 0x03, 0xc2, 0x0d, 0xbf, 0x04, 0xc2, 0x0e, + 0xbf, 0x05, 0xc2, 0x0f, 0xbf, 0x06, 0xc2, 0x10, + 0xbf, 0x07, 0xc2, 0x11, 0xbf, 0x08, 0xc2, 0x15, + 0xbf, 0x09, 0xc2, 0x16, 0xd1, 0xe9, 0xcc, 0xc0, + 0xc2, 0x05, 0xb4, 0xc2, 0x06, 0xb5, 0xc2, 0x08, + 0x26, 0x00, 0x00, 0xc2, 0x09, 0x04, 0x82, 0x02, + 0x00, 0x00, 0x04, 0x85, 0x02, 0x00, 0x00, 0x9d, + 0x04, 0x86, 0x02, 0x00, 0x00, 0x9d, 0x04, 0x87, + 0x02, 0x00, 0x00, 0x9d, 0x04, 0x88, 0x02, 0x00, + 0x00, 0x9d, 0x04, 0x89, 0x02, 0x00, 0x00, 0x9d, + 0x04, 0x8a, 0x02, 0x00, 0x00, 0x9d, 0x04, 0x8b, + 0x02, 0x00, 0x00, 0x9d, 0x04, 0x8c, 0x02, 0x00, + 0x00, 0x9d, 0x04, 0x8d, 0x02, 0x00, 0x00, 0x9d, + 0xc2, 0x12, 0x04, 0x8e, 0x02, 0x00, 0x00, 0xc2, + 0x13, 0x04, 0x8f, 0x02, 0x00, 0x00, 0xc2, 0x14, + 0xb4, 0xc9, 0xc5, 0xc8, 0xa6, 0x69, 0x6a, 0x01, + 0x00, 0x00, 0x07, 0xc2, 0x04, 0xc5, 0xcb, 0xd1, + 0xc5, 0x91, 0xc9, 0x47, 0xce, 0x11, 0x04, 0xd0, + 0x01, 0x00, 0x00, 0xae, 0xeb, 0x1c, 0x11, 0x04, + 0x90, 0x02, 0x00, 0x00, 0xae, 0xeb, 0x13, 0x11, + 0x04, 0x91, 0x02, 0x00, 0x00, 0xae, 0xeb, 0x0a, + 0x11, 0x04, 0x8d, 0x01, 0x00, 0x00, 0xae, 0xea, + 0x04, 0x0e, 0xec, 0xc7, 0x11, 0x04, 0x92, 0x02, + 0x00, 0x00, 0xae, 0xeb, 0x0a, 0x11, 0x04, 0xdb, + 0x01, 0x00, 0x00, 0xae, 0xea, 0x18, 0xc5, 0xc8, + 0xa6, 0xea, 0x0d, 0xd1, 0xc5, 0x47, 0xc6, 0xac, + 0xea, 0x06, 0x93, 0x00, 0x0e, 0xec, 0xa4, 0xb5, + 0xc2, 0x08, 0x0e, 0xec, 0x9e, 0x11, 0x04, 0xb3, + 0x01, 0x00, 0x00, 0xae, 0xea, 0x44, 0xc5, 0xc8, + 0xa6, 0xea, 0x13, 0xd1, 0xc5, 0x47, 0x04, 0x7e, + 0x00, 0x00, 0x00, 0xac, 0xea, 0x08, 0xc1, 0x0d, + 0xee, 0x0e, 0xed, 0xdc, 0x00, 0xc5, 0xc8, 0xa6, + 0xea, 0x13, 0xd1, 0xc5, 0x47, 0x04, 0xb3, 0x01, + 0x00, 0x00, 0xac, 0xea, 0x08, 0xc1, 0x0e, 0xee, + 0x0e, 0xed, 0xc5, 0x00, 0xc1, 0x08, 0xea, 0x0b, + 0xc1, 0x10, 0xee, 0x0e, 0xb4, 0xc2, 0x08, 0xed, + 0xb7, 0x00, 0xb5, 0xc2, 0x08, 0x0e, 0xed, 0x53, + 0xff, 0x11, 0x04, 0xaf, 0x01, 0x00, 0x00, 0xae, + 0xeb, 0x13, 0x11, 0x04, 0xb0, 0x01, 0x00, 0x00, + 0xae, 0xeb, 0x0a, 0x11, 0x04, 0xb1, 0x01, 0x00, + 0x00, 0xae, 0xea, 0x0c, 0xc1, 0x0f, 0xc6, 0xef, + 0x0e, 0xb4, 0xc2, 0x08, 0xed, 0x8a, 0x00, 0x11, + 0x04, 0xc7, 0x01, 0x00, 0x00, 0xae, 0xeb, 0x13, + 0x11, 0x04, 0xd5, 0x01, 0x00, 0x00, 0xae, 0xeb, + 0x0a, 0x11, 0x04, 0x19, 0x02, 0x00, 0x00, 0xae, + 0xea, 0x0f, 0xb5, 0xc2, 0x08, 0x93, 0x06, 0xc1, + 0x0a, 0xc6, 0xef, 0x0e, 0x0e, 0xed, 0x04, 0xff, + 0x11, 0x04, 0xc8, 0x01, 0x00, 0x00, 0xae, 0xeb, + 0x13, 0x11, 0x04, 0xb2, 0x01, 0x00, 0x00, 0xae, + 0xeb, 0x0a, 0x11, 0x04, 0x1a, 0x02, 0x00, 0x00, + 0xae, 0xea, 0x25, 0xb4, 0xc2, 0x08, 0xc1, 0x06, + 0xb4, 0xa8, 0xea, 0x13, 0xde, 0xc1, 0x0b, 0xee, + 0xc6, 0xf0, 0xea, 0x0b, 0x92, 0x06, 0xc1, 0x0c, + 0xee, 0x0e, 0x0e, 0xed, 0xce, 0xfe, 0x04, 0x6c, + 0x02, 0x00, 0x00, 0xc2, 0x04, 0xec, 0x21, 0xdf, + 0xc6, 0xef, 0xea, 0x0a, 0xc1, 0x11, 0xee, 0x0e, + 0xb4, 0xc2, 0x08, 0xec, 0x13, 0xdd, 0xc6, 0xef, + 0xea, 0x07, 0xc1, 0x15, 0xee, 0x0e, 0xec, 0x08, + 0xb5, 0xc2, 0x08, 0x0e, 0xed, 0xa5, 0xfe, 0x0e, + 0xc1, 0x04, 0x69, 0x9f, 0xfe, 0xff, 0xff, 0xc1, + 0x16, 0xc7, 0xc5, 0xf0, 0x0e, 0xed, 0x94, 0xfe, + 0xc1, 0x16, 0xc8, 0xc8, 0xf0, 0x0e, 0xc1, 0x05, + 0xc1, 0x06, 0xc1, 0x09, 0x26, 0x03, 0x00, 0x28, + 0x0c, 0x43, 0x02, 0x01, 0xc2, 0x05, 0x01, 0x00, + 0x01, 0x03, 0x01, 0x00, 0x36, 0x01, 0xc8, 0x06, + 0x00, 0x01, 0x00, 0xc8, 0x03, 0x00, 0x0c, 0x65, + 0x00, 0x00, 0x42, 0x93, 0x02, 0x00, 0x00, 0x04, + 0x94, 0x02, 0x00, 0x00, 0x24, 0x01, 0x00, 0x11, + 0xeb, 0x1b, 0x0e, 0x65, 0x00, 0x00, 0x42, 0x93, + 0x02, 0x00, 0x00, 0x04, 0x95, 0x02, 0x00, 0x00, + 0x24, 0x01, 0x00, 0x11, 0xeb, 0x07, 0x0e, 0x04, + 0xac, 0x01, 0x00, 0x00, 0x04, 0xb3, 0x01, 0x00, + 0x00, 0x9d, 0xd1, 0x9d, 0x28, 0x0c, 0x43, 0x02, + 0x01, 0xc4, 0x05, 0x00, 0x03, 0x00, 0x05, 0x03, + 0x00, 0x67, 0x03, 0xc8, 0x06, 0x00, 0x00, 0x00, + 0xac, 0x0a, 0x00, 0x01, 0x00, 0xbc, 0x09, 0x05, + 0x00, 0x03, 0xec, 0x03, 0x1b, 0x01, 0xc8, 0x03, + 0x00, 0x0c, 0xc2, 0x05, 0x86, 0x01, 0x01, 0xdd, + 0x42, 0xa7, 0x01, 0x00, 0x00, 0xbd, 0x18, 0xfc, + 0x24, 0x01, 0x00, 0x42, 0x5c, 0x00, 0x00, 0x00, + 0x04, 0x8d, 0x01, 0x00, 0x00, 0x24, 0x01, 0x00, + 0x42, 0x43, 0x02, 0x00, 0x00, 0x24, 0x00, 0x00, + 0xcd, 0xea, 0x43, 0x6c, 0x38, 0x00, 0x00, 0x00, + 0x65, 0x01, 0x00, 0x42, 0x97, 0x02, 0x00, 0x00, + 0xdf, 0x04, 0x98, 0x02, 0x00, 0x00, 0xef, 0x04, + 0x31, 0x02, 0x00, 0x00, 0x24, 0x02, 0x00, 0xce, + 0x42, 0x86, 0x01, 0x00, 0x00, 0xc5, 0x04, 0x8d, + 0x01, 0x00, 0x00, 0x9d, 0x24, 0x01, 0x00, 0x0e, + 0xc6, 0x42, 0x99, 0x02, 0x00, 0x00, 0x24, 0x00, + 0x00, 0x0e, 0x0e, 0x29, 0xcb, 0x6c, 0x06, 0x00, + 0x00, 0x00, 0x0e, 0x29, 0x2f, 0x29, 0x0c, 0x43, + 0x02, 0x01, 0xc6, 0x05, 0x00, 0x01, 0x00, 0x04, + 0x04, 0x00, 0x2f, 0x01, 0xe8, 0x05, 0x00, 0x00, + 0x00, 0xc8, 0x03, 0x00, 0x0c, 0xc2, 0x05, 0x86, + 0x01, 0x01, 0xec, 0x03, 0x1b, 0x01, 0xee, 0x03, + 0x1c, 0x01, 0x65, 0x00, 0x00, 0x42, 0x9a, 0x02, + 0x00, 0x00, 0xde, 0x04, 0x98, 0x02, 0x00, 0x00, + 0xef, 0x24, 0x01, 0x00, 0xcd, 0xea, 0x1a, 0xc5, + 0x42, 0x43, 0x02, 0x00, 0x00, 0x24, 0x00, 0x00, + 0x42, 0x5e, 0x00, 0x00, 0x00, 0x04, 0x8d, 0x01, + 0x00, 0x00, 0x24, 0x01, 0x00, 0xe7, 0xe9, 0xe4, + 0x29, 0x0c, 0x43, 0x02, 0x01, 0xc8, 0x05, 0x00, + 0x02, 0x00, 0x04, 0x04, 0x02, 0x51, 0x02, 0x8c, + 0x07, 0x00, 0x00, 0x00, 0xc8, 0x06, 0x00, 0x01, + 0x00, 0xc8, 0x03, 0x00, 0x0c, 0xdc, 0x03, 0x13, + 0x01, 0xda, 0x03, 0x12, 0x01, 0xe2, 0x03, 0x16, + 0x01, 0x07, 0x16, 0x28, 0x5c, 0x64, 0x2b, 0x29, + 0x3b, 0x28, 0x5c, 0x64, 0x2b, 0x29, 0x07, 0xa8, + 0x01, 0x00, 0x00, 0x03, 0x00, 0x4c, 0x00, 0x00, + 0x00, 0x09, 0x06, 0x00, 0x00, 0x00, 0x05, 0x08, + 0xf5, 0xff, 0xff, 0xff, 0x0c, 0x00, 0x0c, 0x01, + 0x1d, 0x08, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, + 0x00, 0xff, 0xff, 0xff, 0x7f, 0x01, 0x00, 0x00, + 0x00, 0x16, 0x01, 0x00, 0x30, 0x00, 0x39, 0x00, + 0x0b, 0x0d, 0x01, 0x01, 0x3b, 0x0c, 0x02, 0x1d, + 0x08, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xff, 0x7f, 0x01, 0x00, 0x00, 0x00, + 0x16, 0x01, 0x00, 0x30, 0x00, 0x39, 0x00, 0x0b, + 0x0d, 0x02, 0x0d, 0x00, 0x0b, 0x65, 0x00, 0x00, + 0x42, 0x93, 0x02, 0x00, 0x00, 0x04, 0x9b, 0x02, + 0x00, 0x00, 0x24, 0x01, 0x00, 0xce, 0xea, 0x21, + 0xc6, 0x42, 0xb4, 0x01, 0x00, 0x00, 0xbe, 0x00, + 0xbe, 0x01, 0x33, 0x24, 0x01, 0x00, 0xcd, 0xea, + 0x10, 0xc5, 0xb6, 0x47, 0x8d, 0xb4, 0xaf, 0xea, + 0x08, 0xdf, 0x41, 0x59, 0x02, 0x00, 0x00, 0xe2, + 0x65, 0x00, 0x00, 0x42, 0x93, 0x02, 0x00, 0x00, + 0x04, 0x9c, 0x02, 0x00, 0x00, 0x24, 0x01, 0x00, + 0xce, 0xea, 0x0b, 0xc6, 0xb4, 0x47, 0x8d, 0xb4, + 0xaf, 0xea, 0x03, 0x09, 0xe4, 0x29, 0xbf, 0x00, + 0xc2, 0x34, 0xbf, 0x01, 0xc2, 0x35, 0xbf, 0x02, + 0xc2, 0x36, 0xbf, 0x03, 0xc2, 0x37, 0xbf, 0x04, + 0xc2, 0x38, 0xbf, 0x05, 0xc2, 0x39, 0xbf, 0x06, + 0xc2, 0x3a, 0xbf, 0x07, 0xc2, 0x3b, 0xbf, 0x08, + 0xc2, 0x3c, 0xbf, 0x09, 0xc2, 0x3d, 0xbf, 0x0a, + 0xc2, 0x3e, 0xbf, 0x0b, 0xc2, 0x3f, 0xbf, 0x0c, + 0xc2, 0x40, 0xbf, 0x0d, 0xc2, 0x41, 0xbf, 0x0e, + 0xc2, 0x42, 0xbf, 0x0f, 0xc2, 0x43, 0xbf, 0x10, + 0xc2, 0x44, 0xbf, 0x11, 0xc2, 0x45, 0xbf, 0x12, + 0xc2, 0x46, 0xbf, 0x13, 0xc2, 0x47, 0xbf, 0x14, + 0xc2, 0x48, 0xbf, 0x15, 0xc2, 0x49, 0xbf, 0x16, + 0xc2, 0x4a, 0xbf, 0x17, 0xc2, 0x4b, 0xbf, 0x18, + 0xc2, 0x4c, 0xbf, 0x19, 0xc2, 0x4d, 0xbf, 0x1a, + 0xc2, 0x4e, 0xbf, 0x1b, 0xc2, 0x4f, 0xbf, 0x1c, + 0xc2, 0x50, 0xbf, 0x1d, 0xc2, 0x51, 0xbf, 0x1e, + 0xc2, 0x52, 0xbf, 0x1f, 0xc2, 0x53, 0xbf, 0x20, + 0xc2, 0x54, 0xbf, 0x21, 0xc2, 0x55, 0xbf, 0x22, + 0xc2, 0x56, 0xbf, 0x23, 0xc2, 0x57, 0xbf, 0x24, + 0xc2, 0x58, 0xbf, 0x25, 0xc2, 0x59, 0xbf, 0x26, + 0xc2, 0x5a, 0xbf, 0x27, 0xc2, 0x5b, 0xbf, 0x28, + 0xc2, 0x5c, 0xbf, 0x29, 0xc2, 0x5d, 0xbf, 0x2a, + 0xc2, 0x5e, 0xbf, 0x2b, 0xc2, 0x5f, 0xbf, 0x2c, + 0xc2, 0x60, 0xbf, 0x2d, 0xc2, 0x61, 0xbf, 0x2e, + 0xc2, 0x62, 0xbf, 0x2f, 0xc2, 0x63, 0xbf, 0x30, + 0xc2, 0x64, 0xbf, 0x31, 0xc2, 0x65, 0xbf, 0x32, + 0xc2, 0x66, 0xbf, 0x33, 0xc2, 0x67, 0xbf, 0x34, + 0xc2, 0x68, 0xbf, 0x35, 0xc2, 0x69, 0xbf, 0x36, + 0xc2, 0x6b, 0xbf, 0x37, 0xc2, 0x6f, 0xbf, 0x38, + 0xc2, 0x70, 0xbf, 0x39, 0xc2, 0x71, 0xbf, 0x3a, + 0xc2, 0x72, 0xbf, 0x3b, 0xc2, 0x73, 0xbf, 0x3c, + 0xc2, 0x74, 0xbf, 0x3e, 0xc2, 0x76, 0xbf, 0x3f, + 0xc2, 0x77, 0xbf, 0x40, 0xc2, 0x78, 0xbf, 0x41, + 0xc2, 0x79, 0xbf, 0x42, 0xc2, 0x7a, 0xbf, 0x43, + 0xc2, 0x7b, 0xbf, 0x4f, 0xc2, 0x7d, 0xbf, 0x50, + 0xc2, 0x7e, 0xbf, 0x51, 0xc2, 0x7f, 0xbf, 0x52, + 0xc2, 0x80, 0xbf, 0x53, 0xc2, 0x81, 0xbf, 0x54, + 0xc2, 0x82, 0xbf, 0x55, 0xc2, 0x83, 0xbf, 0x56, + 0xc2, 0x84, 0xbf, 0x57, 0xc2, 0x85, 0xbf, 0x58, + 0xc2, 0x86, 0xbf, 0x59, 0xc2, 0x87, 0xbf, 0x5a, + 0xc2, 0x88, 0xbf, 0x5b, 0xc2, 0x89, 0xd1, 0x65, + 0x02, 0x00, 0x43, 0xe6, 0x00, 0x00, 0x00, 0xd1, + 0x65, 0x01, 0x00, 0x43, 0xe5, 0x00, 0x00, 0x00, + 0xd1, 0x65, 0x00, 0x00, 0x43, 0xe4, 0x00, 0x00, + 0x00, 0xd1, 0x41, 0x93, 0x00, 0x00, 0x00, 0xc9, + 0xd1, 0x41, 0x97, 0x00, 0x00, 0x00, 0xca, 0xd1, + 0x41, 0x96, 0x00, 0x00, 0x00, 0xcb, 0xd1, 0x41, + 0x98, 0x00, 0x00, 0x00, 0xcc, 0xd1, 0x41, 0xb1, + 0x00, 0x00, 0x00, 0xc2, 0x04, 0xd1, 0x41, 0xa6, + 0x00, 0x00, 0x00, 0xc2, 0x05, 0xd1, 0x41, 0x94, + 0x00, 0x00, 0x00, 0xc2, 0x06, 0xd1, 0x41, 0x9d, + 0x00, 0x00, 0x00, 0xc2, 0x07, 0xd1, 0x41, 0xa1, + 0x00, 0x00, 0x00, 0xc2, 0x08, 0xd1, 0x41, 0x95, + 0x00, 0x00, 0x00, 0xc2, 0x09, 0xd1, 0x41, 0x99, + 0x00, 0x00, 0x00, 0xc2, 0x0a, 0xd1, 0x41, 0x9b, + 0x00, 0x00, 0x00, 0xc2, 0x0b, 0xd1, 0x41, 0x9c, + 0x00, 0x00, 0x00, 0xc2, 0x0c, 0xd1, 0x41, 0xe8, + 0x00, 0x00, 0x00, 0xc2, 0x0d, 0xd1, 0x41, 0xe9, + 0x00, 0x00, 0x00, 0xc2, 0x0e, 0xd1, 0x41, 0xea, + 0x00, 0x00, 0x00, 0xc2, 0x0f, 0xd1, 0x41, 0xeb, + 0x00, 0x00, 0x00, 0xc2, 0x10, 0x0b, 0x04, 0x9d, + 0x02, 0x00, 0x00, 0x4c, 0x87, 0x01, 0x00, 0x00, + 0x04, 0x9e, 0x02, 0x00, 0x00, 0x4c, 0x9f, 0x02, + 0x00, 0x00, 0x04, 0xa0, 0x02, 0x00, 0x00, 0x4c, + 0xa1, 0x02, 0x00, 0x00, 0x04, 0xa2, 0x02, 0x00, + 0x00, 0x4c, 0xa3, 0x02, 0x00, 0x00, 0x04, 0xa4, + 0x02, 0x00, 0x00, 0x4c, 0xa5, 0x02, 0x00, 0x00, + 0x04, 0xa6, 0x02, 0x00, 0x00, 0x4c, 0xa7, 0x02, + 0x00, 0x00, 0x04, 0xa8, 0x02, 0x00, 0x00, 0x4c, + 0xa9, 0x02, 0x00, 0x00, 0x04, 0xaa, 0x02, 0x00, + 0x00, 0x4c, 0xab, 0x02, 0x00, 0x00, 0x04, 0xac, + 0x02, 0x00, 0x00, 0x4c, 0xad, 0x02, 0x00, 0x00, + 0x04, 0xae, 0x02, 0x00, 0x00, 0x4c, 0xaf, 0x02, + 0x00, 0x00, 0x04, 0xae, 0x02, 0x00, 0x00, 0x4c, + 0xb0, 0x02, 0x00, 0x00, 0x04, 0xb1, 0x02, 0x00, + 0x00, 0x4c, 0xb2, 0x02, 0x00, 0x00, 0x04, 0xb3, + 0x02, 0x00, 0x00, 0x4c, 0xb4, 0x02, 0x00, 0x00, + 0x04, 0xb5, 0x02, 0x00, 0x00, 0x4c, 0xb6, 0x02, + 0x00, 0x00, 0x04, 0xb7, 0x02, 0x00, 0x00, 0x4c, + 0xb8, 0x02, 0x00, 0x00, 0x04, 0xb9, 0x02, 0x00, + 0x00, 0x4c, 0xba, 0x02, 0x00, 0x00, 0x04, 0xbb, + 0x02, 0x00, 0x00, 0x4c, 0xbc, 0x02, 0x00, 0x00, + 0x04, 0xbd, 0x02, 0x00, 0x00, 0x4c, 0xbe, 0x02, + 0x00, 0x00, 0xc2, 0x11, 0x0b, 0x0b, 0x04, 0xab, + 0x02, 0x00, 0x00, 0x4c, 0x2b, 0x02, 0x00, 0x00, + 0x04, 0xbe, 0x02, 0x00, 0x00, 0x4c, 0x48, 0x00, + 0x00, 0x00, 0x04, 0xad, 0x02, 0x00, 0x00, 0x4c, + 0x7f, 0x02, 0x00, 0x00, 0x04, 0xa9, 0x02, 0x00, + 0x00, 0x4c, 0x2e, 0x02, 0x00, 0x00, 0x04, 0xb4, + 0x02, 0x00, 0x00, 0x4c, 0x16, 0x00, 0x00, 0x00, + 0x04, 0xb2, 0x02, 0x00, 0x00, 0x4c, 0x6c, 0x02, + 0x00, 0x00, 0x04, 0xb6, 0x02, 0x00, 0x00, 0x4c, + 0x1b, 0x00, 0x00, 0x00, 0x04, 0xb4, 0x02, 0x00, + 0x00, 0x4c, 0x83, 0x02, 0x00, 0x00, 0x04, 0xbe, + 0x02, 0x00, 0x00, 0x4c, 0x30, 0x02, 0x00, 0x00, + 0x04, 0xbe, 0x02, 0x00, 0x00, 0x4c, 0x01, 0x00, + 0x00, 0x00, 0x04, 0xa3, 0x02, 0x00, 0x00, 0x4c, + 0x47, 0x00, 0x00, 0x00, 0x04, 0xad, 0x02, 0x00, + 0x00, 0x4c, 0x2d, 0x02, 0x00, 0x00, 0x04, 0xad, + 0x02, 0x00, 0x00, 0x4c, 0x28, 0x02, 0x00, 0x00, + 0x04, 0xab, 0x02, 0x00, 0x00, 0x4c, 0x2a, 0x02, + 0x00, 0x00, 0x04, 0xbc, 0x02, 0x00, 0x00, 0x4c, + 0x49, 0x00, 0x00, 0x00, 0x04, 0xbe, 0x02, 0x00, + 0x00, 0x4c, 0x4b, 0x00, 0x00, 0x00, 0x04, 0xba, + 0x02, 0x00, 0x00, 0x4c, 0x01, 0x02, 0x00, 0x00, + 0x04, 0xbe, 0x02, 0x00, 0x00, 0x4c, 0x46, 0x00, + 0x00, 0x00, 0x4c, 0x56, 0x02, 0x00, 0x00, 0x0b, + 0x04, 0xab, 0x02, 0x00, 0x00, 0x4c, 0x2b, 0x02, + 0x00, 0x00, 0x04, 0xba, 0x02, 0x00, 0x00, 0x4c, + 0x48, 0x00, 0x00, 0x00, 0x04, 0xb0, 0x02, 0x00, + 0x00, 0x4c, 0x7f, 0x02, 0x00, 0x00, 0x04, 0xa9, + 0x02, 0x00, 0x00, 0x4c, 0x2e, 0x02, 0x00, 0x00, + 0x04, 0x9f, 0x02, 0x00, 0x00, 0x4c, 0x16, 0x00, + 0x00, 0x00, 0x04, 0xa1, 0x02, 0x00, 0x00, 0x4c, + 0x6c, 0x02, 0x00, 0x00, 0x04, 0xb6, 0x02, 0x00, + 0x00, 0x4c, 0x1b, 0x00, 0x00, 0x00, 0x04, 0x9f, + 0x02, 0x00, 0x00, 0x4c, 0x83, 0x02, 0x00, 0x00, + 0x04, 0xba, 0x02, 0x00, 0x00, 0x4c, 0x30, 0x02, + 0x00, 0x00, 0x04, 0xba, 0x02, 0x00, 0x00, 0x4c, + 0x01, 0x00, 0x00, 0x00, 0x04, 0xa3, 0x02, 0x00, + 0x00, 0x4c, 0x47, 0x00, 0x00, 0x00, 0x04, 0x9f, + 0x02, 0x00, 0x00, 0x4c, 0x2d, 0x02, 0x00, 0x00, + 0x04, 0x9f, 0x02, 0x00, 0x00, 0x4c, 0x28, 0x02, + 0x00, 0x00, 0x04, 0xab, 0x02, 0x00, 0x00, 0x4c, + 0x2a, 0x02, 0x00, 0x00, 0x04, 0xbc, 0x02, 0x00, + 0x00, 0x4c, 0x49, 0x00, 0x00, 0x00, 0x04, 0xb0, + 0x02, 0x00, 0x00, 0x4c, 0x4b, 0x00, 0x00, 0x00, + 0x04, 0xba, 0x02, 0x00, 0x00, 0x4c, 0x01, 0x02, + 0x00, 0x00, 0x04, 0xba, 0x02, 0x00, 0x00, 0x4c, + 0x46, 0x00, 0x00, 0x00, 0x4c, 0x59, 0x02, 0x00, + 0x00, 0xc3, 0x12, 0x41, 0x56, 0x02, 0x00, 0x00, + 0xc2, 0x13, 0x0a, 0xc2, 0x14, 0x09, 0xc2, 0x15, + 0x0a, 0xc2, 0x16, 0x09, 0xc2, 0x17, 0xb6, 0xc2, + 0x18, 0x09, 0xc2, 0x19, 0x09, 0xc2, 0x1a, 0x26, + 0x00, 0x00, 0xc2, 0x1b, 0xc0, 0xc2, 0x1d, 0xc0, + 0xc2, 0x1e, 0xc0, 0xc2, 0x1f, 0xb4, 0xc2, 0x20, + 0x04, 0xbf, 0x02, 0x00, 0x00, 0xc2, 0x21, 0x04, + 0xc0, 0x02, 0x00, 0x00, 0xc2, 0x22, 0xb4, 0xc2, + 0x24, 0xc0, 0xc2, 0x25, 0xb4, 0xc2, 0x26, 0xc0, + 0xc2, 0x27, 0xb4, 0xc2, 0x28, 0xc0, 0xc2, 0x29, + 0xb4, 0xc2, 0x2a, 0x09, 0xc2, 0x2d, 0xb4, 0xc2, + 0x2e, 0xb4, 0xc2, 0x2f, 0xb4, 0xc2, 0x33, 0x0b, + 0xc1, 0x46, 0x4c, 0xc1, 0x02, 0x00, 0x00, 0xc1, + 0x49, 0x4c, 0xc2, 0x02, 0x00, 0x00, 0xc1, 0x64, + 0x4c, 0xc3, 0x02, 0x00, 0x00, 0xc1, 0x58, 0x4c, + 0xc4, 0x02, 0x00, 0x00, 0xc1, 0x47, 0x4c, 0xc5, + 0x02, 0x00, 0x00, 0xc1, 0x48, 0x4c, 0xc6, 0x02, + 0x00, 0x00, 0xc1, 0x44, 0x4c, 0xc7, 0x02, 0x00, + 0x00, 0xc1, 0x59, 0x4c, 0xc8, 0x02, 0x00, 0x00, + 0xc1, 0x69, 0x4c, 0x90, 0x02, 0x00, 0x00, 0xc1, + 0x4f, 0x4c, 0x8d, 0x01, 0x00, 0x00, 0xc1, 0x5f, + 0x4c, 0xc9, 0x02, 0x00, 0x00, 0xc1, 0x4e, 0x4c, + 0xca, 0x02, 0x00, 0x00, 0xc1, 0x4f, 0x4c, 0x91, + 0x02, 0x00, 0x00, 0xc1, 0x52, 0x4c, 0xcb, 0x02, + 0x00, 0x00, 0xc1, 0x51, 0x4c, 0xcc, 0x02, 0x00, + 0x00, 0xc1, 0x43, 0x4c, 0xcd, 0x02, 0x00, 0x00, + 0xc1, 0x45, 0x4c, 0xce, 0x02, 0x00, 0x00, 0xc1, + 0x45, 0x4c, 0xcf, 0x02, 0x00, 0x00, 0xc1, 0x5a, + 0x4c, 0xd0, 0x02, 0x00, 0x00, 0xc1, 0x62, 0x4c, + 0xd1, 0x02, 0x00, 0x00, 0xc1, 0x65, 0x4c, 0xd2, + 0x02, 0x00, 0x00, 0xc1, 0x63, 0x4c, 0xd3, 0x02, + 0x00, 0x00, 0xc1, 0x51, 0x4c, 0xd4, 0x02, 0x00, + 0x00, 0xc1, 0x52, 0x4c, 0xd5, 0x02, 0x00, 0x00, + 0xc1, 0x48, 0x4c, 0xd6, 0x02, 0x00, 0x00, 0xc1, + 0x49, 0x4c, 0xd7, 0x02, 0x00, 0x00, 0xc1, 0x4c, + 0x4c, 0xd8, 0x02, 0x00, 0x00, 0xc1, 0x4d, 0x4c, + 0xd9, 0x02, 0x00, 0x00, 0xc1, 0x4c, 0x4c, 0xda, + 0x02, 0x00, 0x00, 0xc1, 0x4d, 0x4c, 0xdb, 0x02, + 0x00, 0x00, 0xc1, 0x46, 0x4c, 0xdc, 0x02, 0x00, + 0x00, 0xc1, 0x57, 0x4c, 0xdd, 0x02, 0x00, 0x00, + 0xc1, 0x47, 0x4c, 0xde, 0x02, 0x00, 0x00, 0xc1, + 0x54, 0x4c, 0xdf, 0x02, 0x00, 0x00, 0xc1, 0x55, + 0x4c, 0xe0, 0x02, 0x00, 0x00, 0xc1, 0x51, 0x4c, + 0xe1, 0x02, 0x00, 0x00, 0xc1, 0x52, 0x4c, 0xe2, + 0x02, 0x00, 0x00, 0xc1, 0x48, 0x4c, 0xe3, 0x02, + 0x00, 0x00, 0xc1, 0x49, 0x4c, 0xe4, 0x02, 0x00, + 0x00, 0xc1, 0x47, 0x4c, 0xe5, 0x02, 0x00, 0x00, + 0xc1, 0x46, 0x4c, 0xe6, 0x02, 0x00, 0x00, 0xc1, + 0x62, 0x4c, 0xe7, 0x02, 0x00, 0x00, 0xc1, 0x4d, + 0x4c, 0xe8, 0x02, 0x00, 0x00, 0xc1, 0x61, 0x4c, + 0xe9, 0x02, 0x00, 0x00, 0xc1, 0x4c, 0x4c, 0xea, + 0x02, 0x00, 0x00, 0xc1, 0x60, 0x4c, 0xeb, 0x02, + 0x00, 0x00, 0xc1, 0x5d, 0x4c, 0xec, 0x02, 0x00, + 0x00, 0xc1, 0x5b, 0x4c, 0xed, 0x02, 0x00, 0x00, + 0xc1, 0x5c, 0x4c, 0xee, 0x02, 0x00, 0x00, 0xc1, + 0x59, 0x4c, 0xef, 0x02, 0x00, 0x00, 0xc2, 0x6a, + 0x0b, 0xc3, 0x75, 0xbf, 0x3d, 0x43, 0x3f, 0x02, + 0x00, 0x00, 0xc5, 0x42, 0x61, 0x00, 0x00, 0x00, + 0x0b, 0xc1, 0x78, 0x4c, 0x53, 0x01, 0x00, 0x00, + 0xc1, 0x79, 0x4c, 0x54, 0x01, 0x00, 0x00, 0xbf, + 0x44, 0x4d, 0xf0, 0x02, 0x00, 0x00, 0x4c, 0xf0, + 0x02, 0x00, 0x00, 0xbf, 0x45, 0x4d, 0xf1, 0x02, + 0x00, 0x00, 0x4c, 0xf1, 0x02, 0x00, 0x00, 0xbf, + 0x46, 0x4d, 0xf2, 0x02, 0x00, 0x00, 0x4c, 0xf2, + 0x02, 0x00, 0x00, 0xbf, 0x47, 0x4d, 0xf3, 0x02, + 0x00, 0x00, 0x4c, 0xf3, 0x02, 0x00, 0x00, 0xbf, + 0x48, 0x4d, 0x3e, 0x02, 0x00, 0x00, 0x4c, 0x3e, + 0x02, 0x00, 0x00, 0xbf, 0x49, 0x4d, 0xf4, 0x02, + 0x00, 0x00, 0x4c, 0xf4, 0x02, 0x00, 0x00, 0xbf, + 0x4a, 0x4d, 0x27, 0x02, 0x00, 0x00, 0x4c, 0x27, + 0x02, 0x00, 0x00, 0xbf, 0x4b, 0x4d, 0x56, 0x02, + 0x00, 0x00, 0x4c, 0x56, 0x02, 0x00, 0x00, 0xbf, + 0x4c, 0x4d, 0x59, 0x02, 0x00, 0x00, 0x4c, 0x59, + 0x02, 0x00, 0x00, 0xbf, 0x4d, 0x4d, 0x99, 0x01, + 0x00, 0x00, 0x4c, 0x99, 0x01, 0x00, 0x00, 0xbf, + 0x4e, 0x4d, 0xf5, 0x02, 0x00, 0x00, 0x4c, 0xf5, + 0x02, 0x00, 0x00, 0x07, 0x24, 0x02, 0x00, 0xc2, + 0x7c, 0xc1, 0x89, 0xee, 0x0e, 0xc1, 0x88, 0xee, + 0x0e, 0xc1, 0x34, 0xee, 0x0e, 0xc1, 0x7d, 0xee, + 0x29, 0x08, 0xea, 0x02, 0x29, 0xbf, 0x00, 0x38, + 0x8c, 0x00, 0x00, 0x00, 0xef, 0x0e, 0x06, 0x2e, +}; + diff --git a/third_party/quickjs/repl.js b/third_party/quickjs/repl.js new file mode 100644 index 0000000000..00dd52bda5 --- /dev/null +++ b/third_party/quickjs/repl.js @@ -0,0 +1,1892 @@ +/* + * QuickJS Read Eval Print Loop + * + * Copyright (c) 2017-2020 Fabrice Bellard + * Copyright (c) 2017-2020 Charlie Gordon + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +import * as std from "qjs:std"; +import * as os from "qjs:os"; +import * as bjson from "qjs:bjson"; + +(function(g) { + /* add 'bjson', 'os' and 'std' bindings */ + g.bjson = bjson; + g.os = os; + g.std = std; + + /* close global objects */ + var Object = g.Object; + var String = g.String; + var Number = g.Number; + var Boolean = g.Boolean; + var BigInt = g.BigInt; + var Uint8Array = g.Uint8Array; + var Array = g.Array; + var Date = g.Date; + var RegExp = g.RegExp; + var Error = g.Error; + var Symbol = g.Symbol; + var Math = g.Math; + var JSON = g.JSON; + var isFinite = g.isFinite; + var isNaN = g.isNaN; + var Infinity = g.Infinity; + var console = g.console; + + var colors = { + none: "\x1b[0m", + black: "\x1b[30m", + red: "\x1b[31m", + green: "\x1b[32m", + yellow: "\x1b[33m", + blue: "\x1b[34m", + magenta: "\x1b[35m", + cyan: "\x1b[36m", + white: "\x1b[37m", + gray: "\x1b[30;1m", + grey: "\x1b[30;1m", + bright_red: "\x1b[31;1m", + bright_green: "\x1b[32;1m", + bright_yellow: "\x1b[33;1m", + bright_blue: "\x1b[34;1m", + bright_magenta: "\x1b[35;1m", + bright_cyan: "\x1b[36;1m", + bright_white: "\x1b[37;1m", + }; + + var themes = { + dark: { + 'annotation': 'cyan', + 'boolean': 'bright_white', + 'comment': 'white', + 'date': 'magenta', + 'default': 'bright_green', + 'error': 'bright_red', + 'function': 'bright_yellow', + 'identifier': 'bright_green', + 'keyword': 'bright_white', + 'null': 'bright_white', + 'number': 'green', + 'other': 'white', + 'propname': 'white', + 'regexp': 'cyan', + 'string': 'bright_cyan', + 'symbol': 'bright_white', + 'type': 'bright_magenta', + 'undefined': 'bright_white', + }, + light: { + 'annotation': 'cyan', + 'boolean': 'bright_magenta', + 'comment': 'grey', + 'date': 'magenta', + 'default': 'black', + 'error': 'red', + 'function': 'bright_yellow', + 'identifier': 'black', + 'keyword': 'bright_magenta', + 'null': 'bright_magenta', + 'number': 'green', + 'other': 'black', + 'propname': 'black', + 'regexp': 'cyan', + 'string': 'bright_cyan', + 'symbol': 'grey', + 'type': 'bright_magenta', + 'undefined': 'bright_magenta', + }, + }; + var styles = themes.dark; + var utf8 = true; + var show_time = false; + var show_colors = true; + var show_hidden = false; + var show_depth = 2; + var hex_mode = false; + var use_strict = false; + + var history = []; + var history_index; + var clip_board = ""; + var pstate = ""; + var prompt = ""; + var plen = 0; + var ps1 = "qjs > "; + var ps2 = " ... "; + var eval_start_time; + var eval_time = 0; + var mexpr = ""; + var level = 0; + var cmd = ""; + var cursor_pos = 0; + var last_cmd = ""; + var last_cursor_pos = 0; + var this_fun, last_fun; + var quote_flag = false; + + var utf8_state = 0; + var utf8_val = 0; + + var term_fd; + var term_read_buf; + var term_width; + /* current X position of the cursor in the terminal */ + var term_cursor_x = 0; + + function termInit() { + var tab; + term_fd = std.in.fileno(); + + /* get the terminal size */ + term_width = 80; + if (os.isatty(term_fd)) { + if (os.ttyGetWinSize) { + tab = os.ttyGetWinSize(term_fd); + if (tab) + term_width = tab[0]; + } + if (os.ttySetRaw) { + /* set the TTY to raw mode */ + os.ttySetRaw(term_fd); + } + } + + /* install a Ctrl-C signal handler */ + os.signal(os.SIGINT, sigint_handler); + + /* install a handler to read stdin */ + term_read_buf = new Uint8Array(64); + os.setReadHandler(term_fd, term_read_handler); + } + + function sigint_handler() { + /* send Ctrl-C to readline */ + handle_byte(3); + } + + function term_read_handler() { + var l, i; + l = os.read(term_fd, term_read_buf.buffer, 0, term_read_buf.length); + for(i = 0; i < l; i++) + handle_byte(term_read_buf[i]); + } + + function handle_byte(c) { + if (!utf8) { + handle_char(c); + } else if (utf8_state !== 0 && (c >= 0x80 && c < 0xc0)) { + utf8_val = (utf8_val << 6) | (c & 0x3F); + utf8_state--; + if (utf8_state === 0) { + handle_char(utf8_val); + } + } else if (c >= 0xc0 && c < 0xf8) { + utf8_state = 1 + (c >= 0xe0) + (c >= 0xf0); + utf8_val = c & ((1 << (6 - utf8_state)) - 1); + } else { + utf8_state = 0; + handle_char(c); + } + } + + function is_alpha(c) { + return typeof c === "string" && + ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z')); + } + + function is_digit(c) { + return typeof c === "string" && (c >= '0' && c <= '9'); + } + + function is_word(c) { + return typeof c === "string" && + (is_alpha(c) || is_digit(c) || c == '_' || c == '$'); + } + + function ucs_length(str) { + var len, c, i, str_len = str.length; + len = 0; + /* we never count the trailing surrogate to have the + following property: ucs_length(str) = + ucs_length(str.substring(0, a)) + ucs_length(str.substring(a, + str.length)) for 0 <= a <= str.length */ + for(i = 0; i < str_len; i++) { + c = str.charCodeAt(i); + if (c < 0xdc00 || c >= 0xe000) + len++; + } + return len; + } + + function is_trailing_surrogate(c) { + var d; + if (typeof c !== "string") + return false; + d = c.codePointAt(0); /* can be NaN if empty string */ + return d >= 0xdc00 && d < 0xe000; + } + + function is_balanced(a, b) { + switch (a + b) { + case "()": + case "[]": + case "{}": + return true; + } + return false; + } + + function print_color_text(str, start, style_names) { + var i, j; + for (j = start; j < str.length;) { + var style = style_names[i = j]; + while (++j < str.length && style_names[j] == style) + continue; + std.puts(colors[styles[style] || 'none']); + std.puts(str.substring(i, j)); + std.puts(colors['none']); + } + } + + function print_csi(n, code) { + std.puts("\x1b[" + ((n != 1) ? n : "") + code); + } + + /* XXX: handle double-width characters */ + function move_cursor(delta) { + var i, l; + if (delta > 0) { + while (delta != 0) { + if (term_cursor_x == (term_width - 1)) { + std.puts("\n"); /* translated to CRLF */ + term_cursor_x = 0; + delta--; + } else { + l = Math.min(term_width - 1 - term_cursor_x, delta); + print_csi(l, "C"); /* right */ + delta -= l; + term_cursor_x += l; + } + } + } else { + delta = -delta; + while (delta != 0) { + if (term_cursor_x == 0) { + print_csi(1, "A"); /* up */ + print_csi(term_width - 1, "C"); /* right */ + delta--; + term_cursor_x = term_width - 1; + } else { + l = Math.min(delta, term_cursor_x); + print_csi(l, "D"); /* left */ + delta -= l; + term_cursor_x -= l; + } + } + } + } + + function update() { + var i, cmd_len; + /* cursor_pos is the position in 16 bit characters inside the + UTF-16 string 'cmd' */ + if (cmd != last_cmd) { + if (!show_colors && last_cmd.substring(0, last_cursor_pos) == cmd.substring(0, last_cursor_pos)) { + /* optimize common case */ + std.puts(cmd.substring(last_cursor_pos)); + } else { + /* goto the start of the line */ + move_cursor(-ucs_length(last_cmd.substring(0, last_cursor_pos))); + if (show_colors) { + var str = mexpr ? mexpr + '\n' + cmd : cmd; + var start = str.length - cmd.length; + var colorstate = colorize_js(str); + print_color_text(str, start, colorstate[2]); + } else { + std.puts(cmd); + } + } + term_cursor_x = (term_cursor_x + ucs_length(cmd)) % term_width; + if (term_cursor_x == 0) { + /* show the cursor on the next line */ + std.puts(" \x08"); + } + /* remove the trailing characters */ + std.puts("\x1b[J"); + last_cmd = cmd; + last_cursor_pos = cmd.length; + } + if (cursor_pos > last_cursor_pos) { + move_cursor(ucs_length(cmd.substring(last_cursor_pos, cursor_pos))); + } else if (cursor_pos < last_cursor_pos) { + move_cursor(-ucs_length(cmd.substring(cursor_pos, last_cursor_pos))); + } + last_cursor_pos = cursor_pos; + std.out.flush(); + } + + /* editing commands */ + function insert(str) { + if (str) { + cmd = cmd.substring(0, cursor_pos) + str + cmd.substring(cursor_pos); + cursor_pos += str.length; + } + } + + function quoted_insert() { + quote_flag = true; + } + + function abort() { + cmd = ""; + cursor_pos = 0; + return -2; + } + + function alert() { + } + + function beginning_of_line() { + cursor_pos = 0; + } + + function end_of_line() { + cursor_pos = cmd.length; + } + + function forward_char() { + if (cursor_pos < cmd.length) { + cursor_pos++; + while (is_trailing_surrogate(cmd.charAt(cursor_pos))) + cursor_pos++; + } + } + + function backward_char() { + if (cursor_pos > 0) { + cursor_pos--; + while (is_trailing_surrogate(cmd.charAt(cursor_pos))) + cursor_pos--; + } + } + + function skip_word_forward(pos) { + while (pos < cmd.length && !is_word(cmd.charAt(pos))) + pos++; + while (pos < cmd.length && is_word(cmd.charAt(pos))) + pos++; + return pos; + } + + function skip_word_backward(pos) { + while (pos > 0 && !is_word(cmd.charAt(pos - 1))) + pos--; + while (pos > 0 && is_word(cmd.charAt(pos - 1))) + pos--; + return pos; + } + + function forward_word() { + cursor_pos = skip_word_forward(cursor_pos); + } + + function backward_word() { + cursor_pos = skip_word_backward(cursor_pos); + } + + function clear_screen() { + directives["clear"](); + return -2; + } + + function accept_line() { + std.puts("\n"); + history_add(cmd); + return -1; + } + + function history_add(str) { + str = str.trimRight(); + if (str) { + while (history.length && !history[history.length - 1]) + history.length--; + history.push(str); + } + history_index = history.length; + } + + function previous_history() { + if (history_index > 0) { + if (history_index == history.length) { + history.push(cmd); + } + history_index--; + cmd = history[history_index]; + cursor_pos = cmd.length; + } + } + + function next_history() { + if (history_index < history.length - 1) { + history_index++; + cmd = history[history_index]; + cursor_pos = cmd.length; + } + } + + function history_search(dir) { + var pos = cursor_pos; + for (var i = 1; i <= history.length; i++) { + var index = (history.length + i * dir + history_index) % history.length; + if (history[index].substring(0, pos) == cmd.substring(0, pos)) { + history_index = index; + cmd = history[index]; + return; + } + } + } + + function history_search_backward() { + return history_search(-1); + } + + function history_search_forward() { + return history_search(1); + } + + function delete_char_dir(dir) { + var start, end; + + start = cursor_pos; + if (dir < 0) { + start--; + while (is_trailing_surrogate(cmd.charAt(start))) + start--; + } + end = start + 1; + while (is_trailing_surrogate(cmd.charAt(end))) + end++; + + if (start >= 0 && start < cmd.length) { + if (last_fun === kill_region) { + kill_region(start, end, dir); + } else { + cmd = cmd.substring(0, start) + cmd.substring(end); + cursor_pos = start; + } + } + } + + function delete_char() { + delete_char_dir(1); + } + + function control_d() { + if (cmd.length == 0) { + std.puts("\n"); + return -3; /* exit read eval print loop */ + } else { + delete_char_dir(1); + } + } + + function backward_delete_char() { + delete_char_dir(-1); + } + + function transpose_chars() { + var pos = cursor_pos; + if (cmd.length > 1 && pos > 0) { + if (pos == cmd.length) + pos--; + cmd = cmd.substring(0, pos - 1) + cmd.substring(pos, pos + 1) + + cmd.substring(pos - 1, pos) + cmd.substring(pos + 1); + cursor_pos = pos + 1; + } + } + + function transpose_words() { + var p1 = skip_word_backward(cursor_pos); + var p2 = skip_word_forward(p1); + var p4 = skip_word_forward(cursor_pos); + var p3 = skip_word_backward(p4); + + if (p1 < p2 && p2 <= cursor_pos && cursor_pos <= p3 && p3 < p4) { + cmd = cmd.substring(0, p1) + cmd.substring(p3, p4) + + cmd.substring(p2, p3) + cmd.substring(p1, p2); + cursor_pos = p4; + } + } + + function upcase_word() { + var end = skip_word_forward(cursor_pos); + cmd = cmd.substring(0, cursor_pos) + + cmd.substring(cursor_pos, end).toUpperCase() + + cmd.substring(end); + } + + function downcase_word() { + var end = skip_word_forward(cursor_pos); + cmd = cmd.substring(0, cursor_pos) + + cmd.substring(cursor_pos, end).toLowerCase() + + cmd.substring(end); + } + + function kill_region(start, end, dir) { + var s = cmd.substring(start, end); + if (last_fun !== kill_region) + clip_board = s; + else if (dir < 0) + clip_board = s + clip_board; + else + clip_board = clip_board + s; + + cmd = cmd.substring(0, start) + cmd.substring(end); + if (cursor_pos > end) + cursor_pos -= end - start; + else if (cursor_pos > start) + cursor_pos = start; + this_fun = kill_region; + } + + function kill_line() { + kill_region(cursor_pos, cmd.length, 1); + } + + function backward_kill_line() { + kill_region(0, cursor_pos, -1); + } + + function kill_word() { + kill_region(cursor_pos, skip_word_forward(cursor_pos), 1); + } + + function backward_kill_word() { + kill_region(skip_word_backward(cursor_pos), cursor_pos, -1); + } + + function yank() { + insert(clip_board); + } + + function control_c() { + if (last_fun === control_c) { + std.puts("\n"); + exit(0); + } else { + std.puts("\n(Press Ctrl-C again to quit)\n"); + readline_print_prompt(); + } + } + + function reset() { + cmd = ""; + cursor_pos = 0; + } + + function get_context_word(line, end) { + var pos = end; + while (pos > 0 && is_word(line[pos - 1])) + pos--; + return line.slice(pos, end); + } + function get_context_object(line, pos) { + if (pos <= 0) + return g; + var c = line[pos - 1]; + if (pos === 1 && (c === '\\' || c === '.')) + return directives; + if ("'\"`@#)]}\\".indexOf(c) >= 0) + return void 0; + if (c === ".") { + pos--; + switch (c = line[pos - 1]) { + case '\'': + case '\"': + case '`': + return "a"; + case ']': + return []; // incorrect for a[b].<TAB> + case '/': + return / /; + default: + if (is_word(c)) { + var base = get_context_word(line, pos); + var base_pos = pos - base.length; + if (base === 'true' || base === 'false') + return true; + if (base === 'null') + return null; + if (base === 'this') + return g; + if (!isNaN(+base)) // number literal, incorrect for 1.<TAB> + return 0; + var obj = get_context_object(line, base_pos); + if (obj === null || obj === void 0) + return obj; + if (typeof obj[base] !== 'undefined') + return obj[base]; + // Check if `base` is a set of regexp flags + // TODO(chqrlie): this is incorrect for a/i<TAB>... + // Should use colorizer to determine the token type + if (base_pos >= 3 && line[base_pos - 1] === '/' && base.match(/^[dgimsuvy]+$/)) + return RegExp(); + // base is a local identifier, complete as generic object + } + break; + } + return {}; + } + return g; + } + + function get_completions(line, pos) { + var s, obj, ctx_obj, r, i, j, paren; + + s = get_context_word(line, pos); + ctx_obj = get_context_object(line, pos - s.length); + r = []; + /* enumerate properties from object and its prototype chain, + add non-numeric regular properties with s as e prefix + */ + for (i = 0, obj = ctx_obj; i < 10 && obj !== null && obj !== void 0; i++) { + var props = Object.getOwnPropertyNames(obj); + /* add non-numeric regular properties */ + for (j = 0; j < props.length; j++) { + var prop = props[j]; + if (typeof prop == "string" && ""+(+prop) != prop && prop.startsWith(s)) + r.push(prop); + } + obj = Object.getPrototypeOf(obj); + } + if (r.length > 1) { + /* sort list with internal names last and remove duplicates */ + function symcmp(a, b) { + if (a[0] != b[0]) { + if (a[0] == '_') + return 1; + if (b[0] == '_') + return -1; + } + if (a < b) + return -1; + if (a > b) + return +1; + return 0; + } + r.sort(symcmp); + for(i = j = 1; i < r.length; i++) { + if (r[i] != r[i - 1]) + r[j++] = r[i]; + } + r.length = j; + } + /* 'tab' = list of completions, 'pos' = cursor position inside + the completions */ + return { tab: r, pos: s.length, ctx: ctx_obj }; + } + + function completion() { + var tab, res, s, i, j, len, t, max_width, col, n_cols, row, n_rows; + res = get_completions(cmd, cursor_pos); + tab = res.tab; + if (tab.length === 0) + return; + s = tab[0]; + len = s.length; + /* add the chars which are identical in all the completions */ + for(i = 1; i < tab.length; i++) { + t = tab[i]; + for(j = 0; j < len; j++) { + if (t[j] !== s[j]) { + len = j; + break; + } + } + } + for(i = res.pos; i < len; i++) { + insert(s[i]); + } + if (last_fun === completion && tab.length == 1) { + /* append parentheses to function names */ + var m = res.ctx[tab[0]]; + if (typeof m == "function") { + insert('('); + if (m.length == 0) + insert(')'); + } else if (typeof m == "object") { + insert('.'); + } + } + /* show the possible completions */ + if (last_fun === completion && tab.length >= 2) { + max_width = 0; + for(i = 0; i < tab.length; i++) + max_width = Math.max(max_width, tab[i].length); + max_width += 2; + n_cols = Math.max(1, Math.floor((term_width + 1) / max_width)); + n_rows = Math.ceil(tab.length / n_cols); + std.puts("\n"); + /* display the sorted list column-wise */ + for (row = 0; row < n_rows; row++) { + for (col = 0; col < n_cols; col++) { + i = col * n_rows + row; + if (i >= tab.length) + break; + s = tab[i]; + if (col != n_cols - 1) + s = s.padEnd(max_width); + std.puts(s); + } + std.puts("\n"); + } + /* show a new prompt */ + readline_print_prompt(); + } + } + + var commands = { /* command table */ + "\x01": beginning_of_line, /* ^A - bol */ + "\x02": backward_char, /* ^B - backward-char */ + "\x03": control_c, /* ^C - abort */ + "\x04": control_d, /* ^D - delete-char or exit */ + "\x05": end_of_line, /* ^E - eol */ + "\x06": forward_char, /* ^F - forward-char */ + "\x07": abort, /* ^G - bell */ + "\x08": backward_delete_char, /* ^H - backspace */ + "\x09": completion, /* ^I - history-search-backward */ + "\x0a": accept_line, /* ^J - newline */ + "\x0b": kill_line, /* ^K - delete to end of line */ + "\x0c": clear_screen, /* ^L - clear screen */ + "\x0d": accept_line, /* ^M - enter */ + "\x0e": next_history, /* ^N - down */ + "\x10": previous_history, /* ^P - up */ + "\x11": quoted_insert, /* ^Q - quoted-insert */ + "\x12": alert, /* ^R - reverse-search */ + "\x13": alert, /* ^S - search */ + "\x14": transpose_chars, /* ^T - transpose */ + "\x17": backward_kill_word, /* ^W - backward_kill_word */ + "\x18": reset, /* ^X - cancel */ + "\x19": yank, /* ^Y - yank */ + "\x1bOA": previous_history, /* ^[OA - up */ + "\x1bOB": next_history, /* ^[OB - down */ + "\x1bOC": forward_char, /* ^[OC - right */ + "\x1bOD": backward_char, /* ^[OD - left */ + "\x1bOF": forward_word, /* ^[OF - ctrl-right */ + "\x1bOH": backward_word, /* ^[OH - ctrl-left */ + "\x1b[1;5C": forward_word, /* ^[[1;5C - ctrl-right */ + "\x1b[1;5D": backward_word, /* ^[[1;5D - ctrl-left */ + "\x1b[1~": beginning_of_line, /* ^[[1~ - bol */ + "\x1b[3~": delete_char, /* ^[[3~ - delete */ + "\x1b[4~": end_of_line, /* ^[[4~ - eol */ + "\x1b[5~": history_search_backward,/* ^[[5~ - page up */ + "\x1b[6~": history_search_forward, /* ^[[5~ - page down */ + "\x1b[A": previous_history, /* ^[[A - up */ + "\x1b[B": next_history, /* ^[[B - down */ + "\x1b[C": forward_char, /* ^[[C - right */ + "\x1b[D": backward_char, /* ^[[D - left */ + "\x1b[F": end_of_line, /* ^[[F - end */ + "\x1b[H": beginning_of_line, /* ^[[H - home */ + "\x1b\x7f": backward_kill_word, /* M-C-? - backward_kill_word */ + "\x1bb": backward_word, /* M-b - backward_word */ + "\x1bd": kill_word, /* M-d - kill_word */ + "\x1bf": forward_word, /* M-f - backward_word */ + "\x1bk": backward_kill_line, /* M-k - backward_kill_line */ + "\x1bl": downcase_word, /* M-l - downcase_word */ + "\x1bt": transpose_words, /* M-t - transpose_words */ + "\x1bu": upcase_word, /* M-u - upcase_word */ + "\x7f": backward_delete_char, /* ^? - delete */ + }; + + function dupstr(str, count) { + var res = ""; + while (count-- > 0) + res += str; + return res; + } + + var readline_keys; + var readline_state; + var readline_cb; + + function readline_print_prompt() + { + std.puts(prompt); + term_cursor_x = ucs_length(prompt) % term_width; + last_cmd = ""; + last_cursor_pos = 0; + } + + function readline_start(defstr, cb) { + cmd = defstr || ""; + cursor_pos = cmd.length; + history_index = history.length; + readline_cb = cb; + + prompt = pstate; + + if (mexpr) { + prompt += dupstr(" ", plen - prompt.length); + prompt += ps2; + } else { + if (show_time) { + var t = eval_time / 1000; + prompt += t.toFixed(6) + " "; + } + plen = prompt.length; + prompt += ps1; + } + readline_print_prompt(); + update(); + readline_state = 0; + } + + function handle_char(c1) { + var c; + c = String.fromCodePoint(c1); + switch(readline_state) { + case 0: + if (c == '\x1b') { /* '^[' - ESC */ + readline_keys = c; + readline_state = 1; + } else { + handle_key(c); + } + break; + case 1: /* '^[ */ + readline_keys += c; + if (c == '[') { + readline_state = 2; + } else if (c == 'O') { + readline_state = 3; + } else { + handle_key(readline_keys); + readline_state = 0; + } + break; + case 2: /* '^[[' - CSI */ + readline_keys += c; + if (!(c == ';' || (c >= '0' && c <= '9'))) { + handle_key(readline_keys); + readline_state = 0; + } + break; + case 3: /* '^[O' - ESC2 */ + readline_keys += c; + handle_key(readline_keys); + readline_state = 0; + break; + } + } + + function handle_key(keys) { + var fun; + + if (quote_flag) { + if (ucs_length(keys) === 1) + insert(keys); + quote_flag = false; + } else if (fun = commands[keys]) { + this_fun = fun; + switch (fun(keys)) { + case -1: + readline_cb(cmd); + return; + case -2: + readline_cb(null); + return; + case -3: + /* uninstall a Ctrl-C signal handler */ + os.signal(os.SIGINT, null); + /* uninstall the stdin read handler */ + os.setReadHandler(term_fd, null); + save_history(); + return; + } + last_fun = this_fun; + } else if (ucs_length(keys) === 1 && keys >= ' ') { + insert(keys); + last_fun = insert; + } else { + alert(); /* beep! */ + } + + cursor_pos = (cursor_pos < 0) ? 0 : + (cursor_pos > cmd.length) ? cmd.length : cursor_pos; + update(); + } + + function number_to_string(a, radix) { + var s; + if (!isFinite(a)) { + /* NaN, Infinite */ + return a.toString(); + } else { + if (a == 0) { + if (1 / a < 0) + s = "-0"; + else + s = "0"; + } else { + if (radix == 16 && a === Math.floor(a)) { + var s; + if (a < 0) { + a = -a; + s = "-"; + } else { + s = ""; + } + s += "0x" + a.toString(16); + } else { + s = a.toString(); + } + } + return s; + } + } + + function bigint_to_string(a, radix) { + var s; + if (radix == 16) { + var s; + if (a < 0) { + a = -a; + s = "-"; + } else { + s = ""; + } + s += "0x" + a.toString(16); + } else { + s = a.toString(); + } + return s + "n"; + } + + var util = {}; + util.inspect = function(val, show_hidden, max_depth, use_colors) { + var options = {}; + if (typeof show_hidden === 'object' && show_hidden !== null) { + options = show_hidden; + show_hidden = options.showHidden; + max_depth = options.depth; + use_colors = options.colors; + } + function set(opt, def) { + return (typeof opt === 'undefined') ? def : (opt === null) ? Infinity : opt; + } + if (typeof show_hidden !== 'boolean') + show_hidden = false; + max_depth = set(max_depth, 2); + use_colors = set(use_colors, true); + var breakLength = set(options.breakLength, Math.min(term_width, 80)); + var maxArrayLength = set(options.maxArrayLength, 100); + var maxObjectLength = set(options.maxObjectLength, maxArrayLength + 10); + var maxStringLength = set(options.maxStringLength, 78); + var refs = [{}]; /* list of circular references */ + var stack = []; /* stack of pending objects */ + var tokens = []; /* list of generated tokens */ + var output = []; /* list of output fragments */ + var last_style = 'none'; + + function quote_str(s) { + if (s.includes("'")) + return JSON.stringify(s); + s = JSON.stringify(s).slice(1, -1).replaceAll('\\"', '"'); + return `'${s}'`; + } + function push_token(s) { + tokens.push("" + s); + } + function append_token(s) { + tokens[tokens.length - 1] += s; + } + function class_tag(o) { + // get the class id of an object + // works for boxed objects, Math, JSON, globalThis... + return Object.prototype.toString.call(o).slice(8, -1); + } + + function print_rec(a, level) { + var n, n0, i, k, keys, key, type, isarray, noindex, nokeys, brace, sep; + + switch (type = typeof(a)) { + case "undefined": + case "boolean": + push_token(a); + break; + case "number": + push_token(number_to_string(a, hex_mode ? 16 : 10)); + break; + case "bigint": + push_token(bigint_to_string(a, hex_mode ? 16 : 10)); + break; + case "string": + if (a.length > maxStringLength) + a = a.substring(0, maxStringLength) + "..."; + push_token(quote_str(a)); + break; + case "symbol": + push_token(String(a)); + break; + case "object": + case "function": + if (a === null) { + push_token(a); + break; + } + if ((n = refs.indexOf(a)) >= 0) { + push_token(`[Circular *${n}]`); + break; + } + if ((n = stack.indexOf(a)) >= 0) { + push_token(`[Circular *${refs.length}]`); + refs.push(stack[n]); + break; + } + var obj_index = tokens.length; + var tag = class_tag(a); + stack.push(a); + // XXX: should have Proxy instances + if (a instanceof Date) { + push_token(`Date ${JSON.stringify(a.toGMTString())}`); + } else if (a instanceof RegExp) { + push_token(a.toString()); + } else if (a instanceof Boolean || a instanceof Number || a instanceof BigInt) { + push_token(`[${tag}: ${a}]`); + } else if (a instanceof String) { + push_token(`[${tag}: ${quote_str(a)}]`); + len = a.length; + noindex = 1; + } else if (Array.isArray(a)) { + push_token("["); + isarray = 1; + } else if (tag.includes('Array') && a instanceof Uint8Array.__proto__) { + push_token(`${tag}(${a.length}) [`); + isarray = 1; + } else if (type === 'function') { + if (a.name) + push_token(`[Function: ${a.name}]`); + else + push_token(`[Function (anonymous)]`); + } else { + var cons = (a.constructor && a.constructor.name) || 'Object'; + if (tag !== 'Object') { + push_token(`${cons} [${tag}] {`); + } else if (a.__proto__ === null) { + push_token(`[${cons}: null prototype] {`); + } else if (cons !== 'Object') { + push_token(`${cons} {`); + } else { + push_token("{"); + } + brace = "}"; + } + keys = null; + n = 0; + n0 = 0; + k = 0; + if (isarray) { + brace = "]"; + var len = a.length; + if (level > max_depth && len) { + push_token("..."); + push_token(brace); + return; + } + for (i = 0; i < len; i++) { + k++; + if (i in a) { + print_rec(a[i], level + 1); + } else { + var start = i; + while (i + 1 < len && !((i + 1) in a)) + i++; + if (i > start) + push_token(`<${i - start + 1} empty items>`); + else + push_token("<empty>"); + } + if (k >= maxArrayLength && len - k > 5) { + push_token(`... ${len - k} more items`); + break; + } + } + noindex = 1; + /* avoid using Object.keys for large arrays */ + if (i !== len && len > 1000) + nokeys = 1; + } + if (!nokeys) { + keys = show_hidden ? Object.getOwnPropertyNames(a) : Object.keys(a); + n = keys.length; + } + if (noindex) { + /* skip all index properties */ + for (; n0 < n; n0++) { + i = +keys[n0]; + if (i !== (i >>> 0) || i >= len) + break; + } + } + if (n0 < n) { + if (!brace) { + append_token(" {"); + brace = "}"; + } + if (level > max_depth && n0 < n) { + push_token("..."); + push_token(brace); + return; + } + for(i = n0; i < n; i++) { + var key = keys[i]; + var desc = Object.getOwnPropertyDescriptor(a, key); + if (!desc) + continue; + if (!desc.enumerable) + push_token(`[${String(key)}]`); + else + if (+key === (key >>> 0) || key.match(/^[a-zA-Z_$][0-9a-zA-Z_$]*/)) + push_token(key); + else + push_token(quote_str(key)); + push_token(":"); + if ('value' in desc) { + print_rec(desc.value, level + 1); + } else { + var fields = []; + if (desc.get) + fields.push("Getter"); + if (desc.set) + fields.push("Setter"); + push_token(`[${fields.join('/')}]`); + } + k++; + if (k > maxObjectLength && n - k > 5) { + push_token(`... ${n - k} more properties`); + break; + } + } + } + if (brace) + push_token(brace); + stack.pop(a); + if ((i = refs.indexOf(a)) > 0) + tokens[obj_index] = `<ref *${i}> ${tokens[obj_index]}`; + break; + default: + push_token(String(a)); + break; + } + }; + function output_str(s, style) { + if (use_colors) { + if (last_style !== style) { + output.push(colors.none); + last_style = style; + } + if (style) { + var color = colors[styles[style]]; + if (color) + output.push(color); + } + } + output.push(s); + } + function output_propname(s) { + if (s[0] >= '0' && s[0] <= '9') + output_str(s, 'number'); + else + output_str(s, 'propname'); + output_str(": "); + } + function output_pretty(s) { + if (!use_colors) { + output_str(s); + return; + } + while (s.length > 0) { + var style = 'none'; + var chunk = s; + var len = 0; + var m = null; + switch (s[0]) { + case '"': + style = 'string'; + m = s.match(/^"([^\\"]|\\.)*"/); + break; + case '\'': + style = 'string'; + m = s.match(/^'([^\\']|\\.)*'/); + break; + case '/': + style = 'regexp'; + break; + case '<': + m = s.match(/^\<[^\>]+\>/); + if (m) + style = 'annotation'; + break; + case '[': + m = s.match(/^\[[^\]]+\]/); + if (m) { + style = 'annotation'; + break; + } + /* fall thru */ + case ']': + case '}': + case ',': + case ' ': + style = 'other'; + len = 1; + break; + case '.': + style = 'annotation'; + break; + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + style = 'number'; + m = s.match(/^[0-9a-z_]+[.]?[0-9a-z_]*[eEpP]?[+-]?[0-9]*/); + break; + case '-': + len = 1; + break; + default: + if (is_block(s)) + len = s.length - 1; + if (s.startsWith('Date')) + style = 'date'; + else if (s.startsWith('Symbol')) + style = 'symbol'; + else if (s === 'Infinity' || s === 'NaN') + style = 'keyword'; + else if (s === 'true' || s === 'false') + style = 'boolean'; + else if (s === 'null') + style = 'null'; + else if (s === 'undefined') + style = 'undefined'; + break; + } + if (m) + len = m[0].length; + if (len > 0) + chunk = s.slice(0, len); + output_str(chunk, style); + s = s.slice(chunk.length); + } + } + function is_block(s) { + var c = s[s.length - 1]; + return c === '[' || c === '{'; + } + function block_width(i) { + var w = tokens[i].length; + if (tokens[i + 1] === ":") { + i += 2; + w += 2 + tokens[i].length; + } + var width = w; + if (is_block(tokens[i])) { + var seplen = 1; + while (++i < tokens.length) { + width += seplen; + var s = tokens[i]; + if (s === ']' || s === '}') + break; + [ i, w ] = block_width(i); + width += w; + seplen = 2; + } + } + return [ i, width ]; + } + function output_single(i, last) { + var sep = ""; + while (i <= last) { + var s = tokens[i++]; + if (s === ']' || s === '}') { + if (sep.length > 1) + output_str(" "); + } else { + output_str(sep); + if (tokens[i] === ":") { + output_propname(s); + i++; + s = tokens[i++]; + } + } + output_pretty(s); + sep = is_block(s) ? " " : ", "; + } + } + function output_spaces(s, count) { + if (count > 0) + s += " ".repeat(count); + output_str(s); + } + function output_indent(indent, from) { + var avail_width = breakLength - indent - 2; + var [ last, width ] = block_width(from); + if (width <= avail_width) { + output_single(from, last); + return [ last, width ]; + } + if (tokens[from + 1] === ":") { + output_propname(tokens[from]); + from += 2; + } + output_pretty(tokens[from]); + if (!is_block(tokens[from])) { + return [ from, width ]; + } + indent += 2; + avail_width -= 2; + var sep = ""; + var first = from + 1; + var i, w; + if (tokens[from].endsWith('[')) { + /* array: try multiple columns for indexed values */ + var k = 0, col, cols; + var tab = []; + for (i = first; i < last; i++) { + if (tokens[i][0] === '.' || tokens[i + 1] === ':') + break; + [ i, w ] = block_width(i); + tab[k++] = w; + } + var colwidth; + for (cols = Math.min(avail_width / 3, tab.length, 16); cols > 1; cols--) { + colwidth = []; + col = 0; + for (k = 0; k < tab.length; k++) { + colwidth[col] = Math.max(colwidth[col] || 0, tab[k] + 2); + col = (col + 1) % cols; + } + w = 0; + for (col = 0; col < cols; col++) { + w += colwidth[col]; + } + if (w <= avail_width) + break; + } + if (cols > 1) { + w = 0; + col = cols - 1; + for (i = first; i < last; i++) { + if (tokens[i][0] === '.' || tokens[i + 1] === ':') + break; + w += sep.length; + output_str(sep); + sep = ","; + if (col === cols - 1) { + output_spaces("\n", indent); + col = 0; + } else { + output_spaces("", colwidth[col++] - w); + } + [i, w] = output_indent(indent, i); + } + first = i; + } + } + for (i = first; i < last; i++) { + output_str(sep); + sep = ","; + output_spaces("\n", indent); + [i, w] = output_indent(indent, i); + } + output_spaces("\n", indent -= 2); + output_pretty(tokens[last]); + return [last, breakLength]; + } + print_rec(val, 0); + output_indent(0, 0); + output_str(""); + return output.join(""); + }; + + function print(val) { + std.puts(util.inspect(val, { depth: show_depth, colors: show_colors, showHidden: show_hidden })); + std.puts("\n"); + } + + /* return true if the string was a directive */ + function handle_directive(a) { + if (a === "?") { + help(); + return true; + } + if (a[0] !== '\\' && a[0] !== '.') + return false; + var pos = 1; + while (pos < a.length && a[pos] !== ' ') { + pos++; + } + var cmd = a.substring(1, pos); + var partial = 0; + var fun; + for (var p in directives) { + if (p.startsWith(cmd)) { + fun = directives[p]; + partial++; + if (p === cmd) { + partial = 0; + break; + } + } + } + if (fun && partial < 2) { + fun(a.substring(pos).trim()); + } else { + std.puts(`Unknown directive: ${cmd}\n`); + } + return true; + } + + function help() { + var sel = (n) => n ? "*": " "; + std.puts(".help print this help\n" + + ".x " + sel(hex_mode) + "hexadecimal number display\n" + + ".dec " + sel(!hex_mode) + "decimal number display\n" + + ".time " + sel(show_time) + "toggle timing display\n" + + ".strict " + sel(use_strict) + "toggle strict mode evaluation\n" + + `.depth set object depth (current: ${show_depth})\n` + + ".hidden " + sel(show_hidden) + "toggle hidden properties display\n" + + ".color " + sel(show_colors) + "toggle colored output\n" + + ".dark " + sel(styles == themes.dark) + "select dark color theme\n" + + ".light " + sel(styles == themes.light) + "select light color theme\n" + + ".clear clear the terminal\n" + + ".load load source code from a file\n" + + ".quit exit\n"); + } + + function load(s) { + if (s.lastIndexOf(".") <= s.lastIndexOf("/")) + s += ".js"; + try { + std.loadScript(s); + } catch (e) { + std.puts(`${e}\n`); + } + } + + function exit(e) { + save_history(); + std.exit(e); + } + + function to_bool(s, def) { + return s ? "1 true yes Yes".includes(s) : def; + } + + var directives = Object.setPrototypeOf({ + "help": help, + "load": load, + "x": (s) => { hex_mode = to_bool(s, true); }, + "dec": (s) => { hex_mode = !to_bool(s, true); }, + "time": (s) => { show_time = to_bool(s, !show_time); }, + "strict": (s) => { use_strict = to_bool(s, !use_strict); }, + "depth": (s) => { show_depth = +s || 2; }, + "hidden": (s) => { show_hidden = to_bool(s, !show_hidden); }, + "color": (s) => { show_colors = to_bool(s, !show_colors); }, + "dark": () => { styles = themes.dark; }, + "light": () => { styles = themes.light; }, + "clear": () => { std.puts("\x1b[H\x1b[J") }, + "quit": () => { exit(0); }, + }, null); + + function cmd_start() { + std.puts('QuickJS-ng - Type ".help" for help\n'); + cmd_readline_start(); + } + + function cmd_readline_start() { + readline_start(dupstr(" ", level), readline_handle_cmd); + } + + function readline_handle_cmd(expr) { + if (!handle_cmd(expr)) { + cmd_readline_start(); + } + } + + /* return true if async termination */ + function handle_cmd(expr) { + if (!expr) + return false; + if (mexpr) { + expr = mexpr + '\n' + expr; + } else { + if (handle_directive(expr)) + return false; + } + var colorstate = colorize_js(expr); + pstate = colorstate[0]; + level = colorstate[1]; + if (pstate) { + mexpr = expr; + return false; + } + mexpr = ""; + + eval_and_print(expr); + + return true; + } + + function eval_and_print(expr) { + var result; + + if (use_strict) + expr = '"use strict"; void 0;' + expr; + eval_start_time = os.now(); + /* eval as a script */ + result = std.evalScript(expr, { backtrace_barrier: true, async: true }); + /* result is a promise */ + result.then(print_eval_result, print_eval_error); + } + + function print_eval_result(result) { + result = result.value; + eval_time = os.now() - eval_start_time; + print(result); + /* set the last result */ + g._ = result; + + handle_cmd_end(); + } + + function print_eval_error(error) { + if (show_colors) { + std.puts(colors[styles.error]); + } + if (error instanceof Error) { + std.puts(error); + std.puts('\n'); + if (error.stack) { + std.puts(error.stack); + } + } else { + std.puts("Throw: "); + std.puts(error); + std.puts('\n'); + } + + if (show_colors) { + std.puts(colors.none); + } + + handle_cmd_end(); + } + + function handle_cmd_end() { + level = 0; + + /* run the garbage collector after each command */ + std.gc(); + + cmd_readline_start(); + } + + function colorize_js(str) { + var i, c, start, n = str.length; + var style, state = "", level = 0; + var primary, can_regex = 1; + var r = []; + + function push_state(c) { state += c; } + function last_state(c) { return state.substring(state.length - 1); } + function pop_state(c) { + var c = last_state(); + state = state.substring(0, state.length - 1); + return c; + } + + function parse_block_comment() { + style = 'comment'; + push_state('/'); + for (i++; i < n - 1; i++) { + if (str[i] == '*' && str[i + 1] == '/') { + i += 2; + pop_state('/'); + break; + } + } + } + + function parse_line_comment() { + style = 'comment'; + for (i++; i < n; i++) { + if (str[i] == '\n') { + break; + } + } + } + + function parse_string(delim) { + style = 'string'; + push_state(delim); + while (i < n) { + c = str[i++]; + if (c == '\n') { + style = 'error'; + continue; + } + if (c == '\\') { + if (i >= n) + break; + i++; + } else + if (c == delim) { + pop_state(); + break; + } + } + } + + function parse_regex() { + style = 'regexp'; + push_state('/'); + while (i < n) { + c = str[i++]; + if (c == '\n') { + style = 'error'; + continue; + } + if (c == '\\') { + if (i < n) { + i++; + } + continue; + } + if (last_state() == '[') { + if (c == ']') { + pop_state() + } + // ECMA 5: ignore '/' inside char classes + continue; + } + if (c == '[') { + push_state('['); + if (str[i] == '[' || str[i] == ']') + i++; + continue; + } + if (c == '/') { + pop_state(); + while (i < n && is_word(str[i])) + i++; + break; + } + } + } + + function parse_number() { + style = 'number'; + // TODO(chqrlie) parse partial number syntax + // TODO(chqrlie) special case bignum + while (i < n && (is_word(str[i]) || (str[i] == '.' && (i == n - 1 || str[i + 1] != '.')))) { + i++; + } + } + + var js_keywords = "|" + + "break|case|catch|continue|debugger|default|delete|do|" + + "else|finally|for|function|if|in|instanceof|new|" + + "return|switch|this|throw|try|typeof|while|with|" + + "class|const|enum|import|export|extends|super|" + + "implements|interface|let|package|private|protected|" + + "public|static|yield|" + + "undefined|null|true|false|Infinity|NaN|" + + "eval|arguments|" + + "await|"; + + var js_no_regex = "|this|super|undefined|null|true|false|Infinity|NaN|arguments|"; + var js_types = "|void|var|"; + + function parse_identifier() { + can_regex = 1; + + while (i < n && is_word(str[i])) + i++; + + var s = str.substring(start, i); + var w = '|' + s + '|'; + + if (js_keywords.indexOf(w) >= 0) { + style = 'keyword'; + if (s === 'true' || s === 'false') + style = 'boolean'; + else if (s === 'true' || s === 'false') + style = 'boolean'; + else if (s === 'null') + style = 'null'; + else if (s === 'undefined') + style = 'undefined'; + if (js_no_regex.indexOf(w) >= 0) + can_regex = 0; + return; + } + + var i1 = i; + while (i1 < n && str[i1] == ' ') + i1++; + + if (i1 < n && str[i1] == '(') { + style = 'function'; + return; + } + + if (js_types.indexOf(w) >= 0) { + style = 'type'; + return; + } + + style = 'identifier'; + can_regex = 0; + } + + function set_style(from, to) { + while (r.length < from) + r.push('default'); + while (r.length < to) + r.push(style); + } + + for (i = 0; i < n;) { + style = null; + start = i; + switch (c = str[i++]) { + case ' ': + case '\t': + case '\r': + case '\n': + continue; + case '+': + case '-': + if (i < n && str[i] == c) { + i++; + continue; + } + can_regex = 1; + continue; + case '/': + if (i < n && str[i] == '*') { // block comment + parse_block_comment(); + break; + } + if (i < n && str[i] == '/') { // line comment + parse_line_comment(); + break; + } + if (can_regex) { + parse_regex(); + can_regex = 0; + break; + } + can_regex = 1; + continue; + case '\'': + case '\"': + case '`': + parse_string(c); + can_regex = 0; + break; + case '(': + case '[': + case '{': + can_regex = 1; + level++; + push_state(c); + continue; + case ')': + case ']': + case '}': + can_regex = 0; + if (level > 0 && is_balanced(last_state(), c)) { + level--; + pop_state(); + continue; + } + style = 'error'; + break; + default: + if (is_digit(c)) { + parse_number(); + can_regex = 0; + break; + } + if (is_word(c)) { + parse_identifier(); + break; + } + can_regex = 1; + continue; + } + if (style) + set_style(start, i); + } + set_style(n, n); + return [ state, level, r ]; + } + + function config_file(s) { + return (std.getenv("HOME") || std.getenv("USERPROFILE") || ".") + "/" + s; + } + function save_history() { + var s = history.slice(-1000).join('\n').trim(); + if (s) { + try { + var f = std.open(config_file(".qjs_history"), "w"); + f.puts(s + '\n'); + f.close(); + } catch (e) { + } + } + } + function load_history() { + var a = std.loadFile(config_file(".qjs_history")); + if (a) { + history = a.trim().split('\n'); + history_index = history.length; + } + } + function load_config() { + var m, s = std.getenv("COLORFGBG"); + if (s && (m = s.match(/(\d+);(\d+)/))) { + if (+m[2] !== 0) { // light background + styles = themes.light; + } + } + s = std.getenv("NO_COLOR"); // https://no-color.org/ + if (s && +s[0] !== 0) { + show_colors = false; + } + } + + load_config(); + load_history(); + termInit(); + cmd_start(); + +})(globalThis); diff --git a/third_party/quickjs/unicode_gen.c b/third_party/quickjs/unicode_gen.c new file mode 100644 index 0000000000..76806e6d02 --- /dev/null +++ b/third_party/quickjs/unicode_gen.c @@ -0,0 +1,3108 @@ +/* + * Generation of Unicode tables + * + * Copyright (c) 2017-2018 Fabrice Bellard + * Copyright (c) 2017-2018 Charlie Gordon + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include <stdlib.h> +#include <stdio.h> +#include <stdarg.h> +#include <inttypes.h> +#include <string.h> +#include <assert.h> +#include <ctype.h> +#include <time.h> + +#include "cutils.h" + +/* define it to be able to test unicode.c */ +//#define USE_TEST +/* profile tests */ +//#define PROFILE + +//#define DUMP_CASE_CONV_TABLE +//#define DUMP_TABLE_SIZE +//#define DUMP_CC_TABLE +//#define DUMP_DECOMP_TABLE +//#define DUMP_CASE_FOLDING_SPECIAL_CASES + +/* Ideas: + - Generalize run length encoding + index for all tables + - remove redundant tables for ID_start, ID_continue, Case_Ignorable, Cased + + Case conversion: + - use a single entry for consecutive U/LF runs + - allow EXT runs of length > 1 + + Decomposition: + - Greek lower case (+1f10/1f10) ? + - allow holes in B runs + - suppress more upper / lower case redundancy +*/ + +#ifdef USE_TEST +#include "libunicode.c" +#endif + +#define CHARCODE_MAX 0x10ffff +#define CC_LEN_MAX 3 + +void *mallocz(size_t size) +{ + void *ptr; + ptr = malloc(size); + memset(ptr, 0, size); + return ptr; +} + +const char *get_field(const char *p, int n) +{ + int i; + for(i = 0; i < n; i++) { + while (*p != ';' && *p != '\0') + p++; + if (*p == '\0') + return NULL; + p++; + } + return p; +} + +const char *get_field_buf(char *buf, size_t buf_size, const char *p, int n) +{ + char *q; + p = get_field(p, n); + q = buf; + while (*p != ';' && *p != '\0') { + if ((q - buf) < buf_size - 1) + *q++ = *p; + p++; + } + *q = '\0'; + return buf; +} + +void add_char(int **pbuf, int *psize, int *plen, int c) +{ + int len, size, *buf; + buf = *pbuf; + size = *psize; + len = *plen; + if (len >= size) { + size = *psize; + size = max_int(len + 1, size * 3 / 2); + buf = realloc(buf, sizeof(buf[0]) * size); + *pbuf = buf; + *psize = size; + } + buf[len++] = c; + *plen = len; +} + +int *get_field_str(int *plen, const char *str, int n) +{ + const char *p; + int *buf, len, size; + p = get_field(str, n); + if (!p) { + *plen = 0; + return NULL; + } + len = 0; + size = 0; + buf = NULL; + for(;;) { + while (isspace(*p)) + p++; + if (!isxdigit(*p)) + break; + add_char(&buf, &size, &len, strtoul(p, (char **)&p, 16)); + } + *plen = len; + return buf; +} + +char *get_line(char *buf, int buf_size, FILE *f) +{ + int len; + if (!fgets(buf, buf_size, f)) + return NULL; + len = strlen(buf); + if (len > 0 && buf[len - 1] == '\n') + buf[len - 1] = '\0'; + return buf; +} + +#define UNICODE_GENERAL_CATEGORY + +typedef enum { +#define DEF(id, str) GCAT_ ## id, +#include "unicode_gen_def.h" +#undef DEF + GCAT_COUNT, +} UnicodeGCEnum1; + +static const char *unicode_gc_name[] = { +#define DEF(id, str) #id, +#include "unicode_gen_def.h" +#undef DEF +}; + +static const char *unicode_gc_short_name[] = { +#define DEF(id, str) str, +#include "unicode_gen_def.h" +#undef DEF +}; + +#undef UNICODE_GENERAL_CATEGORY + +#define UNICODE_SCRIPT + +typedef enum { +#define DEF(id, str) SCRIPT_ ## id, +#include "unicode_gen_def.h" +#undef DEF + SCRIPT_COUNT, +} UnicodeScriptEnum1; + +static const char *unicode_script_name[] = { +#define DEF(id, str) #id, +#include "unicode_gen_def.h" +#undef DEF +}; + +const char *unicode_script_short_name[] = { +#define DEF(id, str) str, +#include "unicode_gen_def.h" +#undef DEF +}; + +#undef UNICODE_SCRIPT + +#define UNICODE_PROP_LIST + +typedef enum { +#define DEF(id, str) PROP_ ## id, +#include "unicode_gen_def.h" +#undef DEF + PROP_COUNT, +} UnicodePropEnum1; + +static const char *unicode_prop_name[] = { +#define DEF(id, str) #id, +#include "unicode_gen_def.h" +#undef DEF +}; + +static const char *unicode_prop_short_name[] = { +#define DEF(id, str) str, +#include "unicode_gen_def.h" +#undef DEF +}; + +#undef UNICODE_PROP_LIST + +typedef struct { + /* case conv */ + uint8_t u_len; + uint8_t l_len; + uint8_t f_len; + int u_data[CC_LEN_MAX]; /* to upper case */ + int l_data[CC_LEN_MAX]; /* to lower case */ + int f_data[CC_LEN_MAX]; /* to case folding */ + + uint8_t combining_class; + uint8_t is_compat:1; + uint8_t is_excluded:1; + uint8_t general_category; + uint8_t script; + uint8_t script_ext_len; + uint8_t *script_ext; + uint32_t prop_bitmap_tab[3]; + /* decomposition */ + int decomp_len; + int *decomp_data; +} CCInfo; + +CCInfo *unicode_db; + +int find_name(const char **tab, int tab_len, const char *name) +{ + int i, len, name_len; + const char *p, *r; + + name_len = strlen(name); + for(i = 0; i < tab_len; i++) { + p = tab[i]; + for(;;) { + r = strchr(p, ','); + if (!r) + len = strlen(p); + else + len = r - p; + if (len == name_len && memcmp(p, name, len) == 0) + return i; + if (!r) + break; + p = r + 1; + } + } + return -1; +} + +static int get_prop(uint32_t c, int prop_idx) +{ + return (unicode_db[c].prop_bitmap_tab[prop_idx >> 5] >> (prop_idx & 0x1f)) & 1; +} + +static void set_prop(uint32_t c, int prop_idx, int val) +{ + uint32_t mask; + mask = 1U << (prop_idx & 0x1f); + if (val) + unicode_db[c].prop_bitmap_tab[prop_idx >> 5] |= mask; + else + unicode_db[c].prop_bitmap_tab[prop_idx >> 5] &= ~mask; +} + +void parse_unicode_data(const char *filename) +{ + FILE *f; + char line[1024]; + char buf1[256]; + const char *p; + int code, lc, uc, last_code; + CCInfo *ci, *tab = unicode_db; + + f = fopen(filename, "rb"); + if (!f) { + perror(filename); + exit(1); + } + + last_code = 0; + for(;;) { + if (!get_line(line, sizeof(line), f)) + break; + p = line; + while (isspace(*p)) + p++; + if (*p == '#') + continue; + + p = get_field(line, 0); + if (!p) + continue; + code = strtoul(p, NULL, 16); + lc = 0; + uc = 0; + + p = get_field(line, 12); + if (p && *p != ';') { + uc = strtoul(p, NULL, 16); + } + + p = get_field(line, 13); + if (p && *p != ';') { + lc = strtoul(p, NULL, 16); + } + ci = &tab[code]; + if (uc > 0 || lc > 0) { + assert(code <= CHARCODE_MAX); + if (uc > 0) { + assert(ci->u_len == 0); + ci->u_len = 1; + ci->u_data[0] = uc; + } + if (lc > 0) { + assert(ci->l_len == 0); + ci->l_len = 1; + ci->l_data[0] = lc; + } + } + + { + int i; + get_field_buf(buf1, sizeof(buf1), line, 2); + i = find_name(unicode_gc_name, countof(unicode_gc_name), buf1); + if (i < 0) { + fprintf(stderr, "General category '%s' not found\n", + buf1); + exit(1); + } + ci->general_category = i; + } + + p = get_field(line, 3); + if (p && *p != ';' && *p != '\0') { + int cc; + cc = strtoul(p, NULL, 0); + if (cc != 0) { + assert(code <= CHARCODE_MAX); + ci->combining_class = cc; + // printf("%05x: %d\n", code, ci->combining_class); + } + } + + p = get_field(line, 5); + if (p && *p != ';' && *p != '\0') { + int size; + assert(code <= CHARCODE_MAX); + ci->is_compat = 0; + if (*p == '<') { + while (*p != '\0' && *p != '>') + p++; + if (*p == '>') + p++; + ci->is_compat = 1; + } + size = 0; + for(;;) { + while (isspace(*p)) + p++; + if (!isxdigit(*p)) + break; + add_char(&ci->decomp_data, &size, &ci->decomp_len, strtoul(p, (char **)&p, 16)); + } + } + + p = get_field(line, 9); + if (p && *p == 'Y') { + set_prop(code, PROP_Bidi_Mirrored, 1); + } + + /* handle ranges */ + get_field_buf(buf1, sizeof(buf1), line, 1); + if (strstr(buf1, " Last>")) { + int i; + // printf("range: 0x%x-%0x\n", last_code, code); + assert(ci->decomp_len == 0); + assert(ci->script_ext_len == 0); + for(i = last_code + 1; i < code; i++) { + unicode_db[i] = *ci; + } + } + last_code = code; + } + + fclose(f); +} + +void parse_special_casing(CCInfo *tab, const char *filename) +{ + FILE *f; + char line[1024]; + const char *p; + int code; + CCInfo *ci; + + f = fopen(filename, "rb"); + if (!f) { + perror(filename); + exit(1); + } + + for(;;) { + if (!get_line(line, sizeof(line), f)) + break; + p = line; + while (isspace(*p)) + p++; + if (*p == '#') + continue; + + p = get_field(line, 0); + if (!p) + continue; + code = strtoul(p, NULL, 16); + assert(code <= CHARCODE_MAX); + ci = &tab[code]; + + p = get_field(line, 4); + if (p) { + /* locale dependent casing */ + while (isspace(*p)) + p++; + if (*p != '#' && *p != '\0') + continue; + } + + + p = get_field(line, 1); + if (p && *p != ';') { + ci->l_len = 0; + for(;;) { + while (isspace(*p)) + p++; + if (*p == ';') + break; + assert(ci->l_len < CC_LEN_MAX); + ci->l_data[ci->l_len++] = strtoul(p, (char **)&p, 16); + } + + if (ci->l_len == 1 && ci->l_data[0] == code) + ci->l_len = 0; + } + + p = get_field(line, 3); + if (p && *p != ';') { + ci->u_len = 0; + for(;;) { + while (isspace(*p)) + p++; + if (*p == ';') + break; + assert(ci->u_len < CC_LEN_MAX); + ci->u_data[ci->u_len++] = strtoul(p, (char **)&p, 16); + } + + if (ci->u_len == 1 && ci->u_data[0] == code) + ci->u_len = 0; + } + } + + fclose(f); +} + +void parse_case_folding(CCInfo *tab, const char *filename) +{ + FILE *f; + char line[1024]; + const char *p; + int code, status; + CCInfo *ci; + + f = fopen(filename, "rb"); + if (!f) { + perror(filename); + exit(1); + } + + for(;;) { + if (!get_line(line, sizeof(line), f)) + break; + p = line; + while (isspace(*p)) + p++; + if (*p == '#') + continue; + + p = get_field(line, 0); + if (!p) + continue; + code = strtoul(p, NULL, 16); + assert(code <= CHARCODE_MAX); + ci = &tab[code]; + + p = get_field(line, 1); + if (!p) + continue; + /* locale dependent casing */ + while (isspace(*p)) + p++; + status = *p; + if (status != 'C' && status != 'S' && status != 'F') + continue; + + p = get_field(line, 2); + assert(p != NULL); + if (status == 'S') { + /* we always select the simple case folding and assume it + * comes after the full case folding case */ + assert(ci->f_len >= 2); + ci->f_len = 0; + } else { + assert(ci->f_len == 0); + } + for(;;) { + while (isspace(*p)) + p++; + if (*p == ';') + break; + assert(ci->l_len < CC_LEN_MAX); + ci->f_data[ci->f_len++] = strtoul(p, (char **)&p, 16); + } + } + + fclose(f); +} + +void parse_composition_exclusions(const char *filename) +{ + FILE *f; + char line[4096], *p; + uint32_t c0; + + f = fopen(filename, "rb"); + if (!f) { + perror(filename); + exit(1); + } + + for(;;) { + if (!get_line(line, sizeof(line), f)) + break; + p = line; + while (isspace(*p)) + p++; + if (*p == '#' || *p == '@' || *p == '\0') + continue; + c0 = strtoul(p, (char **)&p, 16); + assert(c0 > 0 && c0 <= CHARCODE_MAX); + unicode_db[c0].is_excluded = TRUE; + } + fclose(f); +} + +void parse_derived_core_properties(const char *filename) +{ + FILE *f; + char line[4096], *p, buf[256], *q; + uint32_t c0, c1, c; + int i; + + f = fopen(filename, "rb"); + if (!f) { + perror(filename); + exit(1); + } + + for(;;) { + if (!get_line(line, sizeof(line), f)) + break; + p = line; + while (isspace(*p)) + p++; + if (*p == '#' || *p == '@' || *p == '\0') + continue; + c0 = strtoul(p, (char **)&p, 16); + if (*p == '.' && p[1] == '.') { + p += 2; + c1 = strtoul(p, (char **)&p, 16); + } else { + c1 = c0; + } + assert(c1 <= CHARCODE_MAX); + p += strspn(p, " \t"); + if (*p == ';') { + p++; + p += strspn(p, " \t"); + q = buf; + static const char ignore[] = "\t #;"; // includes \0 + while (!memchr(ignore, *p, sizeof(ignore))) { + if ((q - buf) < sizeof(buf) - 1) + *q++ = *p; + p++; + } + *q = '\0'; + i = find_name(unicode_prop_name, + countof(unicode_prop_name), buf); + if (i < 0) { + if (!strcmp(buf, "Grapheme_Link")) + goto next; + fprintf(stderr, "Property not found: %s\n", buf); + exit(1); + } + for(c = c0; c <= c1; c++) { + set_prop(c, i, 1); + } +next: ; + } + } + fclose(f); +} + +void parse_derived_norm_properties(const char *filename) +{ + FILE *f; + char line[4096], *p, buf[256], *q; + uint32_t c0, c1, c; + + f = fopen(filename, "rb"); + if (!f) { + perror(filename); + exit(1); + } + + for(;;) { + if (!get_line(line, sizeof(line), f)) + break; + p = line; + while (isspace(*p)) + p++; + if (*p == '#' || *p == '@' || *p == '\0') + continue; + c0 = strtoul(p, (char **)&p, 16); + if (*p == '.' && p[1] == '.') { + p += 2; + c1 = strtoul(p, (char **)&p, 16); + } else { + c1 = c0; + } + assert(c1 <= CHARCODE_MAX); + p += strspn(p, " \t"); + if (*p == ';') { + p++; + p += strspn(p, " \t"); + q = buf; + while (*p != '\0' && *p != ' ' && *p != '#' && *p != '\t') { + if ((q - buf) < sizeof(buf) - 1) + *q++ = *p; + p++; + } + *q = '\0'; + if (!strcmp(buf, "Changes_When_NFKC_Casefolded")) { + for(c = c0; c <= c1; c++) { + set_prop(c, PROP_Changes_When_NFKC_Casefolded, 1); + } + } + } + } + fclose(f); +} + +void parse_prop_list(const char *filename) +{ + FILE *f; + char line[4096], *p, buf[256], *q; + uint32_t c0, c1, c; + int i; + + f = fopen(filename, "rb"); + if (!f) { + perror(filename); + exit(1); + } + + for(;;) { + if (!get_line(line, sizeof(line), f)) + break; + p = line; + while (isspace(*p)) + p++; + if (*p == '#' || *p == '@' || *p == '\0') + continue; + c0 = strtoul(p, (char **)&p, 16); + if (*p == '.' && p[1] == '.') { + p += 2; + c1 = strtoul(p, (char **)&p, 16); + } else { + c1 = c0; + } + assert(c1 <= CHARCODE_MAX); + p += strspn(p, " \t"); + if (*p == ';') { + p++; + p += strspn(p, " \t"); + q = buf; + while (*p != '\0' && *p != ' ' && *p != '#' && *p != '\t') { + if ((q - buf) < sizeof(buf) - 1) + *q++ = *p; + p++; + } + *q = '\0'; + i = find_name(unicode_prop_name, + countof(unicode_prop_name), buf); + if (i < 0) { + fprintf(stderr, "Property not found: %s\n", buf); + exit(1); + } + for(c = c0; c <= c1; c++) { + set_prop(c, i, 1); + } + } + } + fclose(f); +} + +void parse_scripts(const char *filename) +{ + FILE *f; + char line[4096], *p, buf[256], *q; + uint32_t c0, c1, c; + int i; + + f = fopen(filename, "rb"); + if (!f) { + perror(filename); + exit(1); + } + + for(;;) { + if (!get_line(line, sizeof(line), f)) + break; + p = line; + while (isspace(*p)) + p++; + if (*p == '#' || *p == '@' || *p == '\0') + continue; + c0 = strtoul(p, (char **)&p, 16); + if (*p == '.' && p[1] == '.') { + p += 2; + c1 = strtoul(p, (char **)&p, 16); + } else { + c1 = c0; + } + assert(c1 <= CHARCODE_MAX); + p += strspn(p, " \t"); + if (*p == ';') { + p++; + p += strspn(p, " \t"); + q = buf; + while (*p != '\0' && *p != ' ' && *p != '#' && *p != '\t') { + if ((q - buf) < sizeof(buf) - 1) + *q++ = *p; + p++; + } + *q = '\0'; + i = find_name(unicode_script_name, + countof(unicode_script_name), buf); + if (i < 0) { + fprintf(stderr, "Unknown script: '%s'\n", buf); + exit(1); + } + for(c = c0; c <= c1; c++) + unicode_db[c].script = i; + } + } + fclose(f); +} + +void parse_script_extensions(const char *filename) +{ + FILE *f; + char line[4096], *p, buf[256], *q; + uint32_t c0, c1, c; + int i; + uint8_t script_ext[255]; + int script_ext_len; + + f = fopen(filename, "rb"); + if (!f) { + perror(filename); + exit(1); + } + + for(;;) { + if (!get_line(line, sizeof(line), f)) + break; + p = line; + while (isspace(*p)) + p++; + if (*p == '#' || *p == '@' || *p == '\0') + continue; + c0 = strtoul(p, (char **)&p, 16); + if (*p == '.' && p[1] == '.') { + p += 2; + c1 = strtoul(p, (char **)&p, 16); + } else { + c1 = c0; + } + assert(c1 <= CHARCODE_MAX); + p += strspn(p, " \t"); + script_ext_len = 0; + if (*p == ';') { + p++; + for(;;) { + p += strspn(p, " \t"); + q = buf; + while (*p != '\0' && *p != ' ' && *p != '#' && *p != '\t') { + if ((q - buf) < sizeof(buf) - 1) + *q++ = *p; + p++; + } + *q = '\0'; + if (buf[0] == '\0') + break; + i = find_name(unicode_script_short_name, + countof(unicode_script_short_name), buf); + if (i < 0) { + fprintf(stderr, "Script not found: %s\n", buf); + exit(1); + } + assert(script_ext_len < sizeof(script_ext)); + script_ext[script_ext_len++] = i; + } + for(c = c0; c <= c1; c++) { + CCInfo *ci = &unicode_db[c]; + ci->script_ext_len = script_ext_len; + ci->script_ext = malloc(sizeof(ci->script_ext[0]) * script_ext_len); + for(i = 0; i < script_ext_len; i++) + ci->script_ext[i] = script_ext[i]; + } + } + } + fclose(f); +} + +void dump_cc_info(CCInfo *ci, int i) +{ + int j; + printf("%05x:", i); + if (ci->u_len != 0) { + printf(" U:"); + for(j = 0; j < ci->u_len; j++) + printf(" %05x", ci->u_data[j]); + } + if (ci->l_len != 0) { + printf(" L:"); + for(j = 0; j < ci->l_len; j++) + printf(" %05x", ci->l_data[j]); + } + if (ci->f_len != 0) { + printf(" F:"); + for(j = 0; j < ci->f_len; j++) + printf(" %05x", ci->f_data[j]); + } + printf("\n"); +} + +void dump_unicode_data(CCInfo *tab) +{ + int i; + CCInfo *ci; + for(i = 0; i <= CHARCODE_MAX; i++) { + ci = &tab[i]; + if (ci->u_len != 0 || ci->l_len != 0 || ci->f_len != 0) { + dump_cc_info(ci, i); + } + } +} + +BOOL is_complicated_case(const CCInfo *ci) +{ + return (ci->u_len > 1 || ci->l_len > 1 || + (ci->u_len > 0 && ci->l_len > 0) || + (ci->f_len != ci->l_len) || + (memcmp(ci->f_data, ci->l_data, ci->f_len * sizeof(ci->f_data[0])) != 0)); +} + +#ifndef USE_TEST +enum { + RUN_TYPE_U, + RUN_TYPE_L, + RUN_TYPE_UF, + RUN_TYPE_LF, + RUN_TYPE_UL, + RUN_TYPE_LSU, + RUN_TYPE_U2L_399_EXT2, + RUN_TYPE_UF_D20, + RUN_TYPE_UF_D1_EXT, + RUN_TYPE_U_EXT, + RUN_TYPE_LF_EXT, + RUN_TYPE_UF_EXT2, + RUN_TYPE_LF_EXT2, + RUN_TYPE_UF_EXT3, +}; +#endif + +const char *run_type_str[] = { + "U", + "L", + "UF", + "LF", + "UL", + "LSU", + "U2L_399_EXT2", + "UF_D20", + "UF_D1_EXT", + "U_EXT", + "LF_EXT", + "UF_EXT2", + "LF_EXT2", + "UF_EXT3", +}; + +typedef struct { + int code; + int len; + int type; + int data; + int ext_len; + int ext_data[3]; + int data_index; /* 'data' coming from the table */ +} TableEntry; + +static int simple_to_lower(CCInfo *tab, int c) +{ + if (tab[c].l_len != 1) + return c; + return tab[c].l_data[0]; +} + +/* code (17), len (7), type (4) */ + +void find_run_type(TableEntry *te, CCInfo *tab, int code) +{ + int is_lower, len; + CCInfo *ci, *ci1, *ci2; + + ci = &tab[code]; + ci1 = &tab[code + 1]; + ci2 = &tab[code + 2]; + te->code = code; + + if (ci->l_len == 1 && ci->l_data[0] == code + 2 && + ci->f_len == 1 && ci->f_data[0] == ci->l_data[0] && + ci->u_len == 0 && + + ci1->l_len == 1 && ci1->l_data[0] == code + 2 && + ci1->f_len == 1 && ci1->f_data[0] == ci1->l_data[0] && + ci1->u_len == 1 && ci1->u_data[0] == code && + + ci2->l_len == 0 && + ci2->f_len == 0 && + ci2->u_len == 1 && ci2->u_data[0] == code) { + te->len = 3; + te->data = 0; + te->type = RUN_TYPE_LSU; + return; + } + + if (is_complicated_case(ci)) { + len = 1; + while (code + len <= CHARCODE_MAX) { + ci1 = &tab[code + len]; + if (ci1->u_len != 1 || + ci1->u_data[0] != ci->u_data[0] + len || + ci1->l_len != 0 || + ci1->f_len != 1 || ci1->f_data[0] != ci1->u_data[0]) + break; + len++; + } + if (len > 1) { + te->len = len; + te->type = RUN_TYPE_UF; + te->data = ci->u_data[0]; + return; + } + + if (ci->l_len == 0 && + ci->u_len == 2 && ci->u_data[1] == 0x399 && + ci->f_len == 2 && ci->f_data[1] == 0x3B9 && + ci->f_data[0] == simple_to_lower(tab, ci->u_data[0])) { + len = 1; + while (code + len <= CHARCODE_MAX) { + ci1 = &tab[code + len]; + if (!(ci1->u_len == 2 && + ci1->u_data[1] == ci->u_data[1] && + ci1->u_data[0] == ci->u_data[0] + len && + ci1->f_len == 2 && + ci1->f_data[1] == ci->f_data[1] && + ci1->f_data[0] == ci->f_data[0] + len && + ci1->l_len == 0)) + break; + len++; + } + te->len = len; + te->type = RUN_TYPE_UF_EXT2; + te->ext_data[0] = ci->u_data[0]; + te->ext_data[1] = ci->u_data[1]; + te->ext_len = 2; + return; + } + + if (ci->u_len == 2 && ci->u_data[1] == 0x399 && + ci->l_len == 1 && + ci->f_len == 1 && ci->f_data[0] == ci->l_data[0]) { + len = 1; + while (code + len <= CHARCODE_MAX) { + ci1 = &tab[code + len]; + if (!(ci1->u_len == 2 && + ci1->u_data[1] == 0x399 && + ci1->u_data[0] == ci->u_data[0] + len && + ci1->l_len == 1 && + ci1->l_data[0] == ci->l_data[0] + len && + ci1->f_len == 1 && ci1->f_data[0] == ci1->l_data[0])) + break; + len++; + } + te->len = len; + te->type = RUN_TYPE_U2L_399_EXT2; + te->ext_data[0] = ci->u_data[0]; + te->ext_data[1] = ci->l_data[0]; + te->ext_len = 2; + return; + } + + if (ci->l_len == 1 && ci->u_len == 0 && ci->f_len == 0) { + len = 1; + while (code + len <= CHARCODE_MAX) { + ci1 = &tab[code + len]; + if (!(ci1->l_len == 1 && + ci1->l_data[0] == ci->l_data[0] + len && + ci1->u_len == 0 && ci1->f_len == 0)) + break; + len++; + } + te->len = len; + te->type = RUN_TYPE_L; + te->data = ci->l_data[0]; + return; + } + + if (ci->l_len == 0 && + ci->u_len == 1 && + ci->u_data[0] < 0x1000 && + ci->f_len == 1 && ci->f_data[0] == ci->u_data[0] + 0x20) { + te->len = 1; + te->type = RUN_TYPE_UF_D20; + te->data = ci->u_data[0]; + } else if (ci->l_len == 0 && + ci->u_len == 1 && + ci->f_len == 1 && ci->f_data[0] == ci->u_data[0] + 1) { + te->len = 1; + te->type = RUN_TYPE_UF_D1_EXT; + te->ext_data[0] = ci->u_data[0]; + te->ext_len = 1; + } else if (ci->l_len == 2 && ci->u_len == 0 && ci->f_len == 2 && + ci->l_data[0] == ci->f_data[0] && + ci->l_data[1] == ci->f_data[1]) { + te->len = 1; + te->type = RUN_TYPE_LF_EXT2; + te->ext_data[0] = ci->l_data[0]; + te->ext_data[1] = ci->l_data[1]; + te->ext_len = 2; + } else if (ci->u_len == 2 && ci->l_len == 0 && ci->f_len == 2 && + ci->f_data[0] == simple_to_lower(tab, ci->u_data[0]) && + ci->f_data[1] == simple_to_lower(tab, ci->u_data[1])) { + te->len = 1; + te->type = RUN_TYPE_UF_EXT2; + te->ext_data[0] = ci->u_data[0]; + te->ext_data[1] = ci->u_data[1]; + te->ext_len = 2; + } else if (ci->u_len == 3 && ci->l_len == 0 && ci->f_len == 3 && + ci->f_data[0] == simple_to_lower(tab, ci->u_data[0]) && + ci->f_data[1] == simple_to_lower(tab, ci->u_data[1]) && + ci->f_data[2] == simple_to_lower(tab, ci->u_data[2])) { + te->len = 1; + te->type = RUN_TYPE_UF_EXT3; + te->ext_data[0] = ci->u_data[0]; + te->ext_data[1] = ci->u_data[1]; + te->ext_data[2] = ci->u_data[2]; + te->ext_len = 3; + } else if (ci->u_len == 2 && ci->l_len == 0 && ci->f_len == 1) { + // U+FB05 LATIN SMALL LIGATURE LONG S T + assert(code == 0xFB05); + te->len = 1; + te->type = RUN_TYPE_UF_EXT2; + te->ext_data[0] = ci->u_data[0]; + te->ext_data[1] = ci->u_data[1]; + te->ext_len = 2; + } else if (ci->u_len == 3 && ci->l_len == 0 && ci->f_len == 1) { + // U+1FD3 GREEK SMALL LETTER IOTA WITH DIALYTIKA AND OXIA or + // U+1FE3 GREEK SMALL LETTER UPSILON WITH DIALYTIKA AND OXIA + assert(code == 0x1FD3 || code == 0x1FE3); + te->len = 1; + te->type = RUN_TYPE_UF_EXT3; + te->ext_data[0] = ci->u_data[0]; + te->ext_data[1] = ci->u_data[1]; + te->ext_data[2] = ci->u_data[2]; + te->ext_len = 3; + } else { + printf("unsupported encoding case:\n"); + dump_cc_info(ci, code); + abort(); + } + } else { + /* look for a run of identical conversions */ + len = 0; + for(;;) { + if (code >= CHARCODE_MAX || len >= 126) + break; + ci = &tab[code + len]; + ci1 = &tab[code + len + 1]; + if (is_complicated_case(ci) || is_complicated_case(ci1)) { + break; + } + if (ci->l_len != 1 || ci->l_data[0] != code + len + 1) + break; + if (ci1->u_len != 1 || ci1->u_data[0] != code + len) + break; + len += 2; + } + if (len > 0) { + te->len = len; + te->type = RUN_TYPE_UL; + te->data = 0; + return; + } + + ci = &tab[code]; + is_lower = ci->l_len > 0; + len = 1; + while (code + len <= CHARCODE_MAX) { + ci1 = &tab[code + len]; + if (is_complicated_case(ci1)) + break; + if (is_lower) { + if (ci1->l_len != 1 || + ci1->l_data[0] != ci->l_data[0] + len) + break; + } else { + if (ci1->u_len != 1 || + ci1->u_data[0] != ci->u_data[0] + len) + break; + } + len++; + } + te->len = len; + if (is_lower) { + te->type = RUN_TYPE_LF; + te->data = ci->l_data[0]; + } else { + te->type = RUN_TYPE_U; + te->data = ci->u_data[0]; + } + } +} + +TableEntry conv_table[1000]; +int conv_table_len; +int ext_data[1000]; +int ext_data_len; + +void dump_case_conv_table1(void) +{ + int i, j; + const TableEntry *te; + + for(i = 0; i < conv_table_len; i++) { + te = &conv_table[i]; + printf("%05x %02x %-10s %05x", + te->code, te->len, run_type_str[te->type], te->data); + for(j = 0; j < te->ext_len; j++) { + printf(" %05x", te->ext_data[j]); + } + printf("\n"); + } + printf("table_len=%d ext_len=%d\n", conv_table_len, ext_data_len); +} + +int find_data_index(const TableEntry *conv_table, int len, int data) +{ + int i; + const TableEntry *te; + for(i = 0; i < len; i++) { + te = &conv_table[i]; + if (te->code == data) + return i; + } + return -1; +} + +int find_ext_data_index(int data) +{ + int i; + for(i = 0; i < ext_data_len; i++) { + if (ext_data[i] == data) + return i; + } + assert(ext_data_len < countof(ext_data)); + ext_data[ext_data_len++] = data; + return ext_data_len - 1; +} + +void build_conv_table(CCInfo *tab) +{ + int code, i, j; + CCInfo *ci; + TableEntry *te; + + te = conv_table; + for(code = 0; code <= CHARCODE_MAX; code++) { + ci = &tab[code]; + if (ci->u_len == 0 && ci->l_len == 0 && ci->f_len == 0) + continue; + assert(te - conv_table < countof(conv_table)); + find_run_type(te, tab, code); + assert(te->len <= 127); + code += te->len - 1; + te++; + } + conv_table_len = te - conv_table; + + /* find the data index */ + for(i = 0; i < conv_table_len; i++) { + int data_index; + te = &conv_table[i]; + + switch(te->type) { + case RUN_TYPE_U: + case RUN_TYPE_L: + case RUN_TYPE_UF: + case RUN_TYPE_LF: + data_index = find_data_index(conv_table, conv_table_len, te->data); + if (data_index < 0) { + switch(te->type) { + case RUN_TYPE_U: + te->type = RUN_TYPE_U_EXT; + te->ext_len = 1; + te->ext_data[0] = te->data; + break; + case RUN_TYPE_LF: + te->type = RUN_TYPE_LF_EXT; + te->ext_len = 1; + te->ext_data[0] = te->data; + break; + default: + printf("%05x: index not found\n", te->code); + exit(1); + } + } else { + te->data_index = data_index; + } + break; + case RUN_TYPE_UF_D20: + te->data_index = te->data; + break; + } + } + + /* find the data index for ext_data */ + for(i = 0; i < conv_table_len; i++) { + te = &conv_table[i]; + if (te->type == RUN_TYPE_UF_EXT3) { + int p, v; + v = 0; + for(j = 0; j < 3; j++) { + p = find_ext_data_index(te->ext_data[j]); + assert(p < 16); + v = (v << 4) | p; + } + te->data_index = v; + } + } + + for(i = 0; i < conv_table_len; i++) { + te = &conv_table[i]; + if (te->type == RUN_TYPE_LF_EXT2 || + te->type == RUN_TYPE_UF_EXT2 || + te->type == RUN_TYPE_U2L_399_EXT2) { + int p, v; + v = 0; + for(j = 0; j < 2; j++) { + p = find_ext_data_index(te->ext_data[j]); + assert(p < 64); + v = (v << 6) | p; + } + te->data_index = v; + } + } + + for(i = 0; i < conv_table_len; i++) { + te = &conv_table[i]; + if (te->type == RUN_TYPE_UF_D1_EXT || + te->type == RUN_TYPE_U_EXT || + te->type == RUN_TYPE_LF_EXT) { + te->data_index = find_ext_data_index(te->ext_data[0]); + } + } +#ifdef DUMP_CASE_CONV_TABLE + dump_case_conv_table1(); +#endif +} + +void dump_case_conv_table(FILE *f) +{ + int i; + uint32_t v; + const TableEntry *te; + + fprintf(f, "static const uint32_t case_conv_table1[%u] = {", conv_table_len); + for(i = 0; i < conv_table_len; i++) { + if (i % 4 == 0) + fprintf(f, "\n "); + te = &conv_table[i]; + v = te->code << (32 - 17); + v |= te->len << (32 - 17 - 7); + v |= te->type << (32 - 17 - 7 - 4); + v |= te->data_index >> 8; + fprintf(f, " 0x%08x,", v); + } + fprintf(f, "\n};\n\n"); + + fprintf(f, "static const uint8_t case_conv_table2[%u] = {", conv_table_len); + for(i = 0; i < conv_table_len; i++) { + if (i % 8 == 0) + fprintf(f, "\n "); + te = &conv_table[i]; + fprintf(f, " 0x%02x,", te->data_index & 0xff); + } + fprintf(f, "\n};\n\n"); + + fprintf(f, "static const uint16_t case_conv_ext[%u] = {", ext_data_len); + for(i = 0; i < ext_data_len; i++) { + if (i % 8 == 0) + fprintf(f, "\n "); + fprintf(f, " 0x%04x,", ext_data[i]); + } + fprintf(f, "\n};\n\n"); +} + + +static CCInfo *global_tab; + +static int sp_cc_cmp(const void *p1, const void *p2) +{ + CCInfo *c1 = &global_tab[*(const int *)p1]; + CCInfo *c2 = &global_tab[*(const int *)p2]; + if (c1->f_len < c2->f_len) { + return -1; + } else if (c2->f_len < c1->f_len) { + return 1; + } else { + return memcmp(c1->f_data, c2->f_data, sizeof(c1->f_data[0]) * c1->f_len); + } +} + +/* dump the case special cases (multi character results which are + identical and need specific handling in lre_canonicalize() */ +void dump_case_folding_special_cases(CCInfo *tab) +{ + int i, len, j; + int *perm; + + perm = malloc(sizeof(perm[0]) * (CHARCODE_MAX + 1)); + for(i = 0; i <= CHARCODE_MAX; i++) + perm[i] = i; + global_tab = tab; + qsort(perm, CHARCODE_MAX + 1, sizeof(perm[0]), sp_cc_cmp); + for(i = 0; i <= CHARCODE_MAX;) { + if (tab[perm[i]].f_len <= 1) { + i++; + } else { + len = 1; + while ((i + len) <= CHARCODE_MAX && !sp_cc_cmp(&perm[i], &perm[i + len])) + len++; + + if (len > 1) { + for(j = i; j < i + len; j++) + dump_cc_info(&tab[perm[j]], perm[j]); + } + i += len; + } + } + free(perm); + global_tab = NULL; +} + + +int tabcmp(const int *tab1, const int *tab2, int n) +{ + int i; + for(i = 0; i < n; i++) { + if (tab1[i] != tab2[i]) + return -1; + } + return 0; +} + +void dump_str(const char *str, const int *buf, int len) +{ + int i; + printf("%s=", str); + for(i = 0; i < len; i++) + printf(" %05x", buf[i]); + printf("\n"); +} + +void compute_internal_props(void) +{ + int i; + BOOL has_ul; + + for(i = 0; i <= CHARCODE_MAX; i++) { + CCInfo *ci = &unicode_db[i]; + has_ul = (ci->u_len != 0 || ci->l_len != 0 || ci->f_len != 0); + if (has_ul) { + assert(get_prop(i, PROP_Cased)); + } else { + set_prop(i, PROP_Cased1, get_prop(i, PROP_Cased)); + } + set_prop(i, PROP_ID_Continue1, + get_prop(i, PROP_ID_Continue) & (get_prop(i, PROP_ID_Start) ^ 1)); + set_prop(i, PROP_XID_Start1, + get_prop(i, PROP_ID_Start) ^ get_prop(i, PROP_XID_Start)); + set_prop(i, PROP_XID_Continue1, + get_prop(i, PROP_ID_Continue) ^ get_prop(i, PROP_XID_Continue)); + set_prop(i, PROP_Changes_When_Titlecased1, + get_prop(i, PROP_Changes_When_Titlecased) ^ (ci->u_len != 0)); + set_prop(i, PROP_Changes_When_Casefolded1, + get_prop(i, PROP_Changes_When_Casefolded) ^ (ci->f_len != 0)); + /* XXX: reduce table size (438 bytes) */ + set_prop(i, PROP_Changes_When_NFKC_Casefolded1, + get_prop(i, PROP_Changes_When_NFKC_Casefolded) ^ (ci->f_len != 0)); + } +} + +void dump_byte_table(FILE *f, const char *cname, const uint8_t *tab, int len) +{ + int i; + fprintf(f, "static const uint8_t %s[%d] = {", cname, len); + for(i = 0; i < len; i++) { + if (i % 8 == 0) + fprintf(f, "\n "); + fprintf(f, " 0x%02x,", tab[i]); + } + fprintf(f, "\n};\n\n"); +} + +#define PROP_BLOCK_LEN 32 + +void build_prop_table(FILE *f, int prop_index, BOOL add_index) +{ + int i, j, n, v, offset, code; + DynBuf dbuf_s, *dbuf = &dbuf_s; + DynBuf dbuf1_s, *dbuf1 = &dbuf1_s; + DynBuf dbuf2_s, *dbuf2 = &dbuf2_s; + const uint32_t *buf; + int buf_len, block_end_pos, bit; + char cname[128]; + + dbuf_init(dbuf1); + + for(i = 0; i <= CHARCODE_MAX;) { + v = get_prop(i, prop_index); + j = i + 1; + while (j <= CHARCODE_MAX && get_prop(j, prop_index) == v) { + j++; + } + n = j - i; + if (j == (CHARCODE_MAX + 1) && v == 0) + break; /* no need to encode last zero run */ + //printf("%05x: %d %d\n", i, n, v); + dbuf_put_u32(dbuf1, n - 1); + i += n; + } + + dbuf_init(dbuf); + dbuf_init(dbuf2); + buf = (uint32_t *)dbuf1->buf; + buf_len = dbuf1->size / sizeof(buf[0]); + + /* the first value is assumed to be 0 */ + assert(get_prop(0, prop_index) == 0); + + block_end_pos = PROP_BLOCK_LEN; + i = 0; + code = 0; + bit = 0; + while (i < buf_len) { + if (add_index && dbuf->size >= block_end_pos && bit == 0) { + offset = (dbuf->size - block_end_pos); + /* XXX: offset could be larger in case of runs of small + lengths. Could add code to change the encoding to + prevent it at the expense of one byte loss */ + assert(offset <= 7); + v = code | (offset << 21); + dbuf_putc(dbuf2, v); + dbuf_putc(dbuf2, v >> 8); + dbuf_putc(dbuf2, v >> 16); + block_end_pos += PROP_BLOCK_LEN; + } + + v = buf[i]; + code += v + 1; + bit ^= 1; + if (v < 8 && (i + 1) < buf_len && buf[i + 1] < 8) { + code += buf[i + 1] + 1; + bit ^= 1; + dbuf_putc(dbuf, (v << 3) | buf[i + 1]); + i += 2; + } else if (v < 128) { + dbuf_putc(dbuf, 0x80 + v); + i++; + } else if (v < (1 << 13)) { + dbuf_putc(dbuf, 0x40 + (v >> 8)); + dbuf_putc(dbuf, v); + i++; + } else { + assert(v < (1 << 21)); + dbuf_putc(dbuf, 0x60 + (v >> 16)); + dbuf_putc(dbuf, v >> 8); + dbuf_putc(dbuf, v); + i++; + } + } + + if (add_index) { + /* last index entry */ + v = code; + dbuf_putc(dbuf2, v); + dbuf_putc(dbuf2, v >> 8); + dbuf_putc(dbuf2, v >> 16); + } + +#ifdef DUMP_TABLE_SIZE + printf("prop %s: length=%d bytes\n", unicode_prop_name[prop_index], + (int)(dbuf->size + dbuf2->size)); +#endif + snprintf(cname, sizeof(cname), "unicode_prop_%s_table", unicode_prop_name[prop_index]); + dump_byte_table(f, cname, dbuf->buf, dbuf->size); + if (add_index) { + snprintf(cname, sizeof(cname), "unicode_prop_%s_index", unicode_prop_name[prop_index]); + dump_byte_table(f, cname, dbuf2->buf, dbuf2->size); + } + + dbuf_free(dbuf); + dbuf_free(dbuf1); + dbuf_free(dbuf2); +} + +void build_flags_tables(FILE *f) +{ + build_prop_table(f, PROP_Cased1, TRUE); + build_prop_table(f, PROP_Case_Ignorable, TRUE); + build_prop_table(f, PROP_ID_Start, TRUE); + build_prop_table(f, PROP_ID_Continue1, TRUE); + build_prop_table(f, PROP_White_Space, TRUE); +} + +void dump_name_table(FILE *f, const char *cname, const char **tab_name, int len, + const char **tab_short_name) +{ + int i, w, maxw; + + maxw = 0; + for(i = 0; i < len; i++) { + w = strlen(tab_name[i]); + if (tab_short_name[i][0] != '\0') { + w += 1 + strlen(tab_short_name[i]); + } + if (maxw < w) + maxw = w; + } + + /* generate a sequence of strings terminated by an empty string */ + fprintf(f, "static const char %s[] =\n", cname); + for(i = 0; i < len; i++) { + fprintf(f, " \""); + w = fprintf(f, "%s", tab_name[i]); + if (tab_short_name[i][0] != '\0') { + w += fprintf(f, ",%s", tab_short_name[i]); + } + fprintf(f, "\"%*s\"\\0\"\n", 1 + maxw - w, ""); + } + fprintf(f, ";\n\n"); +} + +void build_general_category_table(FILE *f) +{ + int i, v, j, n, n1; + DynBuf dbuf_s, *dbuf = &dbuf_s; + int cw_count, cw_len_count[4], cw_start; + + fprintf(f, "typedef enum {\n"); + for(i = 0; i < GCAT_COUNT; i++) + fprintf(f, " UNICODE_GC_%s,\n", unicode_gc_name[i]); + fprintf(f, " UNICODE_GC_COUNT,\n"); + fprintf(f, "} UnicodeGCEnum;\n\n"); + + dump_name_table(f, "unicode_gc_name_table", + unicode_gc_name, GCAT_COUNT, + unicode_gc_short_name); + + + dbuf_init(dbuf); + cw_count = 0; + for(i = 0; i < 4; i++) + cw_len_count[i] = 0; + for(i = 0; i <= CHARCODE_MAX;) { + v = unicode_db[i].general_category; + j = i + 1; + while (j <= CHARCODE_MAX && unicode_db[j].general_category == v) + j++; + n = j - i; + /* compress Lu/Ll runs */ + if (v == GCAT_Lu) { + n1 = 1; + while ((i + n1) <= CHARCODE_MAX && unicode_db[i + n1].general_category == (v + (n1 & 1))) { + n1++; + } + if (n1 > n) { + v = 31; + n = n1; + } + } + // printf("%05x %05x %d\n", i, n, v); + cw_count++; + n--; + cw_start = dbuf->size; + if (n < 7) { + dbuf_putc(dbuf, (n << 5) | v); + } else if (n < 7 + 128) { + n1 = n - 7; + assert(n1 < 128); + dbuf_putc(dbuf, (0xf << 5) | v); + dbuf_putc(dbuf, n1); + } else if (n < 7 + 128 + (1 << 14)) { + n1 = n - (7 + 128); + assert(n1 < (1 << 14)); + dbuf_putc(dbuf, (0xf << 5) | v); + dbuf_putc(dbuf, (n1 >> 8) + 128); + dbuf_putc(dbuf, n1); + } else { + n1 = n - (7 + 128 + (1 << 14)); + assert(n1 < (1 << 22)); + dbuf_putc(dbuf, (0xf << 5) | v); + dbuf_putc(dbuf, (n1 >> 16) + 128 + 64); + dbuf_putc(dbuf, n1 >> 8); + dbuf_putc(dbuf, n1); + } + cw_len_count[dbuf->size - cw_start - 1]++; + i += n + 1; + } +#ifdef DUMP_TABLE_SIZE + printf("general category: %d entries [", + cw_count); + for(i = 0; i < 4; i++) + printf(" %d", cw_len_count[i]); + printf(" ], length=%d bytes\n", (int)dbuf->size); +#endif + + dump_byte_table(f, "unicode_gc_table", dbuf->buf, dbuf->size); + + dbuf_free(dbuf); +} + +void build_script_table(FILE *f) +{ + int i, v, j, n, n1, type; + DynBuf dbuf_s, *dbuf = &dbuf_s; + int cw_count, cw_len_count[4], cw_start; + + fprintf(f, "typedef enum {\n"); + for(i = 0; i < SCRIPT_COUNT; i++) + fprintf(f, " UNICODE_SCRIPT_%s,\n", unicode_script_name[i]); + fprintf(f, " UNICODE_SCRIPT_COUNT,\n"); + fprintf(f, "} UnicodeScriptEnum;\n\n"); + + i = 1; + dump_name_table(f, "unicode_script_name_table", + unicode_script_name + i, SCRIPT_COUNT - i, + unicode_script_short_name + i); + + dbuf_init(dbuf); + cw_count = 0; + for(i = 0; i < 4; i++) + cw_len_count[i] = 0; + for(i = 0; i <= CHARCODE_MAX;) { + v = unicode_db[i].script; + j = i + 1; + while (j <= CHARCODE_MAX && unicode_db[j].script == v) + j++; + n = j - i; + if (v == 0 && j == (CHARCODE_MAX + 1)) + break; + // printf("%05x %05x %d\n", i, n, v); + cw_count++; + n--; + cw_start = dbuf->size; + if (v == 0) + type = 0; + else + type = 1; + if (n < 96) { + dbuf_putc(dbuf, n | (type << 7)); + } else if (n < 96 + (1 << 12)) { + n1 = n - 96; + assert(n1 < (1 << 12)); + dbuf_putc(dbuf, ((n1 >> 8) + 96) | (type << 7)); + dbuf_putc(dbuf, n1); + } else { + n1 = n - (96 + (1 << 12)); + assert(n1 < (1 << 20)); + dbuf_putc(dbuf, ((n1 >> 16) + 112) | (type << 7)); + dbuf_putc(dbuf, n1 >> 8); + dbuf_putc(dbuf, n1); + } + if (type != 0) + dbuf_putc(dbuf, v); + + cw_len_count[dbuf->size - cw_start - 1]++; + i += n + 1; + } +#if defined(DUMP_TABLE_SIZE) + printf("script: %d entries [", + cw_count); + for(i = 0; i < 4; i++) + printf(" %d", cw_len_count[i]); + printf(" ], length=%d bytes\n", (int)dbuf->size); +#endif + + dump_byte_table(f, "unicode_script_table", dbuf->buf, dbuf->size); + + dbuf_free(dbuf); +} + +void build_script_ext_table(FILE *f) +{ + int i, j, n, n1, script_ext_len; + DynBuf dbuf_s, *dbuf = &dbuf_s; + int cw_count; + + dbuf_init(dbuf); + cw_count = 0; + for(i = 0; i <= CHARCODE_MAX;) { + script_ext_len = unicode_db[i].script_ext_len; + j = i + 1; + while (j <= CHARCODE_MAX && + unicode_db[j].script_ext_len == script_ext_len && + !memcmp(unicode_db[j].script_ext, unicode_db[i].script_ext, + script_ext_len)) { + j++; + } + n = j - i; + cw_count++; + n--; + if (n < 128) { + dbuf_putc(dbuf, n); + } else if (n < 128 + (1 << 14)) { + n1 = n - 128; + assert(n1 < (1 << 14)); + dbuf_putc(dbuf, (n1 >> 8) + 128); + dbuf_putc(dbuf, n1); + } else { + n1 = n - (128 + (1 << 14)); + assert(n1 < (1 << 22)); + dbuf_putc(dbuf, (n1 >> 16) + 128 + 64); + dbuf_putc(dbuf, n1 >> 8); + dbuf_putc(dbuf, n1); + } + dbuf_putc(dbuf, script_ext_len); + for(j = 0; j < script_ext_len; j++) + dbuf_putc(dbuf, unicode_db[i].script_ext[j]); + i += n + 1; + } +#ifdef DUMP_TABLE_SIZE + printf("script_ext: %d entries", + cw_count); + printf(", length=%d bytes\n", (int)dbuf->size); +#endif + + dump_byte_table(f, "unicode_script_ext_table", dbuf->buf, dbuf->size); + + dbuf_free(dbuf); +} + +/* the following properties are synthetized so no table is necessary */ +#define PROP_TABLE_COUNT PROP_ASCII + +void build_prop_list_table(FILE *f) +{ + int i; + + for(i = 0; i < PROP_TABLE_COUNT; i++) { + if (i == PROP_ID_Start || + i == PROP_Case_Ignorable || + i == PROP_ID_Continue1 || + i == PROP_White_Space) { + /* already generated */ + } else { + build_prop_table(f, i, FALSE); + } + } + + fprintf(f, "typedef enum {\n"); + for(i = 0; i < PROP_COUNT; i++) + fprintf(f, " UNICODE_PROP_%s,\n", unicode_prop_name[i]); + fprintf(f, " UNICODE_PROP_COUNT,\n"); + fprintf(f, "} UnicodePropertyEnum;\n\n"); + + i = PROP_ASCII_Hex_Digit; + dump_name_table(f, "unicode_prop_name_table", + unicode_prop_name + i, PROP_XID_Start - i + 1, + unicode_prop_short_name + i); + + fprintf(f, "static const uint8_t * const unicode_prop_table[] = {\n"); + for(i = 0; i < PROP_TABLE_COUNT; i++) { + fprintf(f, " unicode_prop_%s_table,\n", unicode_prop_name[i]); + } + fprintf(f, "};\n\n"); + + fprintf(f, "static const uint16_t unicode_prop_len_table[] = {\n"); + for(i = 0; i < PROP_TABLE_COUNT; i++) { + fprintf(f, " countof(unicode_prop_%s_table),\n", unicode_prop_name[i]); + } + fprintf(f, "};\n\n"); +} + +#ifdef USE_TEST +int check_conv(uint32_t *res, uint32_t c, int conv_type) +{ + return lre_case_conv(res, c, conv_type); +} + +void check_case_conv(void) +{ + CCInfo *tab = unicode_db; + uint32_t res[3]; + int l, error; + CCInfo ci_s, *ci1, *ci = &ci_s; + int code; + + for(code = 0; code <= CHARCODE_MAX; code++) { + ci1 = &tab[code]; + *ci = *ci1; + if (ci->l_len == 0) { + ci->l_len = 1; + ci->l_data[0] = code; + } + if (ci->u_len == 0) { + ci->u_len = 1; + ci->u_data[0] = code; + } + if (ci->f_len == 0) { + ci->f_len = 1; + ci->f_data[0] = code; + } + + error = 0; + l = check_conv(res, code, 0); + if (l != ci->u_len || tabcmp((int *)res, ci->u_data, l)) { + printf("ERROR: L\n"); + error++; + } + l = check_conv(res, code, 1); + if (l != ci->l_len || tabcmp((int *)res, ci->l_data, l)) { + printf("ERROR: U\n"); + error++; + } + l = check_conv(res, code, 2); + if (l != ci->f_len || tabcmp((int *)res, ci->f_data, l)) { + printf("ERROR: F\n"); + error++; + } + if (error) { + dump_cc_info(ci, code); + exit(1); + } + } +} + +#ifdef PROFILE +static int64_t get_time_ns(void) +{ + struct timespec ts; + clock_gettime(CLOCK_MONOTONIC, &ts); + return (int64_t)ts.tv_sec * 1000000000 + ts.tv_nsec; +} +#endif + + +void check_flags(void) +{ + int c; + BOOL flag_ref, flag; + for(c = 0; c <= CHARCODE_MAX; c++) { + flag_ref = get_prop(c, PROP_Cased); + flag = lre_is_cased(c); + if (flag != flag_ref) { + printf("ERROR: c=%05x cased=%d ref=%d\n", + c, flag, flag_ref); + exit(1); + } + + flag_ref = get_prop(c, PROP_Case_Ignorable); + flag = lre_is_case_ignorable(c); + if (flag != flag_ref) { + printf("ERROR: c=%05x case_ignorable=%d ref=%d\n", + c, flag, flag_ref); + exit(1); + } + + flag_ref = get_prop(c, PROP_ID_Start); + flag = lre_is_id_start(c); + if (flag != flag_ref) { + printf("ERROR: c=%05x id_start=%d ref=%d\n", + c, flag, flag_ref); + exit(1); + } + + flag_ref = get_prop(c, PROP_ID_Continue); + flag = lre_is_id_continue(c); + if (flag != flag_ref) { + printf("ERROR: c=%05x id_cont=%d ref=%d\n", + c, flag, flag_ref); + exit(1); + } + } +#ifdef PROFILE + { + int64_t ti, count; + ti = get_time_ns(); + count = 0; + for(c = 0x20; c <= 0xffff; c++) { + flag_ref = get_prop(c, PROP_ID_Start); + flag = lre_is_id_start(c); + assert(flag == flag_ref); + count++; + } + ti = get_time_ns() - ti; + printf("flags time=%0.1f ns/char\n", + (double)ti / count); + } +#endif +} + +#endif + +#define CC_BLOCK_LEN 32 + +void build_cc_table(FILE *f) +{ + int i, cc, n, cc_table_len, type, n1; + DynBuf dbuf_s, *dbuf = &dbuf_s; + DynBuf dbuf1_s, *dbuf1 = &dbuf1_s; + int cw_len_tab[3], cw_start, block_end_pos; + uint32_t v; + + dbuf_init(dbuf); + dbuf_init(dbuf1); + cc_table_len = 0; + for(i = 0; i < countof(cw_len_tab); i++) + cw_len_tab[i] = 0; + block_end_pos = CC_BLOCK_LEN; + for(i = 0; i <= CHARCODE_MAX;) { + cc = unicode_db[i].combining_class; + assert(cc <= 255); + /* check increasing values */ + n = 1; + while ((i + n) <= CHARCODE_MAX && + unicode_db[i + n].combining_class == (cc + n)) + n++; + if (n >= 2) { + type = 1; + } else { + type = 0; + n = 1; + while ((i + n) <= CHARCODE_MAX && + unicode_db[i + n].combining_class == cc) + n++; + } + /* no need to encode the last run */ + if (cc == 0 && (i + n - 1) == CHARCODE_MAX) + break; +#ifdef DUMP_CC_TABLE + printf("%05x %6d %d %d\n", i, n, type, cc); +#endif + if (type == 0) { + if (cc == 0) + type = 2; + else if (cc == 230) + type = 3; + } + n1 = n - 1; + + /* add an entry to the index if necessary */ + if (dbuf->size >= block_end_pos) { + v = i | ((dbuf->size - block_end_pos) << 21); + dbuf_putc(dbuf1, v); + dbuf_putc(dbuf1, v >> 8); + dbuf_putc(dbuf1, v >> 16); + block_end_pos += CC_BLOCK_LEN; + } + cw_start = dbuf->size; + if (n1 < 48) { + dbuf_putc(dbuf, n1 | (type << 6)); + } else if (n1 < 48 + (1 << 11)) { + n1 -= 48; + dbuf_putc(dbuf, ((n1 >> 8) + 48) | (type << 6)); + dbuf_putc(dbuf, n1); + } else { + n1 -= 48 + (1 << 11); + assert(n1 < (1 << 20)); + dbuf_putc(dbuf, ((n1 >> 16) + 56) | (type << 6)); + dbuf_putc(dbuf, n1 >> 8); + dbuf_putc(dbuf, n1); + } + cw_len_tab[dbuf->size - cw_start - 1]++; + if (type == 0 || type == 1) + dbuf_putc(dbuf, cc); + cc_table_len++; + i += n; + } + + /* last index entry */ + v = i; + dbuf_putc(dbuf1, v); + dbuf_putc(dbuf1, v >> 8); + dbuf_putc(dbuf1, v >> 16); + + dump_byte_table(f, "unicode_cc_table", dbuf->buf, dbuf->size); + dump_byte_table(f, "unicode_cc_index", dbuf1->buf, dbuf1->size); + +#if defined(DUMP_CC_TABLE) || defined(DUMP_TABLE_SIZE) + printf("CC table: size=%d (%d entries) [", + (int)(dbuf->size + dbuf1->size), + cc_table_len); + for(i = 0; i < countof(cw_len_tab); i++) + printf(" %d", cw_len_tab[i]); + printf(" ]\n"); +#endif + dbuf_free(dbuf); + dbuf_free(dbuf1); +} + +/* maximum length of decomposition: 18 chars (1), then 8 */ +#ifndef USE_TEST +typedef enum { + DECOMP_TYPE_C1, /* 16 bit char */ + DECOMP_TYPE_L1, /* 16 bit char table */ + DECOMP_TYPE_L2, + DECOMP_TYPE_L3, + DECOMP_TYPE_L4, + DECOMP_TYPE_L5, /* XXX: not used */ + DECOMP_TYPE_L6, /* XXX: could remove */ + DECOMP_TYPE_L7, /* XXX: could remove */ + DECOMP_TYPE_LL1, /* 18 bit char table */ + DECOMP_TYPE_LL2, + DECOMP_TYPE_S1, /* 8 bit char table */ + DECOMP_TYPE_S2, + DECOMP_TYPE_S3, + DECOMP_TYPE_S4, + DECOMP_TYPE_S5, + DECOMP_TYPE_I1, /* increment 16 bit char value */ + DECOMP_TYPE_I2_0, + DECOMP_TYPE_I2_1, + DECOMP_TYPE_I3_1, + DECOMP_TYPE_I3_2, + DECOMP_TYPE_I4_1, + DECOMP_TYPE_I4_2, + DECOMP_TYPE_B1, /* 16 bit base + 8 bit offset */ + DECOMP_TYPE_B2, + DECOMP_TYPE_B3, + DECOMP_TYPE_B4, + DECOMP_TYPE_B5, + DECOMP_TYPE_B6, + DECOMP_TYPE_B7, + DECOMP_TYPE_B8, + DECOMP_TYPE_B18, + DECOMP_TYPE_LS2, + DECOMP_TYPE_PAT3, + DECOMP_TYPE_S2_UL, + DECOMP_TYPE_LS2_UL, +} DecompTypeEnum; +#endif + +const char *decomp_type_str[] = { + "C1", + "L1", + "L2", + "L3", + "L4", + "L5", + "L6", + "L7", + "LL1", + "LL2", + "S1", + "S2", + "S3", + "S4", + "S5", + "I1", + "I2_0", + "I2_1", + "I3_1", + "I3_2", + "I4_1", + "I4_2", + "B1", + "B2", + "B3", + "B4", + "B5", + "B6", + "B7", + "B8", + "B18", + "LS2", + "PAT3", + "S2_UL", + "LS2_UL", +}; + +const int decomp_incr_tab[4][4] = { + { DECOMP_TYPE_I1, 0, -1 }, + { DECOMP_TYPE_I2_0, 0, 1, -1 }, + { DECOMP_TYPE_I3_1, 1, 2, -1 }, + { DECOMP_TYPE_I4_1, 1, 2, -1 }, +}; + +/* + entry size: + type bits + code 18 + len 7 + compat 1 + type 5 + index 16 + total 47 +*/ + +typedef struct { + int code; + uint8_t len; + uint8_t type; + uint8_t c_len; + uint16_t c_min; + uint16_t data_index; + int cost; /* size in bytes from this entry to the end */ +} DecompEntry; + +int get_decomp_run_size(const DecompEntry *de) +{ + int s; + s = 6; + if (de->type <= DECOMP_TYPE_C1) { + /* nothing more */ + } else if (de->type <= DECOMP_TYPE_L7) { + s += de->len * de->c_len * 2; + } else if (de->type <= DECOMP_TYPE_LL2) { + /* 18 bits per char */ + s += (de->len * de->c_len * 18 + 7) / 8; + } else if (de->type <= DECOMP_TYPE_S5) { + s += de->len * de->c_len; + } else if (de->type <= DECOMP_TYPE_I4_2) { + s += de->c_len * 2; + } else if (de->type <= DECOMP_TYPE_B18) { + s += 2 + de->len * de->c_len; + } else if (de->type <= DECOMP_TYPE_LS2) { + s += de->len * 3; + } else if (de->type <= DECOMP_TYPE_PAT3) { + s += 4 + de->len * 2; + } else if (de->type <= DECOMP_TYPE_S2_UL) { + s += de->len; + } else if (de->type <= DECOMP_TYPE_LS2_UL) { + s += (de->len / 2) * 3; + } else { + abort(); + } + return s; +} + +static const uint16_t unicode_short_table[2] = { 0x2044, 0x2215 }; + +/* return -1 if not found */ +int get_short_code(int c) +{ + int i; + if (c < 0x80) { + return c; + } else if (c >= 0x300 && c < 0x350) { + return c - 0x300 + 0x80; + } else { + for(i = 0; i < countof(unicode_short_table); i++) { + if (c == unicode_short_table[i]) + return i + 0x80 + 0x50; + } + return -1; + } +} + +static BOOL is_short(int code) +{ + return get_short_code(code) >= 0; +} + +static BOOL is_short_tab(const int *tab, int len) +{ + int i; + for(i = 0; i < len; i++) { + if (!is_short(tab[i])) + return FALSE; + } + return TRUE; +} + +static BOOL is_16bit(const int *tab, int len) +{ + int i; + for(i = 0; i < len; i++) { + if (tab[i] > 0xffff) + return FALSE; + } + return TRUE; +} + +static uint32_t to_lower_simple(uint32_t c) +{ + /* Latin1 and Cyrillic */ + if (c < 0x100 || (c >= 0x410 && c <= 0x42f)) + c += 0x20; + else + c++; + return c; +} + +/* select best encoding with dynamic programming */ +void find_decomp_run(DecompEntry *tab_de, int i) +{ + DecompEntry de_s, *de = &de_s; + CCInfo *ci, *ci1, *ci2; + int l, j, n, len_max; + + ci = &unicode_db[i]; + l = ci->decomp_len; + if (l == 0) { + tab_de[i].cost = tab_de[i + 1].cost; + return; + } + + /* the offset for the compose table has only 6 bits, so we must + limit if it can be used by the compose table */ + if (!ci->is_compat && !ci->is_excluded && l == 2) + len_max = 64; + else + len_max = 127; + + tab_de[i].cost = 0x7fffffff; + + if (!is_16bit(ci->decomp_data, l)) { + assert(l <= 2); + + n = 1; + for(;;) { + de->code = i; + de->len = n; + de->type = DECOMP_TYPE_LL1 + l - 1; + de->c_len = l; + de->cost = get_decomp_run_size(de) + tab_de[i + n].cost; + if (de->cost < tab_de[i].cost) { + tab_de[i] = *de; + } + if (!((i + n) <= CHARCODE_MAX && n < len_max)) + break; + ci1 = &unicode_db[i + n]; + /* Note: we accept a hole */ + if (!(ci1->decomp_len == 0 || + (ci1->decomp_len == l && + ci1->is_compat == ci->is_compat))) + break; + n++; + } + return; + } + + if (l <= 7) { + n = 1; + for(;;) { + de->code = i; + de->len = n; + if (l == 1 && n == 1) { + de->type = DECOMP_TYPE_C1; + } else { + assert(l <= 8); + de->type = DECOMP_TYPE_L1 + l - 1; + } + de->c_len = l; + de->cost = get_decomp_run_size(de) + tab_de[i + n].cost; + if (de->cost < tab_de[i].cost) { + tab_de[i] = *de; + } + + if (!((i + n) <= CHARCODE_MAX && n < len_max)) + break; + ci1 = &unicode_db[i + n]; + /* Note: we accept a hole */ + if (!(ci1->decomp_len == 0 || + (ci1->decomp_len == l && + ci1->is_compat == ci->is_compat && + is_16bit(ci1->decomp_data, l)))) + break; + n++; + } + } + + if (l <= 8 || l == 18) { + int c_min, c_max, c; + c_min = c_max = -1; + n = 1; + for(;;) { + ci1 = &unicode_db[i + n - 1]; + for(j = 0; j < l; j++) { + c = ci1->decomp_data[j]; + if (c == 0x20) { + /* we accept space for Arabic */ + } else if (c_min == -1) { + c_min = c_max = c; + } else { + c_min = min_int(c_min, c); + c_max = max_int(c_max, c); + } + } + if ((c_max - c_min) > 254) + break; + de->code = i; + de->len = n; + if (l == 18) + de->type = DECOMP_TYPE_B18; + else + de->type = DECOMP_TYPE_B1 + l - 1; + de->c_len = l; + de->c_min = c_min; + de->cost = get_decomp_run_size(de) + tab_de[i + n].cost; + if (de->cost < tab_de[i].cost) { + tab_de[i] = *de; + } + if (!((i + n) <= CHARCODE_MAX && n < len_max)) + break; + ci1 = &unicode_db[i + n]; + if (!(ci1->decomp_len == l && + ci1->is_compat == ci->is_compat)) + break; + n++; + } + } + + /* find an ascii run */ + if (l <= 5 && is_short_tab(ci->decomp_data, l)) { + n = 1; + for(;;) { + de->code = i; + de->len = n; + de->type = DECOMP_TYPE_S1 + l - 1; + de->c_len = l; + de->cost = get_decomp_run_size(de) + tab_de[i + n].cost; + if (de->cost < tab_de[i].cost) { + tab_de[i] = *de; + } + + if (!((i + n) <= CHARCODE_MAX && n < len_max)) + break; + ci1 = &unicode_db[i + n]; + /* Note: we accept a hole */ + if (!(ci1->decomp_len == 0 || + (ci1->decomp_len == l && + ci1->is_compat == ci->is_compat && + is_short_tab(ci1->decomp_data, l)))) + break; + n++; + } + } + + /* check if a single char is increasing */ + if (l <= 4) { + int idx1, idx; + + for(idx1 = 1; (idx = decomp_incr_tab[l - 1][idx1]) >= 0; idx1++) { + n = 1; + for(;;) { + de->code = i; + de->len = n; + de->type = decomp_incr_tab[l - 1][0] + idx1 - 1; + de->c_len = l; + de->cost = get_decomp_run_size(de) + tab_de[i + n].cost; + if (de->cost < tab_de[i].cost) { + tab_de[i] = *de; + } + + if (!((i + n) <= CHARCODE_MAX && n < len_max)) + break; + ci1 = &unicode_db[i + n]; + if (!(ci1->decomp_len == l && + ci1->is_compat == ci->is_compat)) + goto next1; + for(j = 0; j < l; j++) { + if (j == idx) { + if (ci1->decomp_data[j] != ci->decomp_data[j] + n) + goto next1; + } else { + if (ci1->decomp_data[j] != ci->decomp_data[j]) + goto next1; + } + } + n++; + } + next1: ; + } + } + + if (l == 3) { + n = 1; + for(;;) { + de->code = i; + de->len = n; + de->type = DECOMP_TYPE_PAT3; + de->c_len = l; + de->cost = get_decomp_run_size(de) + tab_de[i + n].cost; + if (de->cost < tab_de[i].cost) { + tab_de[i] = *de; + } + if (!((i + n) <= CHARCODE_MAX && n < len_max)) + break; + ci1 = &unicode_db[i + n]; + if (!(ci1->decomp_len == l && + ci1->is_compat == ci->is_compat && + ci1->decomp_data[1] <= 0xffff && + ci1->decomp_data[0] == ci->decomp_data[0] && + ci1->decomp_data[l - 1] == ci->decomp_data[l - 1])) + break; + n++; + } + } + + if (l == 2 && is_short(ci->decomp_data[1])) { + n = 1; + for(;;) { + de->code = i; + de->len = n; + de->type = DECOMP_TYPE_LS2; + de->c_len = l; + de->cost = get_decomp_run_size(de) + tab_de[i + n].cost; + if (de->cost < tab_de[i].cost) { + tab_de[i] = *de; + } + if (!((i + n) <= CHARCODE_MAX && n < len_max)) + break; + ci1 = &unicode_db[i + n]; + if (!(ci1->decomp_len == 0 || + (ci1->decomp_len == l && + ci1->is_compat == ci->is_compat && + ci1->decomp_data[0] <= 0xffff && + is_short(ci1->decomp_data[1])))) + break; + n++; + } + } + + if (l == 2) { + BOOL is_16bit; + + n = 0; + is_16bit = FALSE; + for(;;) { + if (!((i + n + 1) <= CHARCODE_MAX && n + 2 <= len_max)) + break; + ci1 = &unicode_db[i + n]; + if (!(ci1->decomp_len == l && + ci1->is_compat == ci->is_compat && + is_short(ci1->decomp_data[1]))) + break; + if (!is_16bit && !is_short(ci1->decomp_data[0])) + is_16bit = TRUE; + ci2 = &unicode_db[i + n + 1]; + if (!(ci2->decomp_len == l && + ci2->is_compat == ci->is_compat && + ci2->decomp_data[0] == to_lower_simple(ci1->decomp_data[0]) && + ci2->decomp_data[1] == ci1->decomp_data[1])) + break; + n += 2; + de->code = i; + de->len = n; + de->type = DECOMP_TYPE_S2_UL + is_16bit; + de->c_len = l; + de->cost = get_decomp_run_size(de) + tab_de[i + n].cost; + if (de->cost < tab_de[i].cost) { + tab_de[i] = *de; + } + } + } +} + +void put16(uint8_t *data_buf, int *pidx, uint16_t c) +{ + int idx; + idx = *pidx; + data_buf[idx++] = c; + data_buf[idx++] = c >> 8; + *pidx = idx; +} + +void add_decomp_data(uint8_t *data_buf, int *pidx, DecompEntry *de) +{ + int i, j, idx, c; + CCInfo *ci; + + idx = *pidx; + de->data_index = idx; + if (de->type <= DECOMP_TYPE_C1) { + ci = &unicode_db[de->code]; + assert(ci->decomp_len == 1); + de->data_index = ci->decomp_data[0]; + } else if (de->type <= DECOMP_TYPE_L7) { + for(i = 0; i < de->len; i++) { + ci = &unicode_db[de->code + i]; + for(j = 0; j < de->c_len; j++) { + if (ci->decomp_len == 0) + c = 0; + else + c = ci->decomp_data[j]; + put16(data_buf, &idx, c); + } + } + } else if (de->type <= DECOMP_TYPE_LL2) { + int n, p, k; + n = (de->len * de->c_len * 18 + 7) / 8; + p = de->len * de->c_len * 2; + memset(data_buf + idx, 0, n); + k = 0; + for(i = 0; i < de->len; i++) { + ci = &unicode_db[de->code + i]; + for(j = 0; j < de->c_len; j++) { + if (ci->decomp_len == 0) + c = 0; + else + c = ci->decomp_data[j]; + data_buf[idx + k * 2] = c; + data_buf[idx + k * 2 + 1] = c >> 8; + data_buf[idx + p + (k / 4)] |= (c >> 16) << ((k % 4) * 2); + k++; + } + } + idx += n; + } else if (de->type <= DECOMP_TYPE_S5) { + for(i = 0; i < de->len; i++) { + ci = &unicode_db[de->code + i]; + for(j = 0; j < de->c_len; j++) { + if (ci->decomp_len == 0) + c = 0; + else + c = ci->decomp_data[j]; + c = get_short_code(c); + assert(c >= 0); + data_buf[idx++] = c; + } + } + } else if (de->type <= DECOMP_TYPE_I4_2) { + ci = &unicode_db[de->code]; + assert(ci->decomp_len == de->c_len); + for(j = 0; j < de->c_len; j++) + put16(data_buf, &idx, ci->decomp_data[j]); + } else if (de->type <= DECOMP_TYPE_B18) { + c = de->c_min; + data_buf[idx++] = c; + data_buf[idx++] = c >> 8; + for(i = 0; i < de->len; i++) { + ci = &unicode_db[de->code + i]; + for(j = 0; j < de->c_len; j++) { + assert(ci->decomp_len == de->c_len); + c = ci->decomp_data[j]; + if (c == 0x20) { + c = 0xff; + } else { + c -= de->c_min; + assert((uint32_t)c <= 254); + } + data_buf[idx++] = c; + } + } + } else if (de->type <= DECOMP_TYPE_LS2) { + assert(de->c_len == 2); + for(i = 0; i < de->len; i++) { + ci = &unicode_db[de->code + i]; + if (ci->decomp_len == 0) + c = 0; + else + c = ci->decomp_data[0]; + put16(data_buf, &idx, c); + + if (ci->decomp_len == 0) + c = 0; + else + c = ci->decomp_data[1]; + c = get_short_code(c); + assert(c >= 0); + data_buf[idx++] = c; + } + } else if (de->type <= DECOMP_TYPE_PAT3) { + ci = &unicode_db[de->code]; + assert(ci->decomp_len == 3); + put16(data_buf, &idx, ci->decomp_data[0]); + put16(data_buf, &idx, ci->decomp_data[2]); + for(i = 0; i < de->len; i++) { + ci = &unicode_db[de->code + i]; + assert(ci->decomp_len == 3); + put16(data_buf, &idx, ci->decomp_data[1]); + } + } else if (de->type <= DECOMP_TYPE_S2_UL) { + for(i = 0; i < de->len; i += 2) { + ci = &unicode_db[de->code + i]; + c = ci->decomp_data[0]; + c = get_short_code(c); + assert(c >= 0); + data_buf[idx++] = c; + c = ci->decomp_data[1]; + c = get_short_code(c); + assert(c >= 0); + data_buf[idx++] = c; + } + } else if (de->type <= DECOMP_TYPE_LS2_UL) { + for(i = 0; i < de->len; i += 2) { + ci = &unicode_db[de->code + i]; + c = ci->decomp_data[0]; + put16(data_buf, &idx, c); + c = ci->decomp_data[1]; + c = get_short_code(c); + assert(c >= 0); + data_buf[idx++] = c; + } + } else { + abort(); + } + *pidx = idx; +} + +void build_compose_table(FILE *f, const DecompEntry *tab_de); + +void build_decompose_table(FILE *f) +{ + int i, array_len, code_max, data_len, count; + DecompEntry *tab_de, de_s, *de = &de_s; + uint8_t *data_buf; + + code_max = CHARCODE_MAX; + + tab_de = mallocz((code_max + 2) * sizeof(*tab_de)); + + for(i = code_max; i >= 0; i--) { + find_decomp_run(tab_de, i); + } + + /* build the data buffer */ + data_buf = malloc(100000); + data_len = 0; + array_len = 0; + for(i = 0; i <= code_max; i++) { + de = &tab_de[i]; + if (de->len != 0) { + add_decomp_data(data_buf, &data_len, de); + i += de->len - 1; + array_len++; + } + } + +#ifdef DUMP_DECOMP_TABLE + /* dump */ + { + int size, size1; + + printf("START LEN TYPE L C SIZE\n"); + size = 0; + for(i = 0; i <= code_max; i++) { + de = &tab_de[i]; + if (de->len != 0) { + size1 = get_decomp_run_size(de); + printf("%05x %3d %6s %2d %1d %4d\n", i, de->len, + decomp_type_str[de->type], de->c_len, + unicode_db[i].is_compat, size1); + i += de->len - 1; + size += size1; + } + } + + printf("array_len=%d estimated size=%d bytes actual=%d bytes\n", + array_len, size, array_len * 6 + data_len); + } +#endif + + fprintf(f, "static const uint32_t unicode_decomp_table1[%u] = {", + array_len); + count = 0; + for(i = 0; i <= code_max; i++) { + de = &tab_de[i]; + if (de->len != 0) { + uint32_t v; + if (count++ % 4 == 0) + fprintf(f, "\n "); + v = (de->code << (32 - 18)) | + (de->len << (32 - 18 - 7)) | + (de->type << (32 - 18 - 7 - 6)) | + unicode_db[de->code].is_compat; + fprintf(f, " 0x%08x,", v); + i += de->len - 1; + } + } + fprintf(f, "\n};\n\n"); + + fprintf(f, "static const uint16_t unicode_decomp_table2[%u] = {", + array_len); + count = 0; + for(i = 0; i <= code_max; i++) { + de = &tab_de[i]; + if (de->len != 0) { + if (count++ % 8 == 0) + fprintf(f, "\n "); + fprintf(f, " 0x%04x,", de->data_index); + i += de->len - 1; + } + } + fprintf(f, "\n};\n\n"); + + fprintf(f, "static const uint8_t unicode_decomp_data[%u] = {", + data_len); + for(i = 0; i < data_len; i++) { + if (i % 8 == 0) + fprintf(f, "\n "); + fprintf(f, " 0x%02x,", data_buf[i]); + } + fprintf(f, "\n};\n\n"); + + build_compose_table(f, tab_de); + + free(data_buf); + + free(tab_de); +} + +typedef struct { + uint32_t c[2]; + uint32_t p; +} ComposeEntry; + +#define COMPOSE_LEN_MAX 10000 + +static int ce_cmp(const void *p1, const void *p2) +{ + const ComposeEntry *ce1 = p1; + const ComposeEntry *ce2 = p2; + int i; + + for(i = 0; i < 2; i++) { + if (ce1->c[i] < ce2->c[i]) + return -1; + else if (ce1->c[i] > ce2->c[i]) + return 1; + } + return 0; +} + + +static int get_decomp_pos(const DecompEntry *tab_de, int c) +{ + int i, v, k; + const DecompEntry *de; + + k = 0; + for(i = 0; i <= CHARCODE_MAX; i++) { + de = &tab_de[i]; + if (de->len != 0) { + if (c >= de->code && c < de->code + de->len) { + v = c - de->code; + assert(v < 64); + v |= k << 6; + assert(v < 65536); + return v; + } + i += de->len - 1; + k++; + } + } + return -1; +} + +void build_compose_table(FILE *f, const DecompEntry *tab_de) +{ + int i, v, tab_ce_len; + ComposeEntry *ce, *tab_ce; + + tab_ce = malloc(sizeof(*tab_ce) * COMPOSE_LEN_MAX); + tab_ce_len = 0; + for(i = 0; i <= CHARCODE_MAX; i++) { + CCInfo *ci = &unicode_db[i]; + if (ci->decomp_len == 2 && !ci->is_compat && + !ci->is_excluded) { + assert(tab_ce_len < COMPOSE_LEN_MAX); + ce = &tab_ce[tab_ce_len++]; + ce->c[0] = ci->decomp_data[0]; + ce->c[1] = ci->decomp_data[1]; + ce->p = i; + } + } + qsort(tab_ce, tab_ce_len, sizeof(*tab_ce), ce_cmp); + + fprintf(f, "static const uint16_t unicode_comp_table[%u] = {", + tab_ce_len); + for(i = 0; i < tab_ce_len; i++) { + if (i % 8 == 0) + fprintf(f, "\n "); + v = get_decomp_pos(tab_de, tab_ce[i].p); + if (v < 0) { + printf("ERROR: entry for c=%04x not found\n", + tab_ce[i].p); + exit(1); + } + fprintf(f, " 0x%04x,", v); + } + fprintf(f, "\n};\n\n"); + + free(tab_ce); +} + +#ifdef USE_TEST +void check_decompose_table(void) +{ + int c; + CCInfo *ci; + int res[UNICODE_DECOMP_LEN_MAX], *ref; + int len, ref_len, is_compat; + + for(is_compat = 0; is_compat <= 1; is_compat++) { + for(c = 0; c < CHARCODE_MAX; c++) { + ci = &unicode_db[c]; + ref_len = ci->decomp_len; + ref = ci->decomp_data; + if (!is_compat && ci->is_compat) { + ref_len = 0; + } + len = unicode_decomp_char((uint32_t *)res, c, is_compat); + if (len != ref_len || + tabcmp(res, ref, ref_len) != 0) { + printf("ERROR c=%05x compat=%d\n", c, is_compat); + dump_str("res", res, len); + dump_str("ref", ref, ref_len); + exit(1); + } + } + } +} + +void check_compose_table(void) +{ + int i, p; + /* XXX: we don't test all the cases */ + + for(i = 0; i <= CHARCODE_MAX; i++) { + CCInfo *ci = &unicode_db[i]; + if (ci->decomp_len == 2 && !ci->is_compat && + !ci->is_excluded) { + p = unicode_compose_pair(ci->decomp_data[0], ci->decomp_data[1]); + if (p != i) { + printf("ERROR compose: c=%05x %05x -> %05x ref=%05x\n", + ci->decomp_data[0], ci->decomp_data[1], p, i); + exit(1); + } + } + } + + + +} + +#endif + + + +#ifdef USE_TEST + +void check_str(const char *msg, int num, const int *in_buf, int in_len, + const int *buf1, int len1, + const int *buf2, int len2) +{ + if (len1 != len2 || tabcmp(buf1, buf2, len1) != 0) { + printf("%d: ERROR %s:\n", num, msg); + dump_str(" in", in_buf, in_len); + dump_str("res", buf1, len1); + dump_str("ref", buf2, len2); + exit(1); + } +} + +void check_cc_table(void) +{ + int cc, cc_ref, c; + + for(c = 0; c <= CHARCODE_MAX; c++) { + cc_ref = unicode_db[c].combining_class; + cc = unicode_get_cc(c); + if (cc != cc_ref) { + printf("ERROR: c=%04x cc=%d cc_ref=%d\n", + c, cc, cc_ref); + exit(1); + } + } +#ifdef PROFILE + { + int64_t ti, count; + + ti = get_time_ns(); + count = 0; + /* only do it on meaningful chars */ + for(c = 0x20; c <= 0xffff; c++) { + cc_ref = unicode_db[c].combining_class; + cc = unicode_get_cc(c); + count++; + } + ti = get_time_ns() - ti; + printf("cc time=%0.1f ns/char\n", + (double)ti / count); + } +#endif +} + +void normalization_test(const char *filename) +{ + FILE *f; + char line[4096], *p; + int *in_str, *nfc_str, *nfd_str, *nfkc_str, *nfkd_str; + int in_len, nfc_len, nfd_len, nfkc_len, nfkd_len; + int *buf, buf_len, pos; + + f = fopen(filename, "rb"); + if (!f) { + perror(filename); + exit(1); + } + pos = 0; + for(;;) { + if (!get_line(line, sizeof(line), f)) + break; + pos++; + p = line; + while (isspace(*p)) + p++; + if (*p == '#' || *p == '@') + continue; + in_str = get_field_str(&in_len, p, 0); + nfc_str = get_field_str(&nfc_len, p, 1); + nfd_str = get_field_str(&nfd_len, p, 2); + nfkc_str = get_field_str(&nfkc_len, p, 3); + nfkd_str = get_field_str(&nfkd_len, p, 4); + + // dump_str("in", in_str, in_len); + + buf_len = unicode_normalize((uint32_t **)&buf, (uint32_t *)in_str, in_len, UNICODE_NFD, NULL, NULL); + check_str("nfd", pos, in_str, in_len, buf, buf_len, nfd_str, nfd_len); + free(buf); + + buf_len = unicode_normalize((uint32_t **)&buf, (uint32_t *)in_str, in_len, UNICODE_NFKD, NULL, NULL); + check_str("nfkd", pos, in_str, in_len, buf, buf_len, nfkd_str, nfkd_len); + free(buf); + + buf_len = unicode_normalize((uint32_t **)&buf, (uint32_t *)in_str, in_len, UNICODE_NFC, NULL, NULL); + check_str("nfc", pos, in_str, in_len, buf, buf_len, nfc_str, nfc_len); + free(buf); + + buf_len = unicode_normalize((uint32_t **)&buf, (uint32_t *)in_str, in_len, UNICODE_NFKC, NULL, NULL); + check_str("nfkc", pos, in_str, in_len, buf, buf_len, nfkc_str, nfkc_len); + free(buf); + + free(in_str); + free(nfc_str); + free(nfd_str); + free(nfkc_str); + free(nfkd_str); + } + fclose(f); +} +#endif + +int main(int argc, char **argv) +{ + const char *unicode_db_path, *outfilename; + char filename[1024]; + + if (argc < 2) { + printf("usage: %s unicode_db_path [output_file]\n" + "\n" + "If no output_file is given, a self test is done using the current unicode library\n", + argv[0]); + exit(1); + } + unicode_db_path = argv[1]; + outfilename = NULL; + if (argc >= 3) + outfilename = argv[2]; + + unicode_db = mallocz(sizeof(unicode_db[0]) * (CHARCODE_MAX + 1)); + + snprintf(filename, sizeof(filename), "%s/UnicodeData.txt", unicode_db_path); + + parse_unicode_data(filename); + + snprintf(filename, sizeof(filename), "%s/SpecialCasing.txt", unicode_db_path); + parse_special_casing(unicode_db, filename); + + snprintf(filename, sizeof(filename), "%s/CaseFolding.txt", unicode_db_path); + parse_case_folding(unicode_db, filename); + + snprintf(filename, sizeof(filename), "%s/CompositionExclusions.txt", unicode_db_path); + parse_composition_exclusions(filename); + + snprintf(filename, sizeof(filename), "%s/DerivedCoreProperties.txt", unicode_db_path); + parse_derived_core_properties(filename); + + snprintf(filename, sizeof(filename), "%s/DerivedNormalizationProps.txt", unicode_db_path); + parse_derived_norm_properties(filename); + + snprintf(filename, sizeof(filename), "%s/PropList.txt", unicode_db_path); + parse_prop_list(filename); + + snprintf(filename, sizeof(filename), "%s/Scripts.txt", unicode_db_path); + parse_scripts(filename); + + snprintf(filename, sizeof(filename), "%s/ScriptExtensions.txt", + unicode_db_path); + parse_script_extensions(filename); + + snprintf(filename, sizeof(filename), "%s/emoji-data.txt", + unicode_db_path); + parse_prop_list(filename); + + // dump_unicode_data(unicode_db); + build_conv_table(unicode_db); + +#ifdef DUMP_CASE_FOLDING_SPECIAL_CASES + dump_case_folding_special_cases(unicode_db); +#endif + + if (!outfilename) { +#ifdef USE_TEST + check_case_conv(); + check_flags(); + check_decompose_table(); + check_compose_table(); + check_cc_table(); + snprintf(filename, sizeof(filename), "%s/NormalizationTest.txt", unicode_db_path); + normalization_test(filename); +#else + fprintf(stderr, "Tests are not compiled\n"); + exit(1); +#endif + } else + { + FILE *fo = fopen(outfilename, "wb"); + + if (!fo) { + perror(outfilename); + exit(1); + } + fprintf(fo, + "/* Compressed unicode tables */\n" + "/* Automatically generated file - do not edit */\n" + "\n" + "#include <stdint.h>\n" + "\n"); + dump_case_conv_table(fo); + compute_internal_props(); + build_flags_tables(fo); + build_cc_table(fo); + build_decompose_table(fo); + build_general_category_table(fo); + build_script_table(fo); + build_script_ext_table(fo); + build_prop_list_table(fo); + fclose(fo); + } + return 0; +} diff --git a/third_party/quickjs/unicode_gen_def.h b/third_party/quickjs/unicode_gen_def.h new file mode 100644 index 0000000000..913d173a09 --- /dev/null +++ b/third_party/quickjs/unicode_gen_def.h @@ -0,0 +1,305 @@ +#ifdef UNICODE_GENERAL_CATEGORY +DEF(Cn, "Unassigned") /* must be zero */ +DEF(Lu, "Uppercase_Letter") +DEF(Ll, "Lowercase_Letter") +DEF(Lt, "Titlecase_Letter") +DEF(Lm, "Modifier_Letter") +DEF(Lo, "Other_Letter") +DEF(Mn, "Nonspacing_Mark") +DEF(Mc, "Spacing_Mark") +DEF(Me, "Enclosing_Mark") +DEF(Nd, "Decimal_Number,digit") +DEF(Nl, "Letter_Number") +DEF(No, "Other_Number") +DEF(Sm, "Math_Symbol") +DEF(Sc, "Currency_Symbol") +DEF(Sk, "Modifier_Symbol") +DEF(So, "Other_Symbol") +DEF(Pc, "Connector_Punctuation") +DEF(Pd, "Dash_Punctuation") +DEF(Ps, "Open_Punctuation") +DEF(Pe, "Close_Punctuation") +DEF(Pi, "Initial_Punctuation") +DEF(Pf, "Final_Punctuation") +DEF(Po, "Other_Punctuation") +DEF(Zs, "Space_Separator") +DEF(Zl, "Line_Separator") +DEF(Zp, "Paragraph_Separator") +DEF(Cc, "Control,cntrl") +DEF(Cf, "Format") +DEF(Cs, "Surrogate") +DEF(Co, "Private_Use") +/* synthetic properties */ +DEF(LC, "Cased_Letter") +DEF(L, "Letter") +DEF(M, "Mark,Combining_Mark") +DEF(N, "Number") +DEF(S, "Symbol") +DEF(P, "Punctuation,punct") +DEF(Z, "Separator") +DEF(C, "Other") +#endif + +#ifdef UNICODE_SCRIPT +/* scripts aliases names in PropertyValueAliases.txt */ +DEF(Unknown, "Zzzz") +DEF(Adlam, "Adlm") +DEF(Ahom, "Ahom") +DEF(Anatolian_Hieroglyphs, "Hluw") +DEF(Arabic, "Arab") +DEF(Armenian, "Armn") +DEF(Avestan, "Avst") +DEF(Balinese, "Bali") +DEF(Bamum, "Bamu") +DEF(Bassa_Vah, "Bass") +DEF(Batak, "Batk") +DEF(Bengali, "Beng") +DEF(Bhaiksuki, "Bhks") +DEF(Bopomofo, "Bopo") +DEF(Brahmi, "Brah") +DEF(Braille, "Brai") +DEF(Buginese, "Bugi") +DEF(Buhid, "Buhd") +DEF(Canadian_Aboriginal, "Cans") +DEF(Carian, "Cari") +DEF(Caucasian_Albanian, "Aghb") +DEF(Chakma, "Cakm") +DEF(Cham, "Cham") +DEF(Cherokee, "Cher") +DEF(Chorasmian, "Chrs") +DEF(Common, "Zyyy") +DEF(Coptic, "Copt,Qaac") +DEF(Cuneiform, "Xsux") +DEF(Cypriot, "Cprt") +DEF(Cyrillic, "Cyrl") +DEF(Cypro_Minoan, "Cpmn") +DEF(Deseret, "Dsrt") +DEF(Devanagari, "Deva") +DEF(Dives_Akuru, "Diak") +DEF(Dogra, "Dogr") +DEF(Duployan, "Dupl") +DEF(Egyptian_Hieroglyphs, "Egyp") +DEF(Elbasan, "Elba") +DEF(Elymaic, "Elym") +DEF(Ethiopic, "Ethi") +DEF(Georgian, "Geor") +DEF(Glagolitic, "Glag") +DEF(Gothic, "Goth") +DEF(Garay, "Gara") +DEF(Grantha, "Gran") +DEF(Greek, "Grek") +DEF(Gujarati, "Gujr") +DEF(Gunjala_Gondi, "Gong") +DEF(Gurmukhi, "Guru") +DEF(Gurung_Khema, "Gukh") +DEF(Han, "Hani") +DEF(Hangul, "Hang") +DEF(Hanifi_Rohingya, "Rohg") +DEF(Hanunoo, "Hano") +DEF(Hatran, "Hatr") +DEF(Hebrew, "Hebr") +DEF(Hiragana, "Hira") +DEF(Imperial_Aramaic, "Armi") +DEF(Inherited, "Zinh,Qaai") +DEF(Inscriptional_Pahlavi, "Phli") +DEF(Inscriptional_Parthian, "Prti") +DEF(Javanese, "Java") +DEF(Kaithi, "Kthi") +DEF(Kannada, "Knda") +DEF(Katakana, "Kana") +DEF(Kawi, "Kawi") +DEF(Kayah_Li, "Kali") +DEF(Kharoshthi, "Khar") +DEF(Khmer, "Khmr") +DEF(Khojki, "Khoj") +DEF(Khitan_Small_Script, "Kits") +DEF(Khudawadi, "Sind") +DEF(Kirat_Rai, "Krai") +DEF(Lao, "Laoo") +DEF(Latin, "Latn") +DEF(Lepcha, "Lepc") +DEF(Limbu, "Limb") +DEF(Linear_A, "Lina") +DEF(Linear_B, "Linb") +DEF(Lisu, "Lisu") +DEF(Lycian, "Lyci") +DEF(Lydian, "Lydi") +DEF(Makasar, "Maka") +DEF(Mahajani, "Mahj") +DEF(Malayalam, "Mlym") +DEF(Mandaic, "Mand") +DEF(Manichaean, "Mani") +DEF(Marchen, "Marc") +DEF(Masaram_Gondi, "Gonm") +DEF(Medefaidrin, "Medf") +DEF(Meetei_Mayek, "Mtei") +DEF(Mende_Kikakui, "Mend") +DEF(Meroitic_Cursive, "Merc") +DEF(Meroitic_Hieroglyphs, "Mero") +DEF(Miao, "Plrd") +DEF(Modi, "Modi") +DEF(Mongolian, "Mong") +DEF(Mro, "Mroo") +DEF(Multani, "Mult") +DEF(Myanmar, "Mymr") +DEF(Nabataean, "Nbat") +DEF(Nag_Mundari, "Nagm") +DEF(Nandinagari, "Nand") +DEF(New_Tai_Lue, "Talu") +DEF(Newa, "Newa") +DEF(Nko, "Nkoo") +DEF(Nushu, "Nshu") +DEF(Nyiakeng_Puachue_Hmong, "Hmnp") +DEF(Ogham, "Ogam") +DEF(Ol_Chiki, "Olck") +DEF(Ol_Onal, "Onao") +DEF(Old_Hungarian, "Hung") +DEF(Old_Italic, "Ital") +DEF(Old_North_Arabian, "Narb") +DEF(Old_Permic, "Perm") +DEF(Old_Persian, "Xpeo") +DEF(Old_Sogdian, "Sogo") +DEF(Old_South_Arabian, "Sarb") +DEF(Old_Turkic, "Orkh") +DEF(Old_Uyghur, "Ougr") +DEF(Oriya, "Orya") +DEF(Osage, "Osge") +DEF(Osmanya, "Osma") +DEF(Pahawh_Hmong, "Hmng") +DEF(Palmyrene, "Palm") +DEF(Pau_Cin_Hau, "Pauc") +DEF(Phags_Pa, "Phag") +DEF(Phoenician, "Phnx") +DEF(Psalter_Pahlavi, "Phlp") +DEF(Rejang, "Rjng") +DEF(Runic, "Runr") +DEF(Samaritan, "Samr") +DEF(Saurashtra, "Saur") +DEF(Sharada, "Shrd") +DEF(Shavian, "Shaw") +DEF(Siddham, "Sidd") +DEF(SignWriting, "Sgnw") +DEF(Sinhala, "Sinh") +DEF(Sogdian, "Sogd") +DEF(Sora_Sompeng, "Sora") +DEF(Soyombo, "Soyo") +DEF(Sundanese, "Sund") +DEF(Sunuwar, "Sunu") +DEF(Syloti_Nagri, "Sylo") +DEF(Syriac, "Syrc") +DEF(Tagalog, "Tglg") +DEF(Tagbanwa, "Tagb") +DEF(Tai_Le, "Tale") +DEF(Tai_Tham, "Lana") +DEF(Tai_Viet, "Tavt") +DEF(Takri, "Takr") +DEF(Tamil, "Taml") +DEF(Tangut, "Tang") +DEF(Telugu, "Telu") +DEF(Thaana, "Thaa") +DEF(Thai, "Thai") +DEF(Tibetan, "Tibt") +DEF(Tifinagh, "Tfng") +DEF(Tirhuta, "Tirh") +DEF(Tangsa, "Tnsa") +DEF(Todhri, "Todr") +DEF(Toto, "Toto") +DEF(Tulu_Tigalari, "Tutg") +DEF(Ugaritic, "Ugar") +DEF(Vai, "Vaii") +DEF(Vithkuqi, "Vith") +DEF(Wancho, "Wcho") +DEF(Warang_Citi, "Wara") +DEF(Yezidi, "Yezi") +DEF(Yi, "Yiii") +DEF(Zanabazar_Square, "Zanb") +#endif + +#ifdef UNICODE_PROP_LIST +/* Prop list not exported to regexp */ +DEF(Hyphen, "") +DEF(Other_Math, "") +DEF(Other_Alphabetic, "") +DEF(Other_Lowercase, "") +DEF(Other_Uppercase, "") +DEF(Other_Grapheme_Extend, "") +DEF(Other_Default_Ignorable_Code_Point, "") +DEF(Other_ID_Start, "") +DEF(Other_ID_Continue, "") +DEF(Prepended_Concatenation_Mark, "") +/* additional computed properties for smaller tables */ +DEF(ID_Continue1, "") +DEF(XID_Start1, "") +DEF(XID_Continue1, "") +DEF(Changes_When_Titlecased1, "") +DEF(Changes_When_Casefolded1, "") +DEF(Changes_When_NFKC_Casefolded1, "") + +/* Prop list exported to JS */ +DEF(ASCII_Hex_Digit, "AHex") +DEF(Bidi_Control, "Bidi_C") +DEF(Dash, "") +DEF(Deprecated, "Dep") +DEF(Diacritic, "Dia") +DEF(Extender, "Ext") +DEF(Hex_Digit, "Hex") +DEF(IDS_Unary_Operator, "IDSU") +DEF(IDS_Binary_Operator, "IDSB") +DEF(IDS_Trinary_Operator, "IDST") +DEF(Ideographic, "Ideo") +DEF(Join_Control, "Join_C") +DEF(Logical_Order_Exception, "LOE") +DEF(Modifier_Combining_Mark, "MCM") +DEF(Noncharacter_Code_Point, "NChar") +DEF(Pattern_Syntax, "Pat_Syn") +DEF(Pattern_White_Space, "Pat_WS") +DEF(Quotation_Mark, "QMark") +DEF(Radical, "") +DEF(Regional_Indicator, "RI") +DEF(Sentence_Terminal, "STerm") +DEF(Soft_Dotted, "SD") +DEF(Terminal_Punctuation, "Term") +DEF(Unified_Ideograph, "UIdeo") +DEF(Variation_Selector, "VS") +DEF(White_Space, "space") +DEF(Bidi_Mirrored, "Bidi_M") +DEF(Emoji, "") +DEF(Emoji_Component, "EComp") +DEF(Emoji_Modifier, "EMod") +DEF(Emoji_Modifier_Base, "EBase") +DEF(Emoji_Presentation, "EPres") +DEF(Extended_Pictographic, "ExtPict") +DEF(Default_Ignorable_Code_Point, "DI") +DEF(ID_Start, "IDS") +DEF(Case_Ignorable, "CI") + +/* other binary properties */ +DEF(ASCII,"") +DEF(Alphabetic, "Alpha") +DEF(Any, "") +DEF(Assigned,"") +DEF(Cased, "") +DEF(Changes_When_Casefolded, "CWCF") +DEF(Changes_When_Casemapped, "CWCM") +DEF(Changes_When_Lowercased, "CWL") +DEF(Changes_When_NFKC_Casefolded, "CWKCF") +DEF(Changes_When_Titlecased, "CWT") +DEF(Changes_When_Uppercased, "CWU") +DEF(Grapheme_Base, "Gr_Base") +DEF(Grapheme_Extend, "Gr_Ext") +DEF(ID_Continue, "IDC") +DEF(ID_Compat_Math_Start, "") +DEF(ID_Compat_Math_Continue, "") +DEF(Lowercase, "Lower") +DEF(Math, "") +DEF(Uppercase, "Upper") +DEF(XID_Continue, "XIDC") +DEF(XID_Start, "XIDS") + +/* internal tables with index */ +DEF(Cased1, "") + +/* unused by us */ +DEF(InCB, "") + +#endif diff --git a/third_party/sqlite/BUILD.mk b/third_party/sqlite/BUILD.mk index ddaa68857c..c89555689e 100644 --- a/third_party/sqlite/BUILD.mk +++ b/third_party/sqlite/BUILD.mk @@ -9,10 +9,21 @@ THIRD_PARTY_SQLITE_SRCS = \ THIRD_PARTY_SQLITE_HDRS = \ third_party/sqlite/sqlite3.h \ + third_party/sqlite/sqlite-vec.h \ + third_party/sqlite/sqlite-csv.h \ o/$(MODE)/third_party/sqlite/sqlite3.a: \ o/$(MODE)/third_party/sqlite/sqlite3.o \ +o/$(MODE)/third_party/sqlite/sqlite-csv.a: \ + o/$(MODE)/third_party/sqlite/sqlite-csv.o \ + +o/$(MODE)/third_party/sqlite/sqlite-vec.o: \ + private CFLAGS += -DSQLITE_CORE + +o/$(MODE)/third_party/sqlite/sqlite-vec.a: \ + o/$(MODE)/third_party/sqlite/sqlite-vec.o \ + o/$(MODE)/third_party/sqlite/shell: \ o/$(MODE)/third_party/sqlite/shell.o \ o/$(MODE)/third_party/sqlite/sqlite3.o \ @@ -61,3 +72,5 @@ o/$(MODE)/third_party/sqlite/sqlite3.o: \ o/$(MODE)/third_party/sqlite: \ o/$(MODE)/third_party/sqlite/shell \ o/$(MODE)/third_party/sqlite/sqlite3.a \ + o/$(MODE)/third_party/sqlite/sqlite-csv.a \ + o/$(MODE)/third_party/sqlite/sqlite-vec.a \ diff --git a/third_party/sqlite/README.llamafile b/third_party/sqlite/README.llamafile index a0e3fe9ce1..45b8aabd5b 100644 --- a/third_party/sqlite/README.llamafile +++ b/third_party/sqlite/README.llamafile @@ -1,11 +1,15 @@ DESCRIPTION - SQLite is the world's most popular database + SQLite is the world's most popular database, along with other community SQLite extensions. ORIGIN https://www.sqlite.org/2024/sqlite-amalgamation-3470100.zip + https://sqlite.org/src/file/ext/misc/csv.c + + https://github.com/asg017/sqlite-vec + LICENSE Public domain or MIT @@ -14,3 +18,4 @@ LOCAL CHANGES - Renamed <zlib.h> to <third_party/zlib/zlib.h> - Mangled some quoted includes to not confuse mkdeps + - For sqlite-csv: Eemoved dynamic linking code in favor of static linking \ No newline at end of file diff --git a/third_party/sqlite/sqlite-csv.c b/third_party/sqlite/sqlite-csv.c new file mode 100644 index 0000000000..45ad399f35 --- /dev/null +++ b/third_party/sqlite/sqlite-csv.c @@ -0,0 +1,971 @@ +/* +** 2016-05-28 +** +** The author disclaims copyright to this source code. In place of +** a legal notice, here is a blessing: +** +** May you do good and not evil. +** May you find forgiveness for yourself and forgive others. +** May you share freely, never taking more than you give. +** +****************************************************************************** +** +** This file contains the implementation of an SQLite virtual table for +** reading CSV files. +** +** Usage: +** +** .load ./csv +** CREATE VIRTUAL TABLE temp.csv USING csv(filename=FILENAME); +** SELECT * FROM csv; +** +** The columns are named "c1", "c2", "c3", ... by default. Or the +** application can define its own CREATE TABLE statement using the +** schema= parameter, like this: +** +** CREATE VIRTUAL TABLE temp.csv2 USING csv( +** filename = "../http.log", +** schema = "CREATE TABLE x(date,ipaddr,url,referrer,userAgent)" +** ); +** +** Instead of specifying a file, the text of the CSV can be loaded using +** the data= parameter. +** +** If the columns=N parameter is supplied, then the CSV file is assumed to have +** N columns. If both the columns= and schema= parameters are omitted, then +** the number and names of the columns is determined by the first line of +** the CSV input. +** +** Some extra debugging features (used for testing virtual tables) are available +** if this module is compiled with -DSQLITE_TEST. +*/ +#include <string.h> +#include <stdlib.h> +#include <assert.h> +#include <stdarg.h> +#include <ctype.h> +#include <stdio.h> +#include "third_party/sqlite/sqlite3.h" +#ifndef SQLITE_OMIT_VIRTUALTABLE + +/* +** A macro to hint to the compiler that a function should not be +** inlined. +*/ +#if defined(__GNUC__) +# define CSV_NOINLINE __attribute__((noinline)) +#elif defined(_MSC_VER) && _MSC_VER>=1310 +# define CSV_NOINLINE __declspec(noinline) +#else +# define CSV_NOINLINE +#endif + + +/* Max size of the error message in a CsvReader */ +#define CSV_MXERR 200 + +/* Size of the CsvReader input buffer */ +#define CSV_INBUFSZ 1024 + +/* A context object used when read a CSV file. */ +typedef struct CsvReader CsvReader; +struct CsvReader { + FILE *in; /* Read the CSV text from this input stream */ + char *z; /* Accumulated text for a field */ + int n; /* Number of bytes in z */ + int nAlloc; /* Space allocated for z[] */ + int nLine; /* Current line number */ + int bNotFirst; /* True if prior text has been seen */ + int cTerm; /* Character that terminated the most recent field */ + size_t iIn; /* Next unread character in the input buffer */ + size_t nIn; /* Number of characters in the input buffer */ + char *zIn; /* The input buffer */ + char zErr[CSV_MXERR]; /* Error message */ +}; + +/* Initialize a CsvReader object */ +static void csv_reader_init(CsvReader *p){ + p->in = 0; + p->z = 0; + p->n = 0; + p->nAlloc = 0; + p->nLine = 0; + p->bNotFirst = 0; + p->nIn = 0; + p->zIn = 0; + p->zErr[0] = 0; +} + +/* Close and reset a CsvReader object */ +static void csv_reader_reset(CsvReader *p){ + if( p->in ){ + fclose(p->in); + sqlite3_free(p->zIn); + } + sqlite3_free(p->z); + csv_reader_init(p); +} + +/* Report an error on a CsvReader */ +static void csv_errmsg(CsvReader *p, const char *zFormat, ...){ + va_list ap; + va_start(ap, zFormat); + sqlite3_vsnprintf(CSV_MXERR, p->zErr, zFormat, ap); + va_end(ap); +} + +/* Open the file associated with a CsvReader +** Return the number of errors. +*/ +static int csv_reader_open( + CsvReader *p, /* The reader to open */ + const char *zFilename, /* Read from this filename */ + const char *zData /* ... or use this data */ +){ + if( zFilename ){ + p->zIn = sqlite3_malloc( CSV_INBUFSZ ); + if( p->zIn==0 ){ + csv_errmsg(p, "out of memory"); + return 1; + } + p->in = fopen(zFilename, "rb"); + if( p->in==0 ){ + sqlite3_free(p->zIn); + csv_reader_reset(p); + csv_errmsg(p, "cannot open '%s' for reading", zFilename); + return 1; + } + }else{ + assert( p->in==0 ); + p->zIn = (char*)zData; + p->nIn = strlen(zData); + } + return 0; +} + +/* The input buffer has overflowed. Refill the input buffer, then +** return the next character +*/ +static CSV_NOINLINE int csv_getc_refill(CsvReader *p){ + size_t got; + + assert( p->iIn>=p->nIn ); /* Only called on an empty input buffer */ + assert( p->in!=0 ); /* Only called if reading froma file */ + + got = fread(p->zIn, 1, CSV_INBUFSZ, p->in); + if( got==0 ) return EOF; + p->nIn = got; + p->iIn = 1; + return p->zIn[0]; +} + +/* Return the next character of input. Return EOF at end of input. */ +static int csv_getc(CsvReader *p){ + if( p->iIn >= p->nIn ){ + if( p->in!=0 ) return csv_getc_refill(p); + return EOF; + } + return ((unsigned char*)p->zIn)[p->iIn++]; +} + +/* Increase the size of p->z and append character c to the end. +** Return 0 on success and non-zero if there is an OOM error */ +static CSV_NOINLINE int csv_resize_and_append(CsvReader *p, char c){ + char *zNew; + int nNew = p->nAlloc*2 + 100; + zNew = sqlite3_realloc64(p->z, nNew); + if( zNew ){ + p->z = zNew; + p->nAlloc = nNew; + p->z[p->n++] = c; + return 0; + }else{ + csv_errmsg(p, "out of memory"); + return 1; + } +} + +/* Append a single character to the CsvReader.z[] array. +** Return 0 on success and non-zero if there is an OOM error */ +static int csv_append(CsvReader *p, char c){ + if( p->n>=p->nAlloc-1 ) return csv_resize_and_append(p, c); + p->z[p->n++] = c; + return 0; +} + +/* Read a single field of CSV text. Compatible with rfc4180 and extended +** with the option of having a separator other than ",". +** +** + Input comes from p->in. +** + Store results in p->z of length p->n. Space to hold p->z comes +** from sqlite3_malloc64(). +** + Keep track of the line number in p->nLine. +** + Store the character that terminates the field in p->cTerm. Store +** EOF on end-of-file. +** +** Return 0 at EOF or on OOM. On EOF, the p->cTerm character will have +** been set to EOF. +*/ +static char *csv_read_one_field(CsvReader *p){ + int c; + p->n = 0; + c = csv_getc(p); + if( c==EOF ){ + p->cTerm = EOF; + return 0; + } + if( c=='"' ){ + int pc, ppc; + int startLine = p->nLine; + pc = ppc = 0; + while( 1 ){ + c = csv_getc(p); + if( c<='"' || pc=='"' ){ + if( c=='\n' ) p->nLine++; + if( c=='"' ){ + if( pc=='"' ){ + pc = 0; + continue; + } + } + if( (c==',' && pc=='"') + || (c=='\n' && pc=='"') + || (c=='\n' && pc=='\r' && ppc=='"') + || (c==EOF && pc=='"') + ){ + do{ p->n--; }while( p->z[p->n]!='"' ); + p->cTerm = (char)c; + break; + } + if( pc=='"' && c!='\r' ){ + csv_errmsg(p, "line %d: unescaped %c character", p->nLine, '"'); + break; + } + if( c==EOF ){ + csv_errmsg(p, "line %d: unterminated %c-quoted field\n", + startLine, '"'); + p->cTerm = (char)c; + break; + } + } + if( csv_append(p, (char)c) ) return 0; + ppc = pc; + pc = c; + } + }else{ + /* If this is the first field being parsed and it begins with the + ** UTF-8 BOM (0xEF BB BF) then skip the BOM */ + if( (c&0xff)==0xef && p->bNotFirst==0 ){ + csv_append(p, (char)c); + c = csv_getc(p); + if( (c&0xff)==0xbb ){ + csv_append(p, (char)c); + c = csv_getc(p); + if( (c&0xff)==0xbf ){ + p->bNotFirst = 1; + p->n = 0; + return csv_read_one_field(p); + } + } + } + while( c>',' || (c!=EOF && c!=',' && c!='\n') ){ + if( csv_append(p, (char)c) ) return 0; + c = csv_getc(p); + } + if( c=='\n' ){ + p->nLine++; + if( p->n>0 && p->z[p->n-1]=='\r' ) p->n--; + } + p->cTerm = (char)c; + } + assert( p->z==0 || p->n<p->nAlloc ); + if( p->z ) p->z[p->n] = 0; + p->bNotFirst = 1; + return p->z; +} + + +/* Forward references to the various virtual table methods implemented +** in this file. */ +static int csvtabCreate(sqlite3*, void*, int, const char*const*, + sqlite3_vtab**,char**); +static int csvtabConnect(sqlite3*, void*, int, const char*const*, + sqlite3_vtab**,char**); +static int csvtabBestIndex(sqlite3_vtab*,sqlite3_index_info*); +static int csvtabDisconnect(sqlite3_vtab*); +static int csvtabOpen(sqlite3_vtab*, sqlite3_vtab_cursor**); +static int csvtabClose(sqlite3_vtab_cursor*); +static int csvtabFilter(sqlite3_vtab_cursor*, int idxNum, const char *idxStr, + int argc, sqlite3_value **argv); +static int csvtabNext(sqlite3_vtab_cursor*); +static int csvtabEof(sqlite3_vtab_cursor*); +static int csvtabColumn(sqlite3_vtab_cursor*,sqlite3_context*,int); +static int csvtabRowid(sqlite3_vtab_cursor*,sqlite3_int64*); + +/* An instance of the CSV virtual table */ +typedef struct CsvTable { + sqlite3_vtab base; /* Base class. Must be first */ + char *zFilename; /* Name of the CSV file */ + char *zData; /* Raw CSV data in lieu of zFilename */ + long iStart; /* Offset to start of data in zFilename */ + int nCol; /* Number of columns in the CSV file */ + unsigned int tstFlags; /* Bit values used for testing */ +} CsvTable; + +/* Allowed values for tstFlags */ +#define CSVTEST_FIDX 0x0001 /* Pretend that constrained searchs cost less*/ + +/* A cursor for the CSV virtual table */ +typedef struct CsvCursor { + sqlite3_vtab_cursor base; /* Base class. Must be first */ + CsvReader rdr; /* The CsvReader object */ + char **azVal; /* Value of the current row */ + int *aLen; /* Length of each entry */ + sqlite3_int64 iRowid; /* The current rowid. Negative for EOF */ +} CsvCursor; + +/* Transfer error message text from a reader into a CsvTable */ +static void csv_xfer_error(CsvTable *pTab, CsvReader *pRdr){ + sqlite3_free(pTab->base.zErrMsg); + pTab->base.zErrMsg = sqlite3_mprintf("%s", pRdr->zErr); +} + +/* +** This method is the destructor fo a CsvTable object. +*/ +static int csvtabDisconnect(sqlite3_vtab *pVtab){ + CsvTable *p = (CsvTable*)pVtab; + sqlite3_free(p->zFilename); + sqlite3_free(p->zData); + sqlite3_free(p); + return SQLITE_OK; +} + +/* Skip leading whitespace. Return a pointer to the first non-whitespace +** character, or to the zero terminator if the string has only whitespace */ +static const char *csv_skip_whitespace(const char *z){ + while( isspace((unsigned char)z[0]) ) z++; + return z; +} + +/* Remove trailing whitespace from the end of string z[] */ +static void csv_trim_whitespace(char *z){ + size_t n = strlen(z); + while( n>0 && isspace((unsigned char)z[n]) ) n--; + z[n] = 0; +} + +/* Dequote the string */ +static void csv_dequote(char *z){ + int j; + char cQuote = z[0]; + size_t i, n; + + if( cQuote!='\'' && cQuote!='"' ) return; + n = strlen(z); + if( n<2 || z[n-1]!=z[0] ) return; + for(i=1, j=0; i<n-1; i++){ + if( z[i]==cQuote && z[i+1]==cQuote ) i++; + z[j++] = z[i]; + } + z[j] = 0; +} + +/* Check to see if the string is of the form: "TAG = VALUE" with optional +** whitespace before and around tokens. If it is, return a pointer to the +** first character of VALUE. If it is not, return NULL. +*/ +static const char *csv_parameter(const char *zTag, int nTag, const char *z){ + z = csv_skip_whitespace(z); + if( strncmp(zTag, z, nTag)!=0 ) return 0; + z = csv_skip_whitespace(z+nTag); + if( z[0]!='=' ) return 0; + return csv_skip_whitespace(z+1); +} + +/* Decode a parameter that requires a dequoted string. +** +** Return 1 if the parameter is seen, or 0 if not. 1 is returned +** even if there is an error. If an error occurs, then an error message +** is left in p->zErr. If there are no errors, p->zErr[0]==0. +*/ +static int csv_string_parameter( + CsvReader *p, /* Leave the error message here, if there is one */ + const char *zParam, /* Parameter we are checking for */ + const char *zArg, /* Raw text of the virtual table argment */ + char **pzVal /* Write the dequoted string value here */ +){ + const char *zValue; + zValue = csv_parameter(zParam,(int)strlen(zParam),zArg); + if( zValue==0 ) return 0; + p->zErr[0] = 0; + if( *pzVal ){ + csv_errmsg(p, "more than one '%s' parameter", zParam); + return 1; + } + *pzVal = sqlite3_mprintf("%s", zValue); + if( *pzVal==0 ){ + csv_errmsg(p, "out of memory"); + return 1; + } + csv_trim_whitespace(*pzVal); + csv_dequote(*pzVal); + return 1; +} + + +/* Return 0 if the argument is false and 1 if it is true. Return -1 if +** we cannot really tell. +*/ +static int csv_boolean(const char *z){ + if( sqlite3_stricmp("yes",z)==0 + || sqlite3_stricmp("on",z)==0 + || sqlite3_stricmp("true",z)==0 + || (z[0]=='1' && z[1]==0) + ){ + return 1; + } + if( sqlite3_stricmp("no",z)==0 + || sqlite3_stricmp("off",z)==0 + || sqlite3_stricmp("false",z)==0 + || (z[0]=='0' && z[1]==0) + ){ + return 0; + } + return -1; +} + +/* Check to see if the string is of the form: "TAG = BOOLEAN" or just "TAG". +** If it is, set *pValue to be the value of the boolean ("true" if there is +** not "= BOOLEAN" component) and return non-zero. If the input string +** does not begin with TAG, return zero. +*/ +static int csv_boolean_parameter( + const char *zTag, /* Tag we are looking for */ + int nTag, /* Size of the tag in bytes */ + const char *z, /* Input parameter */ + int *pValue /* Write boolean value here */ +){ + int b; + z = csv_skip_whitespace(z); + if( strncmp(zTag, z, nTag)!=0 ) return 0; + z = csv_skip_whitespace(z + nTag); + if( z[0]==0 ){ + *pValue = 1; + return 1; + } + if( z[0]!='=' ) return 0; + z = csv_skip_whitespace(z+1); + b = csv_boolean(z); + if( b>=0 ){ + *pValue = b; + return 1; + } + return 0; +} + +/* +** Parameters: +** filename=FILENAME Name of file containing CSV content +** data=TEXT Direct CSV content. +** schema=SCHEMA Alternative CSV schema. +** header=YES|NO First row of CSV defines the names of +** columns if "yes". Default "no". +** columns=N Assume the CSV file contains N columns. +** +** Only available if compiled with SQLITE_TEST: +** +** testflags=N Bitmask of test flags. Optional +** +** If schema= is omitted, then the columns are named "c0", "c1", "c2", +** and so forth. If columns=N is omitted, then the file is opened and +** the number of columns in the first row is counted to determine the +** column count. If header=YES, then the first row is skipped. +*/ +static int csvtabConnect( + sqlite3 *db, + void *pAux, + int argc, const char *const*argv, + sqlite3_vtab **ppVtab, + char **pzErr +){ + CsvTable *pNew = 0; /* The CsvTable object to construct */ + int bHeader = -1; /* header= flags. -1 means not seen yet */ + int rc = SQLITE_OK; /* Result code from this routine */ + int i, j; /* Loop counters */ +#ifdef SQLITE_TEST + int tstFlags = 0; /* Value for testflags=N parameter */ +#endif + int b; /* Value of a boolean parameter */ + int nCol = -99; /* Value of the columns= parameter */ + CsvReader sRdr; /* A CSV file reader used to store an error + ** message and/or to count the number of columns */ + static const char *azParam[] = { + "filename", "data", "schema", + }; + char *azPValue[3]; /* Parameter values */ +# define CSV_FILENAME (azPValue[0]) +# define CSV_DATA (azPValue[1]) +# define CSV_SCHEMA (azPValue[2]) + + + assert( sizeof(azPValue)==sizeof(azParam) ); + memset(&sRdr, 0, sizeof(sRdr)); + memset(azPValue, 0, sizeof(azPValue)); + for(i=3; i<argc; i++){ + const char *z = argv[i]; + const char *zValue; + for(j=0; j<sizeof(azParam)/sizeof(azParam[0]); j++){ + if( csv_string_parameter(&sRdr, azParam[j], z, &azPValue[j]) ) break; + } + if( j<sizeof(azParam)/sizeof(azParam[0]) ){ + if( sRdr.zErr[0] ) goto csvtab_connect_error; + }else + if( csv_boolean_parameter("header",6,z,&b) ){ + if( bHeader>=0 ){ + csv_errmsg(&sRdr, "more than one 'header' parameter"); + goto csvtab_connect_error; + } + bHeader = b; + }else +#ifdef SQLITE_TEST + if( (zValue = csv_parameter("testflags",9,z))!=0 ){ + tstFlags = (unsigned int)atoi(zValue); + }else +#endif + if( (zValue = csv_parameter("columns",7,z))!=0 ){ + if( nCol>0 ){ + csv_errmsg(&sRdr, "more than one 'columns' parameter"); + goto csvtab_connect_error; + } + nCol = atoi(zValue); + if( nCol<=0 ){ + csv_errmsg(&sRdr, "column= value must be positive"); + goto csvtab_connect_error; + } + }else + { + csv_errmsg(&sRdr, "bad parameter: '%s'", z); + goto csvtab_connect_error; + } + } + if( (CSV_FILENAME==0)==(CSV_DATA==0) ){ + csv_errmsg(&sRdr, "must specify either filename= or data= but not both"); + goto csvtab_connect_error; + } + + if( (nCol<=0 || bHeader==1) + && csv_reader_open(&sRdr, CSV_FILENAME, CSV_DATA) + ){ + goto csvtab_connect_error; + } + pNew = sqlite3_malloc( sizeof(*pNew) ); + *ppVtab = (sqlite3_vtab*)pNew; + if( pNew==0 ) goto csvtab_connect_oom; + memset(pNew, 0, sizeof(*pNew)); + if( CSV_SCHEMA==0 ){ + sqlite3_str *pStr = sqlite3_str_new(0); + char *zSep = ""; + int iCol = 0; + sqlite3_str_appendf(pStr, "CREATE TABLE x("); + if( nCol<0 && bHeader<1 ){ + nCol = 0; + do{ + csv_read_one_field(&sRdr); + nCol++; + }while( sRdr.cTerm==',' ); + } + if( nCol>0 && bHeader<1 ){ + for(iCol=0; iCol<nCol; iCol++){ + sqlite3_str_appendf(pStr, "%sc%d TEXT", zSep, iCol); + zSep = ","; + } + }else{ + do{ + char *z = csv_read_one_field(&sRdr); + if( (nCol>0 && iCol<nCol) || (nCol<0 && bHeader) ){ + sqlite3_str_appendf(pStr,"%s\"%w\" TEXT", zSep, z); + zSep = ","; + iCol++; + } + }while( sRdr.cTerm==',' ); + if( nCol<0 ){ + nCol = iCol; + }else{ + while( iCol<nCol ){ + sqlite3_str_appendf(pStr,"%sc%d TEXT", zSep, ++iCol); + zSep = ","; + } + } + } + pNew->nCol = nCol; + sqlite3_str_appendf(pStr, ")"); + CSV_SCHEMA = sqlite3_str_finish(pStr); + if( CSV_SCHEMA==0 ) goto csvtab_connect_oom; + }else if( nCol<0 ){ + do{ + csv_read_one_field(&sRdr); + pNew->nCol++; + }while( sRdr.cTerm==',' ); + }else{ + pNew->nCol = nCol; + } + pNew->zFilename = CSV_FILENAME; CSV_FILENAME = 0; + pNew->zData = CSV_DATA; CSV_DATA = 0; +#ifdef SQLITE_TEST + pNew->tstFlags = tstFlags; +#endif + if( bHeader!=1 ){ + pNew->iStart = 0; + }else if( pNew->zData ){ + pNew->iStart = (int)sRdr.iIn; + }else{ + pNew->iStart = (int)(ftell(sRdr.in) - sRdr.nIn + sRdr.iIn); + } + csv_reader_reset(&sRdr); + rc = sqlite3_declare_vtab(db, CSV_SCHEMA); + if( rc ){ + csv_errmsg(&sRdr, "bad schema: '%s' - %s", CSV_SCHEMA, sqlite3_errmsg(db)); + goto csvtab_connect_error; + } + for(i=0; i<sizeof(azPValue)/sizeof(azPValue[0]); i++){ + sqlite3_free(azPValue[i]); + } + /* Rationale for DIRECTONLY: + ** An attacker who controls a database schema could use this vtab + ** to exfiltrate sensitive data from other files in the filesystem. + ** And, recommended practice is to put all CSV virtual tables in the + ** TEMP namespace, so they should still be usable from within TEMP + ** views, so there shouldn't be a serious loss of functionality by + ** prohibiting the use of this vtab from persistent triggers and views. + */ + sqlite3_vtab_config(db, SQLITE_VTAB_DIRECTONLY); + return SQLITE_OK; + +csvtab_connect_oom: + rc = SQLITE_NOMEM; + csv_errmsg(&sRdr, "out of memory"); + +csvtab_connect_error: + if( pNew ) csvtabDisconnect(&pNew->base); + for(i=0; i<sizeof(azPValue)/sizeof(azPValue[0]); i++){ + sqlite3_free(azPValue[i]); + } + if( sRdr.zErr[0] ){ + sqlite3_free(*pzErr); + *pzErr = sqlite3_mprintf("%s", sRdr.zErr); + } + csv_reader_reset(&sRdr); + if( rc==SQLITE_OK ) rc = SQLITE_ERROR; + return rc; +} + +/* +** Reset the current row content held by a CsvCursor. +*/ +static void csvtabCursorRowReset(CsvCursor *pCur){ + CsvTable *pTab = (CsvTable*)pCur->base.pVtab; + int i; + for(i=0; i<pTab->nCol; i++){ + sqlite3_free(pCur->azVal[i]); + pCur->azVal[i] = 0; + pCur->aLen[i] = 0; + } +} + +/* +** The xConnect and xCreate methods do the same thing, but they must be +** different so that the virtual table is not an eponymous virtual table. +*/ +static int csvtabCreate( + sqlite3 *db, + void *pAux, + int argc, const char *const*argv, + sqlite3_vtab **ppVtab, + char **pzErr +){ + return csvtabConnect(db, pAux, argc, argv, ppVtab, pzErr); +} + +/* +** Destructor for a CsvCursor. +*/ +static int csvtabClose(sqlite3_vtab_cursor *cur){ + CsvCursor *pCur = (CsvCursor*)cur; + csvtabCursorRowReset(pCur); + csv_reader_reset(&pCur->rdr); + sqlite3_free(cur); + return SQLITE_OK; +} + +/* +** Constructor for a new CsvTable cursor object. +*/ +static int csvtabOpen(sqlite3_vtab *p, sqlite3_vtab_cursor **ppCursor){ + CsvTable *pTab = (CsvTable*)p; + CsvCursor *pCur; + size_t nByte; + nByte = sizeof(*pCur) + (sizeof(char*)+sizeof(int))*pTab->nCol; + pCur = sqlite3_malloc64( nByte ); + if( pCur==0 ) return SQLITE_NOMEM; + memset(pCur, 0, nByte); + pCur->azVal = (char**)&pCur[1]; + pCur->aLen = (int*)&pCur->azVal[pTab->nCol]; + *ppCursor = &pCur->base; + if( csv_reader_open(&pCur->rdr, pTab->zFilename, pTab->zData) ){ + csv_xfer_error(pTab, &pCur->rdr); + return SQLITE_ERROR; + } + return SQLITE_OK; +} + + +/* +** Advance a CsvCursor to its next row of input. +** Set the EOF marker if we reach the end of input. +*/ +static int csvtabNext(sqlite3_vtab_cursor *cur){ + CsvCursor *pCur = (CsvCursor*)cur; + CsvTable *pTab = (CsvTable*)cur->pVtab; + int i = 0; + char *z; + do{ + z = csv_read_one_field(&pCur->rdr); + if( z==0 ){ + break; + } + if( i<pTab->nCol ){ + if( pCur->aLen[i] < pCur->rdr.n+1 ){ + char *zNew = sqlite3_realloc64(pCur->azVal[i], pCur->rdr.n+1); + if( zNew==0 ){ + csv_errmsg(&pCur->rdr, "out of memory"); + csv_xfer_error(pTab, &pCur->rdr); + break; + } + pCur->azVal[i] = zNew; + pCur->aLen[i] = pCur->rdr.n+1; + } + memcpy(pCur->azVal[i], z, pCur->rdr.n+1); + i++; + } + }while( pCur->rdr.cTerm==',' ); + if( z==0 && i==0 ){ + pCur->iRowid = -1; + }else{ + pCur->iRowid++; + while( i<pTab->nCol ){ + sqlite3_free(pCur->azVal[i]); + pCur->azVal[i] = 0; + pCur->aLen[i] = 0; + i++; + } + } + return SQLITE_OK; +} + +/* +** Return values of columns for the row at which the CsvCursor +** is currently pointing. +*/ +static int csvtabColumn( + sqlite3_vtab_cursor *cur, /* The cursor */ + sqlite3_context *ctx, /* First argument to sqlite3_result_...() */ + int i /* Which column to return */ +){ + CsvCursor *pCur = (CsvCursor*)cur; + CsvTable *pTab = (CsvTable*)cur->pVtab; + if( i>=0 && i<pTab->nCol && pCur->azVal[i]!=0 ){ + sqlite3_result_text(ctx, pCur->azVal[i], -1, SQLITE_TRANSIENT); + } + return SQLITE_OK; +} + +/* +** Return the rowid for the current row. +*/ +static int csvtabRowid(sqlite3_vtab_cursor *cur, sqlite_int64 *pRowid){ + CsvCursor *pCur = (CsvCursor*)cur; + *pRowid = pCur->iRowid; + return SQLITE_OK; +} + +/* +** Return TRUE if the cursor has been moved off of the last +** row of output. +*/ +static int csvtabEof(sqlite3_vtab_cursor *cur){ + CsvCursor *pCur = (CsvCursor*)cur; + return pCur->iRowid<0; +} + +/* +** Only a full table scan is supported. So xFilter simply rewinds to +** the beginning. +*/ +static int csvtabFilter( + sqlite3_vtab_cursor *pVtabCursor, + int idxNum, const char *idxStr, + int argc, sqlite3_value **argv +){ + CsvCursor *pCur = (CsvCursor*)pVtabCursor; + CsvTable *pTab = (CsvTable*)pVtabCursor->pVtab; + pCur->iRowid = 0; + + /* Ensure the field buffer is always allocated. Otherwise, if the + ** first field is zero bytes in size, this may be mistaken for an OOM + ** error in csvtabNext(). */ + if( csv_append(&pCur->rdr, 0) ) return SQLITE_NOMEM; + + if( pCur->rdr.in==0 ){ + assert( pCur->rdr.zIn==pTab->zData ); + assert( pTab->iStart>=0 ); + assert( (size_t)pTab->iStart<=pCur->rdr.nIn ); + pCur->rdr.iIn = pTab->iStart; + }else{ + fseek(pCur->rdr.in, pTab->iStart, SEEK_SET); + pCur->rdr.iIn = 0; + pCur->rdr.nIn = 0; + } + return csvtabNext(pVtabCursor); +} + +/* +** Only a forward full table scan is supported. xBestIndex is mostly +** a no-op. If CSVTEST_FIDX is set, then the presence of equality +** constraints lowers the estimated cost, which is fiction, but is useful +** for testing certain kinds of virtual table behavior. +*/ +static int csvtabBestIndex( + sqlite3_vtab *tab, + sqlite3_index_info *pIdxInfo +){ + pIdxInfo->estimatedCost = 1000000; +#ifdef SQLITE_TEST + if( (((CsvTable*)tab)->tstFlags & CSVTEST_FIDX)!=0 ){ + /* The usual (and sensible) case is to always do a full table scan. + ** The code in this branch only runs when testflags=1. This code + ** generates an artifical and unrealistic plan which is useful + ** for testing virtual table logic but is not helpful to real applications. + ** + ** Any ==, LIKE, or GLOB constraint is marked as usable by the virtual + ** table (even though it is not) and the cost of running the virtual table + ** is reduced from 1 million to just 10. The constraints are *not* marked + ** as omittable, however, so the query planner should still generate a + ** plan that gives a correct answer, even if they plan is not optimal. + */ + int i; + int nConst = 0; + for(i=0; i<pIdxInfo->nConstraint; i++){ + unsigned char op; + if( pIdxInfo->aConstraint[i].usable==0 ) continue; + op = pIdxInfo->aConstraint[i].op; + if( op==SQLITE_INDEX_CONSTRAINT_EQ + || op==SQLITE_INDEX_CONSTRAINT_LIKE + || op==SQLITE_INDEX_CONSTRAINT_GLOB + ){ + pIdxInfo->estimatedCost = 10; + pIdxInfo->aConstraintUsage[nConst].argvIndex = nConst+1; + nConst++; + } + } + } +#endif + return SQLITE_OK; +} + + +static sqlite3_module CsvModule = { + 0, /* iVersion */ + csvtabCreate, /* xCreate */ + csvtabConnect, /* xConnect */ + csvtabBestIndex, /* xBestIndex */ + csvtabDisconnect, /* xDisconnect */ + csvtabDisconnect, /* xDestroy */ + csvtabOpen, /* xOpen - open a cursor */ + csvtabClose, /* xClose - close a cursor */ + csvtabFilter, /* xFilter - configure scan constraints */ + csvtabNext, /* xNext - advance a cursor */ + csvtabEof, /* xEof - check for end of scan */ + csvtabColumn, /* xColumn - read data */ + csvtabRowid, /* xRowid - read data */ + 0, /* xUpdate */ + 0, /* xBegin */ + 0, /* xSync */ + 0, /* xCommit */ + 0, /* xRollback */ + 0, /* xFindMethod */ + 0, /* xRename */ + 0, /* xSavepoint */ + 0, /* xRelease */ + 0, /* xRollbackTo */ + 0, /* xShadowName */ + 0 /* xIntegrity */ +}; + +#ifdef SQLITE_TEST +/* +** For virtual table testing, make a version of the CSV virtual table +** available that has an xUpdate function. But the xUpdate always returns +** SQLITE_READONLY since the CSV file is not really writable. +*/ +static int csvtabUpdate(sqlite3_vtab *p,int n,sqlite3_value**v,sqlite3_int64*x){ + return SQLITE_READONLY; +} +static sqlite3_module CsvModuleFauxWrite = { + 0, /* iVersion */ + csvtabCreate, /* xCreate */ + csvtabConnect, /* xConnect */ + csvtabBestIndex, /* xBestIndex */ + csvtabDisconnect, /* xDisconnect */ + csvtabDisconnect, /* xDestroy */ + csvtabOpen, /* xOpen - open a cursor */ + csvtabClose, /* xClose - close a cursor */ + csvtabFilter, /* xFilter - configure scan constraints */ + csvtabNext, /* xNext - advance a cursor */ + csvtabEof, /* xEof - check for end of scan */ + csvtabColumn, /* xColumn - read data */ + csvtabRowid, /* xRowid - read data */ + csvtabUpdate, /* xUpdate */ + 0, /* xBegin */ + 0, /* xSync */ + 0, /* xCommit */ + 0, /* xRollback */ + 0, /* xFindMethod */ + 0, /* xRename */ + 0, /* xSavepoint */ + 0, /* xRelease */ + 0, /* xRollbackTo */ + 0, /* xShadowName */ + 0 /* xIntegrity */ +}; +#endif /* SQLITE_TEST */ + +#endif /* !defined(SQLITE_OMIT_VIRTUALTABLE) */ + + +#ifdef _WIN32 +__declspec(dllexport) +#endif +/* +** This routine is called when the extension is loaded. The new +** CSV virtual table module is registered with the calling database +** connection. +*/ +int sqlite3_csv_init( + sqlite3 *db, + char **pzErrMsg, + const sqlite3_api_routines *pApi +){ +#ifndef SQLITE_OMIT_VIRTUALTABLE + int rc; + rc = sqlite3_create_module(db, "csv", &CsvModule, 0); +#ifdef SQLITE_TEST + if( rc==SQLITE_OK ){ + rc = sqlite3_create_module(db, "csv_wr", &CsvModuleFauxWrite, 0); + } +#endif + return rc; +#else + return SQLITE_OK; +#endif +} \ No newline at end of file diff --git a/third_party/sqlite/sqlite-csv.h b/third_party/sqlite/sqlite-csv.h new file mode 100644 index 0000000000..35bc65c9b4 --- /dev/null +++ b/third_party/sqlite/sqlite-csv.h @@ -0,0 +1,19 @@ +#ifndef SQLITE_CSV_H +#define SQLITE_CSV_H + +#include "third_party/sqlite/sqlite3.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef _WIN32 +__declspec(dllexport) +#endif +int sqlite3_csv_init(sqlite3 *db, char **pzErrMsg, const sqlite3_api_routines *pApi); + +#ifdef __cplusplus +} /* end of the 'extern "C"' block */ +#endif + +#endif /* ifndef SQLITE__H */ \ No newline at end of file diff --git a/third_party/sqlite/sqlite-vec.c b/third_party/sqlite/sqlite-vec.c new file mode 100644 index 0000000000..d8490cd029 --- /dev/null +++ b/third_party/sqlite/sqlite-vec.c @@ -0,0 +1,9749 @@ +#include "sqlite-vec.h" + +#include <assert.h> +#include <errno.h> +#include <float.h> +#include <inttypes.h> +#include <limits.h> +#include <math.h> +#include <stdbool.h> +#include <stdint.h> +#include <stdlib.h> +#include <string.h> + +#ifndef SQLITE_VEC_OMIT_FS +#include <stdio.h> +#endif + +#ifndef SQLITE_CORE +#include "sqlite3ext.h" +SQLITE_EXTENSION_INIT1 +#else +#include "sqlite3.h" +#endif + +#ifndef UINT32_TYPE +#ifdef HAVE_UINT32_T +#define UINT32_TYPE uint32_t +#else +#define UINT32_TYPE unsigned int +#endif +#endif +#ifndef UINT16_TYPE +#ifdef HAVE_UINT16_T +#define UINT16_TYPE uint16_t +#else +#define UINT16_TYPE unsigned short int +#endif +#endif +#ifndef INT16_TYPE +#ifdef HAVE_INT16_T +#define INT16_TYPE int16_t +#else +#define INT16_TYPE short int +#endif +#endif +#ifndef UINT8_TYPE +#ifdef HAVE_UINT8_T +#define UINT8_TYPE uint8_t +#else +#define UINT8_TYPE unsigned char +#endif +#endif +#ifndef INT8_TYPE +#ifdef HAVE_INT8_T +#define INT8_TYPE int8_t +#else +#define INT8_TYPE signed char +#endif +#endif +#ifndef LONGDOUBLE_TYPE +#define LONGDOUBLE_TYPE long double +#endif + +#ifndef _WIN32 +#ifndef __EMSCRIPTEN__ +#ifndef __COSMOPOLITAN__ +#ifndef __wasi__ +typedef u_int8_t uint8_t; +typedef u_int16_t uint16_t; +typedef u_int64_t uint64_t; +#endif +#endif +#endif +#endif + +typedef int8_t i8; +typedef uint8_t u8; +typedef int16_t i16; +typedef int32_t i32; +typedef sqlite3_int64 i64; +typedef uint32_t u32; +typedef uint64_t u64; +typedef float f32; +typedef size_t usize; + +#ifndef UNUSED_PARAMETER +#define UNUSED_PARAMETER(X) (void)(X) +#endif + +// sqlite3_vtab_in() was added in SQLite version 3.38 (2022-02-22) +// https://www.sqlite.org/changes.html#version_3_38_0 +#if SQLITE_VERSION_NUMBER >= 3038000 +#define COMPILER_SUPPORTS_VTAB_IN 1 +#endif + +#ifndef SQLITE_SUBTYPE +#define SQLITE_SUBTYPE 0x000100000 +#endif + +#ifndef SQLITE_RESULT_SUBTYPE +#define SQLITE_RESULT_SUBTYPE 0x001000000 +#endif + +#ifndef SQLITE_INDEX_CONSTRAINT_LIMIT +#define SQLITE_INDEX_CONSTRAINT_LIMIT 73 +#endif + +#ifndef SQLITE_INDEX_CONSTRAINT_OFFSET +#define SQLITE_INDEX_CONSTRAINT_OFFSET 74 +#endif + +#define countof(x) (sizeof(x) / sizeof((x)[0])) +#define min(a, b) (((a) <= (b)) ? (a) : (b)) + +enum VectorElementType { + // clang-format off + SQLITE_VEC_ELEMENT_TYPE_FLOAT32 = 223 + 0, + SQLITE_VEC_ELEMENT_TYPE_BIT = 223 + 1, + SQLITE_VEC_ELEMENT_TYPE_INT8 = 223 + 2, + // clang-format on +}; + +#ifdef SQLITE_VEC_ENABLE_AVX +#include <immintrin.h> +#define PORTABLE_ALIGN32 __attribute__((aligned(32))) +#define PORTABLE_ALIGN64 __attribute__((aligned(64))) + +static f32 l2_sqr_float_avx(const void *pVect1v, const void *pVect2v, + const void *qty_ptr) { + f32 *pVect1 = (f32 *)pVect1v; + f32 *pVect2 = (f32 *)pVect2v; + size_t qty = *((size_t *)qty_ptr); + f32 PORTABLE_ALIGN32 TmpRes[8]; + size_t qty16 = qty >> 4; + + const f32 *pEnd1 = pVect1 + (qty16 << 4); + + __m256 diff, v1, v2; + __m256 sum = _mm256_set1_ps(0); + + while (pVect1 < pEnd1) { + v1 = _mm256_loadu_ps(pVect1); + pVect1 += 8; + v2 = _mm256_loadu_ps(pVect2); + pVect2 += 8; + diff = _mm256_sub_ps(v1, v2); + sum = _mm256_add_ps(sum, _mm256_mul_ps(diff, diff)); + + v1 = _mm256_loadu_ps(pVect1); + pVect1 += 8; + v2 = _mm256_loadu_ps(pVect2); + pVect2 += 8; + diff = _mm256_sub_ps(v1, v2); + sum = _mm256_add_ps(sum, _mm256_mul_ps(diff, diff)); + } + + _mm256_store_ps(TmpRes, sum); + return sqrt(TmpRes[0] + TmpRes[1] + TmpRes[2] + TmpRes[3] + TmpRes[4] + + TmpRes[5] + TmpRes[6] + TmpRes[7]); +} +#endif + +#ifdef SQLITE_VEC_ENABLE_NEON +#include <arm_neon.h> + +#define PORTABLE_ALIGN32 __attribute__((aligned(32))) + +// thx https://github.com/nmslib/hnswlib/pull/299/files +static f32 l2_sqr_float_neon(const void *pVect1v, const void *pVect2v, + const void *qty_ptr) { + f32 *pVect1 = (f32 *)pVect1v; + f32 *pVect2 = (f32 *)pVect2v; + size_t qty = *((size_t *)qty_ptr); + size_t qty16 = qty >> 4; + + const f32 *pEnd1 = pVect1 + (qty16 << 4); + + float32x4_t diff, v1, v2; + float32x4_t sum0 = vdupq_n_f32(0); + float32x4_t sum1 = vdupq_n_f32(0); + float32x4_t sum2 = vdupq_n_f32(0); + float32x4_t sum3 = vdupq_n_f32(0); + + while (pVect1 < pEnd1) { + v1 = vld1q_f32(pVect1); + pVect1 += 4; + v2 = vld1q_f32(pVect2); + pVect2 += 4; + diff = vsubq_f32(v1, v2); + sum0 = vfmaq_f32(sum0, diff, diff); + + v1 = vld1q_f32(pVect1); + pVect1 += 4; + v2 = vld1q_f32(pVect2); + pVect2 += 4; + diff = vsubq_f32(v1, v2); + sum1 = vfmaq_f32(sum1, diff, diff); + + v1 = vld1q_f32(pVect1); + pVect1 += 4; + v2 = vld1q_f32(pVect2); + pVect2 += 4; + diff = vsubq_f32(v1, v2); + sum2 = vfmaq_f32(sum2, diff, diff); + + v1 = vld1q_f32(pVect1); + pVect1 += 4; + v2 = vld1q_f32(pVect2); + pVect2 += 4; + diff = vsubq_f32(v1, v2); + sum3 = vfmaq_f32(sum3, diff, diff); + } + + f32 sum_scalar = + vaddvq_f32(vaddq_f32(vaddq_f32(sum0, sum1), vaddq_f32(sum2, sum3))); + const f32 *pEnd2 = pVect1 + (qty - (qty16 << 4)); + while (pVect1 < pEnd2) { + f32 diff = *pVect1 - *pVect2; + sum_scalar += diff * diff; + pVect1++; + pVect2++; + } + + return sqrt(sum_scalar); +} + +static f32 l2_sqr_int8_neon(const void *pVect1v, const void *pVect2v, + const void *qty_ptr) { + i8 *pVect1 = (i8 *)pVect1v; + i8 *pVect2 = (i8 *)pVect2v; + size_t qty = *((size_t *)qty_ptr); + + const i8 *pEnd1 = pVect1 + qty; + i32 sum_scalar = 0; + + while (pVect1 < pEnd1 - 7) { + // loading 8 at a time + int8x8_t v1 = vld1_s8(pVect1); + int8x8_t v2 = vld1_s8(pVect2); + pVect1 += 8; + pVect2 += 8; + + // widen to protect against overflow + int16x8_t v1_wide = vmovl_s8(v1); + int16x8_t v2_wide = vmovl_s8(v2); + + int16x8_t diff = vsubq_s16(v1_wide, v2_wide); + int16x8_t squared_diff = vmulq_s16(diff, diff); + int32x4_t sum = vpaddlq_s16(squared_diff); + + sum_scalar += vgetq_lane_s32(sum, 0) + vgetq_lane_s32(sum, 1) + + vgetq_lane_s32(sum, 2) + vgetq_lane_s32(sum, 3); + } + + // handle leftovers + while (pVect1 < pEnd1) { + i16 diff = (i16)*pVect1 - (i16)*pVect2; + sum_scalar += diff * diff; + pVect1++; + pVect2++; + } + + return sqrtf(sum_scalar); +} + +static i32 l1_int8_neon(const void *pVect1v, const void *pVect2v, + const void *qty_ptr) { + i8 *pVect1 = (i8 *)pVect1v; + i8 *pVect2 = (i8 *)pVect2v; + size_t qty = *((size_t *)qty_ptr); + + const int8_t *pEnd1 = pVect1 + qty; + + int32x4_t acc1 = vdupq_n_s32(0); + int32x4_t acc2 = vdupq_n_s32(0); + int32x4_t acc3 = vdupq_n_s32(0); + int32x4_t acc4 = vdupq_n_s32(0); + + while (pVect1 < pEnd1 - 63) { + int8x16_t v1 = vld1q_s8(pVect1); + int8x16_t v2 = vld1q_s8(pVect2); + int8x16_t diff1 = vabdq_s8(v1, v2); + acc1 = vaddq_s32(acc1, vpaddlq_u16(vpaddlq_u8(diff1))); + + v1 = vld1q_s8(pVect1 + 16); + v2 = vld1q_s8(pVect2 + 16); + int8x16_t diff2 = vabdq_s8(v1, v2); + acc2 = vaddq_s32(acc2, vpaddlq_u16(vpaddlq_u8(diff2))); + + v1 = vld1q_s8(pVect1 + 32); + v2 = vld1q_s8(pVect2 + 32); + int8x16_t diff3 = vabdq_s8(v1, v2); + acc3 = vaddq_s32(acc3, vpaddlq_u16(vpaddlq_u8(diff3))); + + v1 = vld1q_s8(pVect1 + 48); + v2 = vld1q_s8(pVect2 + 48); + int8x16_t diff4 = vabdq_s8(v1, v2); + acc4 = vaddq_s32(acc4, vpaddlq_u16(vpaddlq_u8(diff4))); + + pVect1 += 64; + pVect2 += 64; + } + + while (pVect1 < pEnd1 - 15) { + int8x16_t v1 = vld1q_s8(pVect1); + int8x16_t v2 = vld1q_s8(pVect2); + int8x16_t diff = vabdq_s8(v1, v2); + acc1 = vaddq_s32(acc1, vpaddlq_u16(vpaddlq_u8(diff))); + pVect1 += 16; + pVect2 += 16; + } + + int32x4_t acc = vaddq_s32(vaddq_s32(acc1, acc2), vaddq_s32(acc3, acc4)); + + int32_t sum = 0; + while (pVect1 < pEnd1) { + int32_t diff = abs((int32_t)*pVect1 - (int32_t)*pVect2); + sum += diff; + pVect1++; + pVect2++; + } + + return vaddvq_s32(acc) + sum; +} + +static double l1_f32_neon(const void *pVect1v, const void *pVect2v, + const void *qty_ptr) { + f32 *pVect1 = (f32 *)pVect1v; + f32 *pVect2 = (f32 *)pVect2v; + size_t qty = *((size_t *)qty_ptr); + + const f32 *pEnd1 = pVect1 + qty; + float64x2_t acc = vdupq_n_f64(0); + + while (pVect1 < pEnd1 - 3) { + float32x4_t v1 = vld1q_f32(pVect1); + float32x4_t v2 = vld1q_f32(pVect2); + pVect1 += 4; + pVect2 += 4; + + // f32x4 -> f64x2 pad for overflow + float64x2_t low_diff = vabdq_f64(vcvt_f64_f32(vget_low_f32(v1)), + vcvt_f64_f32(vget_low_f32(v2))); + float64x2_t high_diff = + vabdq_f64(vcvt_high_f64_f32(v1), vcvt_high_f64_f32(v2)); + + acc = vaddq_f64(acc, vaddq_f64(low_diff, high_diff)); + } + + double sum = 0; + while (pVect1 < pEnd1) { + sum += fabs((double)*pVect1 - (double)*pVect2); + pVect1++; + pVect2++; + } + + return vaddvq_f64(acc) + sum; +} +#endif + +static f32 l2_sqr_float(const void *pVect1v, const void *pVect2v, + const void *qty_ptr) { + f32 *pVect1 = (f32 *)pVect1v; + f32 *pVect2 = (f32 *)pVect2v; + size_t qty = *((size_t *)qty_ptr); + + f32 res = 0; + for (size_t i = 0; i < qty; i++) { + f32 t = *pVect1 - *pVect2; + pVect1++; + pVect2++; + res += t * t; + } + return sqrt(res); +} + +static f32 l2_sqr_int8(const void *pA, const void *pB, const void *pD) { + i8 *a = (i8 *)pA; + i8 *b = (i8 *)pB; + size_t d = *((size_t *)pD); + + f32 res = 0; + for (size_t i = 0; i < d; i++) { + f32 t = *a - *b; + a++; + b++; + res += t * t; + } + return sqrt(res); +} + +static f32 distance_l2_sqr_float(const void *a, const void *b, const void *d) { +#ifdef SQLITE_VEC_ENABLE_NEON + if ((*(const size_t *)d) > 16) { + return l2_sqr_float_neon(a, b, d); + } +#endif +#ifdef SQLITE_VEC_ENABLE_AVX + if (((*(const size_t *)d) % 16 == 0)) { + return l2_sqr_float_avx(a, b, d); + } +#endif + return l2_sqr_float(a, b, d); +} + +static f32 distance_l2_sqr_int8(const void *a, const void *b, const void *d) { +#ifdef SQLITE_VEC_ENABLE_NEON + if ((*(const size_t *)d) > 7) { + return l2_sqr_int8_neon(a, b, d); + } +#endif + return l2_sqr_int8(a, b, d); +} + +static i32 l1_int8(const void *pA, const void *pB, const void *pD) { + i8 *a = (i8 *)pA; + i8 *b = (i8 *)pB; + size_t d = *((size_t *)pD); + + i32 res = 0; + for (size_t i = 0; i < d; i++) { + res += abs(*a - *b); + a++; + b++; + } + + return res; +} + +static i32 distance_l1_int8(const void *a, const void *b, const void *d) { +#ifdef SQLITE_VEC_ENABLE_NEON + if ((*(const size_t *)d) > 15) { + return l1_int8_neon(a, b, d); + } +#endif + return l1_int8(a, b, d); +} + +static double l1_f32(const void *pA, const void *pB, const void *pD) { + f32 *a = (f32 *)pA; + f32 *b = (f32 *)pB; + size_t d = *((size_t *)pD); + + double res = 0; + for (size_t i = 0; i < d; i++) { + res += fabs((double)*a - (double)*b); + a++; + b++; + } + + return res; +} + +static double distance_l1_f32(const void *a, const void *b, const void *d) { +#ifdef SQLITE_VEC_ENABLE_NEON + if ((*(const size_t *)d) > 3) { + return l1_f32_neon(a, b, d); + } +#endif + return l1_f32(a, b, d); +} + +static f32 distance_cosine_float(const void *pVect1v, const void *pVect2v, + const void *qty_ptr) { + f32 *pVect1 = (f32 *)pVect1v; + f32 *pVect2 = (f32 *)pVect2v; + size_t qty = *((size_t *)qty_ptr); + + f32 dot = 0; + f32 aMag = 0; + f32 bMag = 0; + for (size_t i = 0; i < qty; i++) { + dot += *pVect1 * *pVect2; + aMag += *pVect1 * *pVect1; + bMag += *pVect2 * *pVect2; + pVect1++; + pVect2++; + } + return 1 - (dot / (sqrt(aMag) * sqrt(bMag))); +} +static f32 distance_cosine_int8(const void *pA, const void *pB, + const void *pD) { + i8 *a = (i8 *)pA; + i8 *b = (i8 *)pB; + size_t d = *((size_t *)pD); + + f32 dot = 0; + f32 aMag = 0; + f32 bMag = 0; + for (size_t i = 0; i < d; i++) { + dot += *a * *b; + aMag += *a * *a; + bMag += *b * *b; + a++; + b++; + } + return 1 - (dot / (sqrt(aMag) * sqrt(bMag))); +} + +// https://github.com/facebookresearch/faiss/blob/77e2e79cd0a680adc343b9840dd865da724c579e/faiss/utils/hamming_distance/common.h#L34 +static u8 hamdist_table[256] = { + 0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4, 1, 2, 2, 3, 2, 3, 3, 4, + 2, 3, 3, 4, 3, 4, 4, 5, 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, + 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 1, 2, 2, 3, 2, 3, 3, 4, + 2, 3, 3, 4, 3, 4, 4, 5, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, + 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5, 6, + 4, 5, 5, 6, 5, 6, 6, 7, 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, + 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 2, 3, 3, 4, 3, 4, 4, 5, + 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, + 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5, 6, + 4, 5, 5, 6, 5, 6, 6, 7, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, + 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8}; + +static f32 distance_hamming_u8(u8 *a, u8 *b, size_t n) { + int same = 0; + for (unsigned long i = 0; i < n; i++) { + same += hamdist_table[a[i] ^ b[i]]; + } + return (f32)same; +} + +#ifdef _MSC_VER +#if !defined(__clang__) && (defined(_M_ARM) || defined(_M_ARM64)) +// From +// https://github.com/ngtcp2/ngtcp2/blob/b64f1e77b5e0d880b93d31f474147fae4a1d17cc/lib/ngtcp2_ringbuf.c, +// line 34-43 +static unsigned int __builtin_popcountl(unsigned int x) { + unsigned int c = 0; + for (; x; ++c) { + x &= x - 1; + } + return c; +} +#else +#include <intrin.h> +#define __builtin_popcountl __popcnt64 +#endif +#endif + +static f32 distance_hamming_u64(u64 *a, u64 *b, size_t n) { + int same = 0; + for (unsigned long i = 0; i < n; i++) { + same += __builtin_popcountl(a[i] ^ b[i]); + } + return (f32)same; +} + +/** + * @brief Calculate the hamming distance between two bitvectors. + * + * @param a - first bitvector, MUST have d dimensions + * @param b - second bitvector, MUST have d dimensions + * @param d - pointer to size_t, MUST be divisible by CHAR_BIT + * @return f32 + */ +static f32 distance_hamming(const void *a, const void *b, const void *d) { + size_t dimensions = *((size_t *)d); + + if ((dimensions % 64) == 0) { + return distance_hamming_u64((u64 *)a, (u64 *)b, dimensions / 8 / CHAR_BIT); + } + return distance_hamming_u8((u8 *)a, (u8 *)b, dimensions / CHAR_BIT); +} + +// from SQLite source: +// https://github.com/sqlite/sqlite/blob/a509a90958ddb234d1785ed7801880ccb18b497e/src/json.c#L153 +static const char vecJsonIsSpaceX[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +}; + +#define vecJsonIsspace(x) (vecJsonIsSpaceX[(unsigned char)x]) + +typedef void (*vector_cleanup)(void *p); + +void vector_cleanup_noop(void *_) { UNUSED_PARAMETER(_); } + +#define JSON_SUBTYPE 74 + +void vtab_set_error(sqlite3_vtab *pVTab, const char *zFormat, ...) { + va_list args; + sqlite3_free(pVTab->zErrMsg); + va_start(args, zFormat); + pVTab->zErrMsg = sqlite3_vmprintf(zFormat, args); + va_end(args); +} +struct Array { + size_t element_size; + size_t length; + size_t capacity; + void *z; +}; + +/** + * @brief Initial an array with the given element size and capacity. + * + * @param array + * @param element_size + * @param init_capacity + * @return SQLITE_OK on success, error code on failure. Only error is + * SQLITE_NOMEM + */ +int array_init(struct Array *array, size_t element_size, size_t init_capacity) { + int sz = element_size * init_capacity; + void *z = sqlite3_malloc(sz); + if (!z) { + return SQLITE_NOMEM; + } + memset(z, 0, sz); + + array->element_size = element_size; + array->length = 0; + array->capacity = init_capacity; + array->z = z; + return SQLITE_OK; +} + +int array_append(struct Array *array, const void *element) { + if (array->length == array->capacity) { + size_t new_capacity = array->capacity * 2 + 100; + void *z = sqlite3_realloc64(array->z, array->element_size * new_capacity); + if (z) { + array->capacity = new_capacity; + array->z = z; + } else { + return SQLITE_NOMEM; + } + } + memcpy(&((unsigned char *)array->z)[array->length * array->element_size], + element, array->element_size); + array->length++; + return SQLITE_OK; +} + +void array_cleanup(struct Array *array) { + if (!array) + return; + array->element_size = 0; + array->length = 0; + array->capacity = 0; + sqlite3_free(array->z); + array->z = NULL; +} + +char *vector_subtype_name(int subtype) { + switch (subtype) { + case SQLITE_VEC_ELEMENT_TYPE_FLOAT32: + return "float32"; + case SQLITE_VEC_ELEMENT_TYPE_INT8: + return "int8"; + case SQLITE_VEC_ELEMENT_TYPE_BIT: + return "bit"; + } + return ""; +} +char *type_name(int type) { + switch (type) { + case SQLITE_INTEGER: + return "INTEGER"; + case SQLITE_BLOB: + return "BLOB"; + case SQLITE_TEXT: + return "TEXT"; + case SQLITE_FLOAT: + return "FLOAT"; + case SQLITE_NULL: + return "NULL"; + } + return ""; +} + +typedef void (*fvec_cleanup)(f32 *vector); + +void fvec_cleanup_noop(f32 *_) { UNUSED_PARAMETER(_); } + +static int fvec_from_value(sqlite3_value *value, f32 **vector, + size_t *dimensions, fvec_cleanup *cleanup, + char **pzErr) { + int value_type = sqlite3_value_type(value); + + if (value_type == SQLITE_BLOB) { + const void *blob = sqlite3_value_blob(value); + int bytes = sqlite3_value_bytes(value); + if (bytes == 0) { + *pzErr = sqlite3_mprintf("zero-length vectors are not supported."); + return SQLITE_ERROR; + } + if ((bytes % sizeof(f32)) != 0) { + *pzErr = sqlite3_mprintf("invalid float32 vector BLOB length. Must be " + "divisible by %d, found %d", + sizeof(f32), bytes); + return SQLITE_ERROR; + } + *vector = (f32 *)blob; + *dimensions = bytes / sizeof(f32); + *cleanup = fvec_cleanup_noop; + return SQLITE_OK; + } + + if (value_type == SQLITE_TEXT) { + const char *source = (const char *)sqlite3_value_text(value); + int source_len = sqlite3_value_bytes(value); + if (source_len == 0) { + *pzErr = sqlite3_mprintf("zero-length vectors are not supported."); + return SQLITE_ERROR; + } + int i = 0; + + struct Array x; + int rc = array_init(&x, sizeof(f32), ceil(source_len / 2.0)); + if (rc != SQLITE_OK) { + return rc; + } + + // advance leading whitespace to first '[' + while (i < source_len) { + if (vecJsonIsspace(source[i])) { + i++; + continue; + } + if (source[i] == '[') { + break; + } + + *pzErr = sqlite3_mprintf( + "JSON array parsing error: Input does not start with '['"); + array_cleanup(&x); + return SQLITE_ERROR; + } + if (source[i] != '[') { + *pzErr = sqlite3_mprintf( + "JSON array parsing error: Input does not start with '['"); + array_cleanup(&x); + return SQLITE_ERROR; + } + int offset = i + 1; + + while (offset < source_len) { + char *ptr = (char *)&source[offset]; + char *endptr; + + errno = 0; + double result = strtod(ptr, &endptr); + if ((errno != 0 && result == 0) // some interval error? + || (errno == ERANGE && + (result == HUGE_VAL || result == -HUGE_VAL)) // too big / smalls + ) { + sqlite3_free(x.z); + *pzErr = sqlite3_mprintf("JSON parsing error"); + return SQLITE_ERROR; + } + + if (endptr == ptr) { + if (*ptr != ']') { + sqlite3_free(x.z); + *pzErr = sqlite3_mprintf("JSON parsing error"); + return SQLITE_ERROR; + } + goto done; + } + + f32 res = (f32)result; + array_append(&x, (const void *)&res); + + offset += (endptr - ptr); + while (offset < source_len) { + if (vecJsonIsspace(source[offset])) { + offset++; + continue; + } + if (source[offset] == ',') { + offset++; + continue; + } + if (source[offset] == ']') + goto done; + break; + } + } + + done: + + if (x.length > 0) { + *vector = (f32 *)x.z; + *dimensions = x.length; + *cleanup = (fvec_cleanup)sqlite3_free; + return SQLITE_OK; + } + sqlite3_free(x.z); + *pzErr = sqlite3_mprintf("zero-length vectors are not supported."); + return SQLITE_ERROR; + } + + *pzErr = sqlite3_mprintf( + "Input must have type BLOB (compact format) or TEXT (JSON), found %s", + type_name(value_type)); + return SQLITE_ERROR; +} + +static int bitvec_from_value(sqlite3_value *value, u8 **vector, + size_t *dimensions, vector_cleanup *cleanup, + char **pzErr) { + int value_type = sqlite3_value_type(value); + if (value_type == SQLITE_BLOB) { + const void *blob = sqlite3_value_blob(value); + int bytes = sqlite3_value_bytes(value); + if (bytes == 0) { + *pzErr = sqlite3_mprintf("zero-length vectors are not supported."); + return SQLITE_ERROR; + } + *vector = (u8 *)blob; + *dimensions = bytes * CHAR_BIT; + *cleanup = vector_cleanup_noop; + return SQLITE_OK; + } + *pzErr = sqlite3_mprintf("Unknown type for bitvector."); + return SQLITE_ERROR; +} + +static int int8_vec_from_value(sqlite3_value *value, i8 **vector, + size_t *dimensions, vector_cleanup *cleanup, + char **pzErr) { + int value_type = sqlite3_value_type(value); + if (value_type == SQLITE_BLOB) { + const void *blob = sqlite3_value_blob(value); + int bytes = sqlite3_value_bytes(value); + if (bytes == 0) { + *pzErr = sqlite3_mprintf("zero-length vectors are not supported."); + return SQLITE_ERROR; + } + *vector = (i8 *)blob; + *dimensions = bytes; + *cleanup = vector_cleanup_noop; + return SQLITE_OK; + } + + if (value_type == SQLITE_TEXT) { + const char *source = (const char *)sqlite3_value_text(value); + int source_len = sqlite3_value_bytes(value); + int i = 0; + + if (source_len == 0) { + *pzErr = sqlite3_mprintf("zero-length vectors are not supported."); + return SQLITE_ERROR; + } + + struct Array x; + int rc = array_init(&x, sizeof(i8), ceil(source_len / 2.0)); + if (rc != SQLITE_OK) { + return rc; + } + + // advance leading whitespace to first '[' + while (i < source_len) { + if (vecJsonIsspace(source[i])) { + i++; + continue; + } + if (source[i] == '[') { + break; + } + + *pzErr = sqlite3_mprintf( + "JSON array parsing error: Input does not start with '['"); + array_cleanup(&x); + return SQLITE_ERROR; + } + if (source[i] != '[') { + *pzErr = sqlite3_mprintf( + "JSON array parsing error: Input does not start with '['"); + array_cleanup(&x); + return SQLITE_ERROR; + } + int offset = i + 1; + + while (offset < source_len) { + char *ptr = (char *)&source[offset]; + char *endptr; + + errno = 0; + long result = strtol(ptr, &endptr, 10); + if ((errno != 0 && result == 0) || + (errno == ERANGE && (result == LONG_MAX || result == LONG_MIN))) { + sqlite3_free(x.z); + *pzErr = sqlite3_mprintf("JSON parsing error"); + return SQLITE_ERROR; + } + + if (endptr == ptr) { + if (*ptr != ']') { + sqlite3_free(x.z); + *pzErr = sqlite3_mprintf("JSON parsing error"); + return SQLITE_ERROR; + } + goto done; + } + + if (result < INT8_MIN || result > INT8_MAX) { + sqlite3_free(x.z); + *pzErr = + sqlite3_mprintf("JSON parsing error: value out of range for int8"); + return SQLITE_ERROR; + } + + i8 res = (i8)result; + array_append(&x, (const void *)&res); + + offset += (endptr - ptr); + while (offset < source_len) { + if (vecJsonIsspace(source[offset])) { + offset++; + continue; + } + if (source[offset] == ',') { + offset++; + continue; + } + if (source[offset] == ']') + goto done; + break; + } + } + + done: + + if (x.length > 0) { + *vector = (i8 *)x.z; + *dimensions = x.length; + *cleanup = (vector_cleanup)sqlite3_free; + return SQLITE_OK; + } + sqlite3_free(x.z); + *pzErr = sqlite3_mprintf("zero-length vectors are not supported."); + return SQLITE_ERROR; + } + + *pzErr = sqlite3_mprintf("Unknown type for int8 vector."); + return SQLITE_ERROR; +} + +/** + * @brief Extract a vector from a sqlite3_value. Can be a float32, int8, or bit + * vector. + * + * @param value: the sqlite3_value to read from. + * @param vector: Output pointer to vector data. + * @param dimensions: Output number of dimensions + * @param dimensions: Output vector element type + * @param cleanup + * @param pzErrorMessage + * @return int SQLITE_OK on success, error code otherwise + */ +int vector_from_value(sqlite3_value *value, void **vector, size_t *dimensions, + enum VectorElementType *element_type, + vector_cleanup *cleanup, char **pzErrorMessage) { + int subtype = sqlite3_value_subtype(value); + if (!subtype || (subtype == SQLITE_VEC_ELEMENT_TYPE_FLOAT32) || + (subtype == JSON_SUBTYPE)) { + int rc = fvec_from_value(value, (f32 **)vector, dimensions, + (fvec_cleanup *)cleanup, pzErrorMessage); + if (rc == SQLITE_OK) { + *element_type = SQLITE_VEC_ELEMENT_TYPE_FLOAT32; + } + return rc; + } + + if (subtype == SQLITE_VEC_ELEMENT_TYPE_BIT) { + int rc = bitvec_from_value(value, (u8 **)vector, dimensions, cleanup, + pzErrorMessage); + if (rc == SQLITE_OK) { + *element_type = SQLITE_VEC_ELEMENT_TYPE_BIT; + } + return rc; + } + if (subtype == SQLITE_VEC_ELEMENT_TYPE_INT8) { + int rc = int8_vec_from_value(value, (i8 **)vector, dimensions, cleanup, + pzErrorMessage); + if (rc == SQLITE_OK) { + *element_type = SQLITE_VEC_ELEMENT_TYPE_INT8; + } + return rc; + } + *pzErrorMessage = sqlite3_mprintf("Unknown subtype: %d", subtype); + return SQLITE_ERROR; +} + +int ensure_vector_match(sqlite3_value *aValue, sqlite3_value *bValue, void **a, + void **b, enum VectorElementType *element_type, + size_t *dimensions, vector_cleanup *outACleanup, + vector_cleanup *outBCleanup, char **outError) { + int rc; + enum VectorElementType aType, bType; + size_t aDims, bDims; + char *error = NULL; + vector_cleanup aCleanup, bCleanup; + + rc = vector_from_value(aValue, a, &aDims, &aType, &aCleanup, &error); + if (rc != SQLITE_OK) { + *outError = sqlite3_mprintf("Error reading 1st vector: %s", error); + sqlite3_free(error); + return SQLITE_ERROR; + } + + rc = vector_from_value(bValue, b, &bDims, &bType, &bCleanup, &error); + if (rc != SQLITE_OK) { + *outError = sqlite3_mprintf("Error reading 2nd vector: %s", error); + sqlite3_free(error); + aCleanup(a); + return SQLITE_ERROR; + } + + if (aType != bType) { + *outError = + sqlite3_mprintf("Vector type mistmatch. First vector has type %s, " + "while the second has type %s.", + vector_subtype_name(aType), vector_subtype_name(bType)); + aCleanup(*a); + bCleanup(*b); + return SQLITE_ERROR; + } + if (aDims != bDims) { + *outError = sqlite3_mprintf( + "Vector dimension mistmatch. First vector has %ld dimensions, " + "while the second has %ld dimensions.", + aDims, bDims); + aCleanup(*a); + bCleanup(*b); + return SQLITE_ERROR; + } + *element_type = aType; + *dimensions = aDims; + *outACleanup = aCleanup; + *outBCleanup = bCleanup; + return SQLITE_OK; +} + +int _cmp(const void *a, const void *b) { return (*(i64 *)a - *(i64 *)b); } + +struct VecNpyFile { + char *path; + size_t pathLength; +}; +#define SQLITE_VEC_NPY_FILE_NAME "vec0-npy-file" + +#ifndef SQLITE_VEC_OMIT_FS +static void vec_npy_file(sqlite3_context *context, int argc, + sqlite3_value **argv) { + assert(argc == 1); + char *path = (char *)sqlite3_value_text(argv[0]); + size_t pathLength = sqlite3_value_bytes(argv[0]); + struct VecNpyFile *f; + + f = sqlite3_malloc(sizeof(*f)); + if (!f) { + sqlite3_result_error_nomem(context); + return; + } + memset(f, 0, sizeof(*f)); + + f->path = path; + f->pathLength = pathLength; + sqlite3_result_pointer(context, f, SQLITE_VEC_NPY_FILE_NAME, sqlite3_free); +} +#endif + +#pragma region scalar functions +static void vec_f32(sqlite3_context *context, int argc, sqlite3_value **argv) { + assert(argc == 1); + int rc; + f32 *vector = NULL; + size_t dimensions; + fvec_cleanup cleanup; + char *errmsg; + rc = fvec_from_value(argv[0], &vector, &dimensions, &cleanup, &errmsg); + if (rc != SQLITE_OK) { + sqlite3_result_error(context, errmsg, -1); + sqlite3_free(errmsg); + return; + } + sqlite3_result_blob(context, vector, dimensions * sizeof(f32), + (void (*)(void *))cleanup); + sqlite3_result_subtype(context, SQLITE_VEC_ELEMENT_TYPE_FLOAT32); +} + +static void vec_bit(sqlite3_context *context, int argc, sqlite3_value **argv) { + assert(argc == 1); + int rc; + u8 *vector; + size_t dimensions; + vector_cleanup cleanup; + char *errmsg; + rc = bitvec_from_value(argv[0], &vector, &dimensions, &cleanup, &errmsg); + if (rc != SQLITE_OK) { + sqlite3_result_error(context, errmsg, -1); + sqlite3_free(errmsg); + return; + } + sqlite3_result_blob(context, vector, dimensions / CHAR_BIT, SQLITE_TRANSIENT); + sqlite3_result_subtype(context, SQLITE_VEC_ELEMENT_TYPE_BIT); + cleanup(vector); +} +static void vec_int8(sqlite3_context *context, int argc, sqlite3_value **argv) { + assert(argc == 1); + int rc; + i8 *vector; + size_t dimensions; + vector_cleanup cleanup; + char *errmsg; + rc = int8_vec_from_value(argv[0], &vector, &dimensions, &cleanup, &errmsg); + if (rc != SQLITE_OK) { + sqlite3_result_error(context, errmsg, -1); + sqlite3_free(errmsg); + return; + } + sqlite3_result_blob(context, vector, dimensions, SQLITE_TRANSIENT); + sqlite3_result_subtype(context, SQLITE_VEC_ELEMENT_TYPE_INT8); + cleanup(vector); +} + +static void vec_length(sqlite3_context *context, int argc, + sqlite3_value **argv) { + assert(argc == 1); + int rc; + void *vector; + size_t dimensions; + vector_cleanup cleanup; + char *errmsg; + enum VectorElementType elementType; + rc = vector_from_value(argv[0], &vector, &dimensions, &elementType, &cleanup, + &errmsg); + if (rc != SQLITE_OK) { + sqlite3_result_error(context, errmsg, -1); + sqlite3_free(errmsg); + return; + } + sqlite3_result_int64(context, dimensions); + cleanup(vector); +} + +static void vec_distance_cosine(sqlite3_context *context, int argc, + sqlite3_value **argv) { + assert(argc == 2); + int rc; + void *a = NULL, *b = NULL; + size_t dimensions; + vector_cleanup aCleanup, bCleanup; + char *error; + enum VectorElementType elementType; + rc = ensure_vector_match(argv[0], argv[1], &a, &b, &elementType, &dimensions, + &aCleanup, &bCleanup, &error); + if (rc != SQLITE_OK) { + sqlite3_result_error(context, error, -1); + sqlite3_free(error); + return; + } + + switch (elementType) { + case SQLITE_VEC_ELEMENT_TYPE_BIT: { + sqlite3_result_error( + context, "Cannot calculate cosine distance between two bitvectors.", + -1); + goto finish; + } + case SQLITE_VEC_ELEMENT_TYPE_FLOAT32: { + f32 result = distance_cosine_float(a, b, &dimensions); + sqlite3_result_double(context, result); + goto finish; + } + case SQLITE_VEC_ELEMENT_TYPE_INT8: { + f32 result = distance_cosine_int8(a, b, &dimensions); + sqlite3_result_double(context, result); + goto finish; + } + } + +finish: + aCleanup(a); + bCleanup(b); + return; +} + +static void vec_distance_l2(sqlite3_context *context, int argc, + sqlite3_value **argv) { + assert(argc == 2); + int rc; + void *a = NULL, *b = NULL; + size_t dimensions; + vector_cleanup aCleanup, bCleanup; + char *error; + enum VectorElementType elementType; + rc = ensure_vector_match(argv[0], argv[1], &a, &b, &elementType, &dimensions, + &aCleanup, &bCleanup, &error); + if (rc != SQLITE_OK) { + sqlite3_result_error(context, error, -1); + sqlite3_free(error); + return; + } + + switch (elementType) { + case SQLITE_VEC_ELEMENT_TYPE_BIT: { + sqlite3_result_error( + context, "Cannot calculate L2 distance between two bitvectors.", -1); + goto finish; + } + case SQLITE_VEC_ELEMENT_TYPE_FLOAT32: { + f32 result = distance_l2_sqr_float(a, b, &dimensions); + sqlite3_result_double(context, result); + goto finish; + } + case SQLITE_VEC_ELEMENT_TYPE_INT8: { + f32 result = distance_l2_sqr_int8(a, b, &dimensions); + sqlite3_result_double(context, result); + goto finish; + } + } + +finish: + aCleanup(a); + bCleanup(b); + return; +} + +static void vec_distance_l1(sqlite3_context *context, int argc, + sqlite3_value **argv) { + assert(argc == 2); + int rc; + void *a, *b; + size_t dimensions; + vector_cleanup aCleanup, bCleanup; + char *error; + enum VectorElementType elementType; + rc = ensure_vector_match(argv[0], argv[1], &a, &b, &elementType, &dimensions, + &aCleanup, &bCleanup, &error); + if (rc != SQLITE_OK) { + sqlite3_result_error(context, error, -1); + sqlite3_free(error); + return; + } + + switch (elementType) { + case SQLITE_VEC_ELEMENT_TYPE_BIT: { + sqlite3_result_error( + context, "Cannot calculate L1 distance between two bitvectors.", -1); + goto finish; + } + case SQLITE_VEC_ELEMENT_TYPE_FLOAT32: { + double result = distance_l1_f32(a, b, &dimensions); + sqlite3_result_double(context, result); + goto finish; + } + case SQLITE_VEC_ELEMENT_TYPE_INT8: { + i64 result = distance_l1_int8(a, b, &dimensions); + sqlite3_result_int(context, result); + goto finish; + } + } + +finish: + aCleanup(a); + bCleanup(b); + return; +} + +static void vec_distance_hamming(sqlite3_context *context, int argc, + sqlite3_value **argv) { + assert(argc == 2); + int rc; + void *a = NULL, *b = NULL; + size_t dimensions; + vector_cleanup aCleanup, bCleanup; + char *error; + enum VectorElementType elementType; + rc = ensure_vector_match(argv[0], argv[1], &a, &b, &elementType, &dimensions, + &aCleanup, &bCleanup, &error); + if (rc != SQLITE_OK) { + sqlite3_result_error(context, error, -1); + sqlite3_free(error); + return; + } + + switch (elementType) { + case SQLITE_VEC_ELEMENT_TYPE_BIT: { + sqlite3_result_double(context, distance_hamming(a, b, &dimensions)); + goto finish; + } + case SQLITE_VEC_ELEMENT_TYPE_FLOAT32: { + sqlite3_result_error( + context, + "Cannot calculate hamming distance between two float32 vectors.", -1); + goto finish; + } + case SQLITE_VEC_ELEMENT_TYPE_INT8: { + sqlite3_result_error( + context, "Cannot calculate hamming distance between two int8 vectors.", + -1); + goto finish; + } + } + +finish: + aCleanup(a); + bCleanup(b); + return; +} + +char *vec_type_name(enum VectorElementType elementType) { + switch (elementType) { + case SQLITE_VEC_ELEMENT_TYPE_FLOAT32: + return "float32"; + case SQLITE_VEC_ELEMENT_TYPE_INT8: + return "int8"; + case SQLITE_VEC_ELEMENT_TYPE_BIT: + return "bit"; + } + return ""; +} + +static void vec_type(sqlite3_context *context, int argc, sqlite3_value **argv) { + assert(argc == 1); + void *vector; + size_t dimensions; + vector_cleanup cleanup; + char *pzError; + enum VectorElementType elementType; + int rc = vector_from_value(argv[0], &vector, &dimensions, &elementType, + &cleanup, &pzError); + if (rc != SQLITE_OK) { + sqlite3_result_error(context, pzError, -1); + sqlite3_free(pzError); + return; + } + sqlite3_result_text(context, vec_type_name(elementType), -1, SQLITE_STATIC); + cleanup(vector); +} +static void vec_quantize_binary(sqlite3_context *context, int argc, + sqlite3_value **argv) { + assert(argc == 1); + void *vector; + size_t dimensions; + vector_cleanup vectorCleanup; + char *pzError; + enum VectorElementType elementType; + int rc = vector_from_value(argv[0], &vector, &dimensions, &elementType, + &vectorCleanup, &pzError); + if (rc != SQLITE_OK) { + sqlite3_result_error(context, pzError, -1); + sqlite3_free(pzError); + return; + } + + if (dimensions <= 0) { + sqlite3_result_error(context, "Zero length vectors are not supported.", -1); + goto cleanup; + return; + } + if ((dimensions % CHAR_BIT) != 0) { + sqlite3_result_error( + context, + "Binary quantization requires vectors with a length divisible by 8", + -1); + goto cleanup; + return; + } + + int sz = dimensions / CHAR_BIT; + u8 *out = sqlite3_malloc(sz); + if (!out) { + sqlite3_result_error_code(context, SQLITE_NOMEM); + goto cleanup; + return; + } + memset(out, 0, sz); + + switch (elementType) { + case SQLITE_VEC_ELEMENT_TYPE_FLOAT32: { + + for (size_t i = 0; i < dimensions; i++) { + int res = ((f32 *)vector)[i] > 0.0; + out[i / 8] |= (res << (i % 8)); + } + break; + } + case SQLITE_VEC_ELEMENT_TYPE_INT8: { + for (size_t i = 0; i < dimensions; i++) { + int res = ((i8 *)vector)[i] > 0; + out[i / 8] |= (res << (i % 8)); + } + break; + } + case SQLITE_VEC_ELEMENT_TYPE_BIT: { + sqlite3_result_error(context, + "Can only binary quantize float or int8 vectors", -1); + sqlite3_free(out); + return; + } + } + sqlite3_result_blob(context, out, sz, sqlite3_free); + sqlite3_result_subtype(context, SQLITE_VEC_ELEMENT_TYPE_BIT); + +cleanup: + vectorCleanup(vector); +} + +static void vec_quantize_int8(sqlite3_context *context, int argc, + sqlite3_value **argv) { + assert(argc == 2); + f32 *srcVector; + size_t dimensions; + fvec_cleanup srcCleanup; + char *err; + i8 *out = NULL; + int rc = fvec_from_value(argv[0], &srcVector, &dimensions, &srcCleanup, &err); + if (rc != SQLITE_OK) { + sqlite3_result_error(context, err, -1); + sqlite3_free(err); + return; + } + + int sz = dimensions * sizeof(i8); + out = sqlite3_malloc(sz); + if (!out) { + sqlite3_result_error_nomem(context); + goto cleanup; + } + memset(out, 0, sz); + + if ((sqlite3_value_type(argv[1]) != SQLITE_TEXT) || + (sqlite3_value_bytes(argv[1]) != strlen("unit")) || + (sqlite3_stricmp((const char *)sqlite3_value_text(argv[1]), "unit") != + 0)) { + sqlite3_result_error( + context, "2nd argument to vec_quantize_int8() must be 'unit'.", -1); + sqlite3_free(out); + goto cleanup; + } + f32 step = (1.0 - (-1.0)) / 255; + for (size_t i = 0; i < dimensions; i++) { + out[i] = ((srcVector[i] - (-1.0)) / step) - 128; + } + + sqlite3_result_blob(context, out, dimensions * sizeof(i8), sqlite3_free); + sqlite3_result_subtype(context, SQLITE_VEC_ELEMENT_TYPE_INT8); + +cleanup: + srcCleanup(srcVector); +} + +static void vec_add(sqlite3_context *context, int argc, sqlite3_value **argv) { + assert(argc == 2); + int rc; + void *a = NULL, *b = NULL; + size_t dimensions; + vector_cleanup aCleanup, bCleanup; + char *error; + enum VectorElementType elementType; + rc = ensure_vector_match(argv[0], argv[1], &a, &b, &elementType, &dimensions, + &aCleanup, &bCleanup, &error); + if (rc != SQLITE_OK) { + sqlite3_result_error(context, error, -1); + sqlite3_free(error); + return; + } + + switch (elementType) { + case SQLITE_VEC_ELEMENT_TYPE_BIT: { + sqlite3_result_error(context, "Cannot add two bitvectors together.", -1); + goto finish; + } + case SQLITE_VEC_ELEMENT_TYPE_FLOAT32: { + size_t outSize = dimensions * sizeof(f32); + f32 *out = sqlite3_malloc(outSize); + if (!out) { + sqlite3_result_error_nomem(context); + goto finish; + } + memset(out, 0, outSize); + for (size_t i = 0; i < dimensions; i++) { + out[i] = ((f32 *)a)[i] + ((f32 *)b)[i]; + } + sqlite3_result_blob(context, out, outSize, sqlite3_free); + sqlite3_result_subtype(context, SQLITE_VEC_ELEMENT_TYPE_FLOAT32); + goto finish; + } + case SQLITE_VEC_ELEMENT_TYPE_INT8: { + size_t outSize = dimensions * sizeof(i8); + i8 *out = sqlite3_malloc(outSize); + if (!out) { + sqlite3_result_error_nomem(context); + goto finish; + } + memset(out, 0, outSize); + for (size_t i = 0; i < dimensions; i++) { + out[i] = ((i8 *)a)[i] + ((i8 *)b)[i]; + } + sqlite3_result_blob(context, out, outSize, sqlite3_free); + sqlite3_result_subtype(context, SQLITE_VEC_ELEMENT_TYPE_INT8); + goto finish; + } + } +finish: + aCleanup(a); + bCleanup(b); + return; +} +static void vec_sub(sqlite3_context *context, int argc, sqlite3_value **argv) { + assert(argc == 2); + int rc; + void *a = NULL, *b = NULL; + size_t dimensions; + vector_cleanup aCleanup, bCleanup; + char *error; + enum VectorElementType elementType; + rc = ensure_vector_match(argv[0], argv[1], &a, &b, &elementType, &dimensions, + &aCleanup, &bCleanup, &error); + if (rc != SQLITE_OK) { + sqlite3_result_error(context, error, -1); + sqlite3_free(error); + return; + } + + switch (elementType) { + case SQLITE_VEC_ELEMENT_TYPE_BIT: { + sqlite3_result_error(context, "Cannot subtract two bitvectors together.", + -1); + goto finish; + } + case SQLITE_VEC_ELEMENT_TYPE_FLOAT32: { + size_t outSize = dimensions * sizeof(f32); + f32 *out = sqlite3_malloc(outSize); + if (!out) { + sqlite3_result_error_nomem(context); + goto finish; + } + memset(out, 0, outSize); + for (size_t i = 0; i < dimensions; i++) { + out[i] = ((f32 *)a)[i] - ((f32 *)b)[i]; + } + sqlite3_result_blob(context, out, outSize, sqlite3_free); + sqlite3_result_subtype(context, SQLITE_VEC_ELEMENT_TYPE_FLOAT32); + goto finish; + } + case SQLITE_VEC_ELEMENT_TYPE_INT8: { + size_t outSize = dimensions * sizeof(i8); + i8 *out = sqlite3_malloc(outSize); + if (!out) { + sqlite3_result_error_nomem(context); + goto finish; + } + memset(out, 0, outSize); + for (size_t i = 0; i < dimensions; i++) { + out[i] = ((i8 *)a)[i] - ((i8 *)b)[i]; + } + sqlite3_result_blob(context, out, outSize, sqlite3_free); + sqlite3_result_subtype(context, SQLITE_VEC_ELEMENT_TYPE_INT8); + goto finish; + } + } +finish: + aCleanup(a); + bCleanup(b); + return; +} +static void vec_slice(sqlite3_context *context, int argc, + sqlite3_value **argv) { + assert(argc == 3); + + void *vector; + size_t dimensions; + vector_cleanup cleanup; + char *err; + enum VectorElementType elementType; + + int rc = vector_from_value(argv[0], &vector, &dimensions, &elementType, + &cleanup, &err); + if (rc != SQLITE_OK) { + sqlite3_result_error(context, err, -1); + sqlite3_free(err); + return; + } + + int start = sqlite3_value_int(argv[1]); + int end = sqlite3_value_int(argv[2]); + + if (start < 0) { + sqlite3_result_error(context, + "slice 'start' index must be a postive number.", -1); + goto done; + } + if (end < 0) { + sqlite3_result_error(context, "slice 'end' index must be a postive number.", + -1); + goto done; + } + if (((size_t)start) > dimensions) { + sqlite3_result_error( + context, "slice 'start' index is greater than the number of dimensions", + -1); + goto done; + } + if (((size_t)end) > dimensions) { + sqlite3_result_error( + context, "slice 'end' index is greater than the number of dimensions", + -1); + goto done; + } + if (start > end) { + sqlite3_result_error(context, + "slice 'start' index is greater than 'end' index", -1); + goto done; + } + if (start == end) { + sqlite3_result_error(context, + "slice 'start' index is equal to the 'end' index, " + "vectors must have non-zero length", + -1); + goto done; + } + size_t n = end - start; + + switch (elementType) { + case SQLITE_VEC_ELEMENT_TYPE_FLOAT32: { + int outSize = n * sizeof(f32); + f32 *out = sqlite3_malloc(outSize); + if (!out) { + sqlite3_result_error_nomem(context); + goto done; + } + memset(out, 0, outSize); + for (size_t i = 0; i < n; i++) { + out[i] = ((f32 *)vector)[start + i]; + } + sqlite3_result_blob(context, out, outSize, sqlite3_free); + sqlite3_result_subtype(context, SQLITE_VEC_ELEMENT_TYPE_FLOAT32); + goto done; + } + case SQLITE_VEC_ELEMENT_TYPE_INT8: { + int outSize = n * sizeof(i8); + i8 *out = sqlite3_malloc(outSize); + if (!out) { + sqlite3_result_error_nomem(context); + return; + } + memset(out, 0, outSize); + for (size_t i = 0; i < n; i++) { + out[i] = ((i8 *)vector)[start + i]; + } + sqlite3_result_blob(context, out, outSize, sqlite3_free); + sqlite3_result_subtype(context, SQLITE_VEC_ELEMENT_TYPE_INT8); + goto done; + } + case SQLITE_VEC_ELEMENT_TYPE_BIT: { + if ((start % CHAR_BIT) != 0) { + sqlite3_result_error(context, "start index must be divisible by 8.", -1); + goto done; + } + if ((end % CHAR_BIT) != 0) { + sqlite3_result_error(context, "end index must be divisible by 8.", -1); + goto done; + } + int outSize = n / CHAR_BIT; + u8 *out = sqlite3_malloc(outSize); + if (!out) { + sqlite3_result_error_nomem(context); + return; + } + memset(out, 0, outSize); + for (size_t i = 0; i < n / CHAR_BIT; i++) { + out[i] = ((u8 *)vector)[(start / CHAR_BIT) + i]; + } + sqlite3_result_blob(context, out, outSize, sqlite3_free); + sqlite3_result_subtype(context, SQLITE_VEC_ELEMENT_TYPE_BIT); + goto done; + } + } +done: + cleanup(vector); +} + +static void vec_to_json(sqlite3_context *context, int argc, + sqlite3_value **argv) { + assert(argc == 1); + void *vector; + size_t dimensions; + vector_cleanup cleanup; + char *err; + enum VectorElementType elementType; + + int rc = vector_from_value(argv[0], &vector, &dimensions, &elementType, + &cleanup, &err); + if (rc != SQLITE_OK) { + sqlite3_result_error(context, err, -1); + sqlite3_free(err); + return; + } + + sqlite3_str *str = sqlite3_str_new(sqlite3_context_db_handle(context)); + sqlite3_str_appendall(str, "["); + for (size_t i = 0; i < dimensions; i++) { + if (i != 0) { + sqlite3_str_appendall(str, ","); + } + if (elementType == SQLITE_VEC_ELEMENT_TYPE_FLOAT32) { + f32 value = ((f32 *)vector)[i]; + if (isnan(value)) { + sqlite3_str_appendall(str, "null"); + } else { + sqlite3_str_appendf(str, "%f", value); + } + + } else if (elementType == SQLITE_VEC_ELEMENT_TYPE_INT8) { + sqlite3_str_appendf(str, "%d", ((i8 *)vector)[i]); + } else if (elementType == SQLITE_VEC_ELEMENT_TYPE_BIT) { + u8 b = (((u8 *)vector)[i / 8] >> (i % CHAR_BIT)) & 1; + sqlite3_str_appendf(str, "%d", b); + } + } + sqlite3_str_appendall(str, "]"); + int len = sqlite3_str_length(str); + char *s = sqlite3_str_finish(str); + if (s) { + sqlite3_result_text(context, s, len, sqlite3_free); + sqlite3_result_subtype(context, JSON_SUBTYPE); + } else { + sqlite3_result_error_nomem(context); + } + cleanup(vector); +} + +static void vec_normalize(sqlite3_context *context, int argc, + sqlite3_value **argv) { + assert(argc == 1); + void *vector; + size_t dimensions; + vector_cleanup cleanup; + char *err; + enum VectorElementType elementType; + + int rc = vector_from_value(argv[0], &vector, &dimensions, &elementType, + &cleanup, &err); + if (rc != SQLITE_OK) { + sqlite3_result_error(context, err, -1); + sqlite3_free(err); + return; + } + + if (elementType != SQLITE_VEC_ELEMENT_TYPE_FLOAT32) { + sqlite3_result_error( + context, "only float32 vectors are supported when normalizing", -1); + cleanup(vector); + return; + } + + int outSize = dimensions * sizeof(f32); + f32 *out = sqlite3_malloc(outSize); + if (!out) { + cleanup(vector); + sqlite3_result_error_code(context, SQLITE_NOMEM); + return; + } + memset(out, 0, outSize); + + f32 *v = (f32 *)vector; + + f32 norm = 0; + for (size_t i = 0; i < dimensions; i++) { + norm += v[i] * v[i]; + } + norm = sqrt(norm); + for (size_t i = 0; i < dimensions; i++) { + out[i] = v[i] / norm; + } + + sqlite3_result_blob(context, out, dimensions * sizeof(f32), sqlite3_free); + sqlite3_result_subtype(context, SQLITE_VEC_ELEMENT_TYPE_FLOAT32); + cleanup(vector); +} + +static void _static_text_func(sqlite3_context *context, int argc, + sqlite3_value **argv) { + UNUSED_PARAMETER(argc); + UNUSED_PARAMETER(argv); + sqlite3_result_text(context, sqlite3_user_data(context), -1, SQLITE_STATIC); +} + +#pragma endregion + +enum Vec0TokenType { + TOKEN_TYPE_IDENTIFIER, + TOKEN_TYPE_DIGIT, + TOKEN_TYPE_LBRACKET, + TOKEN_TYPE_RBRACKET, + TOKEN_TYPE_PLUS, + TOKEN_TYPE_EQ, +}; +struct Vec0Token { + enum Vec0TokenType token_type; + char *start; + char *end; +}; + +int is_alpha(char x) { + return (x >= 'a' && x <= 'z') || (x >= 'A' && x <= 'Z'); +} +int is_digit(char x) { return (x >= '0' && x <= '9'); } +int is_whitespace(char x) { + return x == ' ' || x == '\t' || x == '\n' || x == '\r'; +} + +#define VEC0_TOKEN_RESULT_EOF 1 +#define VEC0_TOKEN_RESULT_SOME 2 +#define VEC0_TOKEN_RESULT_ERROR 3 + +int vec0_token_next(char *start, char *end, struct Vec0Token *out) { + char *ptr = start; + while (ptr < end) { + char curr = *ptr; + if (is_whitespace(curr)) { + ptr++; + continue; + } else if (curr == '+') { + ptr++; + out->start = ptr; + out->end = ptr; + out->token_type = TOKEN_TYPE_PLUS; + return VEC0_TOKEN_RESULT_SOME; + } else if (curr == '[') { + ptr++; + out->start = ptr; + out->end = ptr; + out->token_type = TOKEN_TYPE_LBRACKET; + return VEC0_TOKEN_RESULT_SOME; + } else if (curr == ']') { + ptr++; + out->start = ptr; + out->end = ptr; + out->token_type = TOKEN_TYPE_RBRACKET; + return VEC0_TOKEN_RESULT_SOME; + } else if (curr == '=') { + ptr++; + out->start = ptr; + out->end = ptr; + out->token_type = TOKEN_TYPE_EQ; + return VEC0_TOKEN_RESULT_SOME; + } else if (is_alpha(curr)) { + char *start = ptr; + while (ptr < end && (is_alpha(*ptr) || is_digit(*ptr) || *ptr == '_')) { + ptr++; + } + out->start = start; + out->end = ptr; + out->token_type = TOKEN_TYPE_IDENTIFIER; + return VEC0_TOKEN_RESULT_SOME; + } else if (is_digit(curr)) { + char *start = ptr; + while (ptr < end && (is_digit(*ptr))) { + ptr++; + } + out->start = start; + out->end = ptr; + out->token_type = TOKEN_TYPE_DIGIT; + return VEC0_TOKEN_RESULT_SOME; + } else { + return VEC0_TOKEN_RESULT_ERROR; + } + } + return VEC0_TOKEN_RESULT_EOF; +} + +struct Vec0Scanner { + char *start; + char *end; + char *ptr; +}; + +void vec0_scanner_init(struct Vec0Scanner *scanner, const char *source, + int source_length) { + scanner->start = (char *)source; + scanner->end = (char *)source + source_length; + scanner->ptr = (char *)source; +} +int vec0_scanner_next(struct Vec0Scanner *scanner, struct Vec0Token *out) { + int rc = vec0_token_next(scanner->start, scanner->end, out); + if (rc == VEC0_TOKEN_RESULT_SOME) { + scanner->start = out->end; + } + return rc; +} + +int vec0_parse_table_option(const char *source, int source_length, + char **out_key, int *out_key_length, + char **out_value, int *out_value_length) { + int rc; + struct Vec0Scanner scanner; + struct Vec0Token token; + char *key; + char *value; + int keyLength, valueLength; + + vec0_scanner_init(&scanner, source, source_length); + + rc = vec0_scanner_next(&scanner, &token); + if (rc != VEC0_TOKEN_RESULT_SOME && + token.token_type != TOKEN_TYPE_IDENTIFIER) { + return SQLITE_EMPTY; + } + key = token.start; + keyLength = token.end - token.start; + + rc = vec0_scanner_next(&scanner, &token); + if (rc != VEC0_TOKEN_RESULT_SOME && token.token_type != TOKEN_TYPE_EQ) { + return SQLITE_EMPTY; + } + + rc = vec0_scanner_next(&scanner, &token); + if (rc != VEC0_TOKEN_RESULT_SOME && + !((token.token_type == TOKEN_TYPE_IDENTIFIER) || + (token.token_type == TOKEN_TYPE_DIGIT))) { + return SQLITE_ERROR; + } + value = token.start; + valueLength = token.end - token.start; + + rc = vec0_scanner_next(&scanner, &token); + if (rc == VEC0_TOKEN_RESULT_EOF) { + *out_key = key; + *out_key_length = keyLength; + *out_value = value; + *out_value_length = valueLength; + return SQLITE_OK; + } + return SQLITE_ERROR; +} +/** + * @brief Parse an argv[i] entry of a vec0 virtual table definition, and see if + * it's a PARTITION KEY definition. + * + * @param source: argv[i] source string + * @param source_length: length of the source string + * @param out_column_name: If it is a partition key, the output column name. Same lifetime + * as source, points to specific char * + * @param out_column_name_length: Length of out_column_name in bytes + * @param out_column_type: SQLITE_TEXT or SQLITE_INTEGER. + * @return int: SQLITE_EMPTY if not a PK, SQLITE_OK if it is. + */ +int vec0_parse_partition_key_definition(const char *source, int source_length, + char **out_column_name, + int *out_column_name_length, + int *out_column_type) { + struct Vec0Scanner scanner; + struct Vec0Token token; + char *column_name; + int column_name_length; + int column_type; + vec0_scanner_init(&scanner, source, source_length); + + // Check first token is identifier, will be the column name + int rc = vec0_scanner_next(&scanner, &token); + if (rc != VEC0_TOKEN_RESULT_SOME && + token.token_type != TOKEN_TYPE_IDENTIFIER) { + return SQLITE_EMPTY; + } + + column_name = token.start; + column_name_length = token.end - token.start; + + // Check the next token matches "text" or "integer", as column type + rc = vec0_scanner_next(&scanner, &token); + if (rc != VEC0_TOKEN_RESULT_SOME && + token.token_type != TOKEN_TYPE_IDENTIFIER) { + return SQLITE_EMPTY; + } + if (sqlite3_strnicmp(token.start, "text", token.end - token.start) == 0) { + column_type = SQLITE_TEXT; + } else if (sqlite3_strnicmp(token.start, "int", token.end - token.start) == + 0 || + sqlite3_strnicmp(token.start, "integer", + token.end - token.start) == 0) { + column_type = SQLITE_INTEGER; + } else { + return SQLITE_EMPTY; + } + + // Check the next token is identifier and matches "partition" + rc = vec0_scanner_next(&scanner, &token); + if (rc != VEC0_TOKEN_RESULT_SOME && + token.token_type != TOKEN_TYPE_IDENTIFIER) { + return SQLITE_EMPTY; + } + if (sqlite3_strnicmp(token.start, "partition", token.end - token.start) != 0) { + return SQLITE_EMPTY; + } + + // Check the next token is identifier and matches "key" + rc = vec0_scanner_next(&scanner, &token); + if (rc != VEC0_TOKEN_RESULT_SOME && + token.token_type != TOKEN_TYPE_IDENTIFIER) { + return SQLITE_EMPTY; + } + if (sqlite3_strnicmp(token.start, "key", token.end - token.start) != 0) { + return SQLITE_EMPTY; + } + + *out_column_name = column_name; + *out_column_name_length = column_name_length; + *out_column_type = column_type; + + return SQLITE_OK; +} + +/** + * @brief Parse an argv[i] entry of a vec0 virtual table definition, and see if + * it's an auxiliar column definition, ie `+[name] [type]` like `+contents text` + * + * @param source: argv[i] source string + * @param source_length: length of the source string + * @param out_column_name: If it is a partition key, the output column name. Same lifetime + * as source, points to specific char * + * @param out_column_name_length: Length of out_column_name in bytes + * @param out_column_type: SQLITE_TEXT, SQLITE_INTEGER, SQLITE_FLOAT, or SQLITE_BLOB. + * @return int: SQLITE_EMPTY if not an aux column, SQLITE_OK if it is. + */ +int vec0_parse_auxiliary_column_definition(const char *source, int source_length, + char **out_column_name, + int *out_column_name_length, + int *out_column_type) { + struct Vec0Scanner scanner; + struct Vec0Token token; + char *column_name; + int column_name_length; + int column_type; + vec0_scanner_init(&scanner, source, source_length); + + // Check first token is '+', which denotes aux columns + int rc = vec0_scanner_next(&scanner, &token); + if (rc != VEC0_TOKEN_RESULT_SOME || + token.token_type != TOKEN_TYPE_PLUS) { + return SQLITE_EMPTY; + } + + rc = vec0_scanner_next(&scanner, &token); + if (rc != VEC0_TOKEN_RESULT_SOME && + token.token_type != TOKEN_TYPE_IDENTIFIER) { + return SQLITE_EMPTY; + } + + column_name = token.start; + column_name_length = token.end - token.start; + + // Check the next token matches "text" or "integer", as column type + rc = vec0_scanner_next(&scanner, &token); + if (rc != VEC0_TOKEN_RESULT_SOME && + token.token_type != TOKEN_TYPE_IDENTIFIER) { + return SQLITE_EMPTY; + } + if (sqlite3_strnicmp(token.start, "text", token.end - token.start) == 0) { + column_type = SQLITE_TEXT; + } else if (sqlite3_strnicmp(token.start, "int", token.end - token.start) == + 0 || + sqlite3_strnicmp(token.start, "integer", + token.end - token.start) == 0) { + column_type = SQLITE_INTEGER; + } else if (sqlite3_strnicmp(token.start, "float", token.end - token.start) == + 0 || + sqlite3_strnicmp(token.start, "double", + token.end - token.start) == 0) { + column_type = SQLITE_FLOAT; + } else if (sqlite3_strnicmp(token.start, "blob", token.end - token.start) ==0) { + column_type = SQLITE_BLOB; + } else { + return SQLITE_EMPTY; + } + + *out_column_name = column_name; + *out_column_name_length = column_name_length; + *out_column_type = column_type; + + return SQLITE_OK; +} + +typedef enum { + VEC0_METADATA_COLUMN_KIND_BOOLEAN, + VEC0_METADATA_COLUMN_KIND_INTEGER, + VEC0_METADATA_COLUMN_KIND_FLOAT, + VEC0_METADATA_COLUMN_KIND_TEXT, + // future: blob, date, datetime +} vec0_metadata_column_kind; + +/** + * @brief Parse an argv[i] entry of a vec0 virtual table definition, and see if + * it's an metadata column definition, ie `[name] [type]` like `is_released boolean` + * + * @param source: argv[i] source string + * @param source_length: length of the source string + * @param out_column_name: If it is a metadata column, the output column name. Same lifetime + * as source, points to specific char * + * @param out_column_name_length: Length of out_column_name in bytes + * @param out_column_type: one of vec0_metadata_column_kind + * @return int: SQLITE_EMPTY if not an metadata column, SQLITE_OK if it is. + */ +int vec0_parse_metadata_column_definition(const char *source, int source_length, + char **out_column_name, + int *out_column_name_length, + vec0_metadata_column_kind *out_column_type) { + struct Vec0Scanner scanner; + struct Vec0Token token; + char *column_name; + int column_name_length; + vec0_metadata_column_kind column_type; + int rc; + vec0_scanner_init(&scanner, source, source_length); + + rc = vec0_scanner_next(&scanner, &token); + if (rc != VEC0_TOKEN_RESULT_SOME || + token.token_type != TOKEN_TYPE_IDENTIFIER) { + return SQLITE_EMPTY; + } + + column_name = token.start; + column_name_length = token.end - token.start; + + // Check the next token matches a valid metadata type + rc = vec0_scanner_next(&scanner, &token); + if (rc != VEC0_TOKEN_RESULT_SOME || + token.token_type != TOKEN_TYPE_IDENTIFIER) { + return SQLITE_EMPTY; + } + char * t = token.start; + int n = token.end - token.start; + if (sqlite3_strnicmp(t, "boolean", n) == 0 || sqlite3_strnicmp(t, "bool", n) == 0) { + column_type = VEC0_METADATA_COLUMN_KIND_BOOLEAN; + }else if (sqlite3_strnicmp(t, "int64", n) == 0 || sqlite3_strnicmp(t, "integer64", n) == 0 || sqlite3_strnicmp(t, "integer", n) == 0 || sqlite3_strnicmp(t, "int", n) == 0) { + column_type = VEC0_METADATA_COLUMN_KIND_INTEGER; + }else if (sqlite3_strnicmp(t, "float", n) == 0 || sqlite3_strnicmp(t, "double", n) == 0 || sqlite3_strnicmp(t, "float64", n) == 0 || sqlite3_strnicmp(t, "f64", n) == 0) { + column_type = VEC0_METADATA_COLUMN_KIND_FLOAT; + } else if (sqlite3_strnicmp(t, "text", n) == 0) { + column_type = VEC0_METADATA_COLUMN_KIND_TEXT; + } else { + return SQLITE_EMPTY; + } + + *out_column_name = column_name; + *out_column_name_length = column_name_length; + *out_column_type = column_type; + + return SQLITE_OK; +} + +/** + * @brief Parse an argv[i] entry of a vec0 virtual table definition, and see if + * it's a PRIMARY KEY definition. + * + * @param source: argv[i] source string + * @param source_length: length of the source string + * @param out_column_name: If it is a PK, the output column name. Same lifetime + * as source, points to specific char * + * @param out_column_name_length: Length of out_column_name in bytes + * @param out_column_type: SQLITE_TEXT or SQLITE_INTEGER. + * @return int: SQLITE_EMPTY if not a PK, SQLITE_OK if it is. + */ +int vec0_parse_primary_key_definition(const char *source, int source_length, + char **out_column_name, + int *out_column_name_length, + int *out_column_type) { + struct Vec0Scanner scanner; + struct Vec0Token token; + char *column_name; + int column_name_length; + int column_type; + vec0_scanner_init(&scanner, source, source_length); + + // Check first token is identifier, will be the column name + int rc = vec0_scanner_next(&scanner, &token); + if (rc != VEC0_TOKEN_RESULT_SOME && + token.token_type != TOKEN_TYPE_IDENTIFIER) { + return SQLITE_EMPTY; + } + + column_name = token.start; + column_name_length = token.end - token.start; + + // Check the next token matches "text" or "integer", as column type + rc = vec0_scanner_next(&scanner, &token); + if (rc != VEC0_TOKEN_RESULT_SOME && + token.token_type != TOKEN_TYPE_IDENTIFIER) { + return SQLITE_EMPTY; + } + if (sqlite3_strnicmp(token.start, "text", token.end - token.start) == 0) { + column_type = SQLITE_TEXT; + } else if (sqlite3_strnicmp(token.start, "int", token.end - token.start) == + 0 || + sqlite3_strnicmp(token.start, "integer", + token.end - token.start) == 0) { + column_type = SQLITE_INTEGER; + } else { + return SQLITE_EMPTY; + } + + // Check the next token is identifier and matches "primary" + rc = vec0_scanner_next(&scanner, &token); + if (rc != VEC0_TOKEN_RESULT_SOME && + token.token_type != TOKEN_TYPE_IDENTIFIER) { + return SQLITE_EMPTY; + } + if (sqlite3_strnicmp(token.start, "primary", token.end - token.start) != 0) { + return SQLITE_EMPTY; + } + + // Check the next token is identifier and matches "key" + rc = vec0_scanner_next(&scanner, &token); + if (rc != VEC0_TOKEN_RESULT_SOME && + token.token_type != TOKEN_TYPE_IDENTIFIER) { + return SQLITE_EMPTY; + } + if (sqlite3_strnicmp(token.start, "key", token.end - token.start) != 0) { + return SQLITE_EMPTY; + } + + *out_column_name = column_name; + *out_column_name_length = column_name_length; + *out_column_type = column_type; + + return SQLITE_OK; +} + +enum Vec0DistanceMetrics { + VEC0_DISTANCE_METRIC_L2 = 1, + VEC0_DISTANCE_METRIC_COSINE = 2, + VEC0_DISTANCE_METRIC_L1 = 3, +}; + +struct VectorColumnDefinition { + char *name; + int name_length; + size_t dimensions; + enum VectorElementType element_type; + enum Vec0DistanceMetrics distance_metric; +}; + +struct Vec0PartitionColumnDefinition { + int type; + char * name; + int name_length; +}; + +struct Vec0AuxiliaryColumnDefinition { + int type; + char * name; + int name_length; +}; +struct Vec0MetadataColumnDefinition { + vec0_metadata_column_kind kind; + char * name; + int name_length; +}; + +size_t vector_byte_size(enum VectorElementType element_type, + size_t dimensions) { + switch (element_type) { + case SQLITE_VEC_ELEMENT_TYPE_FLOAT32: + return dimensions * sizeof(f32); + case SQLITE_VEC_ELEMENT_TYPE_INT8: + return dimensions * sizeof(i8); + case SQLITE_VEC_ELEMENT_TYPE_BIT: + return dimensions / CHAR_BIT; + } + return 0; +} + +size_t vector_column_byte_size(struct VectorColumnDefinition column) { + return vector_byte_size(column.element_type, column.dimensions); +} + +/** + * @brief Parse an vec0 vtab argv[i] column definition and see if + * it's a vector column defintion, ex `contents_embedding float[768]`. + * + * @param source vec0 argv[i] item + * @param source_length length of source in bytes + * @param outColumn Output the parse vector column to this struct, if success + * @return int SQLITE_OK on success, SQLITE_EMPTY is it's not a vector column + * definition, SQLITE_ERROR on error. + */ +int vec0_parse_vector_column(const char *source, int source_length, + struct VectorColumnDefinition *outColumn) { + // parses a vector column definition like so: + // "abc float[123]", "abc_123 bit[1234]", eetc. + // https://github.com/asg017/sqlite-vec/issues/46 + int rc; + struct Vec0Scanner scanner; + struct Vec0Token token; + + char *name; + int nameLength; + enum VectorElementType elementType; + enum Vec0DistanceMetrics distanceMetric = VEC0_DISTANCE_METRIC_L2; + int dimensions; + + vec0_scanner_init(&scanner, source, source_length); + + // starts with an identifier + rc = vec0_scanner_next(&scanner, &token); + + if (rc != VEC0_TOKEN_RESULT_SOME && + token.token_type != TOKEN_TYPE_IDENTIFIER) { + return SQLITE_EMPTY; + } + + name = token.start; + nameLength = token.end - token.start; + + // vector column type comes next: float, int, or bit + rc = vec0_scanner_next(&scanner, &token); + + if (rc != VEC0_TOKEN_RESULT_SOME || + token.token_type != TOKEN_TYPE_IDENTIFIER) { + return SQLITE_EMPTY; + } + if (sqlite3_strnicmp(token.start, "float", 5) == 0 || + sqlite3_strnicmp(token.start, "f32", 3) == 0) { + elementType = SQLITE_VEC_ELEMENT_TYPE_FLOAT32; + } else if (sqlite3_strnicmp(token.start, "int8", 4) == 0 || + sqlite3_strnicmp(token.start, "i8", 2) == 0) { + elementType = SQLITE_VEC_ELEMENT_TYPE_INT8; + } else if (sqlite3_strnicmp(token.start, "bit", 3) == 0) { + elementType = SQLITE_VEC_ELEMENT_TYPE_BIT; + } else { + return SQLITE_EMPTY; + } + + // left '[' bracket + rc = vec0_scanner_next(&scanner, &token); + if (rc != VEC0_TOKEN_RESULT_SOME && token.token_type != TOKEN_TYPE_LBRACKET) { + return SQLITE_EMPTY; + } + + // digit, for vector dimension length + rc = vec0_scanner_next(&scanner, &token); + if (rc != VEC0_TOKEN_RESULT_SOME && token.token_type != TOKEN_TYPE_DIGIT) { + return SQLITE_ERROR; + } + dimensions = atoi(token.start); + if (dimensions <= 0) { + return SQLITE_ERROR; + } + + // // right ']' bracket + rc = vec0_scanner_next(&scanner, &token); + if (rc != VEC0_TOKEN_RESULT_SOME && token.token_type != TOKEN_TYPE_RBRACKET) { + return SQLITE_ERROR; + } + + // any other tokens left should be column-level options , ex `key=value` + // ex `distance_metric=L2 distance_metric=cosine` should error + while (1) { + // should be EOF or identifier (option key) + rc = vec0_scanner_next(&scanner, &token); + if (rc == VEC0_TOKEN_RESULT_EOF) { + break; + } + + if (rc != VEC0_TOKEN_RESULT_SOME && + token.token_type != TOKEN_TYPE_IDENTIFIER) { + return SQLITE_ERROR; + } + + char *key = token.start; + int keyLength = token.end - token.start; + + if (sqlite3_strnicmp(key, "distance_metric", keyLength) == 0) { + + if (elementType == SQLITE_VEC_ELEMENT_TYPE_BIT) { + return SQLITE_ERROR; + } + // ensure equal sign after distance_metric + rc = vec0_scanner_next(&scanner, &token); + if (rc != VEC0_TOKEN_RESULT_SOME && token.token_type != TOKEN_TYPE_EQ) { + return SQLITE_ERROR; + } + + // distance_metric value, an identifier (L2, cosine, etc) + rc = vec0_scanner_next(&scanner, &token); + if (rc != VEC0_TOKEN_RESULT_SOME && + token.token_type != TOKEN_TYPE_IDENTIFIER) { + return SQLITE_ERROR; + } + + char *value = token.start; + int valueLength = token.end - token.start; + if (sqlite3_strnicmp(value, "l2", valueLength) == 0) { + distanceMetric = VEC0_DISTANCE_METRIC_L2; + } else if (sqlite3_strnicmp(value, "l1", valueLength) == 0) { + distanceMetric = VEC0_DISTANCE_METRIC_L1; + } else if (sqlite3_strnicmp(value, "cosine", valueLength) == 0) { + distanceMetric = VEC0_DISTANCE_METRIC_COSINE; + } else { + return SQLITE_ERROR; + } + } + // unknown key + else { + return SQLITE_ERROR; + } + } + + outColumn->name = sqlite3_mprintf("%.*s", nameLength, name); + if (!outColumn->name) { + return SQLITE_ERROR; + } + outColumn->name_length = nameLength; + outColumn->distance_metric = distanceMetric; + outColumn->element_type = elementType; + outColumn->dimensions = dimensions; + return SQLITE_OK; +} + +#pragma region vec_each table function + +typedef struct vec_each_vtab vec_each_vtab; +struct vec_each_vtab { + sqlite3_vtab base; +}; + +typedef struct vec_each_cursor vec_each_cursor; +struct vec_each_cursor { + sqlite3_vtab_cursor base; + i64 iRowid; + enum VectorElementType vector_type; + void *vector; + size_t dimensions; + vector_cleanup cleanup; +}; + +static int vec_eachConnect(sqlite3 *db, void *pAux, int argc, + const char *const *argv, sqlite3_vtab **ppVtab, + char **pzErr) { + UNUSED_PARAMETER(pAux); + UNUSED_PARAMETER(argc); + UNUSED_PARAMETER(argv); + UNUSED_PARAMETER(pzErr); + vec_each_vtab *pNew; + int rc; + + rc = sqlite3_declare_vtab(db, "CREATE TABLE x(value, vector hidden)"); +#define VEC_EACH_COLUMN_VALUE 0 +#define VEC_EACH_COLUMN_VECTOR 1 + if (rc == SQLITE_OK) { + pNew = sqlite3_malloc(sizeof(*pNew)); + *ppVtab = (sqlite3_vtab *)pNew; + if (pNew == 0) + return SQLITE_NOMEM; + memset(pNew, 0, sizeof(*pNew)); + } + return rc; +} + +static int vec_eachDisconnect(sqlite3_vtab *pVtab) { + vec_each_vtab *p = (vec_each_vtab *)pVtab; + sqlite3_free(p); + return SQLITE_OK; +} + +static int vec_eachOpen(sqlite3_vtab *p, sqlite3_vtab_cursor **ppCursor) { + UNUSED_PARAMETER(p); + vec_each_cursor *pCur; + pCur = sqlite3_malloc(sizeof(*pCur)); + if (pCur == 0) + return SQLITE_NOMEM; + memset(pCur, 0, sizeof(*pCur)); + *ppCursor = &pCur->base; + return SQLITE_OK; +} + +static int vec_eachClose(sqlite3_vtab_cursor *cur) { + vec_each_cursor *pCur = (vec_each_cursor *)cur; + pCur->cleanup(pCur->vector); + sqlite3_free(pCur); + return SQLITE_OK; +} + +static int vec_eachBestIndex(sqlite3_vtab *pVTab, + sqlite3_index_info *pIdxInfo) { + UNUSED_PARAMETER(pVTab); + int hasVector = 0; + for (int i = 0; i < pIdxInfo->nConstraint; i++) { + const struct sqlite3_index_constraint *pCons = &pIdxInfo->aConstraint[i]; + // printf("i=%d iColumn=%d, op=%d, usable=%d\n", i, pCons->iColumn, + // pCons->op, pCons->usable); + switch (pCons->iColumn) { + case VEC_EACH_COLUMN_VECTOR: { + if (pCons->op == SQLITE_INDEX_CONSTRAINT_EQ && pCons->usable) { + hasVector = 1; + pIdxInfo->aConstraintUsage[i].argvIndex = 1; + pIdxInfo->aConstraintUsage[i].omit = 1; + } + break; + } + } + } + if (!hasVector) { + return SQLITE_CONSTRAINT; + } + + pIdxInfo->estimatedCost = (double)100000; + pIdxInfo->estimatedRows = 100000; + + return SQLITE_OK; +} + +static int vec_eachFilter(sqlite3_vtab_cursor *pVtabCursor, int idxNum, + const char *idxStr, int argc, sqlite3_value **argv) { + UNUSED_PARAMETER(idxNum); + UNUSED_PARAMETER(idxStr); + assert(argc == 1); + vec_each_cursor *pCur = (vec_each_cursor *)pVtabCursor; + + if (pCur->vector) { + pCur->cleanup(pCur->vector); + pCur->vector = NULL; + } + + char *pzErrMsg; + int rc = vector_from_value(argv[0], &pCur->vector, &pCur->dimensions, + &pCur->vector_type, &pCur->cleanup, &pzErrMsg); + if (rc != SQLITE_OK) { + return SQLITE_ERROR; + } + pCur->iRowid = 0; + return SQLITE_OK; +} + +static int vec_eachRowid(sqlite3_vtab_cursor *cur, sqlite_int64 *pRowid) { + vec_each_cursor *pCur = (vec_each_cursor *)cur; + *pRowid = pCur->iRowid; + return SQLITE_OK; +} + +static int vec_eachEof(sqlite3_vtab_cursor *cur) { + vec_each_cursor *pCur = (vec_each_cursor *)cur; + return pCur->iRowid >= (i64)pCur->dimensions; +} + +static int vec_eachNext(sqlite3_vtab_cursor *cur) { + vec_each_cursor *pCur = (vec_each_cursor *)cur; + pCur->iRowid++; + return SQLITE_OK; +} + +static int vec_eachColumn(sqlite3_vtab_cursor *cur, sqlite3_context *context, + int i) { + vec_each_cursor *pCur = (vec_each_cursor *)cur; + switch (i) { + case VEC_EACH_COLUMN_VALUE: + switch (pCur->vector_type) { + case SQLITE_VEC_ELEMENT_TYPE_FLOAT32: { + sqlite3_result_double(context, ((f32 *)pCur->vector)[pCur->iRowid]); + break; + } + case SQLITE_VEC_ELEMENT_TYPE_BIT: { + u8 x = ((u8 *)pCur->vector)[pCur->iRowid / CHAR_BIT]; + sqlite3_result_int(context, + (x & (0b10000000 >> ((pCur->iRowid % CHAR_BIT)))) > 0); + break; + } + case SQLITE_VEC_ELEMENT_TYPE_INT8: { + sqlite3_result_int(context, ((i8 *)pCur->vector)[pCur->iRowid]); + break; + } + } + + break; + } + return SQLITE_OK; +} + +static sqlite3_module vec_eachModule = { + /* iVersion */ 0, + /* xCreate */ 0, + /* xConnect */ vec_eachConnect, + /* xBestIndex */ vec_eachBestIndex, + /* xDisconnect */ vec_eachDisconnect, + /* xDestroy */ 0, + /* xOpen */ vec_eachOpen, + /* xClose */ vec_eachClose, + /* xFilter */ vec_eachFilter, + /* xNext */ vec_eachNext, + /* xEof */ vec_eachEof, + /* xColumn */ vec_eachColumn, + /* xRowid */ vec_eachRowid, + /* xUpdate */ 0, + /* xBegin */ 0, + /* xSync */ 0, + /* xCommit */ 0, + /* xRollback */ 0, + /* xFindMethod */ 0, + /* xRename */ 0, + /* xSavepoint */ 0, + /* xRelease */ 0, + /* xRollbackTo */ 0, + /* xShadowName */ 0, +#if SQLITE_VERSION_NUMBER >= 3044000 + /* xIntegrity */ 0 +#endif +}; + +#pragma endregion + +#pragma region vec_npy_each table function + +enum NpyTokenType { + NPY_TOKEN_TYPE_IDENTIFIER, + NPY_TOKEN_TYPE_NUMBER, + NPY_TOKEN_TYPE_LPAREN, + NPY_TOKEN_TYPE_RPAREN, + NPY_TOKEN_TYPE_LBRACE, + NPY_TOKEN_TYPE_RBRACE, + NPY_TOKEN_TYPE_COLON, + NPY_TOKEN_TYPE_COMMA, + NPY_TOKEN_TYPE_STRING, + NPY_TOKEN_TYPE_FALSE, +}; + +struct NpyToken { + enum NpyTokenType token_type; + unsigned char *start; + unsigned char *end; +}; + +int npy_token_next(unsigned char *start, unsigned char *end, + struct NpyToken *out) { + unsigned char *ptr = start; + while (ptr < end) { + unsigned char curr = *ptr; + if (is_whitespace(curr)) { + ptr++; + continue; + } else if (curr == '(') { + out->start = ptr++; + out->end = ptr; + out->token_type = NPY_TOKEN_TYPE_LPAREN; + return VEC0_TOKEN_RESULT_SOME; + } else if (curr == ')') { + out->start = ptr++; + out->end = ptr; + out->token_type = NPY_TOKEN_TYPE_RPAREN; + return VEC0_TOKEN_RESULT_SOME; + } else if (curr == '{') { + out->start = ptr++; + out->end = ptr; + out->token_type = NPY_TOKEN_TYPE_LBRACE; + return VEC0_TOKEN_RESULT_SOME; + } else if (curr == '}') { + out->start = ptr++; + out->end = ptr; + out->token_type = NPY_TOKEN_TYPE_RBRACE; + return VEC0_TOKEN_RESULT_SOME; + } else if (curr == ':') { + out->start = ptr++; + out->end = ptr; + out->token_type = NPY_TOKEN_TYPE_COLON; + return VEC0_TOKEN_RESULT_SOME; + } else if (curr == ',') { + out->start = ptr++; + out->end = ptr; + out->token_type = NPY_TOKEN_TYPE_COMMA; + return VEC0_TOKEN_RESULT_SOME; + } else if (curr == '\'') { + unsigned char *start = ptr; + ptr++; + while (ptr < end) { + if ((*ptr) == '\'') { + break; + } + ptr++; + } + if ((*ptr) != '\'') { + return VEC0_TOKEN_RESULT_ERROR; + } + out->start = start; + out->end = ++ptr; + out->token_type = NPY_TOKEN_TYPE_STRING; + return VEC0_TOKEN_RESULT_SOME; + } else if (curr == 'F' && + strncmp((char *)ptr, "False", strlen("False")) == 0) { + out->start = ptr; + out->end = (ptr + (int)strlen("False")); + ptr = out->end; + out->token_type = NPY_TOKEN_TYPE_FALSE; + return VEC0_TOKEN_RESULT_SOME; + } else if (is_digit(curr)) { + unsigned char *start = ptr; + while (ptr < end && (is_digit(*ptr))) { + ptr++; + } + out->start = start; + out->end = ptr; + out->token_type = NPY_TOKEN_TYPE_NUMBER; + return VEC0_TOKEN_RESULT_SOME; + } else { + return VEC0_TOKEN_RESULT_ERROR; + } + } + return VEC0_TOKEN_RESULT_ERROR; +} + +struct NpyScanner { + unsigned char *start; + unsigned char *end; + unsigned char *ptr; +}; + +void npy_scanner_init(struct NpyScanner *scanner, const unsigned char *source, + int source_length) { + scanner->start = (unsigned char *)source; + scanner->end = (unsigned char *)source + source_length; + scanner->ptr = (unsigned char *)source; +} + +int npy_scanner_next(struct NpyScanner *scanner, struct NpyToken *out) { + int rc = npy_token_next(scanner->start, scanner->end, out); + if (rc == VEC0_TOKEN_RESULT_SOME) { + scanner->start = out->end; + } + return rc; +} + +#define NPY_PARSE_ERROR "Error parsing numpy array: " +int parse_npy_header(sqlite3_vtab *pVTab, const unsigned char *header, + size_t headerLength, + enum VectorElementType *out_element_type, + int *fortran_order, size_t *numElements, + size_t *numDimensions) { + + struct NpyScanner scanner; + struct NpyToken token; + int rc; + npy_scanner_init(&scanner, header, headerLength); + + if (npy_scanner_next(&scanner, &token) != VEC0_TOKEN_RESULT_SOME && + token.token_type != NPY_TOKEN_TYPE_LBRACE) { + vtab_set_error(pVTab, + NPY_PARSE_ERROR "numpy header did not start with '{'"); + return SQLITE_ERROR; + } + while (1) { + rc = npy_scanner_next(&scanner, &token); + if (rc != VEC0_TOKEN_RESULT_SOME) { + vtab_set_error(pVTab, NPY_PARSE_ERROR "expected key in numpy header"); + return SQLITE_ERROR; + } + + if (token.token_type == NPY_TOKEN_TYPE_RBRACE) { + break; + } + if (token.token_type != NPY_TOKEN_TYPE_STRING) { + vtab_set_error(pVTab, NPY_PARSE_ERROR + "expected a string as key in numpy header"); + return SQLITE_ERROR; + } + unsigned char *key = token.start; + + rc = npy_scanner_next(&scanner, &token); + if ((rc != VEC0_TOKEN_RESULT_SOME) || + (token.token_type != NPY_TOKEN_TYPE_COLON)) { + vtab_set_error(pVTab, NPY_PARSE_ERROR + "expected a ':' after key in numpy header"); + return SQLITE_ERROR; + } + + if (strncmp((char *)key, "'descr'", strlen("'descr'")) == 0) { + rc = npy_scanner_next(&scanner, &token); + if ((rc != VEC0_TOKEN_RESULT_SOME) || + (token.token_type != NPY_TOKEN_TYPE_STRING)) { + vtab_set_error(pVTab, NPY_PARSE_ERROR + "expected a string value after 'descr' key"); + return SQLITE_ERROR; + } + if (strncmp((char *)token.start, "'<f4'", strlen("'<f4'")) != 0) { + vtab_set_error( + pVTab, NPY_PARSE_ERROR + "Only '<f4' values are supported in sqlite-vec numpy functions"); + return SQLITE_ERROR; + } + *out_element_type = SQLITE_VEC_ELEMENT_TYPE_FLOAT32; + } else if (strncmp((char *)key, "'fortran_order'", + strlen("'fortran_order'")) == 0) { + rc = npy_scanner_next(&scanner, &token); + if (rc != VEC0_TOKEN_RESULT_SOME || + token.token_type != NPY_TOKEN_TYPE_FALSE) { + vtab_set_error(pVTab, NPY_PARSE_ERROR + "Only fortran_order = False is supported in sqlite-vec " + "numpy functions"); + return SQLITE_ERROR; + } + *fortran_order = 0; + } else if (strncmp((char *)key, "'shape'", strlen("'shape'")) == 0) { + // "(xxx, xxx)" OR (xxx,) + size_t first; + rc = npy_scanner_next(&scanner, &token); + if ((rc != VEC0_TOKEN_RESULT_SOME) || + (token.token_type != NPY_TOKEN_TYPE_LPAREN)) { + vtab_set_error(pVTab, NPY_PARSE_ERROR + "Expected left parenthesis '(' after shape key"); + return SQLITE_ERROR; + } + + rc = npy_scanner_next(&scanner, &token); + if ((rc != VEC0_TOKEN_RESULT_SOME) || + (token.token_type != NPY_TOKEN_TYPE_NUMBER)) { + vtab_set_error(pVTab, NPY_PARSE_ERROR + "Expected an initial number in shape value"); + return SQLITE_ERROR; + } + first = strtol((char *)token.start, NULL, 10); + + rc = npy_scanner_next(&scanner, &token); + if ((rc != VEC0_TOKEN_RESULT_SOME) || + (token.token_type != NPY_TOKEN_TYPE_COMMA)) { + vtab_set_error(pVTab, NPY_PARSE_ERROR + "Expected comma after first shape value"); + return SQLITE_ERROR; + } + + rc = npy_scanner_next(&scanner, &token); + if (rc != VEC0_TOKEN_RESULT_SOME) { + vtab_set_error(pVTab, NPY_PARSE_ERROR + "unexpected header EOF while parsing shape"); + return SQLITE_ERROR; + } + if (token.token_type == NPY_TOKEN_TYPE_NUMBER) { + *numElements = first; + *numDimensions = strtol((char *)token.start, NULL, 10); + rc = npy_scanner_next(&scanner, &token); + if ((rc != VEC0_TOKEN_RESULT_SOME) || + (token.token_type != NPY_TOKEN_TYPE_RPAREN)) { + vtab_set_error(pVTab, NPY_PARSE_ERROR + "expected right parenthesis after shape value"); + return SQLITE_ERROR; + } + } else if (token.token_type == NPY_TOKEN_TYPE_RPAREN) { + // '(0,)' means an empty array! + *numElements = first ? 1 : 0; + *numDimensions = first; + } else { + vtab_set_error(pVTab, NPY_PARSE_ERROR "unknown type in shape value"); + return SQLITE_ERROR; + } + } else { + vtab_set_error(pVTab, NPY_PARSE_ERROR "unknown key in numpy header"); + return SQLITE_ERROR; + } + + rc = npy_scanner_next(&scanner, &token); + if ((rc != VEC0_TOKEN_RESULT_SOME) || + (token.token_type != NPY_TOKEN_TYPE_COMMA)) { + vtab_set_error(pVTab, NPY_PARSE_ERROR "unknown extra token after value"); + return SQLITE_ERROR; + } + } + + return SQLITE_OK; +} + +typedef struct vec_npy_each_vtab vec_npy_each_vtab; +struct vec_npy_each_vtab { + sqlite3_vtab base; +}; + +typedef enum { + VEC_NPY_EACH_INPUT_BUFFER, + VEC_NPY_EACH_INPUT_FILE, +} vec_npy_each_input_type; + +typedef struct vec_npy_each_cursor vec_npy_each_cursor; +struct vec_npy_each_cursor { + sqlite3_vtab_cursor base; + i64 iRowid; + // sqlite-vec compatible type of vector + enum VectorElementType elementType; + // number of vectors in the npy array + size_t nElements; + // number of dimensions each vector has + size_t nDimensions; + + vec_npy_each_input_type input_type; + + // when input_type == VEC_NPY_EACH_INPUT_BUFFER + + // Buffer containing the vector data, when reading from an in-memory buffer. + // Size: nElements * nDimensions * element_size + // Clean up with sqlite3_free() once complete + void *vector; + + // when input_type == VEC_NPY_EACH_INPUT_FILE + + // Opened npy file, when reading from a file. + // fclose() when complete. +#ifndef SQLITE_VEC_OMIT_FS + FILE *file; +#endif + + // an in-memory buffer containing a portion of the npy array. + // Used for faster reading, instead of calling fread a lot. + // Will have a byte-size of fileBufferSize + void *chunksBuffer; + // size of allocated fileBuffer in bytes + size_t chunksBufferSize; + //// Maximum length of the buffer, in terms of number of vectors. + size_t maxChunks; + + // Counter index of the current vector into of fileBuffer to yield. + // Starts at 0 once fileBuffer is read, and iterates to bufferLength. + // Resets to 0 once that "buffer" is yielded and a new one is read. + size_t currentChunkIndex; + size_t currentChunkSize; + + // 0 when there are still more elements to read/yield, 1 when complete. + int eof; +}; + +static unsigned char NPY_MAGIC[6] = "\x93NUMPY"; + +#ifndef SQLITE_VEC_OMIT_FS +int parse_npy_file(sqlite3_vtab *pVTab, FILE *file, vec_npy_each_cursor *pCur) { + int n; + fseek(file, 0, SEEK_END); + long fileSize = ftell(file); + + fseek(file, 0L, SEEK_SET); + + unsigned char header[10]; + n = fread(&header, sizeof(unsigned char), 10, file); + if (n != 10) { + vtab_set_error(pVTab, "numpy array file too short"); + return SQLITE_ERROR; + } + + if (memcmp(NPY_MAGIC, header, sizeof(NPY_MAGIC)) != 0) { + vtab_set_error(pVTab, + "numpy array file does not contain the 'magic' header"); + return SQLITE_ERROR; + } + + u8 major = header[6]; + u8 minor = header[7]; + uint16_t headerLength = 0; + memcpy(&headerLength, &header[8], sizeof(uint16_t)); + + size_t totalHeaderLength = sizeof(NPY_MAGIC) + sizeof(major) + sizeof(minor) + + sizeof(headerLength) + headerLength; + i32 dataSize = fileSize - totalHeaderLength; + if (dataSize < 0) { + vtab_set_error(pVTab, "numpy array file header length is invalid"); + return SQLITE_ERROR; + } + + unsigned char *headerX = sqlite3_malloc(headerLength); + if (headerLength && !headerX) { + return SQLITE_NOMEM; + } + + n = fread(headerX, sizeof(char), headerLength, file); + if (n != headerLength) { + sqlite3_free(headerX); + vtab_set_error(pVTab, "numpy array file header length is invalid"); + return SQLITE_ERROR; + } + + int fortran_order; + enum VectorElementType element_type; + size_t numElements; + size_t numDimensions; + int rc = parse_npy_header(pVTab, headerX, headerLength, &element_type, + &fortran_order, &numElements, &numDimensions); + sqlite3_free(headerX); + if (rc != SQLITE_OK) { + // parse_npy_header already attackes an error emssage + return rc; + } + + i32 expectedDataSize = + numElements * vector_byte_size(element_type, numDimensions); + if (expectedDataSize != dataSize) { + vtab_set_error( + pVTab, "numpy array file error: Expected a data size of %d, found %d", + expectedDataSize, dataSize); + return SQLITE_ERROR; + } + + pCur->maxChunks = 1024; + pCur->chunksBufferSize = + (vector_byte_size(element_type, numDimensions)) * pCur->maxChunks; + pCur->chunksBuffer = sqlite3_malloc(pCur->chunksBufferSize); + if (pCur->chunksBufferSize && !pCur->chunksBuffer) { + return SQLITE_NOMEM; + } + + pCur->currentChunkSize = + fread(pCur->chunksBuffer, vector_byte_size(element_type, numDimensions), + pCur->maxChunks, file); + + pCur->currentChunkIndex = 0; + pCur->elementType = element_type; + pCur->nElements = numElements; + pCur->nDimensions = numDimensions; + pCur->input_type = VEC_NPY_EACH_INPUT_FILE; + + pCur->eof = pCur->currentChunkSize == 0; + pCur->file = file; + return SQLITE_OK; +} +#endif + +int parse_npy_buffer(sqlite3_vtab *pVTab, const unsigned char *buffer, + int bufferLength, void **data, size_t *numElements, + size_t *numDimensions, + enum VectorElementType *element_type) { + + if (bufferLength < 10) { + // IMP: V03312_20150 + vtab_set_error(pVTab, "numpy array too short"); + return SQLITE_ERROR; + } + if (memcmp(NPY_MAGIC, buffer, sizeof(NPY_MAGIC)) != 0) { + // V11954_28792 + vtab_set_error(pVTab, "numpy array does not contain the 'magic' header"); + return SQLITE_ERROR; + } + + u8 major = buffer[6]; + u8 minor = buffer[7]; + uint16_t headerLength = 0; + memcpy(&headerLength, &buffer[8], sizeof(uint16_t)); + + i32 totalHeaderLength = sizeof(NPY_MAGIC) + sizeof(major) + sizeof(minor) + + sizeof(headerLength) + headerLength; + i32 dataSize = bufferLength - totalHeaderLength; + + if (dataSize < 0) { + vtab_set_error(pVTab, "numpy array header length is invalid"); + return SQLITE_ERROR; + } + + const unsigned char *header = &buffer[10]; + int fortran_order; + + int rc = parse_npy_header(pVTab, header, headerLength, element_type, + &fortran_order, numElements, numDimensions); + if (rc != SQLITE_OK) { + return rc; + } + + i32 expectedDataSize = + (*numElements * vector_byte_size(*element_type, *numDimensions)); + if (expectedDataSize != dataSize) { + vtab_set_error(pVTab, + "numpy array error: Expected a data size of %d, found %d", + expectedDataSize, dataSize); + return SQLITE_ERROR; + } + + *data = (void *)&buffer[totalHeaderLength]; + return SQLITE_OK; +} + +static int vec_npy_eachConnect(sqlite3 *db, void *pAux, int argc, + const char *const *argv, sqlite3_vtab **ppVtab, + char **pzErr) { + UNUSED_PARAMETER(pAux); + UNUSED_PARAMETER(argc); + UNUSED_PARAMETER(argv); + UNUSED_PARAMETER(pzErr); + vec_npy_each_vtab *pNew; + int rc; + + rc = sqlite3_declare_vtab(db, "CREATE TABLE x(vector, input hidden)"); +#define VEC_NPY_EACH_COLUMN_VECTOR 0 +#define VEC_NPY_EACH_COLUMN_INPUT 1 + if (rc == SQLITE_OK) { + pNew = sqlite3_malloc(sizeof(*pNew)); + *ppVtab = (sqlite3_vtab *)pNew; + if (pNew == 0) + return SQLITE_NOMEM; + memset(pNew, 0, sizeof(*pNew)); + } + return rc; +} + +static int vec_npy_eachDisconnect(sqlite3_vtab *pVtab) { + vec_npy_each_vtab *p = (vec_npy_each_vtab *)pVtab; + sqlite3_free(p); + return SQLITE_OK; +} + +static int vec_npy_eachOpen(sqlite3_vtab *p, sqlite3_vtab_cursor **ppCursor) { + UNUSED_PARAMETER(p); + vec_npy_each_cursor *pCur; + pCur = sqlite3_malloc(sizeof(*pCur)); + if (pCur == 0) + return SQLITE_NOMEM; + memset(pCur, 0, sizeof(*pCur)); + *ppCursor = &pCur->base; + return SQLITE_OK; +} + +static int vec_npy_eachClose(sqlite3_vtab_cursor *cur) { + vec_npy_each_cursor *pCur = (vec_npy_each_cursor *)cur; +#ifndef SQLITE_VEC_OMIT_FS + if (pCur->file) { + fclose(pCur->file); + pCur->file = NULL; + } +#endif + if (pCur->chunksBuffer) { + sqlite3_free(pCur->chunksBuffer); + pCur->chunksBuffer = NULL; + } + if (pCur->vector) { + pCur->vector = NULL; + } + sqlite3_free(pCur); + return SQLITE_OK; +} + +static int vec_npy_eachBestIndex(sqlite3_vtab *pVTab, + sqlite3_index_info *pIdxInfo) { + int hasInput; + for (int i = 0; i < pIdxInfo->nConstraint; i++) { + const struct sqlite3_index_constraint *pCons = &pIdxInfo->aConstraint[i]; + // printf("i=%d iColumn=%d, op=%d, usable=%d\n", i, pCons->iColumn, + // pCons->op, pCons->usable); + switch (pCons->iColumn) { + case VEC_NPY_EACH_COLUMN_INPUT: { + if (pCons->op == SQLITE_INDEX_CONSTRAINT_EQ && pCons->usable) { + hasInput = 1; + pIdxInfo->aConstraintUsage[i].argvIndex = 1; + pIdxInfo->aConstraintUsage[i].omit = 1; + } + break; + } + } + } + if (!hasInput) { + pVTab->zErrMsg = sqlite3_mprintf("input argument is required"); + return SQLITE_ERROR; + } + + pIdxInfo->estimatedCost = (double)100000; + pIdxInfo->estimatedRows = 100000; + + return SQLITE_OK; +} + +static int vec_npy_eachFilter(sqlite3_vtab_cursor *pVtabCursor, int idxNum, + const char *idxStr, int argc, + sqlite3_value **argv) { + UNUSED_PARAMETER(idxNum); + UNUSED_PARAMETER(idxStr); + assert(argc == 1); + int rc; + + vec_npy_each_cursor *pCur = (vec_npy_each_cursor *)pVtabCursor; + +#ifndef SQLITE_VEC_OMIT_FS + if (pCur->file) { + fclose(pCur->file); + pCur->file = NULL; + } +#endif + if (pCur->chunksBuffer) { + sqlite3_free(pCur->chunksBuffer); + pCur->chunksBuffer = NULL; + } + if (pCur->vector) { + pCur->vector = NULL; + } + +#ifndef SQLITE_VEC_OMIT_FS + struct VecNpyFile *f = NULL; + if ((f = sqlite3_value_pointer(argv[0], SQLITE_VEC_NPY_FILE_NAME))) { + FILE *file = fopen(f->path, "r"); + if (!file) { + vtab_set_error(pVtabCursor->pVtab, "Could not open numpy file"); + return SQLITE_ERROR; + } + + rc = parse_npy_file(pVtabCursor->pVtab, file, pCur); + if (rc != SQLITE_OK) { +#ifndef SQLITE_VEC_OMIT_FS + fclose(file); +#endif + return rc; + } + + } else +#endif + { + + const unsigned char *input = sqlite3_value_blob(argv[0]); + int inputLength = sqlite3_value_bytes(argv[0]); + void *data; + size_t numElements; + size_t numDimensions; + enum VectorElementType element_type; + + rc = parse_npy_buffer(pVtabCursor->pVtab, input, inputLength, &data, + &numElements, &numDimensions, &element_type); + if (rc != SQLITE_OK) { + return rc; + } + + pCur->vector = data; + pCur->elementType = element_type; + pCur->nElements = numElements; + pCur->nDimensions = numDimensions; + pCur->input_type = VEC_NPY_EACH_INPUT_BUFFER; + } + + pCur->iRowid = 0; + return SQLITE_OK; +} + +static int vec_npy_eachRowid(sqlite3_vtab_cursor *cur, sqlite_int64 *pRowid) { + vec_npy_each_cursor *pCur = (vec_npy_each_cursor *)cur; + *pRowid = pCur->iRowid; + return SQLITE_OK; +} + +static int vec_npy_eachEof(sqlite3_vtab_cursor *cur) { + vec_npy_each_cursor *pCur = (vec_npy_each_cursor *)cur; + if (pCur->input_type == VEC_NPY_EACH_INPUT_BUFFER) { + return (!pCur->nElements) || (size_t)pCur->iRowid >= pCur->nElements; + } + return pCur->eof; +} + +static int vec_npy_eachNext(sqlite3_vtab_cursor *cur) { + vec_npy_each_cursor *pCur = (vec_npy_each_cursor *)cur; + pCur->iRowid++; + if (pCur->input_type == VEC_NPY_EACH_INPUT_BUFFER) { + return SQLITE_OK; + } + +#ifndef SQLITE_VEC_OMIT_FS + // else: input is a file + pCur->currentChunkIndex++; + if (pCur->currentChunkIndex >= pCur->currentChunkSize) { + pCur->currentChunkSize = + fread(pCur->chunksBuffer, + vector_byte_size(pCur->elementType, pCur->nDimensions), + pCur->maxChunks, pCur->file); + if (!pCur->currentChunkSize) { + pCur->eof = 1; + } + pCur->currentChunkIndex = 0; + } +#endif + return SQLITE_OK; +} + +static int vec_npy_eachColumnBuffer(vec_npy_each_cursor *pCur, + sqlite3_context *context, int i) { + switch (i) { + case VEC_NPY_EACH_COLUMN_VECTOR: { + sqlite3_result_subtype(context, pCur->elementType); + switch (pCur->elementType) { + case SQLITE_VEC_ELEMENT_TYPE_FLOAT32: { + sqlite3_result_blob( + context, + &((unsigned char *) + pCur->vector)[pCur->iRowid * pCur->nDimensions * sizeof(f32)], + pCur->nDimensions * sizeof(f32), SQLITE_TRANSIENT); + + break; + } + case SQLITE_VEC_ELEMENT_TYPE_INT8: + case SQLITE_VEC_ELEMENT_TYPE_BIT: { + // https://github.com/asg017/sqlite-vec/issues/42 + sqlite3_result_error(context, + "vec_npy_each only supports float32 vectors", -1); + break; + } + } + + break; + } + } + return SQLITE_OK; +} +static int vec_npy_eachColumnFile(vec_npy_each_cursor *pCur, + sqlite3_context *context, int i) { + switch (i) { + case VEC_NPY_EACH_COLUMN_VECTOR: { + switch (pCur->elementType) { + case SQLITE_VEC_ELEMENT_TYPE_FLOAT32: { + sqlite3_result_blob( + context, + &((unsigned char *) + pCur->chunksBuffer)[pCur->currentChunkIndex * + pCur->nDimensions * sizeof(f32)], + pCur->nDimensions * sizeof(f32), SQLITE_TRANSIENT); + break; + } + case SQLITE_VEC_ELEMENT_TYPE_INT8: + case SQLITE_VEC_ELEMENT_TYPE_BIT: { + // https://github.com/asg017/sqlite-vec/issues/42 + sqlite3_result_error(context, + "vec_npy_each only supports float32 vectors", -1); + break; + } + } + break; + } + } + return SQLITE_OK; +} +static int vec_npy_eachColumn(sqlite3_vtab_cursor *cur, + sqlite3_context *context, int i) { + vec_npy_each_cursor *pCur = (vec_npy_each_cursor *)cur; + switch (pCur->input_type) { + case VEC_NPY_EACH_INPUT_BUFFER: + return vec_npy_eachColumnBuffer(pCur, context, i); + case VEC_NPY_EACH_INPUT_FILE: + return vec_npy_eachColumnFile(pCur, context, i); + } + return SQLITE_ERROR; +} + +static sqlite3_module vec_npy_eachModule = { + /* iVersion */ 0, + /* xCreate */ 0, + /* xConnect */ vec_npy_eachConnect, + /* xBestIndex */ vec_npy_eachBestIndex, + /* xDisconnect */ vec_npy_eachDisconnect, + /* xDestroy */ 0, + /* xOpen */ vec_npy_eachOpen, + /* xClose */ vec_npy_eachClose, + /* xFilter */ vec_npy_eachFilter, + /* xNext */ vec_npy_eachNext, + /* xEof */ vec_npy_eachEof, + /* xColumn */ vec_npy_eachColumn, + /* xRowid */ vec_npy_eachRowid, + /* xUpdate */ 0, + /* xBegin */ 0, + /* xSync */ 0, + /* xCommit */ 0, + /* xRollback */ 0, + /* xFindMethod */ 0, + /* xRename */ 0, + /* xSavepoint */ 0, + /* xRelease */ 0, + /* xRollbackTo */ 0, + /* xShadowName */ 0, +#if SQLITE_VERSION_NUMBER >= 3044000 + /* xIntegrity */ 0, +#endif +}; + +#pragma endregion + +#pragma region vec0 virtual table + +#define VEC0_COLUMN_ID 0 +#define VEC0_COLUMN_USERN_START 1 +#define VEC0_COLUMN_OFFSET_DISTANCE 1 +#define VEC0_COLUMN_OFFSET_K 2 + +#define VEC0_SHADOW_INFO_NAME "\"%w\".\"%w_info\"" + +#define VEC0_SHADOW_CHUNKS_NAME "\"%w\".\"%w_chunks\"" +/// 1) schema, 2) original vtab table name +#define VEC0_SHADOW_CHUNKS_CREATE \ + "CREATE TABLE " VEC0_SHADOW_CHUNKS_NAME "(" \ + "chunk_id INTEGER PRIMARY KEY AUTOINCREMENT," \ + "size INTEGER NOT NULL," \ + "validity BLOB NOT NULL," \ + "rowids BLOB NOT NULL" \ + ");" + +#define VEC0_SHADOW_ROWIDS_NAME "\"%w\".\"%w_rowids\"" +/// 1) schema, 2) original vtab table name +#define VEC0_SHADOW_ROWIDS_CREATE_BASIC \ + "CREATE TABLE " VEC0_SHADOW_ROWIDS_NAME "(" \ + "rowid INTEGER PRIMARY KEY AUTOINCREMENT," \ + "id," \ + "chunk_id INTEGER," \ + "chunk_offset INTEGER" \ + ");" + +// vec0 tables with a text primary keys are still backed by int64 primary keys, +// since a fixed-length rowid is required for vec0 chunks. But we add a new 'id +// text unique' column to emulate a text primary key interface. +#define VEC0_SHADOW_ROWIDS_CREATE_PK_TEXT \ + "CREATE TABLE " VEC0_SHADOW_ROWIDS_NAME "(" \ + "rowid INTEGER PRIMARY KEY AUTOINCREMENT," \ + "id TEXT UNIQUE NOT NULL," \ + "chunk_id INTEGER," \ + "chunk_offset INTEGER" \ + ");" + +/// 1) schema, 2) original vtab table name +#define VEC0_SHADOW_VECTOR_N_NAME "\"%w\".\"%w_vector_chunks%02d\"" + +/// 1) schema, 2) original vtab table name +#define VEC0_SHADOW_VECTOR_N_CREATE \ + "CREATE TABLE " VEC0_SHADOW_VECTOR_N_NAME "(" \ + "rowid PRIMARY KEY," \ + "vectors BLOB NOT NULL" \ + ");" + +#define VEC0_SHADOW_AUXILIARY_NAME "\"%w\".\"%w_auxiliary\"" + +#define VEC0_SHADOW_METADATA_N_NAME "\"%w\".\"%w_metadatachunks%02d\"" +#define VEC0_SHADOW_METADATA_TEXT_DATA_NAME "\"%w\".\"%w_metadatatext%02d\"" + +#define VEC_INTERAL_ERROR "Internal sqlite-vec error: " +#define REPORT_URL "https://github.com/asg017/sqlite-vec/issues/new" + +typedef struct vec0_vtab vec0_vtab; + +#define VEC0_MAX_VECTOR_COLUMNS 16 +#define VEC0_MAX_PARTITION_COLUMNS 4 +#define VEC0_MAX_AUXILIARY_COLUMNS 16 +#define VEC0_MAX_METADATA_COLUMNS 16 + +#define SQLITE_VEC_VEC0_MAX_DIMENSIONS 8192 +#define VEC0_METADATA_TEXT_VIEW_BUFFER_LENGTH 16 +#define VEC0_METADATA_TEXT_VIEW_DATA_LENGTH 12 + +typedef enum { + // vector column, ie "contents_embedding float[1024]" + SQLITE_VEC0_USER_COLUMN_KIND_VECTOR = 1, + + // partition key column, ie "user_id integer partition key" + SQLITE_VEC0_USER_COLUMN_KIND_PARTITION = 2, + + // + SQLITE_VEC0_USER_COLUMN_KIND_AUXILIARY = 3, + + // metadata column that can be filtered, ie "genre text" + SQLITE_VEC0_USER_COLUMN_KIND_METADATA = 4, +} vec0_user_column_kind; + +struct vec0_vtab { + sqlite3_vtab base; + + // the SQLite connection of the host database + sqlite3 *db; + + // True if the primary key of the vec0 table has a column type TEXT. + // Will change the schema of the _rowids table, and insert/query logic. + int pkIsText; + + // number of defined vector columns. + int numVectorColumns; + + // number of defined PARTITION KEY columns. + int numPartitionColumns; + + // number of defined auxiliary columns + int numAuxiliaryColumns; + + // number of defined metadata columns + int numMetadataColumns; + + + // Name of the schema the table exists on. + // Must be freed with sqlite3_free() + char *schemaName; + + // Name of the table the table exists on. + // Must be freed with sqlite3_free() + char *tableName; + + // Name of the _rowids shadow table. + // Must be freed with sqlite3_free() + char *shadowRowidsName; + + // Name of the _chunks shadow table. + // Must be freed with sqlite3_free() + char *shadowChunksName; + + // contains enum vec0_user_column_kind values for up to + // numVectorColumns + numPartitionColumns entries + vec0_user_column_kind user_column_kinds[VEC0_MAX_VECTOR_COLUMNS + VEC0_MAX_PARTITION_COLUMNS + VEC0_MAX_AUXILIARY_COLUMNS + VEC0_MAX_METADATA_COLUMNS]; + + uint8_t user_column_idxs[VEC0_MAX_VECTOR_COLUMNS + VEC0_MAX_PARTITION_COLUMNS + VEC0_MAX_AUXILIARY_COLUMNS + VEC0_MAX_METADATA_COLUMNS]; + + + // Name of all the vector chunk shadow tables. + // Ex '_vector_chunks00' + // Only the first numVectorColumns entries will be available. + // The first numVectorColumns entries must be freed with sqlite3_free() + char *shadowVectorChunksNames[VEC0_MAX_VECTOR_COLUMNS]; + + // Name of all metadata chunk shadow tables, ie `_metadatachunks00` + // Only the first numMetadataColumns entries will be available. + // The first numMetadataColumns entries must be freed with sqlite3_free() + char *shadowMetadataChunksNames[VEC0_MAX_METADATA_COLUMNS]; + + struct VectorColumnDefinition vector_columns[VEC0_MAX_VECTOR_COLUMNS]; + struct Vec0PartitionColumnDefinition paritition_columns[VEC0_MAX_PARTITION_COLUMNS]; + struct Vec0AuxiliaryColumnDefinition auxiliary_columns[VEC0_MAX_AUXILIARY_COLUMNS]; + struct Vec0MetadataColumnDefinition metadata_columns[VEC0_MAX_METADATA_COLUMNS]; + + int chunk_size; + + // select latest chunk from _chunks, getting chunk_id + sqlite3_stmt *stmtLatestChunk; + + /** + * Statement to insert a row into the _rowids table, with a rowid. + * Parameters: + * 1: int64, rowid to insert + * Result columns: none + * SQL: "INSERT INTO _rowids(rowid) VALUES (?)" + * + * Must be cleaned up with sqlite3_finalize(). + */ + sqlite3_stmt *stmtRowidsInsertRowid; + + /** + * Statement to insert a row into the _rowids table, with an id. + * The id column isn't a tradition primary key, but instead a unique + * column to handle "text primary key" vec0 tables. The true int64 rowid + * can be retrieved after inserting with sqlite3_last_rowid(). + * + * Parameters: + * 1: text or null, id to insert + * Result columns: none + * + * Must be cleaned up with sqlite3_finalize(). + */ + sqlite3_stmt *stmtRowidsInsertId; + + /** + * Statement to update the "position" columns chunk_id and chunk_offset for + * a given _rowids row. Used when the "next available" chunk position is found + * for a vector. + * + * Parameters: + * 1: int64, chunk_id value + * 2: int64, chunk_offset value + * 3: int64, rowid value + * Result columns: none + * + * Must be cleaned up with sqlite3_finalize(). + */ + sqlite3_stmt *stmtRowidsUpdatePosition; + + /** + * Statement to quickly find the chunk_id + chunk_offset of a given row. + * Parameters: + * 1: rowid of the row/vector to lookup + * Result columns: + * 0: chunk_id (i64) + * 1: chunk_offset (i64) + * SQL: "SELECT id, chunk_id, chunk_offset FROM _rowids WHERE rowid = ?"" + * + * Must be cleaned up with sqlite3_finalize(). + */ + sqlite3_stmt *stmtRowidsGetChunkPosition; +}; + +/** + * @brief Finalize all the sqlite3_stmt members in a vec0_vtab. + * + * @param p vec0_vtab pointer + */ +void vec0_free_resources(vec0_vtab *p) { + sqlite3_finalize(p->stmtLatestChunk); + p->stmtLatestChunk = NULL; + sqlite3_finalize(p->stmtRowidsInsertRowid); + p->stmtRowidsInsertRowid = NULL; + sqlite3_finalize(p->stmtRowidsInsertId); + p->stmtRowidsInsertId = NULL; + sqlite3_finalize(p->stmtRowidsUpdatePosition); + p->stmtRowidsUpdatePosition = NULL; + sqlite3_finalize(p->stmtRowidsGetChunkPosition); + p->stmtRowidsGetChunkPosition = NULL; +} + +/** + * @brief Free all memory and sqlite3_stmt members of a vec0_vtab + * + * @param p vec0_vtab pointer + */ +void vec0_free(vec0_vtab *p) { + vec0_free_resources(p); + + sqlite3_free(p->schemaName); + p->schemaName = NULL; + sqlite3_free(p->tableName); + p->tableName = NULL; + sqlite3_free(p->shadowChunksName); + p->shadowChunksName = NULL; + sqlite3_free(p->shadowRowidsName); + p->shadowRowidsName = NULL; + + for (int i = 0; i < p->numVectorColumns; i++) { + sqlite3_free(p->shadowVectorChunksNames[i]); + p->shadowVectorChunksNames[i] = NULL; + + sqlite3_free(p->vector_columns[i].name); + p->vector_columns[i].name = NULL; + } +} + +int vec0_num_defined_user_columns(vec0_vtab *p) { + return p->numVectorColumns + p->numPartitionColumns + p->numAuxiliaryColumns + p->numMetadataColumns; +} + +/** + * @brief Returns the index of the distance hidden column for the given vec0 + * table. + * + * @param p vec0 table + * @return int + */ +int vec0_column_distance_idx(vec0_vtab *p) { + return VEC0_COLUMN_USERN_START + (vec0_num_defined_user_columns(p) - 1) + + VEC0_COLUMN_OFFSET_DISTANCE; +} + +/** + * @brief Returns the index of the k hidden column for the given vec0 table. + * + * @param p vec0 table + * @return int k column index + */ +int vec0_column_k_idx(vec0_vtab *p) { + return VEC0_COLUMN_USERN_START + (vec0_num_defined_user_columns(p) - 1) + + VEC0_COLUMN_OFFSET_K; +} + +/** + * Returns 1 if the given column-based index is a valid vector column, + * 0 otherwise. + */ +int vec0_column_idx_is_vector(vec0_vtab *pVtab, int column_idx) { + return column_idx >= VEC0_COLUMN_USERN_START && + column_idx <= (VEC0_COLUMN_USERN_START + vec0_num_defined_user_columns(pVtab) - 1) && + pVtab->user_column_kinds[column_idx - VEC0_COLUMN_USERN_START] == SQLITE_VEC0_USER_COLUMN_KIND_VECTOR; +} + +/** + * Returns the vector index of the given user column index. + * ONLY call if validated with vec0_column_idx_is_vector before + */ +int vec0_column_idx_to_vector_idx(vec0_vtab *pVtab, int column_idx) { + UNUSED_PARAMETER(pVtab); + return pVtab->user_column_idxs[column_idx - VEC0_COLUMN_USERN_START]; +} +/** + * Returns 1 if the given column-based index is a "partition key" column, + * 0 otherwise. + */ +int vec0_column_idx_is_partition(vec0_vtab *pVtab, int column_idx) { + return column_idx >= VEC0_COLUMN_USERN_START && + column_idx <= (VEC0_COLUMN_USERN_START + vec0_num_defined_user_columns(pVtab) - 1) && + pVtab->user_column_kinds[column_idx - VEC0_COLUMN_USERN_START] == SQLITE_VEC0_USER_COLUMN_KIND_PARTITION; +} + +/** + * Returns the partition column index of the given user column index. + * ONLY call if validated with vec0_column_idx_is_vector before + */ +int vec0_column_idx_to_partition_idx(vec0_vtab *pVtab, int column_idx) { + UNUSED_PARAMETER(pVtab); + return pVtab->user_column_idxs[column_idx - VEC0_COLUMN_USERN_START]; +} + +/** + * Returns 1 if the given column-based index is a auxiliary column, + * 0 otherwise. + */ +int vec0_column_idx_is_auxiliary(vec0_vtab *pVtab, int column_idx) { + return column_idx >= VEC0_COLUMN_USERN_START && + column_idx <= (VEC0_COLUMN_USERN_START + vec0_num_defined_user_columns(pVtab) - 1) && + pVtab->user_column_kinds[column_idx - VEC0_COLUMN_USERN_START] == SQLITE_VEC0_USER_COLUMN_KIND_AUXILIARY; +} + +/** + * Returns the auxiliary column index of the given user column index. + * ONLY call if validated with vec0_column_idx_to_partition_idx before + */ +int vec0_column_idx_to_auxiliary_idx(vec0_vtab *pVtab, int column_idx) { + UNUSED_PARAMETER(pVtab); + return pVtab->user_column_idxs[column_idx - VEC0_COLUMN_USERN_START]; +} + +/** + * Returns 1 if the given column-based index is a metadata column, + * 0 otherwise. + */ +int vec0_column_idx_is_metadata(vec0_vtab *pVtab, int column_idx) { + return column_idx >= VEC0_COLUMN_USERN_START && + column_idx <= (VEC0_COLUMN_USERN_START + vec0_num_defined_user_columns(pVtab) - 1) && + pVtab->user_column_kinds[column_idx - VEC0_COLUMN_USERN_START] == SQLITE_VEC0_USER_COLUMN_KIND_METADATA; +} + +/** + * Returns the metadata column index of the given user column index. + * ONLY call if validated with vec0_column_idx_is_metadata before + */ +int vec0_column_idx_to_metadata_idx(vec0_vtab *pVtab, int column_idx) { + UNUSED_PARAMETER(pVtab); + return pVtab->user_column_idxs[column_idx - VEC0_COLUMN_USERN_START]; +} + +/** + * @brief Retrieve the chunk_id, chunk_offset, and possible "id" value + * of a vec0_vtab row with the provided rowid + * + * @param p vec0_vtab + * @param rowid the rowid of the row to query + * @param id output, optional sqlite3_value to provide the id. + * Useful for text PK rows. Must be freed with sqlite3_value_free() + * @param chunk_id output, the chunk_id the row belongs to + * @param chunk_offset output, the offset within the chunk the row belongs to + * @return SQLITE_ROW on success, error code otherwise. SQLITE_EMPTY if row DNE + */ +int vec0_get_chunk_position(vec0_vtab *p, i64 rowid, sqlite3_value **id, + i64 *chunk_id, i64 *chunk_offset) { + int rc; + + if (!p->stmtRowidsGetChunkPosition) { + const char *zSql = + sqlite3_mprintf("SELECT id, chunk_id, chunk_offset " + "FROM " VEC0_SHADOW_ROWIDS_NAME " WHERE rowid = ?", + p->schemaName, p->tableName); + if (!zSql) { + rc = SQLITE_NOMEM; + goto cleanup; + } + rc = sqlite3_prepare_v2(p->db, zSql, -1, &p->stmtRowidsGetChunkPosition, 0); + sqlite3_free((void *)zSql); + if (rc != SQLITE_OK) { + vtab_set_error( + &p->base, VEC_INTERAL_ERROR + "could not initialize 'rowids get chunk position' statement"); + goto cleanup; + } + } + + sqlite3_bind_int64(p->stmtRowidsGetChunkPosition, 1, rowid); + rc = sqlite3_step(p->stmtRowidsGetChunkPosition); + // special case: when no results, return SQLITE_EMPTY to convey "that chunk + // position doesnt exist" + if (rc == SQLITE_DONE) { + rc = SQLITE_EMPTY; + goto cleanup; + } + if (rc != SQLITE_ROW) { + goto cleanup; + } + + if (id) { + sqlite3_value *value = + sqlite3_column_value(p->stmtRowidsGetChunkPosition, 0); + *id = sqlite3_value_dup(value); + if (!*id) { + rc = SQLITE_NOMEM; + goto cleanup; + } + } + + if (chunk_id) { + *chunk_id = sqlite3_column_int64(p->stmtRowidsGetChunkPosition, 1); + } + if (chunk_offset) { + *chunk_offset = sqlite3_column_int64(p->stmtRowidsGetChunkPosition, 2); + } + + rc = SQLITE_OK; + +cleanup: + sqlite3_reset(p->stmtRowidsGetChunkPosition); + sqlite3_clear_bindings(p->stmtRowidsGetChunkPosition); + return rc; +} + +/** + * @brief Return the id value from the _rowids table where _rowids.rowid = + * rowid. + * + * @param pVtab: vec0 table to query + * @param rowid: rowid of the row to query. + * @param out: A dup'ed sqlite3_value of the id column. Might be null. + * Must be cleaned up with sqlite3_value_free(). + * @returns SQLITE_OK on success, error code on failure + */ +int vec0_get_id_value_from_rowid(vec0_vtab *pVtab, i64 rowid, + sqlite3_value **out) { + // PERF: different strategy than get_chunk_position? + return vec0_get_chunk_position((vec0_vtab *)pVtab, rowid, out, NULL, NULL); +} + +int vec0_rowid_from_id(vec0_vtab *p, sqlite3_value *valueId, i64 *rowid) { + sqlite3_stmt *stmt = NULL; + int rc; + char *zSql; + zSql = sqlite3_mprintf("SELECT rowid" + " FROM " VEC0_SHADOW_ROWIDS_NAME " WHERE id = ?", + p->schemaName, p->tableName); + if (!zSql) { + rc = SQLITE_NOMEM; + goto cleanup; + } + rc = sqlite3_prepare_v2(p->db, zSql, -1, &stmt, NULL); + sqlite3_free(zSql); + if (rc != SQLITE_OK) { + goto cleanup; + } + sqlite3_bind_value(stmt, 1, valueId); + rc = sqlite3_step(stmt); + if (rc == SQLITE_DONE) { + rc = SQLITE_EMPTY; + goto cleanup; + } + if (rc != SQLITE_ROW) { + goto cleanup; + } + *rowid = sqlite3_column_int64(stmt, 0); + rc = sqlite3_step(stmt); + if (rc != SQLITE_DONE) { + goto cleanup; + } + + rc = SQLITE_OK; + +cleanup: + sqlite3_finalize(stmt); + return rc; +} + +int vec0_result_id(vec0_vtab *p, sqlite3_context *context, i64 rowid) { + if (!p->pkIsText) { + sqlite3_result_int64(context, rowid); + return SQLITE_OK; + } + sqlite3_value *valueId; + int rc = vec0_get_id_value_from_rowid(p, rowid, &valueId); + if (rc != SQLITE_OK) { + return rc; + } + if (!valueId) { + sqlite3_result_error_nomem(context); + } else { + sqlite3_result_value(context, valueId); + sqlite3_value_free(valueId); + } + return SQLITE_OK; +} + +/** + * @brief + * + * @param pVtab: virtual table to query + * @param rowid: row to lookup + * @param vector_column_idx: which vector column to query + * @param outVector: Output pointer to the vector buffer. + * Must be sqlite3_free()'ed. + * @param outVectorSize: Pointer to a int where the size of outVector + * will be stored. + * @return int SQLITE_OK on success. + */ +int vec0_get_vector_data(vec0_vtab *pVtab, i64 rowid, int vector_column_idx, + void **outVector, int *outVectorSize) { + vec0_vtab *p = pVtab; + int rc, brc; + i64 chunk_id; + i64 chunk_offset; + size_t size; + void *buf = NULL; + int blobOffset; + sqlite3_blob *vectorBlob = NULL; + assert((vector_column_idx >= 0) && + (vector_column_idx < pVtab->numVectorColumns)); + + rc = vec0_get_chunk_position(pVtab, rowid, NULL, &chunk_id, &chunk_offset); + if (rc == SQLITE_EMPTY) { + vtab_set_error(&pVtab->base, "Could not find a row with rowid %lld", rowid); + goto cleanup; + } + if (rc != SQLITE_OK) { + goto cleanup; + } + + rc = sqlite3_blob_open(p->db, p->schemaName, + p->shadowVectorChunksNames[vector_column_idx], + "vectors", chunk_id, 0, &vectorBlob); + + if (rc != SQLITE_OK) { + vtab_set_error(&pVtab->base, + "Could not fetch vector data for %lld, opening blob failed", + rowid); + rc = SQLITE_ERROR; + goto cleanup; + } + + size = vector_column_byte_size(pVtab->vector_columns[vector_column_idx]); + blobOffset = chunk_offset * size; + + buf = sqlite3_malloc(size); + if (!buf) { + rc = SQLITE_NOMEM; + goto cleanup; + } + + rc = sqlite3_blob_read(vectorBlob, buf, size, blobOffset); + if (rc != SQLITE_OK) { + sqlite3_free(buf); + buf = NULL; + vtab_set_error( + &pVtab->base, + "Could not fetch vector data for %lld, reading from blob failed", + rowid); + rc = SQLITE_ERROR; + goto cleanup; + } + + *outVector = buf; + if (outVectorSize) { + *outVectorSize = size; + } + rc = SQLITE_OK; + +cleanup: + brc = sqlite3_blob_close(vectorBlob); + if ((rc == SQLITE_OK) && (brc != SQLITE_OK)) { + vtab_set_error( + &p->base, VEC_INTERAL_ERROR + "unknown error, could not close vector blob, please file an issue"); + return brc; + } + + return rc; +} + +/** + * @brief Retrieve the sqlite3_value of the i'th partition value for the given row. + * + * @param pVtab - the vec0_vtab in questions + * @param rowid - rowid of target row + * @param partition_idx - which partition column to retrieve + * @param outValue - output sqlite3_value + * @return int - SQLITE_OK on success, otherwise error code + */ +int vec0_get_partition_value_for_rowid(vec0_vtab *pVtab, i64 rowid, int partition_idx, sqlite3_value ** outValue) { + int rc; + i64 chunk_id; + i64 chunk_offset; + rc = vec0_get_chunk_position(pVtab, rowid, NULL, &chunk_id, &chunk_offset); + if(rc != SQLITE_OK) { + return rc; + } + sqlite3_stmt * stmt = NULL; + char * zSql = sqlite3_mprintf("SELECT partition%02d FROM " VEC0_SHADOW_CHUNKS_NAME " WHERE chunk_id = ?", partition_idx, pVtab->schemaName, pVtab->tableName); + if(!zSql) { + return SQLITE_NOMEM; + } + rc = sqlite3_prepare_v2(pVtab->db, zSql, -1, &stmt, NULL); + sqlite3_free(zSql); + if(rc != SQLITE_OK) { + return rc; + } + sqlite3_bind_int64(stmt, 1, chunk_id); + rc = sqlite3_step(stmt); + if(rc != SQLITE_ROW) { + rc = SQLITE_ERROR; + goto done; + } + *outValue = sqlite3_value_dup(sqlite3_column_value(stmt, 0)); + if(!*outValue) { + rc = SQLITE_NOMEM; + goto done; + } + rc = SQLITE_OK; + + done: + sqlite3_finalize(stmt); + return rc; + +} + +/** + * @brief Get the value of an auxiliary column for the given rowid + * + * @param pVtab vec0_vtab + * @param rowid the rowid of the row to lookup + * @param auxiliary_idx aux index of the column we care about + * @param outValue Output sqlite3_value to store + * @return int SQLITE_OK on success, error code otherwise + */ +int vec0_get_auxiliary_value_for_rowid(vec0_vtab *pVtab, i64 rowid, int auxiliary_idx, sqlite3_value ** outValue) { + int rc; + sqlite3_stmt * stmt = NULL; + char * zSql = sqlite3_mprintf("SELECT value%02d FROM " VEC0_SHADOW_AUXILIARY_NAME " WHERE rowid = ?", auxiliary_idx, pVtab->schemaName, pVtab->tableName); + if(!zSql) { + return SQLITE_NOMEM; + } + rc = sqlite3_prepare_v2(pVtab->db, zSql, -1, &stmt, NULL); + sqlite3_free(zSql); + if(rc != SQLITE_OK) { + return rc; + } + sqlite3_bind_int64(stmt, 1, rowid); + rc = sqlite3_step(stmt); + if(rc != SQLITE_ROW) { + rc = SQLITE_ERROR; + goto done; + } + *outValue = sqlite3_value_dup(sqlite3_column_value(stmt, 0)); + if(!*outValue) { + rc = SQLITE_NOMEM; + goto done; + } + rc = SQLITE_OK; + + done: + sqlite3_finalize(stmt); + return rc; +} + +/** + * @brief Result the given metadata value for the given row and metadata column index. + * Will traverse the metadatachunksNN table with BLOB I/0 for the given rowid. + * + * @param p + * @param rowid + * @param metadata_idx + * @param context + * @return int + */ +int vec0_result_metadata_value_for_rowid(vec0_vtab *p, i64 rowid, int metadata_idx, sqlite3_context * context) { + int rc; + i64 chunk_id; + i64 chunk_offset; + rc = vec0_get_chunk_position(p, rowid, NULL, &chunk_id, &chunk_offset); + if(rc != SQLITE_OK) { + return rc; + } + sqlite3_blob * blobValue; + rc = sqlite3_blob_open(p->db, p->schemaName, p->shadowMetadataChunksNames[metadata_idx], "data", chunk_id, 0, &blobValue); + if(rc != SQLITE_OK) { + return rc; + } + + switch(p->metadata_columns[metadata_idx].kind) { + case VEC0_METADATA_COLUMN_KIND_BOOLEAN: { + u8 block; + rc = sqlite3_blob_read(blobValue, &block, sizeof(block), chunk_offset / CHAR_BIT); + if(rc != SQLITE_OK) { + goto done; + } + int value = block >> ((chunk_offset % CHAR_BIT)) & 1; + sqlite3_result_int(context, value); + break; + } + case VEC0_METADATA_COLUMN_KIND_INTEGER: { + i64 value; + rc = sqlite3_blob_read(blobValue, &value, sizeof(value), chunk_offset * sizeof(i64)); + if(rc != SQLITE_OK) { + goto done; + } + sqlite3_result_int64(context, value); + break; + } + case VEC0_METADATA_COLUMN_KIND_FLOAT: { + double value; + rc = sqlite3_blob_read(blobValue, &value, sizeof(value), chunk_offset * sizeof(double)); + if(rc != SQLITE_OK) { + goto done; + } + sqlite3_result_double(context, value); + break; + } + case VEC0_METADATA_COLUMN_KIND_TEXT: { + u8 view[VEC0_METADATA_TEXT_VIEW_BUFFER_LENGTH]; + rc = sqlite3_blob_read(blobValue, &view, VEC0_METADATA_TEXT_VIEW_BUFFER_LENGTH, chunk_offset * VEC0_METADATA_TEXT_VIEW_BUFFER_LENGTH); + if(rc != SQLITE_OK) { + goto done; + } + int length = ((int *)view)[0]; + if(length <= VEC0_METADATA_TEXT_VIEW_DATA_LENGTH) { + sqlite3_result_text(context, (const char*) (view + 4), length, SQLITE_TRANSIENT); + } + else { + sqlite3_stmt * stmt; + const char * zSql = sqlite3_mprintf("SELECT data FROM " VEC0_SHADOW_METADATA_TEXT_DATA_NAME " WHERE rowid = ?", p->schemaName, p->tableName, metadata_idx); + if(!zSql) { + rc = SQLITE_ERROR; + goto done; + } + rc = sqlite3_prepare_v2(p->db, zSql, -1, &stmt, NULL); + sqlite3_free((void *) zSql); + if(rc != SQLITE_OK) { + goto done; + } + sqlite3_bind_int64(stmt, 1, rowid); + rc = sqlite3_step(stmt); + if(rc != SQLITE_ROW) { + sqlite3_finalize(stmt); + rc = SQLITE_ERROR; + goto done; + } + sqlite3_result_value(context, sqlite3_column_value(stmt, 0)); + sqlite3_finalize(stmt); + rc = SQLITE_OK; + } + break; + } + } + done: + // blobValue is read-only, will not fail on close + sqlite3_blob_close(blobValue); + return rc; + +} + +int vec0_get_latest_chunk_rowid(vec0_vtab *p, i64 *chunk_rowid, sqlite3_value ** partitionKeyValues) { + int rc; + const char *zSql; + // lazy initialize stmtLatestChunk when needed. May be cleared during xSync() + if (!p->stmtLatestChunk) { + if(p->numPartitionColumns > 0) { + sqlite3_str * s = sqlite3_str_new(NULL); + sqlite3_str_appendf(s, "SELECT max(rowid) FROM " VEC0_SHADOW_CHUNKS_NAME " WHERE ", + p->schemaName, p->tableName); + + for(int i = 0; i < p->numPartitionColumns; i++) { + if(i != 0) { + sqlite3_str_appendall(s, " AND "); + } + sqlite3_str_appendf(s, " partition%02d = ? ", i); + } + zSql = sqlite3_str_finish(s); + }else { + zSql = sqlite3_mprintf("SELECT max(rowid) FROM " VEC0_SHADOW_CHUNKS_NAME, + p->schemaName, p->tableName); + } + + if (!zSql) { + rc = SQLITE_NOMEM; + goto cleanup; + } + rc = sqlite3_prepare_v2(p->db, zSql, -1, &p->stmtLatestChunk, 0); + sqlite3_free((void *)zSql); + if (rc != SQLITE_OK) { + // IMP: V21406_05476 + vtab_set_error(&p->base, VEC_INTERAL_ERROR + "could not initialize 'latest chunk' statement"); + goto cleanup; + } + } + + for(int i = 0; i < p->numPartitionColumns; i++) { + sqlite3_bind_value(p->stmtLatestChunk, i+1, (partitionKeyValues[i])); + } + + rc = sqlite3_step(p->stmtLatestChunk); + if (rc != SQLITE_ROW) { + // IMP: V31559_15629 + vtab_set_error(&p->base, VEC_INTERAL_ERROR "Could not find latest chunk"); + rc = SQLITE_ERROR; + goto cleanup; + } + if(sqlite3_column_type(p->stmtLatestChunk, 0) == SQLITE_NULL){ + rc = SQLITE_EMPTY; + goto cleanup; + } + *chunk_rowid = sqlite3_column_int64(p->stmtLatestChunk, 0); + rc = sqlite3_step(p->stmtLatestChunk); + if (rc != SQLITE_DONE) { + vtab_set_error(&p->base, + VEC_INTERAL_ERROR + "unknown result code when closing out stmtLatestChunk. " + "Please file an issue: " REPORT_URL, + p->schemaName, p->shadowChunksName); + goto cleanup; + } + rc = SQLITE_OK; + +cleanup: + if (p->stmtLatestChunk) { + sqlite3_reset(p->stmtLatestChunk); + sqlite3_clear_bindings(p->stmtLatestChunk); + } + return rc; +} + +int vec0_rowids_insert_rowid(vec0_vtab *p, i64 rowid) { + int rc = SQLITE_OK; + int entered = 0; + UNUSED_PARAMETER(entered); // temporary + if (!p->stmtRowidsInsertRowid) { + const char *zSql = + sqlite3_mprintf("INSERT INTO " VEC0_SHADOW_ROWIDS_NAME "(rowid)" + "VALUES (?);", + p->schemaName, p->tableName); + if (!zSql) { + rc = SQLITE_NOMEM; + goto cleanup; + } + rc = sqlite3_prepare_v2(p->db, zSql, -1, &p->stmtRowidsInsertRowid, 0); + sqlite3_free((void *)zSql); + if (rc != SQLITE_OK) { + vtab_set_error(&p->base, VEC_INTERAL_ERROR + "could not initialize 'insert rowids' statement"); + goto cleanup; + } + } + +#if SQLITE_THREADSAFE + if (sqlite3_mutex_enter) { + sqlite3_mutex_enter(sqlite3_db_mutex(p->db)); + entered = 1; + } +#endif + sqlite3_bind_int64(p->stmtRowidsInsertRowid, 1, rowid); + rc = sqlite3_step(p->stmtRowidsInsertRowid); + + if (rc != SQLITE_DONE) { + if (sqlite3_extended_errcode(p->db) == SQLITE_CONSTRAINT_PRIMARYKEY) { + // IMP: V17090_01160 + vtab_set_error(&p->base, "UNIQUE constraint failed on %s primary key", + p->tableName); + } else { + // IMP: V04679_21517 + vtab_set_error(&p->base, + "Error inserting rowid into rowids shadow table: %s", + sqlite3_errmsg(sqlite3_db_handle(p->stmtRowidsInsertId))); + } + rc = SQLITE_ERROR; + goto cleanup; + } + + rc = SQLITE_OK; + +cleanup: + if (p->stmtRowidsInsertRowid) { + sqlite3_reset(p->stmtRowidsInsertRowid); + sqlite3_clear_bindings(p->stmtRowidsInsertRowid); + } + +#if SQLITE_THREADSAFE + if (sqlite3_mutex_leave && entered) { + sqlite3_mutex_leave(sqlite3_db_mutex(p->db)); + } +#endif + return rc; +} + +int vec0_rowids_insert_id(vec0_vtab *p, sqlite3_value *idValue, i64 *rowid) { + int rc = SQLITE_OK; + int entered = 0; + UNUSED_PARAMETER(entered); // temporary + if (!p->stmtRowidsInsertId) { + const char *zSql = + sqlite3_mprintf("INSERT INTO " VEC0_SHADOW_ROWIDS_NAME "(id)" + "VALUES (?);", + p->schemaName, p->tableName); + if (!zSql) { + rc = SQLITE_NOMEM; + goto complete; + } + rc = sqlite3_prepare_v2(p->db, zSql, -1, &p->stmtRowidsInsertId, 0); + sqlite3_free((void *)zSql); + if (rc != SQLITE_OK) { + vtab_set_error(&p->base, VEC_INTERAL_ERROR + "could not initialize 'insert rowids id' statement"); + goto complete; + } + } + +#if SQLITE_THREADSAFE + if (sqlite3_mutex_enter) { + sqlite3_mutex_enter(sqlite3_db_mutex(p->db)); + entered = 1; + } +#endif + + if (idValue) { + sqlite3_bind_value(p->stmtRowidsInsertId, 1, idValue); + } + rc = sqlite3_step(p->stmtRowidsInsertId); + + if (rc != SQLITE_DONE) { + if (sqlite3_extended_errcode(p->db) == SQLITE_CONSTRAINT_UNIQUE) { + // IMP: V20497_04568 + vtab_set_error(&p->base, "UNIQUE constraint failed on %s primary key", + p->tableName); + } else { + // IMP: V24016_08086 + // IMP: V15177_32015 + vtab_set_error(&p->base, + "Error inserting id into rowids shadow table: %s", + sqlite3_errmsg(sqlite3_db_handle(p->stmtRowidsInsertId))); + } + rc = SQLITE_ERROR; + goto complete; + } + + *rowid = sqlite3_last_insert_rowid(p->db); + rc = SQLITE_OK; + +complete: + if (p->stmtRowidsInsertId) { + sqlite3_reset(p->stmtRowidsInsertId); + sqlite3_clear_bindings(p->stmtRowidsInsertId); + } + +#if SQLITE_THREADSAFE + if (sqlite3_mutex_leave && entered) { + sqlite3_mutex_leave(sqlite3_db_mutex(p->db)); + } +#endif + return rc; +} + +int vec0_metadata_chunk_size(vec0_metadata_column_kind kind, int chunk_size) { + switch(kind) { + case VEC0_METADATA_COLUMN_KIND_BOOLEAN: + return chunk_size / 8; + case VEC0_METADATA_COLUMN_KIND_INTEGER: + return chunk_size * sizeof(i64); + case VEC0_METADATA_COLUMN_KIND_FLOAT: + return chunk_size * sizeof(double); + case VEC0_METADATA_COLUMN_KIND_TEXT: + return chunk_size * VEC0_METADATA_TEXT_VIEW_BUFFER_LENGTH; + } + return 0; +} + +int vec0_rowids_update_position(vec0_vtab *p, i64 rowid, i64 chunk_rowid, + i64 chunk_offset) { + int rc = SQLITE_OK; + + if (!p->stmtRowidsUpdatePosition) { + const char *zSql = sqlite3_mprintf(" UPDATE " VEC0_SHADOW_ROWIDS_NAME + " SET chunk_id = ?, chunk_offset = ?" + " WHERE rowid = ?", + p->schemaName, p->tableName); + if (!zSql) { + rc = SQLITE_NOMEM; + goto cleanup; + } + rc = sqlite3_prepare_v2(p->db, zSql, -1, &p->stmtRowidsUpdatePosition, 0); + sqlite3_free((void *)zSql); + if (rc != SQLITE_OK) { + vtab_set_error(&p->base, VEC_INTERAL_ERROR + "could not initialize 'update rowids position' statement"); + goto cleanup; + } + } + + sqlite3_bind_int64(p->stmtRowidsUpdatePosition, 1, chunk_rowid); + sqlite3_bind_int64(p->stmtRowidsUpdatePosition, 2, chunk_offset); + sqlite3_bind_int64(p->stmtRowidsUpdatePosition, 3, rowid); + + rc = sqlite3_step(p->stmtRowidsUpdatePosition); + if (rc != SQLITE_DONE) { + // IMP: V21925_05995 + vtab_set_error(&p->base, + VEC_INTERAL_ERROR + "could not update rowids position for rowid=%lld, " + "chunk_rowid=%lld, chunk_offset=%lld", + rowid, chunk_rowid, chunk_offset); + rc = SQLITE_ERROR; + goto cleanup; + } + rc = SQLITE_OK; + +cleanup: + if (p->stmtRowidsUpdatePosition) { + sqlite3_reset(p->stmtRowidsUpdatePosition); + sqlite3_clear_bindings(p->stmtRowidsUpdatePosition); + } + + return rc; +} + +/** + * @brief Adds a new chunk for the vec0 table, and the corresponding vector + * chunks. + * + * Inserts a new row into the _chunks table, with blank data, and uses that new + * rowid to insert new blank rows into _vector_chunksXX tables. + * + * @param p: vec0 table to add new chunk + * @param paritionKeyValues: Array of partition key valeus for the new chunk, if available + * @param chunk_rowid: Output pointer, if not NULL, then will be filled with the + * new chunk rowid. + * @return int SQLITE_OK on success, error code otherwise. + */ +int vec0_new_chunk(vec0_vtab *p, sqlite3_value ** partitionKeyValues, i64 *chunk_rowid) { + int rc; + char *zSql; + sqlite3_stmt *stmt; + i64 rowid; + + // Step 1: Insert a new row in _chunks, capture that new rowid + if(p->numPartitionColumns > 0) { + sqlite3_str * s = sqlite3_str_new(NULL); + sqlite3_str_appendf(s, "INSERT INTO " VEC0_SHADOW_CHUNKS_NAME, p->schemaName, p->tableName); + sqlite3_str_appendall(s, "(size, validity, rowids"); + for(int i = 0; i < p->numPartitionColumns; i++) { + sqlite3_str_appendf(s, ", partition%02d", i); + } + sqlite3_str_appendall(s, ") VALUES (?, ?, ?"); + for(int i = 0; i < p->numPartitionColumns; i++) { + sqlite3_str_appendall(s, ", ?"); + } + sqlite3_str_appendall(s, ")"); + + zSql = sqlite3_str_finish(s); + }else { + zSql = sqlite3_mprintf("INSERT INTO " VEC0_SHADOW_CHUNKS_NAME + "(size, validity, rowids) " + "VALUES (?, ?, ?);", + p->schemaName, p->tableName); + } + + if (!zSql) { + return SQLITE_NOMEM; + } + rc = sqlite3_prepare_v2(p->db, zSql, -1, &stmt, NULL); + sqlite3_free(zSql); + if (rc != SQLITE_OK) { + sqlite3_finalize(stmt); + return rc; + } + +#if SQLITE_THREADSAFE + if (sqlite3_mutex_enter) { + sqlite3_mutex_enter(sqlite3_db_mutex(p->db)); + } +#endif + + sqlite3_bind_int64(stmt, 1, p->chunk_size); // size + sqlite3_bind_zeroblob(stmt, 2, p->chunk_size / CHAR_BIT); // validity bitmap + sqlite3_bind_zeroblob(stmt, 3, p->chunk_size * sizeof(i64)); // rowids + + for(int i = 0; i < p->numPartitionColumns; i++) { + sqlite3_bind_value(stmt, 4 + i, partitionKeyValues[i]); + } + + rc = sqlite3_step(stmt); + int failed = rc != SQLITE_DONE; + rowid = sqlite3_last_insert_rowid(p->db); +#if SQLITE_THREADSAFE + if (sqlite3_mutex_leave) { + sqlite3_mutex_leave(sqlite3_db_mutex(p->db)); + } +#endif + sqlite3_finalize(stmt); + if (failed) { + return SQLITE_ERROR; + } + + // Step 2: Create new vector chunks for each vector column, with + // that new chunk_rowid. + + for (int i = 0; i < vec0_num_defined_user_columns(p); i++) { + if(p->user_column_kinds[i] != SQLITE_VEC0_USER_COLUMN_KIND_VECTOR) { + continue; + } + int vector_column_idx = p->user_column_idxs[i]; + i64 vectorsSize = + p->chunk_size * vector_column_byte_size(p->vector_columns[vector_column_idx]); + + zSql = sqlite3_mprintf("INSERT INTO " VEC0_SHADOW_VECTOR_N_NAME + "(rowid, vectors)" + "VALUES (?, ?)", + p->schemaName, p->tableName, vector_column_idx); + if (!zSql) { + return SQLITE_NOMEM; + } + rc = sqlite3_prepare_v2(p->db, zSql, -1, &stmt, NULL); + sqlite3_free(zSql); + + if (rc != SQLITE_OK) { + sqlite3_finalize(stmt); + return rc; + } + + sqlite3_bind_int64(stmt, 1, rowid); + sqlite3_bind_zeroblob64(stmt, 2, vectorsSize); + + rc = sqlite3_step(stmt); + sqlite3_finalize(stmt); + if (rc != SQLITE_DONE) { + return rc; + } + } + + // Step 3: Create new metadata chunks for each metadata column + for (int i = 0; i < vec0_num_defined_user_columns(p); i++) { + if(p->user_column_kinds[i] != SQLITE_VEC0_USER_COLUMN_KIND_METADATA) { + continue; + } + int metadata_column_idx = p->user_column_idxs[i]; + zSql = sqlite3_mprintf("INSERT INTO " VEC0_SHADOW_METADATA_N_NAME + "(rowid, data)" + "VALUES (?, ?)", + p->schemaName, p->tableName, metadata_column_idx); + if (!zSql) { + return SQLITE_NOMEM; + } + rc = sqlite3_prepare_v2(p->db, zSql, -1, &stmt, NULL); + sqlite3_free(zSql); + + if (rc != SQLITE_OK) { + sqlite3_finalize(stmt); + return rc; + } + + sqlite3_bind_int64(stmt, 1, rowid); + sqlite3_bind_zeroblob64(stmt, 2, vec0_metadata_chunk_size(p->metadata_columns[metadata_column_idx].kind, p->chunk_size)); + + rc = sqlite3_step(stmt); + sqlite3_finalize(stmt); + if (rc != SQLITE_DONE) { + return rc; + } + } + + + if (chunk_rowid) { + *chunk_rowid = rowid; + } + + return SQLITE_OK; +} + +struct vec0_query_fullscan_data { + sqlite3_stmt *rowids_stmt; + i8 done; +}; +void vec0_query_fullscan_data_clear( + struct vec0_query_fullscan_data *fullscan_data) { + if (!fullscan_data) + return; + + if (fullscan_data->rowids_stmt) { + sqlite3_finalize(fullscan_data->rowids_stmt); + fullscan_data->rowids_stmt = NULL; + } +} + +struct vec0_query_knn_data { + i64 k; + i64 k_used; + // Array of rowids of size k. Must be freed with sqlite3_free(). + i64 *rowids; + // Array of distances of size k. Must be freed with sqlite3_free(). + f32 *distances; + i64 current_idx; +}; +void vec0_query_knn_data_clear(struct vec0_query_knn_data *knn_data) { + if (!knn_data) + return; + + if (knn_data->rowids) { + sqlite3_free(knn_data->rowids); + knn_data->rowids = NULL; + } + if (knn_data->distances) { + sqlite3_free(knn_data->distances); + knn_data->distances = NULL; + } +} + +struct vec0_query_point_data { + i64 rowid; + void *vectors[VEC0_MAX_VECTOR_COLUMNS]; + int done; +}; +void vec0_query_point_data_clear(struct vec0_query_point_data *point_data) { + if (!point_data) + return; + for (int i = 0; i < VEC0_MAX_VECTOR_COLUMNS; i++) { + sqlite3_free(point_data->vectors[i]); + point_data->vectors[i] = NULL; + } +} + +typedef enum { + // If any values are updated, please update the ARCHITECTURE.md docs accordingly! + + VEC0_QUERY_PLAN_FULLSCAN = '1', + VEC0_QUERY_PLAN_POINT = '2', + VEC0_QUERY_PLAN_KNN = '3', +} vec0_query_plan; + +typedef struct vec0_cursor vec0_cursor; +struct vec0_cursor { + sqlite3_vtab_cursor base; + + vec0_query_plan query_plan; + struct vec0_query_fullscan_data *fullscan_data; + struct vec0_query_knn_data *knn_data; + struct vec0_query_point_data *point_data; +}; + +void vec0_cursor_clear(vec0_cursor *pCur) { + if (pCur->fullscan_data) { + vec0_query_fullscan_data_clear(pCur->fullscan_data); + sqlite3_free(pCur->fullscan_data); + pCur->fullscan_data = NULL; + } + if (pCur->knn_data) { + vec0_query_knn_data_clear(pCur->knn_data); + sqlite3_free(pCur->knn_data); + pCur->knn_data = NULL; + } + if (pCur->point_data) { + vec0_query_point_data_clear(pCur->point_data); + sqlite3_free(pCur->point_data); + pCur->point_data = NULL; + } +} + +#define VEC_CONSTRUCTOR_ERROR "vec0 constructor error: " +static int vec0_init(sqlite3 *db, void *pAux, int argc, const char *const *argv, + sqlite3_vtab **ppVtab, char **pzErr, bool isCreate) { + UNUSED_PARAMETER(pAux); + vec0_vtab *pNew; + int rc; + const char *zSql; + + pNew = sqlite3_malloc(sizeof(*pNew)); + if (pNew == 0) + return SQLITE_NOMEM; + memset(pNew, 0, sizeof(*pNew)); + + // Declared chunk_size=N for entire table. + // -1 to use the defualt, otherwise will get re-assigned on `chunk_size=N` + // option + int chunk_size = -1; + int numVectorColumns = 0; + int numPartitionColumns = 0; + int numAuxiliaryColumns = 0; + int numMetadataColumns = 0; + int user_column_idx = 0; + + // track if a "primary key" column is defined + char *pkColumnName = NULL; + int pkColumnNameLength; + int pkColumnType = SQLITE_INTEGER; + + for (int i = 3; i < argc; i++) { + struct VectorColumnDefinition vecColumn; + struct Vec0PartitionColumnDefinition partitionColumn; + struct Vec0AuxiliaryColumnDefinition auxColumn; + struct Vec0MetadataColumnDefinition metadataColumn; + char *cName = NULL; + int cNameLength; + int cType; + + // Scenario #1: Constructor argument is a vector column definition, ie `foo float[1024]` + rc = vec0_parse_vector_column(argv[i], strlen(argv[i]), &vecColumn); + if (rc == SQLITE_ERROR) { + *pzErr = sqlite3_mprintf( + VEC_CONSTRUCTOR_ERROR "could not parse vector column '%s'", argv[i]); + goto error; + } + if (rc == SQLITE_OK) { + if (numVectorColumns >= VEC0_MAX_VECTOR_COLUMNS) { + sqlite3_free(vecColumn.name); + *pzErr = sqlite3_mprintf(VEC_CONSTRUCTOR_ERROR + "Too many provided vector columns, maximum %d", + VEC0_MAX_VECTOR_COLUMNS); + goto error; + } + + if (vecColumn.dimensions > SQLITE_VEC_VEC0_MAX_DIMENSIONS) { + sqlite3_free(vecColumn.name); + *pzErr = sqlite3_mprintf( + VEC_CONSTRUCTOR_ERROR + "Dimension on vector column too large, provided %lld, maximum %lld", + (i64)vecColumn.dimensions, SQLITE_VEC_VEC0_MAX_DIMENSIONS); + goto error; + } + pNew->user_column_kinds[user_column_idx] = SQLITE_VEC0_USER_COLUMN_KIND_VECTOR; + pNew->user_column_idxs[user_column_idx] = numVectorColumns; + memcpy(&pNew->vector_columns[numVectorColumns], &vecColumn, sizeof(vecColumn)); + numVectorColumns++; + user_column_idx++; + + continue; + } + + // Scenario #2: Constructor argument is a partition key column definition, ie `user_id text partition key` + rc = vec0_parse_partition_key_definition(argv[i], strlen(argv[i]), &cName, + &cNameLength, &cType); + if (rc == SQLITE_OK) { + if (numPartitionColumns >= VEC0_MAX_PARTITION_COLUMNS) { + *pzErr = sqlite3_mprintf( + VEC_CONSTRUCTOR_ERROR + "More than %d partition key columns were provided", + VEC0_MAX_PARTITION_COLUMNS); + goto error; + } + partitionColumn.type = cType; + partitionColumn.name_length = cNameLength; + partitionColumn.name = sqlite3_mprintf("%.*s", cNameLength, cName); + if(!partitionColumn.name) { + rc = SQLITE_NOMEM; + goto error; + } + + pNew->user_column_kinds[user_column_idx] = SQLITE_VEC0_USER_COLUMN_KIND_PARTITION; + pNew->user_column_idxs[user_column_idx] = numPartitionColumns; + memcpy(&pNew->paritition_columns[numPartitionColumns], &partitionColumn, sizeof(partitionColumn)); + numPartitionColumns++; + user_column_idx++; + continue; + } + + // Scenario #3: Constructor argument is a primary key column definition, ie `article_id text primary key` + rc = vec0_parse_primary_key_definition(argv[i], strlen(argv[i]), &cName, + &cNameLength, &cType); + if (rc == SQLITE_OK) { + if (pkColumnName) { + *pzErr = sqlite3_mprintf( + VEC_CONSTRUCTOR_ERROR + "More than one primary key definition was provided, vec0 only " + "suports a single primary key column", + argv[i]); + goto error; + } + pkColumnName = cName; + pkColumnNameLength = cNameLength; + pkColumnType = cType; + continue; + } + + // Scenario #4: Constructor argument is a auxiliary column definition, ie `+contents text` + rc = vec0_parse_auxiliary_column_definition(argv[i], strlen(argv[i]), &cName, + &cNameLength, &cType); + if(rc == SQLITE_OK) { + if (numAuxiliaryColumns >= VEC0_MAX_AUXILIARY_COLUMNS) { + *pzErr = sqlite3_mprintf( + VEC_CONSTRUCTOR_ERROR + "More than %d auxiliary columns were provided", + VEC0_MAX_AUXILIARY_COLUMNS); + goto error; + } + auxColumn.type = cType; + auxColumn.name_length = cNameLength; + auxColumn.name = sqlite3_mprintf("%.*s", cNameLength, cName); + if(!auxColumn.name) { + rc = SQLITE_NOMEM; + goto error; + } + + pNew->user_column_kinds[user_column_idx] = SQLITE_VEC0_USER_COLUMN_KIND_AUXILIARY; + pNew->user_column_idxs[user_column_idx] = numAuxiliaryColumns; + memcpy(&pNew->auxiliary_columns[numAuxiliaryColumns], &auxColumn, sizeof(auxColumn)); + numAuxiliaryColumns++; + user_column_idx++; + continue; + } + + vec0_metadata_column_kind kind; + rc = vec0_parse_metadata_column_definition(argv[i], strlen(argv[i]), &cName, + &cNameLength, &kind); + if(rc == SQLITE_OK) { + if (numMetadataColumns >= VEC0_MAX_METADATA_COLUMNS) { + *pzErr = sqlite3_mprintf( + VEC_CONSTRUCTOR_ERROR + "More than %d metadata columns were provided", + VEC0_MAX_METADATA_COLUMNS); + goto error; + } + metadataColumn.kind = kind; + metadataColumn.name_length = cNameLength; + metadataColumn.name = sqlite3_mprintf("%.*s", cNameLength, cName); + if(!metadataColumn.name) { + rc = SQLITE_NOMEM; + goto error; + } + + pNew->user_column_kinds[user_column_idx] = SQLITE_VEC0_USER_COLUMN_KIND_METADATA; + pNew->user_column_idxs[user_column_idx] = numMetadataColumns; + memcpy(&pNew->metadata_columns[numMetadataColumns], &metadataColumn, sizeof(metadataColumn)); + numMetadataColumns++; + user_column_idx++; + continue; + } + + // Scenario #4: Constructor argument is a table-level option, ie `chunk_size` + + char *key; + char *value; + int keyLength, valueLength; + rc = vec0_parse_table_option(argv[i], strlen(argv[i]), &key, &keyLength, + &value, &valueLength); + if (rc == SQLITE_ERROR) { + *pzErr = sqlite3_mprintf( + VEC_CONSTRUCTOR_ERROR "could not parse table option '%s'", argv[i]); + goto error; + } + if (rc == SQLITE_OK) { + if (sqlite3_strnicmp(key, "chunk_size", keyLength) == 0) { + chunk_size = atoi(value); + if (chunk_size <= 0) { + // IMP: V01931_18769 + *pzErr = + sqlite3_mprintf(VEC_CONSTRUCTOR_ERROR + "chunk_size must be a non-zero positive integer"); + goto error; + } + if ((chunk_size % 8) != 0) { + // IMP: V14110_30948 + *pzErr = sqlite3_mprintf(VEC_CONSTRUCTOR_ERROR + "chunk_size must be divisible by 8"); + goto error; + } +#define SQLITE_VEC_CHUNK_SIZE_MAX 4096 + if (chunk_size > SQLITE_VEC_CHUNK_SIZE_MAX) { + *pzErr = + sqlite3_mprintf(VEC_CONSTRUCTOR_ERROR "chunk_size too large"); + goto error; + } + } else { + // IMP: V27642_11712 + *pzErr = sqlite3_mprintf( + VEC_CONSTRUCTOR_ERROR "Unknown table option: %.*s", keyLength, key); + goto error; + } + continue; + } + + // Scenario #5: Unknown constructor argument + *pzErr = + sqlite3_mprintf(VEC_CONSTRUCTOR_ERROR "Could not parse '%s'", argv[i]); + goto error; + } + + if (chunk_size < 0) { + chunk_size = 1024; + } + + if (numVectorColumns <= 0) { + *pzErr = sqlite3_mprintf(VEC_CONSTRUCTOR_ERROR + "At least one vector column is required"); + goto error; + } + + sqlite3_str *createStr = sqlite3_str_new(NULL); + sqlite3_str_appendall(createStr, "CREATE TABLE x("); + if (pkColumnName) { + sqlite3_str_appendf(createStr, "\"%.*w\" primary key, ", pkColumnNameLength, + pkColumnName); + } else { + sqlite3_str_appendall(createStr, "rowid, "); + } + for (int i = 0; i < numVectorColumns + numPartitionColumns + numAuxiliaryColumns + numMetadataColumns; i++) { + switch(pNew->user_column_kinds[i]) { + case SQLITE_VEC0_USER_COLUMN_KIND_VECTOR: { + int vector_idx = pNew->user_column_idxs[i]; + sqlite3_str_appendf(createStr, "\"%.*w\", ", + pNew->vector_columns[vector_idx].name_length, + pNew->vector_columns[vector_idx].name); + break; + } + case SQLITE_VEC0_USER_COLUMN_KIND_PARTITION: { + int partition_idx = pNew->user_column_idxs[i]; + sqlite3_str_appendf(createStr, "\"%.*w\", ", + pNew->paritition_columns[partition_idx].name_length, + pNew->paritition_columns[partition_idx].name); + break; + } + case SQLITE_VEC0_USER_COLUMN_KIND_AUXILIARY: { + int auxiliary_idx = pNew->user_column_idxs[i]; + sqlite3_str_appendf(createStr, "\"%.*w\", ", + pNew->auxiliary_columns[auxiliary_idx].name_length, + pNew->auxiliary_columns[auxiliary_idx].name); + break; + } + case SQLITE_VEC0_USER_COLUMN_KIND_METADATA: { + int metadata_idx = pNew->user_column_idxs[i]; + sqlite3_str_appendf(createStr, "\"%.*w\", ", + pNew->metadata_columns[metadata_idx].name_length, + pNew->metadata_columns[metadata_idx].name); + break; + } + } + + } + sqlite3_str_appendall(createStr, " distance hidden, k hidden) "); + if (pkColumnName) { + sqlite3_str_appendall(createStr, "without rowid "); + } + zSql = sqlite3_str_finish(createStr); + if (!zSql) { + goto error; + } + rc = sqlite3_declare_vtab(db, zSql); + sqlite3_free((void *)zSql); + if (rc != SQLITE_OK) { + *pzErr = sqlite3_mprintf(VEC_CONSTRUCTOR_ERROR + "could not declare virtual table, '%s'", + sqlite3_errmsg(db)); + goto error; + } + + const char *schemaName = argv[1]; + const char *tableName = argv[2]; + + pNew->db = db; + pNew->pkIsText = pkColumnType == SQLITE_TEXT; + pNew->schemaName = sqlite3_mprintf("%s", schemaName); + if (!pNew->schemaName) { + goto error; + } + pNew->tableName = sqlite3_mprintf("%s", tableName); + if (!pNew->tableName) { + goto error; + } + pNew->shadowRowidsName = sqlite3_mprintf("%s_rowids", tableName); + if (!pNew->shadowRowidsName) { + goto error; + } + pNew->shadowChunksName = sqlite3_mprintf("%s_chunks", tableName); + if (!pNew->shadowChunksName) { + goto error; + } + pNew->numVectorColumns = numVectorColumns; + pNew->numPartitionColumns = numPartitionColumns; + pNew->numAuxiliaryColumns = numAuxiliaryColumns; + pNew->numMetadataColumns = numMetadataColumns; + + for (int i = 0; i < pNew->numVectorColumns; i++) { + pNew->shadowVectorChunksNames[i] = + sqlite3_mprintf("%s_vector_chunks%02d", tableName, i); + if (!pNew->shadowVectorChunksNames[i]) { + goto error; + } + } + for (int i = 0; i < pNew->numMetadataColumns; i++) { + pNew->shadowMetadataChunksNames[i] = + sqlite3_mprintf("%s_metadatachunks%02d", tableName, i); + if (!pNew->shadowMetadataChunksNames[i]) { + goto error; + } + } + pNew->chunk_size = chunk_size; + + // if xCreate, then create the necessary shadow tables + if (isCreate) { + sqlite3_stmt *stmt; + int rc; + + char * zCreateInfo = sqlite3_mprintf("CREATE TABLE "VEC0_SHADOW_INFO_NAME " (key text primary key, value any)", pNew->schemaName, pNew->tableName); + if(!zCreateInfo) { + goto error; + } + rc = sqlite3_prepare_v2(db, zCreateInfo, -1, &stmt, NULL); + + sqlite3_free((void *) zCreateInfo); + if ((rc != SQLITE_OK) || (sqlite3_step(stmt) != SQLITE_DONE)) { + // TODO(IMP) + sqlite3_finalize(stmt); + *pzErr = sqlite3_mprintf("Could not create '_info' shadow table: %s", + sqlite3_errmsg(db)); + goto error; + } + sqlite3_finalize(stmt); + + char * zSeedInfo = sqlite3_mprintf( + "INSERT INTO "VEC0_SHADOW_INFO_NAME "(key, value) VALUES " + "(?1, ?2), (?3, ?4), (?5, ?6), (?7, ?8) ", + pNew->schemaName, pNew->tableName + ); + if(!zSeedInfo) { + goto error; + } + rc = sqlite3_prepare_v2(db, zSeedInfo, -1, &stmt, NULL); + sqlite3_free((void *) zSeedInfo); + if (rc != SQLITE_OK) { + // TODO(IMP) + sqlite3_finalize(stmt); + *pzErr = sqlite3_mprintf("Could not seed '_info' shadow table: %s", + sqlite3_errmsg(db)); + goto error; + } + sqlite3_bind_text(stmt, 1, "CREATE_VERSION", -1, SQLITE_STATIC); + sqlite3_bind_text(stmt, 2, SQLITE_VEC_VERSION, -1, SQLITE_STATIC); + sqlite3_bind_text(stmt, 3, "CREATE_VERSION_MAJOR", -1, SQLITE_STATIC); + sqlite3_bind_int(stmt, 4, SQLITE_VEC_VERSION_MAJOR); + sqlite3_bind_text(stmt, 5, "CREATE_VERSION_MINOR", -1, SQLITE_STATIC); + sqlite3_bind_int(stmt, 6, SQLITE_VEC_VERSION_MINOR); + sqlite3_bind_text(stmt, 7, "CREATE_VERSION_PATCH", -1, SQLITE_STATIC); + sqlite3_bind_int(stmt, 8, SQLITE_VEC_VERSION_PATCH); + + if(sqlite3_step(stmt) != SQLITE_DONE) { + // TODO(IMP) + sqlite3_finalize(stmt); + *pzErr = sqlite3_mprintf("Could not seed '_info' shadow table: %s", + sqlite3_errmsg(db)); + goto error; + } + sqlite3_finalize(stmt); + + + + // create the _chunks shadow table + char *zCreateShadowChunks = NULL; + if(pNew->numPartitionColumns) { + sqlite3_str * s = sqlite3_str_new(NULL); + sqlite3_str_appendf(s, "CREATE TABLE " VEC0_SHADOW_CHUNKS_NAME "(", pNew->schemaName, pNew->tableName); + sqlite3_str_appendall(s, "chunk_id INTEGER PRIMARY KEY AUTOINCREMENT," "size INTEGER NOT NULL,"); + sqlite3_str_appendall(s, "sequence_id integer,"); + for(int i = 0; i < pNew->numPartitionColumns;i++) { + sqlite3_str_appendf(s, "partition%02d,", i); + } + sqlite3_str_appendall(s, "validity BLOB NOT NULL, rowids BLOB NOT NULL);"); + zCreateShadowChunks = sqlite3_str_finish(s); + }else { + zCreateShadowChunks = sqlite3_mprintf(VEC0_SHADOW_CHUNKS_CREATE, + pNew->schemaName, pNew->tableName); + } + if (!zCreateShadowChunks) { + goto error; + } + rc = sqlite3_prepare_v2(db, zCreateShadowChunks, -1, &stmt, 0); + sqlite3_free((void *)zCreateShadowChunks); + if ((rc != SQLITE_OK) || (sqlite3_step(stmt) != SQLITE_DONE)) { + // IMP: V17740_01811 + sqlite3_finalize(stmt); + *pzErr = sqlite3_mprintf("Could not create '_chunks' shadow table: %s", + sqlite3_errmsg(db)); + goto error; + } + sqlite3_finalize(stmt); + + // create the _rowids shadow table + char *zCreateShadowRowids; + if (pNew->pkIsText) { + // adds a "text unique not null" constraint to the id column + zCreateShadowRowids = sqlite3_mprintf(VEC0_SHADOW_ROWIDS_CREATE_PK_TEXT, + pNew->schemaName, pNew->tableName); + } else { + zCreateShadowRowids = sqlite3_mprintf(VEC0_SHADOW_ROWIDS_CREATE_BASIC, + pNew->schemaName, pNew->tableName); + } + if (!zCreateShadowRowids) { + goto error; + } + rc = sqlite3_prepare_v2(db, zCreateShadowRowids, -1, &stmt, 0); + sqlite3_free((void *)zCreateShadowRowids); + if ((rc != SQLITE_OK) || (sqlite3_step(stmt) != SQLITE_DONE)) { + // IMP: V11631_28470 + sqlite3_finalize(stmt); + *pzErr = sqlite3_mprintf("Could not create '_rowids' shadow table: %s", + sqlite3_errmsg(db)); + goto error; + } + sqlite3_finalize(stmt); + + for (int i = 0; i < pNew->numVectorColumns; i++) { + char *zSql = sqlite3_mprintf(VEC0_SHADOW_VECTOR_N_CREATE, + pNew->schemaName, pNew->tableName, i); + if (!zSql) { + goto error; + } + rc = sqlite3_prepare_v2(db, zSql, -1, &stmt, 0); + sqlite3_free((void *)zSql); + if ((rc != SQLITE_OK) || (sqlite3_step(stmt) != SQLITE_DONE)) { + // IMP: V25919_09989 + sqlite3_finalize(stmt); + *pzErr = sqlite3_mprintf( + "Could not create '_vector_chunks%02d' shadow table: %s", i, + sqlite3_errmsg(db)); + goto error; + } + sqlite3_finalize(stmt); + } + + for (int i = 0; i < pNew->numMetadataColumns; i++) { + char *zSql = sqlite3_mprintf("CREATE TABLE " VEC0_SHADOW_METADATA_N_NAME "(rowid PRIMARY KEY, data BLOB NOT NULL);", + pNew->schemaName, pNew->tableName, i); + if (!zSql) { + goto error; + } + rc = sqlite3_prepare_v2(db, zSql, -1, &stmt, 0); + sqlite3_free((void *)zSql); + if ((rc != SQLITE_OK) || (sqlite3_step(stmt) != SQLITE_DONE)) { + sqlite3_finalize(stmt); + *pzErr = sqlite3_mprintf( + "Could not create '_metata_chunks%02d' shadow table: %s", i, + sqlite3_errmsg(db)); + goto error; + } + sqlite3_finalize(stmt); + + if(pNew->metadata_columns[i].kind == VEC0_METADATA_COLUMN_KIND_TEXT) { + char *zSql = sqlite3_mprintf("CREATE TABLE " VEC0_SHADOW_METADATA_TEXT_DATA_NAME "(rowid PRIMARY KEY, data TEXT);", + pNew->schemaName, pNew->tableName, i); + if (!zSql) { + goto error; + } + rc = sqlite3_prepare_v2(db, zSql, -1, &stmt, 0); + sqlite3_free((void *)zSql); + if ((rc != SQLITE_OK) || (sqlite3_step(stmt) != SQLITE_DONE)) { + sqlite3_finalize(stmt); + *pzErr = sqlite3_mprintf( + "Could not create '_metadatatext%02d' shadow table: %s", i, + sqlite3_errmsg(db)); + goto error; + } + sqlite3_finalize(stmt); + + } + } + + if(pNew->numAuxiliaryColumns > 0) { + sqlite3_stmt * stmt; + sqlite3_str * s = sqlite3_str_new(NULL); + sqlite3_str_appendf(s, "CREATE TABLE " VEC0_SHADOW_AUXILIARY_NAME "( rowid integer PRIMARY KEY ", pNew->schemaName, pNew->tableName); + for(int i = 0; i < pNew->numAuxiliaryColumns; i++) { + sqlite3_str_appendf(s, ", value%02d", i); + } + sqlite3_str_appendall(s, ")"); + char *zSql = sqlite3_str_finish(s); + if(!zSql) { + goto error; + } + rc = sqlite3_prepare_v2(db, zSql, -1, &stmt, NULL); + if ((rc != SQLITE_OK) || (sqlite3_step(stmt) != SQLITE_DONE)) { + sqlite3_finalize(stmt); + *pzErr = sqlite3_mprintf( + "Could not create auxiliary shadow table: %s", + sqlite3_errmsg(db)); + + goto error; + } + sqlite3_finalize(stmt); + } + } + + *ppVtab = (sqlite3_vtab *)pNew; + return SQLITE_OK; + +error: + vec0_free(pNew); + return SQLITE_ERROR; +} + +static int vec0Create(sqlite3 *db, void *pAux, int argc, + const char *const *argv, sqlite3_vtab **ppVtab, + char **pzErr) { + return vec0_init(db, pAux, argc, argv, ppVtab, pzErr, true); +} +static int vec0Connect(sqlite3 *db, void *pAux, int argc, + const char *const *argv, sqlite3_vtab **ppVtab, + char **pzErr) { + return vec0_init(db, pAux, argc, argv, ppVtab, pzErr, false); +} + +static int vec0Disconnect(sqlite3_vtab *pVtab) { + vec0_vtab *p = (vec0_vtab *)pVtab; + vec0_free(p); + sqlite3_free(p); + return SQLITE_OK; +} +static int vec0Destroy(sqlite3_vtab *pVtab) { + vec0_vtab *p = (vec0_vtab *)pVtab; + sqlite3_stmt *stmt; + int rc; + const char *zSql; + + // Free up any sqlite3_stmt, otherwise DROPs on those tables will fail + vec0_free_resources(p); + + // TODO(test) later: can't evidence-of here, bc always gives "SQL logic error" instead of + // provided error + zSql = sqlite3_mprintf("DROP TABLE " VEC0_SHADOW_CHUNKS_NAME, p->schemaName, + p->tableName); + rc = sqlite3_prepare_v2(p->db, zSql, -1, &stmt, 0); + sqlite3_free((void *)zSql); + if ((rc != SQLITE_OK) || (sqlite3_step(stmt) != SQLITE_DONE)) { + rc = SQLITE_ERROR; + vtab_set_error(pVtab, "could not drop chunks shadow table"); + goto done; + } + sqlite3_finalize(stmt); + + zSql = sqlite3_mprintf("DROP TABLE " VEC0_SHADOW_INFO_NAME, p->schemaName, + p->tableName); + rc = sqlite3_prepare_v2(p->db, zSql, -1, &stmt, 0); + sqlite3_free((void *)zSql); + if ((rc != SQLITE_OK) || (sqlite3_step(stmt) != SQLITE_DONE)) { + rc = SQLITE_ERROR; + vtab_set_error(pVtab, "could not drop info shadow table"); + goto done; + } + sqlite3_finalize(stmt); + + zSql = sqlite3_mprintf("DROP TABLE " VEC0_SHADOW_ROWIDS_NAME, p->schemaName, + p->tableName); + rc = sqlite3_prepare_v2(p->db, zSql, -1, &stmt, 0); + sqlite3_free((void *)zSql); + if ((rc != SQLITE_OK) || (sqlite3_step(stmt) != SQLITE_DONE)) { + rc = SQLITE_ERROR; + goto done; + } + sqlite3_finalize(stmt); + + for (int i = 0; i < p->numVectorColumns; i++) { + zSql = sqlite3_mprintf("DROP TABLE \"%w\".\"%w\"", p->schemaName, + p->shadowVectorChunksNames[i]); + rc = sqlite3_prepare_v2(p->db, zSql, -1, &stmt, 0); + sqlite3_free((void *)zSql); + if ((rc != SQLITE_OK) || (sqlite3_step(stmt) != SQLITE_DONE)) { + rc = SQLITE_ERROR; + goto done; + } + sqlite3_finalize(stmt); + } + + if(p->numAuxiliaryColumns > 0) { + zSql = sqlite3_mprintf("DROP TABLE " VEC0_SHADOW_AUXILIARY_NAME, p->schemaName, p->tableName); + rc = sqlite3_prepare_v2(p->db, zSql, -1, &stmt, 0); + sqlite3_free((void *)zSql); + if ((rc != SQLITE_OK) || (sqlite3_step(stmt) != SQLITE_DONE)) { + rc = SQLITE_ERROR; + goto done; + } + sqlite3_finalize(stmt); + } + + + for (int i = 0; i < p->numMetadataColumns; i++) { + zSql = sqlite3_mprintf("DROP TABLE " VEC0_SHADOW_METADATA_N_NAME, p->schemaName,p->tableName, i); + rc = sqlite3_prepare_v2(p->db, zSql, -1, &stmt, 0); + sqlite3_free((void *)zSql); + if ((rc != SQLITE_OK) || (sqlite3_step(stmt) != SQLITE_DONE)) { + rc = SQLITE_ERROR; + goto done; + } + sqlite3_finalize(stmt); + + if(p->metadata_columns[i].kind == VEC0_METADATA_COLUMN_KIND_TEXT) { + zSql = sqlite3_mprintf("DROP TABLE " VEC0_SHADOW_METADATA_TEXT_DATA_NAME, p->schemaName,p->tableName, i); + rc = sqlite3_prepare_v2(p->db, zSql, -1, &stmt, 0); + sqlite3_free((void *)zSql); + if ((rc != SQLITE_OK) || (sqlite3_step(stmt) != SQLITE_DONE)) { + rc = SQLITE_ERROR; + goto done; + } + sqlite3_finalize(stmt); + } + } + + stmt = NULL; + rc = SQLITE_OK; + +done: + sqlite3_finalize(stmt); + vec0_free(p); + // If there was an error + if (rc == SQLITE_OK) { + sqlite3_free(p); + } + return rc; +} + +static int vec0Open(sqlite3_vtab *p, sqlite3_vtab_cursor **ppCursor) { + UNUSED_PARAMETER(p); + vec0_cursor *pCur; + pCur = sqlite3_malloc(sizeof(*pCur)); + if (pCur == 0) + return SQLITE_NOMEM; + memset(pCur, 0, sizeof(*pCur)); + *ppCursor = &pCur->base; + return SQLITE_OK; +} + +static int vec0Close(sqlite3_vtab_cursor *cur) { + vec0_cursor *pCur = (vec0_cursor *)cur; + vec0_cursor_clear(pCur); + sqlite3_free(pCur); + return SQLITE_OK; +} + +// All the different type of "values" provided to argv/argc in vec0Filter. +// These enums denote the use and purpose of all of them. +typedef enum { + // If any values are updated, please update the ARCHITECTURE.md docs accordingly! + + VEC0_IDXSTR_KIND_KNN_MATCH = '{', + VEC0_IDXSTR_KIND_KNN_K = '}', + VEC0_IDXSTR_KIND_KNN_ROWID_IN = '[', + VEC0_IDXSTR_KIND_KNN_PARTITON_CONSTRAINT = ']', + VEC0_IDXSTR_KIND_POINT_ID = '!', + VEC0_IDXSTR_KIND_METADATA_CONSTRAINT = '&', +} vec0_idxstr_kind; + +// The different SQLITE_INDEX_CONSTRAINT values that vec0 partition key columns +// support, but as characters that fit nicely in idxstr. +typedef enum { + // If any values are updated, please update the ARCHITECTURE.md docs accordingly! + + VEC0_PARTITION_OPERATOR_EQ = 'a', + VEC0_PARTITION_OPERATOR_GT = 'b', + VEC0_PARTITION_OPERATOR_LE = 'c', + VEC0_PARTITION_OPERATOR_LT = 'd', + VEC0_PARTITION_OPERATOR_GE = 'e', + VEC0_PARTITION_OPERATOR_NE = 'f', +} vec0_partition_operator; +typedef enum { + VEC0_METADATA_OPERATOR_EQ = 'a', + VEC0_METADATA_OPERATOR_GT = 'b', + VEC0_METADATA_OPERATOR_LE = 'c', + VEC0_METADATA_OPERATOR_LT = 'd', + VEC0_METADATA_OPERATOR_GE = 'e', + VEC0_METADATA_OPERATOR_NE = 'f', + VEC0_METADATA_OPERATOR_IN = 'g', +} vec0_metadata_operator; + +static int vec0BestIndex(sqlite3_vtab *pVTab, sqlite3_index_info *pIdxInfo) { + vec0_vtab *p = (vec0_vtab *)pVTab; + /** + * Possible query plans are: + * 1. KNN when: + * a) An `MATCH` op on vector column + * b) ORDER BY on distance column + * c) LIMIT + * d) rowid in (...) OPTIONAL + * 2. Point when: + * a) An `EQ` op on rowid column + * 3. else: fullscan + * + */ + int iMatchTerm = -1; + int iMatchVectorTerm = -1; + int iLimitTerm = -1; + int iRowidTerm = -1; + int iKTerm = -1; + int iRowidInTerm = -1; + int hasAuxConstraint = 0; + +#ifdef SQLITE_VEC_DEBUG + printf("pIdxInfo->nOrderBy=%d, pIdxInfo->nConstraint=%d\n", pIdxInfo->nOrderBy, pIdxInfo->nConstraint); +#endif + + for (int i = 0; i < pIdxInfo->nConstraint; i++) { + u8 vtabIn = 0; + +#if COMPILER_SUPPORTS_VTAB_IN + if (sqlite3_libversion_number() >= 3038000) { + vtabIn = sqlite3_vtab_in(pIdxInfo, i, -1); + } +#endif + +#ifdef SQLITE_VEC_DEBUG + printf("xBestIndex [%d] usable=%d iColumn=%d op=%d vtabin=%d\n", i, + pIdxInfo->aConstraint[i].usable, pIdxInfo->aConstraint[i].iColumn, + pIdxInfo->aConstraint[i].op, vtabIn); +#endif + if (!pIdxInfo->aConstraint[i].usable) + continue; + + int iColumn = pIdxInfo->aConstraint[i].iColumn; + int op = pIdxInfo->aConstraint[i].op; + + if (op == SQLITE_INDEX_CONSTRAINT_LIMIT) { + iLimitTerm = i; + } + if (op == SQLITE_INDEX_CONSTRAINT_MATCH && + vec0_column_idx_is_vector(p, iColumn)) { + if (iMatchTerm > -1) { + vtab_set_error( + pVTab, "only 1 MATCH operator is allowed in a single vec0 query"); + return SQLITE_ERROR; + } + iMatchTerm = i; + iMatchVectorTerm = vec0_column_idx_to_vector_idx(p, iColumn); + } + if (op == SQLITE_INDEX_CONSTRAINT_EQ && iColumn == VEC0_COLUMN_ID) { + if (vtabIn) { + if (iRowidInTerm != -1) { + vtab_set_error(pVTab, "only 1 'rowid in (..)' operator is allowed in " + "a single vec0 query"); + return SQLITE_ERROR; + } + iRowidInTerm = i; + + } else { + iRowidTerm = i; + } + } + if (op == SQLITE_INDEX_CONSTRAINT_EQ && iColumn == vec0_column_k_idx(p)) { + iKTerm = i; + } + if( + (op != SQLITE_INDEX_CONSTRAINT_LIMIT && op != SQLITE_INDEX_CONSTRAINT_OFFSET) + && vec0_column_idx_is_auxiliary(p, iColumn)) { + hasAuxConstraint = 1; + } + } + + sqlite3_str *idxStr = sqlite3_str_new(NULL); + int rc; + + if (iMatchTerm >= 0) { + if (iLimitTerm < 0 && iKTerm < 0) { + vtab_set_error( + pVTab, + "A LIMIT or 'k = ?' constraint is required on vec0 knn queries."); + rc = SQLITE_ERROR; + goto done; + } + if (iLimitTerm >= 0 && iKTerm >= 0) { + vtab_set_error(pVTab, "Only LIMIT or 'k =?' can be provided, not both"); + rc = SQLITE_ERROR; + goto done; + } + + if (pIdxInfo->nOrderBy) { + if (pIdxInfo->nOrderBy > 1) { + vtab_set_error(pVTab, "Only a single 'ORDER BY distance' clause is " + "allowed on vec0 KNN queries"); + rc = SQLITE_ERROR; + goto done; + } + if (pIdxInfo->aOrderBy[0].iColumn != vec0_column_distance_idx(p)) { + vtab_set_error(pVTab, + "Only a single 'ORDER BY distance' clause is allowed on " + "vec0 KNN queries, not on other columns"); + rc = SQLITE_ERROR; + goto done; + } + if (pIdxInfo->aOrderBy[0].desc) { + vtab_set_error( + pVTab, "Only ascending in ORDER BY distance clause is supported, " + "DESC is not supported yet."); + rc = SQLITE_ERROR; + goto done; + } + } + + if(hasAuxConstraint) { + // IMP: V25623_09693 + vtab_set_error(pVTab, "An illegal WHERE constraint was provided on a vec0 auxiliary column in a KNN query."); + rc = SQLITE_ERROR; + goto done; + } + + sqlite3_str_appendchar(idxStr, 1, VEC0_QUERY_PLAN_KNN); + + int argvIndex = 1; + pIdxInfo->aConstraintUsage[iMatchTerm].argvIndex = argvIndex++; + pIdxInfo->aConstraintUsage[iMatchTerm].omit = 1; + sqlite3_str_appendchar(idxStr, 1, VEC0_IDXSTR_KIND_KNN_MATCH); + sqlite3_str_appendchar(idxStr, 3, '_'); + + if (iLimitTerm >= 0) { + pIdxInfo->aConstraintUsage[iLimitTerm].argvIndex = argvIndex++; + pIdxInfo->aConstraintUsage[iLimitTerm].omit = 1; + } else { + pIdxInfo->aConstraintUsage[iKTerm].argvIndex = argvIndex++; + pIdxInfo->aConstraintUsage[iKTerm].omit = 1; + } + sqlite3_str_appendchar(idxStr, 1, VEC0_IDXSTR_KIND_KNN_K); + sqlite3_str_appendchar(idxStr, 3, '_'); + +#if COMPILER_SUPPORTS_VTAB_IN + if (iRowidInTerm >= 0) { + // already validated as >= SQLite 3.38 bc iRowidInTerm is only >= 0 when + // vtabIn == 1 + sqlite3_vtab_in(pIdxInfo, iRowidInTerm, 1); + pIdxInfo->aConstraintUsage[iRowidInTerm].argvIndex = argvIndex++; + pIdxInfo->aConstraintUsage[iRowidInTerm].omit = 1; + sqlite3_str_appendchar(idxStr, 1, VEC0_IDXSTR_KIND_KNN_ROWID_IN); + sqlite3_str_appendchar(idxStr, 3, '_'); + } +#endif + + for (int i = 0; i < pIdxInfo->nConstraint; i++) { + if (!pIdxInfo->aConstraint[i].usable) + continue; + + int iColumn = pIdxInfo->aConstraint[i].iColumn; + int op = pIdxInfo->aConstraint[i].op; + if(op == SQLITE_INDEX_CONSTRAINT_LIMIT || op == SQLITE_INDEX_CONSTRAINT_OFFSET) { + continue; + } + if(!vec0_column_idx_is_partition(p, iColumn)) { + continue; + } + + int partition_idx = vec0_column_idx_to_partition_idx(p, iColumn); + char value = 0; + + switch(op) { + case SQLITE_INDEX_CONSTRAINT_EQ: { + value = VEC0_PARTITION_OPERATOR_EQ; + break; + } + case SQLITE_INDEX_CONSTRAINT_GT: { + value = VEC0_PARTITION_OPERATOR_GT; + break; + } + case SQLITE_INDEX_CONSTRAINT_LE: { + value = VEC0_PARTITION_OPERATOR_LE; + break; + } + case SQLITE_INDEX_CONSTRAINT_LT: { + value = VEC0_PARTITION_OPERATOR_LT; + break; + } + case SQLITE_INDEX_CONSTRAINT_GE: { + value = VEC0_PARTITION_OPERATOR_GE; + break; + } + case SQLITE_INDEX_CONSTRAINT_NE: { + value = VEC0_PARTITION_OPERATOR_NE; + break; + } + } + + if(value) { + pIdxInfo->aConstraintUsage[i].argvIndex = argvIndex++; + pIdxInfo->aConstraintUsage[i].omit = 1; + sqlite3_str_appendchar(idxStr, 1, VEC0_IDXSTR_KIND_KNN_PARTITON_CONSTRAINT); + sqlite3_str_appendchar(idxStr, 1, 'A' + partition_idx); + sqlite3_str_appendchar(idxStr, 1, value); + sqlite3_str_appendchar(idxStr, 1, '_'); + } + + } + + for (int i = 0; i < pIdxInfo->nConstraint; i++) { + if (!pIdxInfo->aConstraint[i].usable) + continue; + + int iColumn = pIdxInfo->aConstraint[i].iColumn; + int op = pIdxInfo->aConstraint[i].op; + if(op == SQLITE_INDEX_CONSTRAINT_LIMIT || op == SQLITE_INDEX_CONSTRAINT_OFFSET) { + continue; + } + if(!vec0_column_idx_is_metadata(p, iColumn)) { + continue; + } + + int metadata_idx = vec0_column_idx_to_metadata_idx(p, iColumn); + char value = 0; + + switch(op) { + case SQLITE_INDEX_CONSTRAINT_EQ: { + int vtabIn = 0; + #if COMPILER_SUPPORTS_VTAB_IN + if (sqlite3_libversion_number() >= 3038000) { + vtabIn = sqlite3_vtab_in(pIdxInfo, i, -1); + } + if(vtabIn) { + switch(p->metadata_columns[metadata_idx].kind) { + case VEC0_METADATA_COLUMN_KIND_FLOAT: + case VEC0_METADATA_COLUMN_KIND_BOOLEAN: { + // IMP: V15248_32086 + rc = SQLITE_ERROR; + vtab_set_error(pVTab, "'xxx in (...)' is only available on INTEGER or TEXT metadata columns."); + goto done; + break; + } + case VEC0_METADATA_COLUMN_KIND_INTEGER: + case VEC0_METADATA_COLUMN_KIND_TEXT: { + break; + } + } + value = VEC0_METADATA_OPERATOR_IN; + sqlite3_vtab_in(pIdxInfo, i, 1); + }else + #endif + { + value = VEC0_PARTITION_OPERATOR_EQ; + } + break; + } + case SQLITE_INDEX_CONSTRAINT_GT: { + value = VEC0_METADATA_OPERATOR_GT; + break; + } + case SQLITE_INDEX_CONSTRAINT_LE: { + value = VEC0_METADATA_OPERATOR_LE; + break; + } + case SQLITE_INDEX_CONSTRAINT_LT: { + value = VEC0_METADATA_OPERATOR_LT; + break; + } + case SQLITE_INDEX_CONSTRAINT_GE: { + value = VEC0_METADATA_OPERATOR_GE; + break; + } + case SQLITE_INDEX_CONSTRAINT_NE: { + value = VEC0_METADATA_OPERATOR_NE; + break; + } + default: { + // IMP: V16511_00582 + rc = SQLITE_ERROR; + vtab_set_error(pVTab, + "An illegal WHERE constraint was provided on a vec0 metadata column in a KNN query. " + "Only one of EQUALS, GREATER_THAN, LESS_THAN_OR_EQUAL, LESS_THAN, GREATER_THAN_OR_EQUAL, NOT_EQUALS is allowed." + ); + goto done; + } + } + + if(p->metadata_columns[metadata_idx].kind == VEC0_METADATA_COLUMN_KIND_BOOLEAN) { + if(!(value == VEC0_METADATA_OPERATOR_EQ || value == VEC0_METADATA_OPERATOR_NE)) { + // IMP: V10145_26984 + rc = SQLITE_ERROR; + vtab_set_error(pVTab, "ONLY EQUALS (=) or NOT_EQUALS (!=) operators are allowed on boolean metadata columns."); + goto done; + } + } + + pIdxInfo->aConstraintUsage[i].argvIndex = argvIndex++; + pIdxInfo->aConstraintUsage[i].omit = 1; + sqlite3_str_appendchar(idxStr, 1, VEC0_IDXSTR_KIND_METADATA_CONSTRAINT); + sqlite3_str_appendchar(idxStr, 1, 'A' + metadata_idx); + sqlite3_str_appendchar(idxStr, 1, value); + sqlite3_str_appendchar(idxStr, 1, '_'); + + } + + + + pIdxInfo->idxNum = iMatchVectorTerm; + pIdxInfo->estimatedCost = 30.0; + pIdxInfo->estimatedRows = 10; + + } else if (iRowidTerm >= 0) { + sqlite3_str_appendchar(idxStr, 1, VEC0_QUERY_PLAN_POINT); + pIdxInfo->aConstraintUsage[iRowidTerm].argvIndex = 1; + pIdxInfo->aConstraintUsage[iRowidTerm].omit = 1; + sqlite3_str_appendchar(idxStr, 1, VEC0_IDXSTR_KIND_POINT_ID); + sqlite3_str_appendchar(idxStr, 3, '_'); + pIdxInfo->idxNum = pIdxInfo->colUsed; + pIdxInfo->estimatedCost = 10.0; + pIdxInfo->estimatedRows = 1; + } else { + sqlite3_str_appendchar(idxStr, 1, VEC0_QUERY_PLAN_FULLSCAN); + pIdxInfo->estimatedCost = 3000000.0; + pIdxInfo->estimatedRows = 100000; + } + pIdxInfo->idxStr = sqlite3_str_finish(idxStr); + idxStr = NULL; + if (!pIdxInfo->idxStr) { + rc = SQLITE_OK; + goto done; + } + pIdxInfo->needToFreeIdxStr = 1; + + + rc = SQLITE_OK; + + done: + if(idxStr) { + sqlite3_str_finish(idxStr); + } + return rc; +} + +// forward delcaration bc vec0Filter uses it +static int vec0Next(sqlite3_vtab_cursor *cur); + +void merge_sorted_lists(f32 *a, i64 *a_rowids, i64 a_length, f32 *b, + i64 *b_rowids, i32 *b_top_idxs, i64 b_length, f32 *out, + i64 *out_rowids, i64 out_length, i64 *out_used) { + // assert((a_length >= out_length) || (b_length >= out_length)); + i64 ptrA = 0; + i64 ptrB = 0; + for (int i = 0; i < out_length; i++) { + if ((ptrA >= a_length) && (ptrB >= b_length)) { + *out_used = i; + return; + } + if (ptrA >= a_length) { + out[i] = b[b_top_idxs[ptrB]]; + out_rowids[i] = b_rowids[b_top_idxs[ptrB]]; + ptrB++; + } else if (ptrB >= b_length) { + out[i] = a[ptrA]; + out_rowids[i] = a_rowids[ptrA]; + ptrA++; + } else { + if (a[ptrA] <= b[b_top_idxs[ptrB]]) { + out[i] = a[ptrA]; + out_rowids[i] = a_rowids[ptrA]; + ptrA++; + } else { + out[i] = b[b_top_idxs[ptrB]]; + out_rowids[i] = b_rowids[b_top_idxs[ptrB]]; + ptrB++; + } + } + } + + *out_used = out_length; +} + +u8 *bitmap_new(i32 n) { + assert(n % 8 == 0); + u8 *p = sqlite3_malloc(n * sizeof(u8) / CHAR_BIT); + if (p) { + memset(p, 0, n * sizeof(u8) / CHAR_BIT); + } + return p; +} +u8 *bitmap_new_from(i32 n, u8 *from) { + assert(n % 8 == 0); + u8 *p = sqlite3_malloc(n * sizeof(u8) / CHAR_BIT); + if (p) { + memcpy(p, from, n / CHAR_BIT); + } + return p; +} + +void bitmap_copy(u8 *base, u8 *from, i32 n) { + assert(n % 8 == 0); + memcpy(base, from, n / CHAR_BIT); +} + +void bitmap_and_inplace(u8 *base, u8 *other, i32 n) { + assert((n % 8) == 0); + for (int i = 0; i < n / CHAR_BIT; i++) { + base[i] = base[i] & other[i]; + } +} + +void bitmap_set(u8 *bitmap, i32 position, int value) { + if (value) { + bitmap[position / CHAR_BIT] |= 1 << (position % CHAR_BIT); + } else { + bitmap[position / CHAR_BIT] &= ~(1 << (position % CHAR_BIT)); + } +} + +int bitmap_get(u8 *bitmap, i32 position) { + return (((bitmap[position / CHAR_BIT]) >> (position % CHAR_BIT)) & 1); +} + +void bitmap_clear(u8 *bitmap, i32 n) { + assert((n % 8) == 0); + memset(bitmap, 0, n / CHAR_BIT); +} + +void bitmap_fill(u8 *bitmap, i32 n) { + assert((n % 8) == 0); + memset(bitmap, 0xFF, n / CHAR_BIT); +} + +/** + * @brief Finds the minimum k items in distances, and writes the indicies to + * out. + * + * @param distances input f32 array of size n, the items to consider. + * @param n: size of distances array. + * @param out: Output array of size k, will contain at most k element indicies + * @param k: Size of output array + * @return int + */ +int min_idx(const f32 *distances, i32 n, u8 *candidates, i32 *out, i32 k, + u8 *bTaken, i32 *k_used) { + assert(k > 0); + assert(k <= n); + + bitmap_clear(bTaken, n); + + for (int ik = 0; ik < k; ik++) { + int min_idx = 0; + while (min_idx < n && + (bitmap_get(bTaken, min_idx) || !bitmap_get(candidates, min_idx))) { + min_idx++; + } + if (min_idx >= n) { + *k_used = ik; + return SQLITE_OK; + } + + for (int i = 0; i < n; i++) { + if (distances[i] <= distances[min_idx] && !bitmap_get(bTaken, i) && + (bitmap_get(candidates, i))) { + min_idx = i; + } + } + + out[ik] = min_idx; + bitmap_set(bTaken, min_idx, 1); + } + *k_used = k; + return SQLITE_OK; +} + +int vec0_get_metadata_text_long_value( + vec0_vtab * p, + sqlite3_stmt ** stmt, + int metadata_idx, + i64 rowid, + int *n, + char ** s) { + int rc; + if(!(*stmt)) { + const char * zSql = sqlite3_mprintf("select data from " VEC0_SHADOW_METADATA_TEXT_DATA_NAME " where rowid = ?", p->schemaName, p->tableName, metadata_idx); + if(!zSql) { + rc = SQLITE_NOMEM; + goto done; + } + rc = sqlite3_prepare_v2(p->db, zSql, -1, stmt, NULL); + sqlite3_free( (void *) zSql); + if(rc != SQLITE_OK) { + goto done; + } + } + + sqlite3_reset(*stmt); + sqlite3_bind_int64(*stmt, 1, rowid); + rc = sqlite3_step(*stmt); + if(rc != SQLITE_ROW) { + rc = SQLITE_ERROR; + goto done; + } + *s = (char *) sqlite3_column_text(*stmt, 0); + *n = sqlite3_column_bytes(*stmt, 0); + rc = SQLITE_OK; + done: + return rc; +} + +/** + * @brief Crete at "iterator" (sqlite3_stmt) of chunks with the given constraints + * + * Any VEC0_IDXSTR_KIND_KNN_PARTITON_CONSTRAINT values in idxStr/argv will be applied + * as WHERE constraints in the underlying stmt SQL, and any consumer of the stmt + * can freely step through the stmt with all constraints satisfied. + * + * @param p - vec0_vtab + * @param idxStr - the xBestIndex/xFilter idxstr containing VEC0_IDXSTR values + * @param argc - number of argv values from xFilter + * @param argv - array of sqlite3_value from xFilter + * @param outStmt - output sqlite3_stmt of chunks with all filters applied + * @return int SQLITE_OK on success, error code otherwise + */ +int vec0_chunks_iter(vec0_vtab * p, const char * idxStr, int argc, sqlite3_value ** argv, sqlite3_stmt** outStmt) { + // always null terminated, enforced by SQLite + int idxStrLength = strlen(idxStr); + // "1" refers to the initial vec0_query_plan char, 4 is the number of chars per "element" + int numValueEntries = (idxStrLength-1) / 4; + assert(argc == numValueEntries); + + int rc; + sqlite3_str * s = sqlite3_str_new(NULL); + sqlite3_str_appendf(s, "select chunk_id, validity, rowids " + " from " VEC0_SHADOW_CHUNKS_NAME, + p->schemaName, p->tableName); + + int appendedWhere = 0; + for(int i = 0; i < numValueEntries; i++) { + int idx = 1 + (i * 4); + char kind = idxStr[idx + 0]; + if(kind != VEC0_IDXSTR_KIND_KNN_PARTITON_CONSTRAINT) { + continue; + } + + int partition_idx = idxStr[idx + 1] - 'A'; + int operator = idxStr[idx + 2]; + // idxStr[idx + 3] is just null, a '_' placeholder + + if(!appendedWhere) { + sqlite3_str_appendall(s, " WHERE "); + appendedWhere = 1; + }else { + sqlite3_str_appendall(s, " AND "); + } + switch(operator) { + case VEC0_PARTITION_OPERATOR_EQ: + sqlite3_str_appendf(s, " partition%02d = ? ", partition_idx); + break; + case VEC0_PARTITION_OPERATOR_GT: + sqlite3_str_appendf(s, " partition%02d > ? ", partition_idx); + break; + case VEC0_PARTITION_OPERATOR_LE: + sqlite3_str_appendf(s, " partition%02d <= ? ", partition_idx); + break; + case VEC0_PARTITION_OPERATOR_LT: + sqlite3_str_appendf(s, " partition%02d < ? ", partition_idx); + break; + case VEC0_PARTITION_OPERATOR_GE: + sqlite3_str_appendf(s, " partition%02d >= ? ", partition_idx); + break; + case VEC0_PARTITION_OPERATOR_NE: + sqlite3_str_appendf(s, " partition%02d != ? ", partition_idx); + break; + default: { + char * zSql = sqlite3_str_finish(s); + sqlite3_free(zSql); + return SQLITE_ERROR; + } + + } + + } + + char *zSql = sqlite3_str_finish(s); + if (!zSql) { + return SQLITE_NOMEM; + } + + rc = sqlite3_prepare_v2(p->db, zSql, -1, outStmt, NULL); + sqlite3_free(zSql); + if(rc != SQLITE_OK) { + return rc; + } + + int n = 1; + for(int i = 0; i < numValueEntries; i++) { + int idx = 1 + (i * 4); + char kind = idxStr[idx + 0]; + if(kind != VEC0_IDXSTR_KIND_KNN_PARTITON_CONSTRAINT) { + continue; + } + sqlite3_bind_value(*outStmt, n++, argv[i]); + } + + return rc; +} + +// a single `xxx in (...)` constraint on a metadata column. TEXT or INTEGER only for now. +struct Vec0MetadataIn{ + // index of argv[i]` the constraint is on + int argv_idx; + // metadata column index of the constraint, derived from idxStr + argv_idx + int metadata_idx; + // array of the copied `(...)` values from sqlite3_vtab_in_first()/sqlite3_vtab_in_next() + struct Array array; +}; + +// Array elements for `xxx in (...)` values for a text column. basically just a string +struct Vec0MetadataInTextEntry { + int n; + char * zString; +}; + + +int vec0_metadata_filter_text(vec0_vtab * p, sqlite3_value * value, const void * buffer, int size, vec0_metadata_operator op, u8* b, int metadata_idx, int chunk_rowid, struct Array * aMetadataIn, int argv_idx) { + int rc; + sqlite3_stmt * stmt = NULL; + i64 * rowids = NULL; + sqlite3_blob * rowidsBlob; + const char * sTarget = (const char *) sqlite3_value_text(value); + int nTarget = sqlite3_value_bytes(value); + + + // TODO(perf): only text metadata news the rowids BLOB. Make it so that + // rowids BLOB is re-used when multiple fitlers on text columns, + // ex "name BETWEEN 'a' and 'b'"" + rc = sqlite3_blob_open(p->db, p->schemaName, p->shadowChunksName, "rowids", chunk_rowid, 0, &rowidsBlob); + if(rc != SQLITE_OK) { + return rc; + } + assert(sqlite3_blob_bytes(rowidsBlob) % sizeof(i64) == 0); + assert((sqlite3_blob_bytes(rowidsBlob) / sizeof(i64)) == size); + + rowids = sqlite3_malloc(sqlite3_blob_bytes(rowidsBlob)); + if(!rowids) { + sqlite3_blob_close(rowidsBlob); + return SQLITE_NOMEM; + } + + rc = sqlite3_blob_read(rowidsBlob, rowids, sqlite3_blob_bytes(rowidsBlob), 0); + if(rc != SQLITE_OK) { + sqlite3_blob_close(rowidsBlob); + return rc; + } + sqlite3_blob_close(rowidsBlob); + + switch(op) { + int nPrefix; + char * sPrefix; + char *sFull; + int nFull; + u8 * view; + case VEC0_METADATA_OPERATOR_EQ: { + for(int i = 0; i < size; i++) { + view = &((u8*) buffer)[i * VEC0_METADATA_TEXT_VIEW_BUFFER_LENGTH]; + nPrefix = ((int*) view)[0]; + sPrefix = (char *) &view[4]; + + // for EQ the text lengths must match + if(nPrefix != nTarget) { + bitmap_set(b, i, 0); + continue; + } + int cmpPrefix = strncmp(sPrefix, sTarget, min(nPrefix, VEC0_METADATA_TEXT_VIEW_DATA_LENGTH)); + + // for short strings, use the prefix comparison direclty + if(nPrefix <= VEC0_METADATA_TEXT_VIEW_DATA_LENGTH) { + bitmap_set(b, i, cmpPrefix == 0); + continue; + } + // for EQ on longs strings, the prefix must match + if(cmpPrefix) { + bitmap_set(b, i, 0); + continue; + } + // consult the full string + rc = vec0_get_metadata_text_long_value(p, &stmt, metadata_idx, rowids[i], &nFull, &sFull); + if(rc != SQLITE_OK) { + goto done; + } + if(nPrefix != nFull) { + rc = SQLITE_ERROR; + goto done; + } + bitmap_set(b, i, strncmp(sFull, sTarget, nFull) == 0); + } + break; + } + case VEC0_METADATA_OPERATOR_NE: { + for(int i = 0; i < size; i++) { + view = &((u8*) buffer)[i * VEC0_METADATA_TEXT_VIEW_BUFFER_LENGTH]; + nPrefix = ((int*) view)[0]; + sPrefix = (char *) &view[4]; + + // for NE if text lengths dont match, it never will + if(nPrefix != nTarget) { + bitmap_set(b, i, 1); + continue; + } + + int cmpPrefix = strncmp(sPrefix, sTarget, min(nPrefix, VEC0_METADATA_TEXT_VIEW_DATA_LENGTH)); + + // for short strings, use the prefix comparison direclty + if(nPrefix <= VEC0_METADATA_TEXT_VIEW_DATA_LENGTH) { + bitmap_set(b, i, cmpPrefix != 0); + continue; + } + // for NE on longs strings, if prefixes dont match, then long string wont + if(cmpPrefix) { + bitmap_set(b, i, 1); + continue; + } + // consult the full string + rc = vec0_get_metadata_text_long_value(p, &stmt, metadata_idx, rowids[i], &nFull, &sFull); + if(rc != SQLITE_OK) { + goto done; + } + if(nPrefix != nFull) { + rc = SQLITE_ERROR; + goto done; + } + bitmap_set(b, i, strncmp(sFull, sTarget, nFull) != 0); + } + break; + } + case VEC0_METADATA_OPERATOR_GT: { + for(int i = 0; i < size; i++) { + view = &((u8*) buffer)[i * VEC0_METADATA_TEXT_VIEW_BUFFER_LENGTH]; + nPrefix = ((int*) view)[0]; + sPrefix = (char *) &view[4]; + int cmpPrefix = strncmp(sPrefix, sTarget, min(min(nPrefix, VEC0_METADATA_TEXT_VIEW_DATA_LENGTH), nTarget)); + + if(nPrefix < VEC0_METADATA_TEXT_VIEW_DATA_LENGTH) { + // if prefix match, check which is longer + if(cmpPrefix == 0) { + bitmap_set(b, i, nPrefix > nTarget); + } + else { + bitmap_set(b, i, cmpPrefix > 0); + } + continue; + } + // TODO(perf): may not need to compare full text in some cases + + rc = vec0_get_metadata_text_long_value(p, &stmt, metadata_idx, rowids[i], &nFull, &sFull); + if(rc != SQLITE_OK) { + goto done; + } + if(nPrefix != nFull) { + rc = SQLITE_ERROR; + goto done; + } + bitmap_set(b, i, strncmp(sFull, sTarget, nFull) > 0); + } + break; + } + case VEC0_METADATA_OPERATOR_GE: { + for(int i = 0; i < size; i++) { + view = &((u8*) buffer)[i * VEC0_METADATA_TEXT_VIEW_BUFFER_LENGTH]; + nPrefix = ((int*) view)[0]; + sPrefix = (char *) &view[4]; + int cmpPrefix = strncmp(sPrefix, sTarget, min(min(nPrefix, VEC0_METADATA_TEXT_VIEW_DATA_LENGTH), nTarget)); + + if(nPrefix < VEC0_METADATA_TEXT_VIEW_DATA_LENGTH) { + // if prefix match, check which is longer + if(cmpPrefix == 0) { + bitmap_set(b, i, nPrefix >= nTarget); + } + else { + bitmap_set(b, i, cmpPrefix >= 0); + } + continue; + } + // TODO(perf): may not need to compare full text in some cases + + rc = vec0_get_metadata_text_long_value(p, &stmt, metadata_idx, rowids[i], &nFull, &sFull); + if(rc != SQLITE_OK) { + goto done; + } + if(nPrefix != nFull) { + rc = SQLITE_ERROR; + goto done; + } + bitmap_set(b, i, strncmp(sFull, sTarget, nFull) >= 0); + } + break; + } + case VEC0_METADATA_OPERATOR_LE: { + for(int i = 0; i < size; i++) { + view = &((u8*) buffer)[i * VEC0_METADATA_TEXT_VIEW_BUFFER_LENGTH]; + nPrefix = ((int*) view)[0]; + sPrefix = (char *) &view[4]; + int cmpPrefix = strncmp(sPrefix, sTarget, min(min(nPrefix, VEC0_METADATA_TEXT_VIEW_DATA_LENGTH), nTarget)); + + if(nPrefix < VEC0_METADATA_TEXT_VIEW_DATA_LENGTH) { + // if prefix match, check which is longer + if(cmpPrefix == 0) { + bitmap_set(b, i, nPrefix <= nTarget); + } + else { + bitmap_set(b, i, cmpPrefix <= 0); + } + continue; + } + // TODO(perf): may not need to compare full text in some cases + + rc = vec0_get_metadata_text_long_value(p, &stmt, metadata_idx, rowids[i], &nFull, &sFull); + if(rc != SQLITE_OK) { + goto done; + } + if(nPrefix != nFull) { + rc = SQLITE_ERROR; + goto done; + } + bitmap_set(b, i, strncmp(sFull, sTarget, nFull) <= 0); + } + break; + } + case VEC0_METADATA_OPERATOR_LT: { + for(int i = 0; i < size; i++) { + view = &((u8*) buffer)[i * VEC0_METADATA_TEXT_VIEW_BUFFER_LENGTH]; + nPrefix = ((int*) view)[0]; + sPrefix = (char *) &view[4]; + int cmpPrefix = strncmp(sPrefix, sTarget, min(min(nPrefix, VEC0_METADATA_TEXT_VIEW_DATA_LENGTH), nTarget)); + + if(nPrefix < VEC0_METADATA_TEXT_VIEW_DATA_LENGTH) { + // if prefix match, check which is longer + if(cmpPrefix == 0) { + bitmap_set(b, i, nPrefix < nTarget); + } + else { + bitmap_set(b, i, cmpPrefix < 0); + } + continue; + } + // TODO(perf): may not need to compare full text in some cases + + rc = vec0_get_metadata_text_long_value(p, &stmt, metadata_idx, rowids[i], &nFull, &sFull); + if(rc != SQLITE_OK) { + goto done; + } + if(nPrefix != nFull) { + rc = SQLITE_ERROR; + goto done; + } + bitmap_set(b, i, strncmp(sFull, sTarget, nFull) < 0); + } + break; + } + + case VEC0_METADATA_OPERATOR_IN: { + size_t metadataInIdx = -1; + for(size_t i = 0; i < aMetadataIn->length; i++) { + struct Vec0MetadataIn * metadataIn = &(((struct Vec0MetadataIn *) aMetadataIn->z)[i]); + if(metadataIn->argv_idx == argv_idx) { + metadataInIdx = i; + break; + } + } + if(metadataInIdx < 0) { + rc = SQLITE_ERROR; + goto done; + } + + struct Vec0MetadataIn * metadataIn = &((struct Vec0MetadataIn *) aMetadataIn->z)[metadataInIdx]; + struct Array * aTarget = &(metadataIn->array); + + + int nPrefix; + char * sPrefix; + char *sFull; + int nFull; + u8 * view; + for(int i = 0; i < size; i++) { + view = &((u8*) buffer)[i * VEC0_METADATA_TEXT_VIEW_BUFFER_LENGTH]; + nPrefix = ((int*) view)[0]; + sPrefix = (char *) &view[4]; + for(size_t target_idx = 0; target_idx < aTarget->length; target_idx++) { + struct Vec0MetadataInTextEntry * entry = &(((struct Vec0MetadataInTextEntry*)aTarget->z)[target_idx]); + if(entry->n != nPrefix) { + continue; + } + int cmpPrefix = strncmp(sPrefix, entry->zString, min(nPrefix, VEC0_METADATA_TEXT_VIEW_DATA_LENGTH)); + if(nPrefix <= VEC0_METADATA_TEXT_VIEW_DATA_LENGTH) { + if(cmpPrefix == 0) { + bitmap_set(b, i, 1); + break; + } + continue; + } + if(cmpPrefix) { + continue; + } + + rc = vec0_get_metadata_text_long_value(p, &stmt, metadata_idx, rowids[i], &nFull, &sFull); + if(rc != SQLITE_OK) { + goto done; + } + if(nPrefix != nFull) { + rc = SQLITE_ERROR; + goto done; + } + if(strncmp(sFull, entry->zString, nFull) == 0) { + bitmap_set(b, i, 1); + break; + } + } + } + break; + } + + } + rc = SQLITE_OK; + + done: + sqlite3_finalize(stmt); + sqlite3_free(rowids); + return rc; + +} + +/** + * @brief Fill in bitmap of chunk values, whether or not the values match a metadata constraint + * + * @param p vec0_vtab + * @param metadata_idx index of the metatadata column to perfrom constraints on + * @param value sqlite3_value of the constraints value + * @param blob sqlite3_blob that is already opened on the metdata column's shadow chunk table + * @param chunk_rowid rowid of the chunk to calculate on + * @param b pre-allocated and zero'd out bitmap to write results to + * @param size size of the chunk + * @return int SQLITE_OK on success, error code otherwise + */ +int vec0_set_metadata_filter_bitmap( + vec0_vtab *p, + int metadata_idx, + vec0_metadata_operator op, + sqlite3_value * value, + sqlite3_blob * blob, + i64 chunk_rowid, + u8* b, + int size, + struct Array * aMetadataIn, int argv_idx) { + // TODO: shouldn't this skip in-valid entries from the chunk's validity bitmap? + + int rc; + rc = sqlite3_blob_reopen(blob, chunk_rowid); + if(rc != SQLITE_OK) { + return rc; + } + + vec0_metadata_column_kind kind = p->metadata_columns[metadata_idx].kind; + int szMatch = 0; + int blobSize = sqlite3_blob_bytes(blob); + switch(kind) { + case VEC0_METADATA_COLUMN_KIND_BOOLEAN: { + szMatch = blobSize == size / CHAR_BIT; + break; + } + case VEC0_METADATA_COLUMN_KIND_INTEGER: { + szMatch = blobSize == size * sizeof(i64); + break; + } + case VEC0_METADATA_COLUMN_KIND_FLOAT: { + szMatch = blobSize == size * sizeof(double); + break; + } + case VEC0_METADATA_COLUMN_KIND_TEXT: { + szMatch = blobSize == size * VEC0_METADATA_TEXT_VIEW_BUFFER_LENGTH; + break; + } + } + if(!szMatch) { + return SQLITE_ERROR; + } + void * buffer = sqlite3_malloc(blobSize); + if(!buffer) { + return SQLITE_NOMEM; + } + rc = sqlite3_blob_read(blob, buffer, blobSize, 0); + if(rc != SQLITE_OK) { + goto done; + } + switch(kind) { + case VEC0_METADATA_COLUMN_KIND_BOOLEAN: { + int target = sqlite3_value_int(value); + if( (target && op == VEC0_METADATA_OPERATOR_EQ) || (!target && op == VEC0_METADATA_OPERATOR_NE)) { + for(int i = 0; i < size; i++) { bitmap_set(b, i, bitmap_get((u8*) buffer, i)); } + } + else { + for(int i = 0; i < size; i++) { bitmap_set(b, i, !bitmap_get((u8*) buffer, i)); } + } + break; + } + case VEC0_METADATA_COLUMN_KIND_INTEGER: { + i64 * array = (i64*) buffer; + i64 target = sqlite3_value_int64(value); + switch(op) { + case VEC0_METADATA_OPERATOR_EQ: { + for(int i = 0; i < size; i++) { bitmap_set(b, i, array[i] == target); } + break; + } + case VEC0_METADATA_OPERATOR_GT: { + for(int i = 0; i < size; i++) { bitmap_set(b, i, array[i] > target); } + break; + } + case VEC0_METADATA_OPERATOR_LE: { + for(int i = 0; i < size; i++) { bitmap_set(b, i, array[i] <= target); } + break; + } + case VEC0_METADATA_OPERATOR_LT: { + for(int i = 0; i < size; i++) { bitmap_set(b, i, array[i] < target); } + break; + } + case VEC0_METADATA_OPERATOR_GE: { + for(int i = 0; i < size; i++) { bitmap_set(b, i, array[i] >= target); } + break; + } + case VEC0_METADATA_OPERATOR_NE: { + for(int i = 0; i < size; i++) { bitmap_set(b, i, array[i] != target); } + break; + } + case VEC0_METADATA_OPERATOR_IN: { + int metadataInIdx = -1; + for(size_t i = 0; i < aMetadataIn->length; i++) { + struct Vec0MetadataIn * metadataIn = &((struct Vec0MetadataIn *) aMetadataIn->z)[i]; + if(metadataIn->argv_idx == argv_idx) { + metadataInIdx = i; + break; + } + } + if(metadataInIdx < 0) { + rc = SQLITE_ERROR; + goto done; + } + struct Vec0MetadataIn * metadataIn = &((struct Vec0MetadataIn *) aMetadataIn->z)[metadataInIdx]; + struct Array * aTarget = &(metadataIn->array); + + for(int i = 0; i < size; i++) { + for(size_t target_idx = 0; target_idx < aTarget->length; target_idx++) { + if( ((i64*)aTarget->z)[target_idx] == array[i]) { + bitmap_set(b, i, 1); + break; + } + } + } + break; + } + } + break; + } + case VEC0_METADATA_COLUMN_KIND_FLOAT: { + double * array = (double*) buffer; + double target = sqlite3_value_double(value); + switch(op) { + case VEC0_METADATA_OPERATOR_EQ: { + for(int i = 0; i < size; i++) { bitmap_set(b, i, array[i] == target); } + break; + } + case VEC0_METADATA_OPERATOR_GT: { + for(int i = 0; i < size; i++) { bitmap_set(b, i, array[i] > target); } + break; + } + case VEC0_METADATA_OPERATOR_LE: { + for(int i = 0; i < size; i++) { bitmap_set(b, i, array[i] <= target); } + break; + } + case VEC0_METADATA_OPERATOR_LT: { + for(int i = 0; i < size; i++) { bitmap_set(b, i, array[i] < target); } + break; + } + case VEC0_METADATA_OPERATOR_GE: { + for(int i = 0; i < size; i++) { bitmap_set(b, i, array[i] >= target); } + break; + } + case VEC0_METADATA_OPERATOR_NE: { + for(int i = 0; i < size; i++) { bitmap_set(b, i, array[i] != target); } + break; + } + case VEC0_METADATA_OPERATOR_IN: { + // should never be reached + break; + } + } + break; + } + case VEC0_METADATA_COLUMN_KIND_TEXT: { + rc = vec0_metadata_filter_text(p, value, buffer, size, op, b, metadata_idx, chunk_rowid, aMetadataIn, argv_idx); + if(rc != SQLITE_OK) { + goto done; + } + break; + } + } + done: + sqlite3_free(buffer); + return rc; +} + +int vec0Filter_knn_chunks_iter(vec0_vtab *p, sqlite3_stmt *stmtChunks, + struct VectorColumnDefinition *vector_column, + int vectorColumnIdx, struct Array *arrayRowidsIn, + struct Array * aMetadataIn, + const char * idxStr, int argc, sqlite3_value ** argv, + void *queryVector, i64 k, i64 **out_topk_rowids, + f32 **out_topk_distances, i64 *out_used) { + // for each chunk, get top min(k, chunk_size) rowid + distances to query vec. + // then reconcile all topk_chunks for a true top k. + // output only rowids + distances for now + + int rc = SQLITE_OK; + sqlite3_blob *blobVectors = NULL; + + void *baseVectors = NULL; // memory: chunk_size * dimensions * element_size + + // OWNED BY CALLER ON SUCCESS + i64 *topk_rowids = NULL; // memory: k * 4 + // OWNED BY CALLER ON SUCCESS + f32 *topk_distances = NULL; // memory: k * 4 + + i64 *tmp_topk_rowids = NULL; // memory: k * 4 + f32 *tmp_topk_distances = NULL; // memory: k * 4 + f32 *chunk_distances = NULL; // memory: chunk_size * 4 + u8 *b = NULL; // memory: chunk_size / 8 + u8 *bTaken = NULL; // memory: chunk_size / 8 + i32 *chunk_topk_idxs = NULL; // memory: k * 4 + u8 *bmRowids = NULL; // memory: chunk_size / 8 + u8 *bmMetadata = NULL; // memory: chunk_size / 8 + // // total: a lot??? + + // 6 * (k * 4) + (k * 2) + (chunk_size / 8) + (chunk_size * dimensions * 4) + + topk_rowids = sqlite3_malloc(k * sizeof(i64)); + if (!topk_rowids) { + rc = SQLITE_NOMEM; + goto cleanup; + } + memset(topk_rowids, 0, k * sizeof(i64)); + + topk_distances = sqlite3_malloc(k * sizeof(f32)); + if (!topk_distances) { + rc = SQLITE_NOMEM; + goto cleanup; + } + memset(topk_distances, 0, k * sizeof(f32)); + + tmp_topk_rowids = sqlite3_malloc(k * sizeof(i64)); + if (!tmp_topk_rowids) { + rc = SQLITE_NOMEM; + goto cleanup; + } + memset(tmp_topk_rowids, 0, k * sizeof(i64)); + + tmp_topk_distances = sqlite3_malloc(k * sizeof(f32)); + if (!tmp_topk_distances) { + rc = SQLITE_NOMEM; + goto cleanup; + } + memset(tmp_topk_distances, 0, k * sizeof(f32)); + + i64 k_used = 0; + i64 baseVectorsSize = p->chunk_size * vector_column_byte_size(*vector_column); + baseVectors = sqlite3_malloc(baseVectorsSize); + if (!baseVectors) { + rc = SQLITE_NOMEM; + goto cleanup; + } + + chunk_distances = sqlite3_malloc(p->chunk_size * sizeof(f32)); + if (!chunk_distances) { + rc = SQLITE_NOMEM; + goto cleanup; + } + + b = bitmap_new(p->chunk_size); + if (!b) { + rc = SQLITE_NOMEM; + goto cleanup; + } + + bTaken = bitmap_new(p->chunk_size); + if (!bTaken) { + rc = SQLITE_NOMEM; + goto cleanup; + } + + chunk_topk_idxs = sqlite3_malloc(k * sizeof(i32)); + if (!chunk_topk_idxs) { + rc = SQLITE_NOMEM; + goto cleanup; + } + + bmRowids = arrayRowidsIn ? bitmap_new(p->chunk_size) : NULL; + if (arrayRowidsIn && !bmRowids) { + rc = SQLITE_NOMEM; + goto cleanup; + } + + sqlite3_blob * metadataBlobs[VEC0_MAX_METADATA_COLUMNS]; + memset(metadataBlobs, 0, sizeof(sqlite3_blob*) * VEC0_MAX_METADATA_COLUMNS); + + bmMetadata = bitmap_new(p->chunk_size); + if(!bmMetadata) { + rc = SQLITE_NOMEM; + goto cleanup; + } + + int idxStrLength = strlen(idxStr); + int numValueEntries = (idxStrLength-1) / 4; + assert(numValueEntries == argc); + int hasMetadataFilters = 0; + for(int i = 0; i < argc; i++) { + int idx = 1 + (i * 4); + char kind = idxStr[idx + 0]; + if(kind == VEC0_IDXSTR_KIND_METADATA_CONSTRAINT) { + hasMetadataFilters = 1; + break; + } + } + + while (true) { + rc = sqlite3_step(stmtChunks); + if (rc == SQLITE_DONE) { + break; + } + if (rc != SQLITE_ROW) { + vtab_set_error(&p->base, "chunks iter error"); + rc = SQLITE_ERROR; + goto cleanup; + } + memset(chunk_distances, 0, p->chunk_size * sizeof(f32)); + memset(chunk_topk_idxs, 0, k * sizeof(i32)); + bitmap_clear(b, p->chunk_size); + + i64 chunk_id = sqlite3_column_int64(stmtChunks, 0); + unsigned char *chunkValidity = + (unsigned char *)sqlite3_column_blob(stmtChunks, 1); + i64 validitySize = sqlite3_column_bytes(stmtChunks, 1); + if (validitySize != p->chunk_size / CHAR_BIT) { + // IMP: V05271_22109 + vtab_set_error( + &p->base, + "chunk validity size doesn't match - expected %lld, found %lld", + p->chunk_size / CHAR_BIT, validitySize); + rc = SQLITE_ERROR; + goto cleanup; + } + + i64 *chunkRowids = (i64 *)sqlite3_column_blob(stmtChunks, 2); + i64 rowidsSize = sqlite3_column_bytes(stmtChunks, 2); + if (rowidsSize != p->chunk_size * sizeof(i64)) { + // IMP: V02796_19635 + vtab_set_error(&p->base, "rowids size doesn't match"); + vtab_set_error( + &p->base, + "chunk rowids size doesn't match - expected %lld, found %lld", + p->chunk_size * sizeof(i64), rowidsSize); + rc = SQLITE_ERROR; + goto cleanup; + } + + // open the vector chunk blob for the current chunk + rc = sqlite3_blob_open(p->db, p->schemaName, + p->shadowVectorChunksNames[vectorColumnIdx], + "vectors", chunk_id, 0, &blobVectors); + if (rc != SQLITE_OK) { + vtab_set_error(&p->base, "could not open vectors blob for chunk %lld", + chunk_id); + rc = SQLITE_ERROR; + goto cleanup; + } + + i64 currentBaseVectorsSize = sqlite3_blob_bytes(blobVectors); + i64 expectedBaseVectorsSize = + p->chunk_size * vector_column_byte_size(*vector_column); + if (currentBaseVectorsSize != expectedBaseVectorsSize) { + // IMP: V16465_00535 + vtab_set_error( + &p->base, + "vectors blob size doesn't match - expected %lld, found %lld", + expectedBaseVectorsSize, currentBaseVectorsSize); + rc = SQLITE_ERROR; + goto cleanup; + } + rc = sqlite3_blob_read(blobVectors, baseVectors, currentBaseVectorsSize, 0); + + if (rc != SQLITE_OK) { + vtab_set_error(&p->base, "vectors blob read error for %lld", chunk_id); + rc = SQLITE_ERROR; + goto cleanup; + } + + bitmap_copy(b, chunkValidity, p->chunk_size); + if (arrayRowidsIn) { + bitmap_clear(bmRowids, p->chunk_size); + + for (int i = 0; i < p->chunk_size; i++) { + if (!bitmap_get(chunkValidity, i)) { + continue; + } + i64 rowid = chunkRowids[i]; + void *in = bsearch(&rowid, arrayRowidsIn->z, arrayRowidsIn->length, + sizeof(i64), _cmp); + bitmap_set(bmRowids, i, in ? 1 : 0); + } + bitmap_and_inplace(b, bmRowids, p->chunk_size); + } + + if(hasMetadataFilters) { + for(int i = 0; i < argc; i++) { + int idx = 1 + (i * 4); + char kind = idxStr[idx + 0]; + if(kind != VEC0_IDXSTR_KIND_METADATA_CONSTRAINT) { + continue; + } + int metadata_idx = idxStr[idx + 1] - 'A'; + int operator = idxStr[idx + 2]; + + if(!metadataBlobs[metadata_idx]) { + rc = sqlite3_blob_open(p->db, p->schemaName, p->shadowMetadataChunksNames[metadata_idx], "data", chunk_id, 0, &metadataBlobs[metadata_idx]); + vtab_set_error(&p->base, "Could not open metadata blob"); + if(rc != SQLITE_OK) { + goto cleanup; + } + } + + bitmap_clear(bmMetadata, p->chunk_size); + rc = vec0_set_metadata_filter_bitmap(p, metadata_idx, operator, argv[i], metadataBlobs[metadata_idx], chunk_id, bmMetadata, p->chunk_size, aMetadataIn, i); + if(rc != SQLITE_OK) { + vtab_set_error(&p->base, "Could not filter metadata fields"); + if(rc != SQLITE_OK) { + goto cleanup; + } + } + bitmap_and_inplace(b, bmMetadata, p->chunk_size); + } + } + + + for (int i = 0; i < p->chunk_size; i++) { + if (!bitmap_get(b, i)) { + continue; + }; + + f32 result; + switch (vector_column->element_type) { + case SQLITE_VEC_ELEMENT_TYPE_FLOAT32: { + const f32 *base_i = + ((f32 *)baseVectors) + (i * vector_column->dimensions); + switch (vector_column->distance_metric) { + case VEC0_DISTANCE_METRIC_L2: { + result = distance_l2_sqr_float(base_i, (f32 *)queryVector, + &vector_column->dimensions); + break; + } + case VEC0_DISTANCE_METRIC_L1: { + result = distance_l1_f32(base_i, (f32 *)queryVector, + &vector_column->dimensions); + break; + } + case VEC0_DISTANCE_METRIC_COSINE: { + result = distance_cosine_float(base_i, (f32 *)queryVector, + &vector_column->dimensions); + break; + } + } + break; + } + case SQLITE_VEC_ELEMENT_TYPE_INT8: { + const i8 *base_i = + ((i8 *)baseVectors) + (i * vector_column->dimensions); + switch (vector_column->distance_metric) { + case VEC0_DISTANCE_METRIC_L2: { + result = distance_l2_sqr_int8(base_i, (i8 *)queryVector, + &vector_column->dimensions); + break; + } + case VEC0_DISTANCE_METRIC_L1: { + result = distance_l1_int8(base_i, (i8 *)queryVector, + &vector_column->dimensions); + break; + } + case VEC0_DISTANCE_METRIC_COSINE: { + result = distance_cosine_int8(base_i, (i8 *)queryVector, + &vector_column->dimensions); + break; + } + } + + break; + } + case SQLITE_VEC_ELEMENT_TYPE_BIT: { + const u8 *base_i = + ((u8 *)baseVectors) + (i * (vector_column->dimensions / CHAR_BIT)); + result = distance_hamming(base_i, (u8 *)queryVector, + &vector_column->dimensions); + break; + } + } + + chunk_distances[i] = result; + } + + int used1; + min_idx(chunk_distances, p->chunk_size, b, chunk_topk_idxs, + min(k, p->chunk_size), bTaken, &used1); + + i64 used; + merge_sorted_lists(topk_distances, topk_rowids, k_used, chunk_distances, + chunkRowids, chunk_topk_idxs, + min(min(k, p->chunk_size), used1), tmp_topk_distances, + tmp_topk_rowids, k, &used); + + for (int i = 0; i < used; i++) { + topk_rowids[i] = tmp_topk_rowids[i]; + topk_distances[i] = tmp_topk_distances[i]; + } + k_used = used; + // blobVectors is always opened with read-only permissions, so this never + // fails. + sqlite3_blob_close(blobVectors); + blobVectors = NULL; + } + + *out_topk_rowids = topk_rowids; + *out_topk_distances = topk_distances; + *out_used = k_used; + rc = SQLITE_OK; + +cleanup: + if (rc != SQLITE_OK) { + sqlite3_free(topk_rowids); + sqlite3_free(topk_distances); + } + sqlite3_free(chunk_topk_idxs); + sqlite3_free(tmp_topk_rowids); + sqlite3_free(tmp_topk_distances); + sqlite3_free(b); + sqlite3_free(bTaken); + sqlite3_free(bmRowids); + sqlite3_free(baseVectors); + sqlite3_free(chunk_distances); + sqlite3_free(bmMetadata); + for(int i = 0; i < VEC0_MAX_METADATA_COLUMNS; i++) { + sqlite3_blob_close(metadataBlobs[i]); + } + // blobVectors is always opened with read-only permissions, so this never + // fails. + sqlite3_blob_close(blobVectors); + return rc; +} + +int vec0Filter_knn(vec0_cursor *pCur, vec0_vtab *p, int idxNum, + const char *idxStr, int argc, sqlite3_value **argv) { + assert(argc == (strlen(idxStr)-1) / 4); + int rc; + struct vec0_query_knn_data *knn_data; + + int vectorColumnIdx = idxNum; + struct VectorColumnDefinition *vector_column = + &p->vector_columns[vectorColumnIdx]; + + struct Array *arrayRowidsIn = NULL; + sqlite3_stmt *stmtChunks = NULL; + void *queryVector; + size_t dimensions; + enum VectorElementType elementType; + vector_cleanup queryVectorCleanup = vector_cleanup_noop; + char *pzError; + knn_data = sqlite3_malloc(sizeof(*knn_data)); + if (!knn_data) { + return SQLITE_NOMEM; + } + memset(knn_data, 0, sizeof(*knn_data)); + // array of `struct Vec0MetadataIn`, IF there are any `xxx in (...)` metadata constraints + struct Array * aMetadataIn = NULL; + + int query_idx =-1; + int k_idx = -1; + int rowid_in_idx = -1; + for(int i = 0; i < argc; i++) { + if(idxStr[1 + (i*4)] == VEC0_IDXSTR_KIND_KNN_MATCH) { + query_idx = i; + } + if(idxStr[1 + (i*4)] == VEC0_IDXSTR_KIND_KNN_K) { + k_idx = i; + } + if(idxStr[1 + (i*4)] == VEC0_IDXSTR_KIND_KNN_ROWID_IN) { + rowid_in_idx = i; + } + } + assert(query_idx >= 0); + assert(k_idx >= 0); + + // make sure the query vector matches the vector column (type dimensions etc.) + rc = vector_from_value(argv[query_idx], &queryVector, &dimensions, &elementType, + &queryVectorCleanup, &pzError); + + if (rc != SQLITE_OK) { + vtab_set_error(&p->base, + "Query vector on the \"%.*s\" column is invalid: %z", + vector_column->name_length, vector_column->name, pzError); + rc = SQLITE_ERROR; + goto cleanup; + } + if (elementType != vector_column->element_type) { + vtab_set_error( + &p->base, + "Query vector for the \"%.*s\" column is expected to be of type " + "%s, but a %s vector was provided.", + vector_column->name_length, vector_column->name, + vector_subtype_name(vector_column->element_type), + vector_subtype_name(elementType)); + rc = SQLITE_ERROR; + goto cleanup; + } + if (dimensions != vector_column->dimensions) { + vtab_set_error( + &p->base, + "Dimension mismatch for query vector for the \"%.*s\" column. " + "Expected %d dimensions but received %d.", + vector_column->name_length, vector_column->name, + vector_column->dimensions, dimensions); + rc = SQLITE_ERROR; + goto cleanup; + } + + i64 k = sqlite3_value_int64(argv[k_idx]); + if (k < 0) { + vtab_set_error( + &p->base, "k value in knn queries must be greater than or equal to 0."); + rc = SQLITE_ERROR; + goto cleanup; + } +#define SQLITE_VEC_VEC0_K_MAX 4096 + if (k > SQLITE_VEC_VEC0_K_MAX) { + vtab_set_error( + &p->base, + "k value in knn query too large, provided %lld and the limit is %lld", + k, SQLITE_VEC_VEC0_K_MAX); + rc = SQLITE_ERROR; + goto cleanup; + } + + if (k == 0) { + knn_data->k = 0; + pCur->knn_data = knn_data; + pCur->query_plan = VEC0_QUERY_PLAN_KNN; + rc = SQLITE_OK; + goto cleanup; + } + +// handle when a `rowid in (...)` operation was provided +// Array of all the rowids that appear in any `rowid in (...)` constraint. +// NULL if none were provided, which means a "full" scan. +#if COMPILER_SUPPORTS_VTAB_IN + if (rowid_in_idx >= 0) { + sqlite3_value *item; + int rc; + arrayRowidsIn = sqlite3_malloc(sizeof(*arrayRowidsIn)); + if (!arrayRowidsIn) { + rc = SQLITE_NOMEM; + goto cleanup; + } + memset(arrayRowidsIn, 0, sizeof(*arrayRowidsIn)); + + rc = array_init(arrayRowidsIn, sizeof(i64), 32); + if (rc != SQLITE_OK) { + goto cleanup; + } + for (rc = sqlite3_vtab_in_first(argv[rowid_in_idx], &item); rc == SQLITE_OK && item; + rc = sqlite3_vtab_in_next(argv[rowid_in_idx], &item)) { + i64 rowid; + if (p->pkIsText) { + rc = vec0_rowid_from_id(p, item, &rowid); + if (rc != SQLITE_OK) { + goto cleanup; + } + } else { + rowid = sqlite3_value_int64(item); + } + rc = array_append(arrayRowidsIn, &rowid); + if (rc != SQLITE_OK) { + goto cleanup; + } + } + if (rc != SQLITE_DONE) { + vtab_set_error(&p->base, "error processing rowid in (...) array"); + goto cleanup; + } + qsort(arrayRowidsIn->z, arrayRowidsIn->length, arrayRowidsIn->element_size, + _cmp); + } +#endif + + #if COMPILER_SUPPORTS_VTAB_IN + for(int i = 0; i < argc; i++) { + if(!(idxStr[1 + (i*4)] == VEC0_IDXSTR_KIND_METADATA_CONSTRAINT && idxStr[1 + (i*4) + 2] == VEC0_METADATA_OPERATOR_IN)) { + continue; + } + int metadata_idx = idxStr[1 + (i*4) + 1] - 'A'; + if(!aMetadataIn) { + aMetadataIn = sqlite3_malloc(sizeof(*aMetadataIn)); + if(!aMetadataIn) { + rc = SQLITE_NOMEM; + goto cleanup; + } + memset(aMetadataIn, 0, sizeof(*aMetadataIn)); + rc = array_init(aMetadataIn, sizeof(struct Vec0MetadataIn), 8); + if(rc != SQLITE_OK) { + goto cleanup; + } + } + + struct Vec0MetadataIn item; + memset(&item, 0, sizeof(item)); + item.metadata_idx=metadata_idx; + item.argv_idx = i; + + switch(p->metadata_columns[metadata_idx].kind) { + case VEC0_METADATA_COLUMN_KIND_INTEGER: { + rc = array_init(&item.array, sizeof(i64), 16); + if(rc != SQLITE_OK) { + goto cleanup; + } + sqlite3_value *entry; + for (rc = sqlite3_vtab_in_first(argv[i], &entry); rc == SQLITE_OK && entry; rc = sqlite3_vtab_in_next(argv[i], &entry)) { + i64 v = sqlite3_value_int64(entry); + rc = array_append(&item.array, &v); + if (rc != SQLITE_OK) { + goto cleanup; + } + } + + if (rc != SQLITE_DONE) { + vtab_set_error(&p->base, "Error fetching next value in `x in (...)` integer expression"); + goto cleanup; + } + + break; + } + case VEC0_METADATA_COLUMN_KIND_TEXT: { + rc = array_init(&item.array, sizeof(struct Vec0MetadataInTextEntry), 16); + if(rc != SQLITE_OK) { + goto cleanup; + } + sqlite3_value *entry; + for (rc = sqlite3_vtab_in_first(argv[i], &entry); rc == SQLITE_OK && entry; rc = sqlite3_vtab_in_next(argv[i], &entry)) { + const char * s = (const char *) sqlite3_value_text(entry); + int n = sqlite3_value_bytes(entry); + + struct Vec0MetadataInTextEntry entry; + entry.zString = sqlite3_mprintf("%.*s", n, s); + if(!entry.zString) { + rc = SQLITE_NOMEM; + goto cleanup; + } + entry.n = n; + rc = array_append(&item.array, &entry); + if (rc != SQLITE_OK) { + goto cleanup; + } + } + + if (rc != SQLITE_DONE) { + vtab_set_error(&p->base, "Error fetching next value in `x in (...)` text expression"); + goto cleanup; + } + + break; + } + default: { + vtab_set_error(&p->base, "Internal sqlite-vec error"); + goto cleanup; + } + } + + rc = array_append(aMetadataIn, &item); + if(rc != SQLITE_OK) { + goto cleanup; + } + } + #endif + + rc = vec0_chunks_iter(p, idxStr, argc, argv, &stmtChunks); + if (rc != SQLITE_OK) { + // IMP: V06942_23781 + vtab_set_error(&p->base, "Error preparing stmtChunk: %s", + sqlite3_errmsg(p->db)); + goto cleanup; + } + + i64 *topk_rowids = NULL; + f32 *topk_distances = NULL; + i64 k_used = 0; + rc = vec0Filter_knn_chunks_iter(p, stmtChunks, vector_column, vectorColumnIdx, + arrayRowidsIn, aMetadataIn, idxStr, argc, argv, queryVector, k, &topk_rowids, + &topk_distances, &k_used); + if (rc != SQLITE_OK) { + goto cleanup; + } + + knn_data->current_idx = 0; + knn_data->k = k; + knn_data->rowids = topk_rowids; + knn_data->distances = topk_distances; + knn_data->k_used = k_used; + + pCur->knn_data = knn_data; + pCur->query_plan = VEC0_QUERY_PLAN_KNN; + rc = SQLITE_OK; + +cleanup: + sqlite3_finalize(stmtChunks); + array_cleanup(arrayRowidsIn); + sqlite3_free(arrayRowidsIn); + queryVectorCleanup(queryVector); + if(aMetadataIn) { + for(size_t i = 0; i < aMetadataIn->length; i++) { + struct Vec0MetadataIn* item = &((struct Vec0MetadataIn *) aMetadataIn->z)[i]; + for(size_t j = 0; j < item->array.length; j++) { + if(p->metadata_columns[item->metadata_idx].kind == VEC0_METADATA_COLUMN_KIND_TEXT) { + struct Vec0MetadataInTextEntry entry = ((struct Vec0MetadataInTextEntry*)item->array.z)[j]; + sqlite3_free(entry.zString); + } + } + array_cleanup(&item->array); + } + array_cleanup(aMetadataIn); + } + + sqlite3_free(aMetadataIn); + + return rc; +} + +int vec0Filter_fullscan(vec0_vtab *p, vec0_cursor *pCur) { + int rc; + char *zSql; + struct vec0_query_fullscan_data *fullscan_data; + + fullscan_data = sqlite3_malloc(sizeof(*fullscan_data)); + if (!fullscan_data) { + return SQLITE_NOMEM; + } + memset(fullscan_data, 0, sizeof(*fullscan_data)); + + zSql = sqlite3_mprintf(" SELECT rowid " + " FROM " VEC0_SHADOW_ROWIDS_NAME + " ORDER by chunk_id, chunk_offset ", + p->schemaName, p->tableName); + if (!zSql) { + rc = SQLITE_NOMEM; + goto error; + } + rc = sqlite3_prepare_v2(p->db, zSql, -1, &fullscan_data->rowids_stmt, NULL); + sqlite3_free(zSql); + if (rc != SQLITE_OK) { + // IMP: V09901_26739 + vtab_set_error(&p->base, "Error preparing rowid scan: %s", + sqlite3_errmsg(p->db)); + goto error; + } + + rc = sqlite3_step(fullscan_data->rowids_stmt); + + // DONE when there's no rowids, ROW when there are, both "success" + if (!(rc == SQLITE_ROW || rc == SQLITE_DONE)) { + goto error; + } + + fullscan_data->done = rc == SQLITE_DONE; + pCur->query_plan = VEC0_QUERY_PLAN_FULLSCAN; + pCur->fullscan_data = fullscan_data; + return SQLITE_OK; + +error: + vec0_query_fullscan_data_clear(fullscan_data); + sqlite3_free(fullscan_data); + return rc; +} + +int vec0Filter_point(vec0_cursor *pCur, vec0_vtab *p, int argc, + sqlite3_value **argv) { + int rc; + assert(argc == 1); + i64 rowid; + struct vec0_query_point_data *point_data = NULL; + + point_data = sqlite3_malloc(sizeof(*point_data)); + if (!point_data) { + rc = SQLITE_NOMEM; + goto error; + } + memset(point_data, 0, sizeof(*point_data)); + + if (p->pkIsText) { + rc = vec0_rowid_from_id(p, argv[0], &rowid); + if (rc == SQLITE_EMPTY) { + goto eof; + } + if (rc != SQLITE_OK) { + goto error; + } + } else { + rowid = sqlite3_value_int64(argv[0]); + } + + for (int i = 0; i < p->numVectorColumns; i++) { + rc = vec0_get_vector_data(p, rowid, i, &point_data->vectors[i], NULL); + if (rc == SQLITE_EMPTY) { + goto eof; + } + if (rc != SQLITE_OK) { + goto error; + } + } + + point_data->rowid = rowid; + point_data->done = 0; + pCur->point_data = point_data; + pCur->query_plan = VEC0_QUERY_PLAN_POINT; + return SQLITE_OK; + +eof: + point_data->rowid = rowid; + point_data->done = 1; + pCur->point_data = point_data; + pCur->query_plan = VEC0_QUERY_PLAN_POINT; + return SQLITE_OK; + +error: + vec0_query_point_data_clear(point_data); + sqlite3_free(point_data); + return rc; +} + +static int vec0Filter(sqlite3_vtab_cursor *pVtabCursor, int idxNum, + const char *idxStr, int argc, sqlite3_value **argv) { + vec0_vtab *p = (vec0_vtab *)pVtabCursor->pVtab; + vec0_cursor *pCur = (vec0_cursor *)pVtabCursor; + vec0_cursor_clear(pCur); + + int idxStrLength = strlen(idxStr); + if(idxStrLength <= 0) { + return SQLITE_ERROR; + } + if((idxStrLength-1) % 4 != 0) { + return SQLITE_ERROR; + } + int numValueEntries = (idxStrLength-1) / 4; + if(numValueEntries != argc) { + return SQLITE_ERROR; + } + + char query_plan = idxStr[0]; + switch(query_plan) { + case VEC0_QUERY_PLAN_FULLSCAN: + return vec0Filter_fullscan(p, pCur); + case VEC0_QUERY_PLAN_KNN: + return vec0Filter_knn(pCur, p, idxNum, idxStr, argc, argv); + case VEC0_QUERY_PLAN_POINT: + return vec0Filter_point(pCur, p, argc, argv); + default: + vtab_set_error(pVtabCursor->pVtab, "unknown idxStr '%s'", idxStr); + return SQLITE_ERROR; + } +} + +static int vec0Rowid(sqlite3_vtab_cursor *cur, sqlite_int64 *pRowid) { + vec0_cursor *pCur = (vec0_cursor *)cur; + switch (pCur->query_plan) { + case VEC0_QUERY_PLAN_FULLSCAN: { + *pRowid = sqlite3_column_int64(pCur->fullscan_data->rowids_stmt, 0); + return SQLITE_OK; + } + case VEC0_QUERY_PLAN_POINT: { + *pRowid = pCur->point_data->rowid; + return SQLITE_OK; + } + case VEC0_QUERY_PLAN_KNN: { + vtab_set_error(cur->pVtab, + "Internal sqlite-vec error: expected point query plan in " + "vec0Rowid, found %d", + pCur->query_plan); + return SQLITE_ERROR; + } + } + return SQLITE_ERROR; +} + +static int vec0Next(sqlite3_vtab_cursor *cur) { + vec0_cursor *pCur = (vec0_cursor *)cur; + switch (pCur->query_plan) { + case VEC0_QUERY_PLAN_FULLSCAN: { + if (!pCur->fullscan_data) { + return SQLITE_ERROR; + } + int rc = sqlite3_step(pCur->fullscan_data->rowids_stmt); + if (rc == SQLITE_DONE) { + pCur->fullscan_data->done = 1; + return SQLITE_OK; + } + if (rc == SQLITE_ROW) { + return SQLITE_OK; + } + return SQLITE_ERROR; + } + case VEC0_QUERY_PLAN_KNN: { + if (!pCur->knn_data) { + return SQLITE_ERROR; + } + + pCur->knn_data->current_idx++; + return SQLITE_OK; + } + case VEC0_QUERY_PLAN_POINT: { + if (!pCur->point_data) { + return SQLITE_ERROR; + } + pCur->point_data->done = 1; + return SQLITE_OK; + } + } + return SQLITE_ERROR; +} + +static int vec0Eof(sqlite3_vtab_cursor *cur) { + vec0_cursor *pCur = (vec0_cursor *)cur; + switch (pCur->query_plan) { + case VEC0_QUERY_PLAN_FULLSCAN: { + if (!pCur->fullscan_data) { + return 1; + } + return pCur->fullscan_data->done; + } + case VEC0_QUERY_PLAN_KNN: { + if (!pCur->knn_data) { + return 1; + } + // return (pCur->knn_data->current_idx >= pCur->knn_data->k) || + // (pCur->knn_data->distances[pCur->knn_data->current_idx] == FLT_MAX); + return (pCur->knn_data->current_idx >= pCur->knn_data->k_used); + } + case VEC0_QUERY_PLAN_POINT: { + if (!pCur->point_data) { + return 1; + } + return pCur->point_data->done; + } + } + return 1; +} + +static int vec0Column_fullscan(vec0_vtab *pVtab, vec0_cursor *pCur, + sqlite3_context *context, int i) { + if (!pCur->fullscan_data) { + sqlite3_result_error( + context, "Internal sqlite-vec error: fullscan_data is NULL.", -1); + return SQLITE_ERROR; + } + i64 rowid = sqlite3_column_int64(pCur->fullscan_data->rowids_stmt, 0); + if (i == VEC0_COLUMN_ID) { + return vec0_result_id(pVtab, context, rowid); + } + else if (vec0_column_idx_is_vector(pVtab, i)) { + void *v; + int sz; + int vector_idx = vec0_column_idx_to_vector_idx(pVtab, i); + int rc = vec0_get_vector_data(pVtab, rowid, vector_idx, &v, &sz); + if (rc != SQLITE_OK) { + return rc; + } + sqlite3_result_blob(context, v, sz, sqlite3_free); + sqlite3_result_subtype(context, + pVtab->vector_columns[vector_idx].element_type); + + } + else if (i == vec0_column_distance_idx(pVtab)) { + sqlite3_result_null(context); + } + else if(vec0_column_idx_is_partition(pVtab, i)) { + int partition_idx = vec0_column_idx_to_partition_idx(pVtab, i); + sqlite3_value * v; + int rc = vec0_get_partition_value_for_rowid(pVtab, rowid, partition_idx, &v); + if(rc == SQLITE_OK) { + sqlite3_result_value(context, v); + sqlite3_value_free(v); + }else { + sqlite3_result_error_code(context, rc); + } + } + else if(vec0_column_idx_is_auxiliary(pVtab, i)) { + int auxiliary_idx = vec0_column_idx_to_auxiliary_idx(pVtab, i); + sqlite3_value * v; + int rc = vec0_get_auxiliary_value_for_rowid(pVtab, rowid, auxiliary_idx, &v); + if(rc == SQLITE_OK) { + sqlite3_result_value(context, v); + sqlite3_value_free(v); + }else { + sqlite3_result_error_code(context, rc); + } + } + + else if(vec0_column_idx_is_metadata(pVtab, i)) { + if(sqlite3_vtab_nochange(context)) { + return SQLITE_OK; + } + int metadata_idx = vec0_column_idx_to_metadata_idx(pVtab, i); + int rc = vec0_result_metadata_value_for_rowid(pVtab, rowid, metadata_idx, context); + if(rc != SQLITE_OK) { + // IMP: V15466_32305 + const char * zErr = sqlite3_mprintf( + "Could not extract metadata value for column %.*s at rowid %lld", + pVtab->metadata_columns[metadata_idx].name_length, + pVtab->metadata_columns[metadata_idx].name, rowid + ); + if(zErr) { + sqlite3_result_error(context, zErr, -1); + sqlite3_free((void *) zErr); + }else { + sqlite3_result_error_nomem(context); + } + } + } + + return SQLITE_OK; +} + +static int vec0Column_point(vec0_vtab *pVtab, vec0_cursor *pCur, + sqlite3_context *context, int i) { + if (!pCur->point_data) { + sqlite3_result_error(context, + "Internal sqlite-vec error: point_data is NULL.", -1); + return SQLITE_ERROR; + } + if (i == VEC0_COLUMN_ID) { + return vec0_result_id(pVtab, context, pCur->point_data->rowid); + } + else if (i == vec0_column_distance_idx(pVtab)) { + sqlite3_result_null(context); + return SQLITE_OK; + } + else if (vec0_column_idx_is_vector(pVtab, i)) { + if (sqlite3_vtab_nochange(context)) { + sqlite3_result_null(context); + return SQLITE_OK; + } + int vector_idx = vec0_column_idx_to_vector_idx(pVtab, i); + sqlite3_result_blob( + context, pCur->point_data->vectors[vector_idx], + vector_column_byte_size(pVtab->vector_columns[vector_idx]), + SQLITE_TRANSIENT); + sqlite3_result_subtype(context, + pVtab->vector_columns[vector_idx].element_type); + return SQLITE_OK; + } + else if(vec0_column_idx_is_partition(pVtab, i)) { + if(sqlite3_vtab_nochange(context)) { + return SQLITE_OK; + } + int partition_idx = vec0_column_idx_to_partition_idx(pVtab, i); + i64 rowid = pCur->point_data->rowid; + sqlite3_value * v; + int rc = vec0_get_partition_value_for_rowid(pVtab, rowid, partition_idx, &v); + if(rc == SQLITE_OK) { + sqlite3_result_value(context, v); + sqlite3_value_free(v); + }else { + sqlite3_result_error_code(context, rc); + } + } + else if(vec0_column_idx_is_auxiliary(pVtab, i)) { + if(sqlite3_vtab_nochange(context)) { + return SQLITE_OK; + } + i64 rowid = pCur->point_data->rowid; + int auxiliary_idx = vec0_column_idx_to_auxiliary_idx(pVtab, i); + sqlite3_value * v; + int rc = vec0_get_auxiliary_value_for_rowid(pVtab, rowid, auxiliary_idx, &v); + if(rc == SQLITE_OK) { + sqlite3_result_value(context, v); + sqlite3_value_free(v); + }else { + sqlite3_result_error_code(context, rc); + } + } + + else if(vec0_column_idx_is_metadata(pVtab, i)) { + if(sqlite3_vtab_nochange(context)) { + return SQLITE_OK; + } + i64 rowid = pCur->point_data->rowid; + int metadata_idx = vec0_column_idx_to_metadata_idx(pVtab, i); + int rc = vec0_result_metadata_value_for_rowid(pVtab, rowid, metadata_idx, context); + if(rc != SQLITE_OK) { + const char * zErr = sqlite3_mprintf( + "Could not extract metadata value for column %.*s at rowid %lld", + pVtab->metadata_columns[metadata_idx].name_length, + pVtab->metadata_columns[metadata_idx].name, rowid + ); + if(zErr) { + sqlite3_result_error(context, zErr, -1); + sqlite3_free((void *) zErr); + }else { + sqlite3_result_error_nomem(context); + } + } + } + + return SQLITE_OK; +} + +static int vec0Column_knn(vec0_vtab *pVtab, vec0_cursor *pCur, + sqlite3_context *context, int i) { + if (!pCur->knn_data) { + sqlite3_result_error(context, + "Internal sqlite-vec error: knn_data is NULL.", -1); + return SQLITE_ERROR; + } + if (i == VEC0_COLUMN_ID) { + i64 rowid = pCur->knn_data->rowids[pCur->knn_data->current_idx]; + return vec0_result_id(pVtab, context, rowid); + } + else if (i == vec0_column_distance_idx(pVtab)) { + sqlite3_result_double( + context, pCur->knn_data->distances[pCur->knn_data->current_idx]); + return SQLITE_OK; + } + else if (vec0_column_idx_is_vector(pVtab, i)) { + void *out; + int sz; + int vector_idx = vec0_column_idx_to_vector_idx(pVtab, i); + int rc = vec0_get_vector_data( + pVtab, pCur->knn_data->rowids[pCur->knn_data->current_idx], vector_idx, + &out, &sz); + if (rc != SQLITE_OK) { + return rc; + } + sqlite3_result_blob(context, out, sz, sqlite3_free); + sqlite3_result_subtype(context, + pVtab->vector_columns[vector_idx].element_type); + return SQLITE_OK; + } + else if(vec0_column_idx_is_partition(pVtab, i)) { + int partition_idx = vec0_column_idx_to_partition_idx(pVtab, i); + i64 rowid = pCur->knn_data->rowids[pCur->knn_data->current_idx]; + sqlite3_value * v; + int rc = vec0_get_partition_value_for_rowid(pVtab, rowid, partition_idx, &v); + if(rc == SQLITE_OK) { + sqlite3_result_value(context, v); + sqlite3_value_free(v); + }else { + sqlite3_result_error_code(context, rc); + } + } + else if(vec0_column_idx_is_auxiliary(pVtab, i)) { + int auxiliary_idx = vec0_column_idx_to_auxiliary_idx(pVtab, i); + i64 rowid = pCur->knn_data->rowids[pCur->knn_data->current_idx]; + sqlite3_value * v; + int rc = vec0_get_auxiliary_value_for_rowid(pVtab, rowid, auxiliary_idx, &v); + if(rc == SQLITE_OK) { + sqlite3_result_value(context, v); + sqlite3_value_free(v); + }else { + sqlite3_result_error_code(context, rc); + } + } + + else if(vec0_column_idx_is_metadata(pVtab, i)) { + int metadata_idx = vec0_column_idx_to_metadata_idx(pVtab, i); + i64 rowid = pCur->knn_data->rowids[pCur->knn_data->current_idx]; + int rc = vec0_result_metadata_value_for_rowid(pVtab, rowid, metadata_idx, context); + if(rc != SQLITE_OK) { + const char * zErr = sqlite3_mprintf( + "Could not extract metadata value for column %.*s at rowid %lld", + pVtab->metadata_columns[metadata_idx].name_length, + pVtab->metadata_columns[metadata_idx].name, rowid + ); + if(zErr) { + sqlite3_result_error(context, zErr, -1); + sqlite3_free((void *) zErr); + }else { + sqlite3_result_error_nomem(context); + } + } + } + + return SQLITE_OK; +} + +static int vec0Column(sqlite3_vtab_cursor *cur, sqlite3_context *context, + int i) { + vec0_cursor *pCur = (vec0_cursor *)cur; + vec0_vtab *pVtab = (vec0_vtab *)cur->pVtab; + switch (pCur->query_plan) { + case VEC0_QUERY_PLAN_FULLSCAN: { + return vec0Column_fullscan(pVtab, pCur, context, i); + } + case VEC0_QUERY_PLAN_KNN: { + return vec0Column_knn(pVtab, pCur, context, i); + } + case VEC0_QUERY_PLAN_POINT: { + return vec0Column_point(pVtab, pCur, context, i); + } + } + return SQLITE_OK; +} + +/** + * @brief Handles the "insert rowid" step of a row insert operation of a vec0 + * table. + * + * This function will insert a new row into the _rowids vec0 shadow table. + * + * @param p: virtual table + * @param idValue: Value containing the inserted rowid/id value. + * @param rowid: Output rowid, will point to the "real" i64 rowid + * value that was inserted + * @return int SQLITE_OK on success, error code on failure + */ +int vec0Update_InsertRowidStep(vec0_vtab *p, sqlite3_value *idValue, + i64 *rowid) { + + /** + * An insert into a vec0 table can happen a few different ways: + * 1) With default INTEGER primary key: With a supplied i64 rowid + * 2) With default INTEGER primary key: WITHOUT a supplied rowid + * 3) With TEXT primary key: supplied text rowid + */ + + int rc; + + // Option 3: vtab has a user-defined TEXT primary key, so ensure a text value + // is provided. + if (p->pkIsText) { + if (sqlite3_value_type(idValue) != SQLITE_TEXT) { + // IMP: V04200_21039 + vtab_set_error(&p->base, + "The %s virtual table was declared with a TEXT primary " + "key, but a non-TEXT value was provided in an INSERT.", + p->tableName); + return SQLITE_ERROR; + } + + return vec0_rowids_insert_id(p, idValue, rowid); + } + + // Option 1: User supplied a i64 rowid + if (sqlite3_value_type(idValue) == SQLITE_INTEGER) { + i64 suppliedRowid = sqlite3_value_int64(idValue); + rc = vec0_rowids_insert_rowid(p, suppliedRowid); + if (rc == SQLITE_OK) { + *rowid = suppliedRowid; + } + return rc; + } + + // Option 2: User did not suppled a rowid + + if (sqlite3_value_type(idValue) != SQLITE_NULL) { + // IMP: V30855_14925 + vtab_set_error(&p->base, + "Only integers are allows for primary key values on %s", + p->tableName); + return SQLITE_ERROR; + } + // NULL to get next auto-incremented value + return vec0_rowids_insert_id(p, NULL, rowid); +} + +/** + * @brief Determines the "next available" chunk position for a newly inserted + * vec0 row. + * + * This operation may insert a new "blank" chunk the _chunks table, if there is + * no more space in previous chunks. + * + * @param p: virtual table + * @param partitionKeyValues: array of partition key column values, to constrain + * against any partition key columns. + * @param chunk_rowid: Output rowid of the chunk in the _chunks virtual table + * that has the avialabiity. + * @param chunk_offset: Output the index of the available space insert the + * chunk, based on the index of the first available validity bit. + * @param pBlobValidity: Output blob of the validity column of the available + * chunk. Will be opened with read/write permissions. + * @param pValidity: Output buffer of the original chunk's validity column. + * Needs to be cleaned up with sqlite3_free(). + * @return int SQLITE_OK on success, error code on failure + */ +int vec0Update_InsertNextAvailableStep( + vec0_vtab *p, + sqlite3_value ** partitionKeyValues, + i64 *chunk_rowid, i64 *chunk_offset, + sqlite3_blob **blobChunksValidity, + const unsigned char **bufferChunksValidity) { + + int rc; + i64 validitySize; + *chunk_offset = -1; + + rc = vec0_get_latest_chunk_rowid(p, chunk_rowid, partitionKeyValues); + if(rc == SQLITE_EMPTY) { + goto done; + } + if (rc != SQLITE_OK) { + goto cleanup; + } + + rc = sqlite3_blob_open(p->db, p->schemaName, p->shadowChunksName, "validity", + *chunk_rowid, 1, blobChunksValidity); + if (rc != SQLITE_OK) { + // IMP: V22053_06123 + vtab_set_error(&p->base, + VEC_INTERAL_ERROR + "could not open validity blob on %s.%s.%lld", + p->schemaName, p->shadowChunksName, *chunk_rowid); + goto cleanup; + } + + validitySize = sqlite3_blob_bytes(*blobChunksValidity); + if (validitySize != p->chunk_size / CHAR_BIT) { + // IMP: V29362_13432 + vtab_set_error(&p->base, + VEC_INTERAL_ERROR + "validity blob size mismatch on " + "%s.%s.%lld, expected %lld but received %lld.", + p->schemaName, p->shadowChunksName, *chunk_rowid, + (i64)(p->chunk_size / CHAR_BIT), validitySize); + rc = SQLITE_ERROR; + goto cleanup; + } + + *bufferChunksValidity = sqlite3_malloc(validitySize); + if (!(*bufferChunksValidity)) { + vtab_set_error(&p->base, VEC_INTERAL_ERROR + "Could not allocate memory for validity bitmap"); + rc = SQLITE_NOMEM; + goto cleanup; + } + + rc = sqlite3_blob_read(*blobChunksValidity, (void *)*bufferChunksValidity, + validitySize, 0); + + if (rc != SQLITE_OK) { + vtab_set_error(&p->base, + VEC_INTERAL_ERROR + "Could not read validity bitmap for %s.%s.%lld", + p->schemaName, p->shadowChunksName, *chunk_rowid); + goto cleanup; + } + + // find the next available offset, ie first `0` in the bitmap. + for (int i = 0; i < validitySize; i++) { + if ((*bufferChunksValidity)[i] == 0b11111111) + continue; + for (int j = 0; j < CHAR_BIT; j++) { + if (((((*bufferChunksValidity)[i] >> j) & 1) == 0)) { + *chunk_offset = (i * CHAR_BIT) + j; + goto done; + } + } + } + +done: + // latest chunk was full, so need to create a new one + if (*chunk_offset == -1) { + rc = vec0_new_chunk(p, partitionKeyValues, chunk_rowid); + if (rc != SQLITE_OK) { + // IMP: V08441_25279 + vtab_set_error(&p->base, + VEC_INTERAL_ERROR "Could not insert a new vector chunk"); + rc = SQLITE_ERROR; // otherwise raises a DatabaseError and not operational + // error? + goto cleanup; + } + *chunk_offset = 0; + + // blobChunksValidity and pValidity are stale, pointing to the previous + // (full) chunk. to re-assign them + rc = sqlite3_blob_close(*blobChunksValidity); + sqlite3_free((void *)*bufferChunksValidity); + *blobChunksValidity = NULL; + *bufferChunksValidity = NULL; + if (rc != SQLITE_OK) { + vtab_set_error(&p->base, VEC_INTERAL_ERROR + "unknown error, blobChunksValidity could not be closed, " + "please file an issue."); + rc = SQLITE_ERROR; + goto cleanup; + } + + rc = sqlite3_blob_open(p->db, p->schemaName, p->shadowChunksName, + "validity", *chunk_rowid, 1, blobChunksValidity); + if (rc != SQLITE_OK) { + vtab_set_error( + &p->base, + VEC_INTERAL_ERROR + "Could not open validity blob for newly created chunk %s.%s.%lld", + p->schemaName, p->shadowChunksName, *chunk_rowid); + goto cleanup; + } + validitySize = sqlite3_blob_bytes(*blobChunksValidity); + if (validitySize != p->chunk_size / CHAR_BIT) { + vtab_set_error(&p->base, + VEC_INTERAL_ERROR + "validity blob size mismatch for newly created chunk " + "%s.%s.%lld. Exepcted %lld, got %lld", + p->schemaName, p->shadowChunksName, *chunk_rowid, + p->chunk_size / CHAR_BIT, validitySize); + goto cleanup; + } + *bufferChunksValidity = sqlite3_malloc(validitySize); + rc = sqlite3_blob_read(*blobChunksValidity, (void *)*bufferChunksValidity, + validitySize, 0); + if (rc != SQLITE_OK) { + vtab_set_error(&p->base, + VEC_INTERAL_ERROR + "could not read validity blob newly created chunk " + "%s.%s.%lld", + p->schemaName, p->shadowChunksName, *chunk_rowid); + goto cleanup; + } + } + + rc = SQLITE_OK; + +cleanup: + return rc; +} + +/** + * @brief Write the vector data into the provided vector blob at the given + * offset + * + * @param blobVectors SQLite BLOB to write to + * @param chunk_offset the "offset" (ie validity bitmap position) to write the + * vector to + * @param bVector pointer to the vector containing data + * @param dimensions how many dimensions the vector has + * @param element_type the vector type + * @return result of sqlite3_blob_write, SQLITE_OK on success, otherwise failure + */ +static int +vec0_write_vector_to_vector_blob(sqlite3_blob *blobVectors, i64 chunk_offset, + const void *bVector, size_t dimensions, + enum VectorElementType element_type) { + int n; + int offset; + + switch (element_type) { + case SQLITE_VEC_ELEMENT_TYPE_FLOAT32: + n = dimensions * sizeof(f32); + offset = chunk_offset * dimensions * sizeof(f32); + break; + case SQLITE_VEC_ELEMENT_TYPE_INT8: + n = dimensions * sizeof(i8); + offset = chunk_offset * dimensions * sizeof(i8); + break; + case SQLITE_VEC_ELEMENT_TYPE_BIT: + n = dimensions / CHAR_BIT; + offset = chunk_offset * dimensions / CHAR_BIT; + break; + } + + return sqlite3_blob_write(blobVectors, bVector, n, offset); +} + +/** + * @brief + * + * @param p vec0 virtual table + * @param chunk_rowid: which chunk to write to + * @param chunk_offset: the offset inside the chunk to write the vector to. + * @param rowid: the rowid of the inserting row + * @param vectorDatas: array of the vector data to insert + * @param blobValidity: writeable validity blob of the row's assigned chunk. + * @param validity: snapshot buffer of the valdity column from the row's + * assigned chunk. + * @return int SQLITE_OK on success, error code on failure + */ +int vec0Update_InsertWriteFinalStep(vec0_vtab *p, i64 chunk_rowid, + i64 chunk_offset, i64 rowid, + void *vectorDatas[], + sqlite3_blob *blobChunksValidity, + const unsigned char *bufferChunksValidity) { + int rc, brc; + sqlite3_blob *blobChunksRowids = NULL; + + // mark the validity bit for this row in the chunk's validity bitmap + // Get the byte offset of the bitmap + char unsigned bx = bufferChunksValidity[chunk_offset / CHAR_BIT]; + // set the bit at the chunk_offset position inside that byte + bx = bx | (1 << (chunk_offset % CHAR_BIT)); + // write that 1 byte + rc = sqlite3_blob_write(blobChunksValidity, &bx, 1, chunk_offset / CHAR_BIT); + if (rc != SQLITE_OK) { + vtab_set_error(&p->base, VEC_INTERAL_ERROR "could not mark validity bit "); + return rc; + } + + // Go insert the vector data into the vector chunk shadow tables + for (int i = 0; i < p->numVectorColumns; i++) { + sqlite3_blob *blobVectors; + rc = sqlite3_blob_open(p->db, p->schemaName, p->shadowVectorChunksNames[i], + "vectors", chunk_rowid, 1, &blobVectors); + if (rc != SQLITE_OK) { + vtab_set_error(&p->base, "Error opening vector blob at %s.%s.%lld", + p->schemaName, p->shadowVectorChunksNames[i], chunk_rowid); + goto cleanup; + } + + i64 expected = + p->chunk_size * vector_column_byte_size(p->vector_columns[i]); + i64 actual = sqlite3_blob_bytes(blobVectors); + + if (actual != expected) { + // IMP: V16386_00456 + vtab_set_error( + &p->base, + VEC_INTERAL_ERROR + "vector blob size mismatch on %s.%s.%lld. Expected %lld, actual %lld", + p->schemaName, p->shadowVectorChunksNames[i], chunk_rowid, expected, + actual); + rc = SQLITE_ERROR; + // already error, can ignore result code + sqlite3_blob_close(blobVectors); + goto cleanup; + }; + + rc = vec0_write_vector_to_vector_blob( + blobVectors, chunk_offset, vectorDatas[i], + p->vector_columns[i].dimensions, p->vector_columns[i].element_type); + if (rc != SQLITE_OK) { + vtab_set_error(&p->base, + VEC_INTERAL_ERROR + "could not write vector blob on %s.%s.%lld", + p->schemaName, p->shadowVectorChunksNames[i], chunk_rowid); + rc = SQLITE_ERROR; + // already error, can ignore result code + sqlite3_blob_close(blobVectors); + goto cleanup; + } + rc = sqlite3_blob_close(blobVectors); + if (rc != SQLITE_OK) { + vtab_set_error(&p->base, + VEC_INTERAL_ERROR + "could not close vector blob on %s.%s.%lld", + p->schemaName, p->shadowVectorChunksNames[i], chunk_rowid); + rc = SQLITE_ERROR; + goto cleanup; + } + } + + // write the new rowid to the rowids column of the _chunks table + rc = sqlite3_blob_open(p->db, p->schemaName, p->shadowChunksName, "rowids", + chunk_rowid, 1, &blobChunksRowids); + if (rc != SQLITE_OK) { + // IMP: V09221_26060 + vtab_set_error(&p->base, + VEC_INTERAL_ERROR "could not open rowids blob on %s.%s.%lld", + p->schemaName, p->shadowChunksName, chunk_rowid); + goto cleanup; + } + i64 expected = p->chunk_size * sizeof(i64); + i64 actual = sqlite3_blob_bytes(blobChunksRowids); + if (expected != actual) { + // IMP: V12779_29618 + vtab_set_error( + &p->base, + VEC_INTERAL_ERROR + "rowids blob size mismatch on %s.%s.%lld. Expected %lld, actual %lld", + p->schemaName, p->shadowChunksName, chunk_rowid, expected, actual); + rc = SQLITE_ERROR; + goto cleanup; + } + rc = sqlite3_blob_write(blobChunksRowids, &rowid, sizeof(i64), + chunk_offset * sizeof(i64)); + if (rc != SQLITE_OK) { + vtab_set_error( + &p->base, VEC_INTERAL_ERROR "could not write rowids blob on %s.%s.%lld", + p->schemaName, p->shadowChunksName, chunk_rowid); + rc = SQLITE_ERROR; + goto cleanup; + } + + // Now with all the vectors inserted, go back and update the _rowids table + // with the new chunk_rowid/chunk_offset values + rc = vec0_rowids_update_position(p, rowid, chunk_rowid, chunk_offset); + +cleanup: + brc = sqlite3_blob_close(blobChunksRowids); + if ((rc == SQLITE_OK) && (brc != SQLITE_OK)) { + vtab_set_error( + &p->base, VEC_INTERAL_ERROR "could not close rowids blob on %s.%s.%lld", + p->schemaName, p->shadowChunksName, chunk_rowid); + return brc; + } + return rc; +} + +int vec0_write_metadata_value(vec0_vtab *p, int metadata_column_idx, i64 rowid, i64 chunk_id, i64 chunk_offset, sqlite3_value * v, int isupdate) { + int rc; + struct Vec0MetadataColumnDefinition * metadata_column = &p->metadata_columns[metadata_column_idx]; + vec0_metadata_column_kind kind = metadata_column->kind; + + // verify input value matches column type + switch(kind) { + case VEC0_METADATA_COLUMN_KIND_BOOLEAN: { + if(sqlite3_value_type(v) != SQLITE_INTEGER || ((sqlite3_value_int(v) != 0) && (sqlite3_value_int(v) != 1))) { + rc = SQLITE_ERROR; + vtab_set_error(&p->base, "Expected 0 or 1 for BOOLEAN metadata column %.*s", metadata_column->name_length, metadata_column->name); + goto done; + } + break; + } + case VEC0_METADATA_COLUMN_KIND_INTEGER: { + if(sqlite3_value_type(v) != SQLITE_INTEGER) { + rc = SQLITE_ERROR; + vtab_set_error(&p->base, "Expected integer for INTEGER metadata column %.*s, received %s", metadata_column->name_length, metadata_column->name, type_name(sqlite3_value_type(v))); + goto done; + } + break; + } + case VEC0_METADATA_COLUMN_KIND_FLOAT: { + if(sqlite3_value_type(v) != SQLITE_FLOAT) { + rc = SQLITE_ERROR; + vtab_set_error(&p->base, "Expected float for FLOAT metadata column %.*s, received %s", metadata_column->name_length, metadata_column->name, type_name(sqlite3_value_type(v))); + goto done; + } + break; + } + case VEC0_METADATA_COLUMN_KIND_TEXT: { + if(sqlite3_value_type(v) != SQLITE_TEXT) { + rc = SQLITE_ERROR; + vtab_set_error(&p->base, "Expected text for TEXT metadata column %.*s, received %s", metadata_column->name_length, metadata_column->name, type_name(sqlite3_value_type(v))); + goto done; + } + break; + } + } + + sqlite3_blob * blobValue = NULL; + rc = sqlite3_blob_open(p->db, p->schemaName, p->shadowMetadataChunksNames[metadata_column_idx], "data", chunk_id, 1, &blobValue); + if(rc != SQLITE_OK) { + goto done; + } + + switch(kind) { + case VEC0_METADATA_COLUMN_KIND_BOOLEAN: { + u8 block; + int value = sqlite3_value_int(v); + rc = sqlite3_blob_read(blobValue, &block, sizeof(u8), (int) (chunk_offset / CHAR_BIT)); + if(rc != SQLITE_OK) { + goto done; + } + + if (value) { + block |= 1 << (chunk_offset % CHAR_BIT); + } else { + block &= ~(1 << (chunk_offset % CHAR_BIT)); + } + + rc = sqlite3_blob_write(blobValue, &block, sizeof(u8), chunk_offset / CHAR_BIT); + break; + } + case VEC0_METADATA_COLUMN_KIND_INTEGER: { + i64 value = sqlite3_value_int64(v); + rc = sqlite3_blob_write(blobValue, &value, sizeof(value), chunk_offset * sizeof(i64)); + break; + } + case VEC0_METADATA_COLUMN_KIND_FLOAT: { + double value = sqlite3_value_double(v); + rc = sqlite3_blob_write(blobValue, &value, sizeof(value), chunk_offset * sizeof(double)); + break; + } + case VEC0_METADATA_COLUMN_KIND_TEXT: { + int prev_n; + rc = sqlite3_blob_read(blobValue, &prev_n, sizeof(int), chunk_offset * VEC0_METADATA_TEXT_VIEW_BUFFER_LENGTH); + if(rc != SQLITE_OK) { + goto done; + } + + const char * s = (const char *) sqlite3_value_text(v); + int n = sqlite3_value_bytes(v); + u8 view[VEC0_METADATA_TEXT_VIEW_BUFFER_LENGTH]; + memset(view, 0, VEC0_METADATA_TEXT_VIEW_BUFFER_LENGTH); + memcpy(view, &n, sizeof(int)); + memcpy(view+4, s, min(n, VEC0_METADATA_TEXT_VIEW_BUFFER_LENGTH-4)); + + rc = sqlite3_blob_write(blobValue, &view, VEC0_METADATA_TEXT_VIEW_BUFFER_LENGTH, chunk_offset * VEC0_METADATA_TEXT_VIEW_BUFFER_LENGTH); + if(n > VEC0_METADATA_TEXT_VIEW_DATA_LENGTH) { + const char * zSql; + + if(isupdate && (prev_n > VEC0_METADATA_TEXT_VIEW_DATA_LENGTH)) { + zSql = sqlite3_mprintf("UPDATE " VEC0_SHADOW_METADATA_TEXT_DATA_NAME " SET data = ?2 WHERE rowid = ?1", p->schemaName, p->tableName, metadata_column_idx); + }else { + zSql = sqlite3_mprintf("INSERT INTO " VEC0_SHADOW_METADATA_TEXT_DATA_NAME " (rowid, data) VALUES (?1, ?2)", p->schemaName, p->tableName, metadata_column_idx); + } + if(!zSql) { + rc = SQLITE_NOMEM; + goto done; + } + sqlite3_stmt * stmt; + rc = sqlite3_prepare_v2(p->db, zSql, -1, &stmt, NULL); + if(rc != SQLITE_OK) { + goto done; + } + sqlite3_bind_int64(stmt, 1, rowid); + sqlite3_bind_text(stmt, 2, s, n, SQLITE_STATIC); + rc = sqlite3_step(stmt); + sqlite3_finalize(stmt); + + if(rc != SQLITE_DONE) { + rc = SQLITE_ERROR; + goto done; + } + } + else if(prev_n > VEC0_METADATA_TEXT_VIEW_DATA_LENGTH) { + const char * zSql = sqlite3_mprintf("DELETE FROM " VEC0_SHADOW_METADATA_TEXT_DATA_NAME " WHERE rowid = ?", p->schemaName, p->tableName, metadata_column_idx); + if(!zSql) { + rc = SQLITE_NOMEM; + goto done; + } + sqlite3_stmt * stmt; + rc = sqlite3_prepare_v2(p->db, zSql, -1, &stmt, NULL); + if(rc != SQLITE_OK) { + goto done; + } + sqlite3_bind_int64(stmt, 1, rowid); + rc = sqlite3_step(stmt); + sqlite3_finalize(stmt); + + if(rc != SQLITE_DONE) { + rc = SQLITE_ERROR; + goto done; + } + } + break; + } + } + + if(rc != SQLITE_OK) { + + } + rc = sqlite3_blob_close(blobValue); + if(rc != SQLITE_OK) { + goto done; + } + + done: + return rc; +} + + +/** + * @brief Handles INSERT INTO operations on a vec0 table. + * + * @return int SQLITE_OK on success, otherwise error code on failure + */ +int vec0Update_Insert(sqlite3_vtab *pVTab, int argc, sqlite3_value **argv, + sqlite_int64 *pRowid) { + UNUSED_PARAMETER(argc); + vec0_vtab *p = (vec0_vtab *)pVTab; + int rc; + // Rowid for the inserted row, deterimined by the inserted ID + _rowids shadow + // table + i64 rowid; + + // Array to hold the vector data of the inserted row. Individual elements will + // have a lifetime bound to the argv[..] values. + void *vectorDatas[VEC0_MAX_VECTOR_COLUMNS]; + // Array to hold cleanup functions for vectorDatas[] + vector_cleanup cleanups[VEC0_MAX_VECTOR_COLUMNS]; + + sqlite3_value * partitionKeyValues[VEC0_MAX_PARTITION_COLUMNS]; + + // Rowid of the chunk in the _chunks shadow table that the row will be a part + // of. + i64 chunk_rowid; + // offset within the chunk where the rowid belongs + i64 chunk_offset; + + // a write-able blob of the validity column for the given chunk. Used to mark + // validity bit + sqlite3_blob *blobChunksValidity = NULL; + // buffer for the valididty column for the given chunk. Maybe not needed here? + const unsigned char *bufferChunksValidity = NULL; + int numReadVectors = 0; + + // Read all provided partition key values into partitionKeyValues + for (int i = 0; i < vec0_num_defined_user_columns(p); i++) { + if(p->user_column_kinds[i] != SQLITE_VEC0_USER_COLUMN_KIND_PARTITION) { + continue; + } + int partition_key_idx = p->user_column_idxs[i]; + partitionKeyValues[partition_key_idx] = argv[2+VEC0_COLUMN_USERN_START + i]; + + int new_value_type = sqlite3_value_type(partitionKeyValues[partition_key_idx]); + if((new_value_type != SQLITE_NULL) && (new_value_type != p->paritition_columns[partition_key_idx].type)) { + // IMP: V11454_28292 + vtab_set_error( + pVTab, + "Parition key type mismatch: The partition key column %.*s has type %s, but %s was provided.", + p->paritition_columns[partition_key_idx].name_length, + p->paritition_columns[partition_key_idx].name, + type_name(p->paritition_columns[partition_key_idx].type), + type_name(new_value_type) + ); + rc = SQLITE_ERROR; + goto cleanup; + } + } + + // read all the inserted vectors into vectorDatas, validate their lengths. + for (int i = 0; i < vec0_num_defined_user_columns(p); i++) { + if(p->user_column_kinds[i] != SQLITE_VEC0_USER_COLUMN_KIND_VECTOR) { + continue; + } + int vector_column_idx = p->user_column_idxs[i]; + sqlite3_value *valueVector = argv[2 + VEC0_COLUMN_USERN_START + i]; + size_t dimensions; + + char *pzError; + enum VectorElementType elementType; + rc = vector_from_value(valueVector, &vectorDatas[vector_column_idx], &dimensions, + &elementType, &cleanups[vector_column_idx], &pzError); + if (rc != SQLITE_OK) { + // IMP: V06519_23358 + vtab_set_error( + pVTab, "Inserted vector for the \"%.*s\" column is invalid: %z", + p->vector_columns[vector_column_idx].name_length, p->vector_columns[vector_column_idx].name, pzError); + rc = SQLITE_ERROR; + goto cleanup; + } + + numReadVectors++; + if (elementType != p->vector_columns[vector_column_idx].element_type) { + // IMP: V08221_25059 + vtab_set_error( + pVTab, + "Inserted vector for the \"%.*s\" column is expected to be of type " + "%s, but a %s vector was provided.", + p->vector_columns[i].name_length, p->vector_columns[i].name, + vector_subtype_name(p->vector_columns[i].element_type), + vector_subtype_name(elementType)); + rc = SQLITE_ERROR; + goto cleanup; + } + + if (dimensions != p->vector_columns[vector_column_idx].dimensions) { + // IMP: V01145_17984 + vtab_set_error( + pVTab, + "Dimension mismatch for inserted vector for the \"%.*s\" column. " + "Expected %d dimensions but received %d.", + p->vector_columns[vector_column_idx].name_length, p->vector_columns[vector_column_idx].name, + p->vector_columns[vector_column_idx].dimensions, dimensions); + rc = SQLITE_ERROR; + goto cleanup; + } + } + + // Cannot insert a value in the hidden "distance" column + if (sqlite3_value_type(argv[2 + vec0_column_distance_idx(p)]) != + SQLITE_NULL) { + // IMP: V24228_08298 + vtab_set_error(pVTab, + "A value was provided for the hidden \"distance\" column."); + rc = SQLITE_ERROR; + goto cleanup; + } + // Cannot insert a value in the hidden "k" column + if (sqlite3_value_type(argv[2 + vec0_column_k_idx(p)]) != SQLITE_NULL) { + // IMP: V11875_28713 + vtab_set_error(pVTab, "A value was provided for the hidden \"k\" column."); + rc = SQLITE_ERROR; + goto cleanup; + } + + // Step #1: Insert/get a rowid for this row, from the _rowids table. + rc = vec0Update_InsertRowidStep(p, argv[2 + VEC0_COLUMN_ID], &rowid); + if (rc != SQLITE_OK) { + goto cleanup; + } + + // Step #2: Find the next "available" position in the _chunks table for this + // row. + rc = vec0Update_InsertNextAvailableStep(p, partitionKeyValues, + &chunk_rowid, &chunk_offset, + &blobChunksValidity, + &bufferChunksValidity); + if (rc != SQLITE_OK) { + goto cleanup; + } + + // Step #3: With the next available chunk position, write out all the vectors + // to their specified location. + rc = vec0Update_InsertWriteFinalStep(p, chunk_rowid, chunk_offset, rowid, + vectorDatas, blobChunksValidity, + bufferChunksValidity); + if (rc != SQLITE_OK) { + goto cleanup; + } + + if(p->numAuxiliaryColumns > 0) { + sqlite3_stmt *stmt; + sqlite3_str * s = sqlite3_str_new(NULL); + sqlite3_str_appendf(s, "INSERT INTO " VEC0_SHADOW_AUXILIARY_NAME "(rowid ", p->schemaName, p->tableName); + for(int i = 0; i < p->numAuxiliaryColumns; i++) { + sqlite3_str_appendf(s, ", value%02d", i); + } + sqlite3_str_appendall(s, ") VALUES (? "); + for(int i = 0; i < p->numAuxiliaryColumns; i++) { + sqlite3_str_appendall(s, ", ?"); + } + sqlite3_str_appendall(s, ")"); + char * zSql = sqlite3_str_finish(s); + // TODO double check error handling ehre + if(!zSql) { + rc = SQLITE_NOMEM; + goto cleanup; + } + rc = sqlite3_prepare_v2(p->db, zSql, -1, &stmt, NULL); + if(rc != SQLITE_OK) { + goto cleanup; + } + sqlite3_bind_int64(stmt, 1, rowid); + + for (int i = 0; i < vec0_num_defined_user_columns(p); i++) { + if(p->user_column_kinds[i] != SQLITE_VEC0_USER_COLUMN_KIND_AUXILIARY) { + continue; + } + int auxiliary_key_idx = p->user_column_idxs[i]; + sqlite3_value * v = argv[2+VEC0_COLUMN_USERN_START + i]; + int v_type = sqlite3_value_type(v); + if(v_type != SQLITE_NULL && (v_type != p->auxiliary_columns[auxiliary_key_idx].type)) { + sqlite3_finalize(stmt); + rc = SQLITE_CONSTRAINT; + vtab_set_error( + pVTab, + "Auxiliary column type mismatch: The auxiliary column %.*s has type %s, but %s was provided.", + p->auxiliary_columns[auxiliary_key_idx].name_length, + p->auxiliary_columns[auxiliary_key_idx].name, + type_name(p->auxiliary_columns[auxiliary_key_idx].type), + type_name(v_type) + ); + goto cleanup; + } + // first 1 is for 1-based indexing on sqlite3_bind_*, second 1 is to account for initial rowid parameter + sqlite3_bind_value(stmt, 1 + 1 + auxiliary_key_idx, v); + } + + rc = sqlite3_step(stmt); + if(rc != SQLITE_DONE) { + sqlite3_finalize(stmt); + rc = SQLITE_ERROR; + goto cleanup; + } + sqlite3_finalize(stmt); + } + + + for(int i = 0; i < vec0_num_defined_user_columns(p); i++) { + if(p->user_column_kinds[i] != SQLITE_VEC0_USER_COLUMN_KIND_METADATA) { + continue; + } + int metadata_idx = p->user_column_idxs[i]; + sqlite3_value *v = argv[2 + VEC0_COLUMN_USERN_START + i]; + rc = vec0_write_metadata_value(p, metadata_idx, rowid, chunk_rowid, chunk_offset, v, 0); + if(rc != SQLITE_OK) { + goto cleanup; + } + } + + *pRowid = rowid; + rc = SQLITE_OK; + +cleanup: + for (int i = 0; i < numReadVectors; i++) { + cleanups[i](vectorDatas[i]); + } + sqlite3_free((void *)bufferChunksValidity); + int brc = sqlite3_blob_close(blobChunksValidity); + if ((rc == SQLITE_OK) && (brc != SQLITE_OK)) { + vtab_set_error(&p->base, + VEC_INTERAL_ERROR "unknown error, blobChunksValidity could " + "not be closed, please file an issue"); + return brc; + } + return rc; +} + +int vec0Update_Delete_ClearValidity(vec0_vtab *p, i64 chunk_id, + u64 chunk_offset) { + int rc, brc; + sqlite3_blob *blobChunksValidity = NULL; + char unsigned bx; + int validityOffset = chunk_offset / CHAR_BIT; + + // 2. ensure chunks.validity bit is 1, then set to 0 + rc = sqlite3_blob_open(p->db, p->schemaName, p->shadowChunksName, "validity", + chunk_id, 1, &blobChunksValidity); + if (rc != SQLITE_OK) { + // IMP: V26002_10073 + vtab_set_error(&p->base, "could not open validity blob for %s.%s.%lld", + p->schemaName, p->shadowChunksName, chunk_id); + return SQLITE_ERROR; + } + // will skip the sqlite3_blob_bytes(blobChunksValidity) check for now, + // the read below would catch it + + rc = sqlite3_blob_read(blobChunksValidity, &bx, sizeof(bx), validityOffset); + if (rc != SQLITE_OK) { + // IMP: V21193_05263 + vtab_set_error( + &p->base, "could not read validity blob for %s.%s.%lld at %d", + p->schemaName, p->shadowChunksName, chunk_id, validityOffset); + goto cleanup; + } + if (!(bx >> (chunk_offset % CHAR_BIT))) { + // IMP: V21193_05263 + rc = SQLITE_ERROR; + vtab_set_error( + &p->base, + "vec0 deletion error: validity bit is not set for %s.%s.%lld at %d", + p->schemaName, p->shadowChunksName, chunk_id, validityOffset); + goto cleanup; + } + char unsigned mask = ~(1 << (chunk_offset % CHAR_BIT)); + char result = bx & mask; + rc = sqlite3_blob_write(blobChunksValidity, &result, sizeof(bx), + validityOffset); + if (rc != SQLITE_OK) { + vtab_set_error( + &p->base, "could not write to validity blob for %s.%s.%lld at %d", + p->schemaName, p->shadowChunksName, chunk_id, validityOffset); + goto cleanup; + } + +cleanup: + + brc = sqlite3_blob_close(blobChunksValidity); + if (rc != SQLITE_OK) + return rc; + if (brc != SQLITE_OK) { + vtab_set_error(&p->base, + "vec0 deletion error: Error commiting validity blob " + "transaction on %s.%s.%lld at %d", + p->schemaName, p->shadowChunksName, chunk_id, + validityOffset); + return brc; + } + return SQLITE_OK; +} + +int vec0Update_Delete_DeleteRowids(vec0_vtab *p, i64 rowid) { + int rc; + sqlite3_stmt *stmt = NULL; + + char *zSql = + sqlite3_mprintf("DELETE FROM " VEC0_SHADOW_ROWIDS_NAME " WHERE rowid = ?", + p->schemaName, p->tableName); + if (!zSql) { + return SQLITE_NOMEM; + } + + rc = sqlite3_prepare_v2(p->db, zSql, -1, &stmt, NULL); + sqlite3_free(zSql); + if (rc != SQLITE_OK) { + goto cleanup; + } + sqlite3_bind_int64(stmt, 1, rowid); + rc = sqlite3_step(stmt); + if (rc != SQLITE_DONE) { + goto cleanup; + } + rc = SQLITE_OK; + +cleanup: + sqlite3_finalize(stmt); + return rc; +} + +int vec0Update_Delete_DeleteAux(vec0_vtab *p, i64 rowid) { + int rc; + sqlite3_stmt *stmt = NULL; + + char *zSql = + sqlite3_mprintf("DELETE FROM " VEC0_SHADOW_AUXILIARY_NAME " WHERE rowid = ?", + p->schemaName, p->tableName); + if (!zSql) { + return SQLITE_NOMEM; + } + + rc = sqlite3_prepare_v2(p->db, zSql, -1, &stmt, NULL); + sqlite3_free(zSql); + if (rc != SQLITE_OK) { + goto cleanup; + } + sqlite3_bind_int64(stmt, 1, rowid); + rc = sqlite3_step(stmt); + if (rc != SQLITE_DONE) { + goto cleanup; + } + rc = SQLITE_OK; + +cleanup: + sqlite3_finalize(stmt); + return rc; +} + +int vec0Update_Delete_ClearMetadata(vec0_vtab *p, int metadata_idx, i64 rowid, i64 chunk_id, + u64 chunk_offset) { + int rc; + sqlite3_blob * blobValue; + vec0_metadata_column_kind kind = p->metadata_columns[metadata_idx].kind; + rc = sqlite3_blob_open(p->db, p->schemaName, p->shadowMetadataChunksNames[metadata_idx], "data", chunk_id, 1, &blobValue); + if(rc != SQLITE_OK) { + return rc; + } + + switch(kind) { + case VEC0_METADATA_COLUMN_KIND_BOOLEAN: { + u8 block; + rc = sqlite3_blob_read(blobValue, &block, sizeof(u8), (int) (chunk_offset / CHAR_BIT)); + if(rc != SQLITE_OK) { + goto done; + } + + block &= ~(1 << (chunk_offset % CHAR_BIT)); + rc = sqlite3_blob_write(blobValue, &block, sizeof(u8), chunk_offset / CHAR_BIT); + break; + } + case VEC0_METADATA_COLUMN_KIND_INTEGER: { + i64 v = 0; + rc = sqlite3_blob_write(blobValue, &v, sizeof(v), chunk_offset * sizeof(i64)); + break; + } + case VEC0_METADATA_COLUMN_KIND_FLOAT: { + double v = 0; + rc = sqlite3_blob_write(blobValue, &v, sizeof(v), chunk_offset * sizeof(double)); + break; + } + case VEC0_METADATA_COLUMN_KIND_TEXT: { + int n; + rc = sqlite3_blob_read(blobValue, &n, sizeof(int), chunk_offset * VEC0_METADATA_TEXT_VIEW_BUFFER_LENGTH); + if(rc != SQLITE_OK) { + goto done; + } + + u8 view[VEC0_METADATA_TEXT_VIEW_BUFFER_LENGTH]; + memset(view, 0, VEC0_METADATA_TEXT_VIEW_BUFFER_LENGTH); + rc = sqlite3_blob_write(blobValue, &view, sizeof(view), chunk_offset * VEC0_METADATA_TEXT_VIEW_BUFFER_LENGTH); + if(rc != SQLITE_OK) { + goto done; + } + + if(n > VEC0_METADATA_TEXT_VIEW_DATA_LENGTH) { + const char * zSql = sqlite3_mprintf("DELETE FROM " VEC0_SHADOW_METADATA_TEXT_DATA_NAME " WHERE rowid = ?", p->schemaName, p->tableName, metadata_idx); + if(!zSql) { + rc = SQLITE_NOMEM; + goto done; + } + sqlite3_stmt * stmt; + rc = sqlite3_prepare_v2(p->db, zSql, -1, &stmt, NULL); + if(rc != SQLITE_OK) { + goto done; + } + sqlite3_bind_int64(stmt, 1, rowid); + rc = sqlite3_step(stmt); + if(rc != SQLITE_DONE) { + rc = SQLITE_ERROR; + goto done; + } + sqlite3_finalize(stmt); + } + break; + } + } + int rc2; + done: + rc2 = sqlite3_blob_close(blobValue); + if(rc == SQLITE_OK) { + return rc2; + } + return rc; +} + +int vec0Update_Delete(sqlite3_vtab *pVTab, sqlite3_value *idValue) { + vec0_vtab *p = (vec0_vtab *)pVTab; + int rc; + i64 rowid; + i64 chunk_id; + i64 chunk_offset; + + if (p->pkIsText) { + rc = vec0_rowid_from_id(p, idValue, &rowid); + if (rc != SQLITE_OK) { + return rc; + } + } else { + rowid = sqlite3_value_int64(idValue); + } + + // 1. Find chunk position for given rowid + // 2. Ensure that validity bit for position is 1, then set to 0 + // 3. Zero out rowid in chunks.rowid + // 4. Zero out vector data in all vector column chunks + // 5. Delete value in _rowids table + + // 1. get chunk_id and chunk_offset from _rowids + rc = vec0_get_chunk_position(p, rowid, NULL, &chunk_id, &chunk_offset); + if (rc != SQLITE_OK) { + return rc; + } + + rc = vec0Update_Delete_ClearValidity(p, chunk_id, chunk_offset); + if (rc != SQLITE_OK) { + return rc; + } + + // 3. zero out rowid in chunks.rowids + // https://github.com/asg017/sqlite-vec/issues/54 + + // 4. zero out any data in vector chunks tables + // https://github.com/asg017/sqlite-vec/issues/54 + + // 5. delete from _rowids table + rc = vec0Update_Delete_DeleteRowids(p, rowid); + if (rc != SQLITE_OK) { + return rc; + } + + // 6. delete any auxiliary rows + if(p->numAuxiliaryColumns > 0) { + rc = vec0Update_Delete_DeleteAux(p, rowid); + if (rc != SQLITE_OK) { + return rc; + } + } + + // 6. delete metadata + for(int i = 0; i < p->numMetadataColumns; i++) { + rc = vec0Update_Delete_ClearMetadata(p, i, rowid, chunk_id, chunk_offset); + } + + return SQLITE_OK; +} + +int vec0Update_UpdateAuxColumn(vec0_vtab *p, int auxiliary_column_idx, sqlite3_value * value, i64 rowid) { + int rc; + sqlite3_stmt *stmt; + const char * zSql = sqlite3_mprintf("UPDATE " VEC0_SHADOW_AUXILIARY_NAME " SET value%02d = ? WHERE rowid = ?", p->schemaName, p->tableName, auxiliary_column_idx); + if(!zSql) { + return SQLITE_NOMEM; + } + rc = sqlite3_prepare_v2(p->db, zSql, -1, &stmt, NULL); + if(rc != SQLITE_OK) { + return rc; + } + sqlite3_bind_value(stmt, 1, value); + sqlite3_bind_int64(stmt, 2, rowid); + rc = sqlite3_step(stmt); + if(rc != SQLITE_DONE) { + sqlite3_finalize(stmt); + return SQLITE_ERROR; + } + sqlite3_finalize(stmt); + return SQLITE_OK; +} + +int vec0Update_UpdateVectorColumn(vec0_vtab *p, i64 chunk_id, i64 chunk_offset, + int i, sqlite3_value *valueVector) { + int rc; + + sqlite3_blob *blobVectors = NULL; + + char *pzError; + size_t dimensions; + enum VectorElementType elementType; + void *vector; + vector_cleanup cleanup = vector_cleanup_noop; + // https://github.com/asg017/sqlite-vec/issues/53 + rc = vector_from_value(valueVector, &vector, &dimensions, &elementType, + &cleanup, &pzError); + if (rc != SQLITE_OK) { + // IMP: V15203_32042 + vtab_set_error( + &p->base, "Updated vector for the \"%.*s\" column is invalid: %z", + p->vector_columns[i].name_length, p->vector_columns[i].name, pzError); + rc = SQLITE_ERROR; + goto cleanup; + } + if (elementType != p->vector_columns[i].element_type) { + // IMP: V03643_20481 + vtab_set_error( + &p->base, + "Updated vector for the \"%.*s\" column is expected to be of type " + "%s, but a %s vector was provided.", + p->vector_columns[i].name_length, p->vector_columns[i].name, + vector_subtype_name(p->vector_columns[i].element_type), + vector_subtype_name(elementType)); + rc = SQLITE_ERROR; + goto cleanup; + } + if (dimensions != p->vector_columns[i].dimensions) { + // IMP: V25739_09810 + vtab_set_error( + &p->base, + "Dimension mismatch for new updated vector for the \"%.*s\" column. " + "Expected %d dimensions but received %d.", + p->vector_columns[i].name_length, p->vector_columns[i].name, + p->vector_columns[i].dimensions, dimensions); + rc = SQLITE_ERROR; + goto cleanup; + } + + rc = sqlite3_blob_open(p->db, p->schemaName, p->shadowVectorChunksNames[i], + "vectors", chunk_id, 1, &blobVectors); + if (rc != SQLITE_OK) { + vtab_set_error(&p->base, "Could not open vectors blob for %s.%s.%lld", + p->schemaName, p->shadowVectorChunksNames[i], chunk_id); + goto cleanup; + } + rc = vec0_write_vector_to_vector_blob(blobVectors, chunk_offset, vector, + p->vector_columns[i].dimensions, + p->vector_columns[i].element_type); + if (rc != SQLITE_OK) { + vtab_set_error(&p->base, "Could not write to vectors blob for %s.%s.%lld", + p->schemaName, p->shadowVectorChunksNames[i], chunk_id); + goto cleanup; + } + +cleanup: + cleanup(vector); + int brc = sqlite3_blob_close(blobVectors); + if (rc != SQLITE_OK) { + return rc; + } + if (brc != SQLITE_OK) { + vtab_set_error( + &p->base, + "Could not commit blob transaction for vectors blob for %s.%s.%lld", + p->schemaName, p->shadowVectorChunksNames[i], chunk_id); + return brc; + } + return SQLITE_OK; +} + +int vec0Update_Update(sqlite3_vtab *pVTab, int argc, sqlite3_value **argv) { + UNUSED_PARAMETER(argc); + vec0_vtab *p = (vec0_vtab *)pVTab; + int rc; + i64 chunk_id; + i64 chunk_offset; + + i64 rowid; + if (p->pkIsText) { + const char *a = (const char *)sqlite3_value_text(argv[0]); + const char *b = (const char *)sqlite3_value_text(argv[1]); + // IMP: V08886_25725 + if ((sqlite3_value_bytes(argv[0]) != sqlite3_value_bytes(argv[1])) || + strncmp(a, b, sqlite3_value_bytes(argv[0])) != 0) { + vtab_set_error(pVTab, + "UPDATEs on vec0 primary key values are not allowed."); + return SQLITE_ERROR; + } + rc = vec0_rowid_from_id(p, argv[0], &rowid); + if (rc != SQLITE_OK) { + return rc; + } + } else { + rowid = sqlite3_value_int64(argv[0]); + } + + // 1) get chunk_id and chunk_offset from _rowids + rc = vec0_get_chunk_position(p, rowid, NULL, &chunk_id, &chunk_offset); + if (rc != SQLITE_OK) { + return rc; + } + + // 2) update any partition key values + for (int i = 0; i < vec0_num_defined_user_columns(p); i++) { + if(p->user_column_kinds[i] != SQLITE_VEC0_USER_COLUMN_KIND_PARTITION) { + continue; + } + sqlite3_value * value = argv[2+VEC0_COLUMN_USERN_START + i]; + if(sqlite3_value_nochange(value)) { + continue; + } + vtab_set_error(pVTab, "UPDATE on partition key columns are not supported yet. "); + return SQLITE_ERROR; + } + + // 3) handle auxiliary column updates + for (int i = 0; i < vec0_num_defined_user_columns(p); i++) { + if(p->user_column_kinds[i] != SQLITE_VEC0_USER_COLUMN_KIND_AUXILIARY) { + continue; + } + int auxiliary_column_idx = p->user_column_idxs[i]; + sqlite3_value * value = argv[2+VEC0_COLUMN_USERN_START + i]; + if(sqlite3_value_nochange(value)) { + continue; + } + rc = vec0Update_UpdateAuxColumn(p, auxiliary_column_idx, value, rowid); + if(rc != SQLITE_OK) { + return SQLITE_ERROR; + } + } + + // 4) handle metadata column updates + for (int i = 0; i < vec0_num_defined_user_columns(p); i++) { + if(p->user_column_kinds[i] != SQLITE_VEC0_USER_COLUMN_KIND_METADATA) { + continue; + } + int metadata_column_idx = p->user_column_idxs[i]; + sqlite3_value * value = argv[2+VEC0_COLUMN_USERN_START + i]; + if(sqlite3_value_nochange(value)) { + continue; + } + rc = vec0_write_metadata_value(p, metadata_column_idx, rowid, chunk_id, chunk_offset, value, 1); + if(rc != SQLITE_OK) { + return rc; + } + } + + // 5) iterate over all new vectors, update the vectors + for (int i = 0; i < vec0_num_defined_user_columns(p); i++) { + if(p->user_column_kinds[i] != SQLITE_VEC0_USER_COLUMN_KIND_VECTOR) { + continue; + } + int vector_idx = p->user_column_idxs[i]; + sqlite3_value *valueVector = argv[2 + VEC0_COLUMN_USERN_START + i]; + // in vec0Column, we check sqlite3_vtab_nochange() on vector columns. + // If the vector column isn't being changed, we return NULL; + // That's not great, that means vector columns can never be NULLABLE + // (bc we cant distinguish if an updated vector is truly NULL or nochange). + // Also it means that if someone tries to run `UPDATE v SET X = NULL`, + // we can't effectively detect and raise an error. + // A better solution would be to use a custom result_type for "empty", + // but subtypes don't appear to survive xColumn -> xUpdate, it's always 0. + // So for now, we'll just use NULL and warn people to not SET X = NULL + // in the docs. + if (sqlite3_value_type(valueVector) == SQLITE_NULL) { + continue; + } + + rc = vec0Update_UpdateVectorColumn(p, chunk_id, chunk_offset, vector_idx, + valueVector); + if (rc != SQLITE_OK) { + return SQLITE_ERROR; + } + } + + return SQLITE_OK; +} + +static int vec0Update(sqlite3_vtab *pVTab, int argc, sqlite3_value **argv, + sqlite_int64 *pRowid) { + // DELETE operation + if (argc == 1 && sqlite3_value_type(argv[0]) != SQLITE_NULL) { + return vec0Update_Delete(pVTab, argv[0]); + } + // INSERT operation + else if (argc > 1 && sqlite3_value_type(argv[0]) == SQLITE_NULL) { + return vec0Update_Insert(pVTab, argc, argv, pRowid); + } + // UPDATE operation + else if (argc > 1 && sqlite3_value_type(argv[0]) != SQLITE_NULL) { + return vec0Update_Update(pVTab, argc, argv); + } else { + vtab_set_error(pVTab, "Unrecognized xUpdate operation provided for vec0."); + return SQLITE_ERROR; + } +} + +static int vec0ShadowName(const char *zName) { + static const char *azName[] = { + "rowids", "chunks", "auxiliary", "info", + + // Up to VEC0_MAX_METADATA_COLUMNS + // TODO be smarter about this man + "metadatachunks00", + "metadatachunks01", + "metadatachunks02", + "metadatachunks03", + "metadatachunks04", + "metadatachunks05", + "metadatachunks06", + "metadatachunks07", + "metadatachunks08", + "metadatachunks09", + "metadatachunks10", + "metadatachunks11", + "metadatachunks12", + "metadatachunks13", + "metadatachunks14", + "metadatachunks15", + + // Up to + "metadatatext00", + "metadatatext01", + "metadatatext02", + "metadatatext03", + "metadatatext04", + "metadatatext05", + "metadatatext06", + "metadatatext07", + "metadatatext08", + "metadatatext09", + "metadatatext10", + "metadatatext11", + "metadatatext12", + "metadatatext13", + "metadatatext14", + "metadatatext15", + }; + + for (size_t i = 0; i < sizeof(azName) / sizeof(azName[0]); i++) { + if (sqlite3_stricmp(zName, azName[i]) == 0) + return 1; + } + //for(size_t i = 0; i < )"vector_chunks", "metadatachunks" + return 0; +} + +static int vec0Begin(sqlite3_vtab *pVTab) { + UNUSED_PARAMETER(pVTab); + return SQLITE_OK; +} +static int vec0Sync(sqlite3_vtab *pVTab) { + UNUSED_PARAMETER(pVTab); + vec0_vtab *p = (vec0_vtab *)pVTab; + if (p->stmtLatestChunk) { + sqlite3_finalize(p->stmtLatestChunk); + p->stmtLatestChunk = NULL; + } + if (p->stmtRowidsInsertRowid) { + sqlite3_finalize(p->stmtRowidsInsertRowid); + p->stmtRowidsInsertRowid = NULL; + } + if (p->stmtRowidsInsertId) { + sqlite3_finalize(p->stmtRowidsInsertId); + p->stmtRowidsInsertId = NULL; + } + if (p->stmtRowidsUpdatePosition) { + sqlite3_finalize(p->stmtRowidsUpdatePosition); + p->stmtRowidsUpdatePosition = NULL; + } + if (p->stmtRowidsGetChunkPosition) { + sqlite3_finalize(p->stmtRowidsGetChunkPosition); + p->stmtRowidsGetChunkPosition = NULL; + } + return SQLITE_OK; +} +static int vec0Commit(sqlite3_vtab *pVTab) { + UNUSED_PARAMETER(pVTab); + return SQLITE_OK; +} +static int vec0Rollback(sqlite3_vtab *pVTab) { + UNUSED_PARAMETER(pVTab); + return SQLITE_OK; +} + +static sqlite3_module vec0Module = { + /* iVersion */ 3, + /* xCreate */ vec0Create, + /* xConnect */ vec0Connect, + /* xBestIndex */ vec0BestIndex, + /* xDisconnect */ vec0Disconnect, + /* xDestroy */ vec0Destroy, + /* xOpen */ vec0Open, + /* xClose */ vec0Close, + /* xFilter */ vec0Filter, + /* xNext */ vec0Next, + /* xEof */ vec0Eof, + /* xColumn */ vec0Column, + /* xRowid */ vec0Rowid, + /* xUpdate */ vec0Update, + /* xBegin */ vec0Begin, + /* xSync */ vec0Sync, + /* xCommit */ vec0Commit, + /* xRollback */ vec0Rollback, + /* xFindFunction */ 0, + /* xRename */ 0, // https://github.com/asg017/sqlite-vec/issues/43 + /* xSavepoint */ 0, + /* xRelease */ 0, + /* xRollbackTo */ 0, + /* xShadowName */ vec0ShadowName, +#if SQLITE_VERSION_NUMBER >= 3044000 + /* xIntegrity */ 0, // https://github.com/asg017/sqlite-vec/issues/44 +#endif +}; +#pragma endregion + +static char *POINTER_NAME_STATIC_BLOB_DEF = "vec0-static_blob_def"; +struct static_blob_definition { + void *p; + size_t dimensions; + size_t nvectors; + enum VectorElementType element_type; +}; +static void vec_static_blob_from_raw(sqlite3_context *context, int argc, + sqlite3_value **argv) { + + assert(argc == 4); + struct static_blob_definition *p; + p = sqlite3_malloc(sizeof(*p)); + if (!p) { + sqlite3_result_error_nomem(context); + return; + } + memset(p, 0, sizeof(*p)); + p->p = (void *)sqlite3_value_int64(argv[0]); + p->element_type = SQLITE_VEC_ELEMENT_TYPE_FLOAT32; + p->dimensions = sqlite3_value_int64(argv[2]); + p->nvectors = sqlite3_value_int64(argv[3]); + sqlite3_result_pointer(context, p, POINTER_NAME_STATIC_BLOB_DEF, + sqlite3_free); +} +#pragma region vec_static_blobs() table function + +#define MAX_STATIC_BLOBS 16 + +typedef struct static_blob static_blob; +struct static_blob { + char *name; + void *p; + size_t dimensions; + size_t nvectors; + enum VectorElementType element_type; +}; + +typedef struct vec_static_blob_data vec_static_blob_data; +struct vec_static_blob_data { + static_blob static_blobs[MAX_STATIC_BLOBS]; +}; + +typedef struct vec_static_blobs_vtab vec_static_blobs_vtab; +struct vec_static_blobs_vtab { + sqlite3_vtab base; + vec_static_blob_data *data; +}; + +typedef struct vec_static_blobs_cursor vec_static_blobs_cursor; +struct vec_static_blobs_cursor { + sqlite3_vtab_cursor base; + sqlite3_int64 iRowid; +}; + +static int vec_static_blobsConnect(sqlite3 *db, void *pAux, int argc, + const char *const *argv, + sqlite3_vtab **ppVtab, char **pzErr) { + UNUSED_PARAMETER(argc); + UNUSED_PARAMETER(argv); + UNUSED_PARAMETER(pzErr); + + vec_static_blobs_vtab *pNew; +#define VEC_STATIC_BLOBS_NAME 0 +#define VEC_STATIC_BLOBS_DATA 1 +#define VEC_STATIC_BLOBS_DIMENSIONS 2 +#define VEC_STATIC_BLOBS_COUNT 3 + int rc = sqlite3_declare_vtab( + db, "CREATE TABLE x(name, data, dimensions hidden, count hidden)"); + if (rc == SQLITE_OK) { + pNew = sqlite3_malloc(sizeof(*pNew)); + *ppVtab = (sqlite3_vtab *)pNew; + if (pNew == 0) + return SQLITE_NOMEM; + memset(pNew, 0, sizeof(*pNew)); + pNew->data = pAux; + } + return rc; +} + +static int vec_static_blobsDisconnect(sqlite3_vtab *pVtab) { + vec_static_blobs_vtab *p = (vec_static_blobs_vtab *)pVtab; + sqlite3_free(p); + return SQLITE_OK; +} + +static int vec_static_blobsUpdate(sqlite3_vtab *pVTab, int argc, + sqlite3_value **argv, sqlite_int64 *pRowid) { + UNUSED_PARAMETER(pRowid); + vec_static_blobs_vtab *p = (vec_static_blobs_vtab *)pVTab; + // DELETE operation + if (argc == 1 && sqlite3_value_type(argv[0]) != SQLITE_NULL) { + return SQLITE_ERROR; + } + // INSERT operation + else if (argc > 1 && sqlite3_value_type(argv[0]) == SQLITE_NULL) { + const char *key = + (const char *)sqlite3_value_text(argv[2 + VEC_STATIC_BLOBS_NAME]); + int idx = -1; + for (int i = 0; i < MAX_STATIC_BLOBS; i++) { + if (!p->data->static_blobs[i].name) { + p->data->static_blobs[i].name = sqlite3_mprintf("%s", key); + idx = i; + break; + } + } + if (idx < 0) + abort(); + struct static_blob_definition *def = sqlite3_value_pointer( + argv[2 + VEC_STATIC_BLOBS_DATA], POINTER_NAME_STATIC_BLOB_DEF); + p->data->static_blobs[idx].p = def->p; + p->data->static_blobs[idx].dimensions = def->dimensions; + p->data->static_blobs[idx].nvectors = def->nvectors; + p->data->static_blobs[idx].element_type = def->element_type; + + return SQLITE_OK; + } + // UPDATE operation + else if (argc > 1 && sqlite3_value_type(argv[0]) != SQLITE_NULL) { + return SQLITE_ERROR; + } + return SQLITE_ERROR; +} + +static int vec_static_blobsOpen(sqlite3_vtab *p, + sqlite3_vtab_cursor **ppCursor) { + UNUSED_PARAMETER(p); + vec_static_blobs_cursor *pCur; + pCur = sqlite3_malloc(sizeof(*pCur)); + if (pCur == 0) + return SQLITE_NOMEM; + memset(pCur, 0, sizeof(*pCur)); + *ppCursor = &pCur->base; + return SQLITE_OK; +} + +static int vec_static_blobsClose(sqlite3_vtab_cursor *cur) { + vec_static_blobs_cursor *pCur = (vec_static_blobs_cursor *)cur; + sqlite3_free(pCur); + return SQLITE_OK; +} + +static int vec_static_blobsBestIndex(sqlite3_vtab *pVTab, + sqlite3_index_info *pIdxInfo) { + UNUSED_PARAMETER(pVTab); + pIdxInfo->idxNum = 1; + pIdxInfo->estimatedCost = (double)10; + pIdxInfo->estimatedRows = 10; + return SQLITE_OK; +} + +static int vec_static_blobsNext(sqlite3_vtab_cursor *cur); +static int vec_static_blobsFilter(sqlite3_vtab_cursor *pVtabCursor, int idxNum, + const char *idxStr, int argc, + sqlite3_value **argv) { + UNUSED_PARAMETER(idxNum); + UNUSED_PARAMETER(idxStr); + UNUSED_PARAMETER(argc); + UNUSED_PARAMETER(argv); + vec_static_blobs_cursor *pCur = (vec_static_blobs_cursor *)pVtabCursor; + pCur->iRowid = -1; + vec_static_blobsNext(pVtabCursor); + return SQLITE_OK; +} + +static int vec_static_blobsRowid(sqlite3_vtab_cursor *cur, + sqlite_int64 *pRowid) { + vec_static_blobs_cursor *pCur = (vec_static_blobs_cursor *)cur; + *pRowid = pCur->iRowid; + return SQLITE_OK; +} + +static int vec_static_blobsNext(sqlite3_vtab_cursor *cur) { + vec_static_blobs_cursor *pCur = (vec_static_blobs_cursor *)cur; + vec_static_blobs_vtab *p = (vec_static_blobs_vtab *)pCur->base.pVtab; + pCur->iRowid++; + while (pCur->iRowid < MAX_STATIC_BLOBS) { + if (p->data->static_blobs[pCur->iRowid].name) { + return SQLITE_OK; + } + pCur->iRowid++; + } + return SQLITE_OK; +} + +static int vec_static_blobsEof(sqlite3_vtab_cursor *cur) { + vec_static_blobs_cursor *pCur = (vec_static_blobs_cursor *)cur; + return pCur->iRowid >= MAX_STATIC_BLOBS; +} + +static int vec_static_blobsColumn(sqlite3_vtab_cursor *cur, + sqlite3_context *context, int i) { + vec_static_blobs_cursor *pCur = (vec_static_blobs_cursor *)cur; + vec_static_blobs_vtab *p = (vec_static_blobs_vtab *)cur->pVtab; + switch (i) { + case VEC_STATIC_BLOBS_NAME: + sqlite3_result_text(context, p->data->static_blobs[pCur->iRowid].name, -1, + SQLITE_TRANSIENT); + break; + case VEC_STATIC_BLOBS_DATA: + sqlite3_result_null(context); + break; + case VEC_STATIC_BLOBS_DIMENSIONS: + sqlite3_result_int64(context, + p->data->static_blobs[pCur->iRowid].dimensions); + break; + case VEC_STATIC_BLOBS_COUNT: + sqlite3_result_int64(context, p->data->static_blobs[pCur->iRowid].nvectors); + break; + } + return SQLITE_OK; +} + +static sqlite3_module vec_static_blobsModule = { + /* iVersion */ 3, + /* xCreate */ 0, + /* xConnect */ vec_static_blobsConnect, + /* xBestIndex */ vec_static_blobsBestIndex, + /* xDisconnect */ vec_static_blobsDisconnect, + /* xDestroy */ 0, + /* xOpen */ vec_static_blobsOpen, + /* xClose */ vec_static_blobsClose, + /* xFilter */ vec_static_blobsFilter, + /* xNext */ vec_static_blobsNext, + /* xEof */ vec_static_blobsEof, + /* xColumn */ vec_static_blobsColumn, + /* xRowid */ vec_static_blobsRowid, + /* xUpdate */ vec_static_blobsUpdate, + /* xBegin */ 0, + /* xSync */ 0, + /* xCommit */ 0, + /* xRollback */ 0, + /* xFindMethod */ 0, + /* xRename */ 0, + /* xSavepoint */ 0, + /* xRelease */ 0, + /* xRollbackTo */ 0, + /* xShadowName */ 0, +#if SQLITE_VERSION_NUMBER >= 3044000 + /* xIntegrity */ 0 +#endif +}; +#pragma endregion + +#pragma region vec_static_blob_entries() table function + +typedef struct vec_static_blob_entries_vtab vec_static_blob_entries_vtab; +struct vec_static_blob_entries_vtab { + sqlite3_vtab base; + static_blob *blob; +}; +typedef enum { + VEC_SBE__QUERYPLAN_FULLSCAN = 1, + VEC_SBE__QUERYPLAN_KNN = 2 +} vec_sbe_query_plan; + +struct sbe_query_knn_data { + i64 k; + i64 k_used; + // Array of rowids of size k. Must be freed with sqlite3_free(). + i32 *rowids; + // Array of distances of size k. Must be freed with sqlite3_free(). + f32 *distances; + i64 current_idx; +}; +void sbe_query_knn_data_clear(struct sbe_query_knn_data *knn_data) { + if (!knn_data) + return; + + if (knn_data->rowids) { + sqlite3_free(knn_data->rowids); + knn_data->rowids = NULL; + } + if (knn_data->distances) { + sqlite3_free(knn_data->distances); + knn_data->distances = NULL; + } +} + +typedef struct vec_static_blob_entries_cursor vec_static_blob_entries_cursor; +struct vec_static_blob_entries_cursor { + sqlite3_vtab_cursor base; + sqlite3_int64 iRowid; + vec_sbe_query_plan query_plan; + struct sbe_query_knn_data *knn_data; +}; + +static int vec_static_blob_entriesConnect(sqlite3 *db, void *pAux, int argc, + const char *const *argv, + sqlite3_vtab **ppVtab, char **pzErr) { + UNUSED_PARAMETER(argc); + UNUSED_PARAMETER(argv); + UNUSED_PARAMETER(pzErr); + vec_static_blob_data *blob_data = pAux; + int idx = -1; + for (int i = 0; i < MAX_STATIC_BLOBS; i++) { + if (!blob_data->static_blobs[i].name) + continue; + if (strncmp(blob_data->static_blobs[i].name, argv[3], + strlen(blob_data->static_blobs[i].name)) == 0) { + idx = i; + break; + } + } + if (idx < 0) + abort(); + vec_static_blob_entries_vtab *pNew; +#define VEC_STATIC_BLOB_ENTRIES_VECTOR 0 +#define VEC_STATIC_BLOB_ENTRIES_DISTANCE 1 +#define VEC_STATIC_BLOB_ENTRIES_K 2 + int rc = sqlite3_declare_vtab( + db, "CREATE TABLE x(vector, distance hidden, k hidden)"); + if (rc == SQLITE_OK) { + pNew = sqlite3_malloc(sizeof(*pNew)); + *ppVtab = (sqlite3_vtab *)pNew; + if (pNew == 0) + return SQLITE_NOMEM; + memset(pNew, 0, sizeof(*pNew)); + pNew->blob = &blob_data->static_blobs[idx]; + } + return rc; +} + +static int vec_static_blob_entriesCreate(sqlite3 *db, void *pAux, int argc, + const char *const *argv, + sqlite3_vtab **ppVtab, char **pzErr) { + return vec_static_blob_entriesConnect(db, pAux, argc, argv, ppVtab, pzErr); +} + +static int vec_static_blob_entriesDisconnect(sqlite3_vtab *pVtab) { + vec_static_blob_entries_vtab *p = (vec_static_blob_entries_vtab *)pVtab; + sqlite3_free(p); + return SQLITE_OK; +} + +static int vec_static_blob_entriesOpen(sqlite3_vtab *p, + sqlite3_vtab_cursor **ppCursor) { + UNUSED_PARAMETER(p); + vec_static_blob_entries_cursor *pCur; + pCur = sqlite3_malloc(sizeof(*pCur)); + if (pCur == 0) + return SQLITE_NOMEM; + memset(pCur, 0, sizeof(*pCur)); + *ppCursor = &pCur->base; + return SQLITE_OK; +} + +static int vec_static_blob_entriesClose(sqlite3_vtab_cursor *cur) { + vec_static_blob_entries_cursor *pCur = (vec_static_blob_entries_cursor *)cur; + sqlite3_free(pCur->knn_data); + sqlite3_free(pCur); + return SQLITE_OK; +} + +static int vec_static_blob_entriesBestIndex(sqlite3_vtab *pVTab, + sqlite3_index_info *pIdxInfo) { + vec_static_blob_entries_vtab *p = (vec_static_blob_entries_vtab *)pVTab; + int iMatchTerm = -1; + int iLimitTerm = -1; + // int iRowidTerm = -1; // https://github.com/asg017/sqlite-vec/issues/47 + int iKTerm = -1; + + for (int i = 0; i < pIdxInfo->nConstraint; i++) { + if (!pIdxInfo->aConstraint[i].usable) + continue; + + int iColumn = pIdxInfo->aConstraint[i].iColumn; + int op = pIdxInfo->aConstraint[i].op; + if (op == SQLITE_INDEX_CONSTRAINT_MATCH && + iColumn == VEC_STATIC_BLOB_ENTRIES_VECTOR) { + if (iMatchTerm > -1) { + // https://github.com/asg017/sqlite-vec/issues/51 + return SQLITE_ERROR; + } + iMatchTerm = i; + } + if (op == SQLITE_INDEX_CONSTRAINT_LIMIT) { + iLimitTerm = i; + } + if (op == SQLITE_INDEX_CONSTRAINT_EQ && + iColumn == VEC_STATIC_BLOB_ENTRIES_K) { + iKTerm = i; + } + } + if (iMatchTerm >= 0) { + if (iLimitTerm < 0 && iKTerm < 0) { + // https://github.com/asg017/sqlite-vec/issues/51 + return SQLITE_ERROR; + } + if (iLimitTerm >= 0 && iKTerm >= 0) { + return SQLITE_ERROR; // limit or k, not both + } + if (pIdxInfo->nOrderBy < 1) { + vtab_set_error(pVTab, "ORDER BY distance required"); + return SQLITE_CONSTRAINT; + } + if (pIdxInfo->nOrderBy > 1) { + // https://github.com/asg017/sqlite-vec/issues/51 + vtab_set_error(pVTab, "more than 1 ORDER BY clause provided"); + return SQLITE_CONSTRAINT; + } + if (pIdxInfo->aOrderBy[0].iColumn != VEC_STATIC_BLOB_ENTRIES_DISTANCE) { + vtab_set_error(pVTab, "ORDER BY must be on the distance column"); + return SQLITE_CONSTRAINT; + } + if (pIdxInfo->aOrderBy[0].desc) { + vtab_set_error(pVTab, + "Only ascending in ORDER BY distance clause is supported, " + "DESC is not supported yet."); + return SQLITE_CONSTRAINT; + } + + pIdxInfo->idxNum = VEC_SBE__QUERYPLAN_KNN; + pIdxInfo->estimatedCost = (double)10; + pIdxInfo->estimatedRows = 10; + + pIdxInfo->orderByConsumed = 1; + pIdxInfo->aConstraintUsage[iMatchTerm].argvIndex = 1; + pIdxInfo->aConstraintUsage[iMatchTerm].omit = 1; + if (iLimitTerm >= 0) { + pIdxInfo->aConstraintUsage[iLimitTerm].argvIndex = 2; + pIdxInfo->aConstraintUsage[iLimitTerm].omit = 1; + } else { + pIdxInfo->aConstraintUsage[iKTerm].argvIndex = 2; + pIdxInfo->aConstraintUsage[iKTerm].omit = 1; + } + + } else { + pIdxInfo->idxNum = VEC_SBE__QUERYPLAN_FULLSCAN; + pIdxInfo->estimatedCost = (double)p->blob->nvectors; + pIdxInfo->estimatedRows = p->blob->nvectors; + } + return SQLITE_OK; +} + +static int vec_static_blob_entriesFilter(sqlite3_vtab_cursor *pVtabCursor, + int idxNum, const char *idxStr, + int argc, sqlite3_value **argv) { + UNUSED_PARAMETER(idxStr); + assert(argc >= 0 && argc <= 3); + vec_static_blob_entries_cursor *pCur = + (vec_static_blob_entries_cursor *)pVtabCursor; + vec_static_blob_entries_vtab *p = + (vec_static_blob_entries_vtab *)pCur->base.pVtab; + + if (idxNum == VEC_SBE__QUERYPLAN_KNN) { + assert(argc == 2); + pCur->query_plan = VEC_SBE__QUERYPLAN_KNN; + struct sbe_query_knn_data *knn_data; + knn_data = sqlite3_malloc(sizeof(*knn_data)); + if (!knn_data) { + return SQLITE_NOMEM; + } + memset(knn_data, 0, sizeof(*knn_data)); + + void *queryVector; + size_t dimensions; + enum VectorElementType elementType; + vector_cleanup cleanup; + char *err; + int rc = vector_from_value(argv[0], &queryVector, &dimensions, &elementType, + &cleanup, &err); + if (rc != SQLITE_OK) { + return SQLITE_ERROR; + } + if (elementType != p->blob->element_type) { + return SQLITE_ERROR; + } + if (dimensions != p->blob->dimensions) { + return SQLITE_ERROR; + } + + i64 k = min(sqlite3_value_int64(argv[1]), (i64)p->blob->nvectors); + if (k < 0) { + // HANDLE https://github.com/asg017/sqlite-vec/issues/55 + return SQLITE_ERROR; + } + if (k == 0) { + knn_data->k = 0; + pCur->knn_data = knn_data; + return SQLITE_OK; + } + + size_t bsize = (p->blob->nvectors + 7) & ~7; + + i32 *topk_rowids = sqlite3_malloc(k * sizeof(i32)); + if (!topk_rowids) { + // HANDLE https://github.com/asg017/sqlite-vec/issues/55 + return SQLITE_ERROR; + } + f32 *distances = sqlite3_malloc(bsize * sizeof(f32)); + if (!distances) { + // HANDLE https://github.com/asg017/sqlite-vec/issues/55 + return SQLITE_ERROR; + } + + for (size_t i = 0; i < p->blob->nvectors; i++) { + // https://github.com/asg017/sqlite-vec/issues/52 + float *v = ((float *)p->blob->p) + (i * p->blob->dimensions); + distances[i] = + distance_l2_sqr_float(v, (float *)queryVector, &p->blob->dimensions); + } + u8 *candidates = bitmap_new(bsize); + assert(candidates); + + u8 *taken = bitmap_new(bsize); + assert(taken); + + bitmap_fill(candidates, bsize); + for (size_t i = bsize; i >= p->blob->nvectors; i--) { + bitmap_set(candidates, i, 0); + } + i32 k_used = 0; + min_idx(distances, bsize, candidates, topk_rowids, k, taken, &k_used); + knn_data->current_idx = 0; + knn_data->distances = distances; + knn_data->k = k; + knn_data->rowids = topk_rowids; + + pCur->knn_data = knn_data; + } else { + pCur->query_plan = VEC_SBE__QUERYPLAN_FULLSCAN; + pCur->iRowid = 0; + } + + return SQLITE_OK; +} + +static int vec_static_blob_entriesRowid(sqlite3_vtab_cursor *cur, + sqlite_int64 *pRowid) { + vec_static_blob_entries_cursor *pCur = (vec_static_blob_entries_cursor *)cur; + switch (pCur->query_plan) { + case VEC_SBE__QUERYPLAN_FULLSCAN: { + *pRowid = pCur->iRowid; + return SQLITE_OK; + } + case VEC_SBE__QUERYPLAN_KNN: { + i32 rowid = ((i32 *)pCur->knn_data->rowids)[pCur->knn_data->current_idx]; + *pRowid = (sqlite3_int64)rowid; + return SQLITE_OK; + } + } + return SQLITE_ERROR; +} + +static int vec_static_blob_entriesNext(sqlite3_vtab_cursor *cur) { + vec_static_blob_entries_cursor *pCur = (vec_static_blob_entries_cursor *)cur; + switch (pCur->query_plan) { + case VEC_SBE__QUERYPLAN_FULLSCAN: { + pCur->iRowid++; + return SQLITE_OK; + } + case VEC_SBE__QUERYPLAN_KNN: { + pCur->knn_data->current_idx++; + return SQLITE_OK; + } + } + return SQLITE_ERROR; +} + +static int vec_static_blob_entriesEof(sqlite3_vtab_cursor *cur) { + vec_static_blob_entries_cursor *pCur = (vec_static_blob_entries_cursor *)cur; + vec_static_blob_entries_vtab *p = + (vec_static_blob_entries_vtab *)pCur->base.pVtab; + switch (pCur->query_plan) { + case VEC_SBE__QUERYPLAN_FULLSCAN: { + return (size_t)pCur->iRowid >= p->blob->nvectors; + } + case VEC_SBE__QUERYPLAN_KNN: { + return pCur->knn_data->current_idx >= pCur->knn_data->k; + } + } + return SQLITE_ERROR; +} + +static int vec_static_blob_entriesColumn(sqlite3_vtab_cursor *cur, + sqlite3_context *context, int i) { + vec_static_blob_entries_cursor *pCur = (vec_static_blob_entries_cursor *)cur; + vec_static_blob_entries_vtab *p = (vec_static_blob_entries_vtab *)cur->pVtab; + + switch (pCur->query_plan) { + case VEC_SBE__QUERYPLAN_FULLSCAN: { + switch (i) { + case VEC_STATIC_BLOB_ENTRIES_VECTOR: + + sqlite3_result_blob( + context, + ((unsigned char *)p->blob->p) + + (pCur->iRowid * p->blob->dimensions * sizeof(float)), + p->blob->dimensions * sizeof(float), SQLITE_TRANSIENT); + sqlite3_result_subtype(context, p->blob->element_type); + break; + } + return SQLITE_OK; + } + case VEC_SBE__QUERYPLAN_KNN: { + switch (i) { + case VEC_STATIC_BLOB_ENTRIES_VECTOR: { + i32 rowid = ((i32 *)pCur->knn_data->rowids)[pCur->knn_data->current_idx]; + sqlite3_result_blob(context, + ((unsigned char *)p->blob->p) + + (rowid * p->blob->dimensions * sizeof(float)), + p->blob->dimensions * sizeof(float), + SQLITE_TRANSIENT); + sqlite3_result_subtype(context, p->blob->element_type); + break; + } + } + return SQLITE_OK; + } + } + return SQLITE_ERROR; +} + +static sqlite3_module vec_static_blob_entriesModule = { + /* iVersion */ 3, + /* xCreate */ + vec_static_blob_entriesCreate, // handle rm? + // https://github.com/asg017/sqlite-vec/issues/55 + /* xConnect */ vec_static_blob_entriesConnect, + /* xBestIndex */ vec_static_blob_entriesBestIndex, + /* xDisconnect */ vec_static_blob_entriesDisconnect, + /* xDestroy */ vec_static_blob_entriesDisconnect, + /* xOpen */ vec_static_blob_entriesOpen, + /* xClose */ vec_static_blob_entriesClose, + /* xFilter */ vec_static_blob_entriesFilter, + /* xNext */ vec_static_blob_entriesNext, + /* xEof */ vec_static_blob_entriesEof, + /* xColumn */ vec_static_blob_entriesColumn, + /* xRowid */ vec_static_blob_entriesRowid, + /* xUpdate */ 0, + /* xBegin */ 0, + /* xSync */ 0, + /* xCommit */ 0, + /* xRollback */ 0, + /* xFindMethod */ 0, + /* xRename */ 0, + /* xSavepoint */ 0, + /* xRelease */ 0, + /* xRollbackTo */ 0, + /* xShadowName */ 0, +#if SQLITE_VERSION_NUMBER >= 3044000 + /* xIntegrity */ 0 +#endif +}; +#pragma endregion + +#ifdef SQLITE_VEC_ENABLE_AVX +#define SQLITE_VEC_DEBUG_BUILD_AVX "avx" +#else +#define SQLITE_VEC_DEBUG_BUILD_AVX "" +#endif +#ifdef SQLITE_VEC_ENABLE_NEON +#define SQLITE_VEC_DEBUG_BUILD_NEON "neon" +#else +#define SQLITE_VEC_DEBUG_BUILD_NEON "" +#endif + +#define SQLITE_VEC_DEBUG_BUILD \ + SQLITE_VEC_DEBUG_BUILD_AVX " " SQLITE_VEC_DEBUG_BUILD_NEON + +#define SQLITE_VEC_DEBUG_STRING \ + "Version: " SQLITE_VEC_VERSION "\n" \ + "Date: " SQLITE_VEC_DATE "\n" \ + "Commit: " SQLITE_VEC_SOURCE "\n" \ + "Build flags: " SQLITE_VEC_DEBUG_BUILD + +SQLITE_VEC_API int sqlite3_vec_init(sqlite3 *db, char **pzErrMsg, + const sqlite3_api_routines *pApi) { +#ifndef SQLITE_CORE + SQLITE_EXTENSION_INIT2(pApi); +#endif + int rc = SQLITE_OK; + +#define DEFAULT_FLAGS (SQLITE_UTF8 | SQLITE_INNOCUOUS | SQLITE_DETERMINISTIC) + + rc = sqlite3_create_function_v2(db, "vec_version", 0, DEFAULT_FLAGS, + SQLITE_VEC_VERSION, _static_text_func, NULL, + NULL, NULL); + if (rc != SQLITE_OK) { + return rc; + } + rc = sqlite3_create_function_v2(db, "vec_debug", 0, DEFAULT_FLAGS, + SQLITE_VEC_DEBUG_STRING, _static_text_func, + NULL, NULL, NULL); + if (rc != SQLITE_OK) { + return rc; + } + static struct { + const char *zFName; + void (*xFunc)(sqlite3_context *, int, sqlite3_value **); + int nArg; + int flags; + } aFunc[] = { + // clang-format off + //{"vec_version", _static_text_func, 0, DEFAULT_FLAGS, (void *) SQLITE_VEC_VERSION }, + //{"vec_debug", _static_text_func, 0, DEFAULT_FLAGS, (void *) SQLITE_VEC_DEBUG_STRING }, + {"vec_distance_l2", vec_distance_l2, 2, DEFAULT_FLAGS | SQLITE_SUBTYPE, }, + {"vec_distance_l1", vec_distance_l1, 2, DEFAULT_FLAGS | SQLITE_SUBTYPE, }, + {"vec_distance_hamming",vec_distance_hamming, 2, DEFAULT_FLAGS | SQLITE_SUBTYPE, }, + {"vec_distance_cosine", vec_distance_cosine, 2, DEFAULT_FLAGS | SQLITE_SUBTYPE, }, + {"vec_length", vec_length, 1, DEFAULT_FLAGS | SQLITE_SUBTYPE, }, + {"vec_type", vec_type, 1, DEFAULT_FLAGS, }, + {"vec_to_json", vec_to_json, 1, DEFAULT_FLAGS | SQLITE_SUBTYPE | SQLITE_RESULT_SUBTYPE, }, + {"vec_add", vec_add, 2, DEFAULT_FLAGS | SQLITE_SUBTYPE | SQLITE_RESULT_SUBTYPE, }, + {"vec_sub", vec_sub, 2, DEFAULT_FLAGS | SQLITE_SUBTYPE | SQLITE_RESULT_SUBTYPE, }, + {"vec_slice", vec_slice, 3, DEFAULT_FLAGS | SQLITE_SUBTYPE | SQLITE_RESULT_SUBTYPE, }, + {"vec_normalize", vec_normalize, 1, DEFAULT_FLAGS | SQLITE_SUBTYPE | SQLITE_RESULT_SUBTYPE, }, + {"vec_f32", vec_f32, 1, DEFAULT_FLAGS | SQLITE_SUBTYPE | SQLITE_RESULT_SUBTYPE, }, + {"vec_bit", vec_bit, 1, DEFAULT_FLAGS | SQLITE_SUBTYPE | SQLITE_RESULT_SUBTYPE, }, + {"vec_int8", vec_int8, 1, DEFAULT_FLAGS | SQLITE_SUBTYPE | SQLITE_RESULT_SUBTYPE, }, + {"vec_quantize_int8", vec_quantize_int8, 2, DEFAULT_FLAGS | SQLITE_SUBTYPE | SQLITE_RESULT_SUBTYPE, }, + {"vec_quantize_binary", vec_quantize_binary, 1, DEFAULT_FLAGS | SQLITE_SUBTYPE | SQLITE_RESULT_SUBTYPE, }, + // clang-format on + }; + + static struct { + char *name; + const sqlite3_module *module; + void *p; + void (*xDestroy)(void *); + } aMod[] = { + // clang-format off + {"vec0", &vec0Module, NULL, NULL}, + {"vec_each", &vec_eachModule, NULL, NULL}, + // clang-format on + }; + + for (unsigned long i = 0; i < countof(aFunc) && rc == SQLITE_OK; i++) { + rc = sqlite3_create_function_v2(db, aFunc[i].zFName, aFunc[i].nArg, + aFunc[i].flags, NULL, aFunc[i].xFunc, NULL, + NULL, NULL); + if (rc != SQLITE_OK) { + *pzErrMsg = sqlite3_mprintf("Error creating function %s: %s", + aFunc[i].zFName, sqlite3_errmsg(db)); + return rc; + } + } + + for (unsigned long i = 0; i < countof(aMod) && rc == SQLITE_OK; i++) { + rc = sqlite3_create_module_v2(db, aMod[i].name, aMod[i].module, NULL, NULL); + if (rc != SQLITE_OK) { + *pzErrMsg = sqlite3_mprintf("Error creating module %s: %s", aMod[i].name, + sqlite3_errmsg(db)); + return rc; + } + } + + return SQLITE_OK; +} + +#ifndef SQLITE_VEC_OMIT_FS +SQLITE_VEC_API int sqlite3_vec_numpy_init(sqlite3 *db, char **pzErrMsg, + const sqlite3_api_routines *pApi) { + UNUSED_PARAMETER(pzErrMsg); +#ifndef SQLITE_CORE + SQLITE_EXTENSION_INIT2(pApi); +#endif + int rc = SQLITE_OK; + rc = sqlite3_create_function_v2(db, "vec_npy_file", 1, SQLITE_RESULT_SUBTYPE, + NULL, vec_npy_file, NULL, NULL, NULL); + if(rc != SQLITE_OK) { + return rc; + } + rc = sqlite3_create_module_v2(db, "vec_npy_each", &vec_npy_eachModule, NULL, NULL); + return rc; +} +#endif + +SQLITE_VEC_API int +sqlite3_vec_static_blobs_init(sqlite3 *db, char **pzErrMsg, + const sqlite3_api_routines *pApi) { + UNUSED_PARAMETER(pzErrMsg); +#ifndef SQLITE_CORE + SQLITE_EXTENSION_INIT2(pApi); +#endif + + int rc = SQLITE_OK; + vec_static_blob_data *static_blob_data; + static_blob_data = sqlite3_malloc(sizeof(*static_blob_data)); + if (!static_blob_data) { + return SQLITE_NOMEM; + } + memset(static_blob_data, 0, sizeof(*static_blob_data)); + + rc = sqlite3_create_function_v2( + db, "vec_static_blob_from_raw", 4, + DEFAULT_FLAGS | SQLITE_SUBTYPE | SQLITE_RESULT_SUBTYPE, NULL, + vec_static_blob_from_raw, NULL, NULL, NULL); + if (rc != SQLITE_OK) + return rc; + + rc = sqlite3_create_module_v2(db, "vec_static_blobs", &vec_static_blobsModule, + static_blob_data, sqlite3_free); + if (rc != SQLITE_OK) + return rc; + rc = sqlite3_create_module_v2(db, "vec_static_blob_entries", + &vec_static_blob_entriesModule, + static_blob_data, NULL); + if (rc != SQLITE_OK) + return rc; + return rc; +} diff --git a/third_party/sqlite/sqlite-vec.h b/third_party/sqlite/sqlite-vec.h new file mode 100644 index 0000000000..6592a6bb2c --- /dev/null +++ b/third_party/sqlite/sqlite-vec.h @@ -0,0 +1,41 @@ +#ifndef SQLITE_VEC_H +#define SQLITE_VEC_H + +#ifndef SQLITE_CORE +#include "sqlite3ext.h" +#else +#include "sqlite3.h" +#endif + +#ifdef SQLITE_VEC_STATIC + #define SQLITE_VEC_API +#else + #ifdef _WIN32 + #define SQLITE_VEC_API __declspec(dllexport) + #else + #define SQLITE_VEC_API + #endif +#endif + +#define SQLITE_VEC_VERSION "v0.1.6" +// TODO rm +#define SQLITE_VEC_DATE "2024-11-20T16:39:41Z+0000" +#define SQLITE_VEC_SOURCE "639fca5739fe056fdc98f3d539c4cd79328d7dc7" + + +#define SQLITE_VEC_VERSION_MAJOR 0 +#define SQLITE_VEC_VERSION_MINOR 1 +#define SQLITE_VEC_VERSION_PATCH 6 + +#ifdef __cplusplus +extern "C" { +#endif + +SQLITE_VEC_API int sqlite3_vec_init(sqlite3 *db, char **pzErrMsg, + const sqlite3_api_routines *pApi); + +#ifdef __cplusplus +} /* end of the 'extern "C"' block */ +#endif + +#endif /* ifndef SQLITE_VEC_H */