1 /* Copyright Mozilla Foundation
2 *
3 * Licensed under the Apache License, Version 2.0, or the MIT license,
4 * (the "Licenses") at your option. You may not use this file except in
5 * compliance with one of the Licenses. You may obtain copies of the
6 * Licenses at:
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 * http://opensource.org/licenses/MIT
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the Licenses is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the Licenses for the specific language governing permissions and
15 * limitations under the Licenses.
16 */
17
18 use bumpalo;
19 use env_logger;
20 use jsparagus::ast::source_atom_set::SourceAtomSet;
21 use jsparagus::ast::source_slice_list::SourceSliceList;
22 use jsparagus::ast::types::Program;
23 use jsparagus::emitter::{emit, EmitError, EmitOptions};
24 use jsparagus::parser::{parse_module, parse_script, ParseError, ParseOptions};
25 use jsparagus::stencil::gcthings::GCThing;
26 use jsparagus::stencil::regexp::RegExpItem;
27 use jsparagus::stencil::result::EmitResult;
28 use jsparagus::stencil::scope::{BindingName, ScopeData};
29 use jsparagus::stencil::scope_notes::ScopeNote;
30 use std::boxed::Box;
31 use std::cell::RefCell;
32 use std::os::raw::{c_char, c_void};
33 use std::rc::Rc;
34 use std::{mem, slice, str};
35
36 #[repr(C)]
37 pub struct CVec<T> {
38 pub data: *mut T,
39 pub len: usize,
40 pub capacity: usize,
41 }
42
43 impl<T> CVec<T> {
empty() -> CVec<T>44 fn empty() -> CVec<T> {
45 Self {
46 data: std::ptr::null_mut(),
47 len: 0,
48 capacity: 0,
49 }
50 }
51
from(mut v: Vec<T>) -> CVec<T>52 fn from(mut v: Vec<T>) -> CVec<T> {
53 let result = Self {
54 data: v.as_mut_ptr(),
55 len: v.len(),
56 capacity: v.capacity(),
57 };
58 mem::forget(v);
59 result
60 }
61
into(self) -> Vec<T>62 unsafe fn into(self) -> Vec<T> {
63 Vec::from_raw_parts(self.data, self.len, self.capacity)
64 }
65 }
66
67 #[repr(C)]
68 pub struct SmooshCompileOptions {
69 no_script_rval: bool,
70 }
71
72 #[repr(C)]
73 pub enum SmooshGCThingKind {
74 AtomIndex,
75 ScopeIndex,
76 RegExpIndex,
77 }
78
79 #[repr(C)]
80 pub struct SmooshGCThing {
81 kind: SmooshGCThingKind,
82 index: usize,
83 }
84
85 impl From<GCThing> for SmooshGCThing {
from(item: GCThing) -> Self86 fn from(item: GCThing) -> Self {
87 match item {
88 GCThing::Atom(index) => Self {
89 kind: SmooshGCThingKind::AtomIndex,
90 index: index.into(),
91 },
92 GCThing::Function(_index) => {
93 panic!("Not yet implemented");
94 }
95 GCThing::Scope(index) => Self {
96 kind: SmooshGCThingKind::ScopeIndex,
97 index: index.into(),
98 },
99 GCThing::RegExp(index) => Self {
100 kind: SmooshGCThingKind::RegExpIndex,
101 index: index.into(),
102 },
103 }
104 }
105 }
106
107 #[repr(C)]
108 pub enum SmooshScopeDataKind {
109 Global,
110 Lexical,
111 }
112
113 #[repr(C)]
114 pub struct SmooshBindingName {
115 name: usize,
116 is_closed_over: bool,
117 is_top_level_function: bool,
118 }
119
120 impl From<BindingName> for SmooshBindingName {
from(info: BindingName) -> Self121 fn from(info: BindingName) -> Self {
122 Self {
123 name: info.name.into(),
124 is_closed_over: info.is_closed_over,
125 is_top_level_function: info.is_top_level_function,
126 }
127 }
128 }
129
130 #[repr(C)]
131 pub struct SmooshScopeData {
132 kind: SmooshScopeDataKind,
133 bindings: CVec<SmooshBindingName>,
134 let_start: usize,
135 const_start: usize,
136 enclosing: usize,
137 first_frame_slot: u32,
138 }
139
140 impl From<ScopeData> for SmooshScopeData {
from(data: ScopeData) -> Self141 fn from(data: ScopeData) -> Self {
142 match data {
143 ScopeData::Global(data) => Self {
144 kind: SmooshScopeDataKind::Global,
145 bindings: CVec::from(data.bindings.into_iter().map(|x| x.into()).collect()),
146 let_start: data.let_start,
147 const_start: data.const_start,
148 enclosing: 0,
149 first_frame_slot: 0,
150 },
151 ScopeData::Lexical(data) => Self {
152 kind: SmooshScopeDataKind::Lexical,
153 bindings: CVec::from(data.bindings.into_iter().map(|x| x.into()).collect()),
154 let_start: 0,
155 const_start: data.const_start,
156 enclosing: data.enclosing.into(),
157 first_frame_slot: data.first_frame_slot.into(),
158 },
159 _ => {
160 panic!("Not yet implemented");
161 }
162 }
163 }
164 }
165
166 #[repr(C)]
167 pub struct SmooshScopeNote {
168 index: u32,
169 start: u32,
170 length: u32,
171 parent: u32,
172 }
173
174 impl From<ScopeNote> for SmooshScopeNote {
from(note: ScopeNote) -> Self175 fn from(note: ScopeNote) -> Self {
176 let start = usize::from(note.start) as u32;
177 let end = usize::from(note.end) as u32;
178 let parent = match note.parent {
179 Some(index) => usize::from(index) as u32,
180 None => std::u32::MAX,
181 };
182 Self {
183 index: usize::from(note.index) as u32,
184 start,
185 length: end - start,
186 parent,
187 }
188 }
189 }
190
191 #[repr(C)]
192 pub struct SmooshRegExpItem {
193 pattern: usize,
194 global: bool,
195 ignore_case: bool,
196 multi_line: bool,
197 dot_all: bool,
198 sticky: bool,
199 unicode: bool,
200 }
201
202 impl From<RegExpItem> for SmooshRegExpItem {
from(data: RegExpItem) -> Self203 fn from(data: RegExpItem) -> Self {
204 Self {
205 pattern: data.pattern.into(),
206 global: data.global,
207 ignore_case: data.ignore_case,
208 multi_line: data.multi_line,
209 dot_all: data.dot_all,
210 sticky: data.sticky,
211 unicode: data.unicode,
212 }
213 }
214 }
215
216 #[repr(C)]
217 pub struct SmooshResult {
218 unimplemented: bool,
219 error: CVec<u8>,
220 bytecode: CVec<u8>,
221 gcthings: CVec<SmooshGCThing>,
222 scopes: CVec<SmooshScopeData>,
223 scope_notes: CVec<SmooshScopeNote>,
224 regexps: CVec<SmooshRegExpItem>,
225
226 /// Line and column numbers for the first character of source.
227 lineno: usize,
228 column: usize,
229
230 /// Offset of main entry point from code, after predef'ing prologue.
231 main_offset: usize,
232
233 /// Fixed frame slots.
234 max_fixed_slots: u32,
235
236 /// Maximum stack depth before any instruction.
237 ///
238 /// This value is a function of `bytecode`: there's only one correct value
239 /// for a given script.
240 maximum_stack_depth: u32,
241
242 /// Index into the gcthings array of the body scope.
243 body_scope_index: u32,
244
245 /// Number of instructions in this script that have IC entries.
246 ///
247 /// A function of `bytecode`. See `JOF_IC`.
248 num_ic_entries: u32,
249
250 /// Number of instructions in this script that have JOF_TYPESET.
251 num_type_sets: u32,
252
253 /// `See BaseScript::ImmutableFlags`.
254 immutable_flags: u32,
255
256 all_atoms: *mut c_void,
257 all_atoms_len: usize,
258 slices: *mut c_void,
259 slices_len: usize,
260 allocator: *mut c_void,
261 }
262
263 impl SmooshResult {
unimplemented() -> Self264 fn unimplemented() -> Self {
265 Self::unimplemented_or_error(true, CVec::empty())
266 }
267
error(message: String) -> Self268 fn error(message: String) -> Self {
269 let error = CVec::from(format!("{}\0", message).into_bytes());
270 Self::unimplemented_or_error(false, error)
271 }
272
unimplemented_or_error(unimplemented: bool, error: CVec<u8>) -> Self273 fn unimplemented_or_error(unimplemented: bool, error: CVec<u8>) -> Self {
274 Self {
275 unimplemented,
276 error,
277 bytecode: CVec::empty(),
278 gcthings: CVec::empty(),
279 scopes: CVec::empty(),
280 scope_notes: CVec::empty(),
281 regexps: CVec::empty(),
282 lineno: 0,
283 column: 0,
284 main_offset: 0,
285 max_fixed_slots: 0,
286 maximum_stack_depth: 0,
287 body_scope_index: 0,
288 num_ic_entries: 0,
289 num_type_sets: 0,
290 immutable_flags: 0,
291
292 all_atoms: std::ptr::null_mut(),
293 all_atoms_len: 0,
294 slices: std::ptr::null_mut(),
295 slices_len: 0,
296 allocator: std::ptr::null_mut(),
297 }
298 }
299 }
300
301 enum SmooshError {
302 GenericError(String),
303 NotImplemented,
304 }
305
306 #[no_mangle]
smoosh_init()307 pub unsafe extern "C" fn smoosh_init() {
308 // Gecko might set a logger before we do, which is all fine; try to
309 // initialize ours, and reset the FilterLevel env_logger::try_init might
310 // have set to what it was in case of initialization failure
311 let filter = log::max_level();
312 match env_logger::try_init() {
313 Ok(_) => {}
314 Err(_) => {
315 log::set_max_level(filter);
316 }
317 }
318 }
319
320 #[no_mangle]
smoosh_run( text: *const u8, text_len: usize, options: &SmooshCompileOptions, ) -> SmooshResult321 pub unsafe extern "C" fn smoosh_run(
322 text: *const u8,
323 text_len: usize,
324 options: &SmooshCompileOptions,
325 ) -> SmooshResult {
326 let text = str::from_utf8(slice::from_raw_parts(text, text_len)).expect("Invalid UTF8");
327 let allocator = Box::new(bumpalo::Bump::new());
328 match smoosh(&allocator, text, options) {
329 Ok(result) => {
330 let bytecode = CVec::from(result.script.bytecode);
331 let gcthings = CVec::from(
332 result
333 .script
334 .base
335 .gcthings
336 .into_iter()
337 .map(|x| x.into())
338 .collect(),
339 );
340 let scopes = CVec::from(result.scopes.into_iter().map(|x| x.into()).collect());
341 let scope_notes = CVec::from(
342 result
343 .script
344 .scope_notes
345 .into_iter()
346 .map(|x| x.into())
347 .collect(),
348 );
349 let regexps = CVec::from(
350 result
351 .script
352 .regexps
353 .into_iter()
354 .map(|x| x.into())
355 .collect(),
356 );
357
358 let lineno = result.script.lineno;
359 let column = result.script.column;
360 let main_offset = result.script.main_offset;
361 let max_fixed_slots = result.script.max_fixed_slots.into();
362 let maximum_stack_depth = result.script.maximum_stack_depth;
363 let body_scope_index = result.script.body_scope_index;
364 let num_ic_entries = result.script.num_ic_entries;
365 let num_type_sets = result.script.num_type_sets;
366
367 let immutable_flags = result.script.base.immutable_flags.into();
368
369 let all_atoms_len = result.atoms.len();
370 let all_atoms = Box::new(result.atoms);
371 let raw_all_atoms = Box::into_raw(all_atoms);
372 let opaque_all_atoms = raw_all_atoms as *mut c_void;
373
374 let slices_len = result.slices.len();
375 let slices = Box::new(result.slices);
376 let raw_slices = Box::into_raw(slices);
377 let opaque_slices = raw_slices as *mut c_void;
378
379 let raw_allocator = Box::into_raw(allocator);
380 let opaque_allocator = raw_allocator as *mut c_void;
381
382 SmooshResult {
383 unimplemented: false,
384 error: CVec::empty(),
385 bytecode,
386 gcthings,
387 scopes,
388 scope_notes,
389 regexps,
390 lineno,
391 column,
392 main_offset,
393 max_fixed_slots,
394 maximum_stack_depth,
395 body_scope_index,
396 num_ic_entries,
397 num_type_sets,
398 immutable_flags,
399
400 all_atoms: opaque_all_atoms,
401 all_atoms_len,
402 slices: opaque_slices,
403 slices_len,
404 allocator: opaque_allocator,
405 }
406 }
407 Err(SmooshError::GenericError(message)) => SmooshResult::error(message),
408 Err(SmooshError::NotImplemented) => SmooshResult::unimplemented(),
409 }
410 }
411
412 #[repr(C)]
413 pub struct SmooshParseResult {
414 unimplemented: bool,
415 error: CVec<u8>,
416 }
417
convert_parse_result<'alloc, T>(r: jsparagus::parser::Result<T>) -> SmooshParseResult418 fn convert_parse_result<'alloc, T>(r: jsparagus::parser::Result<T>) -> SmooshParseResult {
419 match r {
420 Ok(_) => SmooshParseResult {
421 unimplemented: false,
422 error: CVec::empty(),
423 },
424 Err(err) => match *err {
425 ParseError::NotImplemented(_) => {
426 let message = err.message();
427 SmooshParseResult {
428 unimplemented: true,
429 error: CVec::from(format!("{}\0", message).into_bytes()),
430 }
431 }
432 _ => {
433 let message = err.message();
434 SmooshParseResult {
435 unimplemented: false,
436 error: CVec::from(format!("{}\0", message).into_bytes()),
437 }
438 }
439 },
440 }
441 }
442
443 #[no_mangle]
smoosh_test_parse_script( text: *const u8, text_len: usize, ) -> SmooshParseResult444 pub unsafe extern "C" fn smoosh_test_parse_script(
445 text: *const u8,
446 text_len: usize,
447 ) -> SmooshParseResult {
448 let text = match str::from_utf8(slice::from_raw_parts(text, text_len)) {
449 Ok(text) => text,
450 Err(_) => {
451 return SmooshParseResult {
452 unimplemented: false,
453 error: CVec::from("Invalid UTF-8\0".to_string().into_bytes()),
454 };
455 }
456 };
457 let allocator = bumpalo::Bump::new();
458 let parse_options = ParseOptions::new();
459 let atoms = Rc::new(RefCell::new(SourceAtomSet::new()));
460 let slices = Rc::new(RefCell::new(SourceSliceList::new()));
461 convert_parse_result(parse_script(
462 &allocator,
463 text,
464 &parse_options,
465 atoms,
466 slices,
467 ))
468 }
469
470 #[no_mangle]
smoosh_test_parse_module( text: *const u8, text_len: usize, ) -> SmooshParseResult471 pub unsafe extern "C" fn smoosh_test_parse_module(
472 text: *const u8,
473 text_len: usize,
474 ) -> SmooshParseResult {
475 let text = match str::from_utf8(slice::from_raw_parts(text, text_len)) {
476 Ok(text) => text,
477 Err(_) => {
478 return SmooshParseResult {
479 unimplemented: false,
480 error: CVec::from("Invalid UTF-8\0".to_string().into_bytes()),
481 };
482 }
483 };
484 let allocator = bumpalo::Bump::new();
485 let parse_options = ParseOptions::new();
486 let atoms = Rc::new(RefCell::new(SourceAtomSet::new()));
487 let slices = Rc::new(RefCell::new(SourceSliceList::new()));
488 convert_parse_result(parse_module(
489 &allocator,
490 text,
491 &parse_options,
492 atoms,
493 slices,
494 ))
495 }
496
497 #[no_mangle]
smoosh_free_parse_result(result: SmooshParseResult)498 pub unsafe extern "C" fn smoosh_free_parse_result(result: SmooshParseResult) {
499 let _ = result.error.into();
500 }
501
502 #[no_mangle]
smoosh_get_atom_at(result: SmooshResult, index: usize) -> *const c_char503 pub unsafe extern "C" fn smoosh_get_atom_at(result: SmooshResult, index: usize) -> *const c_char {
504 let all_atoms = result.all_atoms as *const Vec<&str>;
505 let atom = (*all_atoms)[index];
506 atom.as_ptr() as *const c_char
507 }
508
509 #[no_mangle]
smoosh_get_atom_len_at(result: SmooshResult, index: usize) -> usize510 pub unsafe extern "C" fn smoosh_get_atom_len_at(result: SmooshResult, index: usize) -> usize {
511 let all_atoms = result.all_atoms as *const Vec<&str>;
512 let atom = (*all_atoms)[index];
513 atom.len()
514 }
515
516 #[no_mangle]
smoosh_get_slice_at(result: SmooshResult, index: usize) -> *const c_char517 pub unsafe extern "C" fn smoosh_get_slice_at(result: SmooshResult, index: usize) -> *const c_char {
518 let slices = result.slices as *const Vec<&str>;
519 let slice = (*slices)[index];
520 slice.as_ptr() as *const c_char
521 }
522
523 #[no_mangle]
smoosh_get_slice_len_at(result: SmooshResult, index: usize) -> usize524 pub unsafe extern "C" fn smoosh_get_slice_len_at(result: SmooshResult, index: usize) -> usize {
525 let slices = result.slices as *const Vec<&str>;
526 let slice = (*slices)[index];
527 slice.len()
528 }
529
530 #[no_mangle]
smoosh_free(result: SmooshResult)531 pub unsafe extern "C" fn smoosh_free(result: SmooshResult) {
532 let _ = result.error.into();
533 let _ = result.bytecode.into();
534 let _ = result.gcthings.into();
535 let _ = result.scopes.into();
536 let _ = result.scope_notes.into();
537 let _ = result.regexps.into();
538 //Vec::from_raw_parts(bytecode.data, bytecode.len, bytecode.capacity);
539
540 if !result.all_atoms.is_null() {
541 let _ = Box::from_raw(result.all_atoms as *mut Vec<&str>);
542 }
543 if !result.slices.is_null() {
544 let _ = Box::from_raw(result.slices as *mut Vec<&str>);
545 }
546 if !result.allocator.is_null() {
547 let _ = Box::from_raw(result.allocator as *mut bumpalo::Bump);
548 }
549 }
550
smoosh<'alloc>( allocator: &'alloc bumpalo::Bump, text: &'alloc str, options: &SmooshCompileOptions, ) -> Result<EmitResult<'alloc>, SmooshError>551 fn smoosh<'alloc>(
552 allocator: &'alloc bumpalo::Bump,
553 text: &'alloc str,
554 options: &SmooshCompileOptions,
555 ) -> Result<EmitResult<'alloc>, SmooshError> {
556 let parse_options = ParseOptions::new();
557 let atoms = Rc::new(RefCell::new(SourceAtomSet::new()));
558 let slices = Rc::new(RefCell::new(SourceSliceList::new()));
559 let parse_result = match parse_script(
560 &allocator,
561 text,
562 &parse_options,
563 atoms.clone(),
564 slices.clone(),
565 ) {
566 Ok(result) => result,
567 Err(err) => match *err {
568 ParseError::NotImplemented(_) => {
569 println!("Unimplemented: {}", err.message());
570 return Err(SmooshError::NotImplemented);
571 }
572 _ => {
573 return Err(SmooshError::GenericError(err.message()));
574 }
575 },
576 };
577
578 let mut emit_options = EmitOptions::new();
579 emit_options.no_script_rval = options.no_script_rval;
580 let script = parse_result.unbox();
581 match emit(
582 allocator.alloc(Program::Script(script)),
583 &emit_options,
584 atoms.replace(SourceAtomSet::new_uninitialized()),
585 slices.replace(SourceSliceList::new()),
586 ) {
587 Ok(result) => Ok(result),
588 Err(EmitError::NotImplemented(message)) => {
589 println!("Unimplemented: {}", message);
590 return Err(SmooshError::NotImplemented);
591 }
592 }
593 }
594
595 #[cfg(test)]
596 mod tests {
597 #[test]
it_works()598 fn it_works() {
599 assert_eq!(2 + 2, 4);
600 }
601 }
602