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 | 
6 | 
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 |
--------------------------------------------------------------------------------