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