diff options
Diffstat (limited to '')
-rw-r--r-- | js/src/frontend/smoosh/Cargo.toml | 23 | ||||
-rw-r--r-- | js/src/frontend/smoosh/build.rs | 30 | ||||
-rw-r--r-- | js/src/frontend/smoosh/cbindgen.toml | 19 | ||||
-rw-r--r-- | js/src/frontend/smoosh/moz.build | 15 | ||||
-rw-r--r-- | js/src/frontend/smoosh/src/lib.rs | 769 |
5 files changed, 856 insertions, 0 deletions
diff --git a/js/src/frontend/smoosh/Cargo.toml b/js/src/frontend/smoosh/Cargo.toml new file mode 100644 index 0000000000..8fa413eeff --- /dev/null +++ b/js/src/frontend/smoosh/Cargo.toml @@ -0,0 +1,23 @@ +[package] +name = "smoosh" +version = "0.1.0" +authors = ["The jsparagus Project Developers"] +edition = "2018" +license = "MIT/Apache-2.0" + +[dependencies] +bumpalo = "3.4.0" +log = "0.4" +# Setup RUST_LOG logging. +# Disable regex feature for code size. +env_logger = {version = "0.8", default-features = false} +# For non-jsparagus developers. +jsparagus = { git = "https://github.com/mozilla-spidermonkey/jsparagus", rev = "d910c7a0f2f836cc70ed14bb92b8d0437b4e4a24" } +# For local development, replace above with +# jsparagus = { path = "{path to jsparagus}" } + +[build-dependencies] +# For non-jsparagus developers. +jsparagus = { git = "https://github.com/mozilla-spidermonkey/jsparagus", rev = "d910c7a0f2f836cc70ed14bb92b8d0437b4e4a24" } +# For local development, replace above with +# jsparagus = { path = "{path to jsparagus}" } diff --git a/js/src/frontend/smoosh/build.rs b/js/src/frontend/smoosh/build.rs new file mode 100644 index 0000000000..3ae2cf54cd --- /dev/null +++ b/js/src/frontend/smoosh/build.rs @@ -0,0 +1,30 @@ +use jsparagus::stencil::opcode_info; + +fn compare(name: &str, orig: &str, copied: &str) { + if copied != orig { + panic!( + "{} is out of sync. \ + It's possible that the bytecode generated by jsparagus is \ + based on older opcodes. Please run \ + update_stencil.py in jsparagus. \ + You can disable this check by setting \ + JS_SMOOSH_DISABLE_OPCODE_CHECK environment variable.", + name + ); + } +} + +fn main() { + match std::env::var("JS_SMOOSH_DISABLE_OPCODE_CHECK") { + Ok(_) => { + return; + } + Err(_) => {} + }; + + compare( + "Opcodes.h", + include_str!("../../vm/Opcodes.h"), + opcode_info::get_opcodes_source(), + ); +} diff --git a/js/src/frontend/smoosh/cbindgen.toml b/js/src/frontend/smoosh/cbindgen.toml new file mode 100644 index 0000000000..71a9568b35 --- /dev/null +++ b/js/src/frontend/smoosh/cbindgen.toml @@ -0,0 +1,19 @@ +header = """/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */""" +autogen_warning = """/* DO NOT MODIFY THIS MANUALLY! This file was generated using cbindgen. See RunCbindgen.py */ + +#include "mozilla/Assertions.h" // MOZ_ASSERT +""" +include_version = true +braces = "SameLine" +line_length = 100 +tab_width = 2 +language = "C++" + +[enum] +derive_helper_methods = true +derive_const_casts = true +derive_mut_casts = true + +cast_assert_name = "MOZ_ASSERT" diff --git a/js/src/frontend/smoosh/moz.build b/js/src/frontend/smoosh/moz.build new file mode 100644 index 0000000000..d75c4c18ba --- /dev/null +++ b/js/src/frontend/smoosh/moz.build @@ -0,0 +1,15 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=python: +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +FINAL_LIBRARY = "js" + +# Includes should be relative to parent path +LOCAL_INCLUDES += ["!../..", "../.."] + +include("../../js-config.mozbuild") +include("../../js-cxxflags.mozbuild") + +DIRS += ["../../rust"] diff --git a/js/src/frontend/smoosh/src/lib.rs b/js/src/frontend/smoosh/src/lib.rs new file mode 100644 index 0000000000..f663706533 --- /dev/null +++ b/js/src/frontend/smoosh/src/lib.rs @@ -0,0 +1,769 @@ +/* Copyright Mozilla Foundation + * + * Licensed under the Apache License, Version 2.0, or the MIT license, + * (the "Licenses") at your option. You may not use this file except in + * compliance with one of the Licenses. You may obtain copies of the + * Licenses at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * http://opensource.org/licenses/MIT + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the Licenses is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the Licenses for the specific language governing permissions and + * limitations under the Licenses. + */ + +use bumpalo; +use env_logger; +use jsparagus::ast::source_atom_set::SourceAtomSet; +use jsparagus::ast::source_slice_list::SourceSliceList; +use jsparagus::ast::types::Program; +use jsparagus::emitter::{emit, EmitError, EmitOptions}; +use jsparagus::parser::{parse_module, parse_script, ParseError, ParseOptions}; +use jsparagus::stencil::gcthings::GCThing; +use jsparagus::stencil::regexp::RegExpItem; +use jsparagus::stencil::result::EmitResult; +use jsparagus::stencil::scope::{BindingName, ScopeData}; +use jsparagus::stencil::scope_notes::ScopeNote; +use jsparagus::stencil::script::{ImmutableScriptData, ScriptStencil, SourceExtent}; +use std::boxed::Box; +use std::cell::RefCell; +use std::collections::HashMap; +use std::convert::TryInto; +use std::os::raw::{c_char, c_void}; +use std::rc::Rc; +use std::{mem, slice, str}; + +#[repr(C)] +pub struct CVec<T> { + pub data: *mut T, + pub len: usize, + pub capacity: usize, +} + +impl<T> CVec<T> { + fn empty() -> CVec<T> { + Self { + data: std::ptr::null_mut(), + len: 0, + capacity: 0, + } + } + + fn from(mut v: Vec<T>) -> CVec<T> { + let result = Self { + data: v.as_mut_ptr(), + len: v.len(), + capacity: v.capacity(), + }; + mem::forget(v); + result + } + + unsafe fn into(self) -> Vec<T> { + Vec::from_raw_parts(self.data, self.len, self.capacity) + } +} + +#[repr(C)] +pub struct SmooshCompileOptions { + no_script_rval: bool, +} + +#[repr(C, u8)] +pub enum SmooshGCThing { + Null, + Atom(usize), + Function(usize), + Scope(usize), + RegExp(usize), +} + +fn convert_gcthing(item: GCThing, scope_index_map: &HashMap<usize, usize>) -> SmooshGCThing { + match item { + GCThing::Null => SmooshGCThing::Null, + GCThing::Atom(index) => SmooshGCThing::Atom(index.into()), + GCThing::Function(index) => SmooshGCThing::Function(index.into()), + GCThing::RegExp(index) => SmooshGCThing::RegExp(index.into()), + GCThing::Scope(index) => { + let mapped_index = *scope_index_map + .get(&index.into()) + .expect("Scope map should be populated"); + SmooshGCThing::Scope(mapped_index) + } + } +} + +#[repr(C)] +pub struct SmooshBindingName { + name: usize, + is_closed_over: bool, + is_top_level_function: bool, +} + +impl From<BindingName> for SmooshBindingName { + fn from(info: BindingName) -> Self { + Self { + name: info.name.into(), + is_closed_over: info.is_closed_over, + is_top_level_function: info.is_top_level_function, + } + } +} + +#[repr(C)] +pub struct SmooshGlobalScopeData { + bindings: CVec<SmooshBindingName>, + let_start: usize, + const_start: usize, +} + +#[repr(C)] +pub struct SmooshVarScopeData { + bindings: CVec<SmooshBindingName>, + enclosing: usize, + function_has_extensible_scope: bool, + first_frame_slot: u32, +} + +#[repr(C)] +pub struct SmooshLexicalScopeData { + bindings: CVec<SmooshBindingName>, + const_start: usize, + enclosing: usize, + first_frame_slot: u32, +} + +#[repr(C)] +pub struct SmooshFunctionScopeData { + bindings: CVec<COption<SmooshBindingName>>, + has_parameter_exprs: bool, + non_positional_formal_start: usize, + var_start: usize, + enclosing: usize, + first_frame_slot: u32, + function_index: usize, + is_arrow: bool, +} + +#[repr(C, u8)] +pub enum SmooshScopeData { + Global(SmooshGlobalScopeData), + Var(SmooshVarScopeData), + Lexical(SmooshLexicalScopeData), + Function(SmooshFunctionScopeData), +} + +/// Convert single Scope data, resolving enclosing index with scope_index_map. +fn convert_scope(scope: ScopeData, scope_index_map: &mut HashMap<usize, usize>) -> SmooshScopeData { + match scope { + ScopeData::Alias(_) => panic!("alias should be handled in convert_scopes"), + ScopeData::Global(data) => SmooshScopeData::Global(SmooshGlobalScopeData { + bindings: CVec::from(data.base.bindings.into_iter().map(|x| x.into()).collect()), + let_start: data.let_start, + const_start: data.const_start, + }), + ScopeData::Var(data) => { + let enclosing: usize = data.enclosing.into(); + SmooshScopeData::Var(SmooshVarScopeData { + bindings: CVec::from(data.base.bindings.into_iter().map(|x| x.into()).collect()), + enclosing: *scope_index_map + .get(&enclosing) + .expect("Alias target should be earlier index"), + function_has_extensible_scope: data.function_has_extensible_scope, + first_frame_slot: data.first_frame_slot.into(), + }) + } + ScopeData::Lexical(data) => { + let enclosing: usize = data.enclosing.into(); + SmooshScopeData::Lexical(SmooshLexicalScopeData { + bindings: CVec::from(data.base.bindings.into_iter().map(|x| x.into()).collect()), + const_start: data.const_start, + enclosing: *scope_index_map + .get(&enclosing) + .expect("Alias target should be earlier index"), + first_frame_slot: data.first_frame_slot.into(), + }) + } + ScopeData::Function(data) => { + let enclosing: usize = data.enclosing.into(); + SmooshScopeData::Function(SmooshFunctionScopeData { + bindings: CVec::from( + data.base + .bindings + .into_iter() + .map(|x| COption::from(x.map(|x| x.into()))) + .collect(), + ), + has_parameter_exprs: data.has_parameter_exprs, + non_positional_formal_start: data.non_positional_formal_start, + var_start: data.var_start, + enclosing: *scope_index_map + .get(&enclosing) + .expect("Alias target should be earlier index"), + first_frame_slot: data.first_frame_slot.into(), + function_index: data.function_index.into(), + is_arrow: data.is_arrow, + }) + } + } +} + +/// Convert list of Scope data, removing aliases. +/// Also create a map between original index into index into result vector +/// without aliases. +fn convert_scopes( + scopes: Vec<ScopeData>, + scope_index_map: &mut HashMap<usize, usize>, +) -> CVec<SmooshScopeData> { + let mut result = Vec::with_capacity(scopes.len()); + for (i, scope) in scopes.into_iter().enumerate() { + if let ScopeData::Alias(index) = scope { + let mapped_index = *scope_index_map + .get(&index.into()) + .expect("Alias target should be earlier index"); + scope_index_map.insert(i, mapped_index); + + continue; + } + + scope_index_map.insert(i, result.len()); + result.push(convert_scope(scope, scope_index_map)) + } + + CVec::from(result) +} + +#[repr(C)] +pub struct SmooshScopeNote { + index: u32, + start: u32, + length: u32, + parent: u32, +} + +impl From<ScopeNote> for SmooshScopeNote { + fn from(note: ScopeNote) -> Self { + let start = usize::from(note.start) as u32; + let end = usize::from(note.end) as u32; + let parent = match note.parent { + Some(index) => usize::from(index) as u32, + None => std::u32::MAX, + }; + Self { + index: usize::from(note.index) as u32, + start, + length: end - start, + parent, + } + } +} + +#[repr(C)] +pub struct SmooshRegExpItem { + pattern: usize, + global: bool, + ignore_case: bool, + multi_line: bool, + dot_all: bool, + sticky: bool, + unicode: bool, +} + +impl From<RegExpItem> for SmooshRegExpItem { + fn from(data: RegExpItem) -> Self { + Self { + pattern: data.pattern.into(), + global: data.global, + ignore_case: data.ignore_case, + multi_line: data.multi_line, + dot_all: data.dot_all, + sticky: data.sticky, + unicode: data.unicode, + } + } +} + +#[repr(C)] +pub struct SmooshImmutableScriptData { + pub main_offset: u32, + pub nfixed: u32, + pub nslots: u32, + pub body_scope_index: u32, + pub num_ic_entries: u32, + pub fun_length: u16, + pub bytecode: CVec<u8>, + pub scope_notes: CVec<SmooshScopeNote>, +} + +#[repr(C)] +pub struct SmooshSourceExtent { + pub source_start: u32, + pub source_end: u32, + pub to_string_start: u32, + pub to_string_end: u32, + pub lineno: u32, + pub column: u32, +} + +#[repr(C, u8)] +pub enum COption<T> { + Some(T), + None, +} + +impl<T> COption<T> { + fn from(v: Option<T>) -> Self { + match v { + Option::Some(v) => COption::Some(v), + Option::None => COption::None, + } + } +} + +#[repr(C)] +pub struct SmooshScriptStencil { + pub immutable_flags: u32, + pub gcthings: CVec<SmooshGCThing>, + pub immutable_script_data: COption<usize>, + pub extent: SmooshSourceExtent, + pub fun_name: COption<usize>, + pub fun_nargs: u16, + pub fun_flags: u16, + pub lazy_function_enclosing_scope_index: COption<usize>, + pub is_standalone_function: bool, + pub was_function_emitted: bool, + pub is_singleton_function: bool, +} + +#[repr(C)] +pub struct SmooshResult { + unimplemented: bool, + error: CVec<u8>, + + scopes: CVec<SmooshScopeData>, + regexps: CVec<SmooshRegExpItem>, + + scripts: CVec<SmooshScriptStencil>, + script_data_list: CVec<SmooshImmutableScriptData>, + + all_atoms: *mut c_void, + all_atoms_len: usize, + slices: *mut c_void, + slices_len: usize, + allocator: *mut c_void, +} + +impl SmooshResult { + fn unimplemented() -> Self { + Self::unimplemented_or_error(true, CVec::empty()) + } + + fn error(message: String) -> Self { + let error = CVec::from(format!("{}\0", message).into_bytes()); + Self::unimplemented_or_error(false, error) + } + + fn unimplemented_or_error(unimplemented: bool, error: CVec<u8>) -> Self { + Self { + unimplemented, + error, + + scopes: CVec::empty(), + regexps: CVec::empty(), + + scripts: CVec::empty(), + script_data_list: CVec::empty(), + + all_atoms: std::ptr::null_mut(), + all_atoms_len: 0, + slices: std::ptr::null_mut(), + slices_len: 0, + allocator: std::ptr::null_mut(), + } + } +} + +enum SmooshError { + GenericError(String), + NotImplemented, +} + +#[no_mangle] +pub unsafe extern "C" fn smoosh_init() { + // Gecko might set a logger before we do, which is all fine; try to + // initialize ours, and reset the FilterLevel env_logger::try_init might + // have set to what it was in case of initialization failure + let filter = log::max_level(); + match env_logger::try_init() { + Ok(_) => {} + Err(_) => { + log::set_max_level(filter); + } + } +} + +fn convert_extent(extent: SourceExtent) -> SmooshSourceExtent { + SmooshSourceExtent { + source_start: extent.source_start, + source_end: extent.source_end, + to_string_start: extent.to_string_start, + to_string_end: extent.to_string_end, + lineno: extent.lineno, + column: extent.column, + } +} + +fn convert_script( + script: ScriptStencil, + scope_index_map: &HashMap<usize, usize>, +) -> SmooshScriptStencil { + let immutable_flags = script.immutable_flags.into(); + + let gcthings = CVec::from( + script + .gcthings + .into_iter() + .map(|x| convert_gcthing(x, scope_index_map)) + .collect(), + ); + + let immutable_script_data = COption::from(script.immutable_script_data.map(|n| n.into())); + + let extent = convert_extent(script.extent); + + let fun_name = COption::from(script.fun_name.map(|n| n.into())); + let fun_nargs = script.fun_nargs; + let fun_flags = script.fun_flags.into(); + + let lazy_function_enclosing_scope_index = + COption::from(script.lazy_function_enclosing_scope_index.map(|index| { + *scope_index_map + .get(&index.into()) + .expect("Alias target should be earlier index") + })); + + let is_standalone_function = script.is_standalone_function; + let was_function_emitted = script.was_function_emitted; + let is_singleton_function = script.is_singleton_function; + + SmooshScriptStencil { + immutable_flags, + gcthings, + immutable_script_data, + extent, + fun_name, + fun_nargs, + fun_flags, + lazy_function_enclosing_scope_index, + is_standalone_function, + was_function_emitted, + is_singleton_function, + } +} + +fn convert_script_data(script_data: ImmutableScriptData) -> SmooshImmutableScriptData { + let main_offset = script_data.main_offset; + let nfixed = script_data.nfixed.into(); + let nslots = script_data.nslots; + let body_scope_index = script_data.body_scope_index; + let num_ic_entries = script_data.num_ic_entries; + let fun_length = script_data.fun_length; + + let bytecode = CVec::from(script_data.bytecode); + + let scope_notes = CVec::from( + script_data + .scope_notes + .into_iter() + .map(|x| x.into()) + .collect(), + ); + + SmooshImmutableScriptData { + main_offset, + nfixed, + nslots, + body_scope_index, + num_ic_entries, + fun_length, + bytecode, + scope_notes, + } +} + +#[no_mangle] +pub unsafe extern "C" fn smoosh_run( + text: *const u8, + text_len: usize, + options: &SmooshCompileOptions, +) -> SmooshResult { + let text = str::from_utf8(slice::from_raw_parts(text, text_len)).expect("Invalid UTF8"); + let allocator = Box::new(bumpalo::Bump::new()); + match smoosh(&allocator, text, options) { + Ok(result) => { + let mut scope_index_map = HashMap::new(); + + let scopes = convert_scopes(result.scopes, &mut scope_index_map); + let regexps = CVec::from(result.regexps.into_iter().map(|x| x.into()).collect()); + + let scripts = CVec::from( + result + .scripts + .into_iter() + .map(|x| convert_script(x, &scope_index_map)) + .collect(), + ); + + let script_data_list = CVec::from( + result + .script_data_list + .into_iter() + .map(convert_script_data) + .collect(), + ); + + let all_atoms_len = result.atoms.len(); + let all_atoms = Box::new(result.atoms); + let raw_all_atoms = Box::into_raw(all_atoms); + let opaque_all_atoms = raw_all_atoms as *mut c_void; + + let slices_len = result.slices.len(); + let slices = Box::new(result.slices); + let raw_slices = Box::into_raw(slices); + let opaque_slices = raw_slices as *mut c_void; + + let raw_allocator = Box::into_raw(allocator); + let opaque_allocator = raw_allocator as *mut c_void; + + SmooshResult { + unimplemented: false, + error: CVec::empty(), + + scopes, + regexps, + + scripts, + script_data_list, + + all_atoms: opaque_all_atoms, + all_atoms_len, + slices: opaque_slices, + slices_len, + allocator: opaque_allocator, + } + } + Err(SmooshError::GenericError(message)) => SmooshResult::error(message), + Err(SmooshError::NotImplemented) => SmooshResult::unimplemented(), + } +} + +#[repr(C)] +pub struct SmooshParseResult { + unimplemented: bool, + error: CVec<u8>, +} + +fn convert_parse_result<'alloc, T>(r: jsparagus::parser::Result<T>) -> SmooshParseResult { + match r { + Ok(_) => SmooshParseResult { + unimplemented: false, + error: CVec::empty(), + }, + Err(err) => match *err { + ParseError::NotImplemented(_) => { + let message = err.message(); + SmooshParseResult { + unimplemented: true, + error: CVec::from(format!("{}\0", message).into_bytes()), + } + } + _ => { + let message = err.message(); + SmooshParseResult { + unimplemented: false, + error: CVec::from(format!("{}\0", message).into_bytes()), + } + } + }, + } +} + +#[no_mangle] +pub unsafe extern "C" fn smoosh_test_parse_script( + text: *const u8, + text_len: usize, +) -> SmooshParseResult { + let text = match str::from_utf8(slice::from_raw_parts(text, text_len)) { + Ok(text) => text, + Err(_) => { + return SmooshParseResult { + unimplemented: false, + error: CVec::from("Invalid UTF-8\0".to_string().into_bytes()), + }; + } + }; + let allocator = bumpalo::Bump::new(); + let parse_options = ParseOptions::new(); + let atoms = Rc::new(RefCell::new(SourceAtomSet::new())); + let slices = Rc::new(RefCell::new(SourceSliceList::new())); + convert_parse_result(parse_script( + &allocator, + text, + &parse_options, + atoms, + slices, + )) +} + +#[no_mangle] +pub unsafe extern "C" fn smoosh_test_parse_module( + text: *const u8, + text_len: usize, +) -> SmooshParseResult { + let text = match str::from_utf8(slice::from_raw_parts(text, text_len)) { + Ok(text) => text, + Err(_) => { + return SmooshParseResult { + unimplemented: false, + error: CVec::from("Invalid UTF-8\0".to_string().into_bytes()), + }; + } + }; + let allocator = bumpalo::Bump::new(); + let parse_options = ParseOptions::new(); + let atoms = Rc::new(RefCell::new(SourceAtomSet::new())); + let slices = Rc::new(RefCell::new(SourceSliceList::new())); + convert_parse_result(parse_module( + &allocator, + text, + &parse_options, + atoms, + slices, + )) +} + +#[no_mangle] +pub unsafe extern "C" fn smoosh_free_parse_result(result: SmooshParseResult) { + let _ = result.error.into(); +} + +#[no_mangle] +pub unsafe extern "C" fn smoosh_get_atom_at(result: SmooshResult, index: usize) -> *const c_char { + let all_atoms = result.all_atoms as *const Vec<&str>; + let atom = (*all_atoms)[index]; + atom.as_ptr() as *const c_char +} + +#[no_mangle] +pub unsafe extern "C" fn smoosh_get_atom_len_at(result: SmooshResult, index: usize) -> usize { + let all_atoms = result.all_atoms as *const Vec<&str>; + let atom = (*all_atoms)[index]; + atom.len() +} + +#[no_mangle] +pub unsafe extern "C" fn smoosh_get_slice_at(result: SmooshResult, index: usize) -> *const c_char { + let slices = result.slices as *const Vec<&str>; + let slice = (*slices)[index]; + slice.as_ptr() as *const c_char +} + +#[no_mangle] +pub unsafe extern "C" fn smoosh_get_slice_len_at(result: SmooshResult, index: usize) -> usize { + let slices = result.slices as *const Vec<&str>; + let slice = (*slices)[index]; + slice.len() +} + +unsafe fn free_script(script: SmooshScriptStencil) { + let _ = script.gcthings.into(); +} + +unsafe fn free_script_data(script_data: SmooshImmutableScriptData) { + let _ = script_data.bytecode.into(); + let _ = script_data.scope_notes.into(); +} + +#[no_mangle] +pub unsafe extern "C" fn smoosh_free(result: SmooshResult) { + let _ = result.error.into(); + + let _ = result.scopes.into(); + let _ = result.regexps.into(); + + for fun in result.scripts.into() { + free_script(fun); + } + + for script_data in result.script_data_list.into() { + free_script_data(script_data); + } + + if !result.all_atoms.is_null() { + let _ = Box::from_raw(result.all_atoms as *mut Vec<&str>); + } + if !result.slices.is_null() { + let _ = Box::from_raw(result.slices as *mut Vec<&str>); + } + if !result.allocator.is_null() { + let _ = Box::from_raw(result.allocator as *mut bumpalo::Bump); + } +} + +fn smoosh<'alloc>( + allocator: &'alloc bumpalo::Bump, + text: &'alloc str, + options: &SmooshCompileOptions, +) -> Result<EmitResult<'alloc>, SmooshError> { + let parse_options = ParseOptions::new(); + let atoms = Rc::new(RefCell::new(SourceAtomSet::new())); + let slices = Rc::new(RefCell::new(SourceSliceList::new())); + let text_length = text.len(); + + let parse_result = match parse_script( + &allocator, + text, + &parse_options, + atoms.clone(), + slices.clone(), + ) { + Ok(result) => result, + Err(err) => match *err { + ParseError::NotImplemented(_) => { + println!("Unimplemented: {}", err.message()); + return Err(SmooshError::NotImplemented); + } + _ => { + return Err(SmooshError::GenericError(err.message())); + } + }, + }; + + let extent = SourceExtent::top_level_script(text_length.try_into().unwrap(), 1, 0); + let mut emit_options = EmitOptions::new(extent); + emit_options.no_script_rval = options.no_script_rval; + let script = parse_result.unbox(); + match emit( + allocator.alloc(Program::Script(script)), + &emit_options, + atoms.replace(SourceAtomSet::new_uninitialized()), + slices.replace(SourceSliceList::new()), + ) { + Ok(result) => Ok(result), + Err(EmitError::NotImplemented(message)) => { + println!("Unimplemented: {}", message); + return Err(SmooshError::NotImplemented); + } + } +} + +#[cfg(test)] +mod tests { + #[test] + fn it_works() { + assert_eq!(2 + 2, 4); + } +} |