git.delta.rocks / jrsonnet / refs/commits / c6e9f5b704f3

difftreelog

refactor(bindings) extra interop methods

Yaroslav Bolyukin2024-05-28parent: #7b38a7f.patch.diff
in: master

5 files changed

modifiedbindings/c/libjsonnet_test_file.cdiffbeforeafterboth
1/*
2Copyright 2015 Google Inc. All rights reserved.
3Licensed under the Apache License, Version 2.0 (the "License");
4you may not use this file except in compliance with the License.
5You may obtain a copy of the License at
6 http://www.apache.org/licenses/LICENSE-2.0
7Unless required by applicable law or agreed to in writing, software
8distributed under the License is distributed on an "AS IS" BASIS,
9WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10See the License for the specific language governing permissions and
11limitations under the License.
12*/
13
14#include <stdlib.h>1#include <stdlib.h>
15#include <stdio.h>2#include <stdio.h>
modifiedbindings/jsonnet/Cargo.tomldiffbeforeafterboth
2727
28[lib]28[lib]
29name = "jsonnet"29name = "jsonnet"
30crate-type = ["cdylib"]30crate-type = ["cdylib", "staticlib"]
3131
32[features]32[features]
33default = ["interop-common", "interop-wasm", "interop-threading"]
33# Export additional functions for native integration, i.e ability to set custom trace format34# Export additional functions for native integration, i.e ability to set custom trace format
34interop = []35interop-common = []
36# Provide ability to statically override callbacks from WASM (by using imports)
37interop-wasm = []
38# Provide ability to move jsonnet vm state between threads
39interop-threading = []
40
35experimental = ["exp-preserve-order", "exp-destruct"]41experimental = ["exp-preserve-order", "exp-destruct"]
36exp-preserve-order = ["jrsonnet-evaluator/exp-preserve-order"]42exp-preserve-order = ["jrsonnet-evaluator/exp-preserve-order"]
modifiedbindings/jsonnet/src/import.rsdiffbeforeafterboth
15use jrsonnet_evaluator::{15use jrsonnet_evaluator::{
16 bail,16 bail,
17 error::{ErrorKind::*, Result},17 error::{ErrorKind::*, Result},
18 FileImportResolver, ImportResolver,18 ImportResolver,
19};19};
20use jrsonnet_gcmodule::Trace;20use jrsonnet_gcmodule::Trace;
21use jrsonnet_parser::{SourceDirectory, SourceFile, SourcePath};21use jrsonnet_parser::{SourceDirectory, SourceFile, SourcePath};
107 self107 self
108 }108 }
109
110 fn as_any_mut(&mut self) -> &mut dyn Any {
111 self
112 }
109}113}
110114
111/// # Safety115/// # Safety
117 cb: JsonnetImportCallback,121 cb: JsonnetImportCallback,
118 ctx: *mut c_void,122 ctx: *mut c_void,
119) {123) {
120 vm.state.set_import_resolver(CallbackImportResolver {124 vm.replace_import_resolver(CallbackImportResolver {
121 cb,125 cb,
122 ctx,126 ctx,
123 out: RefCell::new(HashMap::new()),127 out: RefCell::new(HashMap::new()),
131pub unsafe extern "C" fn jsonnet_jpath_add(vm: &VM, path: *const c_char) {135pub unsafe extern "C" fn jsonnet_jpath_add(vm: &VM, path: *const c_char) {
132 let cstr = unsafe { CStr::from_ptr(path) };136 let cstr = unsafe { CStr::from_ptr(path) };
133 let path = PathBuf::from(cstr.to_str().unwrap());137 let path = PathBuf::from(cstr.to_str().unwrap());
134 let any_resolver = vm.state.import_resolver();
135 let resolver = any_resolver
136 .as_any()
137 .downcast_ref::<FileImportResolver>()
138 .expect("jpaths are not compatible with callback imports!");
139 resolver.add_jpath(path);138 vm.add_jpath(path);
140}139}
141140
modifiedbindings/jsonnet/src/interop.rsdiffbeforeafterboth
1//! Jrsonnet specific additional binding helpers1//! Jrsonnet specific additional binding helpers
22
3use std::{3use crate::VM;
4 ffi::c_void,4
5#[cfg(feature = "interop-wasm")]
6pub mod wasm {
5 os::raw::{c_char, c_int},7 use std::ffi::{c_char, c_int, c_void};
6};
78
8use jrsonnet_evaluator::Val;9 use jrsonnet_evaluator::Val;
910
10use crate::{import::jsonnet_import_callback, native::jsonnet_native_callback};11 use crate::VM;
1112
12extern "C" {13 extern "C" {
14
13 pub fn _jrsonnet_static_import_callback(15 pub fn _jrsonnet_static_import_callback(
14 ctx: *mut c_void,16 ctx: *mut c_void,
15 base: *const c_char,17 base: *const c_char,
16 rel: *const c_char,18 rel: *const c_char,
17 found_here: *mut *const c_char,19 found_here: *mut *const c_char,
18 success: &mut c_int,20 buf: *mut *mut c_char,
21 buflen: *mut usize,
19 ) -> *const c_char;22 ) -> c_int;
2023
21 #[allow(improper_ctypes)]24 #[allow(improper_ctypes)]
22 pub fn _jrsonnet_static_native_callback(25 pub fn _jrsonnet_static_native_callback(
26 ) -> *mut Val;29 ) -> *mut Val;
27}30 }
2831
29/// # Safety
30#[no_mangle]32 #[no_mangle]
33 #[cfg(feature = "interop-wasm")]
34 // ctx arg is passed as-is to callback
35 #[allow(clippy::not_unsafe_ptr_arg_deref)]
31pub unsafe extern "C" fn jrsonnet_apply_static_import_callback(vm: &VM, ctx: *mut c_void) {36 pub extern "C" fn jrsonnet_apply_static_import_callback(vm: &VM, ctx: *mut c_void) {
32 jsonnet_import_callback(vm, _jrsonnet_static_import_callback, ctx)37 unsafe { crate::import::jsonnet_import_callback(vm, _jrsonnet_static_import_callback, ctx) }
33}38 }
3439
35/// # Safety40 /// # Safety
41 ///
42 /// `name` and `raw_params` should be correctly initialized
36#[no_mangle]43 #[no_mangle]
44 #[cfg(feature = "interop-wasm")]
37pub unsafe extern "C" fn jrsonnet_apply_static_native_callback(45 pub unsafe extern "C" fn jrsonnet_apply_static_native_callback(
38 vm: &VM,46 vm: &VM,
39 name: *const c_char,47 name: *const c_char,
40 ctx: *mut c_void,48 ctx: *mut c_void,
41 raw_params: *const *const c_char,49 raw_params: *const *const c_char,
42) {50 ) {
51 unsafe {
43 jsonnet_native_callback(vm, name, _jrsonnet_static_native_callback, ctx, raw_params)52 crate::native::jsonnet_native_callback(
53 vm,
54 name,
55 _jrsonnet_static_native_callback,
56 ctx,
57 raw_params,
58 );
59 }
44}60 }
61}
62
63#[cfg(feature = "interop-common")]
64mod common {
65 use jrsonnet_evaluator::trace::{CompactFormat, ExplainingFormat, JsFormat, PathResolver};
66
67 use crate::VM;
4568
46#[no_mangle]69 #[no_mangle]
47pub extern "C" fn jrsonnet_set_trace_format(vm: &VM, format: u8) {70 pub extern "C" fn jrsonnet_set_trace_format(vm: &mut VM, format: u8) {
48 use jrsonnet_evaluator::trace::JsFormat;
49 match format {71 match format {
72 0 => {
73 vm.trace_format = Box::new(CompactFormat {
74 max_trace: 20,
75 resolver: PathResolver::new_cwd_fallback(),
76 padding: 4,
77 });
78 }
50 1 => vm.set_trace_format(Box::new(JsFormat)),79 1 => vm.trace_format = Box::new(JsFormat { max_trace: 20 }),
80 2 => {
81 vm.trace_format = Box::new(ExplainingFormat {
82 resolver: PathResolver::new_cwd_fallback(),
83 max_trace: 20,
84 });
85 }
51 _ => panic!("unknown trace format"),86 _ => panic!("unknown trace format"),
52 }87 }
53}88 }
89}
90
91#[cfg(feature = "interop-threading")]
92mod threading {
93 use std::{ffi::c_int, thread::ThreadId};
94
95 pub struct ThreadCTX {
96 interner: *mut jrsonnet_interner::interop::PoolState,
97 gc: *mut jrsonnet_gcmodule::interop::GcState,
98 }
99
100 /// Golang jrsonnet bindings require Jsonnet VM to be movable.
101 /// Jrsonnet uses `thread_local` in some places, thus making VM
102 /// immovable by default. By using `jrsonnet_exit_thread` and
103 /// `jrsonnet_reenter_thread`, you can move `thread_local` state to
104 /// where it is more convinient to use it.
105 ///
106 /// # Safety
107 ///
108 /// Current thread GC will be broken after this call, need to call
109 /// `jrsonet_enter_thread` before doing anything.
110 #[no_mangle]
111 pub unsafe extern "C" fn jrsonnet_exit_thread() -> *mut ThreadCTX {
112 Box::into_raw(Box::new(ThreadCTX {
113 interner: jrsonnet_interner::interop::exit_thread(),
114 gc: unsafe { jrsonnet_gcmodule::interop::exit_thread() },
115 }))
116 }
117
118 #[no_mangle]
119 pub extern "C" fn jrsonnet_reenter_thread(mut ctx: Box<ThreadCTX>) {
120 use std::ptr::null_mut;
121 assert!(
122 !ctx.interner.is_null() && !ctx.gc.is_null(),
123 "reused context?"
124 );
125 unsafe { jrsonnet_interner::interop::reenter_thread(ctx.interner) }
126 unsafe { jrsonnet_gcmodule::interop::reenter_thread(ctx.gc) }
127 // Just in case
128 ctx.interner = null_mut();
129 ctx.gc = null_mut();
130 }
131
132 // ThreadId is compatible with u64, and there is unstable cast
133 // method... But until it is stabilized, lets erase its type by
134 // boxing.
135 pub enum JrThreadId {}
136
137 #[no_mangle]
138 pub extern "C" fn jrsonnet_thread_id() -> *mut JrThreadId {
139 Box::into_raw(Box::new(std::thread::current().id())).cast()
140 }
141
142 #[no_mangle]
143 pub extern "C" fn jrsonnet_thread_id_compare(
144 a: *const JrThreadId,
145 b: *const JrThreadId,
146 ) -> c_int {
147 let a: &ThreadId = unsafe { *a.cast() };
148 let b: &ThreadId = unsafe { *b.cast() };
149 i32::from(*a == *b)
150 }
151
152 #[no_mangle]
153 pub unsafe extern "C" fn jrsonnet_thread_id_free(id: *mut JrThreadId) {
154 let _id: Box<ThreadId> = unsafe { Box::from_raw(id.cast()) };
155 }
156}
54157
modifiedbindings/jsonnet/src/lib.rsdiffbeforeafterboth
1#![allow(clippy::box_default)]1#![allow(clippy::box_default)]
22
3#[cfg(feature = "interop")]
4pub mod interop;3pub mod interop;
54
6pub mod import;5pub mod import;
1211
13use std::{12use std::{
14 alloc::Layout,13 alloc::Layout,
14 any::Any,
15 borrow::Cow,15 borrow::Cow,
16 cell::RefCell,
16 ffi::{CStr, CString, OsStr},17 ffi::{CStr, CString, OsStr},
17 os::raw::{c_char, c_double, c_int, c_uint},18 os::raw::{c_char, c_double, c_int, c_uint},
18 path::Path,19 path::{Path, PathBuf},
19};20};
2021
21use jrsonnet_evaluator::{22use jrsonnet_evaluator::{
22 apply_tla, bail,23 apply_tla, bail,
23 function::TlaArg,24 function::TlaArg,
24 gc::GcHashMap,25 gc::{GcHashMap, TraceBox},
25 manifest::{JsonFormat, ManifestFormat, ToStringFormat},26 manifest::{JsonFormat, ManifestFormat, ToStringFormat},
26 stack::set_stack_depth_limit,27 stack::set_stack_depth_limit,
27 tb,28 tb,
28 trace::{CompactFormat, PathResolver, TraceFormat},29 trace::{CompactFormat, PathResolver, TraceFormat},
29 FileImportResolver, IStr, Result, State, Val,30 FileImportResolver, IStr, ImportResolver, Result, State, Val,
30};31};
32use jrsonnet_gcmodule::Trace;
33use jrsonnet_parser::SourcePath;
34use jrsonnet_stdlib::ContextInitializer;
3135
32/// WASM stub36/// WASM stub
33#[cfg(target_arch = "wasm32")]37#[cfg(target_arch = "wasm32")]
72 }76 }
73}77}
78
79#[derive(Trace)]
80struct VMImportResolver {
81 #[trace(tracking(force))]
82 inner: RefCell<TraceBox<dyn ImportResolver>>,
83}
84impl VMImportResolver {
85 fn new(value: impl ImportResolver) -> Self {
86 Self {
87 inner: RefCell::new(tb!(value)),
88 }
89 }
90}
91impl ImportResolver for VMImportResolver {
92 fn load_file_contents(&self, resolved: &SourcePath) -> Result<Vec<u8>> {
93 self.inner.borrow().load_file_contents(resolved)
94 }
95
96 fn resolve_from(&self, from: &SourcePath, path: &str) -> Result<SourcePath> {
97 self.inner.borrow().resolve_from(from, path)
98 }
99
100 fn resolve_from_default(&self, path: &str) -> Result<SourcePath> {
101 self.inner.borrow().resolve_from_default(path)
102 }
103
104 fn resolve(&self, path: &Path) -> Result<SourcePath> {
105 self.inner.borrow().resolve(path)
106 }
107
108 fn as_any(&self) -> &dyn Any {
109 self
110 }
111 fn as_any_mut(&mut self) -> &mut dyn Any {
112 self
113 }
114}
74115
75pub struct VM {116pub struct VM {
76 state: State,117 state: State,
77 manifest_format: Box<dyn ManifestFormat>,118 manifest_format: Box<dyn ManifestFormat>,
78 trace_format: Box<dyn TraceFormat>,119 trace_format: Box<dyn TraceFormat>,
79 tla_args: GcHashMap<IStr, TlaArg>,120 tla_args: GcHashMap<IStr, TlaArg>,
80}121}
122impl VM {
123 fn replace_import_resolver(&self, resolver: impl ImportResolver) {
124 *self
125 .state
126 .import_resolver()
127 .as_any()
128 .downcast_ref::<VMImportResolver>()
129 .expect("valid resolver ty")
130 .inner
131 .borrow_mut() = tb!(resolver);
132 }
133 fn add_jpath(&self, path: PathBuf) {
134 self.state
135 .import_resolver()
136 .as_any()
137 .downcast_ref::<VMImportResolver>()
138 .expect("valid resolver ty")
139 .inner
140 .borrow_mut()
141 .as_any_mut()
142 .downcast_mut::<FileImportResolver>()
143 .expect("jpaths are not compatible with callback imports!")
144 .add_jpath(path);
145 }
146}
81147
82/// Creates a new Jsonnet virtual machine.148/// Creates a new Jsonnet virtual machine.
83#[no_mangle]149#[no_mangle]
84#[allow(clippy::box_default)]150#[allow(clippy::box_default)]
85pub extern "C" fn jsonnet_make() -> *mut VM {151pub extern "C" fn jsonnet_make() -> *mut VM {
86 let state = State::default();152 let mut state = State::builder();
87 state.settings_mut().import_resolver = tb!(FileImportResolver::default());
88 state.settings_mut().context_initializer = tb!(jrsonnet_stdlib::ContextInitializer::new(153 state
154 .import_resolver(VMImportResolver::new(FileImportResolver::default()))
89 PathResolver::new_cwd_fallback(),155 .context_initializer(ContextInitializer::new(PathResolver::new_cwd_fallback()));
90 ));156 let state = state.build();
91 Box::into_raw(Box::new(VM {157 Box::into_raw(Box::new(VM {
92 state,158 state,
93 manifest_format: Box::new(JsonFormat::default()),159 manifest_format: Box::new(JsonFormat::default()),