30 KiB
| title | pagination_prev | pagination_next |
|---|---|---|
| Swift + JavaScriptCore | demos/bigdata/index | solutions/input |
import current from '/version.js'; import Tabs from '@theme/Tabs'; import TabItem from '@theme/TabItem'; import CodeBlock from '@theme/CodeBlock';
export const r = {style: {color:"red"}}; export const B = {style: {fontWeight:"bold"}};
JavaScriptCore (JSC) is the JavaScript engine powering the Safari web browser.
SheetJS is a JavaScript library for reading and writing data from spreadsheets.
This demo uses JSC and SheetJS to read and write spreadsheets. We'll explore how to load SheetJS in a JSC context and process spreadsheets and structured data from C++ and Swift programs.
:::note pass
This demo was tested in the following environments:
Swift on MacOS supports JavaScriptCore without additional dependencies.
| Architecture | Swift | Date |
|---|---|---|
darwin-x64 |
6.1 |
2025-04-21 |
darwin-arm |
6.1 |
2025-04-21 |
JavaScriptCore can be built from source and linked in C / C++ programs.
| Architecture | Version | Date |
|---|---|---|
darwin-x64 |
7620.2.4.111.7 |
2025-04-21 |
darwin-arm |
7620.2.4.111.7 |
2025-04-21 |
linux-x64 |
7620.2.4.111.7 |
2025-04-21 |
linux-arm |
7620.2.4.111.7 |
2025-04-21 |
Swift compiler can link against libraries built from the JavaScriptCore source.
| Architecture | Version | Date |
|---|---|---|
linux-x64 |
7620.2.4.111.7 |
2025-04-21 |
linux-arm |
7620.2.4.111.7 |
2025-04-21 |
:::
Integration Details
The SheetJS Standalone scripts can be parsed and evaluated in a JSC context.
Binary strings can be passed back and forth using String.Encoding.isoLatin1.
The SheetJS read method1, with the "binary" type, can parse binary strings.
The write method2, with the "binary" type, can create binary strings.
JSC provides a few special methods for working with Uint8Array objects:
-
JSObjectMakeTypedArrayWithBytesNoCopy3 creates a typed array from a pointer and size. It uses the memory address directly (no copy). -
JSObjectGetTypedArrayLength4 andJSObjectGetTypedArrayBytesPtr5 can return a pointer and size pair from aUint8Arrayin the JSC engine.
The SheetJS read method6 can process Uint8Array objects.
The write method7, with the "buffer" type, creates Uint8Array data.
Initialize JSC
A JSC context can be created with the JSContext function:
var context: JSContext!
do {
context = JSContext();
context.exceptionHandler = { _, X in if let e = X { print(e.toString()!); }; };
} catch { print(error.localizedDescription); }
A JSC context can be created with the JSGlobalContextCreate function:
JSGlobalContextRef ctx = JSGlobalContextCreate(NULL);
JSC does not provide a global variable. It can be created in one line:
do {
// highlight-next-line
context.evaluateScript("var global = (function(){ return this; }).call(null);");
} catch { print(error.localizedDescription); }
#define DOIT(cmd) \
JSStringRef script = JSStringCreateWithUTF8CString(cmd); \
JSValueRef result = JSEvaluateScript(ctx, script, NULL, NULL, 0, NULL); \
JSStringRelease(script);
{ DOIT("var global = (function(){ return this; }).call(null);") }
Load SheetJS Scripts
The main library can be loaded by reading the scripts from the file system and evaluating in the JSC context:
let src = try String(contentsOfFile: "xlsx.full.min.js");
context.evaluateScript(src);
/* load library */
{
size_t sz = 0; char *file = read_file("xlsx.full.min.js", &sz);
DOIT(file);
}
To confirm the library is loaded, XLSX.version can be inspected:
let XLSX: JSValue! = context.objectForKeyedSubscript("XLSX");
if let ver = XLSX.objectForKeyedSubscript("version") { print(ver.toString()); }
#define JS_STR_TO_C \
JSStringRef str = JSValueToStringCopy(ctx, result, NULL); \
size_t sz = JSStringGetMaximumUTF8CStringSize(str); \
char *buf = (char *)malloc(sz); \
JSStringGetUTF8CString(str, buf, sz); \
/* get version string */
{
DOIT("XLSX.version")
JS_STR_TO_C
printf("SheetJS library version %s\n", buf);
}
Reading Files
String(contentsOf:encoding:) reads from a path and returns an encoded string:
/* read sheetjs.xls as Base64 string */
let file_path = shared_dir.appendingPathComponent("sheetjs.xls");
let data: String! = try String(contentsOf: file_path, encoding: String.Encoding.isoLatin1);
This string can be loaded into the JS engine and processed:
/* load data in JSC */
context.setObject(data, forKeyedSubscript: "payload" as (NSCopying & NSObjectProtocol));
/* `payload` (the "forKeyedSubscript" parameter) is a binary string */
context.evaluateScript("var wb = XLSX.read(payload, {type:'binary'});");
Direct Read (click to show)
Uint8Array data can be passed directly, skipping string encoding and decoding:
let url = URL(fileURLWithPath: file)
var data: Data! = try Data(contentsOf: url);
let count = data.count;
/* Note: the operations must be performed in the closure! */
let wb: JSValue! = data.withUnsafeMutableBytes { (dataPtr: UnsafeMutableRawBufferPointer) in
// highlight-next-line
let ab: JSValue! = JSValue(jsValueRef: JSObjectMakeTypedArrayWithBytesNoCopy(context.jsGlobalContextRef, kJSTypedArrayTypeUint8Array, dataPtr.baseAddress, count, nil, nil, nil), in: context)
/* prepare options argument */
context.evaluateScript(String(format: "var readopts = {type:'array', dense:true}"));
let readopts: JSValue = context.objectForKeyedSubscript("readopts");
/* call XLSX.read */
let XLSX: JSValue! = context.objectForKeyedSubscript("XLSX");
let readfunc: JSValue = XLSX.objectForKeyedSubscript("read");
return readfunc.call(withArguments: [ab, readopts]);
}
For broad compatibility with Swift versions, the demo uses the String method.
There are a few steps for loading data into the JSC engine:
A) The file must be read into a char* buffer (using standard C methods)
size_t sz; char *file = read_file(argv[1], &sz);
B) The typed array must be created with JSObjectMakeTypedArrayWithBytesNoCopy
JSValueRef u8 = JSObjectMakeTypedArrayWithBytesNoCopy(ctx, kJSTypedArrayTypeUint8Array, file, sz, NULL, NULL, NULL);
C) The typed array must be bound to a variable in the global scope:
/* assign to `global.buf` */
JSObjectRef global = JSContextGetGlobalObject(ctx);
JSStringRef key = JSStringCreateWithUTF8CString("buf");
JSObjectSetProperty(ctx, global, key, u8, 0, NULL);
JSStringRelease(key);
Writing Files
When writing to binary string in JavaScriptCore, the result should be stored in a variable and converted to string in Swift:
/* write to binary string */
context.evaluateScript("var out = XLSX.write(wb, {type:'binary', bookType:'xlsx'})");
/* `out` from the script is a binary string that can be stringified in Swift */
let outvalue: JSValue! = context.objectForKeyedSubscript("out");
var out: String! = outvalue.toString();
String#write(to:atomically:encoding) writes the string to the specified path:
/* write to sheetjsw.xlsx */
let out_path = shared_dir.appendingPathComponent("sheetjsw.xlsx");
try? out.write(to: out_path, atomically: false, encoding: String.Encoding.isoLatin1);
The SheetJS write method with type "buffer" will return a Uint8Array object:
DOIT("XLSX.write(wb, {type:'buffer', bookType:'xlsb'});")
JSObjectRef u8 = JSValueToObject(ctx, result, NULL);
Given the result object, JSObjectGetTypedArrayLength pulls the length into C:
size_t sz = JSObjectGetTypedArrayLength(ctx, u8, NULL);
JSObjectGetTypedArrayBytesPtr returns a pointer to the result buffer:
char *buf = (char *)JSObjectGetTypedArrayBytesPtr(ctx, u8, NULL);
The data can be written to file using standard C methods:
FILE *f = fopen("sheetjsw.xlsb", "wb"); fwrite(buf, 1, sz, f); fclose(f);
Complete Example
Swift
The demo includes a sample SheetJSCore Wrapper class to simplify operations.
:::caution This demo only runs on MacOS
This example requires MacOS + Swift and will not work on Windows or Linux!
The "Swift C" section covers integration in other platforms.
:::
- Ensure Swift is installed by running the following command in the terminal:
swiftc --version
If the command is not found, install Xcode.
- Create a folder for the project:
mkdir sheetjswift
cd sheetjswift
- Download the SheetJS Standalone script and the test file. Save both files in the project directory:
- xlsx.full.min.js
- pres.numbers
{\ curl -LO https://cdn.sheetjs.com/xlsx-${current}/package/dist/xlsx.full.min.js curl -LO https://docs.sheetjs.com/pres.numbers}
- Download the Swift scripts for the demo
SheetJSCore.swiftWrapper librarymain.swiftCommand-line script
curl -LO https://docs.sheetjs.com/swift/SheetJSCore.swift
curl -LO https://docs.sheetjs.com/swift/main.swift
- Build the
SheetJSwiftprogram:
swiftc SheetJSCore.swift main.swift -o SheetJSwift
- Test the program:
./SheetJSwift pres.numbers
If successful, a CSV will be printed to console. The script also tries to write
to SheetJSwift.xlsx. That file can be verified by opening in Excel / Numbers.
C++
:::danger pass
Older versions of this demo recommended downloading the WebKit release archives.
Microsoft disabled all WebKit archive downloads!
https://codeload.github.com/WebKit/WebKit/zip/refs/tags/WebKit-7620.2.4.111.7 ,
when the demo was last tested, returned HTTP 422 Archive creation is blocked.
The updated instructions now clone the repository. An additional 20GB of storage space and 11GB of bandwidth is required to fetch and store the code.
:::
- Install dependencies
Installation Notes (click to show)
The build requires CMake and Ruby.
On macOS, dependencies should be installed with brew:
brew install cmake ruby
On the Steam Deck, dependencies should be installed with pacman:
sudo pacman -Syu base-devel cmake ruby icu glibc linux-api-headers
On Debian and Ubuntu, dependencies should be installed with apt:
sudo apt-get install build-essential cmake ruby
- Create a project folder:
mkdir sheetjs-jsc
cd sheetjs-jsc
- Clone the WebKit repository and switch to the
WebKit-7620.2.4.111.7tag:
git clone https://github.com/WebKit/WebKit.git WebKit
cd WebKit
git checkout WebKit-7620.2.4.111.7
cd ..
- Build JavaScriptCore:
cd WebKit
env CFLAGS="-Wno-error=all -Wno-deprecated-declarations" CXXFLAGS="-Wno-error=all -Wno-deprecated-declarations" LDFLAGS="-framework Foundation" Tools/Scripts/build-webkit --jsc-only --cmakeargs="-Wno-error=all -DENABLE_STATIC_JSC=ON -DCMAKE_C_FLAGS=\"-Wno-error=all -Wno-deprecated-declarations\" -DCMAKE_CXX_FLAGS=\"-Wno-error=all -Wno-deprecated-declarations\"" --make-args="-Wno-error=all -Wno-deprecated-declarations"
cd ..
:::note pass
In some test runs on ARM64 macOS, JIT elicited runtime errors and WebAssembly elicited compile-time errors. WebAssembly and JIT should be disabled:
cd WebKit
env CFLAGS="-Wno-error=all -Wno-deprecated-declarations" CXXFLAGS="-Wno-error=all -Wno-deprecated-declarations" LDFLAGS="-framework Foundation" Tools/Scripts/build-webkit --jsc-only --cmakeargs="-Wno-error=all -DENABLE_STATIC_JSC=ON -DCMAKE_C_FLAGS=\"-Wno-error=all -Wno-deprecated-declarations\" -DCMAKE_CXX_FLAGS=\"-Wno-error=all -Wno-deprecated-declarations\"" --make-args="-Wno-error=all -Wno-deprecated-declarations" --no-jit --no-webassembly
cd ..
:::
:::note pass
In some test runs, the build failed with the error message
Source/WTF/wtf/text/ASCIILiteral.h:65:34: error: use of undeclared identifier 'NSString'
WTF_EXPORT_PRIVATE RetainPtr<NSString> createNSString() const;
^
1 error generated.
The referenced header file must be patched to declare NSString:
#include <wtf/text/SuperFastHash.h>
// highlight-start
#ifdef __OBJC__
@class NSString;
#endif
// highlight-end
namespace WTF {
:::
:::note pass
In some test runs, the build failed with the error message
Source/JavaScriptCore/runtime/JSCBytecodeCacheVersion.cpp:37:10: fatal error: 'wtf/spi/darwin/dyldSPI.h' file not found
#include <wtf/spi/darwin/dyldSPI.h>
^~~~~~~~~~~~~~~~~~~~~~~~~~
1 error generated.
The #include should be changed to a relative directive:
#include <wtf/NeverDestroyed.h>
// highlight-start
#include "../../WTF/wtf/spi/darwin/dyldSPI.h"
// highlight-end
#endif
:::
cd WebKit
env CFLAGS="-Wno-error=all -Wno-error=volatile-register-var -Wno-dangling-reference" CXXFLAGS="-Wno-error=all -Wno-error=volatile-register-var -Wno-dangling-reference" Tools/Scripts/build-webkit --jsc-only --cmakeargs="-Wno-error=all -Wno-error=volatile-register-var -DENABLE_STATIC_JSC=ON -DUSE_THIN_ARCHIVES=OFF -DCMAKE_C_FLAGS=\"-Wno-error=all -Wno-error=volatile-register-var -Wno-dangling-reference\" -DCMAKE_CXX_FLAGS=\"-Wno-error=all -Wno-error=volatile-register-var \"" --make-args="-j1 -Wno-error=all -Wno-error=volatile-register-var " -j1
cd ..
:::caution pass
When this was last tested on the Steam Deck, the build ran for 24 minutes!
:::
:::note pass
In some test runs on AArch64 Linux, there was a dangling pointer error:
WebKitBuild/JSCOnly/Release/WTF/Headers/wtf/SentinelLinkedList.h:61:55: error: storing the address of local variable ‘toBeRemoved’ in ‘{"*"}MEM[(struct BasicRawSentinelNode {"*"} const &)this_4(D) + 96].WTF::BasicRawSentinelNode<JSC::CallLinkInfoBase>::m_next’ [-Werror=dangling-pointer=] {" 61 |"} void setNext(BasicRawSentinelNode* next) {"{"} m_next = next; {"}"} {" |"} ~~~~~~~^~~~~~
The error can be suppressed with preprocessor directives around the definition:
BasicRawSentinelNode() = default;
// highlight-start
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wdangling-pointer"
// highlight-end
void setPrev(BasicRawSentinelNode* prev) { m_prev = prev; }
void setNext(BasicRawSentinelNode* next) { m_next = next; }
// highlight-next-line
#pragma GCC diagnostic pop
T* prev() const { return static_cast<T*>(PtrTraits::unwrap(m_prev)); }
After patching the header, JSC must be built without WebAssembly or JIT support:
cd WebKit
env CFLAGS="-Wno-error=all -Wno-error=volatile-register-var -Wno-dangling-reference" CXXFLAGS="-Wno-error=all -Wno-error=volatile-register-var -Wno-dangling-reference" Tools/Scripts/build-webkit --jsc-only --cmakeargs="-Wno-error=all -Wno-error=volatile-register-var -DENABLE_STATIC_JSC=ON -DUSE_THIN_ARCHIVES=OFF -DCMAKE_C_FLAGS=\"-Wno-error=all -Wno-error=volatile-register-var -Wno-dangling-reference\" -DCMAKE_CXX_FLAGS=\"-Wno-error=all -Wno-error=volatile-register-var \"" --make-args="-j1 -Wno-error=all -Wno-error=volatile-register-var " -j1 --no-jit --no-webassembly
cd ..
:::
:::caution pass
In some test runs, there was a register error:
WebKit/Source/JavaScriptCore/heap/MarkedBlock.cpp: In member function ‘void JSC::MarkedBlock::dumpInfoAndCrashForInvalidHandle(WTF::AbstractLocker&, JSC::HeapCell*)’:
WebKit/Source/JavaScriptCore/heap/MarkedBlock.cpp:589:32: error: address of explicit register variable ‘savedActualVM’ requested
589 | VMInspector::forEachVM([&](VM& vm) {
| ^~~~~~~~~~~~~
590 | if (blockVM == &vm) {
| ~~~~~~~~~~~~~~~~~~~~~
591 | isValidBlockVM = true;
| ~~~~~~~~~~~~~~~~~~~~~~
592 | SAVE_TO_REG(savedActualVM, &vm);
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
593 | SAVE_TO_REG(savedBitfield, 8);
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
594 | LOG_INVALID_HANDLE_DETAILS("block VM %p is valid\n", &vm);
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
595 | return IterationStatus::Done;
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
596 | }
| ~
597 | return IterationStatus::Continue;
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
598 | });
| ~
Until there is a proper upstream fix, the workaround is to explicitly no-op the
SAVE_TO_REG macro in MarkedBlock.cpp:
#endif
#define SAVE_TO_REG(name, value) do { \
- name = WTF::opaque(value); \
- WTF::compilerFence(); \
} while (false)
NO_RETURN_DUE_TO_CRASH NEVER_INLINE void MarkedBlock::dumpInfoAndCrashForInvalidHandle(AbstractLocker&, HeapCell* heapCell)
:::
- Create a symbolic link to the
Releasefolder in the source tree:
ln -s WebKit/WebKitBuild/JSCOnly/Release .
- Download
sheetjs-jsc.c:
curl -LO https://docs.sheetjs.com/jsc/sheetjs-jsc.c
- Compile the program:
g++ -o sheetjs-jsc sheetjs-jsc.c -IRelease/JavaScriptCore/Headers -LRelease/lib -lbmalloc -licucore -lWTF -lJavaScriptCore -IRelease/JavaScriptCore/Headers -framework Foundation
:::note pass
In some test runs, there were ld warnings about macOS versions:
ld: warning: object file (Release/lib/libWTF.a[2](ASCIICType.cpp.o)) was built for newer 'macOS' version (14.5) than being linked (14.0)
These warnings can be ignored.
:::
g++ -o sheetjs-jsc sheetjs-jsc.c -IRelease/JavaScriptCore/Headers -LRelease/lib -lJavaScriptCore -lWTF -lbmalloc -licui18n -licuuc -latomic -IRelease/JavaScriptCore/Headers
- Download the SheetJS Standalone script and the test file. Save both files in the project directory:
- xlsx.full.min.js
- pres.numbers
{\ curl -LO https://cdn.sheetjs.com/xlsx-${current}/package/dist/xlsx.full.min.js curl -LO https://docs.sheetjs.com/pres.numbers}
- Run the program:
./sheetjs-jsc pres.numbers
If successful, a CSV will be printed to console. The script also tries to write
to sheetjsw.xlsb, which can be opened in a spreadsheet editor.
Swift C
:::note pass
For macOS and iOS deployments, it is strongly encouraged to use the official
JavaScriptCore bindings. This demo is suited for Linux Swift applications.
:::
- Install the Swift toolchain.8
Installation Notes (click to show)
The linux-x64 test was run on Ubuntu 22.04 using Swift 6.1
The linux-arm test was run on Debian 12 "bookworm" using Swift 6.1
-
Follow the entire "C" demo. The shared library will be used in Swift.
-
Enter the
sheetjs-jscfolder from the previous step. -
Create a folder
sheetjswift. It should be in thesheetjs-jscfolder:
mkdir sheetjswift
cd sheetjswift
- Download the SheetJS Standalone script and the test file. Save both files in the project directory:
- xlsx.full.min.js
- pres.numbers
{\ curl -LO https://cdn.sheetjs.com/xlsx-${current}/package/dist/xlsx.full.min.js curl -LO https://docs.sheetjs.com/pres.numbers}
- Copy all generated headers to the current directory:
find ../WebKit/WebKitBuild/JSCOnly/Release/JavaScriptCore/Headers/ -name \*.h | xargs -I '%' cp '%' .
- Edit each header file and replace all instances of
<JavaScriptCore/with<. For example,JavaScript.hincludes<JavaScriptCore/JSBase.h>:
#include <JavaScriptCore/JSBase.h>
This must be changed to <JSBase.h>:
#include <JSBase.h>
- Print the current working directory. It will be the path to
sheetjswift:
pwd
- Create a new header named
JavaScriptCore-Bridging-Header.h:
#import "/tmp/sheetjs-jsc/sheetjswift/JavaScript.h"
Replace the import path to the working directory from step 7. For example, if
the path was /home/sheetjs/sheetjs-jsc/sheetjswift/, the import should be
#import "/home/sheetjs/sheetjs-jsc/JavaScript.h"
- Create the default module map
module.modulemap:
module JavaScriptCore {
header "./JavaScript.h"
link "JavaScriptCore"
}
- Download
SheetJSCRaw.swift:
curl -LO https://docs.sheetjs.com/swift/SheetJSCRaw.swift
- Build
SheetJSwift:
swiftc -Xcc -I$(pwd) -Xlinker -L../WebKit/WebKitBuild/JSCOnly/Release/lib/ -Xlinker -lJavaScriptCore -Xlinker -lWTF -Xlinker -lbmalloc -Xlinker -lstdc++ -Xlinker -latomic -Xlinker -licuuc -Xlinker -licui18n -import-objc-header JavaScriptCore-Bridging-Header.h SheetJSCRaw.swift -o SheetJSwift
- Run the command:
./SheetJSwift pres.numbers
If successful, a CSV will be printed to console. The program also tries to write
to SheetJSwift.xlsx, which can be opened in a spreadsheet editor.
Bindings
It is straightforward to load the platform-native (macOS) or compiled libraries in programs built in other programming languages.
The JavaScriptCore C interface does not use "blingos" (function-like macros), so it is possible to reference each method in an external binding.
Rust
Writing bindings is fairly mechanical. There are 4 parts to the process:
-
Link to the external library.
-
Generate Rust representations of the original C data types.
-
Translate the function declaration.
-
Write a wrapper to convert between Rust concepts and C concepts.
For example, the following C code creates a string within the engine from a UTF8 string:
const char *code = "'Sheet' + 'JS'";
JSStringRef script = JSStringCreateWithUTF8CString(code);
An ergonomic wrapper function would take a Rust string literal and handle unsafe data operations:
let code: &str = "'Sheet' + 'JS'";
let script: JSStringRef = JSC::JSStringCreateWithUTF8CString(code);
Rust Linkage
Custom directives are typically added to build.rs. The cargo::rustc-link-lib
directive instructs the Rust compiler to link against an external library.
The following snippet will instruct the toolchain to link against the system
JavaScriptCore.framework framework on macOS:
#[cfg(target_os = "macos")]
fn main() {
// highlight-next-line
println!("cargo::rustc-link-lib=framework=JavaScriptCore");
}
Rust Types
JSStringRef is a pointer to an opaque data type. The spiritual equivalent
according to the Rustonomicon is a pointer to an opaque struct9:
#[repr(C)]
pub struct JSString {
_data: [u8; 0],
_marker: core::marker::PhantomData<(*mut u8, core::marker::PhantomPinned)>,
}
// highlight-next-line
type JSStringRef = *mut JSContext;
Function Declaration
The JSStringCreateWithUTF8CString function is declared in C as follows:
JSStringRef JSStringCreateWithUTF8CString(const char * string);
The equivalent Rust declaration must be defined in an extern "C" block:
unsafe extern "C" {
// JSStringRef JSStringCreateWithUTF8CString(const char * string);
pub unsafe fn JSStringCreateWithUTF8CString(string: *const u8) -> JSStringRef;
}
Rust Interchange
The std::ffi module includes a number of helpers for passing data between Rust
code and C libraries.
Rust string literals are commonly represented as &str types:
let script: &str = "'Sheet' + 'JS'"; // 'Sheet' + 'JS'
The following unsafe waltz creates a compatible pointer:
A) Convert the &str to a byte slice
B) Create a std::ffi::CString instance from the bytes
C) Use the as_ptr method to generate a pointer
/* start with a string literal */
let script: &str = "'Sheet' + 'JS'";
/* generate CString */
let cstr: std::ffi::CString = std::ffi::CString::new(script.as_bytes()).unwrap();
/* call JSStringCreateWithUTF8CString */
let ref: JSStringRef = JSStringCreateWithUTF8CString(cstr.as_ptr() as *const u8);
The demo makes a safe wrapper to perform the unsafe waltz in one line:
pub struct JSC;
impl JSC {
pub fn JSStringCreateWithUTF8CString(str: &str) -> JSStringRef { unsafe {
// highlight-next-line
JSStringCreateWithUTF8CString(std::ffi::CString::new(str.as_bytes()).unwrap().as_ptr() as *const u8)
} }
}
Rust Example
:::note Tested Deployments
This demo was last tested in the following deployments:
| Architecture | Date |
|---|---|
darwin-x64 |
2025-03-31 |
darwin-arm |
2025-03-30 |
:::
- Create a new project:
cargo new sheetjs-jsc
cd sheetjs-jsc
cargo run
- Download the SheetJS Standalone script to the
srcfolder in the project:
- xlsx.full.min.js
{\ curl -L -o src/xlsx.full.min.js https://cdn.sheetjs.com/xlsx-${current}/package/dist/xlsx.full.min.js}
- Download the test file to the project directory:
{\ curl -LO https://docs.sheetjs.com/pres.numbers}
- Download
main.rsand replacesrc/main.rs:
curl -L -o src/main.rs https://docs.sheetjs.com/jsc/main.rs
- Download
build.rsto the project folder:
curl -LO https://docs.sheetjs.com/jsc/build.rs
- Build and run the app:
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 a spreadsheet
editor that supports XLSB spreadsheets.
-
See
JSObjectMakeTypedArrayWithBytesNoCopyin the JavaScriptCore documentation. ↩︎ -
See
JSObjectGetTypedArrayLengthin the JavaScriptCore documentation. ↩︎ -
See
JSObjectGetTypedArrayBytesPtrin the JavaScriptCore documentation. ↩︎ -
See "Install Swift" in the Swift website. ↩︎
-
See "Representing opaque structs" in the Rustonomicon ↩︎