Τροποποίηση του quickjs για την εισαγωγή συναρτήσεων ρουστίνας - ένας νέος τρόπος σκέψης για την επέκταση του Kraken

Αποθετήριο κώδικα

Η γένεση

Το KrakenΆνοιγμα σε νέο παράθυρο είναι μια μηχανή απόδοσης ιστοσελίδων υψηλής απόδοσης βασισμένη στο Flutter, η οποία χρησιμοποιεί το quickjsΆνοιγμα σε νέο παράθυρο ως μηχανή σεναρίων.

Ήθελα να γράψω κάποιες επεκτάσεις στο Kraken χρησιμοποιώντας το rust.

Το Kraken υποστηρίζει τη συγγραφή επεκτάσεων χρησιμοποιώντας το dartΆνοιγμα σε νέο παράθυρο .

Χρήση του flutter_rust_bridgeΆνοιγμα σε νέο παράθυρο rust και dart.

Συνδυάζοντας αυτά τα δύο σημεία, δεν είναι δύσκολο να γράψετε επεκτάσεις Kraken χρησιμοποιώντας το rust.
Ωστόσο, η επιβάρυνση των επιδόσεων αυτής της λύσης είναι μεγάλη, καθώς υπάρχει μια ποινή απόδοσης για το dart που καλεί το rust και μια άλλη για το quickjs που καλεί το dart.

Από την άλλη πλευρά, ενώ η κοινότητα rust έχει rquickjsΆνοιγμα σε νέο παράθυρο τέτοιες κλήσεις στη βιβλιοθήκη quickjs στο rust.
Ωστόσο, καλούν το quickjs αντί να ενσωματώσουν το quickjsκαι δεν μπορούν να χρησιμοποιηθούν για να μαγέψουν το quickjs.

Σε αυτή την βάση κώδικα, έχω εφαρμόσει μια νέα λύση: την άμεση τροποποίηση του πηγαίου κώδικα του quickjs ώστε να υποστηρίζει την επέκταση rust.

Πρόκειται για μια γενική λύση που μπορεί να χρησιμοποιηθεί όχι μόνο για την τροποποίηση του Kraken, αλλά και για όλα τα πλαίσια και τις βιβλιοθήκες που ενσωματώνουν το quickjs.

Επίδειξη

Ο κώδικας του test.js έχει ως εξής :

const try_run = (func, ...args) => {
  try {
    func(...args)
  } catch (err) {
    console.log('❌', err.message)
    console.log(err.stack)
  }
};

import * as rust from 'rust'
console.log("export from rust :", Object.keys(rust))

import {
  fib,
  sleep
} from 'rust'

(async () => {

  console.log('begin sleep 2s')
  await sleep(2000);
  console.log('sleep done')

  console.log('fib(3) =', fib(3));

  console.log("try catch example :")
  try_run(fib);
  try_run(fib, '*');

})()

Εκτελέστε το ./quickjs/qjs test.js, έξοδος :

export from rust : fib,sleep
begin sleep 2s
sleep done
fib(3) = 6
try catch example :
❌ miss : args need 1 pass 0
    at fib (native)
    at try_run (test.js:8)
    at <anonymous> (test.js:27)

❌ not number : args position 0
    at fib (native)
    at try_run (test.js:6)
    at <anonymous> (test.js:28)

Εφαρμογή της fib στη σκουριά

Η συνάρτηση fib που εισάγεται στο js προέρχεται από το rust/src/export/fib.rs και ο κώδικας έχει ως εξής :

use crate::js::{self, arg};
use quickjs_ffi::{JSContext, JSValue};
use rust_macro::js;
use std::os::raw::c_int;

#[js]
pub fn fib(n: i64) -> i64 {
  if n <= 1 {
    return if n == 1 { 1 } else { 0 };
  }
  n + fib(n - 1)
}

#[no_mangle]
pub extern "C" fn js_fib(
  ctx: *mut JSContext,
  _this: JSValue,
  argc: c_int,
  argv: *mut JSValue,
) -> JSValue {
  if let Err(err) = arg::arg_miss(ctx, argc, fib_args_len) {
    return err;
  }
  match arg::arg_i64(ctx, argv, 0) {
    Err(err) => err,
    Ok(n) => js::val(ctx, fib(n)),
  }
}

Επί του παρόντος, η μακροεντολή της διαδικασίας #[js] προσθέτει απλώς μια σταθερά fib_args_lenπου προσδιορίζει τον αριθμό των επιχειρημάτων της συνάρτησης.

Μελλοντικά, η μακροεντολή διαδικασίας ./rust_macro μπορεί να γραφτεί για να επιτρέψει την πλήρως αυτόματη εξαγωγή λειτουργιών.

Υλοποίηση της συνάρτησης sleep στην rust

Η συνάρτηση sleep που εισάγεται στο js προέρχεται από το rust/src/export/sleep.rs και ο κώδικας έχει ως εξής :

use crate::js::{self, arg};
use quickjs_ffi::{JSContext, JSValue};
use rust_macro::js;
use std::os::raw::c_int;

#[js]
pub fn fib(n: i64) -> i64 {
  if n <= 1 {
    return if n == 1 { 1 } else { 0 };
  }
  n + fib(n - 1)
}

#[no_mangle]
pub extern "C" fn js_fib(
  ctx: *mut JSContext,
  _this: JSValue,
  argc: c_int,
  argv: *mut JSValue,
) -> JSValue {
  if let Err(err) = arg::arg_miss(ctx, argc, fib_args_len) {
    return err;
  }
  match arg::arg_i64(ctx, argv, 0) {
    Err(err) => err,
    Ok(n) => js::val(ctx, fib(n)),
  }
}
use crate::{js::arg, qjs::run};
use async_io::Timer;
use quickjs_ffi::{JSContext, JSValue};
use rust_macro::js;
use std::{os::raw::c_int, time::Duration};

#[js]
pub async fn sleep(n: u64) {
  Timer::after(Duration::from_millis(n)).await;
}

#[no_mangle]
pub extern "C" fn js_sleep(
  ctx: *mut JSContext,
  _this: JSValue,
  argc: c_int,
  argv: *mut JSValue,
) -> JSValue {
  if let Err(err) = arg::arg_miss(ctx, argc, sleep_args_len) {
    return err;
  }
  match arg::arg_i64(ctx, argv, 0) {
    Err(err) => err,
    Ok(n) => run(ctx, async move {
      sleep(n as u64).await;
      Ok(())
    }),
  }
}

Όπως μπορείτε να δείτε παραπάνω, όλες οι εξαγόμενες συναρτήσεις ορίζονται στον κατάλογο ./rust/src/export. Αυτός ο κατάλογος mod.rs δημιουργείται αυτόματα όταν εκτελείται το ./rust/build.xsh, εξάγοντας όλα τα αρχεία .rs που βρίσκονται κάτω από αυτόν.

Ανάγνωση και επικύρωση των εισερχόμενων παραμέτρων js

Οι παράμετροι διαβάζονται και επικυρώνονται στη διεύθυνση src/js/arg.rs με τον ακόλουθο κώδικα :

use crate::js::{self, arg};
use quickjs_ffi::{JSContext, JSValue};
use rust_macro::js;
use std::os::raw::c_int;

#[js]
pub fn fib(n: i64) -> i64 {
  if n <= 1 {
    return if n == 1 { 1 } else { 0 };
  }
  n + fib(n - 1)
}

#[no_mangle]
pub extern "C" fn js_fib(
  ctx: *mut JSContext,
  _this: JSValue,
  argc: c_int,
  argv: *mut JSValue,
) -> JSValue {
  if let Err(err) = arg::arg_miss(ctx, argc, fib_args_len) {
    return err;
  }
  match arg::arg_i64(ctx, argv, 0) {
    Err(err) => err,
    Ok(n) => js::val(ctx, fib(n)),
  }
}
use crate::{js::arg, qjs::run};
use async_io::Timer;
use quickjs_ffi::{JSContext, JSValue};
use rust_macro::js;
use std::{os::raw::c_int, time::Duration};

#[js]
pub async fn sleep(n: u64) {
  Timer::after(Duration::from_millis(n)).await;
}

#[no_mangle]
pub extern "C" fn js_sleep(
  ctx: *mut JSContext,
  _this: JSValue,
  argc: c_int,
  argv: *mut JSValue,
) -> JSValue {
  if let Err(err) = arg::arg_miss(ctx, argc, sleep_args_len) {
    return err;
  }
  match arg::arg_i64(ctx, argv, 0) {
    Err(err) => err,
    Ok(n) => run(ctx, async move {
      sleep(n as u64).await;
      Ok(())
    }),
  }
}
use crate::js::throw;

use quickjs_ffi::{JSContext, JSValue, JS_IsNumber, JS_ToInt64};
use std::{mem::MaybeUninit, os::raw::c_int};

pub(crate) fn arg_miss(ctx: *mut JSContext, argc: c_int, need: c_int) -> Result<(), JSValue> {
  if argc < need {
    throw(ctx, format!("miss : args need {need} pass {argc}"))?
  }
  Ok(())
}

pub(crate) fn arg_i64(ctx: *mut JSContext, argv: *mut JSValue, pos: isize) -> Result<i64, JSValue> {
  unsafe {
    let val = *argv.offset(pos);
    if JS_IsNumber(val) == 0 {
      throw(ctx, format!("not number : args position {pos}"))?
    }
    let mut n = MaybeUninit::uninit();
    JS_ToInt64(ctx, n.as_mut_ptr() as _, val);
    Ok(n.assume_init())
  }
}

Προς το παρόν ελέγχεται μόνο ο αριθμός των ορίων και διαβάζεται ο τύπος i64.

Μπορείτε να προσθέσετε αυτές τις συναρτήσεις ανάλογα με τις ανάγκες σας, δείτε τις συναρτήσεις στο qjs_sysΆνοιγμα σε νέο παράθυρο ξεκινώντας με JS_To.

Μετατροπή τύπου δεδομένων από rust σε js

Η μετατροπή τύπου γίνεται στο src/js/val.rs με τον ακόλουθο κώδικα :

use crate::js::{self, arg};
use quickjs_ffi::{JSContext, JSValue};
use rust_macro::js;
use std::os::raw::c_int;

#[js]
pub fn fib(n: i64) -> i64 {
  if n <= 1 {
    return if n == 1 { 1 } else { 0 };
  }
  n + fib(n - 1)
}

#[no_mangle]
pub extern "C" fn js_fib(
  ctx: *mut JSContext,
  _this: JSValue,
  argc: c_int,
  argv: *mut JSValue,
) -> JSValue {
  if let Err(err) = arg::arg_miss(ctx, argc, fib_args_len) {
    return err;
  }
  match arg::arg_i64(ctx, argv, 0) {
    Err(err) => err,
    Ok(n) => js::val(ctx, fib(n)),
  }
}
use crate::{js::arg, qjs::run};
use async_io::Timer;
use quickjs_ffi::{JSContext, JSValue};
use rust_macro::js;
use std::{os::raw::c_int, time::Duration};

#[js]
pub async fn sleep(n: u64) {
  Timer::after(Duration::from_millis(n)).await;
}

#[no_mangle]
pub extern "C" fn js_sleep(
  ctx: *mut JSContext,
  _this: JSValue,
  argc: c_int,
  argv: *mut JSValue,
) -> JSValue {
  if let Err(err) = arg::arg_miss(ctx, argc, sleep_args_len) {
    return err;
  }
  match arg::arg_i64(ctx, argv, 0) {
    Err(err) => err,
    Ok(n) => run(ctx, async move {
      sleep(n as u64).await;
      Ok(())
    }),
  }
}
use crate::js::throw;

use quickjs_ffi::{JSContext, JSValue, JS_IsNumber, JS_ToInt64};
use std::{mem::MaybeUninit, os::raw::c_int};

pub(crate) fn arg_miss(ctx: *mut JSContext, argc: c_int, need: c_int) -> Result<(), JSValue> {
  if argc < need {
    throw(ctx, format!("miss : args need {need} pass {argc}"))?
  }
  Ok(())
}

pub(crate) fn arg_i64(ctx: *mut JSContext, argv: *mut JSValue, pos: isize) -> Result<i64, JSValue> {
  unsafe {
    let val = *argv.offset(pos);
    if JS_IsNumber(val) == 0 {
      throw(ctx, format!("not number : args position {pos}"))?
    }
    let mut n = MaybeUninit::uninit();
    JS_ToInt64(ctx, n.as_mut_ptr() as _, val);
    Ok(n.assume_init())
  }
}
use quickjs_ffi::{JSContext, JSValue, JS_NewInt64, JS_NewString, JS_NULL, JS_UNDEFINED};
use std::ffi::CString;

pub enum Val {
  None,
  Undefined,
  I64(i64),
  CString(CString),
}

impl From<()> for Val {
  fn from(_: ()) -> Self {
    Val::Undefined
  }
}

impl From<i64> for Val {
  fn from(t: i64) -> Self {
    Val::I64(t)
  }
}

impl From<CString> for Val {
  fn from(t: CString) -> Self {
    Val::CString(t)
  }
}

pub(crate) fn val(ctx: *mut JSContext, t: impl Into<Val>) -> JSValue {
  match t.into() {
    Val::None => JS_NULL,
    Val::Undefined => JS_UNDEFINED,
    Val::I64(n) => unsafe { JS_NewInt64(ctx, n) },
    Val::CString(cstr) => unsafe { JS_NewString(ctx, cstr.as_ptr()) },
  }
}

Μόνο τέσσερις τύποι ορίζονται για τη μετατροπή από None, (), i64και CString σε js. Μπορείτε να προσθέσετε όσους περισσότερους θέλετε.

Περισσότεροι τύποι δεδομένων μπορούν να δηλωθούν στις συναρτήσεις της qjs_sysΆνοιγμα σε νέο παράθυρο που αρχίζουν με JS_New.

Περιβάλλον ανάπτυξης

Αναπτύσσω σε ένα φορητό υπολογιστή της Apple, η σκουριά χρησιμοποιεί την έκδοση 1.62.0-nightly.

Πρώτα εγκαταστήστε το direnvΆνοιγμα σε νέο παράθυρο, πηγαίνετε στον κατάλογο και direnv allow για λίγο

Εγκαταστήστε την python3, στη συνέχεια pip3 install -r ./requirements.txt

run ./build.xsh για να μεταγλωττίσετε και να εκτελέσετε το demo

Από προεπιλογή, το επίσημο αποθετήριο quickjs θα κλωνοποιηθεί, αν θέλετε να τροποποιήσετε το quickjs στο αποθετήριο Kraken, πρώτα

git clone --recursive git@github.com:openkraken/kraken.git --depth=1

τότε κάντε τα εξής

rm -rf quickjs
ln -s ../kraken/bridge/third_party/quickjs .

Τέλος, επανεκτελέστε το ./build.xsh

Δομή καταλόγου

  • ./quickjs_rust
    Τροποποίηση του αρχείου c του κώδικα quickjs

  • ./quickjs_ffi
    Εξάγετε τις συναρτήσεις από το αρχείο κεφαλίδας quickjs στο rust

  • ./rust
    Χρησιμοποιήστε το rust για να υλοποιήσετε τις λειτουργίες του quickjs

    • ./rust/src/qjs.rs
      Υλοποίηση ασύγχρονων κλήσεων. Δεδομένου ότι το quickjs είναι single-threaded, οι κλήσεις συναρτήσεων που αφορούν το quckjs γράφονται στο κύριο νήμα.
  • ./rust_macro
    rust Εφαρμογή της μακροεντολής της διαδικασίας #[js]

    Στο μέλλον, δείτε wasmedge-quickjsΆνοιγμα σε νέο παράθυρο για αυτόματη εξαγωγή συναρτήσεων rust σε συναρτήσεις js. wasmedge-quickjs → JsFunctionTrampolineΆνοιγμα σε νέο παράθυρο

Σενάρια οικοδόμησης build.xsh

Χωρίς άλλη καθυστέρηση, ας πάμε κατευθείαν στον πηγαίο κώδικα του σεναρίου κατασκευής build.xsh

#!/usr/bin/env xonsh

from pathlib import Path
from os.path import dirname,abspath,exists,join
PWD = dirname(abspath(__file__))
cd @(PWD)

p".xonshrc".exists() && source .xonshrc

quickjs = 'quickjs'

if not exists(quickjs):
  git clone git@github.com:bellard/@(quickjs).git --depth=1

./quickjs_rust/patch.py

./rust/build.xsh
./quickjs_rust/gen.py

def ln_s(li):
  for arg in li.split(' '):
    fp = join(quickjs,arg)
    if not exists(fp):
      ln -s @(PWD)/@(arg) @(fp)

ln_s('quickjs_rust rust quickjs_ffi rust_macro')

cd @(quickjs)
make qjs

cd @(PWD)
./quickjs/qjs --unhandled-rejection -m test.js 2>&1 | tee test.js.out

Επεξήγηση της αρχής

quickjs_rust/patch.py

Η εκτέλεση του ./quickjs_rust/patch.py θα κάνει κάποιες μικρές αλλαγές στον πηγαίο κώδικα του quickjs.

Μία από τις συναρτήσεις JS_AddRust χρησιμοποιείται για την έγχυση στην ενότητα rust.

Το rust_run εισάγεται στο JS_ExecutePendingJob για την κλήση ασύγχρονων συναρτήσεων.

Ένα στιγμιότυπο οθόνης με όλες τις αλλαγές παρουσιάζεται παρακάτω :

quickjs_rust.h

Από τις παραπάνω αλλαγές, μπορείτε να δείτε ότι έχουμε εισαγάγει ένα νέο αρχείο κεφαλίδας quickjs_rust.h με τον ακόλουθο κώδικα

#ifndef QUICKJS_RUST_H
#define QUICKJS_RUST_H

#include "../quickjs/quickjs.h"
#include "../rust/rust.h"

#define countof(x) (sizeof(x) / sizeof((x)[0]))
#define JS_RUSTFUNC_DEF(name) JS_CFUNC_DEF(#name, name##_args_len, js_##name)
#include "./js_rust_funcs.h"

static const unsigned int js_rust_funcs_count = countof(js_rust_funcs);

static int
js_rust_init(JSContext* ctx, JSModuleDef* m)
{
  return JS_SetModuleExportList(ctx, m, js_rust_funcs,
      js_rust_funcs_count);
}

#define JS_INIT_MODULE js_init_module_rust

JSModuleDef* JS_INIT_MODULE(JSContext* ctx, const char* module_name)
{
  JSModuleDef* m;
  m = JS_NewCModule(ctx, module_name, js_rust_init);
  if (!m)
    return NULL;
  js_rust_init(ctx, m);
  return m;
}

void JS_AddRust(JSContext* ctx, JSRuntime* rt)
{
  JSModuleDef* m = JS_INIT_MODULE(ctx, "rust");
  for (unsigned int i = 0; i < js_rust_funcs_count; i++) {
    JS_AddModuleExport(ctx, m, js_rust_funcs[i].name);
  }
  rust_init(ctx, rt);
}

#endif

rust/rust.h

Μπορείτε να δείτε ότι το quickjs_rust/quickjs_rust.h εισάγει το quickjs_rust/js_rust_funcs.h, το οποίο παράγεται αυτόματα από το αρχείο κεφαλίδας της λειτουργίας εξαγωγής της ρουστίκ rust/rust.h και δεν πρέπει να τροποποιηθεί με το χέρι.

Και το rust/rust.h δημιουργείται με την κλήση του cbindgen από το ./rust/build.xsh.

rust/build.xsh

#!/usr/bin/env xonsh

from os.path import dirname,abspath
import platform
PWD = dirname(abspath(__file__))
cd @(PWD)

p"../.xonshrc".exists() && source ../.xonshrc

./src/export/mod.gen.py

system = platform.system().lower()
if system == 'darwin':
  system = f'apple-{system}'

TARGET=f'{platform.machine()}-{system}'

def cbindgen():
  cbindgen -q --config cbindgen.toml --crate rust --output rust.h

try:
  cbindgen()
except:
  cargo clean
  cbindgen()

cargo build \
--release \
-Z build-std=std,panic_abort \
-Z build-std-features=panic_immediate_abort \
--target @(TARGET)

mv ./target/@(TARGET)/release/librust.a ./target/release

Σημειώσεις ανάπτυξης

quickjs_ffi

Κώδικας από quijine/main/quijine_core/src/ffi.rsΆνοιγμα σε νέο παράθυρο

με κάποιες μικρές τροποποιήσεις, αντικαθιστώντας

use crate::js::{self, arg};
use quickjs_ffi::{JSContext, JSValue};
use rust_macro::js;
use std::os::raw::c_int;

#[js]
pub fn fib(n: i64) -> i64 {
  if n <= 1 {
    return if n == 1 { 1 } else { 0 };
  }
  n + fib(n - 1)
}

#[no_mangle]
pub extern "C" fn js_fib(
  ctx: *mut JSContext,
  _this: JSValue,
  argc: c_int,
  argv: *mut JSValue,
) -> JSValue {
  if let Err(err) = arg::arg_miss(ctx, argc, fib_args_len) {
    return err;
  }
  match arg::arg_i64(ctx, argv, 0) {
    Err(err) => err,
    Ok(n) => js::val(ctx, fib(n)),
  }
}
use crate::{js::arg, qjs::run};
use async_io::Timer;
use quickjs_ffi::{JSContext, JSValue};
use rust_macro::js;
use std::{os::raw::c_int, time::Duration};

#[js]
pub async fn sleep(n: u64) {
  Timer::after(Duration::from_millis(n)).await;
}

#[no_mangle]
pub extern "C" fn js_sleep(
  ctx: *mut JSContext,
  _this: JSValue,
  argc: c_int,
  argv: *mut JSValue,
) -> JSValue {
  if let Err(err) = arg::arg_miss(ctx, argc, sleep_args_len) {
    return err;
  }
  match arg::arg_i64(ctx, argv, 0) {
    Err(err) => err,
    Ok(n) => run(ctx, async move {
      sleep(n as u64).await;
      Ok(())
    }),
  }
}
use crate::js::throw;

use quickjs_ffi::{JSContext, JSValue, JS_IsNumber, JS_ToInt64};
use std::{mem::MaybeUninit, os::raw::c_int};

pub(crate) fn arg_miss(ctx: *mut JSContext, argc: c_int, need: c_int) -> Result<(), JSValue> {
  if argc < need {
    throw(ctx, format!("miss : args need {need} pass {argc}"))?
  }
  Ok(())
}

pub(crate) fn arg_i64(ctx: *mut JSContext, argv: *mut JSValue, pos: isize) -> Result<i64, JSValue> {
  unsafe {
    let val = *argv.offset(pos);
    if JS_IsNumber(val) == 0 {
      throw(ctx, format!("not number : args position {pos}"))?
    }
    let mut n = MaybeUninit::uninit();
    JS_ToInt64(ctx, n.as_mut_ptr() as _, val);
    Ok(n.assume_init())
  }
}
use quickjs_ffi::{JSContext, JSValue, JS_NewInt64, JS_NewString, JS_NULL, JS_UNDEFINED};
use std::ffi::CString;

pub enum Val {
  None,
  Undefined,
  I64(i64),
  CString(CString),
}

impl From<()> for Val {
  fn from(_: ()) -> Self {
    Val::Undefined
  }
}

impl From<i64> for Val {
  fn from(t: i64) -> Self {
    Val::I64(t)
  }
}

impl From<CString> for Val {
  fn from(t: CString) -> Self {
    Val::CString(t)
  }
}

pub(crate) fn val(ctx: *mut JSContext, t: impl Into<Val>) -> JSValue {
  match t.into() {
    Val::None => JS_NULL,
    Val::Undefined => JS_UNDEFINED,
    Val::I64(n) => unsafe { JS_NewInt64(ctx, n) },
    Val::CString(cstr) => unsafe { JS_NewString(ctx, cstr.as_ptr()) },
  }
}
pub use libquickjs_sys::*;

στο

use crate::js::{self, arg};
use quickjs_ffi::{JSContext, JSValue};
use rust_macro::js;
use std::os::raw::c_int;

#[js]
pub fn fib(n: i64) -> i64 {
  if n <= 1 {
    return if n == 1 { 1 } else { 0 };
  }
  n + fib(n - 1)
}

#[no_mangle]
pub extern "C" fn js_fib(
  ctx: *mut JSContext,
  _this: JSValue,
  argc: c_int,
  argv: *mut JSValue,
) -> JSValue {
  if let Err(err) = arg::arg_miss(ctx, argc, fib_args_len) {
    return err;
  }
  match arg::arg_i64(ctx, argv, 0) {
    Err(err) => err,
    Ok(n) => js::val(ctx, fib(n)),
  }
}
use crate::{js::arg, qjs::run};
use async_io::Timer;
use quickjs_ffi::{JSContext, JSValue};
use rust_macro::js;
use std::{os::raw::c_int, time::Duration};

#[js]
pub async fn sleep(n: u64) {
  Timer::after(Duration::from_millis(n)).await;
}

#[no_mangle]
pub extern "C" fn js_sleep(
  ctx: *mut JSContext,
  _this: JSValue,
  argc: c_int,
  argv: *mut JSValue,
) -> JSValue {
  if let Err(err) = arg::arg_miss(ctx, argc, sleep_args_len) {
    return err;
  }
  match arg::arg_i64(ctx, argv, 0) {
    Err(err) => err,
    Ok(n) => run(ctx, async move {
      sleep(n as u64).await;
      Ok(())
    }),
  }
}
use crate::js::throw;

use quickjs_ffi::{JSContext, JSValue, JS_IsNumber, JS_ToInt64};
use std::{mem::MaybeUninit, os::raw::c_int};

pub(crate) fn arg_miss(ctx: *mut JSContext, argc: c_int, need: c_int) -> Result<(), JSValue> {
  if argc < need {
    throw(ctx, format!("miss : args need {need} pass {argc}"))?
  }
  Ok(())
}

pub(crate) fn arg_i64(ctx: *mut JSContext, argv: *mut JSValue, pos: isize) -> Result<i64, JSValue> {
  unsafe {
    let val = *argv.offset(pos);
    if JS_IsNumber(val) == 0 {
      throw(ctx, format!("not number : args position {pos}"))?
    }
    let mut n = MaybeUninit::uninit();
    JS_ToInt64(ctx, n.as_mut_ptr() as _, val);
    Ok(n.assume_init())
  }
}
use quickjs_ffi::{JSContext, JSValue, JS_NewInt64, JS_NewString, JS_NULL, JS_UNDEFINED};
use std::ffi::CString;

pub enum Val {
  None,
  Undefined,
  I64(i64),
  CString(CString),
}

impl From<()> for Val {
  fn from(_: ()) -> Self {
    Val::Undefined
  }
}

impl From<i64> for Val {
  fn from(t: i64) -> Self {
    Val::I64(t)
  }
}

impl From<CString> for Val {
  fn from(t: CString) -> Self {
    Val::CString(t)
  }
}

pub(crate) fn val(ctx: *mut JSContext, t: impl Into<Val>) -> JSValue {
  match t.into() {
    Val::None => JS_NULL,
    Val::Undefined => JS_UNDEFINED,
    Val::I64(n) => unsafe { JS_NewInt64(ctx, n) },
    Val::CString(cstr) => unsafe { JS_NewString(ctx, cstr.as_ptr()) },
  }
}
pub use libquickjs_sys::*;
#![allow(non_upper_case_globals)]
#![allow(non_camel_case_types)]
#![allow(non_snake_case)]

include!(concat!(env!("OUT_DIR"), "/c.rs"));

Undefined symbols for architecture x86_64: "_JS_ToInt32"

Τροποποίηση '. /rust/Cargo.toml' ως εξής, διατηρώντας μόνο τη staticlib

[lib]
#crate-type = ["lib", "cdylib", "staticlib"]
crate-type = ["staticlib"]

Αναφορές

  1. Από τη μηχανή JS στο πρόγραμμα εκτέλεσης JS (πάνω)Άνοιγμα σε νέο παράθυρο (κάτω)Άνοιγμα σε νέο παράθυρο
  2. Ανάπτυξη μιας εγγενούς μονάδας για το QuickJS σε CΆνοιγμα σε νέο παράθυρο
  3. Χρήση του Rust για την υλοποίηση του JS APIΆνοιγμα σε νέο παράθυρο
  4. Παραδείγματα QuickJSΆνοιγμα σε νέο παράθυρο
  5. rust-bindgenΆνοιγμα σε νέο παράθυρο
  6. Πώς να δημιουργήσετε ασύγχρονο κώδικα για το QuickJSΆνοιγμα σε νέο παράθυρο
  7. rquickjs → JS_NewPromiseCapabilityΆνοιγμα σε νέο παράθυρο
  8. wasmedge-quickjs → new_promiseΆνοιγμα σε νέο παράθυρο
  9. wasmedge-quickjs → JsMethodΆνοιγμα σε νέο παράθυρο
  10. wasmedge-quickjs → κλήσηΆνοιγμα σε νέο παράθυρο
  11. Η απαρατήρητη παγίδα - κλειδαριές στο RustΆνοιγμα σε νέο παράθυρο

Σχετικά με το

Αυτό το έργο αποτελεί μέρος του έργου κώδικα rmw.link ( rmw.linkΆνοιγμα σε νέο παράθυρο ).

rmw.link

Ενημερώσεις:
Από το: gcxfd