forked from sheetjs/docs.sheetjs.com
		
	hermes
This commit is contained in:
		
							parent
							
								
									ba3ffafe47
								
							
						
					
					
						commit
						fecf228fed
					
				| @ -1006,7 +1006,7 @@ When the app is loaded, the data will be displayed in rows. | ||||
| [^4]: See ["Workbook Object"](/docs/csf/book) | ||||
| [^5]: See ["Workbook Object"](/docs/csf/book) | ||||
| [^6]: See ["Sheet Objects"](/docs/csf/sheet) | ||||
| [^7]: See [`sheet_to_csv` in "Utilities"](/docs/api/utilities/csv#csv-output) | ||||
| [^7]: See [`sheet_to_html` in "Utilities"](/docs/api/utilities/html#html-table-output) | ||||
| [^8]: See [`sheet_to_json` in "Utilities"](/docs/api/utilities/array#array-output) | ||||
| [^9]: See [`sheet_to_json` in "Utilities"](/docs/api/utilities/array#array-output) | ||||
| [^10]: See [`!merges` in "Sheet Objects"](/docs/csf/sheet#worksheet-object) | ||||
|  | ||||
| @ -1,13 +1,11 @@ | ||||
| --- | ||||
| title: NetSuite | ||||
| sidebar_title: NetSuite | ||||
| title: Spreadsheets in NetSuite SuiteScripts | ||||
| sidebar_label: NetSuite | ||||
| description: Automate the NetSuite ERP platform with SuiteScripts. Effortlessly read and write spreadsheets using SheetJS. Modernize Excel-powered business processes with confidence. | ||||
| pagination_prev: demos/local/index | ||||
| pagination_next: demos/extensions/index | ||||
| --- | ||||
| 
 | ||||
| # Spreadsheets in NetSuite SuiteScripts | ||||
| 
 | ||||
| import current from '/version.js'; | ||||
| import CodeBlock from '@theme/CodeBlock'; | ||||
| 
 | ||||
| @ -29,6 +27,7 @@ This demo was verified by NetSuite consultants in the following deployments: | ||||
| | ScheduledScript | 2.1            | 2023-03-09 | | ||||
| | Restlet         | 2.1            | 2023-04-20 | | ||||
| | Suitelet        | 2.1            | 2023-07-21 | | ||||
| | MapReduceScript | 2.1            | 2023-07-31 | | ||||
| 
 | ||||
| ::: | ||||
| 
 | ||||
|  | ||||
| @ -1,13 +1,11 @@ | ||||
| --- | ||||
| title: C + QuickJS | ||||
| title: Data Processing with QuickJS | ||||
| sidebar_label: C + QuickJS | ||||
| description: Process structured data in C programs. Seamlessly integrate spreadsheets into your program by pairing QuickJS and SheetJS. Supercharge programs with modern data tools. | ||||
| pagination_prev: demos/bigdata/index | ||||
| pagination_next: solutions/input | ||||
| --- | ||||
| 
 | ||||
| # Data Processing with QuickJS | ||||
| 
 | ||||
| import current from '/version.js'; | ||||
| import CodeBlock from '@theme/CodeBlock'; | ||||
| 
 | ||||
|  | ||||
| @ -1,5 +1,7 @@ | ||||
| --- | ||||
| title: C++ + Hermes | ||||
| title: Sharing Sheets with Hermes | ||||
| sidebar_label: C++ + Hermes | ||||
| description: Process structured data in C++ programs. Seamlessly integrate spreadsheets into your program by pairing Hermes and SheetJS. Handle the most complex files without breaking a sweat. | ||||
| pagination_prev: demos/bigdata/index | ||||
| pagination_next: solutions/input | ||||
| --- | ||||
| @ -7,62 +9,115 @@ pagination_next: solutions/input | ||||
| import current from '/version.js'; | ||||
| import CodeBlock from '@theme/CodeBlock'; | ||||
| 
 | ||||
| Hermes is an embeddable JS engine written in C++. With some light shims, it can | ||||
| run the standalone browser scripts. | ||||
| [Hermes](https://hermesengine.dev/) is an embeddable JS engine written in C++. | ||||
| 
 | ||||
| The [Standalone scripts](/docs/getting-started/installation/standalone) can be | ||||
| parsed and evaluated in a Hermes context. | ||||
| [SheetJS](https://sheetjs.com) is a JavaScript library for reading and writing | ||||
| data from spreadsheets. | ||||
| 
 | ||||
| :::caution Here be Dragons | ||||
| This demo uses Hermes and SheetJS to pull data from a spreadsheet and print CSV | ||||
| rows. We'll explore how to load SheetJS in a Hermes context and process | ||||
| spreadsheets from a C++ program. | ||||
| 
 | ||||
| The ["Integration Example"](#integration-example) section includes a complete | ||||
| command-line tool for reading data from files. | ||||
| 
 | ||||
| ## Integration Details | ||||
| 
 | ||||
| :::info pass | ||||
| 
 | ||||
| Many Hermes functions are not documented. The explanation was verified against | ||||
| commit `70af78b`. | ||||
| 
 | ||||
| ::: | ||||
| 
 | ||||
| :::warning pass | ||||
| 
 | ||||
| The main target for Hermes is React Native.  At the time of writing, there was | ||||
| no official documentation for embedding the Hermes engine in C++ programs. | ||||
| 
 | ||||
| ::: | ||||
| 
 | ||||
| ## Integration Details | ||||
| ### Initialize Hermes | ||||
| 
 | ||||
| _Initialize Hermes_ | ||||
| 
 | ||||
| The runtime can be initialized in one line: | ||||
| A Hermes engine instance is created with `facebook::hermes::makeHermesRuntime`: | ||||
| 
 | ||||
| ```cpp | ||||
| std::unique_ptr<facebook::jsi::Runtime> rt(facebook::hermes::makeHermesRuntime()); | ||||
| ``` | ||||
| 
 | ||||
| Hermes does not expose a `console` or `global` variable, but those can be | ||||
| synthesized from JS code in the runtime. | ||||
| _Essential Objects_ | ||||
| 
 | ||||
| :::note pass | ||||
| Hermes does not expose a `console` or `global` variable, but they can be | ||||
| synthesized from JS code in the runtime: | ||||
| 
 | ||||
| The JavaScript code is shown below: | ||||
| - `global` can be obtained from a reference to `this` in an unbound function: | ||||
| 
 | ||||
| ```js | ||||
| /* create global object */ | ||||
| var global = (function(){ return this; }).call(null); | ||||
| ``` | ||||
| 
 | ||||
| - `console.log` can be constructed from the builtin `print` function: | ||||
| 
 | ||||
| ```js | ||||
| /* create a fake `console` from the hermes `print` builtin */ | ||||
| var console = { log: function(x) { print(x); } }; | ||||
| ``` | ||||
| 
 | ||||
| ::: | ||||
| 
 | ||||
| The code can be stored in a C string and evaluated after creating a runtime: | ||||
| The code can be stored in a C string and evaluated using `prepareJavascript` to | ||||
| prepare code and `evaluatePreparedJavascript` to evaluate: | ||||
| 
 | ||||
| ```cpp | ||||
| auto src = std::make_shared<facebook::jsi::StringBuffer>( | ||||
| const char *init_code = | ||||
|   /* create global object */ | ||||
|   "var global = (function(){ return this; }).call(null);" | ||||
|   /* create a fake `console` from the hermes `print` builtin */ | ||||
|   "var console = { log: function(x) { print(x); } };" | ||||
| ); | ||||
|   ; | ||||
| auto src = std::make_shared<facebook::jsi::StringBuffer>(init_code); | ||||
| auto js = rt->prepareJavaScript(src, std::string("<eval>")); | ||||
| rt->evaluatePreparedJavaScript(js); | ||||
| ``` | ||||
| 
 | ||||
| _Load SheetJS Scripts_ | ||||
| :::info Exception handling | ||||
| 
 | ||||
| Standard C++ exception handling patterns are used in Hermes integration code. | ||||
| The base class for Hermes exceptions is `facebook::jsi::JSIException`: | ||||
| 
 | ||||
| ```cpp | ||||
| try { | ||||
|   const char *init_code = "..."; | ||||
|   auto src = std::make_shared<facebook::jsi::StringBuffer>(init_code); | ||||
|   auto js = rt->prepareJavaScript(src, std::string("<eval>")); | ||||
|   rt->evaluatePreparedJavaScript(js); | ||||
| } catch (const facebook::jsi::JSIException &e) { | ||||
|   std::cerr << "JavaScript exception: " << e.what() << std::endl; | ||||
|   return 1; | ||||
| } | ||||
| ``` | ||||
| 
 | ||||
| ::: | ||||
| 
 | ||||
| ### Load SheetJS Scripts | ||||
| 
 | ||||
| [SheetJS Standalone scripts](/docs/getting-started/installation/standalone) can | ||||
| be parsed and evaluated in a Hermes context. | ||||
| 
 | ||||
| The main library can be loaded by reading the script from the file system and | ||||
| evaluating in the Hermes context: | ||||
| evaluating in the Hermes context. | ||||
| 
 | ||||
| :::tip pass | ||||
| 
 | ||||
| There are nonstandard tricks to embed the entire script in the binary. There are | ||||
| language proposals such as `#embed` (mirroring the same feature in C23). | ||||
| 
 | ||||
| For simplicity, the examples read the script file from the filesystem. | ||||
| 
 | ||||
| ::: | ||||
| 
 | ||||
| _Reading scripts from the filesystem_ | ||||
| 
 | ||||
| For the purposes of this demo, the standard C `<stdio.h>` methods are used: | ||||
| 
 | ||||
| ```cpp | ||||
| static char *read_file(const char *filename, size_t *sz) { | ||||
| @ -75,6 +130,17 @@ static char *read_file(const char *filename, size_t *sz) { | ||||
|   return buf; | ||||
| } | ||||
| 
 | ||||
| // ... | ||||
|   /* read SheetJS library from filesystem */ | ||||
|   size_t sz; char *xlsx_full_min_js = read_file("xlsx.full.min.js", &sz); | ||||
| ``` | ||||
| 
 | ||||
| _Hermes Wrapper_ | ||||
| 
 | ||||
| Hermes does not provide a friendly way to prepare JavaScript code stored in a | ||||
| standard heap-allocated C string. Fortunately a wrapper can be created: | ||||
| 
 | ||||
| ```cpp | ||||
| /* Unfortunately the library provides no C-friendly Buffer classes */ | ||||
| class CBuffer : public facebook::jsi::Buffer { | ||||
|   public: | ||||
| @ -89,24 +155,67 @@ class CBuffer : public facebook::jsi::Buffer { | ||||
| 
 | ||||
| // ... | ||||
|   /* load SheetJS library */ | ||||
|   size_t sz; char *xlsx_full_min_js = read_file("xlsx.full.min.js", &sz); | ||||
|   auto src = std::make_shared<CBuffer>(CBuffer((uint8_t *)xlsx_full_min_js, sz)); | ||||
| ``` | ||||
| 
 | ||||
| _Evaluating SheetJS Library Code_ | ||||
| 
 | ||||
| The code wrapper can be "prepared" with `prepareJavascript` and "evaluated" with | ||||
| `evaluatePreparedJavascript`. | ||||
| 
 | ||||
| The second argument to `preparedJavascript` is a C++ `std::string` that holds | ||||
| the source URL. Typically a name like `xlsx.full.min.js` helps distinguish | ||||
| SheetJS library exceptions from other parts of the application. | ||||
| 
 | ||||
| ```cpp | ||||
|   auto js = rt->prepareJavaScript(src, std::string("xlsx.full.min.js")); | ||||
|   rt->evaluatePreparedJavaScript(js); | ||||
| ``` | ||||
| 
 | ||||
| To confirm the library is loaded, `XLSX.version` can be printed to the console: | ||||
| _Testing_ | ||||
| 
 | ||||
| If the library is loaded, `XLSX.version` will be a string. This string can be | ||||
| pulled into the main C++ program. | ||||
| 
 | ||||
| The `evaluatePreparedJavascript` method returns a `facebook::jsi::Value` object | ||||
| that represents the result: | ||||
| 
 | ||||
| ```cpp | ||||
| auto src = std::make_shared<facebook::jsi::StringBuffer>( | ||||
|   "console.log('SheetJS Library Version: ' + XLSX.version)" | ||||
| ); | ||||
| /* evaluate XLSX.version and capture the result */ | ||||
| auto src = std::make_shared<facebook::jsi::StringBuffer>("XLSX.version"); | ||||
| auto js = rt->prepareJavaScript(src, std::string("<eval>")); | ||||
| rt->evaluatePreparedJavaScript(js); | ||||
| facebook::jsi::Value jsver = rt->evaluatePreparedJavaScript(js); | ||||
| ``` | ||||
| 
 | ||||
| The `getString` method extracts the string value and returns an internal string | ||||
| object (`facebook::jsi::String`). Given that string object, the `utf8` method | ||||
| returns a proper C++ `std::string` that can be printed: | ||||
| 
 | ||||
| ```cpp | ||||
| /* pull the version string into C++ code and print */ | ||||
| facebook::jsi::String jsstr = jsver.getString(*rt); | ||||
| std::string cppver = jsstr.utf8(*rt); | ||||
| std::cout << "SheetJS version " << cppver << std::endl; | ||||
| ``` | ||||
| 
 | ||||
| ### Reading Files | ||||
| 
 | ||||
| Typically C++ code will read files and Hermes will project the data in the JS | ||||
| engine as an `ArrayBuffer`. SheetJS libraries can parse `ArrayBuffer` data. | ||||
| 
 | ||||
| Standard SheetJS operations can pick the first worksheet and generate CSV string | ||||
| data from the worksheet. Hermes provides methods to convert the JS strings back | ||||
| to `std::string` objects for further processing in C++. | ||||
| 
 | ||||
| :::note | ||||
| 
 | ||||
| It is strongly recommended to create a stub function to perform the entire | ||||
| workflow in JS code and pass the final result back to C++. | ||||
| 
 | ||||
| ::: | ||||
| 
 | ||||
| _Hermes Wrapper_ | ||||
| 
 | ||||
| Hermes supports `ArrayBuffer` but has no simple helper to read raw memory. | ||||
| Libraries are expected to implement `MutableBuffer`: | ||||
| 
 | ||||
| @ -122,15 +231,48 @@ class CMutableBuffer : public facebook::jsi::MutableBuffer { | ||||
|     uint8_t *buf; | ||||
|     size_t sz; | ||||
| }; | ||||
| // ... | ||||
|   /* load payload as ArrayBuffer */ | ||||
|   size_t sz; char *data = read_file(argv[1], &sz); | ||||
|   auto payload = std::make_shared<CMutableBuffer>(CMutableBuffer((uint8_t *)data, sz)); | ||||
|   auto ab = facebook::jsi::ArrayBuffer(*rt, payload); | ||||
| ``` | ||||
| 
 | ||||
| It is strongly recommended to create a stub function to perform the entire | ||||
| workflow in JS code and pass the final result back to C++. | ||||
| A `facebook::jsi::ArrayBuffer` object can be created using the wrapper: | ||||
| 
 | ||||
| ```cpp | ||||
| /* load payload as ArrayBuffer */ | ||||
| size_t sz; char *data = read_file("pres.xlsx", &sz); | ||||
| auto payload = std::make_shared<CMutableBuffer>(CMutableBuffer((uint8_t *)data, sz)); | ||||
| auto ab = facebook::jsi::ArrayBuffer(*rt, payload); | ||||
| ``` | ||||
| 
 | ||||
| _SheetJS Operations_ | ||||
| 
 | ||||
| In this example, the goal is to pull the first worksheet and generate CSV rows. | ||||
| 
 | ||||
| `XLSX.read`[^1] parses the `ArrayBuffer` and returns a SheetJS workbook object: | ||||
| 
 | ||||
| ```js | ||||
| var wb = XLSX.read(buf); | ||||
| ``` | ||||
| 
 | ||||
| The `SheetNames` property[^2] is an array of the sheet names in the workbook. | ||||
| The first sheet name can be obtained with the following JS snippet: | ||||
| 
 | ||||
| ```js | ||||
| var first_sheet_name = wb.SheetNames[0]; | ||||
| ``` | ||||
| 
 | ||||
| The `Sheets` property[^3] is an object whose keys are sheet names and whose | ||||
| corresponding values are worksheet objects. | ||||
| 
 | ||||
| ```js | ||||
| var first_sheet = wb.Sheets[first_sheet_name]; | ||||
| ``` | ||||
| 
 | ||||
| The `sheet_to_csv` utility function[^4] generates a CSV string from the sheet: | ||||
| 
 | ||||
| ```js | ||||
| var csv = XLSX.utils.sheet_to_csv(first_sheet); | ||||
| ``` | ||||
| 
 | ||||
| _C++ integration code_ | ||||
| 
 | ||||
| :::note pass | ||||
| 
 | ||||
| @ -146,24 +288,41 @@ function(buf) { | ||||
| 
 | ||||
| ::: | ||||
| 
 | ||||
| _C++ integration code_ | ||||
| The result after evaluating the stub is a `facebook::jsi::Value` object: | ||||
| 
 | ||||
| ```cpp | ||||
|   /* define stub function to read and convert first sheet to CSV */ | ||||
|   auto src = std::make_shared<facebook::jsi::StringBuffer>( | ||||
|     "(function(buf) {" | ||||
|       "var wb = XLSX.read(buf);" | ||||
|       "return XLSX.utils.sheet_to_csv(wb.Sheets[wb.SheetNames[0]]);" | ||||
|     "})" | ||||
|   ); | ||||
|   auto js = rt->prepareJavaScript(src, std::string("<eval>")); | ||||
|   auto func = rt->evaluatePreparedJavaScript(js); | ||||
| /* define stub function to read and convert first sheet to CSV */ | ||||
| auto src = std::make_shared<facebook::jsi::StringBuffer>( | ||||
|   "(function(buf) {" | ||||
|     "var wb = XLSX.read(buf);" | ||||
|     "return XLSX.utils.sheet_to_csv(wb.Sheets[wb.SheetNames[0]]);" | ||||
|   "})" | ||||
| ); | ||||
| auto js = rt->prepareJavaScript(src, std::string("<eval>")); | ||||
| facebook::jsi::Value funcval = rt->evaluatePreparedJavaScript(js); | ||||
| ``` | ||||
| 
 | ||||
|   /* call stub function and capture result */ | ||||
|   auto csv = func.asObject(*rt).asFunction(*rt).call(*rt, ab); | ||||
| To call this function, the opaque `Value` must be converted to a `Function`: | ||||
| 
 | ||||
|   /* interpret as utf8 and print to stdout */ | ||||
|   std::string str = csv.getString(*rt).utf8(*rt); | ||||
| ```cpp | ||||
| facebook::jsi::Function func = func.asObject(*rt).asFunction(*rt); | ||||
| ``` | ||||
| 
 | ||||
| The `Function` exposes a `call` method to perform the function invocation. The | ||||
| stub accepts an `ArrayBuffer` argument: | ||||
| 
 | ||||
| ```cpp | ||||
| /* call stub function and capture result */ | ||||
| facebook::jsi::Value csv = func.call(*rt, ab); | ||||
| ``` | ||||
| 
 | ||||
| In the same way the library version string was pulled into C++ code, the CSV | ||||
| data can be captured using `getString` and `utf8` methods: | ||||
| 
 | ||||
| ```cpp | ||||
| /* interpret as utf8 */ | ||||
| std::string str = csv.getString(*rt).utf8(*rt); | ||||
| std::cout << str << std::endl; | ||||
| ``` | ||||
| 
 | ||||
| ## Complete Example | ||||
| @ -179,7 +338,7 @@ This demo was tested in the following deployments: | ||||
| 
 | ||||
| | Architecture | Git Commit | Date       | | ||||
| |:-------------|:-----------|:-----------| | ||||
| | `darwin-x64` | `9f8603b`  | 2023-07-24 | | ||||
| | `darwin-x64` | `70af78b`  | 2023-08-02 | | ||||
| | `darwin-arm` | `869312f`  | 2023-06-05 | | ||||
| 
 | ||||
| ::: | ||||
| @ -304,4 +463,9 @@ ready, it will read the bundled test data and print the contents as CSV. | ||||
| hermes xlsx.hermes.js | ||||
| ``` | ||||
| 
 | ||||
| If successful, the script will print CSV data from the test file | ||||
| If successful, the script will print CSV data from the test file. | ||||
| 
 | ||||
| [^1]: See [`read` in "Reading Files"](/docs/api/parse-options) | ||||
| [^2]: See ["Workbook Object"](/docs/csf/book) | ||||
| [^3]: See ["Workbook Object"](/docs/csf/book) | ||||
| [^4]: See [`sheet_to_csv` in "Utilities"](/docs/api/utilities/csv#csv-output) | ||||
|  | ||||
| @ -37,7 +37,7 @@ var global = (function(){ return this; }).call(null); | ||||
| **Console** | ||||
| 
 | ||||
| Some engines do not provide a `console` object.  `console.log` can be shimmed | ||||
| using the engine functionality.  For example, `hermes` provides `print()`: | ||||
| using the engine functionality.  For example, `hermes`[^1] provides `print()`: | ||||
| 
 | ||||
| ```js | ||||
| var console = { log: function(x) { print(x); } }; | ||||
| @ -235,3 +235,7 @@ NodeJS and Deno, Adobe UXP and other platforms. | ||||
| 
 | ||||
| This demo has been moved [to a dedicated page](/docs/demos/engines/v8). | ||||
| The demo includes examples in C++ and Rust. | ||||
| 
 | ||||
| The ["Python + Pandas" demo](/docs/demos/engines/pandas) uses V8 with Python. | ||||
| 
 | ||||
| [^1]: See ["Initialize Hermes"](/docs/demos/engines/hermes#initialize-hermes) in the Hermes demo. | ||||
| @ -1,10 +1,10 @@ | ||||
| # Note: The official Hermes documentation includes zero guidance on embedding.
 | ||||
| # Tested against commit 9f8603b9886c957e0ccead61fe4380616188bbb4
 | ||||
| # Tested against commit 70af78ba69391645749b40a3674d7321c4d6177a
 | ||||
| 
 | ||||
| .PHONY: doit | ||||
| doit: sheetjs-hermes | ||||
| 	curl -LO https://cdn.sheetjs.com/xlsx-latest/package/dist/xlsx.full.min.js | ||||
| 	curl -LO https://sheetjs.com/pres.numbers | ||||
| 	if [ ! -e xlsx.full.min.js ]; then curl -LO https://cdn.sheetjs.com/xlsx-latest/package/dist/xlsx.full.min.js; fi | ||||
| 	if [ ! -e pres.numbers ]; then curl -LO https://sheetjs.com/pres.numbers; fi | ||||
| 	./sheetjs-hermes pres.numbers | ||||
| 
 | ||||
| .PHONY: clean | ||||
| @ -53,5 +53,5 @@ sheetjs-hermes: sheetjs-hermes.cpp init | ||||
| 
 | ||||
| .PHONY: init | ||||
| init: | ||||
| 	if [ ! -e hermes ]; then git clone https://github.com/facebook/hermes.git; cd hermes; git checkout 9f8603b9886c957e0ccead61fe4380616188bbb4; cd ..; fi | ||||
| 	if [ ! -e hermes ]; then git clone https://github.com/facebook/hermes.git; cd hermes; git checkout 70af78ba69391645749b40a3674d7321c4d6177a; cd ..; fi | ||||
| 	if [ ! -e build_release ]; then cmake -S hermes -B build_release -G Ninja -DCMAKE_BUILD_TYPE=Release; cmake --build ./build_release; fi | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user