1use super::from_js_error;
3#[cfg(feature = "json")]
4use crate::apis::json;
5use crate::{
6 Config,
7 apis::{console, random, stream_io, text_encoding},
8 config::{JSIntrinsics, JavyIntrinsics},
9};
10
11use anyhow::{Result, bail};
12use rquickjs::{
13 Context, Module, Runtime as QRuntime, WriteOptions,
14 context::{Intrinsic, intrinsic},
15};
16use std::mem::ManuallyDrop;
17
18pub struct Runtime {
24 context: ManuallyDrop<Context>,
36 inner: ManuallyDrop<QRuntime>,
39}
40
41impl Runtime {
42 pub fn new(config: Config) -> Result<Self> {
44 let rt = ManuallyDrop::new(QRuntime::new()?);
45
46 let context = Self::build_from_config(&rt, config)?;
47 Ok(Self { inner: rt, context })
48 }
49
50 fn build_from_config(rt: &QRuntime, cfg: Config) -> Result<ManuallyDrop<Context>> {
51 let cfg = cfg.validate()?;
52 let intrinsics = &cfg.intrinsics;
53 let javy_intrinsics = &cfg.javy_intrinsics;
54
55 rt.set_gc_threshold(cfg.gc_threshold);
56 rt.set_memory_limit(cfg.memory_limit);
57 rt.set_max_stack_size(cfg.max_stack_size);
58
59 let context = Context::base(rt)?;
60
61 context.with(|ctx| {
64 random::register(ctx.clone()).expect("registering `random` APIs to succeed");
71
72 if intrinsics.contains(JSIntrinsics::DATE) {
73 unsafe { intrinsic::Date::add_intrinsic(ctx.as_raw()) }
74 }
75
76 if intrinsics.contains(JSIntrinsics::EVAL) {
77 unsafe { intrinsic::Eval::add_intrinsic(ctx.as_raw()) }
78 }
79
80 if intrinsics.contains(JSIntrinsics::REGEXP_COMPILER) {
81 unsafe { intrinsic::RegExpCompiler::add_intrinsic(ctx.as_raw()) }
82 }
83
84 if intrinsics.contains(JSIntrinsics::REGEXP) {
85 unsafe { intrinsic::RegExp::add_intrinsic(ctx.as_raw()) }
86 }
87
88 if intrinsics.contains(JSIntrinsics::JSON) {
89 unsafe { intrinsic::Json::add_intrinsic(ctx.as_raw()) }
90 }
91
92 #[cfg(feature = "json")]
93 if cfg.simd_json_builtins {
94 json::register(ctx.clone()).expect("registering JSON builtins to succeed");
95 }
96
97 if intrinsics.contains(JSIntrinsics::PROXY) {
98 unsafe { intrinsic::Proxy::add_intrinsic(ctx.as_raw()) }
99 }
100
101 if intrinsics.contains(JSIntrinsics::MAP_SET) {
102 unsafe { intrinsic::MapSet::add_intrinsic(ctx.as_raw()) }
103 }
104
105 if intrinsics.contains(JSIntrinsics::TYPED_ARRAY) {
106 unsafe { intrinsic::TypedArrays::add_intrinsic(ctx.as_raw()) }
107 }
108
109 if intrinsics.contains(JSIntrinsics::PROMISE) {
110 unsafe { intrinsic::Promise::add_intrinsic(ctx.as_raw()) }
111 }
112
113 if intrinsics.contains(JSIntrinsics::BIG_INT) {
114 unsafe { intrinsic::BigInt::add_intrinsic(ctx.as_raw()) }
115 }
116
117 if intrinsics.contains(JSIntrinsics::TEXT_ENCODING) {
118 text_encoding::register(ctx.clone())
119 .expect("registering TextEncoding APIs to succeed");
120 }
121
122 if intrinsics.contains(JSIntrinsics::WEAK_REF) {
123 unsafe { intrinsic::WeakRef::add_intrinsic(ctx.as_raw()) };
124 }
125
126 if intrinsics.contains(JSIntrinsics::PERFORMANCE) {
127 unsafe { intrinsic::Performance::add_intrinsic(ctx.as_raw()) };
128 }
129
130 console::register(ctx.clone(), cfg.log_stream, cfg.err_stream)
131 .expect("registering console to succeed");
132
133 if javy_intrinsics.contains(JavyIntrinsics::STREAM_IO) {
134 stream_io::register(ctx.clone())
135 .expect("registering StreamIO functions to succeed");
136 }
137 });
138
139 Ok(ManuallyDrop::new(context))
140 }
141
142 pub fn context(&self) -> &Context {
144 &self.context
145 }
146
147 pub fn resolve_pending_jobs(&self) -> Result<()> {
149 if self.inner.is_job_pending() {
150 loop {
151 let result = self.inner.execute_pending_job();
152 if let Ok(false) = result {
153 break;
154 }
155
156 if let Err(e) = result {
157 bail!("{e}")
158 }
159 }
160 }
161
162 Ok(())
163 }
164
165 pub fn has_pending_jobs(&self) -> bool {
167 self.inner.is_job_pending()
168 }
169
170 pub fn compile_to_bytecode(&self, name: &str, contents: &str) -> Result<Vec<u8>> {
172 self.context()
173 .with(|this| {
174 Module::declare(this.clone(), name, contents)?.write(WriteOptions::default())
175 })
176 .map_err(|e| self.context().with(|cx| from_js_error(cx.clone(), e)))
177 }
178}
179
180impl Default for Runtime {
181 fn default() -> Self {
186 Self::new(Config::default()).unwrap()
187 }
188}