├── .nvmrc ├── .watchmanconfig ├── example ├── .node-version ├── .watchmanconfig ├── .bundle │ └── config ├── app.json ├── ios │ ├── File.swift │ ├── QuickjsExample │ │ ├── Images.xcassets │ │ │ ├── Contents.json │ │ │ └── AppIcon.appiconset │ │ │ │ └── Contents.json │ │ ├── AppDelegate.h │ │ ├── main.m │ │ ├── Info.plist │ │ ├── AppDelegate.mm │ │ └── LaunchScreen.storyboard │ ├── QuickjsExample-Bridging-Header.h │ ├── QuickjsExample.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata │ │ │ └── IDEWorkspaceChecks.plist │ ├── TTIModule.h │ ├── .xcode.env │ ├── QuickjsExampleTests │ │ ├── Info.plist │ │ └── QuickjsExampleTests.m │ ├── TTIModule.m │ ├── Podfile │ └── QuickjsExample.xcodeproj │ │ └── xcshareddata │ │ └── xcschemes │ │ └── QuickjsExample.xcscheme ├── android │ ├── app │ │ ├── debug.keystore │ │ ├── src │ │ │ ├── main │ │ │ │ ├── res │ │ │ │ │ ├── values │ │ │ │ │ │ ├── strings.xml │ │ │ │ │ │ └── styles.xml │ │ │ │ │ ├── mipmap-hdpi │ │ │ │ │ │ ├── ic_launcher.png │ │ │ │ │ │ └── ic_launcher_round.png │ │ │ │ │ ├── mipmap-mdpi │ │ │ │ │ │ ├── ic_launcher.png │ │ │ │ │ │ └── ic_launcher_round.png │ │ │ │ │ ├── mipmap-xhdpi │ │ │ │ │ │ ├── ic_launcher.png │ │ │ │ │ │ └── ic_launcher_round.png │ │ │ │ │ ├── mipmap-xxhdpi │ │ │ │ │ │ ├── ic_launcher.png │ │ │ │ │ │ └── ic_launcher_round.png │ │ │ │ │ ├── mipmap-xxxhdpi │ │ │ │ │ │ ├── ic_launcher.png │ │ │ │ │ │ └── ic_launcher_round.png │ │ │ │ │ └── drawable │ │ │ │ │ │ └── rn_edit_text_material.xml │ │ │ │ ├── java │ │ │ │ │ └── com │ │ │ │ │ │ └── quickjsexample │ │ │ │ │ │ ├── TTIRecorder.java │ │ │ │ │ │ ├── TTIPackage.java │ │ │ │ │ │ ├── MainActivity.java │ │ │ │ │ │ └── MainApplication.java │ │ │ │ └── AndroidManifest.xml │ │ │ ├── debug │ │ │ │ ├── AndroidManifest.xml │ │ │ │ └── java │ │ │ │ │ └── com │ │ │ │ │ └── quickjsexample │ │ │ │ │ └── ReactNativeFlipper.java │ │ │ └── release │ │ │ │ └── java │ │ │ │ └── com │ │ │ │ └── quickjsexample │ │ │ │ └── ReactNativeFlipper.java │ │ ├── proguard-rules.pro │ │ └── build.gradle │ ├── gradle │ │ └── wrapper │ │ │ ├── gradle-wrapper.jar │ │ │ └── gradle-wrapper.properties │ ├── settings.gradle │ ├── build.gradle │ ├── gradle.properties │ ├── gradlew.bat │ └── gradlew ├── Gemfile ├── index.js ├── react-native.config.js ├── babel.config.js ├── src │ ├── App.tsx │ └── SearchableList.js ├── package.json └── metro.config.js ├── src ├── __tests__ │ └── index.test.tsx └── index.tsx ├── google2c963af918d87364.html ├── .gitattributes ├── tsconfig.build.json ├── babel.config.js ├── docs ├── performance.xlsx ├── png │ ├── AndroidSOSize.png │ └── ExampleTTI&Memory.png └── DemoPerformance.md ├── .yarnrc ├── android ├── src │ └── main │ │ ├── AndroidManifest.xml │ │ ├── java │ │ └── com │ │ │ └── quickjs │ │ │ ├── QuickjsPackage.java │ │ │ ├── QuickJSExecutor.java │ │ │ └── QuickJSExecutorFactory.java │ │ └── jni │ │ ├── QuickJSExecutorFactory.h │ │ ├── OnLoad.cpp │ │ └── QuickJSExecutorFactory.cpp ├── gradle.properties ├── CMakeLists.txt └── build.gradle ├── ios ├── Quickjs.h ├── Quickjs.mm ├── QuickJSExecutorFactory.h ├── QuickJSExecutorFactory.mm └── Quickjs.xcodeproj │ └── project.pbxproj ├── cpp ├── QuickJSRuntimeFactory.h ├── QuickJSRuntimeFactory.cpp ├── QuickJSPointerValue.cpp ├── QuickJSPointerValue.h ├── QuickJSInstrumentation.cpp ├── QuickJSInstrumentation.h ├── ScopedJSValue.h ├── JSIValueConverter.h ├── HostProxy.h ├── engine │ ├── libregexp-opcode.h │ ├── libregexp.h │ ├── list.h │ ├── libunicode.h │ ├── cutils.h │ └── quickjs-atom.h ├── city.h ├── JSIValueConverter.cpp ├── HostProxy.cpp └── QuickJSRuntime.h ├── .editorconfig ├── lefthook.yml ├── addr.sh ├── .github ├── actions │ └── setup │ │ └── action.yml └── workflows │ └── ci.yml ├── tsconfig.json ├── scripts └── bootstrap.js ├── .gitignore ├── LICENSE ├── react-native-quickjs.podspec ├── package.json ├── CONTRIBUTING.md ├── CODE_OF_CONDUCT.md └── README.md /.nvmrc: -------------------------------------------------------------------------------- 1 | 16.18.1 2 | -------------------------------------------------------------------------------- /.watchmanconfig: -------------------------------------------------------------------------------- 1 | {} -------------------------------------------------------------------------------- /example/.node-version: -------------------------------------------------------------------------------- 1 | 18 2 | -------------------------------------------------------------------------------- /example/.watchmanconfig: -------------------------------------------------------------------------------- 1 | {} -------------------------------------------------------------------------------- /src/__tests__/index.test.tsx: -------------------------------------------------------------------------------- 1 | it.todo('write a test'); 2 | -------------------------------------------------------------------------------- /google2c963af918d87364.html: -------------------------------------------------------------------------------- 1 | google-site-verification: google2c963af918d87364.html -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | *.pbxproj -text 2 | # specific for windows script files 3 | *.bat text eol=crlf -------------------------------------------------------------------------------- /example/.bundle/config: -------------------------------------------------------------------------------- 1 | BUNDLE_PATH: "vendor/bundle" 2 | BUNDLE_FORCE_RUBY_PLATFORM: 1 3 | -------------------------------------------------------------------------------- /example/app.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "QuickjsExample", 3 | "displayName": "QuickjsExample" 4 | } -------------------------------------------------------------------------------- /tsconfig.build.json: -------------------------------------------------------------------------------- 1 | 2 | { 3 | "extends": "./tsconfig", 4 | "exclude": ["example"] 5 | } 6 | -------------------------------------------------------------------------------- /babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: ['module:metro-react-native-babel-preset'], 3 | }; 4 | -------------------------------------------------------------------------------- /docs/performance.xlsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bojie-liu/react-native-quickjs/HEAD/docs/performance.xlsx -------------------------------------------------------------------------------- /example/ios/File.swift: -------------------------------------------------------------------------------- 1 | // 2 | // File.swift 3 | // QuickjsExample 4 | // 5 | 6 | import Foundation 7 | -------------------------------------------------------------------------------- /docs/png/AndroidSOSize.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bojie-liu/react-native-quickjs/HEAD/docs/png/AndroidSOSize.png -------------------------------------------------------------------------------- /docs/png/ExampleTTI&Memory.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bojie-liu/react-native-quickjs/HEAD/docs/png/ExampleTTI&Memory.png -------------------------------------------------------------------------------- /.yarnrc: -------------------------------------------------------------------------------- 1 | # Override Yarn command so we can automatically setup the repo on running `yarn` 2 | 3 | yarn-path "scripts/bootstrap.js" 4 | -------------------------------------------------------------------------------- /example/android/app/debug.keystore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bojie-liu/react-native-quickjs/HEAD/example/android/app/debug.keystore -------------------------------------------------------------------------------- /example/android/app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | QuickjsExample 3 | 4 | -------------------------------------------------------------------------------- /example/ios/QuickjsExample/Images.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /example/ios/QuickjsExample-Bridging-Header.h: -------------------------------------------------------------------------------- 1 | // 2 | // Use this file to import your target's public headers that you would like to expose to Swift. 3 | // 4 | -------------------------------------------------------------------------------- /example/ios/QuickjsExample/AppDelegate.h: -------------------------------------------------------------------------------- 1 | #import 2 | #import 3 | 4 | @interface AppDelegate : RCTAppDelegate 5 | 6 | @end 7 | -------------------------------------------------------------------------------- /android/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /example/android/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bojie-liu/react-native-quickjs/HEAD/example/android/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /android/gradle.properties: -------------------------------------------------------------------------------- 1 | Quickjs_kotlinVersion=1.7.0 2 | Quickjs_minSdkVersion=21 3 | Quickjs_targetSdkVersion=31 4 | Quickjs_compileSdkVersion=31 5 | Quickjs_ndkversion=21.4.7075529 6 | -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bojie-liu/react-native-quickjs/HEAD/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bojie-liu/react-native-quickjs/HEAD/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bojie-liu/react-native-quickjs/HEAD/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bojie-liu/react-native-quickjs/HEAD/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bojie-liu/react-native-quickjs/HEAD/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bojie-liu/react-native-quickjs/HEAD/example/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bojie-liu/react-native-quickjs/HEAD/example/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /example/Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | 3 | # You may use http://rbenv.org/ or https://rvm.io/ to install and use this version 4 | ruby '>= 2.6.10' 5 | 6 | gem 'cocoapods', '>= 1.11.3' 7 | -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bojie-liu/react-native-quickjs/HEAD/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bojie-liu/react-native-quickjs/HEAD/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bojie-liu/react-native-quickjs/HEAD/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /example/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @format 3 | */ 4 | 5 | import { AppRegistry } from 'react-native'; 6 | import App from './src/App'; 7 | import { name as appName } from './app.json'; 8 | 9 | AppRegistry.registerComponent(appName, () => App); -------------------------------------------------------------------------------- /example/react-native.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const pak = require('../package.json'); 3 | 4 | module.exports = { 5 | dependencies: { 6 | [pak.name]: { 7 | root: path.join(__dirname, '..'), 8 | }, 9 | }, 10 | }; 11 | -------------------------------------------------------------------------------- /example/android/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.5.1-all.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /ios/Quickjs.h: -------------------------------------------------------------------------------- 1 | #ifdef RCT_NEW_ARCH_ENABLED 2 | #import "RNQuickjsSpec.h" 3 | 4 | @interface Quickjs : NSObject 5 | #else 6 | #import 7 | 8 | @interface Quickjs : NSObject 9 | #endif 10 | 11 | @end 12 | -------------------------------------------------------------------------------- /example/ios/QuickjsExample/main.m: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | #import "AppDelegate.h" 4 | 5 | int main(int argc, char *argv[]) 6 | { 7 | @autoreleasepool { 8 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /cpp/QuickJSRuntimeFactory.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | 7 | namespace jsi = facebook::jsi; 8 | 9 | namespace qjs { 10 | 11 | std::unique_ptr createQuickJSRuntime(const std::string &codeCacheDir); 12 | 13 | } // namespace qjs 14 | -------------------------------------------------------------------------------- /example/android/settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = 'QuickjsExample' 2 | apply from: file("../node_modules/@react-native-community/cli-platform-android/native_modules.gradle"); applyNativeModulesSettingsGradle(settings) 3 | include ':app' 4 | includeBuild('../node_modules/react-native-gradle-plugin') 5 | -------------------------------------------------------------------------------- /example/ios/QuickjsExample.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /example/ios/QuickjsExample.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /example/ios/TTIModule.h: -------------------------------------------------------------------------------- 1 | // 2 | // TTIModule.h 3 | // QuickjsExample 4 | // 5 | // Created by liubojie on 2023/6/9. 6 | // 7 | 8 | #ifndef TTIModule_h 9 | #define TTIModule_h 10 | 11 | #import 12 | 13 | @interface TTIModule : NSObject 14 | 15 | @end 16 | 17 | #endif /* TTIModule_h */ 18 | -------------------------------------------------------------------------------- /cpp/QuickJSRuntimeFactory.cpp: -------------------------------------------------------------------------------- 1 | #include "QuickJSRuntimeFactory.h" 2 | 3 | #include "QuickJSRuntime.h" 4 | #include 5 | 6 | namespace qjs { 7 | 8 | std::unique_ptr createQuickJSRuntime(const std::string &codeCacheDir) { 9 | return std::make_unique(codeCacheDir); 10 | } 11 | 12 | } // namespace qjs 13 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig helps developers define and maintain consistent 2 | # coding styles between different editors and IDEs 3 | # editorconfig.org 4 | 5 | root = true 6 | 7 | [*] 8 | 9 | indent_style = space 10 | indent_size = 2 11 | 12 | end_of_line = lf 13 | charset = utf-8 14 | trim_trailing_whitespace = true 15 | insert_final_newline = true 16 | -------------------------------------------------------------------------------- /example/android/app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /lefthook.yml: -------------------------------------------------------------------------------- 1 | pre-commit: 2 | parallel: true 3 | commands: 4 | lint: 5 | files: git diff --name-only @{push} 6 | glob: "*.{js,ts,jsx,tsx}" 7 | run: npx eslint {files} 8 | types: 9 | files: git diff --name-only @{push} 10 | glob: "*.{js,ts, jsx, tsx}" 11 | run: npx tsc --noEmit 12 | commit-msg: 13 | parallel: true 14 | commands: 15 | commitlint: 16 | run: npx commitlint --edit 17 | -------------------------------------------------------------------------------- /example/babel.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const pak = require('../package.json'); 3 | 4 | module.exports = { 5 | presets: ['module:metro-react-native-babel-preset'], 6 | plugins: [ 7 | [ 8 | 'module-resolver', 9 | { 10 | extensions: ['.tsx', '.ts', '.js', '.json'], 11 | alias: { 12 | [pak.name]: path.join(__dirname, '..', pak.source), 13 | }, 14 | }, 15 | ], 16 | ], 17 | }; 18 | -------------------------------------------------------------------------------- /example/src/App.tsx: -------------------------------------------------------------------------------- 1 | /** 2 | * Sample React Native App 3 | * https://github.com/facebook/react-native 4 | * 5 | * @format 6 | * @flow 7 | */ 8 | 9 | import React from 'react'; 10 | import { SafeAreaView } from 'react-native'; 11 | import SearchableFlatList from './SearchableList'; 12 | 13 | const App = () => ( 14 | 15 | 16 | 17 | ); 18 | 19 | export default App; -------------------------------------------------------------------------------- /addr.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # For app 4 | #/Users/liubojie/env/android-ndk-r19c/toolchains/aarch64-linux-android-4.9/prebuilt/darwin-x86_64/bin/aarch64-linux-android-addr2line -C -f -e ./ReactAndroid/build/react-ndk/exported/arm64-v8a/libreactnativejni.so $@ 5 | 6 | # For RNTester 7 | /Users/liubojie/env/android-ndk-r19c/toolchains/aarch64-linux-android-4.9/prebuilt/darwin-x86_64/bin/aarch64-linux-android-addr2line -C -f -e android/build/intermediates/cmake/debug/obj/arm64-v8a/libquickjsexecutor.so $@ 8 | 9 | -------------------------------------------------------------------------------- /example/android/app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in /usr/local/Cellar/android-sdk/24.3.3/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | -------------------------------------------------------------------------------- /example/ios/.xcode.env: -------------------------------------------------------------------------------- 1 | # This `.xcode.env` file is versioned and is used to source the environment 2 | # used when running script phases inside Xcode. 3 | # To customize your local environment, you can create an `.xcode.env.local` 4 | # file that is not versioned. 5 | 6 | # NODE_BINARY variable contains the PATH to the node executable. 7 | # 8 | # Customize the NODE_BINARY variable here. 9 | # For example, to use nvm with brew, add the following line 10 | # . "$(brew --prefix nvm)/nvm.sh" --no-use 11 | export NODE_BINARY=$(command -v node) 12 | -------------------------------------------------------------------------------- /example/android/app/src/main/java/com/quickjsexample/TTIRecorder.java: -------------------------------------------------------------------------------- 1 | //*** licence placeholder ***// 2 | 3 | package com.quickjsexample; 4 | 5 | import com.facebook.common.logging.FLog; 6 | 7 | class TTIRecorder { 8 | static public long mOnCreateTimestamp; 9 | static public long mTTI; 10 | static public long mComponentDidMount; 11 | 12 | static public void printTTI() { 13 | if (mTTI != 0 && mComponentDidMount != 0) { 14 | FLog.e("TTIRecorder", "TTI: " + mTTI + " : " + "ComponentDidMount: " + mComponentDidMount); 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /cpp/QuickJSPointerValue.cpp: -------------------------------------------------------------------------------- 1 | #include "QuickJSPointerValue.h" 2 | 3 | namespace qjs { 4 | 5 | QuickJSPointerValue::QuickJSPointerValue(JSRuntime *runtime, JSContext *context, JSValue value) 6 | : runtime_(runtime), value_(JS_DupValue(context, value)) { 7 | } 8 | 9 | QuickJSPointerValue::~QuickJSPointerValue() { 10 | JS_FreeValueRT(runtime_, value_); 11 | } 12 | 13 | JSValue QuickJSPointerValue::Get(JSContext *context) const { 14 | return JS_DupValue(context, value_); 15 | } 16 | 17 | void QuickJSPointerValue::invalidate() { 18 | delete this; 19 | } 20 | 21 | } // namespace qjs 22 | -------------------------------------------------------------------------------- /example/android/app/src/debug/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /cpp/QuickJSPointerValue.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "QuickJSRuntime.h" 4 | #include "jsi/jsi.h" 5 | 6 | namespace qjs { 7 | 8 | class QuickJSPointerValue final : public QuickJSRuntime::PointerValue { 9 | public: 10 | QuickJSPointerValue(JSRuntime *runtime, JSContext *context, JSValue value); 11 | ~QuickJSPointerValue(); 12 | 13 | JSValue Get(JSContext *context) const; 14 | 15 | private: 16 | void invalidate() override; 17 | 18 | private: 19 | friend class JSIValueConverter; 20 | friend class ScopedJSValue; 21 | friend class QuickJSRuntime; 22 | 23 | JSRuntime *runtime_; 24 | JSValue value_; 25 | }; 26 | 27 | } // namespace qjs 28 | -------------------------------------------------------------------------------- /example/android/build.gradle: -------------------------------------------------------------------------------- 1 | // Top-level build file where you can add configuration options common to all sub-projects/modules. 2 | 3 | buildscript { 4 | ext { 5 | buildToolsVersion = "33.0.0" 6 | minSdkVersion = 21 7 | compileSdkVersion = 33 8 | targetSdkVersion = 33 9 | 10 | // We use NDK 23 which has both M1 support and is the side-by-side NDK version from AGP. 11 | ndkVersion = "23.1.7779620" 12 | } 13 | repositories { 14 | google() 15 | mavenCentral() 16 | } 17 | dependencies { 18 | classpath("com.android.tools.build:gradle:7.3.1") 19 | classpath("com.facebook.react:react-native-gradle-plugin") 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/index.tsx: -------------------------------------------------------------------------------- 1 | import { NativeModules, Platform } from 'react-native'; 2 | 3 | const LINKING_ERROR = 4 | `The package 'react-native-quickjs' doesn't seem to be linked. Make sure: \n\n` + 5 | Platform.select({ ios: "- You have run 'pod install'\n", default: '' }) + 6 | '- You rebuilt the app after installing the package\n' + 7 | '- You are not using Expo Go\n'; 8 | 9 | const Quickjs = NativeModules.Quickjs 10 | ? NativeModules.Quickjs 11 | : new Proxy( 12 | {}, 13 | { 14 | get() { 15 | throw new Error(LINKING_ERROR); 16 | }, 17 | } 18 | ); 19 | 20 | export function multiply(a: number, b: number): Promise { 21 | return Quickjs.multiply(a, b); 22 | } 23 | -------------------------------------------------------------------------------- /.github/actions/setup/action.yml: -------------------------------------------------------------------------------- 1 | name: Setup 2 | description: Setup Node.js and install dependencies 3 | 4 | runs: 5 | using: composite 6 | steps: 7 | - name: Setup Node.js 8 | uses: actions/setup-node@v3 9 | with: 10 | node-version-file: .nvmrc 11 | 12 | - name: Cache dependencies 13 | id: yarn-cache 14 | uses: actions/cache@v3 15 | with: 16 | path: | 17 | **/node_modules 18 | key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }} 19 | restore-keys: | 20 | ${{ runner.os }}-yarn- 21 | 22 | - name: Install dependencies 23 | if: steps.yarn-cache.outputs.cache-hit != 'true' 24 | run: | 25 | yarn install --cwd example --frozen-lockfile 26 | yarn install --frozen-lockfile 27 | shell: bash 28 | -------------------------------------------------------------------------------- /example/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "QuickjsExample", 3 | "version": "0.0.1", 4 | "private": true, 5 | "scripts": { 6 | "android": "react-native run-android", 7 | "ios": "react-native run-ios", 8 | "start": "react-native start", 9 | "pods": "pod-install --quiet" 10 | }, 11 | "dependencies": { 12 | "react": "18.2.0", 13 | "react-native": "0.71.8", 14 | "react-native-elements": "^3.4.3", 15 | "react-native-safe-area-context": "^4.5.3", 16 | "react-native-vector-icons": "^9.2.0" 17 | }, 18 | "devDependencies": { 19 | "@babel/core": "^7.20.0", 20 | "@babel/preset-env": "^7.20.0", 21 | "@babel/runtime": "^7.20.0", 22 | "babel-plugin-module-resolver": "^4.1.0", 23 | "ios-deploy": "^1.12.2", 24 | "metro-react-native-babel-preset": "0.73.9" 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /android/src/main/java/com/quickjs/QuickjsPackage.java: -------------------------------------------------------------------------------- 1 | package com.quickjs; 2 | 3 | import androidx.annotation.NonNull; 4 | 5 | import com.facebook.react.ReactPackage; 6 | import com.facebook.react.bridge.NativeModule; 7 | import com.facebook.react.bridge.ReactApplicationContext; 8 | import com.facebook.react.uimanager.ViewManager; 9 | 10 | import java.util.Collections; 11 | import java.util.List; 12 | 13 | public class QuickjsPackage implements ReactPackage { 14 | @NonNull 15 | @Override 16 | public List createNativeModules(@NonNull ReactApplicationContext reactContext) { 17 | return Collections.emptyList(); 18 | } 19 | 20 | @NonNull 21 | @Override 22 | public List createViewManagers(@NonNull ReactApplicationContext reactContext) { 23 | return Collections.emptyList(); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /example/android/app/src/release/java/com/quickjsexample/ReactNativeFlipper.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * 4 | *

This source code is licensed under the MIT license found in the LICENSE file in the root 5 | * directory of this source tree. 6 | */ 7 | package com.quickjsexample; 8 | 9 | import android.content.Context; 10 | import com.facebook.react.ReactInstanceManager; 11 | 12 | /** 13 | * Class responsible of loading Flipper inside your React Native application. This is the release 14 | * flavor of it so it's empty as we don't want to load Flipper. 15 | */ 16 | public class ReactNativeFlipper { 17 | public static void initializeFlipper(Context context, ReactInstanceManager reactInstanceManager) { 18 | // Do nothing as we don't want to initialize Flipper on Release. 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "baseUrl": "./", 4 | "paths": { 5 | "react-native-quickjs": ["./src/index"] 6 | }, 7 | "allowUnreachableCode": false, 8 | "allowUnusedLabels": false, 9 | "esModuleInterop": true, 10 | "importsNotUsedAsValues": "error", 11 | "forceConsistentCasingInFileNames": true, 12 | "jsx": "react", 13 | "lib": ["esnext"], 14 | "module": "esnext", 15 | "moduleResolution": "node", 16 | "noFallthroughCasesInSwitch": true, 17 | "noImplicitReturns": true, 18 | "noImplicitUseStrict": false, 19 | "noStrictGenericChecks": false, 20 | "noUncheckedIndexedAccess": true, 21 | "noUnusedLocals": true, 22 | "noUnusedParameters": true, 23 | "resolveJsonModule": true, 24 | "skipLibCheck": true, 25 | "strict": true, 26 | "target": "esnext" 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /example/ios/QuickjsExampleTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | 24 | 25 | -------------------------------------------------------------------------------- /ios/Quickjs.mm: -------------------------------------------------------------------------------- 1 | #import "Quickjs.h" 2 | 3 | @implementation Quickjs 4 | RCT_EXPORT_MODULE() 5 | 6 | // Example method 7 | // See // https://reactnative.dev/docs/native-modules-ios 8 | RCT_REMAP_METHOD(multiply, 9 | multiplyWithA:(double)a withB:(double)b 10 | withResolver:(RCTPromiseResolveBlock)resolve 11 | withRejecter:(RCTPromiseRejectBlock)reject) 12 | { 13 | // NSNumber *result = @(quickjs::multiply(a, b)); 14 | // 15 | // resolve(result); 16 | } 17 | 18 | // Don't compile this code when we build for the old architecture. 19 | #ifdef RCT_NEW_ARCH_ENABLED 20 | - (std::shared_ptr)getTurboModule: 21 | (const facebook::react::ObjCTurboModule::InitParams &)params 22 | { 23 | return std::make_shared(params); 24 | } 25 | #endif 26 | 27 | @end 28 | -------------------------------------------------------------------------------- /android/src/main/java/com/quickjs/QuickJSExecutor.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) Facebook, Inc. and its affiliates. 3 | *

4 | * This source code is licensed under the MIT license found in the LICENSE 5 | * file in the root directory of this source tree. 6 | */ 7 | package com.quickjs; 8 | 9 | import com.facebook.jni.HybridData; 10 | import com.facebook.react.bridge.JavaScriptExecutor; 11 | import com.facebook.soloader.SoLoader; 12 | 13 | public class QuickJSExecutor extends JavaScriptExecutor { 14 | 15 | static { 16 | SoLoader.loadLibrary("quickjsexecutor"); 17 | } 18 | 19 | QuickJSExecutor(final String codeCacheDir) { 20 | super(initHybrid(codeCacheDir)); 21 | } 22 | 23 | @Override 24 | public String getName() { 25 | return "QuickJSExecutor"; 26 | } 27 | 28 | private static native HybridData initHybrid(final String codeCacheDir); 29 | } 30 | -------------------------------------------------------------------------------- /scripts/bootstrap.js: -------------------------------------------------------------------------------- 1 | const os = require('os'); 2 | const path = require('path'); 3 | const child_process = require('child_process'); 4 | 5 | const root = path.resolve(__dirname, '..'); 6 | const args = process.argv.slice(2); 7 | const options = { 8 | cwd: process.cwd(), 9 | env: process.env, 10 | stdio: 'inherit', 11 | encoding: 'utf-8', 12 | }; 13 | 14 | if (os.type() === 'Windows_NT') { 15 | options.shell = true; 16 | } 17 | 18 | let result; 19 | 20 | if (process.cwd() !== root || args.length) { 21 | // We're not in the root of the project, or additional arguments were passed 22 | // In this case, forward the command to `yarn` 23 | result = child_process.spawnSync('yarn', args, options); 24 | } else { 25 | // If `yarn` is run without arguments, perform bootstrap 26 | result = child_process.spawnSync('yarn', ['bootstrap'], options); 27 | } 28 | 29 | process.exitCode = result.status; 30 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # OSX 2 | # 3 | .DS_Store 4 | 5 | # XDE 6 | .expo/ 7 | 8 | # VSCode 9 | .vscode/ 10 | jsconfig.json 11 | 12 | # Xcode 13 | # 14 | build/ 15 | *.pbxuser 16 | !default.pbxuser 17 | *.mode1v3 18 | !default.mode1v3 19 | *.mode2v3 20 | !default.mode2v3 21 | *.perspectivev3 22 | !default.perspectivev3 23 | xcuserdata 24 | *.xccheckout 25 | *.moved-aside 26 | DerivedData 27 | *.hmap 28 | *.ipa 29 | *.xcuserstate 30 | project.xcworkspace 31 | 32 | # Android/IJ 33 | # 34 | .classpath 35 | .cxx 36 | .gradle 37 | .idea 38 | .project 39 | .settings 40 | local.properties 41 | android.iml 42 | 43 | # Cocoapods 44 | # 45 | example/ios/Pods 46 | 47 | # Ruby 48 | example/vendor/ 49 | 50 | # node.js 51 | # 52 | node_modules/ 53 | npm-debug.log 54 | yarn-debug.log 55 | yarn-error.log 56 | 57 | # BUCK 58 | buck-out/ 59 | \.buckd/ 60 | android/app/libs 61 | android/keystores/debug.keystore 62 | 63 | # Expo 64 | .expo/ 65 | 66 | # Turborepo 67 | .turbo/ 68 | 69 | # generated by bob 70 | lib/ 71 | -------------------------------------------------------------------------------- /cpp/QuickJSInstrumentation.cpp: -------------------------------------------------------------------------------- 1 | #include "QuickJSInstrumentation.h" 2 | 3 | #include "QuickJSRuntime.h" 4 | namespace qjs { 5 | QuickJSInstrumentation::QuickJSInstrumentation(QuickJSRuntime *runtime) : runtime_(runtime) {} 6 | 7 | std::string QuickJSInstrumentation::getRecordedGCStats() { 8 | return ""; 9 | } 10 | 11 | std::unordered_map QuickJSInstrumentation::getHeapInfo(bool) { 12 | if (runtime_) { 13 | return runtime_->getHeapInfo(); 14 | } else { 15 | return {}; 16 | } 17 | } 18 | 19 | void QuickJSInstrumentation::collectGarbage(std::string cause) { 20 | JS_RunGC(runtime_->getJSRuntime()); 21 | } 22 | 23 | void QuickJSInstrumentation::createSnapshotToFile(const std::string &) { 24 | } 25 | 26 | void QuickJSInstrumentation::createSnapshotToStream(std::ostream &) { 27 | } 28 | 29 | void QuickJSInstrumentation::writeBasicBlockProfileTraceToFile( 30 | const std::string &) const { 31 | } 32 | 33 | void QuickJSInstrumentation::dumpProfilerSymbolsToFile(const std::string &) const { 34 | } 35 | } // namespace qjs 36 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | on: 3 | push: 4 | branches: 5 | - main 6 | pull_request: 7 | branches: 8 | - main 9 | 10 | jobs: 11 | lint: 12 | runs-on: ubuntu-latest 13 | steps: 14 | - name: Checkout 15 | uses: actions/checkout@v3 16 | 17 | - name: Setup 18 | uses: ./.github/actions/setup 19 | 20 | - name: Lint files 21 | run: yarn lint 22 | 23 | - name: Typecheck files 24 | run: yarn typecheck 25 | 26 | test: 27 | runs-on: ubuntu-latest 28 | steps: 29 | - name: Checkout 30 | uses: actions/checkout@v3 31 | 32 | - name: Setup 33 | uses: ./.github/actions/setup 34 | 35 | - name: Run unit tests 36 | run: yarn test --maxWorkers=2 --coverage 37 | 38 | build: 39 | runs-on: ubuntu-latest 40 | steps: 41 | - name: Checkout 42 | uses: actions/checkout@v3 43 | 44 | - name: Setup 45 | uses: ./.github/actions/setup 46 | 47 | - name: Build package 48 | run: yarn prepack 49 | -------------------------------------------------------------------------------- /example/android/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 12 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 liubojie 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in all 12 | copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | SOFTWARE. 21 | -------------------------------------------------------------------------------- /example/ios/QuickjsExample/Images.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "scale" : "2x", 6 | "size" : "20x20" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "scale" : "3x", 11 | "size" : "20x20" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "scale" : "2x", 16 | "size" : "29x29" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "scale" : "3x", 21 | "size" : "29x29" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "scale" : "2x", 26 | "size" : "40x40" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "scale" : "3x", 31 | "size" : "40x40" 32 | }, 33 | { 34 | "idiom" : "iphone", 35 | "scale" : "2x", 36 | "size" : "60x60" 37 | }, 38 | { 39 | "idiom" : "iphone", 40 | "scale" : "3x", 41 | "size" : "60x60" 42 | }, 43 | { 44 | "idiom" : "ios-marketing", 45 | "scale" : "1x", 46 | "size" : "1024x1024" 47 | } 48 | ], 49 | "info" : { 50 | "author" : "xcode", 51 | "version" : 1 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /ios/QuickJSExecutorFactory.h: -------------------------------------------------------------------------------- 1 | // 2 | // QuickJSExecutorFactory.h 3 | // Pods 4 | // 5 | // Created by liubojie on 2023/5/29. 6 | // 7 | 8 | #ifndef QuickJSExecutorFactory_h 9 | #define QuickJSExecutorFactory_h 10 | 11 | #include 12 | 13 | namespace qjs { 14 | 15 | class QuickJSExecutorFactory : public facebook::react::JSExecutorFactory { 16 | public: 17 | explicit QuickJSExecutorFactory( 18 | facebook::react::JSIExecutor::RuntimeInstaller runtimeInstaller, const std::string &codeCacheDir) 19 | : runtimeInstaller_(std::move(runtimeInstaller)), codeCacheDir_(codeCacheDir) {} 20 | 21 | std::unique_ptr createJSExecutor( 22 | std::shared_ptr delegate, 23 | std::shared_ptr jsQueue) override; 24 | 25 | private: 26 | facebook::react::JSIExecutor::RuntimeInstaller runtimeInstaller_; 27 | std::string codeCacheDir_; 28 | }; 29 | } 30 | 31 | #endif /* QuickJSExecutorFactory_h */ 32 | -------------------------------------------------------------------------------- /example/metro.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const escape = require('escape-string-regexp'); 3 | const exclusionList = require('metro-config/src/defaults/exclusionList'); 4 | const pak = require('../package.json'); 5 | 6 | const root = path.resolve(__dirname, '..'); 7 | 8 | const modules = Object.keys({ 9 | ...pak.peerDependencies, 10 | }); 11 | 12 | module.exports = { 13 | projectRoot: __dirname, 14 | watchFolders: [root], 15 | 16 | // We need to make sure that only one version is loaded for peerDependencies 17 | // So we block them at the root, and alias them to the versions in example's node_modules 18 | resolver: { 19 | blacklistRE: exclusionList( 20 | modules.map( 21 | (m) => 22 | new RegExp(`^${escape(path.join(root, 'node_modules', m))}\\/.*$`) 23 | ) 24 | ), 25 | 26 | extraNodeModules: modules.reduce((acc, name) => { 27 | acc[name] = path.join(__dirname, 'node_modules', name); 28 | return acc; 29 | }, {}), 30 | }, 31 | 32 | transformer: { 33 | getTransformOptions: async () => ({ 34 | transform: { 35 | experimentalImportSupport: false, 36 | inlineRequires: true, 37 | }, 38 | }), 39 | }, 40 | }; 41 | -------------------------------------------------------------------------------- /android/src/main/java/com/quickjs/QuickJSExecutorFactory.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) Facebook, Inc. and its affiliates. 3 | *

4 | * This source code is licensed under the MIT license found in the LICENSE file in the root 5 | * directory of this source tree. 6 | */ 7 | package com.quickjs; 8 | 9 | import java.io.File; 10 | 11 | import com.facebook.common.file.FileUtils; 12 | import com.facebook.react.bridge.JavaScriptExecutor; 13 | import com.facebook.react.bridge.JavaScriptExecutorFactory; 14 | 15 | public class QuickJSExecutorFactory implements JavaScriptExecutorFactory { 16 | 17 | private static final String TAG = "QuickJS"; 18 | private String mCodeCacheDir; 19 | 20 | public QuickJSExecutorFactory(final String codeCacheDir) { 21 | mCodeCacheDir = codeCacheDir; 22 | } 23 | 24 | @Override 25 | public JavaScriptExecutor create() { 26 | try { 27 | FileUtils.mkdirs(new File(mCodeCacheDir)); 28 | } catch (Exception e) { 29 | e.printStackTrace(); 30 | // Empty string to avoid using code cache. 31 | mCodeCacheDir = ""; 32 | } 33 | return new QuickJSExecutor(mCodeCacheDir); 34 | } 35 | 36 | @Override 37 | public void startSamplingProfiler() { 38 | } 39 | 40 | @Override 41 | public void stopSamplingProfiler(String filename) { 42 | } 43 | 44 | @Override 45 | public String toString() { 46 | return "QuickJSExecutor+QuickJSRuntime"; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /cpp/QuickJSInstrumentation.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace jsi = facebook::jsi; 6 | 7 | namespace qjs { 8 | 9 | class QuickJSRuntime; 10 | 11 | class QuickJSInstrumentation : public jsi::Instrumentation { 12 | public: 13 | QuickJSInstrumentation(QuickJSRuntime *runtime); 14 | 15 | std::string getRecordedGCStats() override; 16 | 17 | std::unordered_map getHeapInfo(bool) override; 18 | 19 | void collectGarbage(std::string cause) override; 20 | 21 | void createSnapshotToFile(const std::string &) override; 22 | 23 | void createSnapshotToStream(std::ostream &) override; 24 | 25 | void writeBasicBlockProfileTraceToFile(const std::string &) const override; 26 | 27 | void dumpProfilerSymbolsToFile(const std::string &) const override; 28 | 29 | void startTrackingHeapObjectStackTraces( 30 | std::function stats)> fragmentCallback) override {}; 34 | 35 | void stopTrackingHeapObjectStackTraces() override {}; 36 | 37 | void startHeapSampling(size_t samplingInterval) override {}; 38 | 39 | void stopHeapSampling(std::ostream &os) override {}; 40 | 41 | std::string flushAndDisableBridgeTrafficTrace() override { return ""; }; 42 | 43 | private: 44 | QuickJSRuntime *runtime_; 45 | }; 46 | 47 | } // namespace qjs 48 | -------------------------------------------------------------------------------- /cpp/ScopedJSValue.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "QuickJSRuntime.h" 4 | #include "QuickJSPointerValue.h" 5 | 6 | namespace qjs { 7 | 8 | class ScopedJSValue { 9 | public: 10 | explicit ScopedJSValue(JSContext *context, JSValue *value) 11 | : context_(context), value_(value) { 12 | assert(value_ != nullptr); 13 | }; 14 | 15 | ~ScopedJSValue() { 16 | assert(value_ != nullptr); 17 | JS_FreeValue(context_, *value_); 18 | } 19 | 20 | // Prevent copying of Scope objects. 21 | ScopedJSValue(const ScopedJSValue &) = delete; 22 | ScopedJSValue &operator=(const ScopedJSValue &) = delete; 23 | 24 | JSValue get() const { 25 | return *value_; 26 | }; 27 | 28 | private: 29 | JSContext *context_; 30 | JSValue *value_; 31 | }; 32 | 33 | class ScopedCString { 34 | public: 35 | explicit ScopedCString(JSContext *context, const char *cstring) 36 | : context_(context), cstring_(cstring) { 37 | assert(cstring_ != nullptr); 38 | }; 39 | 40 | ~ScopedCString() { 41 | assert(cstring_ != nullptr); 42 | JS_FreeCString(context_, cstring_); 43 | } 44 | 45 | // Prevent copying of Scope objects. 46 | ScopedCString(const ScopedCString &) = delete; 47 | ScopedCString &operator=(const ScopedCString &) = delete; 48 | 49 | const char *get() const { 50 | return cstring_; 51 | }; 52 | 53 | private: 54 | JSContext *context_; 55 | const char *cstring_; 56 | }; 57 | 58 | } // namespace qjs 59 | -------------------------------------------------------------------------------- /android/src/main/jni/QuickJSExecutorFactory.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace jsi = facebook::jsi; 6 | namespace react = facebook::react; 7 | 8 | namespace qjs { 9 | 10 | class QuickJSExecutorFactory : public react::JSExecutorFactory { 11 | public: 12 | explicit QuickJSExecutorFactory( 13 | react::JSIExecutor::RuntimeInstaller runtimeInstaller, 14 | const react::JSIScopedTimeoutInvoker &timeoutInvoker, 15 | const std::string &codeCacheDir) 16 | : runtimeInstaller_(runtimeInstaller), 17 | timeoutInvoker_(timeoutInvoker), 18 | codeCacheDir_(codeCacheDir) {} 19 | 20 | std::unique_ptr createJSExecutor( 21 | std::shared_ptr delegate, 22 | std::shared_ptr jsQueue) override; 23 | 24 | private: 25 | react::JSIExecutor::RuntimeInstaller runtimeInstaller_; 26 | react::JSIScopedTimeoutInvoker timeoutInvoker_; 27 | std::string codeCacheDir_; 28 | }; 29 | 30 | class QuickJSExecutor : public react::JSIExecutor { 31 | public: 32 | QuickJSExecutor( 33 | std::shared_ptr runtime, 34 | std::shared_ptr delegate, 35 | std::shared_ptr jsQueue, 36 | const react::JSIScopedTimeoutInvoker &timeoutInvoker, 37 | RuntimeInstaller runtimeInstaller); 38 | 39 | private: 40 | react::JSIScopedTimeoutInvoker timeoutInvoker_; 41 | }; 42 | 43 | } // namespace qjs 44 | -------------------------------------------------------------------------------- /example/ios/TTIModule.m: -------------------------------------------------------------------------------- 1 | // 2 | // TTIModule.m 3 | // QuickjsExample 4 | // 5 | // Created by liubojie on 2023/6/9. 6 | // 7 | 8 | #import "TTIModule.h" 9 | 10 | #import 11 | 12 | NSTimeInterval tti; 13 | 14 | NSTimeInterval onCreateTimestamp; 15 | 16 | NSTimeInterval componentDidMount; 17 | 18 | @implementation TTIModule 19 | RCT_EXPORT_MODULE() 20 | 21 | // Example method 22 | // See // https://reactnative.dev/docs/native-modules-ios 23 | RCT_REMAP_METHOD(componentDidMount, 24 | componentDidMount:(double)timestamp) 25 | { 26 | if (componentDidMount == 0) { 27 | componentDidMount = [[NSDate date] timeIntervalSince1970] - onCreateTimestamp; 28 | [self printTTI]; 29 | } 30 | } 31 | 32 | - (instancetype)init { 33 | if (self = [super init]) { 34 | [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(contentDidAppear:) name:RCTContentDidAppearNotification object:nil]; 35 | } 36 | return self; 37 | } 38 | 39 | - (void)dealloc { 40 | [[NSNotificationCenter defaultCenter] removeObserver:self 41 | name:RCTContentDidAppearNotification 42 | object:nil]; 43 | } 44 | 45 | - (void)printTTI { 46 | if (componentDidMount != 0 && tti != 0) { 47 | printf("TTI: %f ComponentDidMount: %f", tti * 1000, componentDidMount * 1000); 48 | } 49 | } 50 | 51 | #pragma mark - Notification 52 | 53 | - (void)contentDidAppear:(NSNotification *)notification { 54 | if (!notification) { 55 | return; 56 | } 57 | tti = [[NSDate date] timeIntervalSince1970] - onCreateTimestamp; 58 | [self printTTI]; 59 | } 60 | 61 | @end 62 | 63 | -------------------------------------------------------------------------------- /cpp/JSIValueConverter.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "QuickJSRuntime.h" 4 | #include "jsi/jsi.h" 5 | 6 | namespace qjs { 7 | 8 | class JSIValueConverter { 9 | private: 10 | JSIValueConverter() = delete; 11 | ~JSIValueConverter() = delete; 12 | JSIValueConverter(JSIValueConverter &&) = delete; 13 | 14 | public: 15 | static jsi::Value ToJSIValue( 16 | const QuickJSRuntime &runtime, 17 | const JSValueConst &value); 18 | 19 | static JSValue ToJSValue( 20 | const QuickJSRuntime &runtime, 21 | const jsi::Value &value); 22 | 23 | static JSValue ToJSString( 24 | const QuickJSRuntime &runtime, 25 | const jsi::String &string); 26 | 27 | static JSValue ToJSString( 28 | const QuickJSRuntime &runtime, 29 | const jsi::PropNameID &propName); 30 | 31 | static JSValue ToJSSymbol( 32 | const QuickJSRuntime &runtime, 33 | const jsi::Symbol &symbol); 34 | 35 | static JSValue ToJSObject( 36 | const QuickJSRuntime &runtime, 37 | const jsi::Object &object); 38 | 39 | static JSValue ToJSBigInt( 40 | const QuickJSRuntime &runtime, 41 | const jsi::BigInt &bigInt); 42 | 43 | static JSValue ToJSArray( 44 | const QuickJSRuntime &runtime, 45 | const jsi::Array &array); 46 | 47 | static JSValue ToJSFunction( 48 | const QuickJSRuntime &runtime, 49 | const jsi::Function &function); 50 | 51 | static jsi::PropNameID ToJSIPropNameID( 52 | const QuickJSRuntime &runtime, 53 | const JSAtom &property); 54 | 55 | static std::string ToSTLString(JSContext *ctx, JSAtom atom); 56 | 57 | static std::string ToSTLString( 58 | JSContext *context, 59 | JSValueConst &string); 60 | }; 61 | 62 | } // namespace qjs 63 | -------------------------------------------------------------------------------- /docs/DemoPerformance.md: -------------------------------------------------------------------------------- 1 | ## Benchmark Results 2 | 3 | Android 11 OnePlus 7t, iOS 13.3 iPhone 11 Pro. 4 | 5 | ![TTI & Memory](./png/ExampleTTI&Memory.png) 6 | ![SO Size](./png/AndroidSOSize.png) 7 | 8 | ## Benchmark 9 | 1. Speed 10 | 1. TTI (Time-To-Interaction). On Android, it measures from "Activity.onCreate" to "CONTENT_APPEARED". On iOS, it measures from "AppDelegate.initialize" to "contentDidAppear". 11 | 2. ComponentDidMount. On Android, it measures from native "Activity.onCreate" to JS "FlatListDemo.ComponentDidMount". On iOS, it measures from native "AppDelegate.initialize" to JS "FlatListDemo.ComponentDidMount". 12 | 13 | 2. Memory 14 | 1. We sampled PSS on Android and footprint memory on iOS after QuickJSExample had finished loading for about 5s. 15 | 16 | ## How to Run it on your device 17 | 18 | 1. Install QuickJSExample in this project according to README.md in release mode. For iOS it needs to set the "Optimization level" to "Fastest(-O3)" but not "Fastest, Smallest(-Os)" in project's "Build Settings" in XCode to enable console log. 19 | 2. Just launch the app. 20 | 3. On Android it prints like: 21 | ```java 22 | FLog.e("TTIRecorder", "TTI: " + mTTI + " : " + "ComponentDidMount: " + mComponentDidMount); 23 | ``` 24 | 4. On iOS it prints like: 25 | ```c 26 | printf("TTI: %f ComponentDidMount: %f", tti * 1000, componentDidMount * 1000); 27 | ``` 28 | 29 | ## Implementation details 30 | 31 | 1. For convenience, QuickJS's code cache is generated and saved on device. There is writing costs for the first launch and reading costs afterward. Hermes precompiles bundle into byte codes as an asset in application. 32 | 2. The compile flags for QuickJS include "-O2". 33 | 3. The example contains a simple flatlist with 20 items. 34 | -------------------------------------------------------------------------------- /android/src/main/jni/OnLoad.cpp: -------------------------------------------------------------------------------- 1 | #include "QuickJSExecutorFactory.h" 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | namespace jni = facebook::jni; 12 | namespace jsi = facebook::jsi; 13 | namespace react = facebook::react; 14 | 15 | namespace qjs { 16 | 17 | static void installBindings(jsi::Runtime &runtime) { 18 | react::Logger androidLogger = 19 | static_cast( 20 | &react::reactAndroidLoggingHook); 21 | react::bindNativeLogger(runtime, androidLogger); 22 | } 23 | 24 | class QuickJSExecutorHolder 25 | : public jni::HybridClass { 26 | public: 27 | static constexpr auto kJavaDescriptor = 28 | "Lcom/quickjs/QuickJSExecutor;"; 29 | 30 | static jni::local_ref initHybrid( 31 | jni::alias_ref, const std::string &codeCacheDir) { 32 | react::JReactMarker::setLogPerfMarkerIfNeeded(); 33 | return makeCxxInstance(folly::make_unique( 34 | installBindings, 35 | react::JSIExecutor::defaultTimeoutInvoker, 36 | codeCacheDir)); 37 | } 38 | 39 | static void registerNatives() { 40 | registerHybrid({ 41 | makeNativeMethod("initHybrid", QuickJSExecutorHolder::initHybrid), 42 | }); 43 | } 44 | 45 | private: 46 | friend HybridBase; 47 | using HybridBase::HybridBase; 48 | }; 49 | 50 | } // namespace qjs 51 | 52 | JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved) { 53 | return facebook::jni::initialize( 54 | vm, [] { qjs::QuickJSExecutorHolder::registerNatives(); }); 55 | } 56 | -------------------------------------------------------------------------------- /example/ios/QuickjsExample/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleDisplayName 8 | QuickjsExample 9 | CFBundleExecutable 10 | $(EXECUTABLE_NAME) 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | $(PRODUCT_NAME) 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | $(MARKETING_VERSION) 21 | CFBundleSignature 22 | ???? 23 | CFBundleVersion 24 | $(CURRENT_PROJECT_VERSION) 25 | LSRequiresIPhoneOS 26 | 27 | NSAppTransportSecurity 28 | 29 | NSExceptionDomains 30 | 31 | localhost 32 | 33 | NSExceptionAllowsInsecureHTTPLoads 34 | 35 | 36 | 37 | 38 | NSLocationWhenInUseUsageDescription 39 | 40 | UILaunchStoryboardName 41 | LaunchScreen 42 | UIRequiredDeviceCapabilities 43 | 44 | armv7 45 | 46 | UISupportedInterfaceOrientations 47 | 48 | UIInterfaceOrientationPortrait 49 | UIInterfaceOrientationLandscapeLeft 50 | UIInterfaceOrientationLandscapeRight 51 | 52 | UIViewControllerBasedStatusBarAppearance 53 | 54 | 55 | 56 | -------------------------------------------------------------------------------- /ios/QuickJSExecutorFactory.mm: -------------------------------------------------------------------------------- 1 | // 2 | // QuickJSExecutorFactory.m 3 | // react-native-quickjs 4 | // 5 | // Created by liubojie on 2023/5/29. 6 | // 7 | 8 | #import "QuickJSExecutorFactory.h" 9 | 10 | #import 11 | #import 12 | #import 13 | 14 | #include "jsi/jsi.h" 15 | 16 | namespace jsi = facebook::jsi; 17 | namespace react = facebook::react; 18 | 19 | namespace qjs { 20 | 21 | std::unique_ptr QuickJSExecutorFactory::createJSExecutor( 22 | std::shared_ptr delegate, 23 | std::shared_ptr jsQueue) 24 | { 25 | auto installBindings = [runtimeInstaller = runtimeInstaller_](jsi::Runtime &runtime) { 26 | react::Logger iosLoggingBinder = [](const std::string &message, unsigned int logLevel) { 27 | _RCTLogJavaScriptInternal(static_cast(logLevel), [NSString stringWithUTF8String:message.c_str()]); 28 | }; 29 | react::bindNativeLogger(runtime, iosLoggingBinder); 30 | // Wrap over the original runtimeInstaller 31 | if (runtimeInstaller) { 32 | runtimeInstaller(runtime); 33 | } 34 | }; 35 | 36 | NSError *error; 37 | if (![[NSFileManager defaultManager] createDirectoryAtPath:[NSString stringWithUTF8String:codeCacheDir_.c_str()] 38 | withIntermediateDirectories:YES 39 | attributes:nil 40 | error:&error]) 41 | { 42 | NSLog(@"Create directory error: %@", error); 43 | codeCacheDir_ = ""; 44 | } 45 | return folly::make_unique( 46 | createQuickJSRuntime(codeCacheDir_), 47 | delegate, 48 | react::JSIExecutor::defaultTimeoutInvoker, 49 | std::move(installBindings)); 50 | } 51 | 52 | } // namespace qjs 53 | -------------------------------------------------------------------------------- /android/src/main/jni/QuickJSExecutorFactory.cpp: -------------------------------------------------------------------------------- 1 | #include "QuickJSExecutorFactory.h" 2 | 3 | #include 4 | 5 | #include "QuickJSRuntime.h" 6 | #include "QuickJSRuntimeFactory.h" 7 | #include "cxxreact/MessageQueueThread.h" 8 | #include "cxxreact/SystraceSection.h" 9 | 10 | namespace qjs { 11 | 12 | namespace { 13 | 14 | std::unique_ptr makeQuickJSRuntimeSystraced(std::shared_ptr 15 | delegate, const std::string &codeCacheDir) { 16 | react::SystraceSection s("QuickJSExecutorFactory::makeQuickJSRuntimeSystraced"); 17 | return createQuickJSRuntime(codeCacheDir); 18 | } 19 | 20 | } // namespace 21 | 22 | std::unique_ptr QuickJSExecutorFactory::createJSExecutor( 23 | std::shared_ptr delegate, 24 | std::shared_ptr jsQueue) { 25 | std::unique_ptr quickJSRuntime = makeQuickJSRuntimeSystraced(delegate, codeCacheDir_); 26 | 27 | // Add js engine information to Error.prototype so in error reporting we 28 | // can send this information. 29 | auto errorPrototype = quickJSRuntime->global() 30 | .getPropertyAsObject(*quickJSRuntime, "Error") 31 | .getPropertyAsObject(*quickJSRuntime, "prototype"); 32 | errorPrototype.setProperty(*quickJSRuntime, "jsEngine", "quickjs"); 33 | 34 | return std::make_unique( 35 | std::move(quickJSRuntime), 36 | delegate, 37 | jsQueue, 38 | timeoutInvoker_, 39 | runtimeInstaller_); 40 | } 41 | 42 | QuickJSExecutor::QuickJSExecutor( 43 | std::shared_ptr runtime, 44 | std::shared_ptr delegate, 45 | std::shared_ptr jsQueue, 46 | const react::JSIScopedTimeoutInvoker &timeoutInvoker, 47 | RuntimeInstaller runtimeInstaller) 48 | : JSIExecutor(runtime, delegate, timeoutInvoker, runtimeInstaller) {} 49 | 50 | } // namespace qjs 51 | -------------------------------------------------------------------------------- /react-native-quickjs.podspec: -------------------------------------------------------------------------------- 1 | require "json" 2 | 3 | package = JSON.parse(File.read(File.join(__dir__, "package.json"))) 4 | compiler_flags = '-DFOLLY_NO_CONFIG -DFOLLY_MOBILE=1 -DFOLLY_USE_LIBCPP=1 -Wno-comma -Wno-shorten-64-to-32' 5 | 6 | if ENV['ENABLE_HASH_CHECK'] == '1' then 7 | compiler_flags += " -DENABLE_HASH_CHECK=1" 8 | end 9 | 10 | Pod::Spec.new do |s| 11 | s.name = "react-native-quickjs" 12 | s.version = package["version"] 13 | s.summary = package["description"] 14 | s.homepage = package["homepage"] 15 | s.license = package["license"] 16 | s.authors = package["author"] 17 | 18 | s.platforms = { :ios => "11.0" } 19 | s.source = { :git => "https://github.com/bojie-liu/react-native-quickjs.git", :tag => "#{s.version}" } 20 | 21 | s.source_files = "ios/**/*.{h,m,mm}", "cpp/**/*.{h,cpp,c}" 22 | 23 | s.dependency "React-Core" 24 | s.dependency "RCT-Folly" 25 | s.dependency 'React-cxxreact' 26 | 27 | s.compiler_flags = compiler_flags 28 | s.pod_target_xcconfig = { 29 | "HEADER_SEARCH_PATHS" => "\"$(PODS_ROOT)/boost\"", 30 | "OTHER_CPLUSPLUSFLAGS" => "-DFOLLY_NO_CONFIG -DFOLLY_MOBILE=1 -DFOLLY_USE_LIBCPP=1", 31 | "CLANG_CXX_LANGUAGE_STANDARD" => "c++17" 32 | } 33 | 34 | # Don't install the dependencies when we run `pod install` in the old architecture. 35 | if ENV['RCT_NEW_ARCH_ENABLED'] == '1' then 36 | s.compiler_flags = compiler_flags + " -DRCT_NEW_ARCH_ENABLED=1" 37 | s.pod_target_xcconfig = { 38 | "HEADER_SEARCH_PATHS" => "\"$(PODS_ROOT)/boost\"", 39 | "OTHER_CPLUSPLUSFLAGS" => "-DFOLLY_NO_CONFIG -DFOLLY_MOBILE=1 -DFOLLY_USE_LIBCPP=1", 40 | "CLANG_CXX_LANGUAGE_STANDARD" => "c++17" 41 | } 42 | s.dependency "React-Codegen" 43 | s.dependency "RCT-Folly" 44 | s.dependency "RCTRequired" 45 | s.dependency "RCTTypeSafety" 46 | s.dependency "ReactCommon/turbomodule/core" 47 | s.dependency 'React-cxxreact' 48 | end 49 | end 50 | -------------------------------------------------------------------------------- /example/android/app/src/main/java/com/quickjsexample/TTIPackage.java: -------------------------------------------------------------------------------- 1 | //*** licence placeholder ***// 2 | 3 | package com.quickjsexample; 4 | 5 | import java.util.ArrayList; 6 | import java.util.Arrays; 7 | import java.util.Collections; 8 | import java.util.List; 9 | 10 | import androidx.annotation.NonNull; 11 | 12 | import com.facebook.common.logging.FLog; 13 | import com.facebook.react.ReactPackage; 14 | import com.facebook.react.bridge.NativeModule; 15 | import com.facebook.react.bridge.ReactApplicationContext; 16 | import com.facebook.react.bridge.ReactContextBaseJavaModule; 17 | import com.facebook.react.bridge.ReactMethod; 18 | import com.facebook.react.module.annotations.ReactModule; 19 | import com.facebook.react.uimanager.ViewManager; 20 | 21 | public class TTIPackage implements ReactPackage { 22 | 23 | @ReactModule(name = TTIModule.NAME) 24 | public class TTIModule extends ReactContextBaseJavaModule { 25 | public static final String NAME = "TTIModule"; 26 | 27 | public TTIModule(ReactApplicationContext reactContext) { 28 | super(reactContext); 29 | } 30 | 31 | @NonNull 32 | @Override 33 | public String getName() { 34 | return NAME; 35 | } 36 | 37 | @ReactMethod 38 | public void componentDidMount(Double timestamp) { 39 | if (TTIRecorder.mComponentDidMount == 0) { 40 | TTIRecorder.mComponentDidMount = (long) (timestamp - TTIRecorder.mOnCreateTimestamp); 41 | TTIRecorder.printTTI(); 42 | } 43 | } 44 | } 45 | 46 | @NonNull 47 | @Override 48 | public List createNativeModules( 49 | @NonNull ReactApplicationContext reactApplicationContext) { 50 | return Collections.singletonList(new TTIModule(reactApplicationContext)); 51 | } 52 | 53 | @NonNull 54 | @Override 55 | public List createViewManagers( 56 | @NonNull ReactApplicationContext reactApplicationContext) { 57 | return new ArrayList<>(); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /example/android/app/src/main/res/drawable/rn_edit_text_material.xml: -------------------------------------------------------------------------------- 1 | 2 | 16 | 21 | 22 | 23 | 32 | 33 | 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /example/android/gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | 3 | # IDE (e.g. Android Studio) users: 4 | # Gradle settings configured through the IDE *will override* 5 | # any settings specified in this file. 6 | 7 | # For more details on how to configure your build environment visit 8 | # http://www.gradle.org/docs/current/userguide/build_environment.html 9 | 10 | # Specifies the JVM arguments used for the daemon process. 11 | # The setting is particularly useful for tweaking memory settings. 12 | # Default value: -Xmx512m -XX:MaxMetaspaceSize=256m 13 | org.gradle.jvmargs=-Xmx2048m -XX:MaxMetaspaceSize=512m 14 | 15 | # When configured, Gradle will run in incubating parallel mode. 16 | # This option should only be used with decoupled projects. More details, visit 17 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 18 | # org.gradle.parallel=true 19 | 20 | # AndroidX package structure to make it clearer which packages are bundled with the 21 | # Android operating system, and which are packaged with your app's APK 22 | # https://developer.android.com/topic/libraries/support-library/androidx-rn 23 | android.useAndroidX=true 24 | # Automatically convert third-party libraries to use AndroidX 25 | android.enableJetifier=true 26 | 27 | # Version of flipper SDK to use with React Native 28 | FLIPPER_VERSION=0.125.0 29 | 30 | # Use this property to specify which architecture you want to build. 31 | # You can also override it from the CLI using 32 | # ./gradlew -PreactNativeArchitectures=x86_64 33 | reactNativeArchitectures=armeabi-v7a,arm64-v8a,x86,x86_64 34 | 35 | # Use this property to enable support to the new architecture. 36 | # This will allow you to use TurboModules and the Fabric render in 37 | # your application. You should enable this flag either if you want 38 | # to write custom TurboModules/Fabric components OR use libraries that 39 | # are providing them. 40 | newArchEnabled=true 41 | 42 | # Use this property to enable or disable the Hermes JS engine. 43 | # If set to false, you will be using JSC instead. 44 | hermesEnabled=false 45 | 46 | nodeExecutablePath=/Users/liubojie/.nvm/versions/node/v14.15.0/bin/node 47 | -------------------------------------------------------------------------------- /cpp/HostProxy.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "QuickJSRuntime.h" 4 | #include "jsi/jsi.h" 5 | 6 | namespace qjs { 7 | 8 | struct OpaqueData { 9 | static void *GetHostData(JSValueConst this_val); 10 | 11 | void *hostData_; 12 | std::shared_ptr nativeState_; 13 | }; 14 | 15 | class OpaqueOwner { 16 | virtual OpaqueData *GetOpaqueData() = 0; 17 | }; 18 | 19 | class HostObjectProxy : public OpaqueOwner { 20 | public: 21 | HostObjectProxy( 22 | QuickJSRuntime &runtime, 23 | std::shared_ptr hostObject); 24 | 25 | std::shared_ptr GetHostObject(); 26 | 27 | OpaqueData *GetOpaqueData() override; 28 | 29 | static JSClassID GetClassID(); 30 | 31 | static JSValue Getter(JSContext *ctx, JSValueConst this_val, JSAtom name); 32 | 33 | static JSValue 34 | Setter(JSContext *ctx, JSValueConst this_val, JSAtom name, JSValue val); 35 | 36 | static JSValue Enumerator(JSContext *ctx, JSValueConst this_val); 37 | 38 | static void Finalizer(JSRuntime *rt, JSValue val); 39 | 40 | static JSClassDef kJSClassDef; 41 | static const JSCFunctionListEntry kTemplateInterceptor[]; 42 | 43 | private: 44 | QuickJSRuntime &runtime_; 45 | std::shared_ptr hostObject_; 46 | OpaqueData opaqueData_; 47 | 48 | static JSClassID kJSClassID; 49 | }; 50 | 51 | class HostFunctionProxy : public OpaqueOwner { 52 | public: 53 | HostFunctionProxy( 54 | QuickJSRuntime &runtime, 55 | jsi::HostFunctionType &&hostFunction); 56 | 57 | jsi::HostFunctionType &GetHostFunction(); 58 | 59 | OpaqueData *GetOpaqueData() override; 60 | 61 | static JSClassID GetClassID(); 62 | 63 | static void Finalizer(JSRuntime *rt, JSValue val); 64 | 65 | static JSValue FunctionCallback( 66 | JSContext *ctx, 67 | JSValueConst func_obj, 68 | JSValueConst val, 69 | int argc, 70 | JSValueConst *argv, 71 | int flags); 72 | 73 | static JSClassDef kJSClassDef; 74 | 75 | private: 76 | QuickJSRuntime &runtime_; 77 | jsi::HostFunctionType hostFunction_; 78 | OpaqueData opaqueData_; 79 | 80 | static JSClassID kJSClassID; 81 | }; 82 | 83 | } // namespace qjs 84 | -------------------------------------------------------------------------------- /example/android/app/src/main/java/com/quickjsexample/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.quickjsexample; 2 | 3 | import android.os.Bundle; 4 | 5 | import com.facebook.react.ReactActivity; 6 | import com.facebook.react.ReactActivityDelegate; 7 | import com.facebook.react.bridge.ReactMarker; 8 | import com.facebook.react.bridge.ReactMarkerConstants; 9 | import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint; 10 | import com.facebook.react.defaults.DefaultReactActivityDelegate; 11 | 12 | public class MainActivity extends ReactActivity { 13 | 14 | /** 15 | * Returns the name of the main component registered from JavaScript. This is used to schedule 16 | * rendering of the component. 17 | */ 18 | @Override 19 | protected String getMainComponentName() { 20 | return "QuickjsExample"; 21 | } 22 | 23 | /** 24 | * Returns the instance of the {@link ReactActivityDelegate}. Here we use a util class {@link 25 | * DefaultReactActivityDelegate} which allows you to easily enable Fabric and Concurrent React 26 | * (aka React 18) with two boolean flags. 27 | */ 28 | @Override 29 | protected ReactActivityDelegate createReactActivityDelegate() { 30 | return new DefaultReactActivityDelegate( 31 | this, 32 | getMainComponentName(), 33 | // If you opted-in for the New Architecture, we enable the Fabric Renderer. 34 | DefaultNewArchitectureEntryPoint.getFabricEnabled(), // fabricEnabled 35 | // If you opted-in for the New Architecture, we enable Concurrent React (i.e. React 18). 36 | DefaultNewArchitectureEntryPoint.getConcurrentReactEnabled() // concurrentRootEnabled 37 | ); 38 | } 39 | 40 | @Override 41 | protected void onCreate(Bundle savedInstanceState) { 42 | TTIRecorder.mOnCreateTimestamp = System.currentTimeMillis(); 43 | super.onCreate(savedInstanceState); 44 | ReactMarker.addListener((reactMarkerConstants, s, i) -> { 45 | if (reactMarkerConstants == ReactMarkerConstants.GET_REACT_INSTANCE_MANAGER_START) { 46 | // Start timestamp in react-native-v8 47 | } else if (reactMarkerConstants == ReactMarkerConstants.CONTENT_APPEARED) { 48 | TTIRecorder.mTTI = System.currentTimeMillis() - TTIRecorder.mOnCreateTimestamp; 49 | TTIRecorder.printTTI(); 50 | } 51 | }); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /example/ios/QuickjsExampleTests/QuickjsExampleTests.m: -------------------------------------------------------------------------------- 1 | #import 2 | #import 3 | 4 | #import 5 | #import 6 | 7 | #define TIMEOUT_SECONDS 600 8 | #define TEXT_TO_LOOK_FOR @"Welcome to React" 9 | 10 | @interface QuickjsExampleTests : XCTestCase 11 | 12 | @end 13 | 14 | @implementation QuickjsExampleTests 15 | 16 | - (BOOL)findSubviewInView:(UIView *)view matching:(BOOL (^)(UIView *view))test 17 | { 18 | if (test(view)) { 19 | return YES; 20 | } 21 | for (UIView *subview in [view subviews]) { 22 | if ([self findSubviewInView:subview matching:test]) { 23 | return YES; 24 | } 25 | } 26 | return NO; 27 | } 28 | 29 | - (void)testRendersWelcomeScreen 30 | { 31 | UIViewController *vc = [[[RCTSharedApplication() delegate] window] rootViewController]; 32 | NSDate *date = [NSDate dateWithTimeIntervalSinceNow:TIMEOUT_SECONDS]; 33 | BOOL foundElement = NO; 34 | 35 | __block NSString *redboxError = nil; 36 | #ifdef DEBUG 37 | RCTSetLogFunction( 38 | ^(RCTLogLevel level, RCTLogSource source, NSString *fileName, NSNumber *lineNumber, NSString *message) { 39 | if (level >= RCTLogLevelError) { 40 | redboxError = message; 41 | } 42 | }); 43 | #endif 44 | 45 | while ([date timeIntervalSinceNow] > 0 && !foundElement && !redboxError) { 46 | [[NSRunLoop mainRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.1]]; 47 | [[NSRunLoop mainRunLoop] runMode:NSRunLoopCommonModes beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.1]]; 48 | 49 | foundElement = [self findSubviewInView:vc.view 50 | matching:^BOOL(UIView *view) { 51 | if ([view.accessibilityLabel isEqualToString:TEXT_TO_LOOK_FOR]) { 52 | return YES; 53 | } 54 | return NO; 55 | }]; 56 | } 57 | 58 | #ifdef DEBUG 59 | RCTSetLogFunction(RCTDefaultLogFunction); 60 | #endif 61 | 62 | XCTAssertNil(redboxError, @"RedBox error: %@", redboxError); 63 | XCTAssertTrue(foundElement, @"Couldn't find element with text '%@' in %d seconds", TEXT_TO_LOOK_FOR, TIMEOUT_SECONDS); 64 | } 65 | 66 | @end 67 | -------------------------------------------------------------------------------- /example/ios/Podfile: -------------------------------------------------------------------------------- 1 | require_relative '../node_modules/react-native/scripts/react_native_pods' 2 | require_relative '../node_modules/@react-native-community/cli-platform-ios/native_modules' 3 | 4 | platform :ios, min_ios_version_supported 5 | prepare_react_native_project! 6 | 7 | # If you are using a `react-native-flipper` your iOS build will fail when `NO_FLIPPER=1` is set. 8 | # because `react-native-flipper` depends on (FlipperKit,...) that will be excluded 9 | # 10 | # To fix this you can also exclude `react-native-flipper` using a `react-native.config.js` 11 | # ```js 12 | # module.exports = { 13 | # dependencies: { 14 | # ...(process.env.NO_FLIPPER ? { 'react-native-flipper': { platforms: { ios: null } } } : {}), 15 | # ``` 16 | flipper_config = ENV['NO_FLIPPER'] == "1" ? FlipperConfiguration.disabled : FlipperConfiguration.enabled 17 | 18 | linkage = ENV['USE_FRAMEWORKS'] 19 | if linkage != nil 20 | Pod::UI.puts "Configuring Pod with #{linkage}ally linked Frameworks".green 21 | use_frameworks! :linkage => linkage.to_sym 22 | end 23 | 24 | target 'QuickjsExample' do 25 | config = use_native_modules! 26 | 27 | # Flags change depending on the env values. 28 | flags = get_default_flags() 29 | 30 | use_react_native!( 31 | :path => config[:reactNativePath], 32 | # Hermes is now enabled by default. Disable by setting this flag to false. 33 | # Upcoming versions of React Native may rely on get_default_flags(), but 34 | # we make it explicit here to aid in the React Native upgrade process. 35 | :hermes_enabled => flags[:hermes_enabled], 36 | :fabric_enabled => flags[:fabric_enabled], 37 | # Enables Flipper. 38 | # 39 | # Note that if you have use_frameworks! enabled, Flipper will not work and 40 | # you should disable the next line. 41 | :flipper_configuration => flipper_config, 42 | # An absolute path to your application root. 43 | :app_path => "#{Pod::Config.instance.installation_root}/.." 44 | ) 45 | 46 | target 'QuickjsExampleTests' do 47 | inherit! :complete 48 | # Pods for testing 49 | end 50 | 51 | post_install do |installer| 52 | react_native_post_install( 53 | installer, 54 | # Set `mac_catalyst_enabled` to `true` in order to apply patches 55 | # necessary for Mac Catalyst builds 56 | :mac_catalyst_enabled => false 57 | ) 58 | __apply_Xcode_12_5_M1_post_install_workaround(installer) 59 | end 60 | end 61 | -------------------------------------------------------------------------------- /cpp/engine/libregexp-opcode.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Regular Expression Engine 3 | * 4 | * Copyright (c) 2017-2018 Fabrice Bellard 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 19 | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | 25 | #ifdef DEF 26 | 27 | DEF(invalid, 1) /* never used */ 28 | DEF(char, 3) 29 | DEF(char32, 5) 30 | DEF(dot, 1) 31 | DEF(any, 1) /* same as dot but match any character including line terminator */ 32 | DEF(line_start, 1) 33 | DEF(line_end, 1) 34 | DEF(goto, 5) 35 | DEF(split_goto_first, 5) 36 | DEF(split_next_first, 5) 37 | DEF(match, 1) 38 | DEF(save_start, 2) /* save start position */ 39 | DEF(save_end, 2) /* save end position, must come after saved_start */ 40 | DEF(save_reset, 3) /* reset save positions */ 41 | DEF(loop, 5) /* decrement the top the stack and goto if != 0 */ 42 | DEF(push_i32, 5) /* push integer on the stack */ 43 | DEF(drop, 1) 44 | DEF(word_boundary, 1) 45 | DEF(not_word_boundary, 1) 46 | DEF(back_reference, 2) 47 | DEF(backward_back_reference, 2) /* must come after back_reference */ 48 | DEF(range, 3) /* variable length */ 49 | DEF(range32, 3) /* variable length */ 50 | DEF(lookahead, 5) 51 | DEF(negative_lookahead, 5) 52 | DEF(push_char_pos, 1) /* push the character position on the stack */ 53 | DEF(bne_char_pos, 5) /* pop one stack element and jump if equal to the character 54 | position */ 55 | DEF(prev, 1) /* go to the previous char */ 56 | DEF(simple_greedy_quant, 17) 57 | 58 | #endif /* DEF */ 59 | -------------------------------------------------------------------------------- /example/ios/QuickjsExample/AppDelegate.mm: -------------------------------------------------------------------------------- 1 | #import "AppDelegate.h" 2 | 3 | #import 4 | #import 5 | #import 6 | #import 7 | 8 | #ifndef RCT_USE_HERMES 9 | #if __has_include() 10 | #define RCT_USE_HERMES 1 11 | #else 12 | #define RCT_USE_HERMES 0 13 | #endif 14 | #endif 15 | 16 | #if RCT_USE_HERMES 17 | #import 18 | //#else 19 | //#import 20 | #endif 21 | #import 22 | 23 | @interface AppDelegate () 24 | @end 25 | 26 | @implementation AppDelegate 27 | 28 | + (void)initialize { 29 | extern NSTimeInterval onCreateTimestamp; 30 | onCreateTimestamp = [[NSDate date] timeIntervalSince1970]; 31 | } 32 | 33 | - (std::unique_ptr)jsExecutorFactoryForBridge:(RCTBridge *)bridge 34 | { 35 | auto installBindings = facebook::react::RCTJSIExecutorRuntimeInstaller(nullptr); 36 | #if RCT_USE_HERMES 37 | return std::make_unique(installBindings); 38 | #else 39 | // return std::make_unique(installBindings); 40 | auto cacheDir = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES).firstObject UTF8String]; 41 | return std::make_unique(installBindings, ""); 42 | #endif 43 | } 44 | 45 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions 46 | { 47 | self.moduleName = @"QuickjsExample"; 48 | // You can add your custom initial props in the dictionary below. 49 | // They will be passed down to the ViewController used by React Native. 50 | self.initialProps = @{}; 51 | 52 | return [super application:application didFinishLaunchingWithOptions:launchOptions]; 53 | } 54 | 55 | - (NSURL *)sourceURLForBridge:(RCTBridge *)bridge 56 | { 57 | #if DEBUG 58 | return [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index"]; 59 | #else 60 | return [[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"]; 61 | #endif 62 | } 63 | 64 | /// This method controls whether the `concurrentRoot`feature of React18 is turned on or off. 65 | /// 66 | /// @see: https://reactjs.org/blog/2022/03/29/react-v18.html 67 | /// @note: This requires to be rendering on Fabric (i.e. on the New Architecture). 68 | /// @return: `true` if the `concurrentRoot` feature is enabled. Otherwise, it returns `false`. 69 | - (BOOL)concurrentRootEnabled 70 | { 71 | return true; 72 | } 73 | 74 | @end 75 | -------------------------------------------------------------------------------- /android/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.4.1) 2 | project(react-native-quickjs) 3 | set (CMAKE_VERBOSE_MAKEFILE ON) 4 | set (CMAKE_CXX_STANDARD 14) 5 | 6 | set(PACKAGE_NAME "quickjsexecutor") 7 | 8 | # Folly compile flags 9 | include("${REACT_NATIVE_DIR}/ReactAndroid/cmake-utils/folly-flags.cmake") 10 | add_compile_options(${folly_FLAGS}) 11 | 12 | # QuickJS compile flags 13 | add_compile_options( 14 | -DCONFIG_VERSION="\"1\"" 15 | -D_GNU_SOURCE 16 | -Wno-unused-variable 17 | -DCONFIG_CC="gcc" 18 | -DLOG_TAG=\"ReactNative\" 19 | -DENABLE_HASH_CHECK=${ENABLE_HASH_CHECK} 20 | -DCONFIG_BIGNUM) 21 | 22 | file(GLOB quickjs_jni_SRC CONFIGURE_DEPENDS ./src/main/jni/*.cpp) 23 | file(GLOB quickjs_SRC CONFIGURE_DEPENDS ../cpp/*.cpp ../cpp/engine/*.c) 24 | add_library(${PACKAGE_NAME} 25 | SHARED 26 | ${quickjs_jni_SRC} 27 | ${quickjs_SRC} 28 | ) 29 | 30 | target_include_directories( 31 | ${PACKAGE_NAME} 32 | PRIVATE 33 | "${REACT_NATIVE_DIR}/ReactCommon/jsiexecutor" 34 | ) 35 | 36 | # Specifies a path to native header files. 37 | include_directories( 38 | ./src/main/jni/ 39 | ../cpp 40 | ../cpp/engine 41 | ) 42 | 43 | # find libraries 44 | find_package(ReactAndroid REQUIRED CONFIG) 45 | 46 | find_package(fbjni REQUIRED CONFIG) 47 | 48 | # reactnative_internal_static 49 | file(GLOB rn_jsi_react_SRC "${REACT_NATIVE_DIR}/ReactCommon/jsiexecutor/jsireact/*.cpp") 50 | file(GLOB react_marker_SRC "${REACT_NATIVE_DIR}/ReactCommon/reactperflogger/reactperflogger/*.cpp") 51 | 52 | add_library( 53 | reactnative_internal_static 54 | STATIC 55 | "${rn_jsi_react_SRC}" 56 | "${react_marker_SRC}" 57 | "${REACT_NATIVE_DIR}/ReactCommon/cxxreact/JSExecutor.cpp" 58 | ) 59 | 60 | target_include_directories( 61 | reactnative_internal_static 62 | PRIVATE 63 | "${REACT_NATIVE_DIR}/ReactCommon/jsiexecutor" 64 | "${REACT_NATIVE_DIR}/ReactCommon/reactperflogger" 65 | ) 66 | 67 | target_link_libraries( 68 | reactnative_internal_static 69 | ReactAndroid::folly_runtime 70 | ReactAndroid::glog 71 | ReactAndroid::jsi 72 | ReactAndroid::reactnativejni 73 | ) 74 | 75 | # link to shared libraries 76 | 77 | target_link_libraries( 78 | ${PACKAGE_NAME} 79 | log 80 | android 81 | fbjni::fbjni 82 | ReactAndroid::folly_runtime 83 | ReactAndroid::glog 84 | ReactAndroid::jsi 85 | ReactAndroid::jsinspector 86 | ReactAndroid::reactnativejni 87 | ReactAndroid::runtimeexecutor 88 | reactnative_internal_static 89 | ) 90 | 91 | # target_compile_options(${PACKAGE_NAME} PUBLIC -fsanitize=address -fno-omit-frame-pointer) 92 | # set_target_properties(${PACKAGE_NAME} PROPERTIES LINK_FLAGS -fsanitize=address) -------------------------------------------------------------------------------- /example/android/app/src/main/java/com/quickjsexample/MainApplication.java: -------------------------------------------------------------------------------- 1 | package com.quickjsexample; 2 | 3 | import android.app.Application; 4 | 5 | import com.facebook.hermes.reactexecutor.HermesExecutorFactory; 6 | import com.facebook.react.PackageList; 7 | import com.facebook.react.ReactApplication; 8 | import com.facebook.react.ReactNativeHost; 9 | import com.facebook.react.ReactPackage; 10 | import com.facebook.react.bridge.JavaScriptExecutorFactory; 11 | import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint; 12 | import com.facebook.react.defaults.DefaultReactNativeHost; 13 | import com.facebook.soloader.SoLoader; 14 | import com.quickjs.QuickJSExecutorFactory; 15 | 16 | import java.util.List; 17 | 18 | import androidx.annotation.Nullable; 19 | 20 | public class MainApplication extends Application implements ReactApplication { 21 | 22 | private final ReactNativeHost mReactNativeHost = 23 | new DefaultReactNativeHost(this) { 24 | @Override 25 | public boolean getUseDeveloperSupport() { 26 | return BuildConfig.DEBUG; 27 | } 28 | 29 | @Override 30 | protected List getPackages() { 31 | List packages = new PackageList(this).getPackages(); 32 | // Packages that cannot be autolinked yet can be added manually here, for example: 33 | packages.add(new TTIPackage()); 34 | return packages; 35 | } 36 | 37 | @Override 38 | protected String getJSMainModuleName() { 39 | return "index"; 40 | } 41 | 42 | @Override 43 | protected boolean isNewArchEnabled() { 44 | return BuildConfig.IS_NEW_ARCHITECTURE_ENABLED; 45 | } 46 | 47 | @Override 48 | protected Boolean isHermesEnabled() { 49 | return BuildConfig.IS_HERMES_ENABLED; 50 | } 51 | 52 | @Nullable 53 | @Override 54 | protected JavaScriptExecutorFactory getJavaScriptExecutorFactory() { 55 | if (Boolean.TRUE.equals(isHermesEnabled())) { 56 | return new HermesExecutorFactory(); 57 | } else { 58 | return new QuickJSExecutorFactory(getApplication().getCacheDir() + "/qjs"); 59 | // return new QuickJSExecutorFactory(""); // Disable code cache 60 | } 61 | } 62 | }; 63 | 64 | @Override 65 | public ReactNativeHost getReactNativeHost() { 66 | return mReactNativeHost; 67 | } 68 | 69 | @Override 70 | public void onCreate() { 71 | super.onCreate(); 72 | SoLoader.init(this, /* native exopackage */ false); 73 | if (BuildConfig.IS_NEW_ARCHITECTURE_ENABLED) { 74 | // If you opted-in for the New Architecture, we load the native entry point for this app. 75 | DefaultNewArchitectureEntryPoint.load(); 76 | } 77 | ReactNativeFlipper.initializeFlipper(this, getReactNativeHost().getReactInstanceManager()); 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /example/android/gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%" == "" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%" == "" set DIRNAME=. 29 | set APP_BASE_NAME=%~n0 30 | set APP_HOME=%DIRNAME% 31 | 32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 34 | 35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 37 | 38 | @rem Find java.exe 39 | if defined JAVA_HOME goto findJavaFromJavaHome 40 | 41 | set JAVA_EXE=java.exe 42 | %JAVA_EXE% -version >NUL 2>&1 43 | if "%ERRORLEVEL%" == "0" goto execute 44 | 45 | echo. 46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 47 | echo. 48 | echo Please set the JAVA_HOME variable in your environment to match the 49 | echo location of your Java installation. 50 | 51 | goto fail 52 | 53 | :findJavaFromJavaHome 54 | set JAVA_HOME=%JAVA_HOME:"=% 55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 56 | 57 | if exist "%JAVA_EXE%" goto execute 58 | 59 | echo. 60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 61 | echo. 62 | echo Please set the JAVA_HOME variable in your environment to match the 63 | echo location of your Java installation. 64 | 65 | goto fail 66 | 67 | :execute 68 | @rem Setup the command line 69 | 70 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 71 | 72 | 73 | @rem Execute Gradle 74 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 75 | 76 | :end 77 | @rem End local scope for the variables with windows NT shell 78 | if "%ERRORLEVEL%"=="0" goto mainEnd 79 | 80 | :fail 81 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 82 | rem the _cmd.exe /c_ return code! 83 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 84 | exit /b 1 85 | 86 | :mainEnd 87 | if "%OS%"=="Windows_NT" endlocal 88 | 89 | :omega 90 | -------------------------------------------------------------------------------- /example/src/SearchableList.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import { 3 | View, 4 | Text, 5 | FlatList, 6 | ActivityIndicator, 7 | NativeModules, 8 | } from 'react-native'; 9 | import { ListItem, SearchBar, Avatar } from 'react-native-elements'; 10 | import RandomUser from './RandomUser.json'; 11 | 12 | class FlatListDemo extends Component { 13 | constructor(props) { 14 | super(props); 15 | 16 | this.state = { 17 | loading: false, 18 | data: [], 19 | error: null, 20 | }; 21 | 22 | this.arrayholder = []; 23 | } 24 | 25 | componentDidMount() { 26 | NativeModules.TTIModule.componentDidMount(Date.now()); 27 | this.makeRemoteRequest(); 28 | } 29 | 30 | makeRemoteRequest = () => { 31 | this.setState({ loading: true }); 32 | 33 | // fetch(`https://randomuser.me/api/?&results=20`) 34 | new Promise((resolve, reject) => { 35 | resolve(RandomUser); 36 | }) 37 | .then((res) => { 38 | this.setState({ 39 | data: res.results, 40 | error: res.error || null, 41 | loading: false, 42 | }); 43 | this.arrayholder = res.results; 44 | }) 45 | .catch((error) => { 46 | this.setState({ error, loading: false }); 47 | }); 48 | }; 49 | 50 | renderSeparator = () => { 51 | return ( 52 | 59 | ); 60 | }; 61 | 62 | searchFilterFunction = (text) => { 63 | this.setState({ 64 | value: text, 65 | }); 66 | 67 | const newData = this.arrayholder.filter((item) => { 68 | const itemData = `${item.name.title.toUpperCase()} ${item.name.first.toUpperCase()} ${item.name.last.toUpperCase()}`; 69 | const textData = text.toUpperCase(); 70 | 71 | return itemData.indexOf(textData) > -1; 72 | }); 73 | this.setState({ 74 | data: newData, 75 | }); 76 | }; 77 | 78 | renderHeader = () => { 79 | return ( 80 | this.searchFilterFunction(text)} 85 | autoCorrect={false} 86 | value={this.state.value} 87 | /> 88 | ); 89 | }; 90 | 91 | render() { 92 | if (this.state.loading) { 93 | return ( 94 | 97 | 98 | 99 | ); 100 | } 101 | return ( 102 | 103 | { 107 | return ( 108 | 109 | 110 | {item.name.first} {item.name.last} 111 | 112 | {item.email} 113 | 114 | ); 115 | }} 116 | keyExtractor={(item) => item.email} 117 | ItemSeparatorComponent={this.renderSeparator} 118 | /> 119 | 120 | ); 121 | } 122 | } 123 | 124 | export default FlatListDemo; 125 | -------------------------------------------------------------------------------- /cpp/engine/libregexp.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Regular Expression Engine 3 | * 4 | * Copyright (c) 2017-2018 Fabrice Bellard 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 19 | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | #ifndef LIBREGEXP_H 25 | #define LIBREGEXP_H 26 | 27 | #include 28 | 29 | #include "libunicode.h" 30 | 31 | #define LRE_BOOL int /* for documentation purposes */ 32 | 33 | #define LRE_FLAG_GLOBAL (1 << 0) 34 | #define LRE_FLAG_IGNORECASE (1 << 1) 35 | #define LRE_FLAG_MULTILINE (1 << 2) 36 | #define LRE_FLAG_DOTALL (1 << 3) 37 | #define LRE_FLAG_UTF16 (1 << 4) 38 | #define LRE_FLAG_STICKY (1 << 5) 39 | 40 | #define LRE_FLAG_NAMED_GROUPS (1 << 7) /* named groups are present in the regexp */ 41 | 42 | uint8_t *lre_compile(int *plen, char *error_msg, int error_msg_size, 43 | const char *buf, size_t buf_len, int re_flags, 44 | void *opaque); 45 | int lre_get_capture_count(const uint8_t *bc_buf); 46 | int lre_get_flags(const uint8_t *bc_buf); 47 | const char *lre_get_groupnames(const uint8_t *bc_buf); 48 | int lre_exec(uint8_t **capture, 49 | const uint8_t *bc_buf, const uint8_t *cbuf, int cindex, int clen, 50 | int cbuf_type, void *opaque); 51 | 52 | int lre_parse_escape(const uint8_t **pp, int allow_utf16); 53 | LRE_BOOL lre_is_space(int c); 54 | 55 | /* must be provided by the user */ 56 | LRE_BOOL lre_check_stack_overflow(void *opaque, size_t alloca_size); 57 | void *lre_realloc(void *opaque, void *ptr, size_t size); 58 | 59 | /* JS identifier test */ 60 | extern uint32_t const lre_id_start_table_ascii[4]; 61 | extern uint32_t const lre_id_continue_table_ascii[4]; 62 | 63 | static inline int lre_js_is_ident_first(int c) 64 | { 65 | if ((uint32_t)c < 128) { 66 | return (lre_id_start_table_ascii[c >> 5] >> (c & 31)) & 1; 67 | } else { 68 | #ifdef CONFIG_ALL_UNICODE 69 | return lre_is_id_start(c); 70 | #else 71 | return !lre_is_space(c); 72 | #endif 73 | } 74 | } 75 | 76 | static inline int lre_js_is_ident_next(int c) 77 | { 78 | if ((uint32_t)c < 128) { 79 | return (lre_id_continue_table_ascii[c >> 5] >> (c & 31)) & 1; 80 | } else { 81 | /* ZWNJ and ZWJ are accepted in identifiers */ 82 | #ifdef CONFIG_ALL_UNICODE 83 | return lre_is_id_continue(c) || c == 0x200C || c == 0x200D; 84 | #else 85 | return !lre_is_space(c) || c == 0x200C || c == 0x200D; 86 | #endif 87 | } 88 | } 89 | 90 | #undef LRE_BOOL 91 | 92 | #endif /* LIBREGEXP_H */ 93 | -------------------------------------------------------------------------------- /cpp/engine/list.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Linux klist like system 3 | * 4 | * Copyright (c) 2016-2017 Fabrice Bellard 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 19 | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | #ifndef LIST_H 25 | #define LIST_H 26 | 27 | #ifndef NULL 28 | #include 29 | #endif 30 | 31 | struct list_head { 32 | struct list_head *prev; 33 | struct list_head *next; 34 | }; 35 | 36 | #define LIST_HEAD_INIT(el) { &(el), &(el) } 37 | 38 | /* return the pointer of type 'type *' containing 'el' as field 'member' */ 39 | #define list_entry(el, type, member) \ 40 | ((type *)((uint8_t *)(el) - offsetof(type, member))) 41 | 42 | static inline void init_list_head(struct list_head *head) 43 | { 44 | head->prev = head; 45 | head->next = head; 46 | } 47 | 48 | /* insert 'el' between 'prev' and 'next' */ 49 | static inline void __list_add(struct list_head *el, 50 | struct list_head *prev, struct list_head *next) 51 | { 52 | prev->next = el; 53 | el->prev = prev; 54 | el->next = next; 55 | next->prev = el; 56 | } 57 | 58 | /* add 'el' at the head of the list 'head' (= after element head) */ 59 | static inline void list_add(struct list_head *el, struct list_head *head) 60 | { 61 | __list_add(el, head, head->next); 62 | } 63 | 64 | /* add 'el' at the end of the list 'head' (= before element head) */ 65 | static inline void list_add_tail(struct list_head *el, struct list_head *head) 66 | { 67 | __list_add(el, head->prev, head); 68 | } 69 | 70 | static inline void list_del(struct list_head *el) 71 | { 72 | struct list_head *prev, *next; 73 | prev = el->prev; 74 | next = el->next; 75 | prev->next = next; 76 | next->prev = prev; 77 | el->prev = NULL; /* fail safe */ 78 | el->next = NULL; /* fail safe */ 79 | } 80 | 81 | static inline int list_empty(struct list_head *el) 82 | { 83 | return el->next == el; 84 | } 85 | 86 | #define list_for_each(el, head) \ 87 | for(el = (head)->next; el != (head); el = el->next) 88 | 89 | #define list_for_each_safe(el, el1, head) \ 90 | for(el = (head)->next, el1 = el->next; el != (head); \ 91 | el = el1, el1 = el->next) 92 | 93 | #define list_for_each_prev(el, head) \ 94 | for(el = (head)->prev; el != (head); el = el->prev) 95 | 96 | #define list_for_each_prev_safe(el, el1, head) \ 97 | for(el = (head)->prev, el1 = el->prev; el != (head); \ 98 | el = el1, el1 = el->prev) 99 | 100 | #endif /* LIST_H */ 101 | -------------------------------------------------------------------------------- /example/android/app/src/debug/java/com/quickjsexample/ReactNativeFlipper.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * 4 | *

This source code is licensed under the MIT license found in the LICENSE file in the root 5 | * directory of this source tree. 6 | */ 7 | package com.quickjsexample; 8 | 9 | import android.content.Context; 10 | import com.facebook.flipper.android.AndroidFlipperClient; 11 | import com.facebook.flipper.android.utils.FlipperUtils; 12 | import com.facebook.flipper.core.FlipperClient; 13 | import com.facebook.flipper.plugins.crashreporter.CrashReporterPlugin; 14 | import com.facebook.flipper.plugins.databases.DatabasesFlipperPlugin; 15 | import com.facebook.flipper.plugins.fresco.FrescoFlipperPlugin; 16 | import com.facebook.flipper.plugins.inspector.DescriptorMapping; 17 | import com.facebook.flipper.plugins.inspector.InspectorFlipperPlugin; 18 | import com.facebook.flipper.plugins.network.FlipperOkhttpInterceptor; 19 | import com.facebook.flipper.plugins.network.NetworkFlipperPlugin; 20 | import com.facebook.flipper.plugins.sharedpreferences.SharedPreferencesFlipperPlugin; 21 | import com.facebook.react.ReactInstanceEventListener; 22 | import com.facebook.react.ReactInstanceManager; 23 | import com.facebook.react.bridge.ReactContext; 24 | import com.facebook.react.modules.network.NetworkingModule; 25 | import okhttp3.OkHttpClient; 26 | 27 | /** 28 | * Class responsible of loading Flipper inside your React Native application. This is the debug 29 | * flavor of it. Here you can add your own plugins and customize the Flipper setup. 30 | */ 31 | public class ReactNativeFlipper { 32 | public static void initializeFlipper(Context context, ReactInstanceManager reactInstanceManager) { 33 | if (FlipperUtils.shouldEnableFlipper(context)) { 34 | final FlipperClient client = AndroidFlipperClient.getInstance(context); 35 | 36 | client.addPlugin(new InspectorFlipperPlugin(context, DescriptorMapping.withDefaults())); 37 | client.addPlugin(new DatabasesFlipperPlugin(context)); 38 | client.addPlugin(new SharedPreferencesFlipperPlugin(context)); 39 | client.addPlugin(CrashReporterPlugin.getInstance()); 40 | 41 | NetworkFlipperPlugin networkFlipperPlugin = new NetworkFlipperPlugin(); 42 | NetworkingModule.setCustomClientBuilder( 43 | new NetworkingModule.CustomClientBuilder() { 44 | @Override 45 | public void apply(OkHttpClient.Builder builder) { 46 | builder.addNetworkInterceptor(new FlipperOkhttpInterceptor(networkFlipperPlugin)); 47 | } 48 | }); 49 | client.addPlugin(networkFlipperPlugin); 50 | client.start(); 51 | 52 | // Fresco Plugin needs to ensure that ImagePipelineFactory is initialized 53 | // Hence we run if after all native modules have been initialized 54 | ReactContext reactContext = reactInstanceManager.getCurrentReactContext(); 55 | if (reactContext == null) { 56 | reactInstanceManager.addReactInstanceEventListener( 57 | new ReactInstanceEventListener() { 58 | @Override 59 | public void onReactContextInitialized(ReactContext reactContext) { 60 | reactInstanceManager.removeReactInstanceEventListener(this); 61 | reactContext.runOnNativeModulesQueueThread( 62 | new Runnable() { 63 | @Override 64 | public void run() { 65 | client.addPlugin(new FrescoFlipperPlugin()); 66 | } 67 | }); 68 | } 69 | }); 70 | } else { 71 | client.addPlugin(new FrescoFlipperPlugin()); 72 | } 73 | } 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /example/ios/QuickjsExample.xcodeproj/xcshareddata/xcschemes/QuickjsExample.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 33 | 39 | 40 | 41 | 42 | 43 | 53 | 55 | 61 | 62 | 63 | 64 | 70 | 72 | 78 | 79 | 80 | 81 | 83 | 84 | 87 | 88 | 89 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-native-quickjs", 3 | "version": "0.0.2", 4 | "description": "Quickjs engine for react native", 5 | "main": "lib/commonjs/index", 6 | "module": "lib/module/index", 7 | "types": "lib/typescript/index.d.ts", 8 | "react-native": "src/index", 9 | "source": "src/index", 10 | "files": [ 11 | "src", 12 | "lib", 13 | "android", 14 | "ios", 15 | "cpp", 16 | "*.podspec", 17 | "!lib/typescript/example", 18 | "!ios/build", 19 | "!android/build", 20 | "!android/gradle", 21 | "!android/gradlew", 22 | "!android/gradlew.bat", 23 | "!android/local.properties", 24 | "!**/__tests__", 25 | "!**/__fixtures__", 26 | "!**/__mocks__", 27 | "!**/.*" 28 | ], 29 | "scripts": { 30 | "test": "jest", 31 | "typecheck": "tsc --noEmit", 32 | "lint": "eslint \"**/*.{js,ts,tsx}\"", 33 | "prepack": "bob build", 34 | "release": "release-it", 35 | "example": "yarn --cwd example", 36 | "bootstrap": "yarn example && yarn install && yarn example pods", 37 | "clean": "del-cli android/build example/android/build example/android/app/build example/ios/build" 38 | }, 39 | "keywords": [ 40 | "react-native", 41 | "ios", 42 | "android" 43 | ], 44 | "repository": "https://github.com/bojie-liu/react-native-quickjs", 45 | "author": "liubojie (https://github.com/bojie-liu)", 46 | "license": "MIT", 47 | "bugs": { 48 | "url": "https://github.com/bojie-liu/react-native-quickjs/issues" 49 | }, 50 | "homepage": "https://github.com/bojie-liu/react-native-quickjs#readme", 51 | "publishConfig": { 52 | "registry": "https://registry.npmjs.org/" 53 | }, 54 | "devDependencies": { 55 | "@react-native-community/eslint-config": "^3.0.2", 56 | "@release-it/conventional-changelog": "^5.0.0", 57 | "@types/jest": "^28.1.2", 58 | "@types/react": "~17.0.21", 59 | "@types/react-native": "0.70.0", 60 | "del-cli": "^5.0.0", 61 | "eslint": "^8.4.1", 62 | "eslint-config-prettier": "^8.5.0", 63 | "eslint-plugin-prettier": "^4.0.0", 64 | "jest": "^28.1.1", 65 | "pod-install": "^0.1.0", 66 | "prettier": "^2.0.5", 67 | "react": "18.2.0", 68 | "react-native": "0.71.8", 69 | "react-native-builder-bob": "^0.20.0", 70 | "release-it": "^15.0.0", 71 | "typescript": "^4.5.2" 72 | }, 73 | "resolutions": { 74 | "@types/react": "17.0.21" 75 | }, 76 | "peerDependencies": { 77 | "react": "*", 78 | "react-native": "*" 79 | }, 80 | "engines": { 81 | "node": ">= 16.0.0" 82 | }, 83 | "packageManager": "^yarn@1.22.15", 84 | "jest": { 85 | "preset": "react-native", 86 | "modulePathIgnorePatterns": [ 87 | "/example/node_modules", 88 | "/lib/" 89 | ] 90 | }, 91 | "release-it": { 92 | "git": { 93 | "commitMessage": "chore: release ${version}", 94 | "tagName": "v${version}" 95 | }, 96 | "npm": { 97 | "publish": true 98 | }, 99 | "github": { 100 | "release": true 101 | }, 102 | "plugins": { 103 | "@release-it/conventional-changelog": { 104 | "preset": "angular" 105 | } 106 | } 107 | }, 108 | "eslintConfig": { 109 | "root": true, 110 | "extends": [ 111 | "@react-native-community", 112 | "prettier" 113 | ], 114 | "rules": { 115 | "prettier/prettier": [ 116 | "error", 117 | { 118 | "quoteProps": "consistent", 119 | "singleQuote": true, 120 | "tabWidth": 2, 121 | "trailingComma": "es5", 122 | "useTabs": false 123 | } 124 | ] 125 | } 126 | }, 127 | "eslintIgnore": [ 128 | "node_modules/", 129 | "lib/" 130 | ], 131 | "prettier": { 132 | "quoteProps": "consistent", 133 | "singleQuote": true, 134 | "tabWidth": 2, 135 | "trailingComma": "es5", 136 | "useTabs": false 137 | }, 138 | "react-native-builder-bob": { 139 | "source": "src", 140 | "output": "lib", 141 | "targets": [ 142 | "commonjs", 143 | "module", 144 | [ 145 | "typescript", 146 | { 147 | "project": "tsconfig.build.json" 148 | } 149 | ] 150 | ] 151 | } 152 | } 153 | -------------------------------------------------------------------------------- /cpp/engine/libunicode.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Unicode utilities 3 | * 4 | * Copyright (c) 2017-2018 Fabrice Bellard 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 19 | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | #ifndef LIBUNICODE_H 25 | #define LIBUNICODE_H 26 | 27 | #include 28 | 29 | #define LRE_BOOL int /* for documentation purposes */ 30 | 31 | /* define it to include all the unicode tables (40KB larger) */ 32 | #define CONFIG_ALL_UNICODE 33 | 34 | #define LRE_CC_RES_LEN_MAX 3 35 | 36 | typedef enum { 37 | UNICODE_NFC, 38 | UNICODE_NFD, 39 | UNICODE_NFKC, 40 | UNICODE_NFKD, 41 | } UnicodeNormalizationEnum; 42 | 43 | int lre_case_conv(uint32_t *res, uint32_t c, int conv_type); 44 | LRE_BOOL lre_is_cased(uint32_t c); 45 | LRE_BOOL lre_is_case_ignorable(uint32_t c); 46 | 47 | /* char ranges */ 48 | 49 | typedef struct { 50 | int len; /* in points, always even */ 51 | int size; 52 | uint32_t *points; /* points sorted by increasing value */ 53 | void *mem_opaque; 54 | void *(*realloc_func)(void *opaque, void *ptr, size_t size); 55 | } CharRange; 56 | 57 | typedef enum { 58 | CR_OP_UNION, 59 | CR_OP_INTER, 60 | CR_OP_XOR, 61 | } CharRangeOpEnum; 62 | 63 | void cr_init(CharRange *cr, void *mem_opaque, void *(*realloc_func)(void *opaque, void *ptr, size_t size)); 64 | void cr_free(CharRange *cr); 65 | int cr_realloc(CharRange *cr, int size); 66 | int cr_copy(CharRange *cr, const CharRange *cr1); 67 | 68 | static inline int cr_add_point(CharRange *cr, uint32_t v) 69 | { 70 | if (cr->len >= cr->size) { 71 | if (cr_realloc(cr, cr->len + 1)) 72 | return -1; 73 | } 74 | cr->points[cr->len++] = v; 75 | return 0; 76 | } 77 | 78 | static inline int cr_add_interval(CharRange *cr, uint32_t c1, uint32_t c2) 79 | { 80 | if ((cr->len + 2) > cr->size) { 81 | if (cr_realloc(cr, cr->len + 2)) 82 | return -1; 83 | } 84 | cr->points[cr->len++] = c1; 85 | cr->points[cr->len++] = c2; 86 | return 0; 87 | } 88 | 89 | int cr_union1(CharRange *cr, const uint32_t *b_pt, int b_len); 90 | 91 | static inline int cr_union_interval(CharRange *cr, uint32_t c1, uint32_t c2) 92 | { 93 | uint32_t b_pt[2]; 94 | b_pt[0] = c1; 95 | b_pt[1] = c2 + 1; 96 | return cr_union1(cr, b_pt, 2); 97 | } 98 | 99 | int cr_op(CharRange *cr, const uint32_t *a_pt, int a_len, 100 | const uint32_t *b_pt, int b_len, int op); 101 | 102 | int cr_invert(CharRange *cr); 103 | 104 | #ifdef CONFIG_ALL_UNICODE 105 | 106 | LRE_BOOL lre_is_id_start(uint32_t c); 107 | LRE_BOOL lre_is_id_continue(uint32_t c); 108 | 109 | int unicode_normalize(uint32_t **pdst, const uint32_t *src, int src_len, 110 | UnicodeNormalizationEnum n_type, 111 | void *opaque, void *(*realloc_func)(void *opaque, void *ptr, size_t size)); 112 | 113 | /* Unicode character range functions */ 114 | 115 | int unicode_script(CharRange *cr, 116 | const char *script_name, LRE_BOOL is_ext); 117 | int unicode_general_category(CharRange *cr, const char *gc_name); 118 | int unicode_prop(CharRange *cr, const char *prop_name); 119 | 120 | #endif /* CONFIG_ALL_UNICODE */ 121 | 122 | #undef LRE_BOOL 123 | 124 | #endif /* LIBUNICODE_H */ 125 | -------------------------------------------------------------------------------- /example/ios/QuickjsExample/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 24 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | Contributions are always welcome, no matter how large or small! 4 | 5 | We want this community to be friendly and respectful to each other. Please follow it in all your interactions with the project. Before contributing, please read the [code of conduct](./CODE_OF_CONDUCT.md). 6 | 7 | ## Development workflow 8 | 9 | To get started with the project, run `yarn` in the root directory to install the required dependencies for each package: 10 | 11 | ```sh 12 | yarn 13 | ``` 14 | 15 | > While it's possible to use [`npm`](https://github.com/npm/cli), the tooling is built around [`yarn`](https://classic.yarnpkg.com/), so you'll have an easier time if you use `yarn` for development. 16 | 17 | While developing, you can run the [example app](/example/) to test your changes. Any changes you make in your library's JavaScript code will be reflected in the example app without a rebuild. If you change any native code, then you'll need to rebuild the example app. 18 | 19 | To start the packager: 20 | 21 | ```sh 22 | yarn example start 23 | ``` 24 | 25 | To run the example app on Android: 26 | 27 | ```sh 28 | yarn example android 29 | ``` 30 | 31 | To run the example app on iOS: 32 | 33 | ```sh 34 | yarn example ios 35 | ``` 36 | 37 | Make sure your code passes TypeScript and ESLint. Run the following to verify: 38 | 39 | ```sh 40 | yarn typecheck 41 | yarn lint 42 | ``` 43 | 44 | To fix formatting errors, run the following: 45 | 46 | ```sh 47 | yarn lint --fix 48 | ``` 49 | 50 | Remember to add tests for your change if possible. Run the unit tests by: 51 | 52 | ```sh 53 | yarn test 54 | ``` 55 | 56 | To edit the Objective-C or Swift files, open `example/ios/QuickjsExample.xcworkspace` in XCode and find the source files at `Pods > Development Pods > react-native-quickjs`. 57 | 58 | To edit the Java or Kotlin files, open `example/android` in Android studio and find the source files at `react-native-quickjs` under `Android`. 59 | 60 | 61 | ### Commit message convention 62 | 63 | We follow the [conventional commits specification](https://www.conventionalcommits.org/en) for our commit messages: 64 | 65 | - `fix`: bug fixes, e.g. fix crash due to deprecated method. 66 | - `feat`: new features, e.g. add new method to the module. 67 | - `refactor`: code refactor, e.g. migrate from class components to hooks. 68 | - `docs`: changes into documentation, e.g. add usage example for the module.. 69 | - `test`: adding or updating tests, e.g. add integration tests using detox. 70 | - `chore`: tooling changes, e.g. change CI config. 71 | 72 | Our pre-commit hooks verify that your commit message matches this format when committing. 73 | 74 | ### Linting and tests 75 | 76 | [ESLint](https://eslint.org/), [Prettier](https://prettier.io/), [TypeScript](https://www.typescriptlang.org/) 77 | 78 | We use [TypeScript](https://www.typescriptlang.org/) for type checking, [ESLint](https://eslint.org/) with [Prettier](https://prettier.io/) for linting and formatting the code, and [Jest](https://jestjs.io/) for testing. 79 | 80 | Our pre-commit hooks verify that the linter and tests pass when committing. 81 | 82 | ### Publishing to npm 83 | 84 | We use [release-it](https://github.com/release-it/release-it) to make it easier to publish new versions. It handles common tasks like bumping version based on semver, creating tags and releases etc. 85 | 86 | To publish new versions, run the following: 87 | 88 | ```sh 89 | yarn release 90 | ``` 91 | 92 | ### Scripts 93 | 94 | The `package.json` file contains various scripts for common tasks: 95 | 96 | - `yarn bootstrap`: setup project by installing all dependencies and pods. 97 | - `yarn typecheck`: type-check files with TypeScript. 98 | - `yarn lint`: lint files with ESLint. 99 | - `yarn test`: run unit tests with Jest. 100 | - `yarn example start`: start the Metro server for the example app. 101 | - `yarn example android`: run the example app on Android. 102 | - `yarn example ios`: run the example app on iOS. 103 | 104 | ### Sending a pull request 105 | 106 | > **Working on your first pull request?** You can learn how from this _free_ series: [How to Contribute to an Open Source Project on GitHub](https://app.egghead.io/playlists/how-to-contribute-to-an-open-source-project-on-github). 107 | 108 | When you're sending a pull request: 109 | 110 | - Prefer small pull requests focused on one change. 111 | - Verify that linters and tests are passing. 112 | - Review the documentation to make sure it looks good. 113 | - Follow the pull request template when opening a pull request. 114 | - For pull requests that change the API or implementation, discuss with maintainers first by opening an issue. 115 | -------------------------------------------------------------------------------- /android/build.gradle: -------------------------------------------------------------------------------- 1 | import groovy.json.JsonSlurper 2 | import org.apache.tools.ant.taskdefs.condition.Os 3 | 4 | def appProject = rootProject.allprojects.find { it.plugins.hasPlugin('com.android.application') } 5 | 6 | buildscript { 7 | repositories { 8 | google() 9 | mavenCentral() 10 | } 11 | 12 | dependencies { 13 | classpath "com.android.tools.build:gradle:7.2.1" 14 | } 15 | } 16 | 17 | def isNewArchitectureEnabled() { 18 | return rootProject.hasProperty("newArchEnabled") && rootProject.getProperty("newArchEnabled") == "true" 19 | } 20 | 21 | def getNodeExecutable() { 22 | return rootProject.getProperties().get("nodeExecutablePath") ?: "node" 23 | } 24 | 25 | def findNodePackageDir(String packageName, boolean absolute = true) { 26 | def nodeCommand = [getNodeExecutable(), "--print", "require.resolve('${packageName}/package.json')"] 27 | def proc = nodeCommand.execute(null, rootDir) 28 | def error = proc.err.text 29 | if (error) { 30 | throw new GradleException("findNodePackageDir() execution failed - nodeCommand[${nodeCommand.join(' ')}]\n" + error) 31 | } 32 | def dir = new File(proc.text.trim()).getParentFile() 33 | return absolute ? dir.getAbsoluteFile() : dir 34 | } 35 | 36 | apply plugin: "com.android.library" 37 | 38 | def reactNativeDir = findNodePackageDir("react-native") 39 | def reactNativeManifest = file("${reactNativeDir}/package.json") 40 | def reactNativeManifestAsJson = new JsonSlurper().parseText(reactNativeManifest.text) 41 | def reactNativeVersion = reactNativeManifestAsJson.version as String 42 | def (major, minor, patch) = reactNativeVersion.tokenize('.') 43 | def rnMinorVersion = Integer.parseInt(minor) 44 | 45 | if (appProject?.ext?.react?.enableHermes) { 46 | throw new GradleException("Please disable Hermes because Hermes will transform the JavaScript bundle as bytecode bundle.\n") 47 | } 48 | 49 | if (isNewArchitectureEnabled()) { 50 | apply plugin: "com.facebook.react" 51 | } 52 | 53 | def getExtOrDefault(name) { 54 | return rootProject.ext.has(name) ? rootProject.ext.get(name) : project.properties["Quickjs_" + name] 55 | } 56 | 57 | def getExtOrIntegerDefault(name) { 58 | return rootProject.ext.has(name) ? rootProject.ext.get(name) : (project.properties["Quickjs_" + name]).toInteger() 59 | } 60 | 61 | def toPlatformFileString(File path) { 62 | def result = path.toString() 63 | if (Os.isFamily(Os.FAMILY_WINDOWS)) { 64 | result = result.replace(File.separatorChar, '/' as char) 65 | } 66 | return result 67 | } 68 | 69 | android { 70 | ndkVersion getExtOrDefault("ndkVersion") 71 | compileSdkVersion getExtOrIntegerDefault("compileSdkVersion") 72 | 73 | defaultConfig { 74 | minSdkVersion getExtOrIntegerDefault("minSdkVersion") 75 | targetSdkVersion getExtOrIntegerDefault("targetSdkVersion") 76 | buildConfigField "boolean", "IS_NEW_ARCHITECTURE_ENABLED", isNewArchitectureEnabled().toString() 77 | externalNativeBuild { 78 | cmake { 79 | cppFlags "-O2 -flto -frtti -fexceptions -Wall -fstack-protector-all" 80 | abiFilters "x86", "x86_64", "armeabi-v7a", "arm64-v8a" 81 | arguments "-DANDROID_STL=c++_shared", 82 | "-DREACT_NATIVE_DIR=${toPlatformFileString(reactNativeDir)}", 83 | "-DREACT_NATIVE_TARGET_VERSION=${rnMinorVersion}", 84 | "-DENABLE_HASH_CHECK=0" 85 | } 86 | } 87 | } 88 | externalNativeBuild { 89 | cmake { 90 | path "CMakeLists.txt" 91 | } 92 | } 93 | buildTypes { 94 | release { 95 | minifyEnabled true 96 | proguardFiles getDefaultProguardFile('proguard-android.txt') 97 | externalNativeBuild { 98 | cmake { 99 | cppFlags "-O2 -frtti -fexceptions -Wall" 100 | } 101 | } 102 | } 103 | } 104 | 105 | lintOptions { 106 | disable "GradleCompatible" 107 | } 108 | 109 | packagingOptions { 110 | // Uncomment to keep debug symbols 111 | // doNotStrip "**/*.so" 112 | 113 | excludes += [ 114 | "**/libc++_shared.so", 115 | "**/libfbjni.so", 116 | "**/libjsi.so", 117 | "**/libfolly_runtime.so", 118 | "**/libglog.so", 119 | "**/libreactnativejni.so", 120 | "**/libjsinspector.so", 121 | "**/libruntimeexecutor.so", 122 | ] 123 | } 124 | 125 | compileOptions { 126 | sourceCompatibility JavaVersion.VERSION_1_8 127 | targetCompatibility JavaVersion.VERSION_1_8 128 | } 129 | 130 | buildFeatures { 131 | prefab true 132 | } 133 | } 134 | 135 | task cleanCmakeCache() { 136 | tasks.getByName("clean").dependsOn(cleanCmakeCache) 137 | doFirst { 138 | delete "${projectDir}/.cxx" 139 | } 140 | } 141 | 142 | repositories { 143 | mavenCentral() 144 | google() 145 | } 146 | 147 | 148 | dependencies { 149 | // For < 0.71, this will be from the local maven repo 150 | // For > 0.71, this will be replaced by `com.facebook.react:react-android:$version` by react gradle plugin 151 | //noinspection GradleDynamicVersion 152 | implementation "com.facebook.yoga:proguard-annotations:1.19.0" 153 | compileOnly "com.facebook.fbjni:fbjni:0.3.0" 154 | implementation "com.facebook.react:react-android" 155 | } 156 | 157 | if (isNewArchitectureEnabled()) { 158 | react { 159 | jsRootDir = file("../src/") 160 | libraryName = "Quickjs" 161 | codegenJavaPackageName = "com.quickjs" 162 | } 163 | } 164 | -------------------------------------------------------------------------------- /cpp/city.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2011 Google, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | // 21 | // CityHash, by Geoff Pike and Jyrki Alakuijala 22 | // 23 | // http://code.google.com/p/cityhash/ 24 | // 25 | // This file provides a few functions for hashing strings. All of them are 26 | // high-quality functions in the sense that they pass standard tests such 27 | // as Austin Appleby's SMHasher. They are also fast. 28 | // 29 | // For 64-bit x86 code, on short strings, we don't know of anything faster than 30 | // CityHash64 that is of comparable quality. We believe our nearest competitor 31 | // is Murmur3. For 64-bit x86 code, CityHash64 is an excellent choice for hash 32 | // tables and most other hashing (excluding cryptography). 33 | // 34 | // For 64-bit x86 code, on long strings, the picture is more complicated. 35 | // On many recent Intel CPUs, such as Nehalem, Westmere, Sandy Bridge, etc., 36 | // CityHashCrc128 appears to be faster than all competitors of comparable 37 | // quality. CityHash128 is also good but not quite as fast. We believe our 38 | // nearest competitor is Bob Jenkins' Spooky. We don't have great data for 39 | // other 64-bit CPUs, but for long strings we know that Spooky is slightly 40 | // faster than CityHash on some relatively recent AMD x86-64 CPUs, for example. 41 | // Note that CityHashCrc128 is declared in citycrc.h. 42 | // 43 | // For 32-bit x86 code, we don't know of anything faster than CityHash32 that 44 | // is of comparable quality. We believe our nearest competitor is Murmur3A. 45 | // (On 64-bit CPUs, it is typically faster to use the other CityHash variants.) 46 | // 47 | // Functions in the CityHash family are not suitable for cryptography. 48 | // 49 | // Please see CityHash's README file for more details on our performance 50 | // measurements and so on. 51 | // 52 | // WARNING: This code has been only lightly tested on big-endian platforms! 53 | // It is known to work well on little-endian platforms that have a small penalty 54 | // for unaligned reads, such as current Intel and AMD moderate-to-high-end CPUs. 55 | // It should work on all 32-bit and 64-bit platforms that allow unaligned reads; 56 | // bug reports are welcome. 57 | // 58 | // By the way, for some hash functions, given strings a and b, the hash 59 | // of a+b is easily derived from the hashes of a and b. This property 60 | // doesn't hold for any hash functions in this file. 61 | 62 | #ifndef CITY_HASH_H_ 63 | #define CITY_HASH_H_ 64 | 65 | #if ENABLE_HASH_CHECK 66 | 67 | #include // for size_t. 68 | #include 69 | #include 70 | 71 | namespace base { 72 | namespace cityhash { 73 | 74 | typedef uint8_t uint8; 75 | typedef uint32_t uint32; 76 | typedef uint64_t uint64; 77 | typedef std::pair uint128; 78 | 79 | inline uint64 Uint128Low64(const uint128& x) { return x.first; } 80 | inline uint64 Uint128High64(const uint128& x) { return x.second; } 81 | 82 | // Hash function for a byte array. 83 | uint64 CityHash64(const char *buf, size_t len); 84 | 85 | // Hash function for a byte array. For convenience, a 64-bit seed is also 86 | // hashed into the result. 87 | uint64 CityHash64WithSeed(const char *buf, size_t len, uint64 seed); 88 | 89 | // Hash function for a byte array. For convenience, two seeds are also 90 | // hashed into the result. 91 | uint64 CityHash64WithSeeds(const char *buf, size_t len, 92 | uint64 seed0, uint64 seed1); 93 | 94 | // Hash function for a byte array. 95 | uint128 CityHash128(const char *s, size_t len); 96 | 97 | // Hash function for a byte array. For convenience, a 128-bit seed is also 98 | // hashed into the result. 99 | uint128 CityHash128WithSeed(const char *s, size_t len, uint128 seed); 100 | 101 | // Hash function for a byte array. Most useful in 32-bit binaries. 102 | uint32 CityHash32(const char *buf, size_t len); 103 | 104 | // Hash 128 input bits down to 64 bits of output. 105 | // This is intended to be a reasonably good hash function. 106 | inline uint64 Hash128to64(const uint128& x) { 107 | // Murmur-inspired hashing. 108 | const uint64 kMul = 0x9ddfea08eb382d69ULL; 109 | uint64 a = (Uint128Low64(x) ^ Uint128High64(x)) * kMul; 110 | a ^= (a >> 47); 111 | uint64 b = (Uint128High64(x) ^ a) * kMul; 112 | b ^= (b >> 47); 113 | b *= kMul; 114 | return b; 115 | } 116 | 117 | 118 | } // namespace cityhash 119 | } // namespace base 120 | 121 | #endif 122 | 123 | #endif // CITY_HASH_H_ -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | 2 | # Contributor Covenant Code of Conduct 3 | 4 | ## Our Pledge 5 | 6 | We as members, contributors, and leaders pledge to make participation in our 7 | community a harassment-free experience for everyone, regardless of age, body 8 | size, visible or invisible disability, ethnicity, sex characteristics, gender 9 | identity and expression, level of experience, education, socio-economic status, 10 | nationality, personal appearance, race, caste, color, religion, or sexual 11 | identity and orientation. 12 | 13 | We pledge to act and interact in ways that contribute to an open, welcoming, 14 | diverse, inclusive, and healthy community. 15 | 16 | ## Our Standards 17 | 18 | Examples of behavior that contributes to a positive environment for our 19 | community include: 20 | 21 | * Demonstrating empathy and kindness toward other people 22 | * Being respectful of differing opinions, viewpoints, and experiences 23 | * Giving and gracefully accepting constructive feedback 24 | * Accepting responsibility and apologizing to those affected by our mistakes, 25 | and learning from the experience 26 | * Focusing on what is best not just for us as individuals, but for the overall 27 | community 28 | 29 | Examples of unacceptable behavior include: 30 | 31 | * The use of sexualized language or imagery, and sexual attention or advances of 32 | any kind 33 | * Trolling, insulting or derogatory comments, and personal or political attacks 34 | * Public or private harassment 35 | * Publishing others' private information, such as a physical or email address, 36 | without their explicit permission 37 | * Other conduct which could reasonably be considered inappropriate in a 38 | professional setting 39 | 40 | ## Enforcement Responsibilities 41 | 42 | Community leaders are responsible for clarifying and enforcing our standards of 43 | acceptable behavior and will take appropriate and fair corrective action in 44 | response to any behavior that they deem inappropriate, threatening, offensive, 45 | or harmful. 46 | 47 | Community leaders have the right and responsibility to remove, edit, or reject 48 | comments, commits, code, wiki edits, issues, and other contributions that are 49 | not aligned to this Code of Conduct, and will communicate reasons for moderation 50 | decisions when appropriate. 51 | 52 | ## Scope 53 | 54 | This Code of Conduct applies within all community spaces, and also applies when 55 | an individual is officially representing the community in public spaces. 56 | Examples of representing our community include using an official e-mail address, 57 | posting via an official social media account, or acting as an appointed 58 | representative at an online or offline event. 59 | 60 | ## Enforcement 61 | 62 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 63 | reported to the community leaders responsible for enforcement at 64 | [INSERT CONTACT METHOD]. 65 | All complaints will be reviewed and investigated promptly and fairly. 66 | 67 | All community leaders are obligated to respect the privacy and security of the 68 | reporter of any incident. 69 | 70 | ## Enforcement Guidelines 71 | 72 | Community leaders will follow these Community Impact Guidelines in determining 73 | the consequences for any action they deem in violation of this Code of Conduct: 74 | 75 | ### 1. Correction 76 | 77 | **Community Impact**: Use of inappropriate language or other behavior deemed 78 | unprofessional or unwelcome in the community. 79 | 80 | **Consequence**: A private, written warning from community leaders, providing 81 | clarity around the nature of the violation and an explanation of why the 82 | behavior was inappropriate. A public apology may be requested. 83 | 84 | ### 2. Warning 85 | 86 | **Community Impact**: A violation through a single incident or series of 87 | actions. 88 | 89 | **Consequence**: A warning with consequences for continued behavior. No 90 | interaction with the people involved, including unsolicited interaction with 91 | those enforcing the Code of Conduct, for a specified period of time. This 92 | includes avoiding interactions in community spaces as well as external channels 93 | like social media. Violating these terms may lead to a temporary or permanent 94 | ban. 95 | 96 | ### 3. Temporary Ban 97 | 98 | **Community Impact**: A serious violation of community standards, including 99 | sustained inappropriate behavior. 100 | 101 | **Consequence**: A temporary ban from any sort of interaction or public 102 | communication with the community for a specified period of time. No public or 103 | private interaction with the people involved, including unsolicited interaction 104 | with those enforcing the Code of Conduct, is allowed during this period. 105 | Violating these terms may lead to a permanent ban. 106 | 107 | ### 4. Permanent Ban 108 | 109 | **Community Impact**: Demonstrating a pattern of violation of community 110 | standards, including sustained inappropriate behavior, harassment of an 111 | individual, or aggression toward or disparagement of classes of individuals. 112 | 113 | **Consequence**: A permanent ban from any sort of public interaction within the 114 | community. 115 | 116 | ## Attribution 117 | 118 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], 119 | version 2.1, available at 120 | [https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1]. 121 | 122 | Community Impact Guidelines were inspired by 123 | [Mozilla's code of conduct enforcement ladder][Mozilla CoC]. 124 | 125 | For answers to common questions about this code of conduct, see the FAQ at 126 | [https://www.contributor-covenant.org/faq][FAQ]. Translations are available at 127 | [https://www.contributor-covenant.org/translations][translations]. 128 | 129 | [homepage]: https://www.contributor-covenant.org 130 | [v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html 131 | [Mozilla CoC]: https://github.com/mozilla/diversity 132 | [FAQ]: https://www.contributor-covenant.org/faq 133 | [translations]: https://www.contributor-covenant.org/translations 134 | -------------------------------------------------------------------------------- /cpp/JSIValueConverter.cpp: -------------------------------------------------------------------------------- 1 | #include "JSIValueConverter.h" 2 | 3 | #include "QuickJSPointerValue.h" 4 | #include "ScopedJSValue.h" 5 | 6 | namespace qjs { 7 | 8 | // static 9 | jsi::Value JSIValueConverter::ToJSIValue( 10 | const QuickJSRuntime &runtime, 11 | const JSValueConst &value) { 12 | auto jsRuntime = runtime.getJSRuntime(); 13 | auto jsContext = runtime.getJSContext(); 14 | if (JS_IsUndefined(value)) { 15 | return jsi::Value::undefined(); 16 | } 17 | if (JS_IsNull(value)) { 18 | return jsi::Value::null(); 19 | } 20 | if (JS_IsBool(value)) { 21 | return jsi::Value(JS_VALUE_GET_BOOL(value)); 22 | } 23 | if (JS_IsNumber(value)) { 24 | if (JS_TAG_IS_FLOAT64(JS_VALUE_GET_TAG(value))) { 25 | return jsi::Value(JS_VALUE_GET_FLOAT64(value)); 26 | } else { 27 | return jsi::Value(JS_VALUE_GET_INT(value)); 28 | } 29 | } 30 | if (JS_IsString(value)) { 31 | return QuickJSRuntime::make(new QuickJSPointerValue(jsRuntime, jsContext, value)); 32 | } 33 | if (JS_IsSymbol(value)) { 34 | return QuickJSRuntime::make(new QuickJSPointerValue(jsRuntime, jsContext, value)); 35 | } 36 | if (JS_IsObject(value)) { 37 | return QuickJSRuntime::make(new QuickJSPointerValue(jsRuntime, jsContext, value)); 38 | } 39 | if (JS_IsBigInt(jsContext, value) || JS_IsBigDecimal(value) || JS_IsBigFloat(value)) { 40 | return QuickJSRuntime::make(new QuickJSPointerValue(jsRuntime, jsContext, value)); 41 | } 42 | 43 | return jsi::Value::undefined(); 44 | } 45 | 46 | JSValue JSIValueConverter::ToJSValue( 47 | const QuickJSRuntime &constRuntime, 48 | const jsi::Value &value) { 49 | QuickJSRuntime &runtime = const_cast(constRuntime); 50 | auto context = const_cast(runtime.getJSContext()); 51 | if (value.isUndefined()) { 52 | return JS_UNDEFINED; 53 | } else if (value.isNull()) { 54 | return JS_NULL; 55 | } else if (value.isBool()) { 56 | return JS_NewBool(context, value.getBool()); 57 | } else if (value.isNumber()) { 58 | return JS_NewFloat64(context, value.getNumber()); 59 | } else if (value.isString()) { 60 | auto str = value.getString(runtime).utf8(runtime); 61 | return JS_NewStringLen(context, str.c_str(), str.size()); 62 | } else if (value.isObject()) { 63 | return ToJSObject(runtime, value.getObject(runtime)); 64 | } else if (value.isBigInt()) { 65 | return ToJSBigInt(runtime, value.getBigInt(runtime)); 66 | } else { 67 | // What are you? 68 | std::abort(); 69 | } 70 | } 71 | 72 | JSValue JSIValueConverter::ToJSString( 73 | const QuickJSRuntime &runtime, 74 | const jsi::String &string) { 75 | const QuickJSPointerValue *quickJSPointerValue = 76 | static_cast(runtime.getPointerValue(string)); 77 | auto jsObject = quickJSPointerValue->Get(runtime.getJSContext()); 78 | assert(JS_IsString(jsObject)); 79 | return jsObject; 80 | } 81 | 82 | JSValue JSIValueConverter::ToJSString( 83 | const QuickJSRuntime &runtime, 84 | const jsi::PropNameID &propName) { 85 | const QuickJSPointerValue *quickJSPointerValue = 86 | static_cast(runtime.getPointerValue(propName)); 87 | auto jsObject = quickJSPointerValue->Get(runtime.getJSContext()); 88 | assert(JS_IsString(jsObject)); 89 | return jsObject; 90 | } 91 | 92 | JSValue JSIValueConverter::ToJSSymbol( 93 | const QuickJSRuntime &runtime, 94 | const jsi::Symbol &symbol) { 95 | const QuickJSPointerValue *quickJSPointerValue = 96 | static_cast(runtime.getPointerValue(symbol)); 97 | auto jsObject = quickJSPointerValue->Get(runtime.getJSContext()); 98 | assert(JS_IsSymbol(jsObject)); 99 | return jsObject; 100 | } 101 | 102 | JSValue JSIValueConverter::ToJSObject( 103 | const QuickJSRuntime &runtime, 104 | const jsi::Object &object) { 105 | const QuickJSPointerValue *quickJSPointerValue = 106 | static_cast(runtime.getPointerValue(object)); 107 | auto jsObject = quickJSPointerValue->Get(runtime.getJSContext()); 108 | assert(JS_IsObject(jsObject)); 109 | return jsObject; 110 | } 111 | 112 | JSValue JSIValueConverter::ToJSBigInt( 113 | const QuickJSRuntime &runtime, 114 | const jsi::BigInt &bigInt) { 115 | const QuickJSPointerValue *quickJSPointerValue = 116 | static_cast(runtime.getPointerValue(bigInt)); 117 | auto jsBigInt = quickJSPointerValue->Get(runtime.getJSContext()); 118 | assert(JS_IsBigInt(runtime.getJSContext(), jsBigInt)); 119 | return jsBigInt; 120 | } 121 | 122 | JSValue JSIValueConverter::ToJSArray( 123 | const QuickJSRuntime &runtime, 124 | const jsi::Array &array) { 125 | const QuickJSPointerValue *quickJSPointerValue = 126 | static_cast(runtime.getPointerValue(array)); 127 | auto jsObject = quickJSPointerValue->Get(runtime.getJSContext()); 128 | assert(JS_IsObject(jsObject)); 129 | return jsObject; 130 | } 131 | 132 | JSValue JSIValueConverter::ToJSFunction( 133 | const QuickJSRuntime &runtime, 134 | const jsi::Function &function) { 135 | const QuickJSPointerValue *quickJSPointerValue = 136 | static_cast(runtime.getPointerValue(function)); 137 | auto jsObject = quickJSPointerValue->Get(runtime.getJSContext()); 138 | assert(JS_IsFunction(runtime.getJSContext(), jsObject)); 139 | return jsObject; 140 | } 141 | 142 | jsi::PropNameID JSIValueConverter::ToJSIPropNameID( 143 | const QuickJSRuntime &runtime, 144 | const JSAtom &property) { 145 | JSValue jsValue = JS_AtomToValue(runtime.getJSContext(), property); 146 | ScopedJSValue scopeJsValue(runtime.getJSContext(), &jsValue); 147 | return runtime.make( 148 | new QuickJSPointerValue(runtime.getJSRuntime(), runtime.getJSContext(), jsValue)); 149 | } 150 | 151 | std::string JSIValueConverter::ToSTLString( 152 | JSContext *ctx, JSAtom atom) { 153 | const char *str = JS_AtomToCString(ctx, atom); 154 | ScopedCString scopedCString(ctx, str); 155 | if (str) { 156 | return std::string(str); 157 | } 158 | return {}; 159 | } 160 | 161 | // static 162 | std::string JSIValueConverter::ToSTLString( 163 | JSContext *context, 164 | JSValueConst &string) { 165 | assert(JS_IsString(string)); 166 | const char *str = JS_ToCString(context, string); 167 | ScopedCString scopedCString(context, str); 168 | return std::string(str); 169 | } 170 | 171 | } // namespace qjs 172 | -------------------------------------------------------------------------------- /cpp/HostProxy.cpp: -------------------------------------------------------------------------------- 1 | #include "HostProxy.h" 2 | 3 | #include 4 | 5 | #include "JSIValueConverter.h" 6 | 7 | namespace qjs { 8 | 9 | JSClassID HostObjectProxy::kJSClassID = 0; 10 | 11 | JSClassDef HostObjectProxy::kJSClassDef = { 12 | .class_name = "HostObjectProxy", 13 | .finalizer = &HostObjectProxy::Finalizer, 14 | }; 15 | 16 | void *OpaqueData::GetHostData(JSValueConst this_val) { 17 | auto opaqueData = 18 | reinterpret_cast(JS_GetOpaqueUnsafe(this_val)); 19 | 20 | if (opaqueData) { 21 | return opaqueData->hostData_; 22 | } 23 | 24 | return nullptr; 25 | } 26 | 27 | const JSCFunctionListEntry HostObjectProxy::kTemplateInterceptor[] = { 28 | JS_CINTERCEPTOR_DEF( 29 | "HostObjectProxyInterceptor", 30 | &HostObjectProxy::Getter, 31 | &HostObjectProxy::Setter, 32 | nullptr, 33 | nullptr, 34 | &HostObjectProxy::Enumerator), 35 | }; 36 | 37 | HostObjectProxy::HostObjectProxy( 38 | QuickJSRuntime &runtime, 39 | std::shared_ptr hostObject) 40 | : runtime_(runtime), hostObject_(hostObject) { 41 | opaqueData_.hostData_ = this; 42 | } 43 | 44 | std::shared_ptr HostObjectProxy::GetHostObject() { 45 | return hostObject_; 46 | } 47 | 48 | OpaqueData *HostObjectProxy::GetOpaqueData() { 49 | return &opaqueData_; 50 | } 51 | 52 | JSClassID HostObjectProxy::GetClassID() { 53 | if (!kJSClassID) { 54 | JS_NewClassID(&kJSClassID); 55 | } 56 | return kJSClassID; 57 | } 58 | 59 | JSValue 60 | HostObjectProxy::Getter(JSContext *ctx, JSValueConst this_val, JSAtom name) { 61 | HostObjectProxy *hostObjectProxy = 62 | reinterpret_cast(OpaqueData::GetHostData(this_val)); 63 | 64 | assert(hostObjectProxy); 65 | 66 | QuickJSRuntime &runtime = hostObjectProxy->runtime_; 67 | jsi::PropNameID sym = JSIValueConverter::ToJSIPropNameID(runtime, name); 68 | JS_FreeAtom(ctx, name); 69 | jsi::Value ret; 70 | try { 71 | ret = hostObjectProxy->hostObject_->get(runtime, sym); 72 | } catch (const jsi::JSError &error) { 73 | JS_Throw(ctx, JSIValueConverter::ToJSValue(runtime, error.value())); 74 | return JS_UNDEFINED; 75 | } catch (const std::exception &ex) { 76 | return JS_UNDEFINED; 77 | } catch (...) { 78 | return JS_UNDEFINED; 79 | } 80 | return JSIValueConverter::ToJSValue(runtime, ret); 81 | } 82 | 83 | JSValue HostObjectProxy::Setter( 84 | JSContext *ctx, 85 | JSValueConst this_val, 86 | JSAtom name, 87 | JSValue val) { 88 | HostObjectProxy *hostObjectProxy = 89 | reinterpret_cast(OpaqueData::GetHostData(this_val)); 90 | assert(hostObjectProxy); 91 | 92 | QuickJSRuntime &runtime = hostObjectProxy->runtime_; 93 | jsi::PropNameID sym = JSIValueConverter::ToJSIPropNameID(runtime, name); 94 | JS_FreeAtom(ctx, name); 95 | try { 96 | hostObjectProxy->hostObject_->set( 97 | runtime, sym, JSIValueConverter::ToJSIValue(runtime, val)); 98 | } catch (const jsi::JSError &error) { 99 | JS_Throw(ctx, JSIValueConverter::ToJSValue(runtime, error.value())); 100 | return JS_UNDEFINED; 101 | } catch (const std::exception &ex) { 102 | return JS_UNDEFINED; 103 | } catch (...) { 104 | return JS_UNDEFINED; 105 | } 106 | return JS_UNDEFINED; 107 | } 108 | 109 | JSValue HostObjectProxy::Enumerator(JSContext *ctx, JSValueConst this_val) { 110 | HostObjectProxy *hostObjectProxy = 111 | reinterpret_cast(OpaqueData::GetHostData(this_val)); 112 | assert(hostObjectProxy); 113 | 114 | QuickJSRuntime &runtime = hostObjectProxy->runtime_; 115 | auto names = hostObjectProxy->hostObject_->getPropertyNames(runtime); 116 | 117 | auto result = JS_NewArray(ctx); 118 | for (uint32_t i = 0; i < names.size(); ++i) { 119 | JSValue value = JSIValueConverter::ToJSString(runtime, names[i]); 120 | JS_SetPropertyInt64(ctx, result, i, value); 121 | } 122 | 123 | return result; 124 | } 125 | 126 | void HostObjectProxy::Finalizer(JSRuntime *rt, JSValue val) { 127 | auto hostObjectProxy = 128 | reinterpret_cast(OpaqueData::GetHostData(val)); 129 | assert(hostObjectProxy->hostObject_.use_count() == 1); 130 | hostObjectProxy->opaqueData_.nativeState_ = nullptr; 131 | delete hostObjectProxy; 132 | } 133 | 134 | JSClassID HostFunctionProxy::kJSClassID = 0; 135 | 136 | JSClassDef HostFunctionProxy::kJSClassDef = { 137 | .class_name = "HostFunctionProxy", 138 | .finalizer = &HostFunctionProxy::Finalizer, 139 | .call = &HostFunctionProxy::FunctionCallback, 140 | }; 141 | 142 | HostFunctionProxy::HostFunctionProxy( 143 | QuickJSRuntime &runtime, 144 | jsi::HostFunctionType &&hostFunction) 145 | : runtime_(runtime), hostFunction_(std::move(hostFunction)) { 146 | opaqueData_.hostData_ = this; 147 | } 148 | 149 | jsi::HostFunctionType &HostFunctionProxy::GetHostFunction() { 150 | return hostFunction_; 151 | } 152 | 153 | OpaqueData *HostFunctionProxy::GetOpaqueData() { 154 | return &opaqueData_; 155 | } 156 | 157 | void HostFunctionProxy::Finalizer(JSRuntime *rt, JSValue val) { 158 | auto hostFunctionProxy = 159 | reinterpret_cast(OpaqueData::GetHostData(val)); 160 | hostFunctionProxy->opaqueData_.nativeState_ = nullptr; 161 | delete hostFunctionProxy; 162 | } 163 | 164 | JSClassID HostFunctionProxy::GetClassID() { 165 | if (!kJSClassID) { 166 | JS_NewClassID(&kJSClassID); 167 | } 168 | return kJSClassID; 169 | } 170 | 171 | JSValue HostFunctionProxy::FunctionCallback( 172 | JSContext *ctx, 173 | JSValueConst func_obj, 174 | JSValueConst val, 175 | int argc, 176 | JSValueConst *argv, 177 | int flags) { 178 | auto *hostFunctionProxy = 179 | reinterpret_cast(OpaqueData::GetHostData(func_obj)); 180 | 181 | auto &runtime = hostFunctionProxy->runtime_; 182 | 183 | const unsigned maxStackArgCount = 8; 184 | jsi::Value stackArgs[maxStackArgCount]; 185 | std::unique_ptr heapArgs; 186 | jsi::Value *args; 187 | if (argc > maxStackArgCount) { 188 | heapArgs = std::make_unique(argc); 189 | for (size_t i = 0; i < argc; i++) { 190 | heapArgs[i] = JSIValueConverter::ToJSIValue(runtime, argv[i]); 191 | } 192 | args = heapArgs.get(); 193 | } else { 194 | for (size_t i = 0; i < argc; i++) { 195 | stackArgs[i] = JSIValueConverter::ToJSIValue(runtime, argv[i]); 196 | } 197 | args = stackArgs; 198 | } 199 | 200 | jsi::Value thisVal(JSIValueConverter::ToJSIValue(runtime, val)); 201 | try { 202 | return JSIValueConverter::ToJSValue( 203 | runtime, 204 | hostFunctionProxy->hostFunction_(runtime, thisVal, args, argc)); 205 | } catch (const jsi::JSError &error) { 206 | JS_Throw(ctx, JSIValueConverter::ToJSValue(runtime, error.value())); 207 | return JS_UNDEFINED; 208 | } catch (const std::exception &ex) { 209 | return JS_UNDEFINED; 210 | } catch (...) { 211 | return JS_UNDEFINED; 212 | } 213 | } 214 | 215 | } // namespace qjs 216 | -------------------------------------------------------------------------------- /cpp/QuickJSRuntime.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #include "jsi/jsi.h" 8 | #include "quickjs.h" 9 | 10 | namespace jsi = facebook::jsi; 11 | 12 | namespace qjs { 13 | 14 | class QuickJSInstrumentation; 15 | class QuickJSPointerValue; 16 | 17 | struct CodeCacheItem { 18 | enum Result { 19 | UNINITIALIZED, 20 | INITIALIZED, 21 | REQUEST_UPDATE, 22 | UPDATED 23 | }; 24 | 25 | std::unique_ptr data = nullptr; 26 | size_t size = 0; 27 | Result result = UNINITIALIZED; 28 | }; 29 | 30 | class QuickJSRuntime : public jsi::Runtime { 31 | public: 32 | QuickJSRuntime(const std::string &codeCacheDir); 33 | ~QuickJSRuntime(); 34 | 35 | std::unordered_map getHeapInfo(); 36 | 37 | private: 38 | void checkAndThrowException(JSContext *context) const; 39 | void loadCodeCache(CodeCacheItem &codeCacheItem, const std::string& url, const char *source, 40 | size_t size); 41 | void updateCodeCache(CodeCacheItem &codeCacheItem, const std::string& url, const char *source, 42 | size_t size); 43 | 44 | 45 | // 46 | // jsi::Runtime implementations 47 | // 48 | public: 49 | jsi::Value evaluateJavaScript( 50 | const std::shared_ptr &buffer, 51 | const std::string &sourceURL) override; 52 | 53 | std::shared_ptr prepareJavaScript( 54 | const std::shared_ptr &buffer, 55 | std::string sourceURL) override; 56 | jsi::Value evaluatePreparedJavaScript( 57 | const std::shared_ptr &js) override; 58 | 59 | jsi::Object global() override; 60 | std::string description() override; 61 | bool isInspectable() override; 62 | 63 | jsi::Instrumentation& instrumentation() override; 64 | 65 | protected: 66 | PointerValue *cloneSymbol(const Runtime::PointerValue *pv) override; 67 | PointerValue* cloneBigInt(const Runtime::PointerValue* pv) override; 68 | PointerValue *cloneString(const Runtime::PointerValue *pv) override; 69 | PointerValue *cloneObject(const Runtime::PointerValue *pv) override; 70 | PointerValue *clonePropNameID(const Runtime::PointerValue *pv) override; 71 | 72 | bool bigintIsInt64(const jsi::BigInt&) override; 73 | bool bigintIsUint64(const jsi::BigInt&) override; 74 | uint64_t truncate(const jsi::BigInt&) override; 75 | 76 | bool hasNativeState(const jsi::Object&) override; 77 | std::shared_ptr getNativeState(const jsi::Object&) override; 78 | void setNativeState( 79 | const jsi::Object&, 80 | std::shared_ptr state) override; 81 | 82 | bool drainMicrotasks(int maxMicrotasksHint) override; 83 | 84 | jsi::PropNameID createPropNameIDFromSymbol(const jsi::Symbol& sym) override; 85 | jsi::PropNameID createPropNameIDFromAscii(const char *str, size_t length) 86 | override; 87 | jsi::PropNameID createPropNameIDFromUtf8(const uint8_t *utf8, size_t length) 88 | override; 89 | jsi::PropNameID createPropNameIDFromString(const jsi::String &str) override; 90 | std::string utf8(const jsi::PropNameID &) override; 91 | bool compare(const jsi::PropNameID &, const jsi::PropNameID &) override; 92 | 93 | jsi::BigInt createBigIntFromInt64(int64_t) override; 94 | jsi::BigInt createBigIntFromUint64(uint64_t) override; 95 | jsi::String bigintToString(const jsi::BigInt&, int) override; 96 | std::string symbolToString(const jsi::Symbol &) override; 97 | 98 | jsi::String createStringFromAscii(const char *str, size_t length) override; 99 | jsi::String createStringFromUtf8(const uint8_t *utf8, size_t length) override; 100 | std::string utf8(const jsi::String &) override; 101 | 102 | jsi::Object createObject() override; 103 | jsi::Object createObject( 104 | std::shared_ptr hostObject) override; 105 | std::shared_ptr getHostObject(const jsi::Object &) override; 106 | jsi::HostFunctionType &getHostFunction(const jsi::Function &) override; 107 | 108 | jsi::Value getProperty(const jsi::Object &, const jsi::PropNameID &name) 109 | override; 110 | jsi::Value getProperty(const jsi::Object &, const jsi::String &name) override; 111 | bool hasProperty(const jsi::Object &, const jsi::PropNameID &name) override; 112 | bool hasProperty(const jsi::Object &, const jsi::String &name) override; 113 | void setPropertyValue( 114 | jsi::Object &, 115 | const jsi::PropNameID &name, 116 | const jsi::Value &value) override; 117 | void setPropertyValue(jsi::Object &, 118 | const jsi::String &name, 119 | const jsi::Value &value) override; 120 | 121 | bool isArray(const jsi::Object &) const override; 122 | bool isArrayBuffer(const jsi::Object &) const override; 123 | bool isFunction(const jsi::Object &) const override; 124 | bool isHostObject(const jsi::Object &) const override; 125 | bool isHostFunction(const jsi::Function &) const override; 126 | jsi::Array getPropertyNames(const jsi::Object &) override; 127 | 128 | jsi::WeakObject createWeakObject(const jsi::Object &) override; 129 | jsi::Value lockWeakObject(jsi::WeakObject &) override; 130 | 131 | jsi::Array createArray(size_t length) override; 132 | jsi::ArrayBuffer createArrayBuffer( 133 | std::shared_ptr buffer) override; 134 | size_t size(const jsi::Array &) override; 135 | size_t size(const jsi::ArrayBuffer &) override; 136 | uint8_t *data(const jsi::ArrayBuffer &) override; 137 | jsi::Value getValueAtIndex(const jsi::Array &, size_t i) override; 138 | void setValueAtIndexImpl(jsi::Array &, size_t i, const jsi::Value &value) 139 | override; 140 | 141 | jsi::Function createFunctionFromHostFunction( 142 | const jsi::PropNameID &name, 143 | unsigned int paramCount, 144 | jsi::HostFunctionType func) override; 145 | jsi::Value call( 146 | const jsi::Function &, 147 | const jsi::Value &jsThis, 148 | const jsi::Value *args, 149 | size_t count) override; 150 | jsi::Value callAsConstructor( 151 | const jsi::Function &, 152 | const jsi::Value *args, 153 | size_t count) override; 154 | bool strictEquals(const jsi::BigInt& a, const jsi::BigInt& b) const override; 155 | 156 | bool strictEquals(const jsi::Symbol &a, const jsi::Symbol &b) const override; 157 | bool strictEquals(const jsi::String &a, const jsi::String &b) const override; 158 | bool strictEquals(const jsi::Object &a, const jsi::Object &b) const override; 159 | 160 | bool instanceOf(const jsi::Object &o, const jsi::Function &f) override; 161 | 162 | private: 163 | friend class QuickJSPointerValue; 164 | friend class JSIValueConverter; 165 | 166 | public: 167 | JSRuntime *getJSRuntime() const { 168 | return runtime_; 169 | }; 170 | 171 | JSContext *getJSContext() const { 172 | return context_; 173 | }; 174 | 175 | private: 176 | JSRuntime *runtime_; 177 | JSContext *context_; 178 | std::string codeCacheDir_; 179 | 180 | std::unique_ptr instrumentation_; 181 | }; 182 | 183 | } // namespace qjs 184 | -------------------------------------------------------------------------------- /example/android/app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: "com.android.application" 2 | apply plugin: "com.facebook.react" 3 | 4 | import com.android.build.OutputFile 5 | 6 | /** 7 | * This is the configuration block to customize your React Native Android app. 8 | * By default you don't need to apply any configuration, just uncomment the lines you need. 9 | */ 10 | react { 11 | /* Folders */ 12 | // The root of your project, i.e. where "package.json" lives. Default is '..' 13 | // root = file("../") 14 | // The folder where the react-native NPM package is. Default is ../node_modules/react-native 15 | // reactNativeDir = file("../node_modules/react-native") 16 | // The folder where the react-native Codegen package is. Default is ../node_modules/react-native-codegen 17 | // codegenDir = file("../node_modules/react-native-codegen") 18 | // The cli.js file which is the React Native CLI entrypoint. Default is ../node_modules/react-native/cli.js 19 | // cliFile = file("../node_modules/react-native/cli.js") 20 | 21 | /* Variants */ 22 | // The list of variants to that are debuggable. For those we're going to 23 | // skip the bundling of the JS bundle and the assets. By default is just 'debug'. 24 | // If you add flavors like lite, prod, etc. you'll have to list your debuggableVariants. 25 | // debuggableVariants = ["liteDebug", "prodDebug"] 26 | 27 | /* Bundling */ 28 | // A list containing the node command and its flags. Default is just 'node'. 29 | nodeExecutableAndArgs = ["/Users/liubojie/.nvm/versions/node/v14.15.0/bin/node"] 30 | // 31 | // The command to run when bundling. By default is 'bundle' 32 | // bundleCommand = "ram-bundle" 33 | // 34 | // The path to the CLI configuration file. Default is empty. 35 | // bundleConfig = file(../rn-cli.config.js) 36 | // 37 | // The name of the generated asset file containing your JS bundle 38 | // bundleAssetName = "MyApplication.android.bundle" 39 | // 40 | // The entry file for bundle generation. Default is 'index.android.js' or 'index.js' 41 | // entryFile = file("../js/MyApplication.android.js") 42 | // 43 | // A list of extra flags to pass to the 'bundle' commands. 44 | // See https://github.com/react-native-community/cli/blob/main/docs/commands.md#bundle 45 | // extraPackagerArgs = [] 46 | 47 | /* Hermes Commands */ 48 | // The hermes compiler command to run. By default it is 'hermesc' 49 | // hermesCommand = "$rootDir/my-custom-hermesc/bin/hermesc" 50 | // 51 | // The list of flags to pass to the Hermes compiler. By default is "-O", "-output-source-map" 52 | // hermesFlags = ["-O", "-output-source-map"] 53 | } 54 | 55 | /** 56 | * Set this to true to create four separate APKs instead of one, 57 | * one for each native architecture. This is useful if you don't 58 | * use App Bundles (https://developer.android.com/guide/app-bundle/) 59 | * and want to have separate APKs to upload to the Play Store. 60 | */ 61 | def enableSeparateBuildPerCPUArchitecture = false 62 | 63 | /** 64 | * Set this to true to Run Proguard on Release builds to minify the Java bytecode. 65 | */ 66 | def enableProguardInReleaseBuilds = false 67 | 68 | /** 69 | * The preferred build flavor of JavaScriptCore (JSC) 70 | * 71 | * For example, to use the international variant, you can use: 72 | * `def jscFlavor = 'org.webkit:android-jsc-intl:+'` 73 | * 74 | * The international variant includes ICU i18n library and necessary data 75 | * allowing to use e.g. `Date.toLocaleString` and `String.localeCompare` that 76 | * give correct results when using with locales other than en-US. Note that 77 | * this variant is about 6MiB larger per architecture than default. 78 | */ 79 | def jscFlavor = 'org.webkit:android-jsc:+' 80 | 81 | /** 82 | * Private function to get the list of Native Architectures you want to build. 83 | * This reads the value from reactNativeArchitectures in your gradle.properties 84 | * file and works together with the --active-arch-only flag of react-native run-android. 85 | */ 86 | def reactNativeArchitectures() { 87 | def value = project.getProperties().get("reactNativeArchitectures") 88 | return value ? value.split(",") : ["armeabi-v7a", "x86", "x86_64", "arm64-v8a"] 89 | } 90 | 91 | android { 92 | ndkVersion rootProject.ext.ndkVersion 93 | 94 | compileSdkVersion rootProject.ext.compileSdkVersion 95 | 96 | namespace "com.quickjsexample" 97 | defaultConfig { 98 | applicationId "com.quickjsexample" 99 | minSdkVersion rootProject.ext.minSdkVersion 100 | targetSdkVersion rootProject.ext.targetSdkVersion 101 | versionCode 1 102 | versionName "1.0" 103 | } 104 | 105 | splits { 106 | abi { 107 | reset() 108 | enable enableSeparateBuildPerCPUArchitecture 109 | universalApk false // If true, also generate a universal APK 110 | include (*reactNativeArchitectures()) 111 | } 112 | } 113 | signingConfigs { 114 | debug { 115 | storeFile file('debug.keystore') 116 | storePassword 'android' 117 | keyAlias 'androiddebugkey' 118 | keyPassword 'android' 119 | } 120 | } 121 | buildTypes { 122 | debug { 123 | signingConfig signingConfigs.debug 124 | } 125 | release { 126 | // Caution! In production, you need to generate your own keystore file. 127 | // see https://reactnative.dev/docs/signed-apk-android. 128 | signingConfig signingConfigs.debug 129 | minifyEnabled enableProguardInReleaseBuilds 130 | proguardFiles getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro" 131 | } 132 | } 133 | 134 | // applicationVariants are e.g. debug, release 135 | applicationVariants.all { variant -> 136 | variant.outputs.each { output -> 137 | // For each separate APK per architecture, set a unique version code as described here: 138 | // https://developer.android.com/studio/build/configure-apk-splits.html 139 | // Example: versionCode 1 will generate 1001 for armeabi-v7a, 1002 for x86, etc. 140 | def versionCodes = ["armeabi-v7a": 1, "x86": 2, "arm64-v8a": 3, "x86_64": 4] 141 | def abi = output.getFilter(OutputFile.ABI) 142 | if (abi != null) { // null for the universal-debug, universal-release variants 143 | output.versionCodeOverride = 144 | defaultConfig.versionCode * 1000 + versionCodes.get(abi) 145 | } 146 | 147 | } 148 | } 149 | } 150 | 151 | dependencies { 152 | // The version of react-native is set by the React Native Gradle Plugin 153 | implementation("com.facebook.react:react-android") 154 | 155 | implementation("androidx.swiperefreshlayout:swiperefreshlayout:1.0.0") 156 | 157 | debugImplementation("com.facebook.flipper:flipper:${FLIPPER_VERSION}") 158 | debugImplementation("com.facebook.flipper:flipper-network-plugin:${FLIPPER_VERSION}") { 159 | exclude group:'com.squareup.okhttp3', module:'okhttp' 160 | } 161 | 162 | debugImplementation("com.facebook.flipper:flipper-fresco-plugin:${FLIPPER_VERSION}") 163 | if (hermesEnabled.toBoolean()) { 164 | implementation("com.facebook.react:hermes-android") 165 | } else { 166 | implementation jscFlavor 167 | } 168 | } 169 | 170 | apply from: file("../../node_modules/@react-native-community/cli-platform-android/native_modules.gradle"); applyNativeModulesAppBuildGradle(project) 171 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # react-native-quickjs 2 | 3 | An lightweight javascript engine for RN. 4 | 5 | ## Run the example 6 | 7 | ```sh 8 | cd example && yarn 9 | yarn android 10 | yarn ios 11 | ``` 12 | 13 | ## Run with existing project 14 | 15 | **Only tested for React Native >= 0.71.8. Please create an issue if it does not work for other version. Will fix it ASAP.** 16 | 17 | 1. Install dependency 18 | 19 | ```sh 20 | yarn add react-native-quickjs 21 | ``` 22 | 23 | ##### For Android 24 | 25 | 2. Override "getJavaScriptExecutorFactory" to use QuickJS as JS runtime 26 | 27 | ``` diff 28 | diff --git a/android/app/src/main/java/com/awesomeproject/MainApplication.java b/android/app/src/main/java/com/awesomeproject/MainApplication.java 29 | index 105e48c..b309fea 100644 30 | --- a/android/app/src/main/java/com/awesomeproject/MainApplication.java 31 | +++ b/android/app/src/main/java/com/awesomeproject/MainApplication.java 32 | @@ -5,11 +5,16 @@ import com.facebook.react.PackageList; 33 | import com.facebook.react.ReactApplication; 34 | import com.facebook.react.ReactNativeHost; 35 | import com.facebook.react.ReactPackage; 36 | +import com.facebook.react.bridge.JavaScriptExecutorFactory; 37 | import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint; 38 | import com.facebook.react.defaults.DefaultReactNativeHost; 39 | import com.facebook.soloader.SoLoader; 40 | +import com.quickjs.QuickJSExecutorFactory; 41 | + 42 | import java.util.List; 43 | 44 | +import androidx.annotation.Nullable; 45 | + 46 | public class MainApplication extends Application implements ReactApplication { 47 | 48 | private final ReactNativeHost mReactNativeHost = 49 | @@ -42,6 +47,13 @@ public class MainApplication extends Application implements ReactApplication { 50 | protected Boolean isHermesEnabled() { 51 | return BuildConfig.IS_HERMES_ENABLED; 52 | } 53 | + 54 | + @Nullable 55 | + @Override 56 | + protected JavaScriptExecutorFactory getJavaScriptExecutorFactory() { 57 | + // Pass empty string to disable code cache. 58 | + return new QuickJSExecutorFactory(getApplication().getCacheDir().getAbsolutePath() + "/qjs"); 59 | + } 60 | }; 61 | 62 | @Override 63 | 64 | ``` 65 | 66 | 3. Disable Hermes and its bundling procedure 67 | 68 | ``` diff 69 | --- a/android/gradle.properties 70 | +++ b/android/gradle.properties 71 | @@ -41,4 +41,4 @@ newArchEnabled=false 72 | 73 | # Use this property to enable or disable the Hermes JS engine. 74 | # If set to false, you will be using JSC instead. 75 | -hermesEnabled=true 76 | +hermesEnabled=false 77 | ``` 78 | 79 | 4. (Optional) Exclude unused libraries to reduce APK size 80 | 81 | ``` diff 82 | --- a/android/app/build.gradle 83 | +++ b/android/app/build.gradle 84 | @@ -161,11 +161,18 @@ android { 85 | } 86 | } 87 | } 88 | + 89 | + packagingOptions { 90 | + // Make sure libjsc.so does not packed in APK 91 | + exclude "**/libjsc.so" 92 | + } 93 | } 94 | ``` 95 | 96 | 5. Run your application 97 | 98 | a. For Debug. 99 | ``` sh 100 | yarn android 101 | ``` 102 | b. For Release. Run this command in the project root. 103 | ``` sh 104 | cd android && ./gradlew installRelease 105 | ``` 106 | 107 | ##### For iOS 108 | 109 | 2. Disable Hermes and its bundling procedure 110 | 111 | ``` sh 112 | USE_HERMES=0 pod install 113 | ``` 114 | 115 | 3. Use "QuickJSExecutorFactory" in your application 116 | 117 | ``` diff 118 | diff --git a/ios/AwesomeProject/AppDelegate.mm b/ios/AwesomeProject/AppDelegate.mm 119 | index 029aa44..2f579c3 100644 120 | --- a/ios/AwesomeProject/AppDelegate.mm 121 | +++ b/ios/AwesomeProject/AppDelegate.mm 122 | @@ -2,6 +2,27 @@ 123 | 124 | #import 125 | 126 | +#import 127 | +#import 128 | +#import 129 | +#ifndef RCT_USE_HERMES 130 | +#if __has_include() 131 | +#define RCT_USE_HERMES 1 132 | +#else 133 | +#define RCT_USE_HERMES 0 134 | +#endif 135 | +#endif 136 | + 137 | +#if RCT_USE_HERMES 138 | +#import 139 | +//#else 140 | +//#import 141 | +#endif 142 | +#import 143 | + 144 | +@interface AppDelegate () 145 | +@end 146 | + 147 | @implementation AppDelegate 148 | 149 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions 150 | @@ -14,6 +35,20 @@ 151 | return [super application:application didFinishLaunchingWithOptions:launchOptions]; 152 | } 153 | 154 | +#pragma mark - RCTCxxBridgeDelegate 155 | + 156 | +- (std::unique_ptr)jsExecutorFactoryForBridge:(RCTBridge *)bridge 157 | +{ 158 | + auto installBindings = facebook::react::RCTJSIExecutorRuntimeInstaller(nullptr); 159 | +#if RCT_USE_HERMES 160 | + return std::make_unique(installBindings); 161 | +#else 162 | +// return std::make_unique(installBindings); 163 | + auto cacheDir = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES).firstObject UTF8String]; 164 | + return std::make_unique(installBindings, ""); // pass empty string to disable code cache 165 | +#endif 166 | +} 167 | + 168 | - (NSURL *)sourceURLForBridge:(RCTBridge *)bridge 169 | { 170 | #if DEBUG 171 | ``` 172 | 173 | 5. Run your application 174 | 175 | a. For Debug. Just run it in XCode. 176 | 177 | b. For Release. Run this command in the project root. 178 | ``` sh 179 | npx react-native run-ios --mode Release 180 | ``` 181 | 182 | ## Performance 183 | 184 | 1. [Example performance](./docs/DemoPerformance.md) 185 | 2. Real world bundle performance 186 | 1. We tested QuickJS/V8/JSC with an online bundle size of 1.43M. 187 | 2. On Android, we enabled code cache for both QuickJS and V8. For TTI, QuickJS is 5-20% slower than V8. For PSS memory, QuickJS is 40-(-5)% lower than V8. 188 | 3. On iOS, TTI using QuickJS is 15% slower than JSC without code cache. With code cache, QuickJS is 15% faster than JSC and 50% lower than JSC in footprint memory usage. 189 | 190 | ## ESx Compatibility 191 | 192 | As listed on official QuickJS [website](https://bellard.org/quickjs/). QuickJS passed 82% of ECMA-262 tests. Meanwhile V8 passed 86% and JSC passed 85% in 2022. If internationalization tests which accounts for nearly 3% are excluded, QuickJS is fairly close to V8 and JSC. You can checkout [https://test262.report/](https://test262.report/) for failed cases just in case. 193 | 194 | ## Why would I choose QuickJS for React Native 195 | 196 | IMHO: 197 | 198 | 1. On Android, QuickJS is slower than V8 in most of my tests. Although it has advantages in memory and binary size which is important on some resource-limited devices. Also QuickJS is benefit from its startup time for some simple bundles. 199 | 2. On iOS, QuickJS simply better than JSC with code cache and worst than JSC without code cache. 200 | 3. QuickJS is easy to customize when it compared with V8 or Hermes. Like adding some high performance builtin functions or customized classes. It depends on your business. 201 | 4. Currently QuickJS have no inspector. But it seems some open source projects have made it worked. Anyway it needs extra works to support inspector in the future. 202 | 203 | ## Contributing 204 | 205 | See the [contributing guide](CONTRIBUTING.md) to learn how to contribute to the repository and the development workflow. 206 | 207 | Pull requests are always welcome. 208 | 209 | ## License 210 | 211 | MIT 212 | 213 | --- 214 | 215 | Made with [create-react-native-library](https://github.com/callstack/react-native-builder-bob) 216 | -------------------------------------------------------------------------------- /cpp/engine/cutils.h: -------------------------------------------------------------------------------- 1 | /* 2 | * C utilities 3 | * 4 | * Copyright (c) 2017 Fabrice Bellard 5 | * Copyright (c) 2018 Charlie Gordon 6 | * 7 | * Permission is hereby granted, free of charge, to any person obtaining a copy 8 | * of this software and associated documentation files (the "Software"), to deal 9 | * in the Software without restriction, including without limitation the rights 10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | * copies of the Software, and to permit persons to whom the Software is 12 | * furnished to do so, subject to the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be included in 15 | * all copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 20 | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 | * THE SOFTWARE. 24 | */ 25 | #ifndef CUTILS_H 26 | #define CUTILS_H 27 | 28 | #include 29 | #include 30 | 31 | /* set if CPU is big endian */ 32 | #undef WORDS_BIGENDIAN 33 | 34 | #define likely(x) __builtin_expect(!!(x), 1) 35 | #define unlikely(x) __builtin_expect(!!(x), 0) 36 | #define force_inline inline __attribute__((always_inline)) 37 | #define no_inline __attribute__((noinline)) 38 | #define __maybe_unused __attribute__((unused)) 39 | 40 | #define xglue(x, y) x ## y 41 | #define glue(x, y) xglue(x, y) 42 | #define stringify(s) tostring(s) 43 | #define tostring(s) #s 44 | 45 | #ifndef offsetof 46 | #define offsetof(type, field) ((size_t) &((type *)0)->field) 47 | #endif 48 | #ifndef countof 49 | #define countof(x) (sizeof(x) / sizeof((x)[0])) 50 | #endif 51 | 52 | typedef int BOOL; 53 | 54 | #ifndef FALSE 55 | enum { 56 | FALSE = 0, 57 | TRUE = 1, 58 | }; 59 | #endif 60 | 61 | void pstrcpy(char *buf, int buf_size, const char *str); 62 | char *pstrcat(char *buf, int buf_size, const char *s); 63 | int strstart(const char *str, const char *val, const char **ptr); 64 | int has_suffix(const char *str, const char *suffix); 65 | 66 | static inline int max_int(int a, int b) 67 | { 68 | if (a > b) 69 | return a; 70 | else 71 | return b; 72 | } 73 | 74 | static inline int min_int(int a, int b) 75 | { 76 | if (a < b) 77 | return a; 78 | else 79 | return b; 80 | } 81 | 82 | static inline uint32_t max_uint32(uint32_t a, uint32_t b) 83 | { 84 | if (a > b) 85 | return a; 86 | else 87 | return b; 88 | } 89 | 90 | static inline uint32_t min_uint32(uint32_t a, uint32_t b) 91 | { 92 | if (a < b) 93 | return a; 94 | else 95 | return b; 96 | } 97 | 98 | static inline int64_t max_int64(int64_t a, int64_t b) 99 | { 100 | if (a > b) 101 | return a; 102 | else 103 | return b; 104 | } 105 | 106 | static inline int64_t min_int64(int64_t a, int64_t b) 107 | { 108 | if (a < b) 109 | return a; 110 | else 111 | return b; 112 | } 113 | 114 | /* WARNING: undefined if a = 0 */ 115 | static inline int clz32(unsigned int a) 116 | { 117 | return __builtin_clz(a); 118 | } 119 | 120 | /* WARNING: undefined if a = 0 */ 121 | static inline int clz64(uint64_t a) 122 | { 123 | return __builtin_clzll(a); 124 | } 125 | 126 | /* WARNING: undefined if a = 0 */ 127 | static inline int ctz32(unsigned int a) 128 | { 129 | return __builtin_ctz(a); 130 | } 131 | 132 | /* WARNING: undefined if a = 0 */ 133 | static inline int ctz64(uint64_t a) 134 | { 135 | return __builtin_ctzll(a); 136 | } 137 | 138 | struct __attribute__((packed)) packed_u64 { 139 | uint64_t v; 140 | }; 141 | 142 | struct __attribute__((packed)) packed_u32 { 143 | uint32_t v; 144 | }; 145 | 146 | struct __attribute__((packed)) packed_u16 { 147 | uint16_t v; 148 | }; 149 | 150 | static inline uint64_t get_u64(const uint8_t *tab) 151 | { 152 | return ((const struct packed_u64 *)tab)->v; 153 | } 154 | 155 | static inline int64_t get_i64(const uint8_t *tab) 156 | { 157 | return (int64_t)((const struct packed_u64 *)tab)->v; 158 | } 159 | 160 | static inline void put_u64(uint8_t *tab, uint64_t val) 161 | { 162 | ((struct packed_u64 *)tab)->v = val; 163 | } 164 | 165 | static inline uint32_t get_u32(const uint8_t *tab) 166 | { 167 | return ((const struct packed_u32 *)tab)->v; 168 | } 169 | 170 | static inline int32_t get_i32(const uint8_t *tab) 171 | { 172 | return (int32_t)((const struct packed_u32 *)tab)->v; 173 | } 174 | 175 | static inline void put_u32(uint8_t *tab, uint32_t val) 176 | { 177 | ((struct packed_u32 *)tab)->v = val; 178 | } 179 | 180 | static inline uint32_t get_u16(const uint8_t *tab) 181 | { 182 | return ((const struct packed_u16 *)tab)->v; 183 | } 184 | 185 | static inline int32_t get_i16(const uint8_t *tab) 186 | { 187 | return (int16_t)((const struct packed_u16 *)tab)->v; 188 | } 189 | 190 | static inline void put_u16(uint8_t *tab, uint16_t val) 191 | { 192 | ((struct packed_u16 *)tab)->v = val; 193 | } 194 | 195 | static inline uint32_t get_u8(const uint8_t *tab) 196 | { 197 | return *tab; 198 | } 199 | 200 | static inline int32_t get_i8(const uint8_t *tab) 201 | { 202 | return (int8_t)*tab; 203 | } 204 | 205 | static inline void put_u8(uint8_t *tab, uint8_t val) 206 | { 207 | *tab = val; 208 | } 209 | 210 | static inline uint16_t bswap16(uint16_t x) 211 | { 212 | return (x >> 8) | (x << 8); 213 | } 214 | 215 | static inline uint32_t bswap32(uint32_t v) 216 | { 217 | return ((v & 0xff000000) >> 24) | ((v & 0x00ff0000) >> 8) | 218 | ((v & 0x0000ff00) << 8) | ((v & 0x000000ff) << 24); 219 | } 220 | 221 | static inline uint64_t bswap64(uint64_t v) 222 | { 223 | return ((v & ((uint64_t)0xff << (7 * 8))) >> (7 * 8)) | 224 | ((v & ((uint64_t)0xff << (6 * 8))) >> (5 * 8)) | 225 | ((v & ((uint64_t)0xff << (5 * 8))) >> (3 * 8)) | 226 | ((v & ((uint64_t)0xff << (4 * 8))) >> (1 * 8)) | 227 | ((v & ((uint64_t)0xff << (3 * 8))) << (1 * 8)) | 228 | ((v & ((uint64_t)0xff << (2 * 8))) << (3 * 8)) | 229 | ((v & ((uint64_t)0xff << (1 * 8))) << (5 * 8)) | 230 | ((v & ((uint64_t)0xff << (0 * 8))) << (7 * 8)); 231 | } 232 | 233 | /* XXX: should take an extra argument to pass slack information to the caller */ 234 | typedef void *DynBufReallocFunc(void *opaque, void *ptr, size_t size); 235 | 236 | typedef struct DynBuf { 237 | uint8_t *buf; 238 | size_t size; 239 | size_t allocated_size; 240 | BOOL error; /* true if a memory allocation error occurred */ 241 | DynBufReallocFunc *realloc_func; 242 | void *opaque; /* for realloc_func */ 243 | } DynBuf; 244 | 245 | void dbuf_init(DynBuf *s); 246 | void dbuf_init2(DynBuf *s, void *opaque, DynBufReallocFunc *realloc_func); 247 | int dbuf_realloc(DynBuf *s, size_t new_size); 248 | int dbuf_write(DynBuf *s, size_t offset, const uint8_t *data, size_t len); 249 | int dbuf_put(DynBuf *s, const uint8_t *data, size_t len); 250 | int dbuf_put_self(DynBuf *s, size_t offset, size_t len); 251 | int dbuf_putc(DynBuf *s, uint8_t c); 252 | int dbuf_putstr(DynBuf *s, const char *str); 253 | static inline int dbuf_put_u16(DynBuf *s, uint16_t val) 254 | { 255 | return dbuf_put(s, (uint8_t *)&val, 2); 256 | } 257 | static inline int dbuf_put_u32(DynBuf *s, uint32_t val) 258 | { 259 | return dbuf_put(s, (uint8_t *)&val, 4); 260 | } 261 | static inline int dbuf_put_u64(DynBuf *s, uint64_t val) 262 | { 263 | return dbuf_put(s, (uint8_t *)&val, 8); 264 | } 265 | int __attribute__((format(printf, 2, 3))) dbuf_printf(DynBuf *s, 266 | const char *fmt, ...); 267 | void dbuf_free(DynBuf *s); 268 | static inline BOOL dbuf_error(DynBuf *s) { 269 | return s->error; 270 | } 271 | static inline void dbuf_set_error(DynBuf *s) 272 | { 273 | s->error = TRUE; 274 | } 275 | 276 | #define UTF8_CHAR_LEN_MAX 6 277 | 278 | int unicode_to_utf8(uint8_t *buf, unsigned int c); 279 | int unicode_from_utf8(const uint8_t *p, int max_len, const uint8_t **pp); 280 | 281 | static inline int from_hex(int c) 282 | { 283 | if (c >= '0' && c <= '9') 284 | return c - '0'; 285 | else if (c >= 'A' && c <= 'F') 286 | return c - 'A' + 10; 287 | else if (c >= 'a' && c <= 'f') 288 | return c - 'a' + 10; 289 | else 290 | return -1; 291 | } 292 | 293 | void rqsort(void *base, size_t nmemb, size_t size, 294 | int (*cmp)(const void *, const void *, void *), 295 | void *arg); 296 | 297 | #endif /* CUTILS_H */ 298 | -------------------------------------------------------------------------------- /example/android/gradlew: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # 4 | # Copyright © 2015-2021 the original authors. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # https://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | 19 | ############################################################################## 20 | # 21 | # Gradle start up script for POSIX generated by Gradle. 22 | # 23 | # Important for running: 24 | # 25 | # (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is 26 | # noncompliant, but you have some other compliant shell such as ksh or 27 | # bash, then to run this script, type that shell name before the whole 28 | # command line, like: 29 | # 30 | # ksh Gradle 31 | # 32 | # Busybox and similar reduced shells will NOT work, because this script 33 | # requires all of these POSIX shell features: 34 | # * functions; 35 | # * expansions «$var», «${var}», «${var:-default}», «${var+SET}», 36 | # «${var#prefix}», «${var%suffix}», and «$( cmd )»; 37 | # * compound commands having a testable exit status, especially «case»; 38 | # * various built-in commands including «command», «set», and «ulimit». 39 | # 40 | # Important for patching: 41 | # 42 | # (2) This script targets any POSIX shell, so it avoids extensions provided 43 | # by Bash, Ksh, etc; in particular arrays are avoided. 44 | # 45 | # The "traditional" practice of packing multiple parameters into a 46 | # space-separated string is a well documented source of bugs and security 47 | # problems, so this is (mostly) avoided, by progressively accumulating 48 | # options in "$@", and eventually passing that to Java. 49 | # 50 | # Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, 51 | # and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; 52 | # see the in-line comments for details. 53 | # 54 | # There are tweaks for specific operating systems such as AIX, CygWin, 55 | # Darwin, MinGW, and NonStop. 56 | # 57 | # (3) This script is generated from the Groovy template 58 | # https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt 59 | # within the Gradle project. 60 | # 61 | # You can find Gradle at https://github.com/gradle/gradle/. 62 | # 63 | ############################################################################## 64 | 65 | # Attempt to set APP_HOME 66 | 67 | # Resolve links: $0 may be a link 68 | app_path=$0 69 | 70 | # Need this for daisy-chained symlinks. 71 | while 72 | APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path 73 | [ -h "$app_path" ] 74 | do 75 | ls=$( ls -ld "$app_path" ) 76 | link=${ls#*' -> '} 77 | case $link in #( 78 | /*) app_path=$link ;; #( 79 | *) app_path=$APP_HOME$link ;; 80 | esac 81 | done 82 | 83 | APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit 84 | 85 | APP_NAME="Gradle" 86 | APP_BASE_NAME=${0##*/} 87 | 88 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 89 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' 90 | 91 | # Use the maximum available, or set MAX_FD != -1 to use that value. 92 | MAX_FD=maximum 93 | 94 | warn () { 95 | echo "$*" 96 | } >&2 97 | 98 | die () { 99 | echo 100 | echo "$*" 101 | echo 102 | exit 1 103 | } >&2 104 | 105 | # OS specific support (must be 'true' or 'false'). 106 | cygwin=false 107 | msys=false 108 | darwin=false 109 | nonstop=false 110 | case "$( uname )" in #( 111 | CYGWIN* ) cygwin=true ;; #( 112 | Darwin* ) darwin=true ;; #( 113 | MSYS* | MINGW* ) msys=true ;; #( 114 | NONSTOP* ) nonstop=true ;; 115 | esac 116 | 117 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 118 | 119 | 120 | # Determine the Java command to use to start the JVM. 121 | if [ -n "$JAVA_HOME" ] ; then 122 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 123 | # IBM's JDK on AIX uses strange locations for the executables 124 | JAVACMD=$JAVA_HOME/jre/sh/java 125 | else 126 | JAVACMD=$JAVA_HOME/bin/java 127 | fi 128 | if [ ! -x "$JAVACMD" ] ; then 129 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 130 | 131 | Please set the JAVA_HOME variable in your environment to match the 132 | location of your Java installation." 133 | fi 134 | else 135 | JAVACMD=java 136 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 137 | 138 | Please set the JAVA_HOME variable in your environment to match the 139 | location of your Java installation." 140 | fi 141 | 142 | # Increase the maximum file descriptors if we can. 143 | if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then 144 | case $MAX_FD in #( 145 | max*) 146 | MAX_FD=$( ulimit -H -n ) || 147 | warn "Could not query maximum file descriptor limit" 148 | esac 149 | case $MAX_FD in #( 150 | '' | soft) :;; #( 151 | *) 152 | ulimit -n "$MAX_FD" || 153 | warn "Could not set maximum file descriptor limit to $MAX_FD" 154 | esac 155 | fi 156 | 157 | # Collect all arguments for the java command, stacking in reverse order: 158 | # * args from the command line 159 | # * the main class name 160 | # * -classpath 161 | # * -D...appname settings 162 | # * --module-path (only if needed) 163 | # * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. 164 | 165 | # For Cygwin or MSYS, switch paths to Windows format before running java 166 | if "$cygwin" || "$msys" ; then 167 | APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) 168 | CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) 169 | 170 | JAVACMD=$( cygpath --unix "$JAVACMD" ) 171 | 172 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 173 | for arg do 174 | if 175 | case $arg in #( 176 | -*) false ;; # don't mess with options #( 177 | /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath 178 | [ -e "$t" ] ;; #( 179 | *) false ;; 180 | esac 181 | then 182 | arg=$( cygpath --path --ignore --mixed "$arg" ) 183 | fi 184 | # Roll the args list around exactly as many times as the number of 185 | # args, so each arg winds up back in the position where it started, but 186 | # possibly modified. 187 | # 188 | # NB: a `for` loop captures its iteration list before it begins, so 189 | # changing the positional parameters here affects neither the number of 190 | # iterations, nor the values presented in `arg`. 191 | shift # remove old arg 192 | set -- "$@" "$arg" # push replacement arg 193 | done 194 | fi 195 | 196 | # Collect all arguments for the java command; 197 | # * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of 198 | # shell script including quotes and variable substitutions, so put them in 199 | # double quotes to make sure that they get re-expanded; and 200 | # * put everything else in single quotes, so that it's not re-expanded. 201 | 202 | set -- \ 203 | "-Dorg.gradle.appname=$APP_BASE_NAME" \ 204 | -classpath "$CLASSPATH" \ 205 | org.gradle.wrapper.GradleWrapperMain \ 206 | "$@" 207 | 208 | # Use "xargs" to parse quoted args. 209 | # 210 | # With -n1 it outputs one arg per line, with the quotes and backslashes removed. 211 | # 212 | # In Bash we could simply go: 213 | # 214 | # readarray ARGS < <( xargs -n1 <<<"$var" ) && 215 | # set -- "${ARGS[@]}" "$@" 216 | # 217 | # but POSIX shell has neither arrays nor command substitution, so instead we 218 | # post-process each arg (as a line of input to sed) to backslash-escape any 219 | # character that might be a shell metacharacter, then use eval to reverse 220 | # that process (while maintaining the separation between arguments), and wrap 221 | # the whole thing up as a single "set" statement. 222 | # 223 | # This will of course break if any of these variables contains a newline or 224 | # an unmatched quote. 225 | # 226 | 227 | eval "set -- $( 228 | printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | 229 | xargs -n1 | 230 | sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | 231 | tr '\n' ' ' 232 | )" '"$@"' 233 | 234 | exec "$JAVACMD" "$@" 235 | -------------------------------------------------------------------------------- /cpp/engine/quickjs-atom.h: -------------------------------------------------------------------------------- 1 | /* 2 | * QuickJS atom definitions 3 | * 4 | * Copyright (c) 2017-2018 Fabrice Bellard 5 | * Copyright (c) 2017-2018 Charlie Gordon 6 | * 7 | * Permission is hereby granted, free of charge, to any person obtaining a copy 8 | * of this software and associated documentation files (the "Software"), to deal 9 | * in the Software without restriction, including without limitation the rights 10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | * copies of the Software, and to permit persons to whom the Software is 12 | * furnished to do so, subject to the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be included in 15 | * all copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 20 | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 | * THE SOFTWARE. 24 | */ 25 | 26 | #ifdef DEF 27 | 28 | /* Note: first atoms are considered as keywords in the parser */ 29 | DEF(null, "null") /* must be first */ 30 | DEF(false, "false") 31 | DEF(true, "true") 32 | DEF(if, "if") 33 | DEF(else, "else") 34 | DEF(return, "return") 35 | DEF(var, "var") 36 | DEF(this, "this") 37 | DEF(delete, "delete") 38 | DEF(void, "void") 39 | DEF(typeof, "typeof") 40 | DEF(new, "new") 41 | DEF(in, "in") 42 | DEF(instanceof, "instanceof") 43 | DEF(do, "do") 44 | DEF(while, "while") 45 | DEF(for, "for") 46 | DEF(break, "break") 47 | DEF(continue, "continue") 48 | DEF(switch, "switch") 49 | DEF(case, "case") 50 | DEF(default, "default") 51 | DEF(throw, "throw") 52 | DEF(try, "try") 53 | DEF(catch, "catch") 54 | DEF(finally, "finally") 55 | DEF(function, "function") 56 | DEF(debugger, "debugger") 57 | DEF(with, "with") 58 | /* FutureReservedWord */ 59 | DEF(class, "class") 60 | DEF(const, "const") 61 | DEF(enum, "enum") 62 | DEF(export, "export") 63 | DEF(extends, "extends") 64 | DEF(import, "import") 65 | DEF(super, "super") 66 | /* FutureReservedWords when parsing strict mode code */ 67 | DEF(implements, "implements") 68 | DEF(interface, "interface") 69 | DEF(let, "let") 70 | DEF(package, "package") 71 | DEF(private, "private") 72 | DEF(protected, "protected") 73 | DEF(public, "public") 74 | DEF(static, "static") 75 | DEF(yield, "yield") 76 | DEF(await, "await") 77 | 78 | /* empty string */ 79 | DEF(empty_string, "") 80 | /* identifiers */ 81 | DEF(length, "length") 82 | DEF(fileName, "fileName") 83 | DEF(lineNumber, "lineNumber") 84 | DEF(message, "message") 85 | DEF(errors, "errors") 86 | DEF(stack, "stack") 87 | DEF(name, "name") 88 | DEF(toString, "toString") 89 | DEF(toLocaleString, "toLocaleString") 90 | DEF(valueOf, "valueOf") 91 | DEF(eval, "eval") 92 | DEF(prototype, "prototype") 93 | DEF(constructor, "constructor") 94 | DEF(configurable, "configurable") 95 | DEF(writable, "writable") 96 | DEF(enumerable, "enumerable") 97 | DEF(value, "value") 98 | DEF(get, "get") 99 | DEF(set, "set") 100 | DEF(of, "of") 101 | DEF(__proto__, "__proto__") 102 | DEF(undefined, "undefined") 103 | DEF(number, "number") 104 | DEF(boolean, "boolean") 105 | DEF(string, "string") 106 | DEF(object, "object") 107 | DEF(symbol, "symbol") 108 | DEF(integer, "integer") 109 | DEF(unknown, "unknown") 110 | DEF(arguments, "arguments") 111 | DEF(callee, "callee") 112 | DEF(caller, "caller") 113 | DEF(_eval_, "") 114 | DEF(_ret_, "") 115 | DEF(_var_, "") 116 | DEF(_arg_var_, "") 117 | DEF(_with_, "") 118 | DEF(lastIndex, "lastIndex") 119 | DEF(target, "target") 120 | DEF(index, "index") 121 | DEF(input, "input") 122 | DEF(defineProperties, "defineProperties") 123 | DEF(apply, "apply") 124 | DEF(join, "join") 125 | DEF(concat, "concat") 126 | DEF(split, "split") 127 | DEF(construct, "construct") 128 | DEF(getPrototypeOf, "getPrototypeOf") 129 | DEF(setPrototypeOf, "setPrototypeOf") 130 | DEF(isExtensible, "isExtensible") 131 | DEF(preventExtensions, "preventExtensions") 132 | DEF(has, "has") 133 | DEF(deleteProperty, "deleteProperty") 134 | DEF(defineProperty, "defineProperty") 135 | DEF(getOwnPropertyDescriptor, "getOwnPropertyDescriptor") 136 | DEF(ownKeys, "ownKeys") 137 | DEF(add, "add") 138 | DEF(done, "done") 139 | DEF(next, "next") 140 | DEF(values, "values") 141 | DEF(source, "source") 142 | DEF(flags, "flags") 143 | DEF(global, "global") 144 | DEF(unicode, "unicode") 145 | DEF(raw, "raw") 146 | DEF(new_target, "new.target") 147 | DEF(this_active_func, "this.active_func") 148 | DEF(home_object, "") 149 | DEF(computed_field, "") 150 | DEF(static_computed_field, "") /* must come after computed_fields */ 151 | DEF(class_fields_init, "") 152 | DEF(brand, "") 153 | DEF(hash_constructor, "#constructor") 154 | DEF(as, "as") 155 | DEF(from, "from") 156 | DEF(meta, "meta") 157 | DEF(_default_, "*default*") 158 | DEF(_star_, "*") 159 | DEF(Module, "Module") 160 | DEF(then, "then") 161 | DEF(resolve, "resolve") 162 | DEF(reject, "reject") 163 | DEF(promise, "promise") 164 | DEF(proxy, "proxy") 165 | DEF(revoke, "revoke") 166 | DEF(async, "async") 167 | DEF(exec, "exec") 168 | DEF(groups, "groups") 169 | DEF(status, "status") 170 | DEF(reason, "reason") 171 | DEF(globalThis, "globalThis") 172 | #ifdef CONFIG_BIGNUM 173 | DEF(bigint, "bigint") 174 | DEF(bigfloat, "bigfloat") 175 | DEF(bigdecimal, "bigdecimal") 176 | DEF(roundingMode, "roundingMode") 177 | DEF(maximumSignificantDigits, "maximumSignificantDigits") 178 | DEF(maximumFractionDigits, "maximumFractionDigits") 179 | #endif 180 | #ifdef CONFIG_ATOMICS 181 | DEF(not_equal, "not-equal") 182 | DEF(timed_out, "timed-out") 183 | DEF(ok, "ok") 184 | #endif 185 | DEF(toJSON, "toJSON") 186 | /* class names */ 187 | DEF(Object, "Object") 188 | DEF(Array, "Array") 189 | DEF(Error, "Error") 190 | DEF(Number, "Number") 191 | DEF(String, "String") 192 | DEF(Boolean, "Boolean") 193 | DEF(Symbol, "Symbol") 194 | DEF(Arguments, "Arguments") 195 | DEF(Math, "Math") 196 | DEF(JSON, "JSON") 197 | DEF(Date, "Date") 198 | DEF(Function, "Function") 199 | DEF(GeneratorFunction, "GeneratorFunction") 200 | DEF(ForInIterator, "ForInIterator") 201 | DEF(RegExp, "RegExp") 202 | DEF(ArrayBuffer, "ArrayBuffer") 203 | DEF(SharedArrayBuffer, "SharedArrayBuffer") 204 | /* must keep same order as class IDs for typed arrays */ 205 | DEF(Uint8ClampedArray, "Uint8ClampedArray") 206 | DEF(Int8Array, "Int8Array") 207 | DEF(Uint8Array, "Uint8Array") 208 | DEF(Int16Array, "Int16Array") 209 | DEF(Uint16Array, "Uint16Array") 210 | DEF(Int32Array, "Int32Array") 211 | DEF(Uint32Array, "Uint32Array") 212 | #ifdef CONFIG_BIGNUM 213 | DEF(BigInt64Array, "BigInt64Array") 214 | DEF(BigUint64Array, "BigUint64Array") 215 | #endif 216 | DEF(Float32Array, "Float32Array") 217 | DEF(Float64Array, "Float64Array") 218 | DEF(DataView, "DataView") 219 | #ifdef CONFIG_BIGNUM 220 | DEF(BigInt, "BigInt") 221 | DEF(BigFloat, "BigFloat") 222 | DEF(BigFloatEnv, "BigFloatEnv") 223 | DEF(BigDecimal, "BigDecimal") 224 | DEF(OperatorSet, "OperatorSet") 225 | DEF(Operators, "Operators") 226 | #endif 227 | DEF(Map, "Map") 228 | DEF(Set, "Set") /* Map + 1 */ 229 | DEF(WeakMap, "WeakMap") /* Map + 2 */ 230 | DEF(WeakSet, "WeakSet") /* Map + 3 */ 231 | DEF(Map_Iterator, "Map Iterator") 232 | DEF(Set_Iterator, "Set Iterator") 233 | DEF(Array_Iterator, "Array Iterator") 234 | DEF(String_Iterator, "String Iterator") 235 | DEF(RegExp_String_Iterator, "RegExp String Iterator") 236 | DEF(Generator, "Generator") 237 | DEF(Proxy, "Proxy") 238 | DEF(Promise, "Promise") 239 | DEF(PromiseResolveFunction, "PromiseResolveFunction") 240 | DEF(PromiseRejectFunction, "PromiseRejectFunction") 241 | DEF(AsyncFunction, "AsyncFunction") 242 | DEF(AsyncFunctionResolve, "AsyncFunctionResolve") 243 | DEF(AsyncFunctionReject, "AsyncFunctionReject") 244 | DEF(AsyncGeneratorFunction, "AsyncGeneratorFunction") 245 | DEF(AsyncGenerator, "AsyncGenerator") 246 | DEF(EvalError, "EvalError") 247 | DEF(RangeError, "RangeError") 248 | DEF(ReferenceError, "ReferenceError") 249 | DEF(SyntaxError, "SyntaxError") 250 | DEF(TypeError, "TypeError") 251 | DEF(URIError, "URIError") 252 | DEF(InternalError, "InternalError") 253 | /* private symbols */ 254 | DEF(Private_brand, "") 255 | /* symbols */ 256 | DEF(Symbol_toPrimitive, "Symbol.toPrimitive") 257 | DEF(Symbol_iterator, "Symbol.iterator") 258 | DEF(Symbol_match, "Symbol.match") 259 | DEF(Symbol_matchAll, "Symbol.matchAll") 260 | DEF(Symbol_replace, "Symbol.replace") 261 | DEF(Symbol_search, "Symbol.search") 262 | DEF(Symbol_split, "Symbol.split") 263 | DEF(Symbol_toStringTag, "Symbol.toStringTag") 264 | DEF(Symbol_isConcatSpreadable, "Symbol.isConcatSpreadable") 265 | DEF(Symbol_hasInstance, "Symbol.hasInstance") 266 | DEF(Symbol_species, "Symbol.species") 267 | DEF(Symbol_unscopables, "Symbol.unscopables") 268 | DEF(Symbol_asyncIterator, "Symbol.asyncIterator") 269 | #ifdef CONFIG_BIGNUM 270 | DEF(Symbol_operatorSet, "Symbol.operatorSet") 271 | #endif 272 | 273 | #endif /* DEF */ 274 | -------------------------------------------------------------------------------- /ios/Quickjs.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 5E46D8CD2428F78900513E24 /* example.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 5E46D8CB2428F78900513E24 /* example.cpp */; }; 11 | 5E555C0D2413F4C50049A1A2 /* Quickjs.mm in Sources */ = {isa = PBXBuildFile; fileRef = B3E7B5891CC2AC0600A0062D /* Quickjs.mm */; }; 12 | /* End PBXBuildFile section */ 13 | 14 | /* Begin PBXCopyFilesBuildPhase section */ 15 | 58B511D91A9E6C8500147676 /* CopyFiles */ = { 16 | isa = PBXCopyFilesBuildPhase; 17 | buildActionMask = 2147483647; 18 | dstPath = "include/$(PRODUCT_NAME)"; 19 | dstSubfolderSpec = 16; 20 | files = ( 21 | ); 22 | runOnlyForDeploymentPostprocessing = 0; 23 | }; 24 | /* End PBXCopyFilesBuildPhase section */ 25 | 26 | /* Begin PBXFileReference section */ 27 | 134814201AA4EA6300B7C361 /* libQuickjs.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libQuickjs.a; sourceTree = BUILT_PRODUCTS_DIR; }; 28 | 5E46D8CB2428F78900513E24 /* example.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = example.cpp; path = ../cpp/example.cpp; sourceTree = ""; }; 29 | 5E46D8CC2428F78900513E24 /* example.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = example.h; path = ../cpp/example.h; sourceTree = ""; }; 30 | B3E7B5891CC2AC0600A0062D /* Quickjs.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = Quickjs.mm; sourceTree = ""; }; 31 | /* End PBXFileReference section */ 32 | 33 | /* Begin PBXFrameworksBuildPhase section */ 34 | 58B511D81A9E6C8500147676 /* Frameworks */ = { 35 | isa = PBXFrameworksBuildPhase; 36 | buildActionMask = 2147483647; 37 | files = ( 38 | ); 39 | runOnlyForDeploymentPostprocessing = 0; 40 | }; 41 | /* End PBXFrameworksBuildPhase section */ 42 | 43 | /* Begin PBXGroup section */ 44 | 134814211AA4EA7D00B7C361 /* Products */ = { 45 | isa = PBXGroup; 46 | children = ( 47 | 134814201AA4EA6300B7C361 /* libQuickjs.a */, 48 | ); 49 | name = Products; 50 | sourceTree = ""; 51 | }; 52 | 58B511D21A9E6C8500147676 = { 53 | isa = PBXGroup; 54 | children = ( 55 | 5E46D8CB2428F78900513E24 /* example.cpp */, 56 | 5E46D8CC2428F78900513E24 /* example.h */, 57 | B3E7B5891CC2AC0600A0062D /* Quickjs.mm */, 58 | 134814211AA4EA7D00B7C361 /* Products */, 59 | ); 60 | sourceTree = ""; 61 | }; 62 | /* End PBXGroup section */ 63 | 64 | /* Begin PBXNativeTarget section */ 65 | 58B511DA1A9E6C8500147676 /* Quickjs */ = { 66 | isa = PBXNativeTarget; 67 | buildConfigurationList = 58B511EF1A9E6C8500147676 /* Build configuration list for PBXNativeTarget "Quickjs" */; 68 | buildPhases = ( 69 | 58B511D71A9E6C8500147676 /* Sources */, 70 | 58B511D81A9E6C8500147676 /* Frameworks */, 71 | 58B511D91A9E6C8500147676 /* CopyFiles */, 72 | ); 73 | buildRules = ( 74 | ); 75 | dependencies = ( 76 | ); 77 | name = Quickjs; 78 | productName = RCTDataManager; 79 | productReference = 134814201AA4EA6300B7C361 /* libQuickjs.a */; 80 | productType = "com.apple.product-type.library.static"; 81 | }; 82 | /* End PBXNativeTarget section */ 83 | 84 | /* Begin PBXProject section */ 85 | 58B511D31A9E6C8500147676 /* Project object */ = { 86 | isa = PBXProject; 87 | attributes = { 88 | LastUpgradeCheck = 0920; 89 | ORGANIZATIONNAME = Facebook; 90 | TargetAttributes = { 91 | 58B511DA1A9E6C8500147676 = { 92 | CreatedOnToolsVersion = 6.1.1; 93 | }; 94 | }; 95 | }; 96 | buildConfigurationList = 58B511D61A9E6C8500147676 /* Build configuration list for PBXProject "Quickjs" */; 97 | compatibilityVersion = "Xcode 3.2"; 98 | developmentRegion = English; 99 | hasScannedForEncodings = 0; 100 | knownRegions = ( 101 | English, 102 | en, 103 | ); 104 | mainGroup = 58B511D21A9E6C8500147676; 105 | productRefGroup = 58B511D21A9E6C8500147676; 106 | projectDirPath = ""; 107 | projectRoot = ""; 108 | targets = ( 109 | 58B511DA1A9E6C8500147676 /* Quickjs */, 110 | ); 111 | }; 112 | /* End PBXProject section */ 113 | 114 | /* Begin PBXSourcesBuildPhase section */ 115 | 58B511D71A9E6C8500147676 /* Sources */ = { 116 | isa = PBXSourcesBuildPhase; 117 | buildActionMask = 2147483647; 118 | files = ( 119 | 5E46D8CD2428F78900513E24 /* example.cpp in Sources */, 120 | 5E555C0D2413F4C50049A1A2 /* Quickjs.mm in Sources */, 121 | ); 122 | runOnlyForDeploymentPostprocessing = 0; 123 | }; 124 | /* End PBXSourcesBuildPhase section */ 125 | 126 | /* Begin XCBuildConfiguration section */ 127 | 58B511ED1A9E6C8500147676 /* Debug */ = { 128 | isa = XCBuildConfiguration; 129 | buildSettings = { 130 | ALWAYS_SEARCH_USER_PATHS = NO; 131 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 132 | CLANG_CXX_LIBRARY = "libc++"; 133 | CLANG_ENABLE_MODULES = YES; 134 | CLANG_ENABLE_OBJC_ARC = YES; 135 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 136 | CLANG_WARN_BOOL_CONVERSION = YES; 137 | CLANG_WARN_COMMA = YES; 138 | CLANG_WARN_CONSTANT_CONVERSION = YES; 139 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 140 | CLANG_WARN_EMPTY_BODY = YES; 141 | CLANG_WARN_ENUM_CONVERSION = YES; 142 | CLANG_WARN_INFINITE_RECURSION = YES; 143 | CLANG_WARN_INT_CONVERSION = YES; 144 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 145 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 146 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 147 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 148 | CLANG_WARN_STRICT_PROTOTYPES = YES; 149 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 150 | CLANG_WARN_UNREACHABLE_CODE = YES; 151 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 152 | COPY_PHASE_STRIP = NO; 153 | ENABLE_STRICT_OBJC_MSGSEND = YES; 154 | ENABLE_TESTABILITY = YES; 155 | "EXCLUDED_ARCHS[sdk=*]" = arm64; 156 | GCC_C_LANGUAGE_STANDARD = gnu99; 157 | GCC_DYNAMIC_NO_PIC = NO; 158 | GCC_NO_COMMON_BLOCKS = YES; 159 | GCC_OPTIMIZATION_LEVEL = 0; 160 | GCC_PREPROCESSOR_DEFINITIONS = ( 161 | "DEBUG=1", 162 | "$(inherited)", 163 | ); 164 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 165 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 166 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 167 | GCC_WARN_UNDECLARED_SELECTOR = YES; 168 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 169 | GCC_WARN_UNUSED_FUNCTION = YES; 170 | GCC_WARN_UNUSED_VARIABLE = YES; 171 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 172 | MTL_ENABLE_DEBUG_INFO = YES; 173 | ONLY_ACTIVE_ARCH = YES; 174 | SDKROOT = iphoneos; 175 | }; 176 | name = Debug; 177 | }; 178 | 58B511EE1A9E6C8500147676 /* Release */ = { 179 | isa = XCBuildConfiguration; 180 | buildSettings = { 181 | ALWAYS_SEARCH_USER_PATHS = NO; 182 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 183 | CLANG_CXX_LIBRARY = "libc++"; 184 | CLANG_ENABLE_MODULES = YES; 185 | CLANG_ENABLE_OBJC_ARC = YES; 186 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 187 | CLANG_WARN_BOOL_CONVERSION = YES; 188 | CLANG_WARN_COMMA = YES; 189 | CLANG_WARN_CONSTANT_CONVERSION = YES; 190 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 191 | CLANG_WARN_EMPTY_BODY = YES; 192 | CLANG_WARN_ENUM_CONVERSION = YES; 193 | CLANG_WARN_INFINITE_RECURSION = YES; 194 | CLANG_WARN_INT_CONVERSION = YES; 195 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 196 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 197 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 198 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 199 | CLANG_WARN_STRICT_PROTOTYPES = YES; 200 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 201 | CLANG_WARN_UNREACHABLE_CODE = YES; 202 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 203 | COPY_PHASE_STRIP = YES; 204 | ENABLE_NS_ASSERTIONS = NO; 205 | ENABLE_STRICT_OBJC_MSGSEND = YES; 206 | "EXCLUDED_ARCHS[sdk=*]" = arm64; 207 | GCC_C_LANGUAGE_STANDARD = gnu99; 208 | GCC_NO_COMMON_BLOCKS = YES; 209 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 210 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 211 | GCC_WARN_UNDECLARED_SELECTOR = YES; 212 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 213 | GCC_WARN_UNUSED_FUNCTION = YES; 214 | GCC_WARN_UNUSED_VARIABLE = YES; 215 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 216 | MTL_ENABLE_DEBUG_INFO = NO; 217 | SDKROOT = iphoneos; 218 | VALIDATE_PRODUCT = YES; 219 | }; 220 | name = Release; 221 | }; 222 | 58B511F01A9E6C8500147676 /* Debug */ = { 223 | isa = XCBuildConfiguration; 224 | buildSettings = { 225 | HEADER_SEARCH_PATHS = ( 226 | "$(inherited)", 227 | /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include, 228 | "$(SRCROOT)/../../../React/**", 229 | "$(SRCROOT)/../../react-native/React/**", 230 | ); 231 | LIBRARY_SEARCH_PATHS = "$(inherited)"; 232 | OTHER_LDFLAGS = "-ObjC"; 233 | PRODUCT_NAME = Quickjs; 234 | SKIP_INSTALL = YES; 235 | }; 236 | name = Debug; 237 | }; 238 | 58B511F11A9E6C8500147676 /* Release */ = { 239 | isa = XCBuildConfiguration; 240 | buildSettings = { 241 | HEADER_SEARCH_PATHS = ( 242 | "$(inherited)", 243 | /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include, 244 | "$(SRCROOT)/../../../React/**", 245 | "$(SRCROOT)/../../react-native/React/**", 246 | ); 247 | LIBRARY_SEARCH_PATHS = "$(inherited)"; 248 | OTHER_LDFLAGS = "-ObjC"; 249 | PRODUCT_NAME = Quickjs; 250 | SKIP_INSTALL = YES; 251 | }; 252 | name = Release; 253 | }; 254 | /* End XCBuildConfiguration section */ 255 | 256 | /* Begin XCConfigurationList section */ 257 | 58B511D61A9E6C8500147676 /* Build configuration list for PBXProject "Quickjs" */ = { 258 | isa = XCConfigurationList; 259 | buildConfigurations = ( 260 | 58B511ED1A9E6C8500147676 /* Debug */, 261 | 58B511EE1A9E6C8500147676 /* Release */, 262 | ); 263 | defaultConfigurationIsVisible = 0; 264 | defaultConfigurationName = Release; 265 | }; 266 | 58B511EF1A9E6C8500147676 /* Build configuration list for PBXNativeTarget "Quickjs" */ = { 267 | isa = XCConfigurationList; 268 | buildConfigurations = ( 269 | 58B511F01A9E6C8500147676 /* Debug */, 270 | 58B511F11A9E6C8500147676 /* Release */, 271 | ); 272 | defaultConfigurationIsVisible = 0; 273 | defaultConfigurationName = Release; 274 | }; 275 | /* End XCConfigurationList section */ 276 | }; 277 | rootObject = 58B511D31A9E6C8500147676 /* Project object */; 278 | } 279 | --------------------------------------------------------------------------------