forked from sheetjs/docs.sheetjs.com
		
	v8
This commit is contained in:
		
							parent
							
								
									b6095aff3f
								
							
						
					
					
						commit
						8000cfcbd7
					
				
							
								
								
									
										2
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										2
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							| @ -2,4 +2,4 @@ | ||||
| *.bak | ||||
| package-lock.json | ||||
| pnpm-lock.yaml | ||||
| docs | ||||
| /docs | ||||
|  | ||||
							
								
								
									
										2
									
								
								Makefile
									
									
									
									
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										2
									
								
								Makefile
									
									
									
									
									
								
							| @ -13,7 +13,7 @@ init: | ||||
| 
 | ||||
| .PHONY: dev | ||||
| dev: | ||||
| 	cd docz; npm run start; cd .. | ||||
| 	cd docz; npm run start -- --host=0.0.0.0; cd .. | ||||
| 
 | ||||
| .PHONY: serve | ||||
| serve: | ||||
|  | ||||
							
								
								
									
										261
									
								
								docz/docs/03-demos/12-engines/02_v8.md
									
									
									
									
									
										Normal file
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										261
									
								
								docz/docs/03-demos/12-engines/02_v8.md
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,261 @@ | ||||
| --- | ||||
| title: C++ + V8 | ||||
| pagination_prev: demos/bigdata/index | ||||
| pagination_next: solutions/input | ||||
| --- | ||||
| 
 | ||||
| import current from '/version.js'; | ||||
| 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 | | ||||
| 
 | ||||
| ::: | ||||
| 
 | ||||
| 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 | ||||
| 
 | ||||
| At the time of writing, there were errors in the official V8 embed guide for the | ||||
| macOS platform. The correct instructions are included below. | ||||
| 
 | ||||
| ::: | ||||
| 
 | ||||
| :::caution | ||||
| 
 | ||||
| The build process is long and will test your patience. | ||||
| 
 | ||||
| ::: | ||||
| 
 | ||||
| ### Preparation | ||||
| 
 | ||||
| 1) Download and install `depot_tools`: | ||||
| 
 | ||||
| ```bash | ||||
| mkdir -p /usr/local/lib | ||||
| cd /usr/local/lib | ||||
| 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. | ||||
| 
 | ||||
| ```bash | ||||
| tools/dev/v8gen.py x64.release.sample | ||||
| ninja -C out.gn/x64.release.sample v8_monolith | ||||
| ``` | ||||
| 
 | ||||
| 7) Ensure the sample `hello-world` compiles and runs: | ||||
| 
 | ||||
| ```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 | ||||
| ``` | ||||
| 
 | ||||
| ### 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: | ||||
| 
 | ||||
| ```bash | ||||
| ln -s ~/dev/v8/v8/include | ||||
| ln -s ~/dev/v8/v8/out.gn/x64.release.sample/obj | ||||
| ``` | ||||
| 
 | ||||
| 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. | ||||
							
								
								
									
										106
									
								
								docz/static/v8/sheetjs.v8.cc
									
									
									
									
									
										Normal file
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										106
									
								
								docz/static/v8/sheetjs.v8.cc
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,106 @@ | ||||
| /* sheetjs.v8 (C) SheetJS -- https://sheetjs.com */ | ||||
| /* based on the official V8 samples ("BSD-3-Clause") */ | ||||
| #include <stdio.h> | ||||
| #include <stdlib.h> | ||||
| #include <string.h> | ||||
| #include "include/libplatform/libplatform.h" | ||||
| #include "include/v8.h" | ||||
| 
 | ||||
| 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; | ||||
| } | ||||
| 
 | ||||
| v8::Local<v8::Value> eval_code(v8::Isolate *isolate, v8::Local<v8::Context> context, char* code) { | ||||
|   v8::Local<v8::String> source = v8::String::NewFromUtf8(isolate, code).ToLocalChecked(); | ||||
|   v8::Local<v8::Script> script = v8::Script::Compile(context, source).ToLocalChecked(); | ||||
|   return script->Run(context).ToLocalChecked(); | ||||
| } | ||||
| #define EVAL_CODE(x) eval_code(isolate, context, (char *)x) | ||||
| 
 | ||||
| int main(int argc, char* argv[]) { | ||||
|   /* initialize -- this part is from the hello world example */ | ||||
|   v8::V8::InitializeICUDefaultLocation(argv[0]); | ||||
|   v8::V8::InitializeExternalStartupData(argv[0]); | ||||
|   std::unique_ptr<v8::Platform> platform = v8::platform::NewDefaultPlatform(); | ||||
|   v8::V8::InitializePlatform(platform.get()); | ||||
|   v8::V8::Initialize(); | ||||
| 
 | ||||
|   v8::Isolate::CreateParams create_params; | ||||
|   create_params.array_buffer_allocator = v8::ArrayBuffer::Allocator::NewDefaultAllocator(); | ||||
|   v8::Isolate* isolate = v8::Isolate::New(create_params); | ||||
|   { | ||||
|     v8::Isolate::Scope isolate_scope(isolate); | ||||
|     v8::HandleScope handle_scope(isolate); | ||||
|     v8::Local<v8::Context> context = v8::Context::New(isolate); | ||||
|     v8::Context::Scope context_scope(context); | ||||
| 
 | ||||
|     /* load library */ | ||||
|     { | ||||
|       /* read file */ | ||||
|       size_t sz; char *file = read_file("xlsx.full.min.js", &sz); | ||||
|       if(!file) { perror("Error reading xlsx.full.min.js"); return 1; } | ||||
| 
 | ||||
|       /* evaluate */ | ||||
|       v8::Local<v8::Value> result = EVAL_CODE(file); | ||||
| 
 | ||||
|       /* free */ | ||||
|       free(file); | ||||
|     } | ||||
| 
 | ||||
|     /* get version string */ | ||||
|     { | ||||
|       v8::Local<v8::Value> result = EVAL_CODE("XLSX.version"); | ||||
|       v8::String::Utf8Value vers(isolate, result); | ||||
|       printf("SheetJS library version %s\n", *vers); | ||||
|     } | ||||
| 
 | ||||
|     /* read file */ | ||||
|     { | ||||
|       /* read bytes */ | ||||
|       size_t sz; char *file = read_file(argv[1], &sz); | ||||
|       if(!file) { perror("Error reading file"); return 1; } | ||||
| 
 | ||||
|       /* copy into array buffer and assign to `buf` in the global scope */ | ||||
|       { | ||||
|         std::unique_ptr<v8::BackingStore> back = v8::ArrayBuffer::NewBackingStore(isolate, sz); | ||||
|         memcpy(back->Data(), file, sz); | ||||
|         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); | ||||
|       } | ||||
| 
 | ||||
|       printf("Loaded file %s\n", argv[1]); | ||||
|     } | ||||
| 
 | ||||
|     /* parse workbook and assign to global `wb` property */ | ||||
|     { | ||||
|       v8::Local<v8::Value> result = EVAL_CODE("globalThis.wb = XLSX.read(buf)"); | ||||
|     } | ||||
| 
 | ||||
|     /* print CSV of first worksheet */ | ||||
|     { | ||||
|       v8::Local<v8::Value> result = EVAL_CODE("XLSX.utils.sheet_to_csv(wb.Sheets[wb.SheetNames[0]])"); | ||||
|       v8::String::Utf8Value csv(isolate, result); | ||||
|       printf("%s\n", *csv); | ||||
|     } | ||||
| 
 | ||||
|     // write sheetjsw.xlsb
 | ||||
|     { | ||||
|       v8::Local<v8::Value> result = EVAL_CODE("XLSX.write(wb, {type:'array', bookType:'xlsb'})"); | ||||
|       v8::Local<v8::ArrayBuffer> ab = v8::Local<v8::ArrayBuffer>::Cast(result); | ||||
|       FILE *f = fopen("sheetjsw.xlsb", "wb"); fwrite((char *)ab->Data(), 1, ab->ByteLength(), f); fclose(f); | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   /* cleanup -- this part is from the hello world example */ | ||||
|   isolate->Dispose(); | ||||
|   v8::V8::Dispose(); | ||||
|   v8::V8::DisposePlatform(); | ||||
|   delete create_params.array_buffer_allocator; | ||||
|   return 0; | ||||
| } | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user