---
title: C++ + V8
pagination_prev: demos/bigdata/index
pagination_next: solutions/input
---

import current from '/version.js';
import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';
import CodeBlock from '@theme/CodeBlock';

V8 is an embeddable JS engine written in C++. It powers Chromium and Chrome,
NodeJS and Deno, Adobe UXP and other platforms.

The [Standalone scripts](/docs/getting-started/installation/standalone) can be
parsed and evaluated in a V8 context.

## Integration Details

_Initialize V8_

The official V8 `hello-world` example covers initialization and cleanup. For the
purposes of this demo, the key variables are noted below:

```cpp
v8::Isolate* isolate = v8::Isolate::New(create_params);
v8::Local<v8::Context> context = v8::Context::New(isolate);
```

The following helper function evaluates C strings as JS code:

```cpp
v8::Local<v8::Value> eval_code(v8::Isolate *i, v8::Local<v8::Context> c, char* code) {
  v8::Local<v8::String> source = v8::String::NewFromUtf8(i, code).ToLocalChecked();
  v8::Local<v8::Script> script = v8::Script::Compile(i, source).ToLocalChecked();
  return script->Run(c).ToLocalChecked();
}
```

_Load SheetJS Scripts_

The main library can be loaded by reading the scripts from the file system and
evaluating in the V8 context:

```cpp
/* simple wrapper to read the entire script file */
static char *read_file(const char *filename, size_t *sz) {
  FILE *f = fopen(filename, "rb");
  if(!f) return NULL;
  long fsize; { fseek(f, 0, SEEK_END); fsize = ftell(f); fseek(f, 0, SEEK_SET); }
  char *buf = (char *)malloc(fsize * sizeof(char));
  *sz = fread((void *) buf, 1, fsize, f);
  fclose(f);
  return buf;
}

// ...
  size_t sz; char *file = read_file("xlsx.full.min.js", &sz);
  v8::Local<v8::Value> result = eval_code(isolate, context, file);
```

To confirm the library is loaded, `XLSX.version` can be inspected:

```cpp
  /* get version string */
  v8::Local<v8::Value> result = eval_code(isolate, context, "XLSX.version");
  v8::String::Utf8Value vers(isolate, result);
  printf("SheetJS library version %s\n", *vers);
```

### Reading Files

V8 supports `ArrayBuffer` natively. Assuming `buf` is a C byte array, with
length `len`, this snippet stores the data as an `ArrayBuffer` in global scope:

```cpp
/* load C char array and save to an ArrayBuffer */
std::unique_ptr<v8::BackingStore> back = v8::ArrayBuffer::NewBackingStore(isolate, len);
memcpy(back->Data(), buf, len);
v8::Local<v8::ArrayBuffer> ab = v8::ArrayBuffer::New(isolate, std::move(back));
v8::Maybe<bool> res = context->Global()->Set(context, v8::String::NewFromUtf8Literal(isolate, "buf"), ab);

/* parse with SheetJS */
v8::Local<v8::Value> result = eval_code(isolate, context, "globalThis.wb = XLSX.read(buf)");
```

`wb` will be a variable in the JS environment that can be inspected using the
various SheetJS API functions.

### Writing Files

The underlying memory from an `ArrayBuffer` can be recovered:

```c
/* write with SheetJS using type: "array" */
v8::Local<v8::Value> result = eval_code(isolate, context, "XLSX.write(wb, {type:'array', bookType:'xlsb'})");

/* pull result back to C++ */
v8::Local<v8::ArrayBuffer> ab = v8::Local<v8::ArrayBuffer>::Cast(result);
size_t sz = ab->ByteLength();
char *buf = ab->Data();
```

The resulting `buf` can be written to file with `fwrite`.

## Complete Example

:::note

This demo was tested in the following deployments:

| V8 Version    | Platform     | OS Version   | Compiler       | Date       |
|:--------------|:-------------|:-------------|:---------------|:-----------|
| `11.3.244.11` | `darwin-x64` | macOS 13.2   | `clang 14.0.3` | 2023-05-20 |
| `11.3.244.11` | `darwin-arm` | macOS 13.0   | `clang 14.0.3` | 2023-06-05 |
| `11.3.244.11` | `linux-x64`  | HoloOS 3.4.6 | `gcc 12.2.0`   | 2023-05-20 |

:::

This program parses a file and prints CSV data from the first worksheet. It also
generates an XLSB file and writes to the filesystem.

:::caution

When the demo was last tested, there were errors in the official V8 embed guide.
The correct instructions are included below.

:::

:::caution

The build process is long and will test your patience.

:::

### Preparation

0) Prepare `/usr/local/lib`:

```bash
mkdir -p /usr/local/lib
cd /usr/local/lib
```

:::caution

If this step throws a permission error, run:

```bash
sudo mkdir -p /usr/local/lib
sudo chmod 777 /usr/local/lib
```

:::


1) Download and install `depot_tools`:

```bash
git clone https://chromium.googlesource.com/chromium/tools/depot_tools.git
```

2) Add the path to the `PATH` environment variable:

```bash
export PATH="/usr/local/lib/depot_tools:$PATH"
```

At this point, it is strongly recommended to add the line to a shell startup
script such as `.bashrc` or `.zshrc`

3) Run `gclient` once to update `depot_tools`:

```bash
gclient
```

### Clone V8

4) Create a base directory:

```bash
mkdir -p ~/dev/v8
cd ~/dev/v8
fetch v8
cd v8
```

Note that the actual repo will be placed in `~/dev/v8/v8`.

5) Checkout the desired version. The following command pulls `11.3.244.11`:

```bash
git checkout refs/tags/11.3.244.11 -b sample -t
```

### Build V8

6) Build the static library.

<Tabs groupId="triple">
  <TabItem value="darwin-x64" label="Intel Mac">

```bash
tools/dev/v8gen.py x64.release.sample
ninja -C out.gn/x64.release.sample v8_monolith
```

  </TabItem>
  <TabItem value="darwin-arm" label="Apple Silicon">

```bash
tools/dev/v8gen.py arm64.release.sample
ninja -C out.gn/arm64.release.sample v8_monolith
```

  </TabItem>
</Tabs>

7) Ensure the sample `hello-world` compiles and runs:

<Tabs groupId="triple">
  <TabItem value="darwin-x64" label="Intel Mac">

```bash
g++ -I. -Iinclude samples/hello-world.cc -o hello_world -fno-rtti -lv8_monolith \
    -lv8_libbase -lv8_libplatform -ldl -Lout.gn/x64.release.sample/obj/ -pthread \
    -std=c++17 -DV8_COMPRESS_POINTERS=1 -DV8_ENABLE_SANDBOX
./hello_world
```

  </TabItem>
  <TabItem value="darwin-arm" label="Apple Silicon">

```bash
g++ -I. -Iinclude samples/hello-world.cc -o hello_world -fno-rtti -lv8_monolith \
    -lv8_libbase -lv8_libplatform -ldl -Lout.gn/arm64.release.sample/obj/ -pthread \
    -std=c++17 -DV8_COMPRESS_POINTERS=1 -DV8_ENABLE_SANDBOX
./hello_world
```

  </TabItem>
</Tabs>


### Prepare Project

8) Make a new project folder:

```bash
cd ~/dev
mkdir sheetjs-v8
cd sheetjs-v8
```

9) Copy the sample source:

```bash
cp ~/dev/v8/v8/samples/hello-world.cc .
```

10) Create symbolic links to the `include` headers and `obj` library folders:

<Tabs groupId="triple">
  <TabItem value="darwin-x64" label="Intel Mac">

```bash
ln -s ~/dev/v8/v8/include
ln -s ~/dev/v8/v8/out.gn/x64.release.sample/obj
```

  </TabItem>
  <TabItem value="darwin-arm" label="Apple Silicon">

```bash
ln -s ~/dev/v8/v8/include
ln -s ~/dev/v8/v8/out.gn/arm64.release.sample/obj
```

  </TabItem>
</Tabs>

11) Build and run the `hello-world` example from this folder:

```bash
g++ -I. -Iinclude hello-world.cc -o hello_world -fno-rtti -lv8_monolith \
    -lv8_libbase -lv8_libplatform -ldl -Lobj/ -pthread -std=c++17 \
    -DV8_COMPRESS_POINTERS=1 -DV8_ENABLE_SANDBOX
./hello_world
```

### Add SheetJS

12) Download the standalone script and test file:

<ul>
<li><a href={`https://cdn.sheetjs.com/xlsx-${current}/package/dist/xlsx.full.min.js`}>xlsx.full.min.js</a></li>
<li><a href="https://sheetjs.com/pres.numbers">pres.numbers</a></li>
</ul>

<CodeBlock language="bash">{`\
curl -LO https://cdn.sheetjs.com/xlsx-${current}/package/dist/xlsx.full.min.js
curl -LO https://sheetjs.com/pres.numbers`}
</CodeBlock>

13) Download [`sheetjs.v8.cc`](pathname:///v8/sheetjs.v8.cc):

```bash
curl -LO https://docs.sheetjs.com/v8/sheetjs.v8.cc
```

14) Compile standalone `sheetjs.v8` binary

```bash
g++ -I. -Iinclude sheetjs.v8.cc -o sheetjs.v8 -fno-rtti -lv8_monolith \
    -lv8_libbase -lv8_libplatform -ldl -Lobj/ -pthread -std=c++17 \
    -DV8_COMPRESS_POINTERS=1 -DV8_ENABLE_SANDBOX
```

15) Run the demo:

```bash
./sheetjs.v8 pres.numbers
```

If the program succeeded, the CSV contents will be printed to console and the
file `sheetjsw.xlsb` will be created.  That file can be opened with Excel.

## Bindings

Bindings exist for many languages. As these bindings require "native" code, they
may not work on every platform.

### Rust

The `v8` crate provides binary builds and straightforward bindings. The Rust
code is similar to the C++ code.

Pulling data from an `ArrayBuffer` back into Rust involves an unsafe operation:

```rust
/* assuming JS code returns an ArrayBuffer, copy result to a Vec<u8> */
fn eval_code_ab(scope: &mut v8::HandleScope, code: &str) -> Vec<u8> {
  let source = v8::String::new(scope, &code).unwrap();
  let script = v8::Script::compile(scope, source, None).unwrap();
  let result: v8::Local<v8::ArrayBuffer> = script.run(scope).unwrap().try_into().unwrap();
  /* In C++, `Data` returns a pointer. Collecting data into Vec<u8> is unsafe */
  unsafe { return std::slice::from_raw_parts_mut(
    result.data().unwrap().cast::<u8>().as_ptr(),
    result.byte_length()
  ).to_vec(); }
}

```

:::note

This demo was last tested in the following deployments:

| Architecture | V8 Crate | Date       |
|:-------------|:---------|:-----------|
| `darwin-x64` | `0.71.2` | 2023-05-22 |
| `darwin-arm` | `0.73.0` | 2023-06-05 |
| `linux-x64`  | `0.71.2` | 2023-05-23 |
| `win32-x64`  | `0.71.2` | 2023-05-23 |

:::

1) Create a new project:

```bash
cargo new sheetjs-rustyv8
cd sheetjs-rustyv8
cargo run
```

2) Add the `v8` crate:

```bash
cargo add v8
cargo run
```

3) Download the [Standalone build](/docs/getting-started/installation/standalone):

<CodeBlock language="bash">{`\
curl -LO https://cdn.sheetjs.com/xlsx-${current}/package/dist/xlsx.full.min.js`}
</CodeBlock>

4) Download [`main.rs`](pathname:///v8/main.rs) and replace `src/main.rs`:

```bash
curl -L -o src/main.rs https://docs.sheetjs.com/v8/main.rs
```

5) Download [the test file](https://sheetjs.com/pres.numbers) and run:

```bash
curl -LO https://sheetjs.com/pres.numbers
cargo run pres.numbers
```

If the program succeeded, the CSV contents will be printed to console and the
file `sheetjsw.xlsb` will be created.  That file can be opened with Excel.