From 863ab5c7e1a572caf542b717e052a70a3230bfde Mon Sep 17 00:00:00 2001 From: Redfire Date: Thu, 4 Jan 2024 22:07:28 +0800 Subject: [PATCH 01/12] Refactored Request and Response Added ResponseBody Fixed Body Not Being Sent in Requests --- Cargo.lock | 11 +++ runtime/Cargo.toml | 1 + runtime/src/globals/fetch/body.rs | 10 +++ runtime/src/globals/fetch/mod.rs | 70 ++++++++-------- runtime/src/globals/fetch/request/mod.rs | 39 +++------ runtime/src/globals/fetch/response/body.rs | 29 +++++++ runtime/src/globals/fetch/response/mod.rs | 95 ++++++---------------- 7 files changed, 123 insertions(+), 132 deletions(-) create mode 100644 runtime/src/globals/fetch/response/body.rs diff --git a/Cargo.lock b/Cargo.lock index 5e32271c..d3699bee 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1447,6 +1447,7 @@ dependencies = [ "sys-locale", "term-table", "tokio", + "uri-url", "url", ] @@ -2291,6 +2292,16 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" +[[package]] +name = "uri-url" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "208b24f86e526648c8b6349eeda3dba47af0eb79d070b29edb5c8c8b1e394721" +dependencies = [ + "http", + "url", +] + [[package]] name = "url" version = "2.5.0" diff --git a/runtime/Cargo.toml b/runtime/Cargo.toml index d38c11ab..29995774 100644 --- a/runtime/Cargo.toml +++ b/runtime/Cargo.toml @@ -15,6 +15,7 @@ form_urlencoded = "1.2.1" indexmap = "2.1.0" sha3 = "0.10.8" term-table = "1.3.2" +uri-url = "0.2.0" bytes.workspace = true chrono.workspace = true diff --git a/runtime/src/globals/fetch/body.rs b/runtime/src/globals/fetch/body.rs index 00767036..154e703a 100644 --- a/runtime/src/globals/fetch/body.rs +++ b/runtime/src/globals/fetch/body.rs @@ -9,6 +9,8 @@ use std::fmt::{Display, Formatter}; use bytes::Bytes; use form_urlencoded::Serializer; +use http::header::CONTENT_TYPE; +use http::{HeaderMap, HeaderValue}; use hyper::Body; use mozjs::jsapi::Heap; use mozjs::jsval::JSVal; @@ -80,6 +82,14 @@ impl FetchBody { FetchBodyInner::Bytes(bytes) => Body::from(bytes.clone()), } } + + pub(crate) fn add_content_type_header(&self, headers: &mut HeaderMap) { + if let Some(kind) = &self.kind { + if !headers.contains_key(CONTENT_TYPE) { + headers.append(CONTENT_TYPE, HeaderValue::from_str(&kind.to_string()).unwrap()); + } + } + } } impl Clone for FetchBody { diff --git a/runtime/src/globals/fetch/mod.rs b/runtime/src/globals/fetch/mod.rs index 7eb288e6..46d54e00 100644 --- a/runtime/src/globals/fetch/mod.rs +++ b/runtime/src/globals/fetch/mod.rs @@ -5,7 +5,6 @@ */ use std::iter::once; -use std::mem::take; use std::str; use std::str::FromStr; @@ -24,6 +23,7 @@ use mozjs::jsapi::JSObject; use mozjs::rust::IntoHandle; use sys_locale::get_locales; use tokio::fs::read; +use uri_url::url_to_uri; use url::Url; pub use client::{default_client, GLOBAL_CLIENT}; @@ -265,14 +265,15 @@ async fn main_fetch(cx: &Context, request: &mut Request, client: Client, redirec } if !opaque_redirect - && (request.request.method() == Method::HEAD - || request.request.method() == Method::CONNECT - || response.status == Some(StatusCode::SWITCHING_PROTOCOLS) + && (matches!(request.method, Method::HEAD | Method::CONNECT) || response.status.as_ref().map(StatusCode::as_u16) == Some(103) // Early Hints - || response.status == Some(StatusCode::NO_CONTENT) - || response.status == Some(StatusCode::RESET_CONTENT) - || response.status == Some(StatusCode::NOT_MODIFIED)) - { + || matches!( + response.status, + Some(StatusCode::SWITCHING_PROTOCOLS) + | Some(StatusCode::NO_CONTENT) + | Some(StatusCode::RESET_CONTENT) + | Some(StatusCode::NOT_MODIFIED) + )) { response.body = None; } @@ -417,24 +418,20 @@ async fn http_fetch( } #[async_recursion(?Send)] -async fn http_network_fetch(cx: &Context, req: &Request, client: Client, is_new: bool) -> Response { - let mut request = req.clone(); - let mut headers = Object::from(unsafe { Local::from_heap(&req.headers) }); - let headers = Headers::get_mut_private(&mut headers); - *request.request.headers_mut() = headers.headers.clone(); +async fn http_network_fetch(cx: &Context, request: &Request, client: Client, is_new: bool) -> Response { + let mut headers = Object::from(unsafe { Local::from_heap(&request.headers) }); + let mut headers = Headers::get_mut_private(&mut headers).headers.clone(); - let length = request.body.len().or_else(|| { - (request.body.is_none() - && (request.request.method() == Method::POST || request.request.method() == Method::PUT)) - .then_some(0) - }); + let length = request + .body + .len() + .or_else(|| (request.body.is_none() && matches!(request.method, Method::POST | Method::PUT)).then_some(0)); - let headers = request.request.headers_mut(); if let Some(length) = length { headers.append(CONTENT_LENGTH, HeaderValue::from_str(&length.to_string()).unwrap()); } - if let Referrer::Url(url) = request.referrer { + if let Referrer::Url(url) = &request.referrer { headers.append(REFERER, HeaderValue::from_str(url.as_str()).unwrap()); } @@ -442,21 +439,22 @@ async fn http_network_fetch(cx: &Context, req: &Request, client: Client, is_new: headers.append(USER_AGENT, HeaderValue::from_static(DEFAULT_USER_AGENT)); } - if request.cache == RequestCache::Default + let mut cache = request.cache; + if cache == RequestCache::Default && (headers.contains_key(IF_MODIFIED_SINCE) || headers.contains_key(IF_NONE_MATCH) || headers.contains_key(IF_UNMODIFIED_SINCE) || headers.contains_key(IF_MATCH) || headers.contains_key(IF_RANGE)) { - request.cache = RequestCache::NoStore; + cache = RequestCache::NoStore; } - if request.cache == RequestCache::NoCache && !headers.contains_key(CACHE_CONTROL) { + if cache == RequestCache::NoCache && !headers.contains_key(CACHE_CONTROL) { headers.append(CACHE_CONTROL, HeaderValue::from_static("max-age=0")); } - if request.cache == RequestCache::NoStore || request.cache == RequestCache::Reload { + if cache == RequestCache::NoStore || cache == RequestCache::Reload { if !headers.contains_key(PRAGMA) { headers.append(PRAGMA, HeaderValue::from_static("no-cache")); } @@ -490,13 +488,18 @@ async fn http_network_fetch(cx: &Context, req: &Request, client: Client, is_new: let range_requested = headers.contains_key(RANGE); - let mut response = match client.request(request.request).await { + let uri = url_to_uri(&request.url).unwrap(); + let mut builder = hyper::Request::builder().method(request.method.clone()).uri(uri); + *builder.headers_mut().unwrap() = headers; + let req = builder.body(request.body.to_http_body()).unwrap(); + + let mut response = match client.request(req).await { Ok(response) => { - let mut response = Response::new(response, req.url.clone()); + let (headers, response) = Response::from_hyper(response, request.url.clone()); let headers = Headers { reflector: Reflector::default(), - headers: take(response.response.as_mut().unwrap().headers_mut()), + headers, kind: HeadersKind::Immutable, }; response.headers.set(Headers::new_object(cx, Box::new(headers))); @@ -507,12 +510,12 @@ async fn http_network_fetch(cx: &Context, req: &Request, client: Client, is_new: response.range_requested = range_requested; - if response.status == Some(StatusCode::PROXY_AUTHENTICATION_REQUIRED) && !req.client_window { + if response.status == Some(StatusCode::PROXY_AUTHENTICATION_REQUIRED) && !request.client_window { return network_error(); } - if response.status == Some(StatusCode::MISDIRECTED_REQUEST) && !is_new && req.body.is_not_stream() { - return http_network_fetch(cx, req, client, true).await; + if response.status == Some(StatusCode::MISDIRECTED_REQUEST) && !is_new && request.body.is_not_stream() { + return http_network_fetch(cx, request, client, true).await; } response @@ -561,11 +564,10 @@ async fn http_redirect_fetch( } if ((response.status == Some(StatusCode::MOVED_PERMANENTLY) || response.status == Some(StatusCode::FOUND)) - && request.request.method() == Method::POST) - || (response.status == Some(StatusCode::SEE_OTHER) - && (request.request.method() != Method::GET || request.request.method() != Method::HEAD)) + && request.method == Method::POST) + || (response.status == Some(StatusCode::SEE_OTHER) && !matches!(request.method, Method::GET | Method::HEAD)) { - *request.request.method_mut() = Method::GET; + request.method = Method::GET; request.body = FetchBody::default(); let mut headers = Object::from(unsafe { Local::from_heap(&request.headers) }); let headers = Headers::get_mut_private(&mut headers); diff --git a/runtime/src/globals/fetch/request/mod.rs b/runtime/src/globals/fetch/request/mod.rs index c5298b1e..c9c082a1 100644 --- a/runtime/src/globals/fetch/request/mod.rs +++ b/runtime/src/globals/fetch/request/mod.rs @@ -6,9 +6,7 @@ use std::str::FromStr; -use http::{HeaderMap, HeaderValue}; -use http::header::CONTENT_TYPE; -use hyper::{Body, Method, Uri}; +use http::{HeaderMap, Method}; use mozjs::jsapi::{Heap, JSObject}; use url::Url; @@ -35,12 +33,12 @@ pub enum RequestInfo<'cx> { pub struct Request { reflector: Reflector, - #[trace(no_trace)] - pub(crate) request: hyper::Request, pub(crate) headers: Box>, pub(crate) body: FetchBody, pub(crate) body_used: bool, + #[trace(no_trace)] + pub(crate) method: Method, #[trace(no_trace)] pub(crate) url: Url, #[trace(no_trace)] @@ -73,23 +71,21 @@ impl Request { let mut request = match info { RequestInfo::Request(request) => request.clone(), RequestInfo::String(url) => { - let uri = Uri::from_str(&url)?; let url = Url::from_str(&url)?; if url.username() != "" || url.password().is_some() { return Err(Error::new("Received URL with embedded credentials", ErrorKind::Type)); } - let request = hyper::Request::builder().uri(uri).body(Body::empty())?; fallback_cors = true; Request { reflector: Reflector::default(), - request, headers: Box::default(), body: FetchBody::default(), body_used: false, + method: Method::GET, url: url.clone(), locations: vec![url], @@ -169,7 +165,7 @@ impl Request { if method == Method::CONNECT || method == Method::TRACE { return Err(Error::new("Received invalid request method", ErrorKind::Type)); } - *request.request.method_mut() = method; + request.method = method; } headers = init.headers; @@ -183,11 +179,8 @@ impl Request { )); } - if request.mode == RequestMode::NoCors { - let method = request.request.method(); - if method != Method::GET && method != Method::HEAD && method != Method::POST { - return Err(Error::new("Invalid request method", ErrorKind::Type)); - } + if request.mode == RequestMode::NoCors && !matches!(request.method, Method::GET | Method::HEAD | Method::POST) { + return Err(Error::new("Invalid request method", ErrorKind::Type)); } let kind = if request.mode == RequestMode::NoCors { @@ -207,12 +200,7 @@ impl Request { }; if let Some(body) = body { - if let Some(kind) = &body.kind { - if !headers.headers.contains_key(CONTENT_TYPE) { - headers.headers.append(CONTENT_TYPE, HeaderValue::from_str(&kind.to_string()).unwrap()); - } - } - + body.add_content_type_header(&mut headers.headers); request.body = body; } request.headers.set(Headers::new_object(cx, Box::new(headers))); @@ -222,12 +210,12 @@ impl Request { #[ion(get)] pub fn get_method(&self) -> String { - self.request.method().to_string() + self.method.to_string() } #[ion(get)] pub fn get_url(&self) -> String { - self.request.uri().to_string() + self.url.to_string() } #[ion(get)] @@ -303,21 +291,16 @@ impl Request { impl Clone for Request { fn clone(&self) -> Request { - let method = self.request.method().clone(); - let uri = self.request.uri().clone(); - - let request = hyper::Request::builder().method(method).uri(uri); - let request = request.body(Body::empty()).unwrap(); let url = self.locations.last().unwrap().clone(); Request { reflector: Reflector::default(), - request, headers: Box::default(), body: self.body.clone(), body_used: self.body_used, + method: self.method.clone(), url: url.clone(), locations: vec![url], diff --git a/runtime/src/globals/fetch/response/body.rs b/runtime/src/globals/fetch/response/body.rs new file mode 100644 index 00000000..4cd34206 --- /dev/null +++ b/runtime/src/globals/fetch/response/body.rs @@ -0,0 +1,29 @@ +/* + * 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/. + */ + +use hyper::{Body, body}; +use ion::Result; +use crate::globals::fetch::body::FetchBody; + +#[derive(Traceable)] +pub enum ResponseBody { + Fetch(FetchBody), + Hyper(#[trace(no_trace)] Body), +} + +impl ResponseBody { + pub async fn read_to_bytes(self) -> Result> { + let body = match self { + ResponseBody::Fetch(body) => body.to_http_body(), + ResponseBody::Hyper(body) => body, + }; + + match body::to_bytes(body).await { + Ok(bytes) => Ok(bytes.to_vec()), + Err(error) => Err(error.into()), + } + } +} diff --git a/runtime/src/globals/fetch/response/mod.rs b/runtime/src/globals/fetch/response/mod.rs index 820ef15d..e104c3fd 100644 --- a/runtime/src/globals/fetch/response/mod.rs +++ b/runtime/src/globals/fetch/response/mod.rs @@ -4,14 +4,11 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -use bytes::{Buf, BufMut, Bytes}; -use http::{HeaderMap, HeaderValue}; -use http::header::CONTENT_TYPE; +use bytes::Bytes; +use http::HeaderMap; use hyper::{Body, StatusCode}; -use hyper::body::HttpBody; use hyper::ext::ReasonPhrase; use mozjs::jsapi::{Heap, JSObject}; -use mozjs::rust::IntoHandle; use url::Url; use ion::{ClassDefinition, Context, Error, ErrorKind, Local, Object, Promise, Result}; @@ -22,19 +19,18 @@ pub use options::*; use crate::globals::fetch::body::FetchBody; use crate::globals::fetch::header::HeadersKind; use crate::globals::fetch::Headers; +use crate::globals::fetch::response::body::ResponseBody; use crate::promise::future_to_promise; +mod body; mod options; #[js_class] pub struct Response { reflector: Reflector, - #[trace(no_trace)] - pub(crate) response: Option>, pub(crate) headers: Box>, - pub(crate) body: Option, - pub(crate) body_used: bool, + pub(crate) body: Option, pub(crate) kind: ResponseKind, #[trace(no_trace)] @@ -49,42 +45,40 @@ pub struct Response { } impl Response { - pub fn new(response: hyper::Response, url: Url) -> Response { - let status = response.status(); - let status_text = if let Some(reason) = response.extensions().get::() { + pub fn from_hyper(response: hyper::Response, url: Url) -> (HeaderMap, Response) { + let (parts, body) = response.into_parts(); + + let status_text = if let Some(reason) = parts.extensions.get::() { Some(String::from_utf8(reason.as_bytes().to_vec()).unwrap()) } else { - status.canonical_reason().map(String::from) + parts.status.canonical_reason().map(String::from) }; - Response { + let response = Response { reflector: Reflector::default(), - response: Some(response), headers: Box::default(), - body: None, - body_used: false, + body: Some(ResponseBody::Hyper(body)), kind: ResponseKind::default(), url: Some(url), redirected: false, - status: Some(status), + status: Some(parts.status), status_text, range_requested: false, - } + }; + + (parts.headers, response) } pub fn new_from_bytes(bytes: Bytes, url: Url) -> Response { - let response = hyper::Response::builder().body(Body::from(bytes)).unwrap(); Response { reflector: Reflector::default(), - response: Some(response), headers: Box::default(), - body: None, - body_used: false, + body: Some(ResponseBody::Hyper(Body::from(bytes))), kind: ResponseKind::Basic, url: Some(url), @@ -104,14 +98,11 @@ impl Response { pub fn constructor(cx: &Context, body: Option, init: Option) -> Result { let init = init.unwrap_or_default(); - let response = hyper::Response::builder().status(init.status).body(Body::empty())?; let mut response = Response { reflector: Reflector::default(), - response: Some(response), headers: Box::default(), - body: None, - body_used: false, + body: Some(ResponseBody::Hyper(Body::empty())), kind: ResponseKind::default(), url: None, @@ -136,12 +127,8 @@ impl Response { )); } - if let Some(kind) = &body.kind { - if !headers.headers.contains_key(CONTENT_TYPE) { - headers.headers.append(CONTENT_TYPE, HeaderValue::from_str(&kind.to_string()).unwrap()); - } - } - response.body = Some(body); + body.add_content_type_header(&mut headers.headers); + response.body = Some(ResponseBody::Fetch(body)); } response.headers.set(Headers::new_object(cx, Box::new(headers))); @@ -186,51 +173,21 @@ impl Response { #[ion(get)] pub fn get_body_used(&self) -> bool { - self.body_used + self.body.is_none() } async fn read_to_bytes(&mut self) -> Result> { - if self.body_used { + if self.body.is_none() { return Err(Error::new("Response body has already been used.", None)); } - self.body_used = true; - - match &mut self.response { - None => Err(Error::new("Response is a network error and cannot be read.", None)), - Some(response) => { - let body = response.body_mut(); - - let first = if let Some(buf) = body.data().await { - buf? - } else { - return Ok(Vec::new()); - }; - - let second = if let Some(buf) = body.data().await { - buf? - } else { - return Ok(first.to_vec()); - }; - - let cap = first.remaining() + second.remaining() + body.size_hint().lower() as usize; - let mut vec = Vec::with_capacity(cap); - vec.put(first); - vec.put(second); - - while let Some(buf) = body.data().await { - vec.put(buf?); - } - - Ok(vec) - } - } + self.body.take().unwrap().read_to_bytes().await } #[ion(name = "arrayBuffer")] pub fn array_buffer<'cx>(&mut self, cx: &'cx Context) -> Option> { let this = cx.root_persistent_object(self.reflector().get()); let cx2 = unsafe { Context::new_unchecked(cx.as_ptr()) }; - let this = this.handle().into_handle(); + let this = this.handle().into(); future_to_promise::<_, _, Error>(cx, async move { let mut response = Object::from(unsafe { Local::from_raw_handle(this) }); let response = Response::get_mut_private(&mut response); @@ -243,7 +200,7 @@ impl Response { pub fn text<'cx>(&mut self, cx: &'cx Context) -> Option> { let this = cx.root_persistent_object(self.reflector().get()); let cx2 = unsafe { Context::new_unchecked(cx.as_ptr()) }; - let this = this.handle().into_handle(); + let this = this.handle().into(); future_to_promise::<_, _, Error>(cx, async move { let mut response = Object::from(unsafe { Local::from_raw_handle(this) }); let response = Response::get_mut_private(&mut response); @@ -258,10 +215,8 @@ pub fn network_error() -> Response { Response { reflector: Reflector::default(), - response: None, headers: Box::default(), body: None, - body_used: false, kind: ResponseKind::Error, url: None, From e8614b67465deff01a703b7dc2a6abb97339bde8 Mon Sep 17 00:00:00 2001 From: Redfire Date: Mon, 8 Jan 2024 21:28:20 +0800 Subject: [PATCH 02/12] Overhauled Native Function Argument Conversion Removed `#[ion(strict)]` and `#[ion(varargs)]` in favour of `Strict` and `Rest` Removed Special Handling of `Option` in Arguments Fixed Handling of Non-Ident Patterns in Arguments --- Cargo.lock | 12 +- ion-proc/Cargo.toml | 2 +- ion-proc/src/attribute/function.rs | 10 +- ion-proc/src/class/accessor.rs | 15 +- ion-proc/src/class/constructor.rs | 6 +- ion-proc/src/class/method.rs | 4 +- ion-proc/src/class/struct.rs | 15 +- ion-proc/src/function/inner.rs | 2 +- ion-proc/src/function/mod.rs | 5 +- ion-proc/src/function/parameter.rs | 254 ++++++++++++++ ion-proc/src/function/parameters.rs | 376 --------------------- ion-proc/src/function/wrapper.rs | 36 +- ion-proc/src/utils.rs | 21 +- ion-proc/src/value/from.rs | 9 +- ion/examples/macros/js_fn/complex.rs | 5 +- ion/examples/macros/js_fn/varargs.rs | 7 +- ion/src/class/mod.rs | 17 +- ion/src/conversions/value/to.rs | 2 +- ion/src/functions/arguments.rs | 225 +++++++----- ion/src/functions/closure.rs | 4 +- ion/src/functions/mod.rs | 23 +- ion/src/lib.rs | 2 +- ion/src/local.rs | 12 +- ion/src/module.rs | 10 +- ion/src/objects/array.rs | 10 +- ion/src/objects/iterator.rs | 8 +- ion/src/objects/map.rs | 6 +- ion/src/objects/object.rs | 30 +- ion/src/objects/set.rs | 6 +- ion/src/objects/typedarray/buffer.rs | 7 +- ion/src/objects/typedarray/view.rs | 3 +- ion/tests/objects/array.rs | 2 +- ion/tests/objects/object.rs | 2 +- ion/tests/rooting.rs | 4 +- modules/src/assert/assert.rs | 2 +- modules/src/fs/fs.rs | 4 +- modules/src/lib.rs | 4 +- modules/src/path/path.rs | 7 +- modules/src/url/url.rs | 14 +- modules/tests/assert.rs | 2 +- runtime/src/cache/mod.rs | 1 - runtime/src/event_loop/macrotasks.rs | 10 +- runtime/src/globals/abort.rs | 4 +- runtime/src/globals/base64.rs | 2 +- runtime/src/globals/console.rs | 37 +- runtime/src/globals/encoding/encoder.rs | 3 +- runtime/src/globals/encoding/mod.rs | 2 +- runtime/src/globals/fetch/body.rs | 2 +- runtime/src/globals/fetch/header.rs | 2 +- runtime/src/globals/fetch/mod.rs | 24 +- runtime/src/globals/fetch/response/body.rs | 2 + runtime/src/globals/fetch/response/mod.rs | 8 +- runtime/src/globals/file/mod.rs | 2 +- runtime/src/globals/microtasks.rs | 2 +- runtime/src/globals/mod.rs | 6 +- runtime/src/globals/timers.rs | 11 +- runtime/src/globals/url/mod.rs | 6 +- runtime/src/globals/url/search_params.rs | 4 +- runtime/src/lib.rs | 2 +- runtime/src/modules/loader.rs | 2 +- runtime/src/modules/standard.rs | 18 +- runtime/src/runtime.rs | 14 +- 62 files changed, 639 insertions(+), 710 deletions(-) create mode 100644 ion-proc/src/function/parameter.rs delete mode 100644 ion-proc/src/function/parameters.rs diff --git a/Cargo.lock b/Cargo.lock index d3699bee..f6e3be5e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1315,9 +1315,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.71" +version = "1.0.76" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75cb1540fadbd5b8fbccc4dddad2734eba435053f725621c070711a14bb5f4b8" +checksum = "95fc56cda0b5c3325f5fbbd7ff9fda9e02bb00bb3dac51252d2f1bfa1cb8cc8c" dependencies = [ "unicode-ident", ] @@ -1333,9 +1333,9 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.33" +version = "1.0.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" +checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" dependencies = [ "proc-macro2", ] @@ -2066,9 +2066,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.42" +version = "2.0.48" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b7d0a2c048d661a1a59fcd7355baa232f7ed34e0ee4df2eef3c1c1c0d3852d8" +checksum = "0f3531638e407dfc0814761abb7c00a5b54992b849452a0646b7f65c9f770f3f" dependencies = [ "proc-macro2", "quote", diff --git a/ion-proc/Cargo.toml b/ion-proc/Cargo.toml index 67bc3393..98995e5b 100644 --- a/ion-proc/Cargo.toml +++ b/ion-proc/Cargo.toml @@ -13,7 +13,7 @@ prettyplease = "0.2.15" proc-macro2 = "1.0.71" [dependencies.syn] -version = "2.0.42" +version = "2.0.45" features = ["visit-mut"] [lints] diff --git a/ion-proc/src/attribute/function.rs b/ion-proc/src/attribute/function.rs index 709fd927..05152907 100644 --- a/ion-proc/src/attribute/function.rs +++ b/ion-proc/src/attribute/function.rs @@ -12,22 +12,16 @@ use crate::attribute::{ParseArgument, ParseAttribute}; #[derive(Default)] pub(crate) struct ParameterAttribute { pub(crate) this: bool, - pub(crate) varargs: bool, pub(crate) convert: Option>, - pub(crate) strict: bool, } impl ParseAttribute for ParameterAttribute { fn parse(&mut self, meta: &ParseNestedMeta) -> Result<()> { self.this.parse_argument(meta, "this", "Parameter")?; - self.varargs.parse_argument(meta, "varargs", "Parameter")?; self.convert.parse_argument(meta, "convert", "Parameter")?; - self.strict.parse_argument(meta, "strict", "Parameter")?; - if self.this && (self.varargs || self.convert.is_some() || self.strict) { - return Err( - meta.error("Parameter with `this` attribute cannot have `varargs`, `convert`, or `strict` attributes.") - ); + if self.this && self.convert.is_some() { + return Err(meta.error("Parameter with `this` attribute cannot have `convert` attributes.")); } Ok(()) diff --git a/ion-proc/src/class/accessor.rs b/ion-proc/src/class/accessor.rs index 7df88616..1ece6881 100644 --- a/ion-proc/src/class/accessor.rs +++ b/ion-proc/src/class/accessor.rs @@ -13,7 +13,7 @@ use syn::{Error, ItemFn, Result, Type}; use syn::spanned::Spanned; use crate::class::method::{impl_method, Method}; -use crate::function::parameters::{Parameter, Parameters}; +use crate::function::parameter::Parameters; pub(super) struct Accessor(pub(super) Option, Option); @@ -98,14 +98,11 @@ pub(super) fn impl_accessor( }; let (mut accessor, parameters) = impl_method(ion, method, ty, |sig| { let parameters = Parameters::parse(&sig.inputs, Some(ty))?; - let nargs = parameters.parameters.iter().fold(0, |mut acc, param| { - if let Parameter::Regular { pat_ty, .. } = ¶m { - if let Type::Path(_) = &*pat_ty.ty { - acc += 1; - } - } - acc - }); + let nargs: i32 = parameters + .parameters + .iter() + .map(|param| matches!(&*param.pat_ty.ty, Type::Path(_)) as i32) + .sum(); (nargs == expected_args).then_some(()).ok_or(error) })?; accessor.method.sig.ident = ident; diff --git a/ion-proc/src/class/constructor.rs b/ion-proc/src/class/constructor.rs index 1d1c4535..a2c82100 100644 --- a/ion-proc/src/class/constructor.rs +++ b/ion-proc/src/class/constructor.rs @@ -24,7 +24,7 @@ pub(super) fn impl_constructor(ion: &TokenStream, mut constructor: ItemFn, ty: & let args = &mut #ion::Arguments::new(cx, argc, vp); let mut this = #ion::Object::from( cx.root_object( - ::mozjs::jsapi::JS_NewObjectForConstructor(cx.as_ptr(), &<#ty as #ion::ClassDefinition>::class().base, &args.call_args()) + ::mozjs::jsapi::JS_NewObjectForConstructor(cx.as_ptr(), &<#ty as #ion::ClassDefinition>::class().base, args.call_args()) ) ); @@ -38,7 +38,7 @@ pub(super) fn impl_constructor(ion: &TokenStream, mut constructor: ItemFn, ty: & wrapper(cx, args, &mut this) })); - #ion::functions::__handle_native_constructor_result(cx, result, &this, args.rval()) + #ion::functions::__handle_native_constructor_result(cx, result, &this, &mut args.rval()) }); constructor.block = body; constructor.sig.ident = format_ident!("__ion_bindings_constructor", span = constructor.sig.ident.span()); @@ -46,7 +46,7 @@ pub(super) fn impl_constructor(ion: &TokenStream, mut constructor: ItemFn, ty: & let method = Method { receiver: MethodReceiver::Static, method: constructor, - nargs: parameters.nargs.0, + nargs: parameters.nargs, names: vec![], }; Ok(method) diff --git a/ion-proc/src/class/method.rs b/ion-proc/src/class/method.rs index 23192a2d..d2466087 100644 --- a/ion-proc/src/class/method.rs +++ b/ion-proc/src/class/method.rs @@ -9,7 +9,7 @@ use syn::{ItemFn, Result, Signature, Type}; use crate::attribute::name::Name; use crate::function::{check_abi, impl_fn_body, set_signature}; -use crate::function::parameters::Parameters; +use crate::function::parameter::Parameters; use crate::function::wrapper::impl_wrapper_fn; #[derive(Copy, Clone, Debug, PartialEq, Eq)] @@ -57,7 +57,7 @@ where MethodReceiver::Static }, method, - nargs: parameters.nargs.0, + nargs: parameters.nargs, names: vec![], }; Ok((method, parameters)) diff --git a/ion-proc/src/class/struct.rs b/ion-proc/src/class/struct.rs index b53573e9..967539e2 100644 --- a/ion-proc/src/class/struct.rs +++ b/ion-proc/src/class/struct.rs @@ -12,7 +12,7 @@ use syn::punctuated::Punctuated; use syn::spanned::Spanned; use crate::attribute::krate::crate_from_attributes; -use crate::utils::path_ends_with; +use crate::utils::{new_token, path_ends_with}; pub(super) fn impl_js_class_struct(r#struct: &mut ItemStruct) -> Result<[ItemImpl; 6]> { let ion = &crate_from_attributes(&mut r#struct.attrs); @@ -178,21 +178,20 @@ fn class_impls( fn impl_from_value(ion: &TokenStream, span: Span, name: &str, r#type: &Type, mutable: bool) -> Result { let from_value_error = LitStr::new(&format!("Expected {}", name), span); - let function = if mutable { - quote!(get_mut_private) + let (function, mutable) = if mutable { + (quote!(get_mut_private), Some(new_token![mut])) } else { - quote!(get_private) + (quote!(get_private), None) }; - let mutable = mutable.then(::default); parse2( quote_spanned!(span => impl<'cx> #ion::conversions::FromValue<'cx> for &'cx #mutable #r#type { type Config = (); fn from_value(cx: &'cx #ion::Context, value: &#ion::Value, strict: ::core::primitive::bool, _: ()) -> #ion::Result<&'cx #mutable #r#type> { - let #mutable object = #ion::Object::from_value(cx, value, strict, ())?; - if <#r#type as #ion::class::ClassDefinition>::instance_of(cx, &object, None) { - Ok(<#r#type as #ion::class::ClassDefinition>::#function(&#mutable object)) + let object = #ion::Object::from_value(cx, value, strict, ())?; + if <#r#type as #ion::class::ClassDefinition>::instance_of(cx, &object) { + Ok(<#r#type as #ion::class::ClassDefinition>::#function(&object)) } else { Err(#ion::Error::new(#from_value_error, #ion::ErrorKind::Type)) } diff --git a/ion-proc/src/function/inner.rs b/ion-proc/src/function/inner.rs index e2e03562..5a3ef108 100644 --- a/ion-proc/src/function/inner.rs +++ b/ion-proc/src/function/inner.rs @@ -7,7 +7,7 @@ use syn::ItemFn; use syn::punctuated::Punctuated; -use crate::function::parameters::Parameters; +use crate::function::parameter::Parameters; pub(crate) fn impl_inner_fn(mut function: ItemFn, parameters: &Parameters, keep_inner: bool) -> ItemFn { function.sig.inputs = Punctuated::from_iter(parameters.to_args()); diff --git a/ion-proc/src/function/mod.rs b/ion-proc/src/function/mod.rs index a07e4def..ddc83712 100644 --- a/ion-proc/src/function/mod.rs +++ b/ion-proc/src/function/mod.rs @@ -10,9 +10,10 @@ use syn::punctuated::Punctuated; use crate::attribute::krate::crate_from_attributes; use crate::function::wrapper::impl_wrapper_fn; +use crate::utils::new_token; pub(crate) mod inner; -pub(crate) mod parameters; +pub(crate) mod parameter; pub(crate) mod wrapper; // TODO: Partially Remove Error Handling in Infallible Functions @@ -41,7 +42,7 @@ pub(crate) fn check_abi(function: &mut ItemFn) -> Result<()> { } pub(crate) fn set_signature(function: &mut ItemFn) -> Result<()> { - function.sig.unsafety = Some(::default()); + function.sig.unsafety = Some(new_token![unsafe]); let params: [FnArg; 3] = [ parse_quote!(cx: *mut ::mozjs::jsapi::JSContext), parse_quote!(argc: ::core::primitive::u32), diff --git a/ion-proc/src/function/parameter.rs b/ion-proc/src/function/parameter.rs new file mode 100644 index 00000000..9d6e9ad1 --- /dev/null +++ b/ion-proc/src/function/parameter.rs @@ -0,0 +1,254 @@ +/* + * 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/. + */ + +use proc_macro2::{Span, TokenStream}; +use syn::{Error, Expr, FnArg, Ident, parse2, Pat, PatType, Receiver, Result, Stmt, Type}; +use syn::punctuated::Punctuated; +use syn::spanned::Spanned; +use syn::visit_mut::visit_type_mut; + +use crate::attribute::function::ParameterAttribute; +use crate::attribute::ParseAttribute; +use crate::utils::{pat_is_ident, path_ends_with}; +use crate::visitors::{LifetimeRemover, SelfRenamer}; + +pub(crate) struct Parameter { + pub(crate) pat_ty: PatType, + convert: Option>, +} + +#[derive(Clone)] +pub(crate) enum ThisKind { + Ref { ty: Box }, + Object, + Owned, +} + +pub(crate) struct ThisParameter { + pat_ty: PatType, + kind: ThisKind, +} + +pub(crate) struct Parameters { + pub(crate) parameters: Vec, + pub(crate) this: Option<(ThisParameter, Ident, usize)>, + pub(crate) nargs: usize, +} + +impl Parameter { + pub(crate) fn from_arg(arg: &FnArg) -> Result { + match arg { + FnArg::Typed(pat_ty) => { + let mut pat_ty = pat_ty.clone(); + let attribute = ParameterAttribute::from_attributes_mut("ion", &mut pat_ty.attrs)?; + Ok(Parameter { pat_ty, convert: attribute.convert }) + } + FnArg::Receiver(_) => Err(Error::new(arg.span(), "Expected Typed Function Argument")), + } + } + + pub(crate) fn get_type_without_lifetimes(&self) -> Box { + let mut ty = self.pat_ty.ty.clone(); + visit_type_mut(&mut LifetimeRemover, &mut ty); + ty + } + + pub(crate) fn to_statement(&self, ion: &TokenStream, ident: &Ident) -> Result { + let span = self.pat_ty.span(); + let ty = self.get_type_without_lifetimes(); + + let pat_ty: PatType = parse2(quote_spanned!(span => #ident: #ty))?; + + let convert; + let convert = match &self.convert { + Some(convert) => convert, + None => { + convert = parse_quote!(()); + &convert + } + }; + + parse2(quote_spanned!(span => + let #pat_ty = #ion::functions::FromArgument::from_argument(&mut __accessor, #convert)?; + )) + } +} + +impl ThisParameter { + pub(crate) fn from_arg(arg: &FnArg, class_ty: Option<&Type>) -> Result> { + match arg { + FnArg::Typed(pat_ty) => { + let span = pat_ty.span(); + let mut pat_ty = pat_ty.clone(); + match class_ty { + Some(class_ty) if pat_is_ident(&pat_ty.pat, "self") => { + visit_type_mut(&mut SelfRenamer { ty: class_ty }, &mut pat_ty.ty); + parse_this(pat_ty, true, span).map(Some) + } + _ => { + let attribute = ParameterAttribute::from_attributes_mut("ion", &mut pat_ty.attrs)?; + if attribute.this { + return parse_this(pat_ty, class_ty.is_some(), span).map(Some); + } + Ok(None) + } + } + } + FnArg::Receiver(recv) => { + if class_ty.is_none() { + return Err(Error::new(arg.span(), "Can only have self on Class Methods")); + } + + let class_ty = class_ty.unwrap(); + if recv.colon_token.is_some() { + return Err(Error::new(arg.span(), "Invalid type for self")); + } + + let Receiver { + attrs, reference, mutability, self_token, .. + } = recv; + let lifetime = &reference.as_ref().unwrap().1; + + let pat_ty = + parse2(quote_spanned!(recv.span() => #(#attrs)* #self_token: &#lifetime #mutability #class_ty))?; + parse_this(pat_ty, true, recv.span()).map(Some) + } + } + } + + pub(crate) fn to_statement(&self, ion: &TokenStream, is_class: bool) -> Result { + let ThisParameter { pat_ty, kind } = self; + let mut pat_ty = pat_ty.clone(); + pat_ty.attrs.clear(); + + if is_class && pat_is_ident(&pat_ty.pat, "self") { + pat_ty.pat = parse_quote!(self_); + } + + match kind { + ThisKind::Ref { ty } => { + let mut ty = ty.clone(); + visit_type_mut(&mut LifetimeRemover, &mut ty); + + if is_class { + parse2(quote!( + let #pat_ty = <#ty as #ion::ClassDefinition>::get_mut_private(__this); + )) + } else { + parse2(quote!( + let #pat_ty = <#ty as #ion::conversions::FromValue>::from_value(__cx, __accessor.this(), true, ())?; + )) + } + } + ThisKind::Object => parse2(quote!(let #pat_ty = __this;)), + ThisKind::Owned => Err(Error::new(pat_ty.span(), "This cannot be owned")), + } + } +} + +impl Parameters { + pub(crate) fn parse(parameters: &Punctuated, ty: Option<&Type>) -> Result { + let mut nargs = 0; + let mut this: Option<(ThisParameter, Ident, usize)> = None; + + let parameters: Vec<_> = parameters + .iter() + .enumerate() + .filter_map(|(i, arg)| { + let this_param = ThisParameter::from_arg(arg, ty); + match this_param { + Ok(Some(this_param)) => { + if let Pat::Ident(ident) = &*this_param.pat_ty.pat { + if let Some(this) = &this { + return Some(Err(Error::new( + this.1.span(), + "Unable to have multiple this/self parameters", + ))); + } + let ident = ident.ident.clone(); + this = Some((this_param, ident, i)); + } + return None; + } + Err(e) => return Some(Err(e)), + _ => (), + } + + let param = match Parameter::from_arg(arg) { + Ok(param) => param, + Err(e) => return Some(Err(e)), + }; + if let Type::Path(ty) = &*param.pat_ty.ty { + if !path_ends_with(&ty.path, "Opt") { + nargs += 1; + } + } + Some(Ok(param)) + }) + .collect::>()?; + + Ok(Parameters { parameters, this, nargs }) + } + + pub(crate) fn to_statements(&self, ion: &TokenStream) -> Result<(Vec, Vec)> { + let mut statements = Vec::with_capacity(self.parameters.len()); + let mut idents = Vec::with_capacity(self.parameters.len() + 1); + for (i, parameter) in self.parameters.iter().enumerate() { + let ident = format_ident!("__ion_var{}", i); + statements.push(parameter.to_statement(ion, &ident)?); + idents.push(ident); + } + + if let Some((_, ident, index)) = &self.this { + if ident != "self" { + idents.insert(*index, ident.clone()); + } + } + + Ok((statements, idents)) + } + + pub(crate) fn get_this_ident(&self) -> Option { + self.this.as_ref().map(|x| x.1.clone()) + } + + pub(crate) fn to_this_statement(&self, ion: &TokenStream, is_class: bool) -> Result> { + self.this.as_ref().map(|(this, _, _)| this.to_statement(ion, is_class)).transpose() + } + + pub(crate) fn to_args(&self) -> Vec { + let mut args = Vec::with_capacity(self.parameters.len() + self.this.is_some() as usize); + + args.extend( + self.parameters + .iter() + .map(|parameter| FnArg::Typed(parameter.pat_ty.clone())) + .collect::>(), + ); + + if let Some((ThisParameter { pat_ty, .. }, _, index)) = &self.this { + args.insert(*index, FnArg::Typed(pat_ty.clone())); + } + + args + } +} + +pub(crate) fn parse_this(pat_ty: PatType, is_class: bool, span: Span) -> Result { + match &*pat_ty.ty { + Type::Reference(reference) => { + let elem = reference.clone().elem; + match &*elem { + Type::Path(ty) if path_ends_with(&ty.path, "Object") => { + Ok(ThisParameter { pat_ty, kind: ThisKind::Object }) + } + _ => Ok(ThisParameter { pat_ty, kind: ThisKind::Ref { ty: elem } }), + } + } + _ if !is_class => Ok(ThisParameter { pat_ty, kind: ThisKind::Owned }), + _ => Err(Error::new(span, "Invalid type for self")), + } +} diff --git a/ion-proc/src/function/parameters.rs b/ion-proc/src/function/parameters.rs deleted file mode 100644 index 26ff8854..00000000 --- a/ion-proc/src/function/parameters.rs +++ /dev/null @@ -1,376 +0,0 @@ -/* - * 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/. - */ - -use proc_macro2::{Span, TokenStream}; -use quote::ToTokens; -use syn::{ - Error, Expr, FnArg, GenericArgument, Ident, parse2, Pat, PathArguments, PatIdent, PatType, Receiver, Result, Stmt, - Type, -}; -use syn::ext::IdentExt; -use syn::parse::Parser; -use syn::punctuated::Punctuated; -use syn::spanned::Spanned; -use syn::visit_mut::visit_type_mut; - -use crate::attribute::function::ParameterAttribute; -use crate::attribute::ParseAttribute; -use crate::utils::{format_pat, pat_is_ident, path_ends_with}; -use crate::visitors::{LifetimeRemover, SelfRenamer}; - -pub(crate) enum Parameter { - Regular { - pat_ty: PatType, - convert: Box, - strict: bool, - option: Option>, - }, - VarArgs { - pat_ty: PatType, - convert: Box, - strict: bool, - }, - Context(PatType), - Arguments(PatType), -} - -#[derive(Clone)] -pub(crate) enum ThisKind { - Ref { ty: Box }, - Object, - Owned, -} - -pub(crate) struct ThisParameter { - pat_ty: PatType, - kind: ThisKind, -} - -pub(crate) struct Parameters { - pub(crate) parameters: Vec, - pub(crate) this: Option<(ThisParameter, Ident, usize)>, - pub(crate) idents: Vec, - pub(crate) nargs: (usize, usize), -} - -impl Parameter { - pub(crate) fn from_arg(arg: &FnArg) -> Result { - match arg { - FnArg::Typed(pat_ty) => { - let mut pat_ty = pat_ty.clone(); - if let Type::Reference(reference) = &*pat_ty.ty { - if let Type::Path(path) = &*reference.elem { - if path_ends_with(&path.path, "Context") { - return Ok(Parameter::Context(pat_ty)); - } else if path_ends_with(&path.path, "Arguments") { - return Ok(Parameter::Arguments(pat_ty)); - } - } - } - - let mut option = None; - - let attribute = ParameterAttribute::from_attributes_mut("ion", &mut pat_ty.attrs)?; - let ParameterAttribute { varargs, convert, strict, .. } = attribute; - let convert = convert.unwrap_or_else(|| parse_quote!(())); - - if let Type::Path(ty) = &*pat_ty.ty { - if path_ends_with(&ty.path, "Option") { - let option_segment = ty.path.segments.last().unwrap(); - if let PathArguments::AngleBracketed(inner) = &option_segment.arguments { - if let GenericArgument::Type(inner) = inner.args.last().unwrap() { - option = Some(Box::new(inner.clone())); - } - } - } - } - - if varargs { - Ok(Parameter::VarArgs { pat_ty, convert, strict }) - } else { - Ok(Parameter::Regular { pat_ty, convert, strict, option }) - } - } - FnArg::Receiver(_) => unreachable!(), - } - } - - pub(crate) fn pat_type(&self) -> &PatType { - use Parameter as P; - let (P::Regular { pat_ty, .. } | P::VarArgs { pat_ty, .. } | P::Context(pat_ty) | P::Arguments(pat_ty)) = self; - pat_ty - } - - pub(crate) fn get_type_without_lifetimes(&self) -> Box { - let mut ty = self.pat_type().ty.clone(); - visit_type_mut(&mut LifetimeRemover, &mut ty); - ty - } - - pub(crate) fn to_statement(&self, ion: &TokenStream) -> Result { - use Parameter as P; - let ty = self.get_type_without_lifetimes(); - let mut pat_ty = self.pat_type().clone(); - pat_ty.attrs.clear(); - match self { - P::Regular { convert, strict, option, .. } => { - pat_ty.ty = ty; - regular_param_statement(ion, &pat_ty, option.as_deref(), convert, *strict) - } - P::VarArgs { convert, strict, .. } => { - let mut pat_ty = pat_ty.clone(); - pat_ty.ty = ty; - varargs_param_statement(&pat_ty, convert, *strict) - } - P::Context(..) => parse2(quote!(let #pat_ty = __cx;)), - P::Arguments(..) => parse2(quote!(let #pat_ty = __args;)), - } - } -} - -impl ThisParameter { - pub(crate) fn from_arg(arg: &FnArg, class_ty: Option<&Type>) -> Result> { - match arg { - FnArg::Typed(pat_ty) => { - let span = pat_ty.span(); - let mut pat_ty = pat_ty.clone(); - match class_ty { - Some(class_ty) if pat_is_ident(&pat_ty.pat, "self") => { - visit_type_mut(&mut SelfRenamer { ty: class_ty }, &mut pat_ty.ty); - parse_this(pat_ty, true, span).map(Some) - } - _ => { - let attribute = ParameterAttribute::from_attributes_mut("ion", &mut pat_ty.attrs)?; - if attribute.this { - return parse_this(pat_ty, class_ty.is_some(), span).map(Some); - } - Ok(None) - } - } - } - FnArg::Receiver(recv) => { - if class_ty.is_none() { - return Err(Error::new(arg.span(), "Can only have self on Class Methods")); - } - - let class_ty = class_ty.unwrap(); - if recv.colon_token.is_some() { - return Err(Error::new(arg.span(), "Invalid type for self")); - } - - let Receiver { - attrs, reference, mutability, self_token, .. - } = recv; - let lifetime = &reference.as_ref().unwrap().1; - - let pat_ty = PatType { - attrs: attrs.clone(), - pat: Box::new(Pat::Ident(PatIdent { - attrs: Vec::new(), - by_ref: None, - mutability: None, - ident: Ident::parse_any.parse2(self_token.to_token_stream()).unwrap(), - subpat: None, - })), - colon_token: ::default(), - ty: parse2(quote_spanned!(recv.span() => &#lifetime #mutability #class_ty)).unwrap(), - }; - parse_this(pat_ty, true, recv.span()).map(Some) - } - } - } - - pub(crate) fn to_statement(&self, ion: &TokenStream, is_class: bool) -> Result { - let ThisParameter { pat_ty, kind } = self; - let mut pat_ty = pat_ty.clone(); - pat_ty.attrs.clear(); - - if is_class && pat_is_ident(&pat_ty.pat, "self") { - pat_ty.pat = parse_quote!(self_); - } - - match kind { - ThisKind::Ref { ty } => { - let mut ty = ty.clone(); - visit_type_mut(&mut LifetimeRemover, &mut ty); - - if is_class { - parse2(quote!( - let #pat_ty = <#ty as #ion::ClassDefinition>::get_mut_private(__this); - )) - } else { - parse2(quote!( - let #pat_ty = <#ty as #ion::conversions::FromValue>::from_value(__cx, __accessor.this(), true, ())?; - )) - } - } - ThisKind::Object => parse2(quote!(let #pat_ty = __this;)), - ThisKind::Owned => Err(Error::new(pat_ty.span(), "This cannot be owned")), - } - } -} - -impl Parameters { - pub(crate) fn parse(parameters: &Punctuated, ty: Option<&Type>) -> Result { - let mut nargs = (0, 0); - let mut this: Option<(ThisParameter, Ident, usize)> = None; - let mut idents = Vec::new(); - - let parameters: Vec<_> = parameters - .iter() - .enumerate() - .filter_map(|(i, arg)| { - let this_param = ThisParameter::from_arg(arg, ty); - match this_param { - Ok(Some(this_param)) => { - if let Pat::Ident(ident) = &*this_param.pat_ty.pat { - if let Some(this) = &this { - return Some(Err(Error::new( - this.1.span(), - "Unable to have multiple this/self parameters", - ))); - } - let ident = ident.ident.clone(); - this = Some((this_param, ident, i)); - } - return None; - } - Err(e) => return Some(Err(e)), - _ => (), - } - - let param = Parameter::from_arg(arg); - match ¶m { - Ok(Parameter::Regular { pat_ty, option, .. }) => { - if option.is_none() { - nargs.0 += 1; - } else { - nargs.1 += 1; - } - if let Some(ident) = get_ident(&pat_ty.pat) { - idents.push(ident); - } - } - Ok(Parameter::VarArgs { pat_ty, .. }) => { - if let Some(ident) = get_ident(&pat_ty.pat) { - idents.push(ident); - } - } - Ok(Parameter::Context(pat_ty)) => { - if let Some(ident) = get_ident(&pat_ty.pat) { - idents.push(ident); - } - } - Ok(Parameter::Arguments(pat_ty)) => { - if let Some(ident) = get_ident(&pat_ty.pat) { - idents.push(ident); - } - } - _ => {} - } - Some(param) - }) - .collect::>()?; - - Ok(Parameters { parameters, this, idents, nargs }) - } - - pub(crate) fn to_statements(&self, ion: &TokenStream) -> Result> { - self.parameters.iter().map(|parameter| parameter.to_statement(ion)).collect() - } - - pub(crate) fn get_this_ident(&self) -> Option { - self.this.as_ref().map(|x| x.1.clone()) - } - - pub(crate) fn to_this_statement(&self, ion: &TokenStream, is_class: bool) -> Result> { - match &self.this { - Some((this, _, _)) => Ok(Some(this.to_statement(ion, is_class)?)), - None => Ok(None), - } - } - - pub(crate) fn to_args(&self) -> Vec { - let mut args = Vec::with_capacity(self.this.is_some() as usize + self.parameters.len()); - - args.extend( - self.parameters - .iter() - .map(|parameter| FnArg::Typed(parameter.pat_type().clone())) - .collect::>(), - ); - - if let Some((ThisParameter { pat_ty, .. }, _, index)) = &self.this { - args.insert(*index, FnArg::Typed(pat_ty.clone())); - } - - args - } - - pub(crate) fn to_idents(&self) -> Vec { - let mut idents = self.idents.clone(); - if let Some((_, ident, index)) = &self.this { - if ident != &Into::::into(::default()) { - idents.insert(*index, ident.clone()); - } - } - idents - } -} - -fn regular_param_statement( - ion: &TokenStream, pat_ty: &PatType, option: Option<&Type>, conversion: &Expr, strict: bool, -) -> Result { - let not_found_error = if let Some(pat) = format_pat(&pat_ty.pat) { - format!("Argument {} at index {{}} was not found.", pat) - } else { - String::from("Argument at index {{}} was not found.") - }; - - let if_none: Expr = if option.is_some() { - parse2(quote!(::std::option::Option::None)).unwrap() - } else { - parse2(quote!( - return ::std::result::Result::Err(#ion::Error::new(#not_found_error, #ion::ErrorKind::Type).into()) - )) - .unwrap() - }; - - parse2(quote!( - let #pat_ty = match unsafe { __accessor.arg(#strict, #conversion) } { - ::std::option::Option::Some(value) => value?, - ::std::option::Option::None => #if_none - }; - )) -} - -fn varargs_param_statement(pat_ty: &PatType, conversion: &Expr, strict: bool) -> Result { - parse2(quote!(let #pat_ty = unsafe { __accessor.args(#strict, #conversion)? };)) -} - -pub(crate) fn get_ident(pat: &Pat) -> Option { - if let Pat::Ident(ident) = pat { - Some(ident.ident.clone()) - } else { - None - } -} - -pub(crate) fn parse_this(pat_ty: PatType, is_class: bool, span: Span) -> Result { - match &*pat_ty.ty { - Type::Reference(reference) => { - let elem = reference.clone().elem; - match &*elem { - Type::Path(ty) if path_ends_with(&ty.path, "Object") => { - Ok(ThisParameter { pat_ty, kind: ThisKind::Object }) - } - _ => Ok(ThisParameter { pat_ty, kind: ThisKind::Ref { ty: elem } }), - } - } - _ if !is_class => Ok(ThisParameter { pat_ty, kind: ThisKind::Owned }), - _ => Err(Error::new(span, "Invalid type for self")), - } -} diff --git a/ion-proc/src/function/wrapper.rs b/ion-proc/src/function/wrapper.rs index 455b4f6d..c5b6e75c 100644 --- a/ion-proc/src/function/wrapper.rs +++ b/ion-proc/src/function/wrapper.rs @@ -4,15 +4,15 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -use proc_macro2::{Ident, TokenStream}; +use proc_macro2::TokenStream; use quote::ToTokens; use syn::{Error, FnArg, GenericParam, ItemFn, parse2, Result, ReturnType, Type}; use syn::punctuated::Punctuated; use syn::spanned::Spanned; use crate::function::inner::impl_inner_fn; -use crate::function::parameters::Parameters; -use crate::utils::path_ends_with; +use crate::function::parameter::Parameters; +use crate::utils::{new_token, path_ends_with}; pub(crate) fn impl_wrapper_fn( ion: &TokenStream, mut function: ItemFn, class_ty: Option<&Type>, is_constructor: bool, @@ -25,8 +25,7 @@ pub(crate) fn impl_wrapper_fn( } let parameters = Parameters::parse(&function.sig.inputs, class_ty)?; - let idents = parameters.to_idents(); - let statements = parameters.to_statements(ion)?; + let (statements, idents) = parameters.to_statements(ion)?; let inner = impl_inner_fn(function.clone(), ¶meters, class_ty.is_none()); @@ -36,7 +35,7 @@ pub(crate) fn impl_wrapper_fn( parse_quote!(__args: &'a mut #ion::Arguments<'cx>), ]; - let argument_checker = argument_checker(ion, &function.sig.ident, parameters.nargs.0); + let nargs = parameters.nargs; let mut this_statements = parameters.to_this_statement(ion, class_ty.is_some())?.map(ToTokens::into_token_stream); if is_constructor { @@ -44,7 +43,7 @@ pub(crate) fn impl_wrapper_fn( } else { this_statements = this_statements.map(|statement| { quote!( - let __this = &mut __accessor.this_mut().to_object(__cx); + let __this = &mut __accessor.this().to_object(__cx); #statement ) }); @@ -66,7 +65,7 @@ pub(crate) fn impl_wrapper_fn( }; let result = quote!(#result.map(Box::new)); let result = if !is_constructor { - quote!(#result.map(|__result| #ion::conversions::IntoValue::into_value(__result, __cx, __args.rval()))) + quote!(#result.map(|__result| #ion::conversions::IntoValue::into_value(__result, __cx, &mut __args.rval()))) } else { quote!(#result.map(|__result| #ion::ClassDefinition::set_private(__this.handle().get(), __result))) }; @@ -80,14 +79,15 @@ pub(crate) fn impl_wrapper_fn( quote!(inner) }; - let inner_call = if parameters.get_this_ident() == Some(::default().into()) { + let this_ident = parameters.get_this_ident(); + let inner_call = if this_ident.is_some() && this_ident.unwrap() == "self" { quote!(#call(self_, #(#idents),*)) } else { quote!(#call(#(#idents),*)) }; let body = parse2(quote_spanned!(function.span() => { - #argument_checker + __args.check_args(__cx, #nargs)?; let mut __accessor = __args.access(); #this_statements @@ -100,28 +100,14 @@ pub(crate) fn impl_wrapper_fn( #result }))?; + function.sig.unsafety = Some(new_token![unsafe]); function.sig.ident = format_ident!("wrapper", span = function.sig.ident.span()); function.sig.inputs = Punctuated::from_iter(wrapper_args); function.sig.generics.params = Punctuated::from_iter(wrapper_generics); function.sig.output = parse_quote!(-> #ion::ResultExc<()>); - function.sig.unsafety = Some(::default()); function.attrs.clear(); function.block = body; Ok((function, parameters)) } - -pub(crate) fn argument_checker(ion: &TokenStream, ident: &Ident, nargs: usize) -> TokenStream { - if nargs != 0 { - let plural = if nargs == 1 { "" } else { "s" }; - let error = format!("{}() requires at least {} argument{}", ident, nargs, plural); - quote!( - if __args.len() < #nargs { - return ::std::result::Result::Err(#ion::Error::new(#error, ::std::option::Option::None).into()); - } - ) - } else { - TokenStream::new() - } -} diff --git a/ion-proc/src/utils.rs b/ion-proc/src/utils.rs index 9e7bd760..7c2672b1 100644 --- a/ion-proc/src/utils.rs +++ b/ion-proc/src/utils.rs @@ -51,19 +51,10 @@ pub(crate) fn format_type(ty: &Type) -> String { String::from(ty.trim()) } -pub(crate) fn format_pat(pat: &Pat) -> Option { - let ident = match pat { - Pat::Ident(ident) => ident.ident.clone(), - _ => return None, - }; - let pat = unparse( - &parse2(quote!( - const #ident: () = (); - )) - .unwrap(), - ); - let mut pat = String::from(pat.trim()); - pat.drain((pat.len() - 10)..(pat.len())); - pat.drain(0..5); - Some(String::from(pat.trim())) +macro_rules! new_token { + [$i:tt] => { + ::default() + }; } + +pub(crate) use new_token; diff --git a/ion-proc/src/value/from.rs b/ion-proc/src/value/from.rs index 3cb93006..d8b26560 100644 --- a/ion-proc/src/value/from.rs +++ b/ion-proc/src/value/from.rs @@ -70,8 +70,13 @@ pub(crate) fn impl_from_value(mut input: DeriveInput) -> Result { let (body, requires_object) = impl_body(ion, input.span(), &input.data, name, tag, inherit, repr)?; - let object = requires_object - .then(|| quote_spanned!(input.span() => let __object = #ion::Object::from_value(cx, value, true, ())?;)); + let object = if requires_object { + Some(quote_spanned!(input.span() => + let __object = #ion::Object::from_value(cx, value, true, ())?; + )) + } else { + None + }; parse2(quote_spanned!(input.span() => #[automatically_derived] diff --git a/ion/examples/macros/js_fn/complex.rs b/ion/examples/macros/js_fn/complex.rs index deb9ad84..53154bfe 100644 --- a/ion/examples/macros/js_fn/complex.rs +++ b/ion/examples/macros/js_fn/complex.rs @@ -1,11 +1,12 @@ use ion::{Context, Function, js_fn, Object, Promise, Value}; use ion::conversions::ConversionBehavior; +use ion::functions::{Rest, Strict}; #[allow(clippy::too_many_arguments)] #[js_fn] pub fn many_inputs( _cx: &Context, #[ion(this)] _this: &Object, #[ion(convert = ConversionBehavior::EnforceRange)] _integer: i8, - #[ion(strict)] _boolean: bool, #[ion(convert = (), strict)] _string: String, _function: Function, - _promise: Promise, #[ion(varargs)] _values: Vec, + _boolean: Strict, #[ion(convert = ())] _string: String, _function: Function, _promise: Promise, + _values: Rest, ) { } diff --git a/ion/examples/macros/js_fn/varargs.rs b/ion/examples/macros/js_fn/varargs.rs index 9753c60e..92fdabb9 100644 --- a/ion/examples/macros/js_fn/varargs.rs +++ b/ion/examples/macros/js_fn/varargs.rs @@ -1,11 +1,12 @@ use ion::{js_fn, Object}; use ion::conversions::ConversionBehavior; +use ion::functions::Rest; #[js_fn] -pub fn varargs(#[ion(varargs)] _strings: Vec) {} +pub fn varargs(Rest(_strings): Rest) {} #[js_fn] -pub fn varargs_integer(#[ion(varargs, convert = ConversionBehavior::EnforceRange)] _integers: Vec) {} +pub fn varargs_integer(#[ion(convert = ConversionBehavior::EnforceRange)] Rest(_integers): Rest) {} #[js_fn] -pub fn varargs_object(#[ion(varargs)] _objects: Vec) {} +pub fn varargs_object(Rest(_objects): Rest) {} diff --git a/ion/src/class/mod.rs b/ion/src/class/mod.rs index 673018e1..242be511 100644 --- a/ion/src/class/mod.rs +++ b/ion/src/class/mod.rs @@ -16,7 +16,7 @@ use mozjs::jsapi::{ }; use mozjs::jsval::{PrivateValue, UndefinedValue}; -use crate::{Arguments, Context, Function, Local, Object}; +use crate::{Context, Function, Local, Object}; pub use crate::class::native::{MAX_PROTO_CHAIN_LENGTH, NativeClass, PrototypeChain, TypeIdWrapper}; pub use crate::class::reflect::{Castable, DerivedFrom, NativeObject, Reflector}; use crate::functions::NativeFunction; @@ -60,7 +60,7 @@ pub trait ClassDefinition: NativeObject { &[JSPropertySpec::ZERO] } - fn init_class<'cx>(cx: &'cx Context, object: &mut Object) -> (bool, &'cx ClassInfo) { + fn init_class<'cx>(cx: &'cx Context, object: &Object) -> (bool, &'cx ClassInfo) { let infos = unsafe { &mut (*cx.get_inner_data().as_ptr()).class_infos }; let entry = infos.entry(TypeId::of::()); @@ -138,7 +138,8 @@ pub trait ClassDefinition: NativeObject { } } - fn get_mut_private<'a>(object: &mut Object<'a>) -> &'a mut Self { + #[allow(clippy::mut_from_ref)] + fn get_mut_private<'a>(object: &Object<'a>) -> &'a mut Self { unsafe { let mut value = UndefinedValue(); JS_GetReservedSlot(object.handle().get(), 0, &mut value); @@ -153,10 +154,14 @@ pub trait ClassDefinition: NativeObject { } } - fn instance_of(cx: &Context, object: &Object, args: Option<&Arguments>) -> bool { + fn instance_of(cx: &Context, object: &Object) -> bool { unsafe { - let args = args.map(|a| a.call_args()).as_mut().map_or(ptr::null_mut(), |args| args); - JS_InstanceOf(cx.as_ptr(), object.handle().into(), &Self::class().base, args) + JS_InstanceOf( + cx.as_ptr(), + object.handle().into(), + &Self::class().base, + ptr::null_mut(), + ) } } } diff --git a/ion/src/conversions/value/to.rs b/ion/src/conversions/value/to.rs index caed5537..ef319291 100644 --- a/ion/src/conversions/value/to.rs +++ b/ion/src/conversions/value/to.rs @@ -286,7 +286,7 @@ impl<'cx, T: ToValue<'cx>> ToValue<'cx> for Option { impl<'cx, T: ToValue<'cx>> ToValue<'cx> for [T] { fn to_value(&self, cx: &'cx Context, value: &mut Value) { - let mut array = Array::new_with_length(cx, self.len()); + let array = Array::new_with_length(cx, self.len()); for (i, t) in self.iter().enumerate() { assert!(array.set_as(cx, i as u32, t)); diff --git a/ion/src/functions/arguments.rs b/ion/src/functions/arguments.rs index f73772d1..034282b9 100644 --- a/ion/src/functions/arguments.rs +++ b/ion/src/functions/arguments.rs @@ -4,113 +4,114 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -use std::ops::{Deref, DerefMut}; - -use mozjs::jsapi::CallArgs; +use mozjs::jsapi::{CallArgs, JS_GetFunctionId, JS_GetObjectFunction}; use mozjs::jsval::JSVal; -use crate::{Context, Local, Object, Result, Value}; +use crate::{Context, Error, ErrorKind, Local, Object, Result, Value}; use crate::conversions::FromValue; +use crate::functions::{Opt, Rest}; -/// Represents Arguments to a [JavaScript Function](crate::Function) +/// Represents Arguments to a [JavaScript Function](crate::Function). /// Wrapper around [CallArgs] to provide lifetimes and root all arguments. pub struct Arguments<'cx> { cx: &'cx Context, - values: Box<[Value<'cx>]>, + args: usize, callee: Object<'cx>, - this: Value<'cx>, - rval: Value<'cx>, call_args: CallArgs, } impl<'cx> Arguments<'cx> { - /// Creates new [Arguments] from raw arguments, pub unsafe fn new(cx: &'cx Context, argc: u32, vp: *mut JSVal) -> Arguments<'cx> { unsafe { let call_args = CallArgs::from_vp(vp, argc); - let values = (0..argc).map(|i| Local::from_raw_handle(call_args.get(i)).into()).collect(); let callee = cx.root_object(call_args.callee()).into(); - let this = cx.root_value(call_args.thisv().get()).into(); - let rval = Local::from_raw_handle_mut(call_args.rval()).into(); Arguments { cx, - values, + args: argc as usize, callee, - this, - rval, call_args, } } } - /// Returns the number of arguments passed to the function. - pub fn len(&self) -> usize { - self.values.len() - } - - pub fn is_empty(&self) -> bool { - self.values.len() == 0 - } - - pub fn access(&mut self) -> Accessor<'_, 'cx> { - Accessor { args: self, index: 0 } + /// Checks if the expected minimum number of arguments were passed. + pub fn check_args(&self, cx: &Context, min: usize) -> Result<()> { + if self.args < min { + let func = crate::String::from(unsafe { + Local::from_marked(&JS_GetFunctionId(JS_GetObjectFunction(self.callee.handle().get()))) + }) + .to_owned(cx); + + return Err(Error::new( + &format!( + "Error while calling `{func}()` with {} argument(s) while {min} were expected.", + self.args + ), + ErrorKind::Normal, + )); + } + Ok(()) } - /// Gets the [Value] at the given index. - /// Returns [None] if the given index is larger than the number of arguments. - pub fn value(&self, index: usize) -> Option<&Value<'cx>> { - if index < self.len() { - return Some(&self.values[index]); - } - None + /// Returns the number of arguments. + pub fn len(&self) -> usize { + self.args } - /// Returns a vector of arguments as [Values](Value) based on the indices of the iterator. - pub fn range>(&self, range: R) -> Vec<&Value<'cx>> { - range.filter_map(|index| self.value(index)).collect() + /// Returns `true` if there are no arguments. + pub fn is_empty(&self) -> bool { + self.len() == 0 } + /// Returns the context associated with the arguments. pub fn cx(&self) -> &'cx Context { self.cx } /// Returns the value of the function being called. - pub fn callee(&self) -> &Object<'cx> { - &self.callee - } - - /// Returns a mutable reference to the function being called. - pub fn callee_mut(&mut self) -> &mut Object<'cx> { - &mut self.callee + pub fn callee(&self) -> Object<'cx> { + Object::from(Local::from_handle(self.callee.handle())) } /// Returns the `this` value of the function. /// Refer to [MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/this) for more details. - pub fn this(&self) -> &Value<'cx> { - &self.this - } - - /// Returns a mutable reference to the `this` value of the function. - /// See [Arguments::this] for more details. - pub fn this_mut(&mut self) -> &mut Value<'cx> { - &mut self.this + pub fn this(&self) -> Value<'cx> { + Value::from(unsafe { Local::from_raw_handle(self.call_args.thisv()) }) } /// Returns the return value of the function. /// This value can be modified to change the return value. - pub fn rval(&mut self) -> &mut Value<'cx> { - &mut self.rval + pub fn rval(&mut self) -> Value<'cx> { + Value::from(unsafe { Local::from_raw_handle_mut(self.call_args.rval()) }) + } + + /// Returns the argument at a given index. + pub fn value(&self, index: usize) -> Option> { + if index < self.len() { + return Some(Value::from(unsafe { + Local::from_raw_handle(self.call_args.get(index as u32)) + })); + } + None } - /// Returns true if the function was called with `new`. + /// Returns `true` if the function was called with `new`. pub fn is_constructing(&self) -> bool { self.call_args.constructing_() } - /// Returns the raw [CallArgs]. - pub fn call_args(&self) -> CallArgs { - self.call_args + /// Returns `true` if the function ignores the return value. + pub fn ignores_return_value(&self) -> bool { + self.call_args.ignoresReturnValue_() + } + + pub fn call_args(&self) -> &CallArgs { + &self.call_args + } + + pub fn access<'a>(&'a mut self) -> Accessor<'a, 'cx> { + Accessor { args: self, index: 0 } } } @@ -119,48 +120,108 @@ pub struct Accessor<'a, 'cx> { index: usize, } -impl<'a, 'cx> Accessor<'a, 'cx> { - /// Returns the number of remaining arguments. +impl<'cx> Accessor<'_, 'cx> { + /// Returns the number of arguments remaining. pub fn len(&self) -> usize { self.args.len() - self.index } + /// Returns `true` if there are no arguments remaining. pub fn is_empty(&self) -> bool { - self.args.len() == self.index + self.index == 0 } - pub fn index(&self) -> usize { - self.index + /// Returns the context associated with the arguments. + pub fn cx(&self) -> &'cx Context { + self.args.cx() + } + + /// Returns the value of the function being called. + pub fn callee(&self) -> Object<'cx> { + self.args.callee() } - pub fn arg>(&mut self, strict: bool, config: T::Config) -> Option> { - self.args.values.get(self.index).map(|value| { - self.index += 1; - T::from_value(self.args.cx, value, strict, config) - }) + /// Returns the `this` value of the function. + /// Refer to [MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/this) for more details. + pub fn this(&self) -> Value<'cx> { + self.args.this() } - pub fn args>(&mut self, strict: bool, config: T::Config) -> Result> - where - T::Config: Clone, - { - self.args.values[self.index..] - .iter() - .map(|value| T::from_value(self.args.cx, value, strict, config.clone())) - .collect() + /// Returns the argument at the current index. + /// + /// ### Panics + /// Panics if there are no arguments remaining. + pub fn value(&mut self) -> Value<'cx> { + assert!(self.index < self.args.len()); + let arg = self.args.value(self.index).unwrap(); + self.index += 1; + arg + } + + /// Returns `true` if the function was called with `new`. + pub fn is_constructing(&self) -> bool { + self.args.is_constructing() + } + + /// Returns `true` if the function ignores the return value. + pub fn ignores_return_value(&self) -> bool { + self.args.ignores_return_value() } } -impl<'cx> Deref for Accessor<'_, 'cx> { - type Target = Arguments<'cx>; +pub trait FromArgument<'a, 'cx>: Sized { + type Config; - fn deref(&self) -> &Self::Target { - self.args + /// Converts from an argument. + fn from_argument(accessor: &'a mut Accessor<'_, 'cx>, config: Self::Config) -> Result; +} + +impl<'cx> FromArgument<'_, 'cx> for &'cx Context { + type Config = (); + + fn from_argument(accessor: &mut Accessor<'_, 'cx>, _: ()) -> Result<&'cx Context> { + Ok(accessor.cx()) } } -impl<'cx> DerefMut for Accessor<'_, 'cx> { - fn deref_mut(&mut self) -> &mut Self::Target { - self.args +impl<'a, 'cx> FromArgument<'a, 'cx> for &'a mut Arguments<'cx> { + type Config = (); + + fn from_argument(accessor: &'a mut Accessor<'_, 'cx>, _: ()) -> Result<&'a mut Arguments<'cx>> { + Ok(accessor.args) + } +} + +impl<'cx, T: FromValue<'cx>> FromArgument<'_, 'cx> for T { + type Config = T::Config; + + fn from_argument(accessor: &mut Accessor<'_, 'cx>, config: T::Config) -> Result { + T::from_value(accessor.cx(), &accessor.value(), false, config) + } +} + +impl<'cx, T: FromValue<'cx>> FromArgument<'_, 'cx> for Opt { + type Config = T::Config; + + fn from_argument(accessor: &mut Accessor<'_, 'cx>, config: Self::Config) -> Result> { + if accessor.is_empty() { + Ok(Opt(None)) + } else { + T::from_value(accessor.cx(), &accessor.value(), false, config).map(Some).map(Opt) + } + } +} + +impl<'cx, T: FromValue<'cx>> FromArgument<'_, 'cx> for Rest +where + T::Config: Clone, +{ + type Config = T::Config; + + fn from_argument(accessor: &mut Accessor<'_, 'cx>, config: Self::Config) -> Result> { + (0..accessor.len()) + .map(|_| T::from_value(accessor.cx(), &accessor.value(), false, config.clone())) + .collect::>>() + .map(Rest) } } diff --git a/ion/src/functions/closure.rs b/ion/src/functions/closure.rs index dbbaa507..73d9c172 100644 --- a/ion/src/functions/closure.rs +++ b/ion/src/functions/closure.rs @@ -69,7 +69,7 @@ pub(crate) unsafe extern "C" fn call_closure_once(cx: *mut JSContext, argc: u32, if let Some(closure) = closure.take() { let result = catch_unwind(AssertUnwindSafe(|| { - closure(args).map(|result| result.to_value(cx, args.rval())) + closure(args).map(|result| result.to_value(cx, &mut args.rval())) })); __handle_native_function_result(cx, result) } else { @@ -87,7 +87,7 @@ pub(crate) unsafe extern "C" fn call_closure(cx: *mut JSContext, argc: u32, vp: let closure = unsafe { &mut *(value.to_private() as *mut Box) }; let result = catch_unwind(AssertUnwindSafe(|| { - closure(args).map(|result| result.to_value(cx, args.rval())) + closure(args).map(|result| result.to_value(cx, &mut args.rval())) })); __handle_native_function_result(cx, result) } diff --git a/ion/src/functions/mod.rs b/ion/src/functions/mod.rs index 0a7af144..2910dfdb 100644 --- a/ion/src/functions/mod.rs +++ b/ion/src/functions/mod.rs @@ -8,12 +8,12 @@ use std::any::Any; use std::mem::forget; use std::thread::Result; -pub use arguments::Arguments; +pub use arguments::{Accessor, Arguments, FromArgument}; pub use closure::{Closure, ClosureOnce}; pub use function::{Function, NativeFunction}; use crate::{Context, Error, Object, ResultExc, ThrowException, Value}; -use crate::conversions::ToValue; +use crate::conversions::{FromValue, ToValue}; mod arguments; mod closure; @@ -59,3 +59,22 @@ fn handle_unwind_error(cx: &Context, unwind_error: Box) -> bool } false } + +/// Helper type for optional arguments. +pub struct Opt(pub Option); + +/// Helper type for rest/spread/variable arguments. +pub struct Rest(pub Box<[T]>); + +pub type VarArgs = Rest; + +/// Helper type for strict arguments. +pub struct Strict(pub T); + +impl<'cx, T: FromValue<'cx>> FromValue<'cx> for Strict { + type Config = T::Config; + + fn from_value(cx: &'cx Context, value: &Value, _: bool, config: Self::Config) -> crate::Result> { + T::from_value(cx, value, true, config).map(Strict) + } +} diff --git a/ion/src/lib.rs b/ion/src/lib.rs index 295330c7..1ad301e4 100644 --- a/ion/src/lib.rs +++ b/ion/src/lib.rs @@ -4,7 +4,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -#![allow(clippy::missing_safety_doc)] +#![allow(clippy::missing_safety_doc, clippy::module_inception)] #[macro_use] extern crate mozjs; diff --git a/ion/src/local.rs b/ion/src/local.rs index 0944ef5c..ea5dd080 100644 --- a/ion/src/local.rs +++ b/ion/src/local.rs @@ -28,13 +28,10 @@ where impl<'local, T: Copy + GCMethods + RootKind> Local<'local, T> { /// Forms a [Handle] to the [Local] which can be passed to SpiderMonkey APIs. - pub fn handle<'a>(&'a self) -> Handle<'a, T> - where - 'local: 'a, - { + pub fn handle(&self) -> Handle<'local, T> { match self { Self::Rooted(root) => unsafe { Handle::from_marked_location(&root.ptr) }, - Self::Mutable(handle) => handle.handle(), + Self::Mutable(handle) => unsafe { Handle::from_marked_location(&handle.get()) }, Self::Handle(handle) => *handle, } } @@ -43,10 +40,7 @@ impl<'local, T: Copy + GCMethods + RootKind> Local<'local, T> { /// /// ### Panics /// Panics when a [`Local::Handle`] is passed. - pub fn handle_mut<'a>(&'a mut self) -> MutableHandle<'a, T> - where - 'local: 'a, - { + pub fn handle_mut(&mut self) -> MutableHandle<'local, T> { match self { Local::Rooted(root) => unsafe { MutableHandle::from_marked_location(&mut root.ptr) }, Local::Mutable(handle) => *handle, diff --git a/ion/src/module.rs b/ion/src/module.rs index 2cc8dd60..deacfbc7 100644 --- a/ion/src/module.rs +++ b/ion/src/module.rs @@ -35,7 +35,7 @@ impl ModuleData { /// Converts [ModuleData] to an [Object] for storage. pub fn to_object<'cx>(&self, cx: &'cx Context) -> Object<'cx> { - let mut object = Object::new(cx); + let object = Object::new(cx); object.set_as(cx, "path", &self.path); object } @@ -178,7 +178,7 @@ pub trait ModuleLoader { fn register(&mut self, cx: &Context, module: *mut JSObject, request: &ModuleRequest) -> *mut JSObject; /// Returns metadata of a module, used to populate `import.meta`. - fn metadata(&self, cx: &Context, private: &Value, meta: &mut Object) -> bool; + fn metadata(&self, cx: &Context, private: &Value, meta: &Object) -> bool; } impl ModuleLoader for () { @@ -190,7 +190,7 @@ impl ModuleLoader for () { ptr::null_mut() } - fn metadata(&self, _: &Context, _: &Value, _: &mut Object) -> bool { + fn metadata(&self, _: &Context, _: &Value, _: &Object) -> bool { true } } @@ -223,8 +223,8 @@ pub fn init_module_loader(cx: &Context, loader: ML) .as_mut() .map(|loader| { let private = Value::from(unsafe { Local::from_raw_handle(private_data) }); - let mut metadata = Object::from(unsafe { Local::from_raw_handle(metadata) }); - loader.metadata(&cx, &private, &mut metadata) + let metadata = Object::from(unsafe { Local::from_raw_handle(metadata) }); + loader.metadata(&cx, &private, &metadata) }) .unwrap_or_else(|| true) } diff --git a/ion/src/objects/array.rs b/ion/src/objects/array.rs index 8ef00b38..3bd3e2d7 100644 --- a/ion/src/objects/array.rs +++ b/ion/src/objects/array.rs @@ -113,33 +113,33 @@ impl<'a> Array<'a> { /// Sets the [Value] at the given index of the [Array]. /// Returns `false` if the element cannot be set. - pub fn set(&mut self, cx: &Context, index: u32, value: &Value) -> bool { + pub fn set(&self, cx: &Context, index: u32, value: &Value) -> bool { self.arr.set(cx, index, value) } /// Sets the Rust type at the given index of the [Array]. /// Returns `false` if the element cannot be set. - pub fn set_as<'cx, T: ToValue<'cx> + ?Sized>(&mut self, cx: &'cx Context, index: u32, value: &T) -> bool { + pub fn set_as<'cx, T: ToValue<'cx> + ?Sized>(&self, cx: &'cx Context, index: u32, value: &T) -> bool { self.arr.set_as(cx, index, value) } /// Defines the [Value] at the given index of the [Array] with the given attributes. /// Returns `false` if the element cannot be defined. - pub fn define(&mut self, cx: &Context, index: u32, value: &Value, attrs: PropertyFlags) -> bool { + pub fn define(&self, cx: &Context, index: u32, value: &Value, attrs: PropertyFlags) -> bool { self.arr.define(cx, index, value, attrs) } /// Defines the Rust type at the given index of the [Array] with the given attributes. /// Returns `false` if the element cannot be defined. pub fn define_as<'cx, T: ToValue<'cx> + ?Sized>( - &mut self, cx: &'cx Context, index: u32, value: &T, attrs: PropertyFlags, + &self, cx: &'cx Context, index: u32, value: &T, attrs: PropertyFlags, ) -> bool { self.arr.define_as(cx, index, value, attrs) } /// Deletes the [JSVal] at the given index. /// Returns `false` if the element cannot be deleted. - pub fn delete(&mut self, cx: &Context, index: u32) -> bool { + pub fn delete(&self, cx: &Context, index: u32) -> bool { self.arr.delete(cx, index) } diff --git a/ion/src/objects/iterator.rs b/ion/src/objects/iterator.rs index 4e2209cf..7f5ac4af 100644 --- a/ion/src/objects/iterator.rs +++ b/ion/src/objects/iterator.rs @@ -48,7 +48,7 @@ pub struct IteratorResult<'cx> { impl<'cx> ToValue<'cx> for IteratorResult<'cx> { fn to_value(&self, cx: &'cx Context, value: &mut Value) { - let mut object = Object::new(cx); + let object = Object::new(cx); object.set_as(cx, "value", &self.value); object.set_as(cx, "done", &self.done); object.to_value(cx, value); @@ -91,11 +91,11 @@ impl Iterator { let cx = &unsafe { Context::new_unchecked(cx) }; let args = &mut unsafe { Arguments::new(cx, argc, vp) }; - let mut this = args.this().to_object(cx); - let iterator = Iterator::get_mut_private(&mut this); + let this = args.this().to_object(cx); + let iterator = Iterator::get_mut_private(&this); let result = iterator.next_value(cx); - result.to_value(cx, args.rval()); + result.to_value(cx, &mut args.rval()); true } diff --git a/ion/src/objects/map.rs b/ion/src/objects/map.rs index 148255e9..2773184b 100644 --- a/ion/src/objects/map.rs +++ b/ion/src/objects/map.rs @@ -75,7 +75,7 @@ impl<'m> Map<'m> { } /// Sets the value of the [Map] at the given key. - pub fn set(&mut self, cx: &Context, key: &Value, value: &Value) -> bool { + pub fn set(&self, cx: &Context, key: &Value, value: &Value) -> bool { unsafe { MapSet( cx.as_ptr(), @@ -87,13 +87,13 @@ impl<'m> Map<'m> { } /// Deletes the value of the [Map] at the given key. - pub fn delete(&mut self, cx: &Context, key: &Value) -> bool { + pub fn delete(&self, cx: &Context, key: &Value) -> bool { let mut rval = false; unsafe { MapDelete(cx.as_ptr(), self.handle().into(), key.handle().into(), &mut rval) && rval } } /// Clears the contents of the [Map]. - pub fn clear(&mut self, cx: &Context) -> bool { + pub fn clear(&self, cx: &Context) -> bool { unsafe { MapClear(cx.as_ptr(), self.handle().into()) } } diff --git a/ion/src/objects/object.rs b/ion/src/objects/object.rs index a75f85d8..798e050b 100644 --- a/ion/src/objects/object.rs +++ b/ion/src/objects/object.rs @@ -109,7 +109,7 @@ impl<'o> Object<'o> { /// Sets the [Value] at the given key of the [Object]. /// /// Returns `false` if the property cannot be set. - pub fn set<'cx, K: ToPropertyKey<'cx>>(&mut self, cx: &'cx Context, key: K, value: &Value) -> bool { + pub fn set<'cx, K: ToPropertyKey<'cx>>(&self, cx: &'cx Context, key: K, value: &Value) -> bool { let key = key.to_key(cx).unwrap(); unsafe { JS_SetPropertyById( @@ -125,7 +125,7 @@ impl<'o> Object<'o> { /// /// Returns `false` if the property cannot be set. pub fn set_as<'cx, K: ToPropertyKey<'cx>, T: ToValue<'cx> + ?Sized>( - &mut self, cx: &'cx Context, key: K, value: &T, + &self, cx: &'cx Context, key: K, value: &T, ) -> bool { self.set(cx, key, &value.as_value(cx)) } @@ -134,7 +134,7 @@ impl<'o> Object<'o> { /// /// Returns `false` if the property cannot be defined. pub fn define<'cx, K: ToPropertyKey<'cx>>( - &mut self, cx: &'cx Context, key: K, value: &Value, attrs: PropertyFlags, + &self, cx: &'cx Context, key: K, value: &Value, attrs: PropertyFlags, ) -> bool { let key = key.to_key(cx).unwrap(); unsafe { @@ -152,7 +152,7 @@ impl<'o> Object<'o> { /// /// Returns `false` if the property cannot be defined. pub fn define_as<'cx, K: ToPropertyKey<'cx>, T: ToValue<'cx> + ?Sized>( - &mut self, cx: &'cx Context, key: K, value: &T, attrs: PropertyFlags, + &self, cx: &'cx Context, key: K, value: &T, attrs: PropertyFlags, ) -> bool { self.define(cx, key, &value.as_value(cx), attrs) } @@ -161,7 +161,7 @@ impl<'o> Object<'o> { /// /// Parameters are similar to [create_function_spec](crate::spec::create_function_spec). pub fn define_method<'cx, K: ToPropertyKey<'cx>>( - &mut self, cx: &'cx Context, key: K, method: NativeFunction, nargs: u32, attrs: PropertyFlags, + &self, cx: &'cx Context, key: K, method: NativeFunction, nargs: u32, attrs: PropertyFlags, ) -> Function<'cx> { let key = key.to_key(cx).unwrap(); cx.root_function(unsafe { @@ -184,21 +184,21 @@ impl<'o> Object<'o> { feature = "macros", doc = "\nThey can be created through [function_spec](crate::function_spec)." )] - pub unsafe fn define_methods(&mut self, cx: &Context, methods: &[JSFunctionSpec]) -> bool { + pub unsafe fn define_methods(&self, cx: &Context, methods: &[JSFunctionSpec]) -> bool { unsafe { JS_DefineFunctions(cx.as_ptr(), self.handle().into(), methods.as_ptr()) } } /// Defines methods on the objects using the given [specs](JSFunctionSpecWithHelp), with help. /// /// The final element of the `methods` slice must be `JSFunctionSpecWithHelp::ZERO`. - pub unsafe fn define_methods_with_help(&mut self, cx: &Context, methods: &[JSFunctionSpecWithHelp]) -> bool { + pub unsafe fn define_methods_with_help(&self, cx: &Context, methods: &[JSFunctionSpecWithHelp]) -> bool { unsafe { JS_DefineFunctionsWithHelp(cx.as_ptr(), self.handle().into(), methods.as_ptr()) } } /// Defines properties on the object using the given [specs](JSPropertySpec). /// /// The final element of the `properties` slice must be `JSPropertySpec::ZERO`. - pub unsafe fn define_properties(&mut self, cx: &Context, properties: &[JSPropertySpec]) -> bool { + pub unsafe fn define_properties(&self, cx: &Context, properties: &[JSPropertySpec]) -> bool { unsafe { JS_DefineProperties(cx.as_ptr(), self.handle().into(), properties.as_ptr()) } } @@ -301,8 +301,8 @@ impl<'o> DerefMut for Object<'o> { pub struct ObjectKeysIter<'cx> { cx: &'cx Context, - keys: IdVector, slice: &'static [JSPropertyKey], + keys: IdVector, index: usize, count: usize, } @@ -314,24 +314,18 @@ impl<'cx> ObjectKeysIter<'cx> { let keys_slice = unsafe { slice::from_raw_parts(keys_slice.as_ptr(), count) }; ObjectKeysIter { cx, - keys, slice: keys_slice, + keys, index: 0, count, } } } -impl Drop for ObjectKeysIter<'_> { - fn drop(&mut self) { - self.slice = &[]; - } -} - impl<'cx> Iterator for ObjectKeysIter<'cx> { type Item = PropertyKey<'cx>; - fn next(&mut self) -> Option> { + fn next(&mut self) -> Option { if self.index < self.count { let key = &self.slice[self.index]; self.index += 1; @@ -347,7 +341,7 @@ impl<'cx> Iterator for ObjectKeysIter<'cx> { } impl<'cx> DoubleEndedIterator for ObjectKeysIter<'cx> { - fn next_back(&mut self) -> Option> { + fn next_back(&mut self) -> Option { if self.index < self.count { self.count -= 1; let key = &self.keys[self.count]; diff --git a/ion/src/objects/set.rs b/ion/src/objects/set.rs index 19d1cf1a..6d4290cb 100644 --- a/ion/src/objects/set.rs +++ b/ion/src/objects/set.rs @@ -62,18 +62,18 @@ impl<'s> Set<'s> { } /// Adds the key to the [Set]. - pub fn add(&mut self, cx: &Context, key: &Value) -> bool { + pub fn add(&self, cx: &Context, key: &Value) -> bool { unsafe { SetAdd(cx.as_ptr(), self.handle().into(), key.handle().into()) } } /// Deletes the key from the [Set]. - pub fn delete(&mut self, cx: &Context, key: &Value) -> bool { + pub fn delete(&self, cx: &Context, key: &Value) -> bool { let mut rval = false; unsafe { SetDelete(cx.as_ptr(), self.handle().into(), key.handle().into(), &mut rval) && rval } } /// Clears the contents of the [Set]. - pub fn clear(&mut self, cx: &Context) -> bool { + pub fn clear(&self, cx: &Context) -> bool { unsafe { SetClear(cx.as_ptr(), self.handle().into()) } } diff --git a/ion/src/objects/typedarray/buffer.rs b/ion/src/objects/typedarray/buffer.rs index 0132eb27..edf7191b 100644 --- a/ion/src/objects/typedarray/buffer.rs +++ b/ion/src/objects/typedarray/buffer.rs @@ -102,7 +102,8 @@ impl<'ab> ArrayBuffer<'ab> { /// Returns a mutable slice to the contents of the [ArrayBuffer]. /// /// The slice may be invalidated if the [ArrayBuffer] is detached. - pub unsafe fn as_mut_slice(&mut self) -> &mut [u8] { + #[allow(clippy::mut_from_ref)] + pub unsafe fn as_mut_slice(&self) -> &mut [u8] { let (ptr, len, _) = self.data(); unsafe { slice::from_raw_parts_mut(ptr, len) } } @@ -134,11 +135,11 @@ impl<'ab> ArrayBuffer<'ab> { } } - pub fn detach(&mut self, cx: &Context) -> bool { + pub fn detach(&self, cx: &Context) -> bool { unsafe { DetachArrayBuffer(cx.as_ptr(), self.handle().into()) } } - pub fn transfer<'cx>(&mut self, cx: &'cx Context) -> Result> { + pub fn transfer<'cx>(&self, cx: &'cx Context) -> Result> { let len = self.data().1; let data = unsafe { StealArrayBufferContents(cx.as_ptr(), self.handle().into()) }; if data.is_null() { diff --git a/ion/src/objects/typedarray/view.rs b/ion/src/objects/typedarray/view.rs index 7f6c8af7..85640ab2 100644 --- a/ion/src/objects/typedarray/view.rs +++ b/ion/src/objects/typedarray/view.rs @@ -194,7 +194,8 @@ impl<'bv, T: TypedArrayElement> TypedArray<'bv, T> { /// Returns a mutable slice to the contents of the [TypedArray]. /// /// The slice may be invalidated if the underlying [ArrayBuffer] is detached. - pub unsafe fn as_mut_slice(&mut self) -> &mut [T::Element] { + #[allow(clippy::mut_from_ref)] + pub unsafe fn as_mut_slice(&self) -> &mut [T::Element] { let (ptr, len) = self.data(); unsafe { slice::from_raw_parts_mut(ptr, len) } } diff --git a/ion/tests/objects/array.rs b/ion/tests/objects/array.rs index c4076c68..d2a60b2c 100644 --- a/ion/tests/objects/array.rs +++ b/ion/tests/objects/array.rs @@ -15,7 +15,7 @@ fn array() { let global = default_new_global(cx); let _realm = JSAutoRealm::new(runtime.cx(), global.handle().get()); - let mut array = Array::new(cx); + let array = Array::new(cx); array.set(cx, 0, &Value::null(cx)); array.define(cx, 2, &Value::undefined(cx), PropertyFlags::all()); diff --git a/ion/tests/objects/object.rs b/ion/tests/objects/object.rs index c7352953..1a0085ad 100644 --- a/ion/tests/objects/object.rs +++ b/ion/tests/objects/object.rs @@ -15,7 +15,7 @@ fn object() { let global = default_new_global(cx); let _realm = JSAutoRealm::new(runtime.cx(), global.handle().get()); - let mut object = Object::new(cx); + let object = Object::new(cx); object.set(cx, "key1", &Value::null(cx)); object.define(cx, "key2", &Value::undefined(cx), PropertyFlags::all()); diff --git a/ion/tests/rooting.rs b/ion/tests/rooting.rs index 2233854a..88f1b42d 100644 --- a/ion/tests/rooting.rs +++ b/ion/tests/rooting.rs @@ -12,7 +12,7 @@ fn main() { let runtime = Runtime::new(engine.handle()); let cx = &Context::from_runtime(&runtime); - let mut global = default_new_global(cx); + let global = default_new_global(cx); let _realm = JSAutoRealm::new(cx.as_ptr(), global.handle().get()); let _native = global.define_method(cx, "native", native, 1, PropertyFlags::all()); @@ -43,7 +43,7 @@ unsafe extern "C" fn native(cx: *mut JSContext, argc: u32, vp: *mut JSVal) -> bo } let arg2 = args.value(2).unwrap(); - if arg2.handle().is_string() && String::from_value(cx, arg2, false, ()).unwrap() == *"Old String" { + if arg2.handle().is_string() && String::from_value(cx, &arg2, false, ()).unwrap() == *"Old String" { correct_args += 1; } diff --git a/modules/src/assert/assert.rs b/modules/src/assert/assert.rs index 522a3e1f..35d5ea8a 100644 --- a/modules/src/assert/assert.rs +++ b/modules/src/assert/assert.rs @@ -65,7 +65,7 @@ impl NativeModule for Assert { const SOURCE: &'static str = include_str!("assert.js"); fn module(cx: &Context) -> Option { - let mut assert = Object::new(cx); + let assert = Object::new(cx); unsafe { assert.define_methods(cx, FUNCTIONS).then_some(assert) } } } diff --git a/modules/src/fs/fs.rs b/modules/src/fs/fs.rs index 105fa6e8..34a93beb 100644 --- a/modules/src/fs/fs.rs +++ b/modules/src/fs/fs.rs @@ -433,8 +433,8 @@ impl NativeModule for FileSystem { const SOURCE: &'static str = include_str!("fs.js"); fn module(cx: &Context) -> Option { - let mut fs = Object::new(cx); - let mut sync = Object::new(cx); + let fs = Object::new(cx); + let sync = Object::new(cx); if unsafe { fs.define_methods(cx, ASYNC_FUNCTIONS) } && unsafe { sync.define_methods(cx, SYNC_FUNCTIONS) } diff --git a/modules/src/lib.rs b/modules/src/lib.rs index d80cefc0..f9cb527e 100644 --- a/modules/src/lib.rs +++ b/modules/src/lib.rs @@ -25,14 +25,14 @@ mod url; pub struct Modules; impl StandardModules for Modules { - fn init(self, cx: &Context, global: &mut Object) -> bool { + fn init(self, cx: &Context, global: &Object) -> bool { init_module::(cx, global) && init_module::(cx, global) && init_module::(cx, global) && init_module::(cx, global) } - fn init_globals(self, cx: &Context, global: &mut Object) -> bool { + fn init_globals(self, cx: &Context, global: &Object) -> bool { init_global_module::(cx, global) && init_global_module::(cx, global) && init_global_module::(cx, global) diff --git a/modules/src/path/path.rs b/modules/src/path/path.rs index 99950cf8..808c3552 100644 --- a/modules/src/path/path.rs +++ b/modules/src/path/path.rs @@ -10,6 +10,7 @@ use mozjs::jsapi::{JSFunctionSpec, JSPropertySpec}; use ion::{Context, Error, Object, Result}; use ion::flags::PropertyFlags; +use ion::functions::Rest; use ion::spec::create_property_spec_string; use runtime::modules::NativeModule; @@ -24,9 +25,9 @@ const DELIMITER: &str = ";\0"; const DELIMITER: &str = ":\0"; #[js_fn] -fn join(#[ion(varargs)] segments: Vec) -> String { +fn join(Rest(segments): Rest) -> String { let mut path = PathBuf::new(); - path.extend(segments); + path.extend(segments.into_vec()); String::from(path.to_str().unwrap()) } @@ -133,7 +134,7 @@ impl NativeModule for PathM { const SOURCE: &'static str = include_str!("path.js"); fn module(cx: &Context) -> Option { - let mut path = Object::new(cx); + let path = Object::new(cx); if unsafe { path.define_methods(cx, FUNCTIONS) && path.define_properties(cx, PROPERTIES) } { return Some(path); } diff --git a/modules/src/url/url.rs b/modules/src/url/url.rs index 3efec8cc..86e4473d 100644 --- a/modules/src/url/url.rs +++ b/modules/src/url/url.rs @@ -41,20 +41,20 @@ impl NativeModule for UrlM { const SOURCE: &'static str = include_str!("url.js"); fn module(cx: &Context) -> Option { - let mut url = Object::new(cx); + let url = Object::new(cx); let global = Object::global(cx); if unsafe { url.define_methods(cx, FUNCTIONS) } { - if let Some(global_url) = global.get(cx, stringify!(URL)) { - url.set(cx, stringify!(URL), &global_url); + if let Some(global_url) = global.get(cx, "URL") { + url.set(cx, "URL", &global_url); } else { - URL::init_class(cx, &mut url); + URL::init_class(cx, &url); } - if let Some(url_search_params) = global.get(cx, stringify!(URLSearchParams)) { - url.set(cx, stringify!(URLSearchParams), &url_search_params); + if let Some(url_search_params) = global.get(cx, "URLSearchParams") { + url.set(cx, "URLSearchParams", &url_search_params); } else { - URLSearchParams::init_class(cx, &mut url); + URLSearchParams::init_class(cx, &url); } return Some(url); diff --git a/modules/tests/assert.rs b/modules/tests/assert.rs index a7c8b886..988fc71b 100644 --- a/modules/tests/assert.rs +++ b/modules/tests/assert.rs @@ -75,7 +75,7 @@ pub async fn eval_module(rt: &Runtime<'_>, cx: &Context, test: (&str, &str)) { #[ion::js_fn] fn on_rejected(cx: &Context, value: Value) { - let mut global = Object::global(cx); + let global = Object::global(cx); global.set(cx, EXCEPTION_STRING, &value); Exception::clear(cx); } diff --git a/runtime/src/cache/mod.rs b/runtime/src/cache/mod.rs index 262cefbf..9cf25b1b 100644 --- a/runtime/src/cache/mod.rs +++ b/runtime/src/cache/mod.rs @@ -10,7 +10,6 @@ use sourcemap::SourceMap; pub use cache::*; -#[allow(clippy::module_inception)] mod cache; pub mod map; diff --git a/runtime/src/event_loop/macrotasks.rs b/runtime/src/event_loop/macrotasks.rs index abe26583..15b40a50 100644 --- a/runtime/src/event_loop/macrotasks.rs +++ b/runtime/src/event_loop/macrotasks.rs @@ -44,7 +44,7 @@ impl Debug for SignalMacrotask { #[derive(Debug)] pub struct TimerMacrotask { callback: *mut JSFunction, - arguments: Vec, + arguments: Box<[JSVal]>, repeat: bool, scheduled: DateTime, duration: Duration, @@ -52,7 +52,7 @@ pub struct TimerMacrotask { } impl TimerMacrotask { - pub fn new(callback: Function, arguments: Vec, repeat: bool, duration: Duration) -> TimerMacrotask { + pub fn new(callback: Function, arguments: Box<[JSVal]>, repeat: bool, duration: Duration) -> TimerMacrotask { TimerMacrotask { callback: callback.get(), arguments, @@ -109,14 +109,14 @@ impl Macrotask { } let (callback, args) = match &self { Macrotask::Timer(timer) => (timer.callback, timer.arguments.clone()), - Macrotask::User(user) => (user.callback, Vec::new()), + Macrotask::User(user) => (user.callback, Box::default()), _ => unreachable!(), }; let callback = Function::from(cx.root_function(callback)); - let args: Vec<_> = args.into_iter().map(|value| Value::from(cx.root_value(value))).collect(); + let args: Vec<_> = args.into_vec().into_iter().map(|value| Value::from(cx.root_value(value))).collect(); - callback.call(cx, &Object::global(cx), args.as_slice()).map(|_| (Some(self))) + callback.call(cx, &Object::global(cx), args.as_slice()).map(|_| Some(self)) } fn terminate(&self) -> bool { diff --git a/runtime/src/globals/abort.rs b/runtime/src/globals/abort.rs index 4027c0a6..6373286e 100644 --- a/runtime/src/globals/abort.rs +++ b/runtime/src/globals/abort.rs @@ -192,7 +192,7 @@ impl<'cx> FromValue<'cx> for AbortSignal { type Config = (); fn from_value(cx: &'cx Context, value: &Value, strict: bool, _: ()) -> Result { let object = Object::from_value(cx, value, strict, ())?; - if AbortSignal::instance_of(cx, &object, None) { + if AbortSignal::instance_of(cx, &object) { Ok(AbortSignal { reflector: Reflector::default(), signal: AbortSignal::get_private(&object).signal.clone(), @@ -203,6 +203,6 @@ impl<'cx> FromValue<'cx> for AbortSignal { } } -pub fn define(cx: &Context, global: &mut Object) -> bool { +pub fn define(cx: &Context, global: &Object) -> bool { AbortController::init_class(cx, global).0 && AbortSignal::init_class(cx, global).0 } diff --git a/runtime/src/globals/base64.rs b/runtime/src/globals/base64.rs index 0b9cbed5..20157f14 100644 --- a/runtime/src/globals/base64.rs +++ b/runtime/src/globals/base64.rs @@ -28,6 +28,6 @@ fn atob(data: ByteString) -> Result { const FUNCTIONS: &[JSFunctionSpec] = &[function_spec!(btoa, 1), function_spec!(atob, 1), JSFunctionSpec::ZERO]; -pub fn define(cx: &Context, global: &mut Object) -> bool { +pub fn define(cx: &Context, global: &Object) -> bool { unsafe { global.define_methods(cx, FUNCTIONS) } } diff --git a/runtime/src/globals/console.rs b/runtime/src/globals/console.rs index fd3454a9..6a79715e 100644 --- a/runtime/src/globals/console.rs +++ b/runtime/src/globals/console.rs @@ -22,6 +22,7 @@ use ion::format::{format_value, INDENT}; use ion::format::Config as FormatConfig; use ion::format::key::format_key; use ion::format::primitive::format_primitive; +use ion::functions::Rest; use crate::cache::map::find_sourcemap; use crate::config::{Config, LogLevel}; @@ -68,43 +69,43 @@ fn get_label(label: Option) -> String { } #[js_fn] -fn log(cx: &Context, #[ion(varargs)] values: Vec) { +fn log(cx: &Context, Rest(values): Rest) { if Config::global().log_level >= LogLevel::Info { print_indent(false); - print_args(cx, values.as_slice(), false); + print_args(cx, &values, false); println!(); } } #[js_fn] -fn warn(cx: &Context, #[ion(varargs)] values: Vec) { +fn warn(cx: &Context, Rest(values): Rest) { if Config::global().log_level >= LogLevel::Warn { print_indent(true); - print_args(cx, values.as_slice(), true); + print_args(cx, &values, true); println!(); } } #[js_fn] -fn error(cx: &Context, #[ion(varargs)] values: Vec) { +fn error(cx: &Context, Rest(values): Rest) { if Config::global().log_level >= LogLevel::Error { print_indent(true); - print_args(cx, values.as_slice(), true); + print_args(cx, &values, true); println!(); } } #[js_fn] -fn debug(cx: &Context, #[ion(varargs)] values: Vec) { +fn debug(cx: &Context, Rest(values): Rest) { if Config::global().log_level == LogLevel::Debug { print_indent(false); - print_args(cx, values.as_slice(), false); + print_args(cx, &values, false); println!(); } } #[js_fn] -fn assert(cx: &Context, assertion: Option, #[ion(varargs)] values: Vec) { +fn assert(cx: &Context, assertion: Option, Rest(values): Rest) { if Config::global().log_level >= LogLevel::Error { if let Some(assertion) = assertion { if assertion { @@ -130,7 +131,7 @@ fn assert(cx: &Context, assertion: Option, #[ion(varargs)] values: Vec) { +fn trace(cx: &Context, Rest(values): Rest) { if Config::global().log_level == LogLevel::Debug { print_indent(false); print!("Trace: "); - print_args(cx, values.as_slice(), false); + print_args(cx, &values, false); println!(); let mut stack = Stack::from_capture(cx); @@ -172,11 +173,11 @@ fn trace(cx: &Context, #[ion(varargs)] values: Vec) { } #[js_fn] -fn group(cx: &Context, #[ion(varargs)] values: Vec) { +fn group(cx: &Context, Rest(values): Rest) { INDENTS.set(INDENTS.get().min(u16::MAX - 1) + 1); if Config::global().log_level >= LogLevel::Info { - print_args(cx, values.as_slice(), false); + print_args(cx, &values, false); println!(); } } @@ -240,7 +241,7 @@ fn time(label: Option) { } #[js_fn] -fn timeLog(cx: &Context, label: Option, #[ion(varargs)] values: Vec) { +fn timeLog(cx: &Context, label: Option, Rest(values): Rest) { let label = get_label(label); TIMER_MAP.with_borrow(|timers| match timers.get(&label) { Some(start) => { @@ -248,7 +249,7 @@ fn timeLog(cx: &Context, label: Option, #[ion(varargs)] values: Vec bool { - let mut console = Object::new(cx); +pub fn define(cx: &Context, global: &Object) -> bool { + let console = Object::new(cx); (unsafe { console.define_methods(cx, METHODS) }) && global.define_as(cx, "console", &console, PropertyFlags::CONSTANT_ENUMERATED) } diff --git a/runtime/src/globals/encoding/encoder.rs b/runtime/src/globals/encoding/encoder.rs index 93cdc517..447beb27 100644 --- a/runtime/src/globals/encoding/encoder.rs +++ b/runtime/src/globals/encoding/encoder.rs @@ -18,7 +18,7 @@ pub struct EncodeResult { impl<'cx> ToValue<'cx> for EncodeResult { fn to_value(&self, cx: &'cx Context, value: &mut Value) { - let mut object = Object::new(cx); + let object = Object::new(cx); object.set_as(cx, "read", &self.read); object.set_as(cx, "written", &self.written); object.to_value(cx, value); @@ -52,7 +52,6 @@ impl TextEncoder { #[ion(name = "encodeInto")] pub fn encode_into(&mut self, input: String, destination: Uint8Array) -> EncodeResult { - let mut destination = destination; let (_, read, written, _) = self.encoder.encode_from_utf8(&input, unsafe { destination.as_mut_slice() }, true); EncodeResult { read: read as u64, diff --git a/runtime/src/globals/encoding/mod.rs b/runtime/src/globals/encoding/mod.rs index 53d08acf..91e76332 100644 --- a/runtime/src/globals/encoding/mod.rs +++ b/runtime/src/globals/encoding/mod.rs @@ -11,6 +11,6 @@ use ion::{ClassDefinition, Context, Object}; mod decoder; mod encoder; -pub fn define(cx: &Context, global: &mut Object) -> bool { +pub fn define(cx: &Context, global: &Object) -> bool { TextDecoder::init_class(cx, global).0 && TextEncoder::init_class(cx, global).0 } diff --git a/runtime/src/globals/fetch/body.rs b/runtime/src/globals/fetch/body.rs index 154e703a..7bde8ae2 100644 --- a/runtime/src/globals/fetch/body.rs +++ b/runtime/src/globals/fetch/body.rs @@ -9,8 +9,8 @@ use std::fmt::{Display, Formatter}; use bytes::Bytes; use form_urlencoded::Serializer; -use http::header::CONTENT_TYPE; use http::{HeaderMap, HeaderValue}; +use http::header::CONTENT_TYPE; use hyper::Body; use mozjs::jsapi::Heap; use mozjs::jsval::JSVal; diff --git a/runtime/src/globals/fetch/header.rs b/runtime/src/globals/fetch/header.rs index d0138542..b12f3d8a 100644 --- a/runtime/src/globals/fetch/header.rs +++ b/runtime/src/globals/fetch/header.rs @@ -70,7 +70,7 @@ impl<'cx> FromValue<'cx> for HeaderEntry { impl ToValue<'_> for HeaderEntry { fn to_value(&self, cx: &Context, value: &mut Value) { - let mut array = Array::new(cx); + let array = Array::new(cx); array.set_as(cx, 0, &self.name); array.set_as(cx, 1, &self.value); array.to_value(cx, value); diff --git a/runtime/src/globals/fetch/mod.rs b/runtime/src/globals/fetch/mod.rs index 46d54e00..667745b8 100644 --- a/runtime/src/globals/fetch/mod.rs +++ b/runtime/src/globals/fetch/mod.rs @@ -73,8 +73,8 @@ fn fetch<'cx>(cx: &'cx Context, resource: RequestInfo, init: Option return Some(promise); } - let mut headers = Object::from(unsafe { Local::from_heap(&request.headers) }); - let headers = Headers::get_mut_private(&mut headers); + let headers = Object::from(unsafe { Local::from_heap(&request.headers) }); + let headers = Headers::get_mut_private(&headers); if !headers.headers.contains_key(ACCEPT) { headers.headers.append(ACCEPT, HeaderValue::from_static("*/*")); } @@ -95,14 +95,14 @@ fn fetch<'cx>(cx: &'cx Context, resource: RequestInfo, init: Option let cx2 = unsafe { Context::new_unchecked(cx.as_ptr()) }; let request = request.handle().into_handle(); future_to_promise(cx, async move { - let mut request = Object::from(unsafe { Local::from_raw_handle(request) }); - let res = fetch_internal(&cx2, &mut request, GLOBAL_CLIENT.get().unwrap().clone()).await; + let request = Object::from(unsafe { Local::from_raw_handle(request) }); + let res = fetch_internal(&cx2, &request, GLOBAL_CLIENT.get().unwrap().clone()).await; cx2.unroot_persistent_object(request.handle().get()); res }) } -async fn fetch_internal<'o>(cx: &Context, request: &mut Object<'o>, client: Client) -> ResultExc<*mut JSObject> { +async fn fetch_internal<'o>(cx: &Context, request: &Object<'o>, client: Client) -> ResultExc<*mut JSObject> { let request = Request::get_mut_private(request); let signal = Object::from(unsafe { Local::from_heap(&request.signal_object) }); let signal = AbortSignal::get_private(&signal).signal.clone().poll(); @@ -250,8 +250,8 @@ async fn main_fetch(cx: &Context, request: &mut Request, client: Client, redirec response.url.get_or_insert(request.url.clone()); - let mut headers = Object::from(unsafe { Local::from_heap(&response.headers) }); - let headers = Headers::get_mut_private(&mut headers); + let headers = Object::from(unsafe { Local::from_heap(&response.headers) }); + let headers = Headers::get_mut_private(&headers); if !opaque_redirect && taint == ResponseTaint::Opaque @@ -419,8 +419,8 @@ async fn http_fetch( #[async_recursion(?Send)] async fn http_network_fetch(cx: &Context, request: &Request, client: Client, is_new: bool) -> Response { - let mut headers = Object::from(unsafe { Local::from_heap(&request.headers) }); - let mut headers = Headers::get_mut_private(&mut headers).headers.clone(); + let headers = Object::from(unsafe { Local::from_heap(&request.headers) }); + let mut headers = Headers::get_mut_private(&headers).headers.clone(); let length = request .body @@ -569,8 +569,8 @@ async fn http_redirect_fetch( { request.method = Method::GET; request.body = FetchBody::default(); - let mut headers = Object::from(unsafe { Local::from_heap(&request.headers) }); - let headers = Headers::get_mut_private(&mut headers); + let headers = Object::from(unsafe { Local::from_heap(&request.headers) }); + let headers = Headers::get_mut_private(&headers); remove_all_header_entries(&mut headers.headers, &CONTENT_ENCODING); remove_all_header_entries(&mut headers.headers, &CONTENT_LANGUAGE); remove_all_header_entries(&mut headers.headers, &CONTENT_LOCATION); @@ -591,7 +591,7 @@ async fn http_redirect_fetch( main_fetch(cx, request, client, redirections + 1).await } -pub fn define(cx: &Context, global: &mut Object) -> bool { +pub fn define(cx: &Context, global: &Object) -> bool { let _ = GLOBAL_CLIENT.set(default_client()); global.define_method(cx, "fetch", fetch, 1, PropertyFlags::CONSTANT_ENUMERATED); Headers::init_class(cx, global).0 && Request::init_class(cx, global).0 && Response::init_class(cx, global).0 diff --git a/runtime/src/globals/fetch/response/body.rs b/runtime/src/globals/fetch/response/body.rs index 4cd34206..a5fbb91b 100644 --- a/runtime/src/globals/fetch/response/body.rs +++ b/runtime/src/globals/fetch/response/body.rs @@ -5,7 +5,9 @@ */ use hyper::{Body, body}; + use ion::Result; + use crate::globals::fetch::body::FetchBody; #[derive(Traceable)] diff --git a/runtime/src/globals/fetch/response/mod.rs b/runtime/src/globals/fetch/response/mod.rs index e104c3fd..9329a8e5 100644 --- a/runtime/src/globals/fetch/response/mod.rs +++ b/runtime/src/globals/fetch/response/mod.rs @@ -189,8 +189,8 @@ impl Response { let cx2 = unsafe { Context::new_unchecked(cx.as_ptr()) }; let this = this.handle().into(); future_to_promise::<_, _, Error>(cx, async move { - let mut response = Object::from(unsafe { Local::from_raw_handle(this) }); - let response = Response::get_mut_private(&mut response); + let response = Object::from(unsafe { Local::from_raw_handle(this) }); + let response = Response::get_mut_private(&response); let bytes = response.read_to_bytes().await?; cx2.unroot_persistent_object(this.get()); Ok(ArrayBufferWrapper::from(bytes)) @@ -202,8 +202,8 @@ impl Response { let cx2 = unsafe { Context::new_unchecked(cx.as_ptr()) }; let this = this.handle().into(); future_to_promise::<_, _, Error>(cx, async move { - let mut response = Object::from(unsafe { Local::from_raw_handle(this) }); - let response = Response::get_mut_private(&mut response); + let response = Object::from(unsafe { Local::from_raw_handle(this) }); + let response = Response::get_mut_private(&response); let bytes = response.read_to_bytes().await?; cx2.unroot_persistent_object(this.get()); String::from_utf8(bytes).map_err(|e| Error::new(&format!("Invalid UTF-8 sequence: {}", e), None)) diff --git a/runtime/src/globals/file/mod.rs b/runtime/src/globals/file/mod.rs index 31e301a9..8f9d5569 100644 --- a/runtime/src/globals/file/mod.rs +++ b/runtime/src/globals/file/mod.rs @@ -57,7 +57,7 @@ impl File { } } -pub fn define(cx: &Context, object: &mut Object) -> bool { +pub fn define(cx: &Context, object: &Object) -> bool { Blob::init_class(cx, object).0 && File::init_class(cx, object).0 && FileReader::init_class(cx, object).0 diff --git a/runtime/src/globals/microtasks.rs b/runtime/src/globals/microtasks.rs index bf7173be..1a8e530f 100644 --- a/runtime/src/globals/microtasks.rs +++ b/runtime/src/globals/microtasks.rs @@ -25,7 +25,7 @@ fn queueMicrotask(cx: &Context, callback: Function) -> Result<()> { const FUNCTION: JSFunctionSpec = function_spec!(queueMicrotask, 0); -pub fn define(cx: &Context, global: &mut Object) -> bool { +pub fn define(cx: &Context, global: &Object) -> bool { global.define_as( cx, "queueMicrotask", diff --git a/runtime/src/globals/mod.rs b/runtime/src/globals/mod.rs index 91554302..02067929 100644 --- a/runtime/src/globals/mod.rs +++ b/runtime/src/globals/mod.rs @@ -17,7 +17,7 @@ pub mod microtasks; pub mod timers; pub mod url; -pub fn init_globals(cx: &Context, global: &mut Object) -> bool { +pub fn init_globals(cx: &Context, global: &Object) -> bool { let result = base64::define(cx, global) && console::define(cx, global) && encoding::define(cx, global) @@ -34,10 +34,10 @@ pub fn init_globals(cx: &Context, global: &mut Object) -> bool { } } -pub fn init_timers(cx: &Context, global: &mut Object) -> bool { +pub fn init_timers(cx: &Context, global: &Object) -> bool { timers::define(cx, global) && abort::define(cx, global) } -pub fn init_microtasks(cx: &Context, global: &mut Object) -> bool { +pub fn init_microtasks(cx: &Context, global: &Object) -> bool { microtasks::define(cx, global) } diff --git a/runtime/src/globals/timers.rs b/runtime/src/globals/timers.rs index 1cdd2857..7d576890 100644 --- a/runtime/src/globals/timers.rs +++ b/runtime/src/globals/timers.rs @@ -10,6 +10,7 @@ use mozjs::jsapi::JSFunctionSpec; use mozjs::jsval::JSVal; use ion::{Context, Error, Function, Object, Result}; +use ion::functions::Rest; use crate::ContextExt; use crate::event_loop::macrotasks::{Macrotask, TimerMacrotask, UserMacrotask}; @@ -18,7 +19,7 @@ const MINIMUM_DELAY: i32 = 1; const MINIMUM_DELAY_NESTED: i32 = 4; fn set_timer( - cx: &Context, callback: Function, duration: Option, arguments: Vec, repeat: bool, + cx: &Context, callback: Function, duration: Option, arguments: Box<[JSVal]>, repeat: bool, ) -> Result { let event_loop = unsafe { &mut cx.get_private().event_loop }; if let Some(queue) = &mut event_loop.macrotasks { @@ -52,16 +53,14 @@ fn clear_timer(cx: &Context, id: Option) -> Result<()> { #[js_fn] fn setTimeout( - cx: &Context, callback: Function, #[ion(convert = Clamp)] duration: Option, - #[ion(varargs)] arguments: Vec, + cx: &Context, callback: Function, #[ion(convert = Clamp)] duration: Option, Rest(arguments): Rest, ) -> Result { set_timer(cx, callback, duration, arguments, false) } #[js_fn] fn setInterval( - cx: &Context, callback: Function, #[ion(convert = Clamp)] duration: Option, - #[ion(varargs)] arguments: Vec, + cx: &Context, callback: Function, #[ion(convert = Clamp)] duration: Option, Rest(arguments): Rest, ) -> Result { set_timer(cx, callback, duration, arguments, true) } @@ -96,6 +95,6 @@ const FUNCTIONS: &[JSFunctionSpec] = &[ JSFunctionSpec::ZERO, ]; -pub fn define(cx: &Context, global: &mut Object) -> bool { +pub fn define(cx: &Context, global: &Object) -> bool { unsafe { global.define_methods(cx, FUNCTIONS) } } diff --git a/runtime/src/globals/url/mod.rs b/runtime/src/globals/url/mod.rs index 5408a376..36e7951a 100644 --- a/runtime/src/globals/url/mod.rs +++ b/runtime/src/globals/url/mod.rs @@ -93,8 +93,8 @@ impl URL { pub fn set_href(&mut self, input: String) -> Result<()> { match Url::parse(&input) { Ok(url) => { - let mut search_params = Object::from(unsafe { Local::from_heap(&self.search_params) }); - let search_params = URLSearchParams::get_mut_private(&mut search_params); + let search_params = Object::from(unsafe { Local::from_heap(&self.search_params) }); + let search_params = URLSearchParams::get_mut_private(&search_params); search_params.set_pairs(url.query_pairs().into_owned().collect()); self.url = url; Ok(()) @@ -234,6 +234,6 @@ impl URL { } } -pub fn define(cx: &Context, global: &mut Object) -> bool { +pub fn define(cx: &Context, global: &Object) -> bool { URL::init_class(cx, global).0 && URLSearchParams::init_class(cx, global).0 } diff --git a/runtime/src/globals/url/search_params.rs b/runtime/src/globals/url/search_params.rs index d82ae691..c51871a1 100644 --- a/runtime/src/globals/url/search_params.rs +++ b/runtime/src/globals/url/search_params.rs @@ -163,8 +163,8 @@ impl URLSearchParams { fn update(&mut self) { if let Some(url) = &self.url { - let mut url = Object::from(unsafe { Local::from_heap(url) }); - let url = URL::get_mut_private(&mut url); + let url = Object::from(unsafe { Local::from_heap(url) }); + let url = URL::get_mut_private(&url); if self.pairs.is_empty() { url.url.set_query(None); } else { diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index 68603a51..def5ce7e 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -4,7 +4,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -#![allow(clippy::missing_safety_doc)] +#![allow(clippy::missing_safety_doc, clippy::module_inception)] #[macro_use] extern crate ion; diff --git a/runtime/src/modules/loader.rs b/runtime/src/modules/loader.rs index 1bcd59e0..bedef5be 100644 --- a/runtime/src/modules/loader.rs +++ b/runtime/src/modules/loader.rs @@ -82,7 +82,7 @@ impl ModuleLoader for Loader { } } - fn metadata(&self, cx: &Context, private: &Value, meta: &mut Object) -> bool { + fn metadata(&self, cx: &Context, private: &Value, meta: &Object) -> bool { let data = ModuleData::from_private(cx, private); if let Some(data) = data { diff --git a/runtime/src/modules/standard.rs b/runtime/src/modules/standard.rs index 9cb93440..70b5782e 100644 --- a/runtime/src/modules/standard.rs +++ b/runtime/src/modules/standard.rs @@ -9,17 +9,17 @@ use ion::flags::PropertyFlags; use ion::module::{Module, ModuleRequest}; pub trait StandardModules { - fn init(self, cx: &Context, global: &mut Object) -> bool; + fn init(self, cx: &Context, global: &Object) -> bool; - fn init_globals(self, cx: &Context, global: &mut Object) -> bool; + fn init_globals(self, cx: &Context, global: &Object) -> bool; } impl StandardModules for () { - fn init(self, _: &Context, _: &mut Object) -> bool { + fn init(self, _: &Context, _: &Object) -> bool { true } - fn init_globals(self, _: &Context, _: &mut Object) -> bool { + fn init_globals(self, _: &Context, _: &Object) -> bool { true } } @@ -32,23 +32,23 @@ pub trait NativeModule { } impl StandardModules for M { - fn init(self, cx: &Context, global: &mut Object) -> bool { + fn init(self, cx: &Context, global: &Object) -> bool { init_module::(cx, global) } - fn init_globals(self, cx: &Context, global: &mut Object) -> bool { + fn init_globals(self, cx: &Context, global: &Object) -> bool { init_global_module::(cx, global) } } // TODO: Remove JS Wrapper, Stop Global Scope Pollution, Use CreateEmptyModule and AddModuleExport // TODO: Waiting on https://bugzilla.mozilla.org/show_bug.cgi?id=1722802 -pub fn init_module(cx: &Context, global: &mut Object) -> bool { +pub fn init_module(cx: &Context, global: &Object) -> bool { let internal = format!("______{}Internal______", M::NAME); let module = M::module(cx); if let Some(module) = module { - if global.define_as(cx, &internal, &module, PropertyFlags::CONSTANT) { + if global.define_as(cx, internal, &module, PropertyFlags::CONSTANT) { let (module, _) = Module::compile(cx, M::NAME, None, M::SOURCE).unwrap(); let loader = unsafe { &mut (*cx.get_inner_data().as_ptr()).module_loader }; return loader.as_mut().is_some_and(|loader| { @@ -61,7 +61,7 @@ pub fn init_module(cx: &Context, global: &mut Object) -> bool { false } -pub fn init_global_module(cx: &Context, global: &mut Object) -> bool { +pub fn init_global_module(cx: &Context, global: &Object) -> bool { let module = M::module(cx); if let Some(module) = module { diff --git a/runtime/src/runtime.rs b/runtime/src/runtime.rs index d8711185..d96a3588 100644 --- a/runtime/src/runtime.rs +++ b/runtime/src/runtime.rs @@ -52,7 +52,7 @@ impl<'cx> Runtime<'cx> { &self.global } - pub fn global_mut(&mut self) -> &mut Object<'cx> { + pub fn global_mut(&mut self) -> &Object<'cx> { &mut self.global } @@ -103,18 +103,18 @@ impl RuntimeBuilder< } pub fn build(self, cx: &mut Context) -> Runtime { - let mut global = default_new_global(cx); + let global = default_new_global(cx); let realm = JSAutoRealm::new(cx.as_ptr(), global.handle().get()); let global_obj = global.handle().get(); global.set_as(cx, "global", &global_obj); - init_globals(cx, &mut global); + init_globals(cx, &global); let mut private = Box::::default(); if self.microtask_queue { private.event_loop.microtasks = Some(MicrotaskQueue::default()); - init_microtasks(cx, &mut global); + init_microtasks(cx, &global); private.event_loop.futures = Some(FutureQueue::default()); unsafe { @@ -134,7 +134,7 @@ impl RuntimeBuilder< } if self.macrotask_queue { private.event_loop.macrotasks = Some(MacrotaskQueue::default()); - init_timers(cx, &mut global); + init_timers(cx, &global); } let _options = unsafe { &mut *ContextOptionsRef(cx.as_ptr()) }; @@ -148,9 +148,9 @@ impl RuntimeBuilder< if let Some(standard_modules) = self.standard_modules { if has_loader { - standard_modules.init(cx, &mut global); + standard_modules.init(cx, &global); } else { - standard_modules.init_globals(cx, &mut global); + standard_modules.init_globals(cx, &global); } } From 50e97686e41c3ec525cc1f72580b41dd6e494938 Mon Sep 17 00:00:00 2001 From: Redfire Date: Mon, 8 Jan 2024 21:30:40 +0800 Subject: [PATCH 03/12] Added Resolved and Rejected Promise Constructors --- ion/src/objects/promise.rs | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/ion/src/objects/promise.rs b/ion/src/objects/promise.rs index 1a08afa6..96704344 100644 --- a/ion/src/objects/promise.rs +++ b/ion/src/objects/promise.rs @@ -10,8 +10,8 @@ use std::ops::{Deref, DerefMut}; use futures::executor::block_on; use mozjs::glue::JS_GetPromiseResult; use mozjs::jsapi::{ - AddPromiseReactions, GetPromiseID, GetPromiseState, IsPromiseObject, JSObject, NewPromiseObject, PromiseState, - RejectPromise, ResolvePromise, + AddPromiseReactions, CallOriginalPromiseReject, CallOriginalPromiseResolve, GetPromiseID, GetPromiseState, + IsPromiseObject, JSObject, NewPromiseObject, PromiseState, RejectPromise, ResolvePromise, }; use mozjs::rust::HandleObject; @@ -37,7 +37,7 @@ impl<'p> Promise<'p> { /// Creates a new [Promise] with an executor. /// The executor is a function that takes in two functions, `resolve` and `reject`. /// `resolve` and `reject` can be called with a [Value] to resolve or reject the promise with the given value. - pub fn new_with_executor(cx: &'p Context, executor: F) -> Option> + pub fn with_executor(cx: &'p Context, executor: F) -> Option> where F: for<'cx> FnOnce(&'cx Context, Function<'cx>, Function<'cx>) -> crate::Result<()> + 'static, { @@ -81,7 +81,7 @@ impl<'p> Promise<'p> { Output: for<'cx> ToValue<'cx> + 'static, Error: for<'cx> ToValue<'cx> + 'static, { - Promise::new_with_executor(cx, move |cx, resolve, reject| { + Promise::with_executor(cx, move |cx, resolve, reject| { let null = Object::null(cx); block_on(async move { match future.await { @@ -103,6 +103,22 @@ impl<'p> Promise<'p> { }) } + /// Creates a new [Promise], that is resolved to the given value. + /// Similar to `Promise.resolve` + pub fn resolved(cx: &'p Context, value: &Value) -> Promise<'p> { + Promise { + promise: cx.root_object(unsafe { CallOriginalPromiseResolve(cx.as_ptr(), value.handle().into()) }), + } + } + + /// Creates a new [Promise], that is rejected to the given value. + /// Similar to `Promise.reject` + pub fn rejected(cx: &'p Context, value: &Value) -> Promise<'p> { + Promise { + promise: cx.root_object(unsafe { CallOriginalPromiseReject(cx.as_ptr(), value.handle().into()) }), + } + } + /// Creates a [Promise] from an object. pub fn from(object: Local<'p, *mut JSObject>) -> Option> { if Promise::is_promise(&object) { From 765c13dc6c566903d3e38dbe30a02289a50588f8 Mon Sep 17 00:00:00 2001 From: Redfire Date: Mon, 8 Jan 2024 21:53:06 +0800 Subject: [PATCH 04/12] Fixed Optional Arguments in Native Functions Fixed Complex Function Example --- cli/src/evaluate.rs | 2 +- ion-proc/src/class/constructor.rs | 2 +- ion-proc/src/class/impl/mod.rs | 2 +- ion-proc/src/class/struct.rs | 2 +- ion-proc/src/function/mod.rs | 2 +- ion-proc/src/function/parameter.rs | 2 +- ion/examples/macros/js_fn/complex.rs | 6 +++--- ion/examples/macros/js_fn/varargs.rs | 2 +- ion/src/class/mod.rs | 2 +- ion/src/conversions/value/from.rs | 2 +- ion/src/conversions/value/to.rs | 2 +- ion/src/format/regexp.rs | 2 +- ion/src/{functions => function}/arguments.rs | 2 +- ion/src/{functions => function}/closure.rs | 4 ++-- ion/src/{functions => function}/function.rs | 2 +- ion/src/{functions => function}/mod.rs | 0 ion/src/lib.rs | 8 ++++---- ion/src/{objects => object}/array.rs | 2 +- ion/src/{objects => object}/date.rs | 0 ion/src/{objects => object}/descriptor.rs | 0 ion/src/{objects => object}/iterator.rs | 4 ++-- ion/src/{objects => object}/key.rs | 0 ion/src/{objects => object}/map.rs | 0 ion/src/{objects => object}/mod.rs | 0 ion/src/{objects => object}/object.rs | 2 +- ion/src/{objects => object}/promise.rs | 0 ion/src/{objects => object}/regexp.rs | 0 ion/src/{objects => object}/set.rs | 0 .../{objects => object}/typedarray/buffer.rs | 0 ion/src/{objects => object}/typedarray/mod.rs | 0 .../{objects => object}/typedarray/view.rs | 0 ion/tests/conversions/from.rs | 2 +- ion/tests/objects/array.rs | 2 +- ion/tests/objects/date.rs | 2 +- ion/tests/objects/object.rs | 2 +- ion/tests/rooting.rs | 2 +- modules/src/assert/assert.rs | 11 ++++++----- modules/src/fs/fs.rs | 2 +- modules/src/lib.rs | 2 +- modules/src/path/path.rs | 4 ++-- modules/src/url/url.rs | 5 +++-- modules/tests/assert.rs | 2 +- runtime/src/globals/abort.rs | 10 ++++++---- runtime/src/globals/console.rs | 16 ++++++++-------- runtime/src/globals/encoding/decoder.rs | 5 +++-- runtime/src/globals/encoding/encoder.rs | 3 ++- runtime/src/globals/fetch/header.rs | 3 ++- runtime/src/globals/fetch/mod.rs | 3 ++- runtime/src/globals/fetch/request/mod.rs | 3 ++- runtime/src/globals/fetch/response/mod.rs | 3 ++- runtime/src/globals/file/blob.rs | 9 +++++---- runtime/src/globals/file/mod.rs | 5 +++-- runtime/src/globals/file/reader.rs | 5 +++-- runtime/src/globals/timers.rs | 10 +++++----- runtime/src/globals/url/mod.rs | 19 ++++++++++--------- runtime/src/globals/url/search_params.rs | 7 ++++--- runtime/src/lib.rs | 2 +- runtime/src/{modules => module}/loader.rs | 0 runtime/src/{modules => module}/mod.rs | 0 runtime/src/{modules => module}/standard.rs | 0 runtime/src/runtime.rs | 4 ++-- runtime/tests/modules.rs | 2 +- 62 files changed, 106 insertions(+), 91 deletions(-) rename ion/src/{functions => function}/arguments.rs (99%) rename ion/src/{functions => function}/closure.rs (98%) rename ion/src/{functions => function}/function.rs (99%) rename ion/src/{functions => function}/mod.rs (100%) rename ion/src/{objects => object}/array.rs (99%) rename ion/src/{objects => object}/date.rs (100%) rename ion/src/{objects => object}/descriptor.rs (100%) rename ion/src/{objects => object}/iterator.rs (98%) rename ion/src/{objects => object}/key.rs (100%) rename ion/src/{objects => object}/map.rs (100%) rename ion/src/{objects => object}/mod.rs (100%) rename ion/src/{objects => object}/object.rs (99%) rename ion/src/{objects => object}/promise.rs (100%) rename ion/src/{objects => object}/regexp.rs (100%) rename ion/src/{objects => object}/set.rs (100%) rename ion/src/{objects => object}/typedarray/buffer.rs (100%) rename ion/src/{objects => object}/typedarray/mod.rs (100%) rename ion/src/{objects => object}/typedarray/view.rs (100%) rename runtime/src/{modules => module}/loader.rs (100%) rename runtime/src/{modules => module}/mod.rs (100%) rename runtime/src/{modules => module}/standard.rs (100%) diff --git a/cli/src/evaluate.rs b/cli/src/evaluate.rs index 32ba4bcd..43d0f05e 100644 --- a/cli/src/evaluate.rs +++ b/cli/src/evaluate.rs @@ -23,7 +23,7 @@ use runtime::{Runtime, RuntimeBuilder}; use runtime::cache::locate_in_cache; use runtime::cache::map::{save_sourcemap, transform_error_report_with_sourcemaps}; use runtime::config::Config; -use runtime::modules::Loader; +use runtime::module::Loader; pub(crate) async fn eval_inline(rt: &Runtime<'_>, source: &str) { let result = Script::compile_and_evaluate(rt.cx(), Path::new("inline.js"), source); diff --git a/ion-proc/src/class/constructor.rs b/ion-proc/src/class/constructor.rs index a2c82100..186f27bc 100644 --- a/ion-proc/src/class/constructor.rs +++ b/ion-proc/src/class/constructor.rs @@ -38,7 +38,7 @@ pub(super) fn impl_constructor(ion: &TokenStream, mut constructor: ItemFn, ty: & wrapper(cx, args, &mut this) })); - #ion::functions::__handle_native_constructor_result(cx, result, &this, &mut args.rval()) + #ion::function::__handle_native_constructor_result(cx, result, &this, &mut args.rval()) }); constructor.block = body; constructor.sig.ident = format_ident!("__ion_bindings_constructor", span = constructor.sig.ident.span()); diff --git a/ion-proc/src/class/impl/mod.rs b/ion-proc/src/class/impl/mod.rs index c882e7a6..8f42cf17 100644 --- a/ion-proc/src/class/impl/mod.rs +++ b/ion-proc/src/class/impl/mod.rs @@ -189,7 +189,7 @@ fn class_definition( Self::__ion_native_class() } - fn constructor() -> (#ion::functions::NativeFunction, ::core::primitive::u32) { + fn constructor() -> (#ion::function::NativeFunction, ::core::primitive::u32) { (Self::__ion_bindings_constructor, #constructor_nargs) } diff --git a/ion-proc/src/class/struct.rs b/ion-proc/src/class/struct.rs index 967539e2..598956f7 100644 --- a/ion-proc/src/class/struct.rs +++ b/ion-proc/src/class/struct.rs @@ -152,7 +152,7 @@ fn class_impls( const ION_NATIVE_CLASS: #ion::class::NativeClass = #ion::class::NativeClass { base: ::mozjs::jsapi::JSClass { name: #name.as_ptr().cast(), - flags: #ion::objects::class_reserved_slots(1) | ::mozjs::jsapi::JSCLASS_BACKGROUND_FINALIZE, + flags: #ion::object::class_reserved_slots(1) | ::mozjs::jsapi::JSCLASS_BACKGROUND_FINALIZE, cOps: &ION_CLASS_OPERATIONS as *const _, spec: ::std::ptr::null_mut(), ext: ::std::ptr::null_mut(), diff --git a/ion-proc/src/function/mod.rs b/ion-proc/src/function/mod.rs index ddc83712..390798f3 100644 --- a/ion-proc/src/function/mod.rs +++ b/ion-proc/src/function/mod.rs @@ -61,6 +61,6 @@ pub(crate) fn impl_fn_body(ion: &TokenStream, wrapper: &ItemFn) -> Result - let #pat_ty = #ion::functions::FromArgument::from_argument(&mut __accessor, #convert)?; + let #pat_ty = #ion::function::FromArgument::from_argument(&mut __accessor, #convert)?; )) } } diff --git a/ion/examples/macros/js_fn/complex.rs b/ion/examples/macros/js_fn/complex.rs index 53154bfe..7588509a 100644 --- a/ion/examples/macros/js_fn/complex.rs +++ b/ion/examples/macros/js_fn/complex.rs @@ -1,12 +1,12 @@ use ion::{Context, Function, js_fn, Object, Promise, Value}; use ion::conversions::ConversionBehavior; -use ion::functions::{Rest, Strict}; +use ion::function::{Rest, Strict}; #[allow(clippy::too_many_arguments)] #[js_fn] pub fn many_inputs( _cx: &Context, #[ion(this)] _this: &Object, #[ion(convert = ConversionBehavior::EnforceRange)] _integer: i8, - _boolean: Strict, #[ion(convert = ())] _string: String, _function: Function, _promise: Promise, - _values: Rest, + Strict(_boolean): Strict, #[ion(convert = ())] Strict(_string): Strict, _function: Function, + _promise: Promise, Rest(_values): Rest, ) { } diff --git a/ion/examples/macros/js_fn/varargs.rs b/ion/examples/macros/js_fn/varargs.rs index 92fdabb9..548611cc 100644 --- a/ion/examples/macros/js_fn/varargs.rs +++ b/ion/examples/macros/js_fn/varargs.rs @@ -1,6 +1,6 @@ use ion::{js_fn, Object}; use ion::conversions::ConversionBehavior; -use ion::functions::Rest; +use ion::function::Rest; #[js_fn] pub fn varargs(Rest(_strings): Rest) {} diff --git a/ion/src/class/mod.rs b/ion/src/class/mod.rs index 242be511..516f48ee 100644 --- a/ion/src/class/mod.rs +++ b/ion/src/class/mod.rs @@ -19,7 +19,7 @@ use mozjs::jsval::{PrivateValue, UndefinedValue}; use crate::{Context, Function, Local, Object}; pub use crate::class::native::{MAX_PROTO_CHAIN_LENGTH, NativeClass, PrototypeChain, TypeIdWrapper}; pub use crate::class::reflect::{Castable, DerivedFrom, NativeObject, Reflector}; -use crate::functions::NativeFunction; +use crate::function::NativeFunction; mod native; mod reflect; diff --git a/ion/src/conversions/value/from.rs b/ion/src/conversions/value/from.rs index 48fd0033..6c3f2b0b 100644 --- a/ion/src/conversions/value/from.rs +++ b/ion/src/conversions/value/from.rs @@ -19,7 +19,7 @@ use mozjs::typedarray::JSObjectStorage; use crate::{ Array, Context, Date, Error, ErrorKind, Exception, Function, Object, Promise, Result, StringRef, Symbol, Value, }; -use crate::objects::RegExp; +use crate::object::RegExp; use crate::string::byte::{BytePredicate, ByteString}; use crate::typedarray::{ArrayBuffer, TypedArray, TypedArrayElement}; diff --git a/ion/src/conversions/value/to.rs b/ion/src/conversions/value/to.rs index ef319291..b17f750e 100644 --- a/ion/src/conversions/value/to.rs +++ b/ion/src/conversions/value/to.rs @@ -19,7 +19,7 @@ use mozjs::rust::{maybe_wrap_object_or_null_value, maybe_wrap_object_value, mayb use mozjs::typedarray as jsta; use crate::{Array, Context, Date, Function, Object, Promise, PropertyKey, Symbol, Value}; -use crate::objects::RegExp; +use crate::object::RegExp; use crate::string::byte::{BytePredicate, ByteStr, ByteString}; use crate::typedarray::{ArrayBuffer, TypedArray, TypedArrayElement}; diff --git a/ion/src/format/regexp.rs b/ion/src/format/regexp.rs index d7cd9002..e394c1a8 100644 --- a/ion/src/format/regexp.rs +++ b/ion/src/format/regexp.rs @@ -11,7 +11,7 @@ use colored::Colorize; use crate::Context; use crate::format::Config; -use crate::objects::RegExp; +use crate::object::RegExp; /// Formats a [RegExp object](RegExp) as a string using the given [configuration](Config). pub fn format_regexp<'cx>(cx: &'cx Context, cfg: Config, regexp: &'cx RegExp<'cx>) -> RegExpDisplay<'cx> { diff --git a/ion/src/functions/arguments.rs b/ion/src/function/arguments.rs similarity index 99% rename from ion/src/functions/arguments.rs rename to ion/src/function/arguments.rs index 034282b9..31f020ba 100644 --- a/ion/src/functions/arguments.rs +++ b/ion/src/function/arguments.rs @@ -9,7 +9,7 @@ use mozjs::jsval::JSVal; use crate::{Context, Error, ErrorKind, Local, Object, Result, Value}; use crate::conversions::FromValue; -use crate::functions::{Opt, Rest}; +use crate::function::{Opt, Rest}; /// Represents Arguments to a [JavaScript Function](crate::Function). /// Wrapper around [CallArgs] to provide lifetimes and root all arguments. diff --git a/ion/src/functions/closure.rs b/ion/src/function/closure.rs similarity index 98% rename from ion/src/functions/closure.rs rename to ion/src/function/closure.rs index 73d9c172..8ae88672 100644 --- a/ion/src/functions/closure.rs +++ b/ion/src/function/closure.rs @@ -16,8 +16,8 @@ use mozjs::jsval::{JSVal, PrivateValue, UndefinedValue}; use crate::{Arguments, Context, Error, ErrorKind, Object, ResultExc, ThrowException, Value}; use crate::conversions::ToValue; -use crate::functions::__handle_native_function_result; -use crate::objects::class_reserved_slots; +use crate::function::__handle_native_function_result; +use crate::object::class_reserved_slots; const CLOSURE_SLOT: u32 = 0; diff --git a/ion/src/functions/function.rs b/ion/src/function/function.rs similarity index 99% rename from ion/src/functions/function.rs rename to ion/src/function/function.rs index 63fc7647..940bc0f5 100644 --- a/ion/src/functions/function.rs +++ b/ion/src/function/function.rs @@ -18,7 +18,7 @@ use mozjs::jsval::{JSVal, ObjectValue}; use crate::{Context, ErrorReport, Local, Object, Value}; use crate::flags::PropertyFlags; -use crate::functions::closure::{ +use crate::function::closure::{ call_closure, call_closure_once, Closure, ClosureOnce, create_closure_object, create_closure_once_object, }; diff --git a/ion/src/functions/mod.rs b/ion/src/function/mod.rs similarity index 100% rename from ion/src/functions/mod.rs rename to ion/src/function/mod.rs diff --git a/ion/src/lib.rs b/ion/src/lib.rs index 1ad301e4..7da98b0d 100644 --- a/ion/src/lib.rs +++ b/ion/src/lib.rs @@ -15,12 +15,12 @@ pub use class::ClassDefinition; pub use context::{Context, ContextInner}; pub use error::{Error, ErrorKind}; pub use exception::{ErrorReport, Exception, ThrowException}; -pub use functions::{Arguments, Function}; +pub use function::{Arguments, Function}; pub use future::PromiseFuture; #[cfg(feature = "macros")] pub use ion_proc::*; pub use local::Local; -pub use objects::*; +pub use object::*; pub use stack::{Stack, StackRecord}; pub use string::{String, StringRef}; pub use symbol::Symbol; @@ -34,11 +34,11 @@ mod error; pub mod exception; pub mod flags; pub mod format; -pub mod functions; +pub mod function; mod future; pub mod local; pub mod module; -pub mod objects; +pub mod object; pub mod script; pub mod spec; pub mod stack; diff --git a/ion/src/objects/array.rs b/ion/src/object/array.rs similarity index 99% rename from ion/src/objects/array.rs rename to ion/src/object/array.rs index 3bd3e2d7..31437a90 100644 --- a/ion/src/objects/array.rs +++ b/ion/src/object/array.rs @@ -13,7 +13,7 @@ use mozjs::jsval::{JSVal, ObjectValue}; use crate::{Context, Local, Object, Value}; use crate::conversions::{FromValue, ToValue}; use crate::flags::{IteratorFlags, PropertyFlags}; -use crate::objects::object::ObjectKeysIter; +use crate::object::object::ObjectKeysIter; /// Represents an [Array] in the JavaScript Runtime. /// Refer to [MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array) for more details. diff --git a/ion/src/objects/date.rs b/ion/src/object/date.rs similarity index 100% rename from ion/src/objects/date.rs rename to ion/src/object/date.rs diff --git a/ion/src/objects/descriptor.rs b/ion/src/object/descriptor.rs similarity index 100% rename from ion/src/objects/descriptor.rs rename to ion/src/object/descriptor.rs diff --git a/ion/src/objects/iterator.rs b/ion/src/object/iterator.rs similarity index 98% rename from ion/src/objects/iterator.rs rename to ion/src/object/iterator.rs index 7f5ac4af..3de085f1 100644 --- a/ion/src/objects/iterator.rs +++ b/ion/src/object/iterator.rs @@ -19,8 +19,8 @@ use crate::{Arguments, ClassDefinition, Context, Error, ErrorKind, Local, Object use crate::class::{NativeClass, NativeObject, Reflector, TypeIdWrapper}; use crate::conversions::{IntoValue, ToValue}; use crate::flags::PropertyFlags; -use crate::functions::NativeFunction; -use crate::objects::class_reserved_slots; +use crate::function::NativeFunction; +use crate::object::class_reserved_slots; use crate::spec::{create_function_spec, create_function_spec_symbol}; use crate::symbol::WellKnownSymbolCode; diff --git a/ion/src/objects/key.rs b/ion/src/object/key.rs similarity index 100% rename from ion/src/objects/key.rs rename to ion/src/object/key.rs diff --git a/ion/src/objects/map.rs b/ion/src/object/map.rs similarity index 100% rename from ion/src/objects/map.rs rename to ion/src/object/map.rs diff --git a/ion/src/objects/mod.rs b/ion/src/object/mod.rs similarity index 100% rename from ion/src/objects/mod.rs rename to ion/src/object/mod.rs diff --git a/ion/src/objects/object.rs b/ion/src/object/object.rs similarity index 99% rename from ion/src/objects/object.rs rename to ion/src/object/object.rs index 798e050b..0b2d938d 100644 --- a/ion/src/objects/object.rs +++ b/ion/src/object/object.rs @@ -23,7 +23,7 @@ use mozjs::rust::IdVector; use crate::{Context, Exception, Function, Local, OwnedKey, PropertyKey, Value}; use crate::conversions::{FromValue, ToPropertyKey, ToValue}; use crate::flags::{IteratorFlags, PropertyFlags}; -use crate::functions::NativeFunction; +use crate::function::NativeFunction; /// Represents an [Object] in the JS Runtime. /// diff --git a/ion/src/objects/promise.rs b/ion/src/object/promise.rs similarity index 100% rename from ion/src/objects/promise.rs rename to ion/src/object/promise.rs diff --git a/ion/src/objects/regexp.rs b/ion/src/object/regexp.rs similarity index 100% rename from ion/src/objects/regexp.rs rename to ion/src/object/regexp.rs diff --git a/ion/src/objects/set.rs b/ion/src/object/set.rs similarity index 100% rename from ion/src/objects/set.rs rename to ion/src/object/set.rs diff --git a/ion/src/objects/typedarray/buffer.rs b/ion/src/object/typedarray/buffer.rs similarity index 100% rename from ion/src/objects/typedarray/buffer.rs rename to ion/src/object/typedarray/buffer.rs diff --git a/ion/src/objects/typedarray/mod.rs b/ion/src/object/typedarray/mod.rs similarity index 100% rename from ion/src/objects/typedarray/mod.rs rename to ion/src/object/typedarray/mod.rs diff --git a/ion/src/objects/typedarray/view.rs b/ion/src/object/typedarray/view.rs similarity index 100% rename from ion/src/objects/typedarray/view.rs rename to ion/src/object/typedarray/view.rs diff --git a/ion/tests/conversions/from.rs b/ion/tests/conversions/from.rs index fa538fd3..f30d98b7 100644 --- a/ion/tests/conversions/from.rs +++ b/ion/tests/conversions/from.rs @@ -8,7 +8,7 @@ use mozjs::rust::{JSEngine, Runtime}; use ion::{Array, Context, Date, Object, Promise, Value}; use ion::conversions::{FromValue, ToValue}; use ion::conversions::ConversionBehavior; -use ion::objects::default_new_global; +use ion::object::default_new_global; #[test] fn from_value() { diff --git a/ion/tests/objects/array.rs b/ion/tests/objects/array.rs index d2a60b2c..b7662770 100644 --- a/ion/tests/objects/array.rs +++ b/ion/tests/objects/array.rs @@ -4,7 +4,7 @@ use mozjs::rust::{JSEngine, Runtime}; use ion::{Array, Context, Value}; use ion::conversions::FromValue; use ion::flags::PropertyFlags; -use ion::objects::default_new_global; +use ion::object::default_new_global; #[test] fn array() { diff --git a/ion/tests/objects/date.rs b/ion/tests/objects/date.rs index fd5c9480..ea57436f 100644 --- a/ion/tests/objects/date.rs +++ b/ion/tests/objects/date.rs @@ -3,7 +3,7 @@ use mozjs::jsapi::JSAutoRealm; use mozjs::rust::{JSEngine, Runtime}; use ion::{Context, Date}; -use ion::objects::default_new_global; +use ion::object::default_new_global; const EPOCH: i64 = 0; // 01 January 1970 const POST_EPOCH: i64 = 1615766400; // 15 March 2021 diff --git a/ion/tests/objects/object.rs b/ion/tests/objects/object.rs index 1a0085ad..24acf330 100644 --- a/ion/tests/objects/object.rs +++ b/ion/tests/objects/object.rs @@ -4,7 +4,7 @@ use mozjs::rust::{JSEngine, Runtime}; use ion::{Context, Object, OwnedKey, Value}; use ion::conversions::FromValue; use ion::flags::PropertyFlags; -use ion::objects::default_new_global; +use ion::object::default_new_global; #[test] fn object() { diff --git a/ion/tests/rooting.rs b/ion/tests/rooting.rs index 88f1b42d..1cc54a9d 100644 --- a/ion/tests/rooting.rs +++ b/ion/tests/rooting.rs @@ -5,7 +5,7 @@ use mozjs::rust::{JSEngine, Runtime}; use ion::{Arguments, Context, Function, Object, Value}; use ion::conversions::{ConversionBehavior, FromValue}; use ion::flags::PropertyFlags; -use ion::objects::default_new_global; +use ion::object::default_new_global; fn main() { let engine = JSEngine::init().unwrap(); diff --git a/modules/src/assert/assert.rs b/modules/src/assert/assert.rs index 35d5ea8a..20438663 100644 --- a/modules/src/assert/assert.rs +++ b/modules/src/assert/assert.rs @@ -7,7 +7,8 @@ use mozjs::jsapi::JSFunctionSpec; use ion::{Context, Error, Function, Object, Result, Value}; -use runtime::modules::NativeModule; +use ion::function::Opt; +use runtime::module::NativeModule; fn assert_internal(message: Option) -> Result<()> { let error = match message { @@ -18,7 +19,7 @@ fn assert_internal(message: Option) -> Result<()> { } #[js_fn] -fn ok(assertion: Option, message: Option) -> Result<()> { +fn ok(Opt(assertion): Opt, Opt(message): Opt) -> Result<()> { if let Some(true) = assertion { Ok(()) } else { @@ -27,7 +28,7 @@ fn ok(assertion: Option, message: Option) -> Result<()> { } #[js_fn] -fn equals(cx: &Context, actual: Value, expected: Value, message: Option) -> Result<()> { +fn equals(cx: &Context, actual: Value, expected: Value, Opt(message): Opt) -> Result<()> { if actual.is_same(cx, &expected) { Ok(()) } else { @@ -36,7 +37,7 @@ fn equals(cx: &Context, actual: Value, expected: Value, message: Option) } #[js_fn] -fn throws(cx: &Context, func: Function, message: Option) -> Result<()> { +fn throws(cx: &Context, func: Function, Opt(message): Opt) -> Result<()> { if func.call(cx, &Object::global(cx), &[]).is_err() { assert_internal(message) } else { @@ -45,7 +46,7 @@ fn throws(cx: &Context, func: Function, message: Option) -> Result<()> { } #[js_fn] -fn fail(message: Option) -> Result<()> { +fn fail(Opt(message): Opt) -> Result<()> { assert_internal(message) } diff --git a/modules/src/fs/fs.rs b/modules/src/fs/fs.rs index 34a93beb..b8713c2f 100644 --- a/modules/src/fs/fs.rs +++ b/modules/src/fs/fs.rs @@ -15,7 +15,7 @@ use tokio_stream::wrappers::ReadDirStream; use ion::{Context, Error, Object, Promise, Result}; use ion::flags::PropertyFlags; use ion::typedarray::Uint8ArrayWrapper; -use runtime::modules::NativeModule; +use runtime::module::NativeModule; use runtime::promise::future_to_promise; fn check_exists(path: &Path) -> Result<()> { diff --git a/modules/src/lib.rs b/modules/src/lib.rs index f9cb527e..3d0385c0 100644 --- a/modules/src/lib.rs +++ b/modules/src/lib.rs @@ -10,7 +10,7 @@ extern crate ion; use ion::{Context, Object}; -use runtime::modules::{init_global_module, init_module, StandardModules}; +use runtime::module::{init_global_module, init_module, StandardModules}; pub use crate::assert::Assert; pub use crate::fs::FileSystem; diff --git a/modules/src/path/path.rs b/modules/src/path/path.rs index 808c3552..528b2bcf 100644 --- a/modules/src/path/path.rs +++ b/modules/src/path/path.rs @@ -10,9 +10,9 @@ use mozjs::jsapi::{JSFunctionSpec, JSPropertySpec}; use ion::{Context, Error, Object, Result}; use ion::flags::PropertyFlags; -use ion::functions::Rest; +use ion::function::Rest; use ion::spec::create_property_spec_string; -use runtime::modules::NativeModule; +use runtime::module::NativeModule; #[cfg(windows)] const SEPARATOR: &str = "\\\0"; diff --git a/modules/src/url/url.rs b/modules/src/url/url.rs index 86e4473d..fc6b4e79 100644 --- a/modules/src/url/url.rs +++ b/modules/src/url/url.rs @@ -8,11 +8,12 @@ use idna::{domain_to_ascii, domain_to_ascii_strict, domain_to_unicode}; use mozjs::jsapi::JSFunctionSpec; use ion::{ClassDefinition, Context, Object, Result}; +use ion::function::Opt; use runtime::globals::url::{URL, URLSearchParams}; -use runtime::modules::NativeModule; +use runtime::module::NativeModule; #[js_fn] -fn domainToASCII(domain: String, strict: Option) -> Result { +fn domainToASCII(domain: String, Opt(strict): Opt) -> Result { let strict = strict.unwrap_or(false); let domain = if !strict { domain_to_ascii(&domain) diff --git a/modules/tests/assert.rs b/modules/tests/assert.rs index 988fc71b..0736597f 100644 --- a/modules/tests/assert.rs +++ b/modules/tests/assert.rs @@ -15,7 +15,7 @@ use ion::module::Module; use modules::Assert; use runtime::{Runtime, RuntimeBuilder}; use runtime::config::{Config, CONFIG, LogLevel}; -use runtime::modules::Loader; +use runtime::module::Loader; const OK: (&str, &str) = ("ok", include_str!("scripts/assert/ok.js")); const EQUALS: (&str, &str) = ("equals", include_str!("scripts/assert/equals.js")); diff --git a/runtime/src/globals/abort.rs b/runtime/src/globals/abort.rs index 6373286e..2ba42286 100644 --- a/runtime/src/globals/abort.rs +++ b/runtime/src/globals/abort.rs @@ -18,7 +18,9 @@ use tokio::sync::watch::{channel, Receiver, Sender}; use ion::{ClassDefinition, Context, Error, ErrorKind, Exception, Object, Result, ResultExc, Value}; use ion::class::Reflector; -use ion::conversions::{ConversionBehavior, FromValue, ToValue}; +use ion::conversions::{FromValue, ToValue}; +use ion::conversions::ConversionBehavior::EnforceRange; +use ion::function::Opt; use crate::ContextExt; use crate::event_loop::macrotasks::{Macrotask, SignalMacrotask}; @@ -103,7 +105,7 @@ impl AbortController { ) } - pub fn abort<'cx>(&self, cx: &'cx Context, reason: Option>) { + pub fn abort<'cx>(&self, cx: &'cx Context, Opt(reason): Opt>) { let reason = reason.unwrap_or_else(|| Error::new("AbortError", None).as_value(cx)); self.sender.send_replace(Some(reason.get())); } @@ -147,7 +149,7 @@ impl AbortSignal { } } - pub fn abort<'cx>(cx: &'cx Context, reason: Option>) -> *mut JSObject { + pub fn abort<'cx>(cx: &'cx Context, Opt(reason): Opt>) -> *mut JSObject { let reason = reason.unwrap_or_else(|| Error::new("AbortError", None).as_value(cx)); AbortSignal::new_object( cx, @@ -158,7 +160,7 @@ impl AbortSignal { ) } - pub fn timeout(cx: &Context, #[ion(convert = ConversionBehavior::EnforceRange)] time: u64) -> *mut JSObject { + pub fn timeout(cx: &Context, #[ion(convert = EnforceRange)] time: u64) -> *mut JSObject { let (sender, receiver) = channel(None); let terminate = Arc::new(AtomicBool::new(false)); let terminate2 = terminate.clone(); diff --git a/runtime/src/globals/console.rs b/runtime/src/globals/console.rs index 6a79715e..f92670e6 100644 --- a/runtime/src/globals/console.rs +++ b/runtime/src/globals/console.rs @@ -22,7 +22,7 @@ use ion::format::{format_value, INDENT}; use ion::format::Config as FormatConfig; use ion::format::key::format_key; use ion::format::primitive::format_primitive; -use ion::functions::Rest; +use ion::function::{Opt, Rest}; use crate::cache::map::find_sourcemap; use crate::config::{Config, LogLevel}; @@ -105,7 +105,7 @@ fn debug(cx: &Context, Rest(values): Rest) { } #[js_fn] -fn assert(cx: &Context, assertion: Option, Rest(values): Rest) { +fn assert(cx: &Context, Opt(assertion): Opt, Rest(values): Rest) { if Config::global().log_level >= LogLevel::Error { if let Some(assertion) = assertion { if assertion { @@ -188,7 +188,7 @@ fn groupEnd() { } #[js_fn] -fn count(label: Option) { +fn count(Opt(label): Opt) { let label = get_label(label); COUNT_MAP.with_borrow_mut(|counts| match counts.entry(label.clone()) { Entry::Vacant(v) => { @@ -209,7 +209,7 @@ fn count(label: Option) { } #[js_fn] -fn countReset(label: Option) { +fn countReset(Opt(label): Opt) { let label = get_label(label); COUNT_MAP.with_borrow_mut(|counts| match counts.entry(label.clone()) { Entry::Vacant(_) => { @@ -225,7 +225,7 @@ fn countReset(label: Option) { } #[js_fn] -fn time(label: Option) { +fn time(Opt(label): Opt) { let label = get_label(label); TIMER_MAP.with_borrow_mut(|timers| match timers.entry(label.clone()) { Entry::Vacant(v) => { @@ -241,7 +241,7 @@ fn time(label: Option) { } #[js_fn] -fn timeLog(cx: &Context, label: Option, Rest(values): Rest) { +fn timeLog(cx: &Context, Opt(label): Opt, Rest(values): Rest) { let label = get_label(label); TIMER_MAP.with_borrow(|timers| match timers.get(&label) { Some(start) => { @@ -263,7 +263,7 @@ fn timeLog(cx: &Context, label: Option, Rest(values): Rest) { } #[js_fn] -fn timeEnd(label: Option) { +fn timeEnd(Opt(label): Opt) { let label = get_label(label); TIMER_MAP.with_borrow_mut(|timers| match timers.entry(label.clone()) { Entry::Vacant(_) => { @@ -285,7 +285,7 @@ fn timeEnd(label: Option) { } #[js_fn] -fn table(cx: &Context, data: Value, columns: Option>) { +fn table(cx: &Context, data: Value, Opt(columns): Opt>) { fn sort_keys<'cx, I: IntoIterator>>(cx: &'cx Context, unsorted: I) -> IndexSet> { let mut indexes = IndexSet::::new(); let mut headers = IndexSet::::new(); diff --git a/runtime/src/globals/encoding/decoder.rs b/runtime/src/globals/encoding/decoder.rs index 30bf153b..a3a2a3b5 100644 --- a/runtime/src/globals/encoding/decoder.rs +++ b/runtime/src/globals/encoding/decoder.rs @@ -8,6 +8,7 @@ use encoding_rs::{Decoder, DecoderResult, Encoding, UTF_8}; use ion::{Error, ErrorKind, Result}; use ion::class::Reflector; +use ion::function::Opt; use crate::globals::file::BufferSource; @@ -37,7 +38,7 @@ pub struct TextDecoder { #[js_class] impl TextDecoder { #[ion(constructor)] - pub fn constructor(label: Option, options: Option) -> Result { + pub fn constructor(Opt(label): Opt, Opt(options): Opt) -> Result { let encoding; if let Some(label) = label { let enc = Encoding::for_label_no_replacement(label.as_bytes()); @@ -70,7 +71,7 @@ impl TextDecoder { } pub fn decode( - &mut self, #[ion(convert = true)] buffer: BufferSource, options: Option, + &mut self, #[ion(convert = true)] buffer: BufferSource, Opt(options): Opt, ) -> Result { let mut string = String::with_capacity(self.decoder.max_utf8_buffer_length(buffer.len()).unwrap()); let stream = options.unwrap_or_default().stream; diff --git a/runtime/src/globals/encoding/encoder.rs b/runtime/src/globals/encoding/encoder.rs index 447beb27..872c84b9 100644 --- a/runtime/src/globals/encoding/encoder.rs +++ b/runtime/src/globals/encoding/encoder.rs @@ -9,6 +9,7 @@ use encoding_rs::{Encoder, UTF_8}; use ion::{Context, Object, Value}; use ion::class::Reflector; use ion::conversions::ToValue; +use ion::function::Opt; use ion::typedarray::{Uint8Array, Uint8ArrayWrapper}; pub struct EncodeResult { @@ -42,7 +43,7 @@ impl TextEncoder { } } - pub fn encode(&mut self, input: Option) -> Uint8ArrayWrapper { + pub fn encode(&mut self, Opt(input): Opt) -> Uint8ArrayWrapper { let input = input.unwrap_or_default(); let buf_len = self.encoder.max_buffer_length_from_utf8_if_no_unmappables(input.len()).unwrap(); let mut buf = Vec::with_capacity(buf_len); diff --git a/runtime/src/globals/fetch/header.rs b/runtime/src/globals/fetch/header.rs index b12f3d8a..31989471 100644 --- a/runtime/src/globals/fetch/header.rs +++ b/runtime/src/globals/fetch/header.rs @@ -25,6 +25,7 @@ use ion::{Array, Context, Error, ErrorKind, Object, OwnedKey, Result, Value}; use ion::{ClassDefinition, JSIterator}; use ion::class::Reflector; use ion::conversions::{FromValue, ToValue}; +use ion::function::Opt; use ion::string::byte::{ByteString, VisibleAscii}; use ion::symbol::WellKnownSymbolCode; @@ -179,7 +180,7 @@ impl Headers { #[js_class] impl Headers { #[ion(constructor)] - pub fn constructor(init: Option) -> Result { + pub fn constructor(Opt(init): Opt) -> Result { init.unwrap_or_default().into_headers(HeaderMap::default(), HeadersKind::None) } diff --git a/runtime/src/globals/fetch/mod.rs b/runtime/src/globals/fetch/mod.rs index 667745b8..0478cecf 100644 --- a/runtime/src/globals/fetch/mod.rs +++ b/runtime/src/globals/fetch/mod.rs @@ -32,6 +32,7 @@ use ion::{ClassDefinition, Context, Error, ErrorKind, Exception, Local, Object, use ion::class::Reflector; use ion::conversions::ToValue; use ion::flags::PropertyFlags; +use ion::function::Opt; pub use request::{Request, RequestInfo, RequestInit}; pub use response::Response; @@ -55,7 +56,7 @@ mod response; const DEFAULT_USER_AGENT: &str = concatcp!("Spiderfire/", VERSION); #[js_fn] -fn fetch<'cx>(cx: &'cx Context, resource: RequestInfo, init: Option) -> Option> { +fn fetch<'cx>(cx: &'cx Context, resource: RequestInfo, init: Opt) -> Option> { let promise = Promise::new(cx); let request = match Request::constructor(cx, resource, init) { diff --git a/runtime/src/globals/fetch/request/mod.rs b/runtime/src/globals/fetch/request/mod.rs index c9c082a1..6e7950e2 100644 --- a/runtime/src/globals/fetch/request/mod.rs +++ b/runtime/src/globals/fetch/request/mod.rs @@ -12,6 +12,7 @@ use url::Url; use ion::{ClassDefinition, Context, Error, ErrorKind, Result}; use ion::class::Reflector; +use ion::function::Opt; pub use options::*; use crate::globals::abort::AbortSignal; @@ -65,7 +66,7 @@ pub struct Request { #[js_class] impl Request { #[ion(constructor)] - pub fn constructor(cx: &Context, info: RequestInfo, init: Option) -> Result { + pub fn constructor(cx: &Context, info: RequestInfo, Opt(init): Opt) -> Result { let mut fallback_cors = false; let mut request = match info { diff --git a/runtime/src/globals/fetch/response/mod.rs b/runtime/src/globals/fetch/response/mod.rs index 9329a8e5..34b52e48 100644 --- a/runtime/src/globals/fetch/response/mod.rs +++ b/runtime/src/globals/fetch/response/mod.rs @@ -13,6 +13,7 @@ use url::Url; use ion::{ClassDefinition, Context, Error, ErrorKind, Local, Object, Promise, Result}; use ion::class::{NativeObject, Reflector}; +use ion::function::Opt; use ion::typedarray::ArrayBufferWrapper; pub use options::*; @@ -95,7 +96,7 @@ impl Response { #[js_class] impl Response { #[ion(constructor)] - pub fn constructor(cx: &Context, body: Option, init: Option) -> Result { + pub fn constructor(cx: &Context, Opt(body): Opt, Opt(init): Opt) -> Result { let init = init.unwrap_or_default(); let mut response = Response { diff --git a/runtime/src/globals/file/blob.rs b/runtime/src/globals/file/blob.rs index b676ac33..cafa9590 100644 --- a/runtime/src/globals/file/blob.rs +++ b/runtime/src/globals/file/blob.rs @@ -8,13 +8,14 @@ use std::str::FromStr; use bytes::Bytes; use encoding_rs::UTF_8; -use mozjs::conversions::ConversionBehavior; +use mozjs::conversions::ConversionBehavior::Clamp; use mozjs::jsapi::JSObject; use ion::{ClassDefinition, Context, Error, ErrorKind, Object, Promise, Result, Value}; use ion::class::Reflector; use ion::conversions::FromValue; use ion::format::NEWLINE; +use ion::function::Opt; use ion::typedarray::{ArrayBuffer, ArrayBufferView, ArrayBufferWrapper}; use crate::promise::future_to_promise; @@ -151,7 +152,7 @@ impl Blob { #[js_class] impl Blob { #[ion(constructor)] - pub fn constructor(parts: Option>, options: Option) -> Blob { + pub fn constructor(Opt(parts): Opt>, Opt(options): Opt) -> Blob { let options = options.unwrap_or_default(); let mut bytes = Vec::new(); @@ -204,8 +205,8 @@ impl Blob { } pub fn slice( - &self, cx: &Context, #[ion(convert = ConversionBehavior::Clamp)] start: Option, - #[ion(convert = ConversionBehavior::Clamp)] end: Option, kind: Option, + &self, cx: &Context, #[ion(convert = Clamp)] Opt(start): Opt, #[ion(convert = Clamp)] Opt(end): Opt, + Opt(kind): Opt, ) -> *mut JSObject { let size = self.bytes.len() as i64; diff --git a/runtime/src/globals/file/mod.rs b/runtime/src/globals/file/mod.rs index 8f9d5569..f923b18f 100644 --- a/runtime/src/globals/file/mod.rs +++ b/runtime/src/globals/file/mod.rs @@ -9,6 +9,7 @@ use mozjs::conversions::ConversionBehavior; pub use blob::{Blob, BufferSource}; use ion::{ClassDefinition, Context, Object}; +use ion::function::Opt; use crate::globals::file::blob::{BlobOptions, BlobPart}; use crate::globals::file::reader::{FileReader, FileReaderSync}; @@ -35,9 +36,9 @@ pub struct File { #[js_class] impl File { #[ion(constructor)] - pub fn constructor(parts: Vec, name: String, options: Option) -> File { + pub fn constructor(parts: Vec, name: String, Opt(options): Opt) -> File { let options = options.unwrap_or_default(); - let blob = Blob::constructor(Some(parts), Some(options.blob)); + let blob = Blob::constructor(Opt(Some(parts)), Opt(Some(options.blob))); let modified = options .modified .and_then(|d| Utc.timestamp_millis_opt(d).single()) diff --git a/runtime/src/globals/file/reader.rs b/runtime/src/globals/file/reader.rs index 480e8cdb..7db7afe9 100644 --- a/runtime/src/globals/file/reader.rs +++ b/runtime/src/globals/file/reader.rs @@ -18,6 +18,7 @@ use mozjs::rust::IntoHandle; use ion::{ClassDefinition, Context, Error, ErrorKind, Local, Object, Result}; use ion::class::{NativeObject, Reflector}; use ion::conversions::ToValue; +use ion::function::Opt; use ion::string::byte::{ByteString, Latin1}; use ion::typedarray::ArrayBufferWrapper; @@ -132,7 +133,7 @@ impl FileReader { } #[ion(name = "readAsText")] - pub fn read_as_text(&mut self, cx: &Context, blob: &Blob, encoding: Option) -> Result<()> { + pub fn read_as_text(&mut self, cx: &Context, blob: &Blob, Opt(encoding): Opt) -> Result<()> { self.state.validate()?; let bytes = blob.as_bytes().clone(); let mime = blob.kind(); @@ -216,7 +217,7 @@ impl FileReaderSync { } #[ion(name = "readAsText")] - pub fn read_as_text(&mut self, blob: &Blob, encoding: Option) -> String { + pub fn read_as_text(&mut self, blob: &Blob, Opt(encoding): Opt) -> String { let encoding = encoding_from_string_mime(encoding.as_deref(), blob.kind().as_deref()); encoding.decode_without_bom_handling(blob.as_bytes()).0.into_owned() } diff --git a/runtime/src/globals/timers.rs b/runtime/src/globals/timers.rs index 7d576890..04954c00 100644 --- a/runtime/src/globals/timers.rs +++ b/runtime/src/globals/timers.rs @@ -10,7 +10,7 @@ use mozjs::jsapi::JSFunctionSpec; use mozjs::jsval::JSVal; use ion::{Context, Error, Function, Object, Result}; -use ion::functions::Rest; +use ion::function::{Opt, Rest}; use crate::ContextExt; use crate::event_loop::macrotasks::{Macrotask, TimerMacrotask, UserMacrotask}; @@ -53,25 +53,25 @@ fn clear_timer(cx: &Context, id: Option) -> Result<()> { #[js_fn] fn setTimeout( - cx: &Context, callback: Function, #[ion(convert = Clamp)] duration: Option, Rest(arguments): Rest, + cx: &Context, callback: Function, #[ion(convert = Clamp)] Opt(duration): Opt, Rest(arguments): Rest, ) -> Result { set_timer(cx, callback, duration, arguments, false) } #[js_fn] fn setInterval( - cx: &Context, callback: Function, #[ion(convert = Clamp)] duration: Option, Rest(arguments): Rest, + cx: &Context, callback: Function, #[ion(convert = Clamp)] Opt(duration): Opt, Rest(arguments): Rest, ) -> Result { set_timer(cx, callback, duration, arguments, true) } #[js_fn] -fn clearTimeout(cx: &Context, #[ion(convert = EnforceRange)] id: Option) -> Result<()> { +fn clearTimeout(cx: &Context, #[ion(convert = EnforceRange)] Opt(id): Opt) -> Result<()> { clear_timer(cx, id) } #[js_fn] -fn clearInterval(cx: &Context, #[ion(convert = EnforceRange)] id: Option) -> Result<()> { +fn clearInterval(cx: &Context, #[ion(convert = EnforceRange)] Opt(id): Opt) -> Result<()> { clear_timer(cx, id) } diff --git a/runtime/src/globals/url/mod.rs b/runtime/src/globals/url/mod.rs index 36e7951a..49b215a0 100644 --- a/runtime/src/globals/url/mod.rs +++ b/runtime/src/globals/url/mod.rs @@ -12,6 +12,7 @@ use url::Url; use ion::{ClassDefinition, Context, Error, Local, Object, Result}; use ion::class::Reflector; +use ion::function::Opt; pub use search_params::URLSearchParams; mod search_params; @@ -37,7 +38,7 @@ pub struct URL { #[js_class] impl URL { #[ion(constructor)] - pub fn constructor(#[ion(this)] this: &Object, cx: &Context, input: String, base: Option) -> Result { + pub fn constructor(#[ion(this)] this: &Object, cx: &Context, input: String, Opt(base): Opt) -> Result { let base = base.as_ref().and_then(|base| Url::parse(base).ok()); let url = Url::options() .base_url(base.as_ref()) @@ -56,12 +57,12 @@ impl URL { } #[ion(name = "canParse")] - pub fn can_parse(input: String, base: Option) -> bool { + pub fn can_parse(input: String, Opt(base): Opt) -> bool { let base = base.as_ref().and_then(|base| Url::parse(base).ok()); Url::options().base_url(base.as_ref()).parse(&input).is_ok() } - pub fn format(&self, options: Option) -> Result { + pub fn format(&self, Opt(options): Opt) -> Result { let mut url = self.url.clone(); let options = options.unwrap_or_default(); @@ -125,7 +126,7 @@ impl URL { } #[ion(set)] - pub fn set_host(&mut self, host: Option) -> Result<()> { + pub fn set_host(&mut self, Opt(host): Opt) -> Result<()> { if let Some(host) = host { let segments: Vec<&str> = host.split(':').collect(); let (host, port) = match segments.len().cmp(&2) { @@ -156,7 +157,7 @@ impl URL { } #[ion(set)] - pub fn set_hostname(&mut self, hostname: Option) -> Result<()> { + pub fn set_hostname(&mut self, Opt(hostname): Opt) -> Result<()> { self.url .set_host(hostname.as_deref()) .map_err(|error| Error::new(&error.to_string(), None)) @@ -173,7 +174,7 @@ impl URL { } #[ion(set)] - pub fn set_port(&mut self, #[ion(convert = EnforceRange)] port: Option) -> Result<()> { + pub fn set_port(&mut self, #[ion(convert = EnforceRange)] Opt(port): Opt) -> Result<()> { self.url.set_port(port).map_err(|_| Error::new("Invalid Port", None)) } @@ -204,7 +205,7 @@ impl URL { } #[ion(set)] - pub fn set_password(&mut self, password: Option) -> Result<()> { + pub fn set_password(&mut self, Opt(password): Opt) -> Result<()> { self.url.set_password(password.as_deref()).map_err(|_| Error::new("Invalid Url", None)) } @@ -214,7 +215,7 @@ impl URL { } #[ion(set)] - pub fn set_search(&mut self, search: Option) { + pub fn set_search(&mut self, Opt(search): Opt) { self.url.set_query(search.as_deref()); } @@ -224,7 +225,7 @@ impl URL { } #[ion(set)] - pub fn set_hash(&mut self, hash: Option) { + pub fn set_hash(&mut self, Opt(hash): Opt) { self.url.set_fragment(hash.as_deref()); } diff --git a/runtime/src/globals/url/search_params.rs b/runtime/src/globals/url/search_params.rs index c51871a1..f03d02fd 100644 --- a/runtime/src/globals/url/search_params.rs +++ b/runtime/src/globals/url/search_params.rs @@ -9,6 +9,7 @@ use mozjs::jsapi::{Heap, JSObject}; use ion::{ClassDefinition, Context, Error, ErrorKind, JSIterator, Local, Object, OwnedKey, Result, Value}; use ion::class::Reflector; use ion::conversions::{FromValue, ToValue}; +use ion::function::Opt; use ion::symbol::WellKnownSymbolCode; use crate::globals::url::URL; @@ -68,7 +69,7 @@ impl URLSearchParams { #[js_class] impl URLSearchParams { #[ion(constructor)] - pub fn constructor(init: Option) -> URLSearchParams { + pub fn constructor(Opt(init): Opt) -> URLSearchParams { let pairs = init.map(|init| init.0).unwrap_or_default(); URLSearchParams { reflector: Reflector::default(), @@ -99,7 +100,7 @@ impl URLSearchParams { self.update(); } - pub fn delete(&mut self, name: String, value: Option) { + pub fn delete(&mut self, name: String, Opt(value): Opt) { if let Some(value) = value { self.pairs.retain(|(k, v)| k != &name && v != &value) } else { @@ -117,7 +118,7 @@ impl URLSearchParams { self.pairs.iter().filter(|(k, _)| k == &key).map(|(_, v)| v.clone()).collect() } - pub fn has(&self, key: String, value: Option) -> bool { + pub fn has(&self, key: String, Opt(value): Opt) -> bool { if let Some(value) = value { self.pairs.iter().any(|(k, v)| k == &key && v == &value) } else { diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index def5ce7e..9e2fcc5d 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -15,7 +15,7 @@ pub mod cache; pub mod config; pub mod event_loop; pub mod globals; -pub mod modules; +pub mod module; pub mod promise; mod runtime; pub mod typescript; diff --git a/runtime/src/modules/loader.rs b/runtime/src/module/loader.rs similarity index 100% rename from runtime/src/modules/loader.rs rename to runtime/src/module/loader.rs diff --git a/runtime/src/modules/mod.rs b/runtime/src/module/mod.rs similarity index 100% rename from runtime/src/modules/mod.rs rename to runtime/src/module/mod.rs diff --git a/runtime/src/modules/standard.rs b/runtime/src/module/standard.rs similarity index 100% rename from runtime/src/modules/standard.rs rename to runtime/src/module/standard.rs diff --git a/runtime/src/runtime.rs b/runtime/src/runtime.rs index d96a3588..1ca515a2 100644 --- a/runtime/src/runtime.rs +++ b/runtime/src/runtime.rs @@ -11,14 +11,14 @@ use mozjs::jsapi::{ContextOptionsRef, JSAutoRealm, SetJobQueue, SetPromiseReject use ion::{Context, ErrorReport, Object}; use ion::module::{init_module_loader, ModuleLoader}; -use ion::objects::default_new_global; +use ion::object::default_new_global; use crate::event_loop::{EventLoop, promise_rejection_tracker_callback}; use crate::event_loop::future::FutureQueue; use crate::event_loop::macrotasks::MacrotaskQueue; use crate::event_loop::microtasks::{JOB_QUEUE_TRAPS, MicrotaskQueue}; use crate::globals::{init_globals, init_microtasks, init_timers}; -use crate::modules::StandardModules; +use crate::module::StandardModules; #[derive(Default)] pub struct ContextPrivate { diff --git a/runtime/tests/modules.rs b/runtime/tests/modules.rs index 93706bca..02aeccd6 100644 --- a/runtime/tests/modules.rs +++ b/runtime/tests/modules.rs @@ -11,7 +11,7 @@ use mozjs::rust::{JSEngine, Runtime}; use ion::Context; use ion::module::Module; use runtime::config::{Config, CONFIG, LogLevel}; -use runtime::modules::Loader; +use runtime::module::Loader; use runtime::RuntimeBuilder; const FILE_NAME: &str = "module-import.js"; From f1fe6ab5392ee21204017d221c77a5cb5a34e801 Mon Sep 17 00:00:00 2001 From: Redfire Date: Mon, 8 Jan 2024 22:36:13 +0800 Subject: [PATCH 05/12] Added Helper Types for Integer Arguments --- ion-proc/src/value/from.rs | 6 ++-- ion/examples/macros/from_value/structure.rs | 4 +-- ion/examples/macros/js_fn/complex.rs | 9 +++-- ion/examples/macros/js_fn/integer.rs | 8 ++--- ion/examples/macros/js_fn/varargs.rs | 5 ++- ion/src/function/mod.rs | 38 +++++++++++++++++++++ runtime/src/globals/abort.rs | 5 ++- runtime/src/globals/file/blob.rs | 10 +++--- runtime/src/globals/file/mod.rs | 8 ++--- runtime/src/globals/timers.rs | 19 +++++------ runtime/src/globals/url/mod.rs | 7 ++-- 11 files changed, 74 insertions(+), 45 deletions(-) diff --git a/ion-proc/src/value/from.rs b/ion-proc/src/value/from.rs index d8b26560..ea1cadc3 100644 --- a/ion-proc/src/value/from.rs +++ b/ion-proc/src/value/from.rs @@ -218,9 +218,9 @@ fn impl_body( if unit { if let Some(repr) = repr { - if_unit = Some( - quote_spanned!(repr.span() => let discriminant: #repr = #ion::conversions::FromValue::from_value(cx, value, true, #ion::conversions::ConversionBehavior::EnforceRange)?;), - ); + if_unit = Some(quote_spanned!(repr.span() => + let discriminant: #repr = #ion::conversions::FromValue::from_value(cx, value, true, #ion::conversions::ConversionBehavior::EnforceRange)?; + )); } } diff --git a/ion/examples/macros/from_value/structure.rs b/ion/examples/macros/from_value/structure.rs index 48be07de..93c9eca7 100644 --- a/ion/examples/macros/from_value/structure.rs +++ b/ion/examples/macros/from_value/structure.rs @@ -3,14 +3,14 @@ use std::sync::atomic::AtomicU64; use ion::{Context, FromValue, Object, Result, Value}; use ion::conversions::{ConversionBehavior, FromValue}; +use ion::function::{Enforce, Strict}; #[derive(FromValue)] pub struct Complex<'cx> { #[ion(inherit)] pub raw: Object<'cx>, pub truth: bool, - #[ion(convert = ConversionBehavior::EnforceRange, strict)] - pub mode: u32, + pub mode: Strict>, #[ion(default)] pub new: bool, #[ion(default = String::from("string"))] diff --git a/ion/examples/macros/js_fn/complex.rs b/ion/examples/macros/js_fn/complex.rs index 7588509a..0f17d02f 100644 --- a/ion/examples/macros/js_fn/complex.rs +++ b/ion/examples/macros/js_fn/complex.rs @@ -1,12 +1,11 @@ use ion::{Context, Function, js_fn, Object, Promise, Value}; -use ion::conversions::ConversionBehavior; -use ion::function::{Rest, Strict}; +use ion::function::{Enforce, Rest, Strict}; #[allow(clippy::too_many_arguments)] #[js_fn] pub fn many_inputs( - _cx: &Context, #[ion(this)] _this: &Object, #[ion(convert = ConversionBehavior::EnforceRange)] _integer: i8, - Strict(_boolean): Strict, #[ion(convert = ())] Strict(_string): Strict, _function: Function, - _promise: Promise, Rest(_values): Rest, + _cx: &Context, #[ion(this)] _this: &Object, Enforce(_integer): Enforce, Strict(_boolean): Strict, + #[ion(convert = ())] Strict(_string): Strict, _function: Function, _promise: Promise, + Rest(_values): Rest, ) { } diff --git a/ion/examples/macros/js_fn/integer.rs b/ion/examples/macros/js_fn/integer.rs index 34539a5b..1987437a 100644 --- a/ion/examples/macros/js_fn/integer.rs +++ b/ion/examples/macros/js_fn/integer.rs @@ -1,11 +1,11 @@ -use ion::conversions::ConversionBehavior; +use ion::function::Clamp; use ion::js_fn; #[js_fn] -pub fn integer(#[ion(convert = ConversionBehavior::Clamp)] _integer: u8) {} +pub fn integer(Clamp(_integer): Clamp) {} #[js_fn] -pub fn integer_optional(#[ion(convert = ConversionBehavior::Clamp)] _integer: Option) {} +pub fn integer_optional(_integer: Option>) {} #[js_fn] -pub fn integer_vec(#[ion(convert = ConversionBehavior::Clamp)] _integers: Vec) {} +pub fn integer_vec(_integers: Vec>) {} diff --git a/ion/examples/macros/js_fn/varargs.rs b/ion/examples/macros/js_fn/varargs.rs index 548611cc..7e951996 100644 --- a/ion/examples/macros/js_fn/varargs.rs +++ b/ion/examples/macros/js_fn/varargs.rs @@ -1,12 +1,11 @@ use ion::{js_fn, Object}; -use ion::conversions::ConversionBehavior; -use ion::function::Rest; +use ion::function::{Enforce, Rest}; #[js_fn] pub fn varargs(Rest(_strings): Rest) {} #[js_fn] -pub fn varargs_integer(#[ion(convert = ConversionBehavior::EnforceRange)] Rest(_integers): Rest) {} +pub fn varargs_integer(Rest(_integers): Rest>) {} #[js_fn] pub fn varargs_object(Rest(_objects): Rest) {} diff --git a/ion/src/function/mod.rs b/ion/src/function/mod.rs index 2910dfdb..eecb6b09 100644 --- a/ion/src/function/mod.rs +++ b/ion/src/function/mod.rs @@ -8,6 +8,8 @@ use std::any::Any; use std::mem::forget; use std::thread::Result; +use mozjs::conversions::ConversionBehavior; + pub use arguments::{Accessor, Arguments, FromArgument}; pub use closure::{Closure, ClosureOnce}; pub use function::{Function, NativeFunction}; @@ -78,3 +80,39 @@ impl<'cx, T: FromValue<'cx>> FromValue<'cx> for Strict { T::from_value(cx, value, true, config).map(Strict) } } + +/// Helper type for integer arguments that wraps. +#[derive(Clone, Copy, Debug, Default)] +pub struct Wrap(pub T); + +impl<'cx, T: FromValue<'cx, Config = ConversionBehavior>> FromValue<'cx> for Wrap { + type Config = (); + + fn from_value(cx: &'cx Context, value: &Value, strict: bool, _: ()) -> crate::Result> { + T::from_value(cx, value, strict, ConversionBehavior::Default).map(Wrap) + } +} + +/// Helper type for integer arguments that enforces the range. +#[derive(Clone, Copy, Debug, Default)] +pub struct Enforce(pub T); + +impl<'cx, T: FromValue<'cx, Config = ConversionBehavior>> FromValue<'cx> for Enforce { + type Config = (); + + fn from_value(cx: &'cx Context, value: &Value, strict: bool, _: ()) -> crate::Result> { + T::from_value(cx, value, strict, ConversionBehavior::EnforceRange).map(Enforce) + } +} + +/// Helper type for integer arguments that clamps. +#[derive(Clone, Copy, Debug, Default)] +pub struct Clamp(pub T); + +impl<'cx, T: FromValue<'cx, Config = ConversionBehavior>> FromValue<'cx> for Clamp { + type Config = (); + + fn from_value(cx: &'cx Context, value: &Value, strict: bool, _: ()) -> crate::Result> { + T::from_value(cx, value, strict, ConversionBehavior::Clamp).map(Clamp) + } +} diff --git a/runtime/src/globals/abort.rs b/runtime/src/globals/abort.rs index 2ba42286..e4f2cf7b 100644 --- a/runtime/src/globals/abort.rs +++ b/runtime/src/globals/abort.rs @@ -19,8 +19,7 @@ use tokio::sync::watch::{channel, Receiver, Sender}; use ion::{ClassDefinition, Context, Error, ErrorKind, Exception, Object, Result, ResultExc, Value}; use ion::class::Reflector; use ion::conversions::{FromValue, ToValue}; -use ion::conversions::ConversionBehavior::EnforceRange; -use ion::function::Opt; +use ion::function::{Enforce, Opt}; use crate::ContextExt; use crate::event_loop::macrotasks::{Macrotask, SignalMacrotask}; @@ -160,7 +159,7 @@ impl AbortSignal { ) } - pub fn timeout(cx: &Context, #[ion(convert = EnforceRange)] time: u64) -> *mut JSObject { + pub fn timeout(cx: &Context, Enforce(time): Enforce) -> *mut JSObject { let (sender, receiver) = channel(None); let terminate = Arc::new(AtomicBool::new(false)); let terminate2 = terminate.clone(); diff --git a/runtime/src/globals/file/blob.rs b/runtime/src/globals/file/blob.rs index cafa9590..e4f14605 100644 --- a/runtime/src/globals/file/blob.rs +++ b/runtime/src/globals/file/blob.rs @@ -8,14 +8,13 @@ use std::str::FromStr; use bytes::Bytes; use encoding_rs::UTF_8; -use mozjs::conversions::ConversionBehavior::Clamp; use mozjs::jsapi::JSObject; use ion::{ClassDefinition, Context, Error, ErrorKind, Object, Promise, Result, Value}; use ion::class::Reflector; use ion::conversions::FromValue; use ion::format::NEWLINE; -use ion::function::Opt; +use ion::function::{Clamp, Opt}; use ion::typedarray::{ArrayBuffer, ArrayBufferView, ArrayBufferWrapper}; use crate::promise::future_to_promise; @@ -205,18 +204,17 @@ impl Blob { } pub fn slice( - &self, cx: &Context, #[ion(convert = Clamp)] Opt(start): Opt, #[ion(convert = Clamp)] Opt(end): Opt, - Opt(kind): Opt, + &self, cx: &Context, Opt(start): Opt>, Opt(end): Opt>, Opt(kind): Opt, ) -> *mut JSObject { let size = self.bytes.len() as i64; - let mut start = start.unwrap_or(0); + let mut start = start.unwrap_or_default().0; if start < 0 { start = 0.max(size + start); } let start = start.min(size) as usize; - let mut end = end.unwrap_or(size); + let mut end = end.unwrap_or(Clamp(size)).0; if end < 0 { end = 0.max(size + end); } diff --git a/runtime/src/globals/file/mod.rs b/runtime/src/globals/file/mod.rs index f923b18f..b85e0bff 100644 --- a/runtime/src/globals/file/mod.rs +++ b/runtime/src/globals/file/mod.rs @@ -5,11 +5,10 @@ */ use chrono::{DateTime, TimeZone, Utc}; -use mozjs::conversions::ConversionBehavior; pub use blob::{Blob, BufferSource}; use ion::{ClassDefinition, Context, Object}; -use ion::function::Opt; +use ion::function::{Opt, Wrap}; use crate::globals::file::blob::{BlobOptions, BlobPart}; use crate::globals::file::reader::{FileReader, FileReaderSync}; @@ -21,8 +20,7 @@ mod reader; pub struct FileOptions { #[ion(inherit)] blob: BlobOptions, - #[ion(convert = ConversionBehavior::Default)] - modified: Option, + modified: Option>, } #[js_class] @@ -41,7 +39,7 @@ impl File { let blob = Blob::constructor(Opt(Some(parts)), Opt(Some(options.blob))); let modified = options .modified - .and_then(|d| Utc.timestamp_millis_opt(d).single()) + .and_then(|d| Utc.timestamp_millis_opt(d.0).single()) .unwrap_or_else(Utc::now); File { blob, name, modified } diff --git a/runtime/src/globals/timers.rs b/runtime/src/globals/timers.rs index 04954c00..85fa4da5 100644 --- a/runtime/src/globals/timers.rs +++ b/runtime/src/globals/timers.rs @@ -5,12 +5,11 @@ */ use chrono::Duration; -use mozjs::conversions::ConversionBehavior::{Clamp, EnforceRange}; use mozjs::jsapi::JSFunctionSpec; use mozjs::jsval::JSVal; use ion::{Context, Error, Function, Object, Result}; -use ion::function::{Opt, Rest}; +use ion::function::{Clamp, Enforce, Opt, Rest}; use crate::ContextExt; use crate::event_loop::macrotasks::{Macrotask, TimerMacrotask, UserMacrotask}; @@ -19,7 +18,7 @@ const MINIMUM_DELAY: i32 = 1; const MINIMUM_DELAY_NESTED: i32 = 4; fn set_timer( - cx: &Context, callback: Function, duration: Option, arguments: Box<[JSVal]>, repeat: bool, + cx: &Context, callback: Function, duration: Option>, arguments: Box<[JSVal]>, repeat: bool, ) -> Result { let event_loop = unsafe { &mut cx.get_private().event_loop }; if let Some(queue) = &mut event_loop.macrotasks { @@ -29,7 +28,7 @@ fn set_timer( MINIMUM_DELAY }; - let duration = duration.map(|t| t.max(minimum)).unwrap_or(minimum); + let duration = duration.map(|t| t.0.max(minimum)).unwrap_or(minimum); let timer = TimerMacrotask::new(callback, arguments, repeat, Duration::milliseconds(duration as i64)); Ok(queue.enqueue(Macrotask::Timer(timer), None)) } else { @@ -37,11 +36,11 @@ fn set_timer( } } -fn clear_timer(cx: &Context, id: Option) -> Result<()> { +fn clear_timer(cx: &Context, id: Option>) -> Result<()> { if let Some(id) = id { let event_loop = unsafe { &mut cx.get_private().event_loop }; if let Some(queue) = &mut event_loop.macrotasks { - queue.remove(id); + queue.remove(id.0); Ok(()) } else { Err(Error::new("Macrotask Queue has not been initialised.", None)) @@ -53,25 +52,25 @@ fn clear_timer(cx: &Context, id: Option) -> Result<()> { #[js_fn] fn setTimeout( - cx: &Context, callback: Function, #[ion(convert = Clamp)] Opt(duration): Opt, Rest(arguments): Rest, + cx: &Context, callback: Function, Opt(duration): Opt>, Rest(arguments): Rest, ) -> Result { set_timer(cx, callback, duration, arguments, false) } #[js_fn] fn setInterval( - cx: &Context, callback: Function, #[ion(convert = Clamp)] Opt(duration): Opt, Rest(arguments): Rest, + cx: &Context, callback: Function, Opt(duration): Opt>, Rest(arguments): Rest, ) -> Result { set_timer(cx, callback, duration, arguments, true) } #[js_fn] -fn clearTimeout(cx: &Context, #[ion(convert = EnforceRange)] Opt(id): Opt) -> Result<()> { +fn clearTimeout(cx: &Context, Opt(id): Opt>) -> Result<()> { clear_timer(cx, id) } #[js_fn] -fn clearInterval(cx: &Context, #[ion(convert = EnforceRange)] Opt(id): Opt) -> Result<()> { +fn clearInterval(cx: &Context, Opt(id): Opt>) -> Result<()> { clear_timer(cx, id) } diff --git a/runtime/src/globals/url/mod.rs b/runtime/src/globals/url/mod.rs index 49b215a0..f6307ba4 100644 --- a/runtime/src/globals/url/mod.rs +++ b/runtime/src/globals/url/mod.rs @@ -6,13 +6,12 @@ use std::cmp::Ordering; -use mozjs::conversions::ConversionBehavior::EnforceRange; use mozjs::jsapi::{Heap, JSObject}; use url::Url; use ion::{ClassDefinition, Context, Error, Local, Object, Result}; use ion::class::Reflector; -use ion::function::Opt; +use ion::function::{Enforce, Opt}; pub use search_params::URLSearchParams; mod search_params; @@ -174,8 +173,8 @@ impl URL { } #[ion(set)] - pub fn set_port(&mut self, #[ion(convert = EnforceRange)] Opt(port): Opt) -> Result<()> { - self.url.set_port(port).map_err(|_| Error::new("Invalid Port", None)) + pub fn set_port(&mut self, Opt(port): Opt>) -> Result<()> { + self.url.set_port(port.map(|p| p.0)).map_err(|_| Error::new("Invalid Port", None)) } #[ion(get)] From 8ecaf4c26929488dba151759b22d4ddc56ab2dda Mon Sep 17 00:00:00 2001 From: Redfire Date: Mon, 15 Jan 2024 22:10:05 +0800 Subject: [PATCH 06/12] Improved ClassDefinition API Added ArrayVec for Safer Prototype Chain Usage Refactored Spec Function Generation Fixed Empty Check in Argument Parsing --- .github/workflows/release.yml | 2 +- .github/workflows/rust.yml | 2 +- ion-proc/src/class/impl/mod.rs | 23 +---- ion-proc/src/class/impl/spec.rs | 172 ++++++++++++++----------------- ion-proc/src/class/method.rs | 26 ++++- ion-proc/src/class/property.rs | 16 +-- ion-proc/src/class/struct.rs | 18 +--- ion-proc/src/function/wrapper.rs | 4 +- ion-proc/src/trace.rs | 6 +- ion/src/class/mod.rs | 79 +++++++++----- ion/src/class/native.rs | 28 +++-- ion/src/class/reflect.rs | 18 ++-- ion/src/function/arguments.rs | 2 +- ion/src/object/iterator.rs | 30 ++---- ion/src/utils.rs | 66 ++++++++++++ 15 files changed, 279 insertions(+), 213 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 831d68ca..3e8df670 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -91,7 +91,7 @@ jobs: mv ./target/$TARGET/release/cli ./target/$TARGET/release/spiderfire - name: Upload Executables as Artifacts - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: spiderfire-${{ github.sha }}-${{ matrix.id }} path: target/${{ matrix.target }}/release/spiderfire${{ matrix.id == 'windows' && '.exe' || '' }} diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 974c87ee..08841aac 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -67,7 +67,7 @@ jobs: strip ./target/release/spiderfire - name: Upload Executables as Artifacts - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 if: matrix.rust == 'stable' with: name: spiderfire-${{ github.sha }}-${{ matrix.id }} diff --git a/ion-proc/src/class/impl/mod.rs b/ion-proc/src/class/impl/mod.rs index 8f42cf17..993e35d8 100644 --- a/ion-proc/src/class/impl/mod.rs +++ b/ion-proc/src/class/impl/mod.rs @@ -170,21 +170,19 @@ fn parse_class_method( fn class_definition( ion: &TokenStream, span: Span, r#type: &Type, ident: &Ident, constructor: Method, specs: PrototypeSpecs, ) -> Result<[ItemImpl; 2]> { - let spec_functions = specs.to_spec_functions(ion, span, ident)?.into_array(); + let (spec_fns, def_fns) = specs.to_impl_fns(ion, span, ident)?; let constructor_function = constructor.method; let functions = specs.into_functions().into_iter().map(|method| method.method); let mut spec_impls: ItemImpl = parse2(quote_spanned!(span => impl #r#type { #constructor_function #(#functions)* - #(#spec_functions)* + #(#spec_fns)* }))?; spec_impls.attrs.push(parse_quote!(#[doc(hidden)])); let constructor_nargs = constructor.nargs as u32; let class_definition = parse2(quote_spanned!(span => impl #ion::ClassDefinition for #r#type { - const NAME: &'static str = ::std::stringify!(#ident); - fn class() -> &'static #ion::class::NativeClass { Self::__ion_native_class() } @@ -193,21 +191,8 @@ fn class_definition( (Self::__ion_bindings_constructor, #constructor_nargs) } - fn functions() -> &'static [::mozjs::jsapi::JSFunctionSpec] { - Self::__ion_function_specs() - } - - fn properties() -> &'static [::mozjs::jsapi::JSPropertySpec] { - Self::__ion_property_specs() - } - - fn static_functions() -> &'static [::mozjs::jsapi::JSFunctionSpec] { - Self::__ion_static_function_specs() - } - - fn static_properties() -> &'static [::mozjs::jsapi::JSPropertySpec] { - Self::__ion_static_property_specs() - } + #(#def_fns)* }))?; + Ok([spec_impls, class_definition]) } diff --git a/ion-proc/src/class/impl/spec.rs b/ion-proc/src/class/impl/spec.rs index 6cb08900..2f2f0710 100644 --- a/ion-proc/src/class/impl/spec.rs +++ b/ion-proc/src/class/impl/spec.rs @@ -6,11 +6,9 @@ use std::collections::HashMap; -use convert_case::{Case, Casing}; use proc_macro2::{Ident, Span, TokenStream}; use syn::{ImplItemFn, parse2, Result, Type}; -use crate::attribute::name::Name; use crate::class::accessor::{Accessor, flatten_accessors}; use crate::class::method::Method; use crate::class::property::Property; @@ -23,23 +21,45 @@ pub(super) struct PrototypeSpecs { } impl PrototypeSpecs { - pub(super) fn to_spec_functions(&self, ion: &TokenStream, span: Span, ident: &Ident) -> Result { - Ok(SpecFunctions { - methods: ( - methods_to_spec_function(ion, span, ident, &self.methods.0, false)?, - methods_to_spec_function(ion, span, ident, &self.methods.1, true)?, - ), - properties: ( - properties_to_spec_function(ion, span, ident, &self.properties.0, &self.accessors.0, false)?, - properties_to_spec_function(ion, span, ident, &self.properties.1, &self.accessors.1, true)?, - ), - }) + pub(super) fn to_impl_fns( + &self, ion: &TokenStream, span: Span, ident: &Ident, + ) -> Result<(Vec, Vec)> { + let mut impl_fns = Vec::with_capacity(4); + + if !self.methods.0.is_empty() { + impl_fns.push(methods_to_impl_fn(ion, span, ident, &self.methods.0, false)?); + } + if !self.methods.1.is_empty() { + impl_fns.push(methods_to_impl_fn(ion, span, ident, &self.methods.1, true)?); + } + + if !self.properties.0.is_empty() || !self.accessors.0.is_empty() { + impl_fns.push(properties_to_spec_function( + ion, + span, + ident, + &self.properties.0, + &self.accessors.0, + false, + )?); + } + if !self.properties.1.is_empty() || !self.accessors.1.is_empty() { + impl_fns.push(properties_to_spec_function( + ion, + span, + ident, + &self.properties.1, + &self.accessors.1, + true, + )?); + } + + Ok(impl_fns.into_iter().unzip()) } pub(super) fn into_functions(self) -> Vec { - let mut functions = Vec::with_capacity( - self.methods.0.len() + self.methods.1.len() + self.accessors.0.len() + self.accessors.1.len(), - ); + let len = self.methods.0.len() + self.methods.1.len() + self.accessors.0.len() + self.accessors.1.len(); + let mut functions = Vec::with_capacity(len); functions.extend(self.methods.0); functions.extend(self.methods.1); @@ -50,99 +70,59 @@ impl PrototypeSpecs { } } -pub(super) struct SpecFunctions { - methods: (ImplItemFn, ImplItemFn), - properties: (ImplItemFn, ImplItemFn), -} - -impl SpecFunctions { - pub(super) fn into_array(self) -> [ImplItemFn; 4] { - [self.methods.0, self.properties.0, self.methods.1, self.properties.1] +fn methods_to_impl_fn( + ion: &TokenStream, span: Span, class: &Ident, methods: &[Method], r#static: bool, +) -> Result<(ImplItemFn, ImplItemFn)> { + let mut ident = parse_quote!(functions); + if r#static { + ident = format_ident!("static_{}", ident); } -} + let function_ident = format_ident!("__ion_{}_specs", ident); -fn methods_to_spec_function( - ion: &TokenStream, span: Span, class: &Ident, methods: &[Method], r#static: bool, -) -> Result { - let ident = if r#static { - parse_quote!(ION_STATIC_FUNCTIONS) - } else { - parse_quote!(ION_FUNCTIONS) - }; - let function_ident = if r#static { - parse_quote!(__ion_static_function_specs) - } else { - parse_quote!(__ion_function_specs) - }; - - let mut specs: Vec<_> = methods - .iter() - .flat_map(|method| { - let ident = method.method.sig.ident.clone(); - let nargs = method.nargs as u16; - (*method.names) - .iter() - .map(|name| match name { - Name::String(literal) => { - let mut name = literal.value(); - if name.is_case(Case::Snake) { - name = name.to_case(Case::Camel) - } - quote!(#ion::function_spec!(#class::#ident, #name, #nargs, #ion::flags::PropertyFlags::CONSTANT_ENUMERATED)) - } - Name::Symbol(symbol) => { - quote!(#ion::function_spec_symbol!(#class::#ident, #symbol, #nargs, #ion::flags::PropertyFlags::CONSTANT)) - } - }) - .collect::>() - }) - .collect(); - specs.push(parse_quote!(::mozjs::jsapi::JSFunctionSpec::ZERO)); - - spec_function( - span, - &ident, - &function_ident, - &specs, - parse_quote!(::mozjs::jsapi::JSFunctionSpec), - ) + let specs: Vec<_> = methods.iter().flat_map(|method| method.to_specs(ion, class)).collect(); + + let ty = parse_quote!(::mozjs::jsapi::JSFunctionSpec); + Ok(( + spec_function(span, &function_ident, &specs, &ty)?, + def_function(span, &ident, &function_ident, &ty)?, + )) } fn properties_to_spec_function( ion: &TokenStream, span: Span, class: &Ident, properties: &[Property], accessors: &HashMap, r#static: bool, -) -> Result { - let ident: Ident = if r#static { - parse_quote!(ION_STATIC_PROPERTIES) - } else { - parse_quote!(ION_PROPERTIES) - }; - let function_ident: Ident = if r#static { - parse_quote!(__ion_static_property_specs) - } else { - parse_quote!(__ion_property_specs) - }; +) -> Result<(ImplItemFn, ImplItemFn)> { + let mut ident = parse_quote!(properties); + if r#static { + ident = format_ident!("static_{}", ident); + } + let function_ident = format_ident!("__ion_{}_specs", ident); let mut specs: Vec<_> = properties.iter().flat_map(|property| property.to_specs(ion, class)).collect(); accessors.values().for_each(|accessor| specs.extend(accessor.to_specs(ion, class))); - specs.push(parse_quote!(::mozjs::jsapi::JSPropertySpec::ZERO)); - - spec_function( - span, - &ident, - &function_ident, - &specs, - parse_quote!(::mozjs::jsapi::JSPropertySpec), - ) + + let ty = parse_quote!(::mozjs::jsapi::JSPropertySpec); + Ok(( + spec_function(span, &function_ident, &specs, &ty)?, + def_function(span, &ident, &function_ident, &ty)?, + )) } -fn spec_function( - span: Span, ident: &Ident, function_ident: &Ident, specs: &[TokenStream], ty: Type, -) -> Result { +fn spec_function(span: Span, function_ident: &Ident, specs: &[TokenStream], ty: &Type) -> Result { + assert!(!specs.is_empty()); parse2(quote_spanned!(span => fn #function_ident() -> &'static [#ty] { - static #ident: &[#ty] = &[ - #(#specs),* + static SPECS: &[#ty] = &[ + #(#specs,)* + #ty::ZERO, ]; - #ident + SPECS })) } + +fn def_function(span: Span, ident: &Ident, function_ident: &Ident, ty: &Type) -> Result { + parse2( + quote_spanned!(span => fn #ident() -> ::std::option::Option<&'static [#ty]> { + ::std::option::Option::Some(Self::#function_ident()) + }), + ) +} diff --git a/ion-proc/src/class/method.rs b/ion-proc/src/class/method.rs index d2466087..ce8c00bd 100644 --- a/ion-proc/src/class/method.rs +++ b/ion-proc/src/class/method.rs @@ -4,7 +4,8 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -use proc_macro2::TokenStream; +use convert_case::{Case, Casing}; +use proc_macro2::{Ident, TokenStream}; use syn::{ItemFn, Result, Signature, Type}; use crate::attribute::name::Name; @@ -33,6 +34,29 @@ pub(super) struct Method { pub(super) names: Vec, } +impl Method { + pub(super) fn to_specs(&self, ion: &TokenStream, class: &Ident) -> Vec { + let ident = &self.method.sig.ident; + let nargs = self.nargs as u16; + + self.names + .iter() + .map(|name| match name { + Name::String(literal) => { + let mut name = literal.value(); + if name.is_case(Case::Snake) { + name = name.to_case(Case::Camel) + } + quote!(#ion::function_spec!(#class::#ident, #name, #nargs, #ion::flags::PropertyFlags::CONSTANT_ENUMERATED)) + } + Name::Symbol(symbol) => { + quote!(#ion::function_spec_symbol!(#class::#ident, #symbol, #nargs, #ion::flags::PropertyFlags::CONSTANT)) + } + }) + .collect() + } +} + pub(super) fn impl_method( ion: &TokenStream, mut method: ItemFn, ty: &Type, predicate: F, ) -> Result<(Method, Parameters)> diff --git a/ion-proc/src/class/property.rs b/ion-proc/src/class/property.rs index 8c06320c..ebc89c0b 100644 --- a/ion-proc/src/class/property.rs +++ b/ion-proc/src/class/property.rs @@ -81,17 +81,11 @@ impl Property { let (key, flags) = name.to_property_spec(ion, &mut function_ident); - match self.ty { - PropertyType::Int32 => { - function_ident = format_ident!("{}_int", function_ident); - } - PropertyType::Double => { - function_ident = format_ident!("{}_double", function_ident); - } - PropertyType::String => { - function_ident = format_ident!("{}_string", function_ident); - } - } + function_ident = match self.ty { + PropertyType::Int32 => format_ident!("{}_int", function_ident), + PropertyType::Double => format_ident!("{}_double", function_ident), + PropertyType::String => format_ident!("{}_string", function_ident), + }; quote!(#ion::spec::#function_ident(#key, #class::#ident, #flags)) }) diff --git a/ion-proc/src/class/struct.rs b/ion-proc/src/class/struct.rs index 598956f7..7c28a6bc 100644 --- a/ion-proc/src/class/struct.rs +++ b/ion-proc/src/class/struct.rs @@ -4,8 +4,6 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -use std::ffi::CString; - use proc_macro2::{Ident, Span, TokenStream}; use syn::{Error, Fields, ImplItemFn, ItemImpl, ItemStruct, LitStr, Member, parse2, Path, Result, Type}; use syn::punctuated::Punctuated; @@ -113,26 +111,14 @@ fn class_impls( let none = quote!(::std::option::Option::None); let operations = class_operations(span)?; - let name = String::from_utf8(CString::new(name).unwrap().into_bytes_with_nul()).unwrap(); + let name = format!("{}\0", name); let mut operations_native_class: ItemImpl = parse2(quote_spanned!(span => impl #r#type { #(#operations)* pub const fn __ion_native_prototype_chain() -> #ion::class::PrototypeChain { const ION_TYPE_ID: #ion::class::TypeIdWrapper<#r#type> = #ion::class::TypeIdWrapper::new(); - - let mut proto_chain = #super_type::__ion_native_prototype_chain(); - let mut i = 0; - while i < #ion::class::MAX_PROTO_CHAIN_LENGTH { - match proto_chain[i] { - Some(_) => i += 1, - None => { - proto_chain[i] = Some(&ION_TYPE_ID); - break; - } - } - } - proto_chain + #super_type::__ion_native_prototype_chain().push(&ION_TYPE_ID) } pub const fn __ion_native_class() -> &'static #ion::class::NativeClass { diff --git a/ion-proc/src/function/wrapper.rs b/ion-proc/src/function/wrapper.rs index c5b6e75c..aeeec611 100644 --- a/ion-proc/src/function/wrapper.rs +++ b/ion-proc/src/function/wrapper.rs @@ -81,9 +81,9 @@ pub(crate) fn impl_wrapper_fn( let this_ident = parameters.get_this_ident(); let inner_call = if this_ident.is_some() && this_ident.unwrap() == "self" { - quote!(#call(self_, #(#idents),*)) + quote!(#call(self_, #(#idents,)*)) } else { - quote!(#call(#(#idents),*)) + quote!(#call(#(#idents,)*)) }; let body = parse2(quote_spanned!(function.span() => { diff --git a/ion-proc/src/trace.rs b/ion-proc/src/trace.rs index e3c87af0..89978f1f 100644 --- a/ion-proc/src/trace.rs +++ b/ion-proc/src/trace.rs @@ -40,7 +40,7 @@ fn impl_body(span: Span, data: &Data) -> Result> { .enumerate() .filter_map(|(index, ident)| (!skip.contains(&index)).then_some(ident)); parse2(quote_spanned!(span => { - let Self { #(#idents),* } = self; + let Self { #(#idents,)* } = self; #(::mozjs::gc::Traceable::trace(#traced, __ion_tracer));* })) } @@ -56,10 +56,10 @@ fn impl_body(span: Span, data: &Data) -> Result> { .enumerate() .filter_map(|(index, ident)| (!skip.contains(&index)).then_some(ident)); match &variant.fields { - Fields::Named(_) => parse2(quote_spanned!(variant.span() => Self::#ident { #(#idents),* } => { + Fields::Named(_) => parse2(quote_spanned!(variant.span() => Self::#ident { #(#idents,)* } => { #(::mozjs::gc::Traceable::trace(#traced, __ion_tracer));* })), - Fields::Unnamed(_) => parse2(quote_spanned!(variant.span() => Self::#ident(#(#idents),* ) => { + Fields::Unnamed(_) => parse2(quote_spanned!(variant.span() => Self::#ident(#(#idents,)* ) => { #(::mozjs::gc::Traceable::trace(#traced, __ion_tracer));* })), Fields::Unit => parse2(quote_spanned!(variant.span() => Self::#ident => {})), diff --git a/ion/src/class/mod.rs b/ion/src/class/mod.rs index 516f48ee..fb02638c 100644 --- a/ion/src/class/mod.rs +++ b/ion/src/class/mod.rs @@ -6,7 +6,6 @@ use std::any::TypeId; use std::collections::hash_map::Entry; -use std::ffi::CString; use std::ptr; use mozjs::glue::JS_GetReservedSlot; @@ -15,6 +14,7 @@ use mozjs::jsapi::{ JSFunctionSpec, JSObject, JSPropertySpec, }; use mozjs::jsval::{PrivateValue, UndefinedValue}; +use mozjs::rust::HandleObject; use crate::{Context, Function, Local, Object}; pub use crate::class::native::{MAX_PROTO_CHAIN_LENGTH, NativeClass, PrototypeChain, TypeIdWrapper}; @@ -34,63 +34,68 @@ pub struct ClassInfo { } pub trait ClassDefinition: NativeObject { - const NAME: &'static str; - fn class() -> &'static NativeClass; - fn parent_class_info(_: &Context) -> Option<(&'static NativeClass, Local<*mut JSObject>)> { + fn proto_class() -> Option<&'static NativeClass> { + None + } + + fn parent_prototype(_: &Context) -> Option> { None } fn constructor() -> (NativeFunction, u32); - fn functions() -> &'static [JSFunctionSpec] { - &[JSFunctionSpec::ZERO] + fn functions() -> Option<&'static [JSFunctionSpec]> { + None } - fn properties() -> &'static [JSPropertySpec] { - &[JSPropertySpec::ZERO] + fn properties() -> Option<&'static [JSPropertySpec]> { + None } - fn static_functions() -> &'static [JSFunctionSpec] { - &[JSFunctionSpec::ZERO] + fn static_functions() -> Option<&'static [JSFunctionSpec]> { + None } - fn static_properties() -> &'static [JSPropertySpec] { - &[JSPropertySpec::ZERO] + fn static_properties() -> Option<&'static [JSPropertySpec]> { + None } fn init_class<'cx>(cx: &'cx Context, object: &Object) -> (bool, &'cx ClassInfo) { let infos = unsafe { &mut (*cx.get_inner_data().as_ptr()).class_infos }; - let entry = infos.entry(TypeId::of::()); - match entry { + match infos.entry(TypeId::of::()) { Entry::Occupied(o) => (false, o.into_mut()), Entry::Vacant(entry) => { - let (parent_class, parent_proto) = Self::parent_class_info(cx) - .map(|(class, proto)| (&class.base as *const _, Object::from(proto))) - .unwrap_or_else(|| (ptr::null(), Object::new(cx))); + let proto_class = Self::proto_class().map_or_else(ptr::null, |class| &class.base as *const _); + let parent_proto = Self::parent_prototype(cx).map_or_else(HandleObject::null, |proto| proto.handle()); + let (constructor, nargs) = Self::constructor(); + let properties = Self::properties(); let functions = Self::functions(); let static_properties = Self::static_properties(); let static_functions = Self::static_functions(); - let name = CString::new(Self::NAME).unwrap(); + assert!(has_zero_spec(properties)); + assert!(has_zero_spec(functions)); + assert!(has_zero_spec(static_properties)); + assert!(has_zero_spec(static_functions)); let class = unsafe { JS_InitClass( cx.as_ptr(), object.handle().into(), - parent_class, - parent_proto.handle().into(), - name.as_ptr().cast(), + proto_class, + parent_proto.into(), + Self::class().base.name, Some(constructor), nargs, - properties.as_ptr(), - functions.as_ptr(), - static_properties.as_ptr(), - static_functions.as_ptr(), + unwrap_specs(properties), + unwrap_specs(functions), + unwrap_specs(static_properties), + unwrap_specs(static_functions), ) }; let prototype = cx.root_object(class); @@ -165,3 +170,27 @@ pub trait ClassDefinition: NativeObject { } } } + +trait SpecZero { + fn is_zeroed(&self) -> bool; +} + +impl SpecZero for JSFunctionSpec { + fn is_zeroed(&self) -> bool { + self.is_zeroed() + } +} + +impl SpecZero for JSPropertySpec { + fn is_zeroed(&self) -> bool { + self.is_zeroed() + } +} + +fn has_zero_spec(specs: Option<&[T]>) -> bool { + specs.and_then(|s| s.last()).map_or(true, |specs| specs.is_zeroed()) +} + +fn unwrap_specs(specs: Option<&[T]>) -> *const T { + specs.map_or_else(ptr::null, |specs| specs.as_ptr()) +} diff --git a/ion/src/class/native.rs b/ion/src/class/native.rs index ed9ed912..1e734f36 100644 --- a/ion/src/class/native.rs +++ b/ion/src/class/native.rs @@ -6,11 +6,13 @@ use std::any::TypeId; use std::fmt; -use std::fmt::{Debug, Formatter}; +use std::fmt::{Debug, Formatter, Write}; use std::marker::PhantomData; use mozjs::jsapi::JSClass; +use crate::utils::ArrayVec; + pub const MAX_PROTO_CHAIN_LENGTH: usize = 8; pub trait TypeIdWrap: private::Sealed + 'static { @@ -43,7 +45,7 @@ unsafe impl Send for TypeIdWrapper {} unsafe impl Sync for TypeIdWrapper {} -pub type PrototypeChain = [Option<&'static (dyn TypeIdWrap + Send + Sync)>; MAX_PROTO_CHAIN_LENGTH]; +pub type PrototypeChain = ArrayVec; #[repr(C)] pub struct NativeClass { @@ -53,15 +55,23 @@ pub struct NativeClass { impl Debug for NativeClass { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + struct ChainDebug { + len: usize, + } + + impl Debug for ChainDebug { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + f.write_str("[TypeIdWrapper; ")?; + self.len.fmt(f)?; + f.write_char('/')?; + MAX_PROTO_CHAIN_LENGTH.fmt(f)?; + f.write_char(']') + } + } + f.debug_struct("NativeClass") .field("base", &self.base) - .field( - "prototype_chain", - &format!( - "[TypeIdWrapper; {}]", - self.prototype_chain.iter().filter(|proto| proto.is_some()).count() - ), - ) + .field("prototype_chain", &ChainDebug { len: self.prototype_chain.len() }) .finish() } } diff --git a/ion/src/class/reflect.rs b/ion/src/class/reflect.rs index eb7a8fd9..e0a36dc5 100644 --- a/ion/src/class/reflect.rs +++ b/ion/src/class/reflect.rs @@ -4,7 +4,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -use std::any::TypeId; +use std::any::{Any, TypeId}; use mozjs::gc::Traceable; use mozjs::jsapi::{Heap, JSObject, JSTracer}; @@ -26,13 +26,15 @@ pub trait Castable: NativeObject { T: NativeObject, { let class = unsafe { get_object_class(self.reflector().get()) }; - let native_class = class.cast::(); - let mut proto_chain = unsafe { (*native_class).prototype_chain.iter() }; - let mut is = false; - while let Some(Some(proto)) = proto_chain.next() { - is |= proto.type_id() == TypeId::of::() + if class.is_null() { + return false; + } + unsafe { + (*class.cast::()) + .prototype_chain + .iter() + .any(|proto| proto.type_id() == TypeId::of::()) } - is } fn upcast(&self) -> &T @@ -74,7 +76,7 @@ impl Reflector { #[doc(hidden)] pub const fn __ion_native_prototype_chain() -> PrototypeChain { - [None; 8] + PrototypeChain::new() } } diff --git a/ion/src/function/arguments.rs b/ion/src/function/arguments.rs index 31f020ba..fb02f5b8 100644 --- a/ion/src/function/arguments.rs +++ b/ion/src/function/arguments.rs @@ -128,7 +128,7 @@ impl<'cx> Accessor<'_, 'cx> { /// Returns `true` if there are no arguments remaining. pub fn is_empty(&self) -> bool { - self.index == 0 + self.index == self.args.len() } /// Returns the context associated with the arguments. diff --git a/ion/src/object/iterator.rs b/ion/src/object/iterator.rs index 3de085f1..e7560649 100644 --- a/ion/src/object/iterator.rs +++ b/ion/src/object/iterator.rs @@ -16,7 +16,7 @@ use mozjs::jsapi::{ use mozjs::jsval::{JSVal, NullValue}; use crate::{Arguments, ClassDefinition, Context, Error, ErrorKind, Local, Object, ThrowException, Value}; -use crate::class::{NativeClass, NativeObject, Reflector, TypeIdWrapper}; +use crate::class::{NativeClass, NativeObject, PrototypeChain, Reflector, TypeIdWrapper}; use crate::conversions::{IntoValue, ToValue}; use crate::flags::PropertyFlags; use crate::function::NativeFunction; @@ -171,16 +171,7 @@ static ITERATOR_CLASS: NativeClass = NativeClass { ext: ptr::null_mut(), oOps: ptr::null_mut(), }, - prototype_chain: [ - Some(&TypeIdWrapper::::new()), - None, - None, - None, - None, - None, - None, - None, - ], + prototype_chain: PrototypeChain::new().push(&TypeIdWrapper::::new()), }; static ITERATOR_METHODS: &[JSFunctionSpec] = &[ @@ -212,24 +203,23 @@ impl NativeObject for Iterator { } impl ClassDefinition for Iterator { - const NAME: &'static str = ""; - fn class() -> &'static NativeClass { &ITERATOR_CLASS } - fn parent_class_info(cx: &Context) -> Option<(&'static NativeClass, Local<*mut JSObject>)> { - Some(( - &ITERATOR_CLASS, - cx.root_object(unsafe { GetRealmIteratorPrototype(cx.as_ptr()) }), - )) + fn proto_class() -> Option<&'static NativeClass> { + None + } + + fn parent_prototype(cx: &Context) -> Option> { + Some(cx.root_object(unsafe { GetRealmIteratorPrototype(cx.as_ptr()) })) } fn constructor() -> (NativeFunction, u32) { (Iterator::constructor, 0) } - fn functions() -> &'static [JSFunctionSpec] { - ITERATOR_METHODS + fn functions() -> Option<&'static [JSFunctionSpec]> { + Some(ITERATOR_METHODS) } } diff --git a/ion/src/utils.rs b/ion/src/utils.rs index f2505184..a2ede03a 100644 --- a/ion/src/utils.rs +++ b/ion/src/utils.rs @@ -4,8 +4,10 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +use std::mem::MaybeUninit; use std::path::{Component, Path, PathBuf}; use std::slice; +use std::slice::Iter; /// Normalises a [Path] by removing all `./` and resolving all `../` simplistically. /// This function does not follow symlinks and may result in unexpected behaviour. @@ -49,3 +51,67 @@ impl BoxExt for Box<[T]> { } } } + +#[derive(Clone, Copy, Debug)] +pub struct ArrayVec { + elements: [MaybeUninit; CAP], + len: usize, +} + +impl ArrayVec { + pub const fn new() -> ArrayVec { + ArrayVec { + elements: unsafe { MaybeUninit::uninit().assume_init() }, + len: 0, + } + } + + pub const fn len(&self) -> usize { + self.len + } + + pub const fn is_empty(&self) -> bool { + self.len() == 0 + } + + pub const fn is_full(&self) -> bool { + self.len() == CAP + } + + pub const fn push(mut self, element: T) -> ArrayVec { + if self.len == CAP { + panic!("Exceeded capacity of ArrayVec."); + } + self.elements[self.len] = MaybeUninit::new(element); + self.len += 1; + self + } + + pub const fn pop(mut self) -> (ArrayVec, Option) { + if self.len == 0 { + return (self, None); + } + let element = unsafe { self.elements[self.len].assume_init() }; + self.len -= 1; + (self, Some(element)) + } + + pub const fn get(&self, index: usize) -> Option<&T> { + if self.is_empty() || index >= self.len() { + return None; + } + Some(unsafe { self.elements[index].assume_init_ref() }) + } + + pub const fn truncate(mut self, new_len: usize) -> ArrayVec { + if new_len >= self.len { + return self; + } + self.len = new_len; + self + } + + pub fn iter(&self) -> Iter<'_, T> { + unsafe { slice::from_raw_parts(self.elements.as_ptr().cast(), self.len()).iter() } + } +} From f4b385cb54fed902fd5da33312007940976f8b13 Mon Sep 17 00:00:00 2001 From: Redfire Date: Tue, 16 Jan 2024 00:16:57 +0800 Subject: [PATCH 07/12] Improved Safety of Private Data --- ion-proc/src/class/struct.rs | 15 ++++------- ion-proc/src/function/parameter.rs | 2 +- ion/src/class/mod.rs | 32 ++++++++++++++++++++--- ion/src/format/descriptor.rs | 0 ion/src/object/iterator.rs | 8 +++++- runtime/src/globals/abort.rs | 2 +- runtime/src/globals/fetch/header.rs | 2 +- runtime/src/globals/fetch/mod.rs | 16 ++++++------ runtime/src/globals/fetch/response/mod.rs | 4 +-- runtime/src/globals/file/reader.rs | 24 ++++++++--------- runtime/src/globals/url/mod.rs | 4 +-- runtime/src/globals/url/search_params.rs | 4 +-- 12 files changed, 70 insertions(+), 43 deletions(-) create mode 100644 ion/src/format/descriptor.rs diff --git a/ion-proc/src/class/struct.rs b/ion-proc/src/class/struct.rs index 7c28a6bc..3bf70bc8 100644 --- a/ion-proc/src/class/struct.rs +++ b/ion-proc/src/class/struct.rs @@ -5,7 +5,7 @@ */ use proc_macro2::{Ident, Span, TokenStream}; -use syn::{Error, Fields, ImplItemFn, ItemImpl, ItemStruct, LitStr, Member, parse2, Path, Result, Type}; +use syn::{Error, Fields, ImplItemFn, ItemImpl, ItemStruct, Member, parse2, Path, Result, Type}; use syn::punctuated::Punctuated; use syn::spanned::Spanned; @@ -95,8 +95,8 @@ pub(super) fn impl_js_class_struct(r#struct: &mut ItemStruct) -> Result<[ItemImp fn class_impls( ion: &TokenStream, span: Span, name: &str, r#type: &Type, super_field: &Member, super_type: &Type, ) -> Result<[ItemImpl; 6]> { - let from_value = impl_from_value(ion, span, name, r#type, false)?; - let from_value_mut = impl_from_value(ion, span, name, r#type, true)?; + let from_value = impl_from_value(ion, span, r#type, false)?; + let from_value_mut = impl_from_value(ion, span, r#type, true)?; let derived_from = quote_spanned!(span => unsafe impl #ion::class::DerivedFrom<#super_type> for #r#type {}); let derived_from = parse2(derived_from)?; @@ -162,8 +162,7 @@ fn class_impls( ]) } -fn impl_from_value(ion: &TokenStream, span: Span, name: &str, r#type: &Type, mutable: bool) -> Result { - let from_value_error = LitStr::new(&format!("Expected {}", name), span); +fn impl_from_value(ion: &TokenStream, span: Span, r#type: &Type, mutable: bool) -> Result { let (function, mutable) = if mutable { (quote!(get_mut_private), Some(new_token![mut])) } else { @@ -176,11 +175,7 @@ fn impl_from_value(ion: &TokenStream, span: Span, name: &str, r#type: &Type, mut fn from_value(cx: &'cx #ion::Context, value: &#ion::Value, strict: ::core::primitive::bool, _: ()) -> #ion::Result<&'cx #mutable #r#type> { let object = #ion::Object::from_value(cx, value, strict, ())?; - if <#r#type as #ion::class::ClassDefinition>::instance_of(cx, &object) { - Ok(<#r#type as #ion::class::ClassDefinition>::#function(&object)) - } else { - Err(#ion::Error::new(#from_value_error, #ion::ErrorKind::Type)) - } + <#r#type as #ion::class::ClassDefinition>::#function(cx, &object) } }), ) diff --git a/ion-proc/src/function/parameter.rs b/ion-proc/src/function/parameter.rs index 57328713..9decb021 100644 --- a/ion-proc/src/function/parameter.rs +++ b/ion-proc/src/function/parameter.rs @@ -135,7 +135,7 @@ impl ThisParameter { if is_class { parse2(quote!( - let #pat_ty = <#ty as #ion::ClassDefinition>::get_mut_private(__this); + let #pat_ty = <#ty as #ion::ClassDefinition>::get_mut_private(__cx, __this)?; )) } else { parse2(quote!( diff --git a/ion/src/class/mod.rs b/ion/src/class/mod.rs index fb02638c..5495ffcc 100644 --- a/ion/src/class/mod.rs +++ b/ion/src/class/mod.rs @@ -6,6 +6,7 @@ use std::any::TypeId; use std::collections::hash_map::Entry; +use std::ffi::CStr; use std::ptr; use mozjs::glue::JS_GetReservedSlot; @@ -16,7 +17,7 @@ use mozjs::jsapi::{ use mozjs::jsval::{PrivateValue, UndefinedValue}; use mozjs::rust::HandleObject; -use crate::{Context, Function, Local, Object}; +use crate::{Context, Error, ErrorKind, Function, Local, Object, Result}; pub use crate::class::native::{MAX_PROTO_CHAIN_LENGTH, NativeClass, PrototypeChain, TypeIdWrapper}; pub use crate::class::reflect::{Castable, DerivedFrom, NativeObject, Reflector}; use crate::function::NativeFunction; @@ -135,7 +136,7 @@ pub trait ClassDefinition: NativeObject { object } - fn get_private<'a>(object: &Object<'a>) -> &'a Self { + unsafe fn get_private_unchecked<'a>(object: &Object<'a>) -> &'a Self { unsafe { let mut value = UndefinedValue(); JS_GetReservedSlot(object.handle().get(), 0, &mut value); @@ -143,8 +144,16 @@ pub trait ClassDefinition: NativeObject { } } + fn get_private<'a>(cx: &Context, object: &Object<'a>) -> Result<&'a Self> { + if Self::instance_of(cx, object) { + Ok(unsafe { Self::get_private_unchecked(object) }) + } else { + Err(private_error(Self::class())) + } + } + #[allow(clippy::mut_from_ref)] - fn get_mut_private<'a>(object: &Object<'a>) -> &'a mut Self { + unsafe fn get_mut_private_unchecked<'a>(object: &Object<'a>) -> &'a mut Self { unsafe { let mut value = UndefinedValue(); JS_GetReservedSlot(object.handle().get(), 0, &mut value); @@ -152,6 +161,15 @@ pub trait ClassDefinition: NativeObject { } } + #[allow(clippy::mut_from_ref)] + fn get_mut_private<'a>(cx: &Context, object: &Object<'a>) -> Result<&'a mut Self> { + if Self::instance_of(cx, object) { + Ok(unsafe { Self::get_mut_private_unchecked(object) }) + } else { + Err(private_error(Self::class())) + } + } + unsafe fn set_private(object: *mut JSObject, native: Box) { native.reflector().set(object); unsafe { @@ -194,3 +212,11 @@ fn has_zero_spec(specs: Option<&[T]>) -> bool { fn unwrap_specs(specs: Option<&[T]>) -> *const T { specs.map_or_else(ptr::null, |specs| specs.as_ptr()) } + +fn private_error(class: &'static NativeClass) -> Error { + let name = unsafe { CStr::from_ptr(class.base.name).to_str().unwrap() }; + Error::new( + &format!("Object does not implement interface {}", name), + ErrorKind::Type, + ) +} diff --git a/ion/src/format/descriptor.rs b/ion/src/format/descriptor.rs new file mode 100644 index 00000000..e69de29b diff --git a/ion/src/object/iterator.rs b/ion/src/object/iterator.rs index e7560649..29b6b6d6 100644 --- a/ion/src/object/iterator.rs +++ b/ion/src/object/iterator.rs @@ -92,7 +92,13 @@ impl Iterator { let args = &mut unsafe { Arguments::new(cx, argc, vp) }; let this = args.this().to_object(cx); - let iterator = Iterator::get_mut_private(&this); + let iterator = match Iterator::get_mut_private(cx, &this) { + Ok(iterator) => iterator, + Err(e) => { + e.throw(cx); + return false; + } + }; let result = iterator.next_value(cx); result.to_value(cx, &mut args.rval()); diff --git a/runtime/src/globals/abort.rs b/runtime/src/globals/abort.rs index e4f2cf7b..322a342c 100644 --- a/runtime/src/globals/abort.rs +++ b/runtime/src/globals/abort.rs @@ -196,7 +196,7 @@ impl<'cx> FromValue<'cx> for AbortSignal { if AbortSignal::instance_of(cx, &object) { Ok(AbortSignal { reflector: Reflector::default(), - signal: AbortSignal::get_private(&object).signal.clone(), + signal: AbortSignal::get_private(cx, &object)?.signal.clone(), }) } else { Err(Error::new("Expected AbortSignal", ErrorKind::Type)) diff --git a/runtime/src/globals/fetch/header.rs b/runtime/src/globals/fetch/header.rs index 31989471..3eaac61f 100644 --- a/runtime/src/globals/fetch/header.rs +++ b/runtime/src/globals/fetch/header.rs @@ -277,7 +277,7 @@ pub struct HeadersIterator { impl JSIterator for HeadersIterator { fn next_value<'cx>(&mut self, cx: &'cx Context, private: &Value<'cx>) -> Option> { let object = private.to_object(cx); - let headers = Headers::get_private(&object); + let headers = Headers::get_private(cx, &object).unwrap(); let key = self.keys.next(); key.and_then(|key| { if key == SET_COOKIE.as_str() { diff --git a/runtime/src/globals/fetch/mod.rs b/runtime/src/globals/fetch/mod.rs index 0478cecf..1db2239f 100644 --- a/runtime/src/globals/fetch/mod.rs +++ b/runtime/src/globals/fetch/mod.rs @@ -68,14 +68,14 @@ fn fetch<'cx>(cx: &'cx Context, resource: RequestInfo, init: Opt) - }; let signal = Object::from(unsafe { Local::from_heap(&request.signal_object) }); - let signal = AbortSignal::get_private(&signal); + let signal = AbortSignal::get_private(cx, &signal).unwrap(); if let Some(reason) = signal.get_reason() { promise.reject(cx, &cx.root_value(reason).into()); return Some(promise); } let headers = Object::from(unsafe { Local::from_heap(&request.headers) }); - let headers = Headers::get_mut_private(&headers); + let headers = Headers::get_mut_private(cx, &headers).unwrap(); if !headers.headers.contains_key(ACCEPT) { headers.headers.append(ACCEPT, HeaderValue::from_static("*/*")); } @@ -104,9 +104,9 @@ fn fetch<'cx>(cx: &'cx Context, resource: RequestInfo, init: Opt) - } async fn fetch_internal<'o>(cx: &Context, request: &Object<'o>, client: Client) -> ResultExc<*mut JSObject> { - let request = Request::get_mut_private(request); + let request = Request::get_mut_private(cx, request)?; let signal = Object::from(unsafe { Local::from_heap(&request.signal_object) }); - let signal = AbortSignal::get_private(&signal).signal.clone().poll(); + let signal = AbortSignal::get_private(cx, &signal)?.signal.clone().poll(); let send = Box::pin(main_fetch(cx, request, client, 0)); let response = match select(send, signal).await { Either::Left((response, _)) => Ok(response), @@ -252,7 +252,7 @@ async fn main_fetch(cx: &Context, request: &mut Request, client: Client, redirec response.url.get_or_insert(request.url.clone()); let headers = Object::from(unsafe { Local::from_heap(&response.headers) }); - let headers = Headers::get_mut_private(&headers); + let headers = Headers::get_mut_private(cx, &headers).unwrap(); if !opaque_redirect && taint == ResponseTaint::Opaque @@ -421,7 +421,7 @@ async fn http_fetch( #[async_recursion(?Send)] async fn http_network_fetch(cx: &Context, request: &Request, client: Client, is_new: bool) -> Response { let headers = Object::from(unsafe { Local::from_heap(&request.headers) }); - let mut headers = Headers::get_mut_private(&headers).headers.clone(); + let mut headers = Headers::get_mut_private(cx, &headers).unwrap().headers.clone(); let length = request .body @@ -526,7 +526,7 @@ async fn http_redirect_fetch( cx: &Context, request: &mut Request, response: Response, client: Client, taint: ResponseTaint, redirections: u8, ) -> Response { let headers = Object::from(unsafe { Local::from_heap(&response.headers) }); - let headers = Headers::get_private(&headers); + let headers = Headers::get_private(cx, &headers).unwrap(); let mut location = headers.headers.get_all(LOCATION).into_iter(); let location = match location.size_hint().1 { Some(0) => return response, @@ -571,7 +571,7 @@ async fn http_redirect_fetch( request.method = Method::GET; request.body = FetchBody::default(); let headers = Object::from(unsafe { Local::from_heap(&request.headers) }); - let headers = Headers::get_mut_private(&headers); + let headers = Headers::get_mut_private(cx, &headers).unwrap(); remove_all_header_entries(&mut headers.headers, &CONTENT_ENCODING); remove_all_header_entries(&mut headers.headers, &CONTENT_LANGUAGE); remove_all_header_entries(&mut headers.headers, &CONTENT_LOCATION); diff --git a/runtime/src/globals/fetch/response/mod.rs b/runtime/src/globals/fetch/response/mod.rs index 34b52e48..308042e2 100644 --- a/runtime/src/globals/fetch/response/mod.rs +++ b/runtime/src/globals/fetch/response/mod.rs @@ -191,7 +191,7 @@ impl Response { let this = this.handle().into(); future_to_promise::<_, _, Error>(cx, async move { let response = Object::from(unsafe { Local::from_raw_handle(this) }); - let response = Response::get_mut_private(&response); + let response = Response::get_mut_private(&cx2, &response)?; let bytes = response.read_to_bytes().await?; cx2.unroot_persistent_object(this.get()); Ok(ArrayBufferWrapper::from(bytes)) @@ -204,7 +204,7 @@ impl Response { let this = this.handle().into(); future_to_promise::<_, _, Error>(cx, async move { let response = Object::from(unsafe { Local::from_raw_handle(this) }); - let response = Response::get_mut_private(&response); + let response = Response::get_mut_private(&cx2, &response)?; let bytes = response.read_to_bytes().await?; cx2.unroot_persistent_object(this.get()); String::from_utf8(bytes).map_err(|e| Error::new(&format!("Invalid UTF-8 sequence: {}", e), None)) diff --git a/runtime/src/globals/file/reader.rs b/runtime/src/globals/file/reader.rs index 7db7afe9..574a8333 100644 --- a/runtime/src/globals/file/reader.rs +++ b/runtime/src/globals/file/reader.rs @@ -101,13 +101,13 @@ impl FileReader { let cx2 = unsafe { Context::new_unchecked(cx.as_ptr()) }; let this = this.handle().into_handle(); - future_to_promise(cx, async move { + future_to_promise::<_, _, Error>(cx, async move { let reader = Object::from(unsafe { Local::from_raw_handle(this) }); - let reader = FileReader::get_private(&reader); + let reader = FileReader::get_private(&cx2, &reader)?; let array_buffer = ArrayBufferWrapper::from(bytes.to_vec()); reader.result.set(array_buffer.as_value(&cx2).get()); cx2.unroot_persistent_object(this.get()); - Ok::<_, ()>(()) + Ok(()) }); Ok(()) } @@ -121,13 +121,13 @@ impl FileReader { let cx2 = unsafe { Context::new_unchecked(cx.as_ptr()) }; let this = this.handle().into_handle(); - future_to_promise(cx, async move { + future_to_promise::<_, _, Error>(cx, async move { let reader = Object::from(unsafe { Local::from_raw_handle(this) }); - let reader = FileReader::get_private(&reader); + let reader = FileReader::get_private(&cx2, &reader)?; let byte_string = unsafe { ByteString::::from_unchecked(bytes.to_vec()) }; reader.result.set(byte_string.as_value(&cx2).get()); cx2.unroot_persistent_object(this.get()); - Ok::<_, ()>(()) + Ok(()) }); Ok(()) } @@ -142,15 +142,15 @@ impl FileReader { let cx2 = unsafe { Context::new_unchecked(cx.as_ptr()) }; let this = this.handle().into_handle(); - future_to_promise(cx, async move { + future_to_promise::<_, _, Error>(cx, async move { let encoding = encoding_from_string_mime(encoding.as_deref(), mime.as_deref()); let reader = Object::from(unsafe { Local::from_raw_handle(this) }); - let reader = FileReader::get_private(&reader); + let reader = FileReader::get_private(&cx2, &reader)?; let str = encoding.decode_without_bom_handling(&bytes).0; reader.result.set(str.as_value(&cx2).get()); cx2.unroot_persistent_object(this.get()); - Ok::<_, ()>(()) + Ok(()) }); Ok(()) } @@ -165,9 +165,9 @@ impl FileReader { let cx2 = unsafe { Context::new_unchecked(cx.as_ptr()) }; let this = this.handle().into_handle(); - future_to_promise(cx, async move { + future_to_promise::<_, _, Error>(cx, async move { let reader = Object::from(unsafe { Local::from_raw_handle(this) }); - let reader = FileReader::get_private(&reader); + let reader = FileReader::get_private(&cx2, &reader)?; let base64 = BASE64_STANDARD.encode(&bytes); let data_url = match mime { Some(mime) => format!("data:{};base64,{}", mime, base64), @@ -176,7 +176,7 @@ impl FileReader { reader.result.set(data_url.as_value(&cx2).get()); cx2.unroot_persistent_object(this.get()); - Ok::<_, ()>(()) + Ok(()) }); Ok(()) } diff --git a/runtime/src/globals/url/mod.rs b/runtime/src/globals/url/mod.rs index f6307ba4..af2985d6 100644 --- a/runtime/src/globals/url/mod.rs +++ b/runtime/src/globals/url/mod.rs @@ -90,11 +90,11 @@ impl URL { } #[ion(set)] - pub fn set_href(&mut self, input: String) -> Result<()> { + pub fn set_href(&mut self, cx: &Context, input: String) -> Result<()> { match Url::parse(&input) { Ok(url) => { let search_params = Object::from(unsafe { Local::from_heap(&self.search_params) }); - let search_params = URLSearchParams::get_mut_private(&search_params); + let search_params = URLSearchParams::get_mut_private(cx, &search_params)?; search_params.set_pairs(url.query_pairs().into_owned().collect()); self.url = url; Ok(()) diff --git a/runtime/src/globals/url/search_params.rs b/runtime/src/globals/url/search_params.rs index f03d02fd..d27e01ec 100644 --- a/runtime/src/globals/url/search_params.rs +++ b/runtime/src/globals/url/search_params.rs @@ -165,7 +165,7 @@ impl URLSearchParams { fn update(&mut self) { if let Some(url) = &self.url { let url = Object::from(unsafe { Local::from_heap(url) }); - let url = URL::get_mut_private(&url); + let url = unsafe { URL::get_mut_private_unchecked(&url) }; if self.pairs.is_empty() { url.url.set_query(None); } else { @@ -187,7 +187,7 @@ pub struct SearchParamsIterator(usize); impl JSIterator for SearchParamsIterator { fn next_value<'cx>(&mut self, cx: &'cx Context, private: &Value<'cx>) -> Option> { let object = private.to_object(cx); - let search_params = URLSearchParams::get_private(&object); + let search_params = URLSearchParams::get_private(cx, &object).unwrap(); let pair = search_params.pairs.get(self.0); pair.map(move |(k, v)| { self.0 += 1; From 43b9a52857e8bdf6abea14cafde02a60f9dc7a9e Mon Sep 17 00:00:00 2001 From: Redfire Date: Tue, 16 Jan 2024 00:27:53 +0800 Subject: [PATCH 08/12] Added Descriptor Formatting Improved Getter and Setter Formatting in Prototypes (#43) --- ion/src/format/array.rs | 32 +++----- ion/src/format/descriptor.rs | 37 +++++++++ ion/src/format/key.rs | 2 +- ion/src/format/mod.rs | 1 + ion/src/format/object.rs | 151 ++++++++++++----------------------- ion/src/object/array.rs | 8 +- ion/src/object/descriptor.rs | 10 ++- ion/src/object/object.rs | 34 +++++++- 8 files changed, 147 insertions(+), 128 deletions(-) diff --git a/ion/src/format/array.rs b/ion/src/format/array.rs index c428eda3..06842a7e 100644 --- a/ion/src/format/array.rs +++ b/ion/src/format/array.rs @@ -10,8 +10,9 @@ use std::fmt::{Display, Formatter, Write}; use colored::Colorize; use crate::{Array, Context}; -use crate::format::{format_value, INDENT, NEWLINE}; +use crate::format::{INDENT, NEWLINE}; use crate::format::Config; +use crate::format::descriptor::format_descriptor; use crate::format::object::write_remaining; /// Formats an [JavaScript Array](Array) as a string using the given [configuration](Config). @@ -29,8 +30,7 @@ impl Display for ArrayDisplay<'_> { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { let colour = self.cfg.colours.array; if self.cfg.depth < 5 { - let vec = self.array.to_vec(self.cx); - let length = vec.len(); + let length = self.array.len(self.cx); if length == 0 { write!(f, "{}", "[]".color(colour)) @@ -43,14 +43,11 @@ impl Display for ArrayDisplay<'_> { let inner = INDENT.repeat((self.cfg.indentation + self.cfg.depth + 1) as usize); - for value in vec.into_iter().take(len) { + for index in 0..len { f.write_str(&inner)?; - write!( - f, - "{}", - format_value(self.cx, self.cfg.depth(self.cfg.depth + 1).quoted(true), &value) - )?; - write!(f, "{}", ",".color(colour))?; + let desc = self.array.get_descriptor(self.cx, index).unwrap(); + format_descriptor(self.cx, self.cfg, &desc).fmt(f)?; + ",".color(colour).fmt(f)?; f.write_str(NEWLINE)?; } @@ -59,15 +56,12 @@ impl Display for ArrayDisplay<'_> { f.write_char(' ')?; let len = length.clamp(0, 3); - for (i, value) in vec.into_iter().enumerate().take(len) { - write!( - f, - "{}", - format_value(self.cx, self.cfg.depth(self.cfg.depth + 1).quoted(true), &value) - )?; + for index in 0..len { + let desc = self.array.get_descriptor(self.cx, index).unwrap(); + format_descriptor(self.cx, self.cfg, &desc).fmt(f)?; - if i != len - 1 { - write!(f, "{}", ",".color(colour))?; + if index != len - 1 { + ",".color(colour).fmt(f)?; f.write_char(' ')?; } } @@ -75,7 +69,7 @@ impl Display for ArrayDisplay<'_> { (length - len, None) }; - write_remaining(f, remaining, inner.as_deref(), colour)?; + write_remaining(f, remaining as usize, inner.as_deref(), colour)?; if self.cfg.multiline { f.write_str(&INDENT.repeat((self.cfg.indentation + self.cfg.depth) as usize))?; diff --git a/ion/src/format/descriptor.rs b/ion/src/format/descriptor.rs index e69de29b..5e9c1770 100644 --- a/ion/src/format/descriptor.rs +++ b/ion/src/format/descriptor.rs @@ -0,0 +1,37 @@ +/* + * 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/. + */ + +use std::fmt::{Display, Formatter}; +use colored::Colorize; +use crate::format::{Config, format_value}; +use crate::{Context, PropertyDescriptor}; + +/// Formats a [descriptor](PropertyDescriptor) with the given [configuration](Config). +pub fn format_descriptor<'cx>( + cx: &'cx Context, cfg: Config, desc: &'cx PropertyDescriptor<'cx>, +) -> DescriptorDisplay<'cx> { + DescriptorDisplay { cx, cfg, desc } +} + +pub struct DescriptorDisplay<'cx> { + cx: &'cx Context, + cfg: Config, + desc: &'cx PropertyDescriptor<'cx>, +} + +impl Display for DescriptorDisplay<'_> { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + match (self.desc.getter(self.cx), self.desc.setter(self.cx)) { + (Some(_), Some(_)) => "[Getter/Setter]".color(self.cfg.colours.function).fmt(f), + (Some(_), None) => "[Getter]".color(self.cfg.colours.function).fmt(f), + (None, Some(_)) => "[Setter]".color(self.cfg.colours.function).fmt(f), + (None, None) => match self.desc.value(self.cx) { + Some(value) => format_value(self.cx, self.cfg.depth(self.cfg.depth + 1).quoted(true), &value).fmt(f), + None => unreachable!(), + }, + } + } +} diff --git a/ion/src/format/key.rs b/ion/src/format/key.rs index 3c1a3336..d9a47538 100644 --- a/ion/src/format/key.rs +++ b/ion/src/format/key.rs @@ -14,7 +14,7 @@ use crate::{Context, OwnedKey}; use crate::format::Config; use crate::format::symbol::format_symbol; -/// Formats the [key of an object](OwnedKey) as a string with the given [configuration](Config), +/// Formats the [key of an object](OwnedKey) with the given [configuration](Config). pub fn format_key<'cx>(cx: &'cx Context, cfg: Config, key: &'cx OwnedKey<'cx>) -> KeyDisplay<'cx> { KeyDisplay { cx, cfg, key } } diff --git a/ion/src/format/mod.rs b/ion/src/format/mod.rs index 6072ad2e..aa64667e 100644 --- a/ion/src/format/mod.rs +++ b/ion/src/format/mod.rs @@ -18,6 +18,7 @@ pub mod boxed; pub mod class; mod config; pub mod date; +pub mod descriptor; pub mod function; pub mod key; pub mod object; diff --git a/ion/src/format/object.rs b/ion/src/format/object.rs index 41a0b9ef..999f9d7a 100644 --- a/ion/src/format/object.rs +++ b/ion/src/format/object.rs @@ -12,14 +12,15 @@ use colored::{Color, Colorize}; use itoa::Buffer; use mozjs::jsapi::{ESClass, Type}; -use crate::{Array, Context, Date, Exception, Function, Object, Promise, PropertyKey, RegExp, Value}; +use crate::{Array, Context, Date, Exception, Function, Object, Promise, PropertyDescriptor, PropertyKey, RegExp}; use crate::conversions::ToValue; -use crate::format::{format_value, INDENT, NEWLINE}; +use crate::format::{INDENT, NEWLINE}; use crate::format::array::format_array; use crate::format::boxed::format_boxed; use crate::format::class::format_class_object; use crate::format::Config; use crate::format::date::format_date; +use crate::format::descriptor::format_descriptor; use crate::format::function::format_function; use crate::format::key::format_key; use crate::format::promise::format_promise; @@ -53,99 +54,47 @@ impl Display for ObjectDisplay<'_> { let class = self.object.get_builtin_class(cx); match class { - ESC::Boolean | ESC::Number | ESC::String | ESC::BigInt => { - write!(f, "{}", format_boxed(cx, cfg, &self.object)) - } - ESC::Array => write!( - f, - "{}", - format_array(cx, cfg, &Array::from(cx, object.into_local()).unwrap()) - ), - ESC::Date => write!( - f, - "{}", - format_date(cx, cfg, &Date::from(cx, object.into_local()).unwrap()) - ), - ESC::Promise => write!( - f, - "{}", - format_promise(cx, cfg, &Promise::from(object.into_local()).unwrap()) - ), - ESC::RegExp => write!( - f, - "{}", - format_regexp(cx, cfg, &RegExp::from(cx, object.into_local()).unwrap()) - ), - ESC::Function => write!( - f, - "{}", - format_function(cx, cfg, &Function::from_object(cx, &self.object).unwrap()) - ), - ESC::ArrayBuffer => write!( - f, - "{}", - format_array_buffer(cfg, &ArrayBuffer::from(object.into_local()).unwrap()) - ), + ESC::Boolean | ESC::Number | ESC::String | ESC::BigInt => format_boxed(cx, cfg, &self.object).fmt(f), + ESC::Array => format_array(cx, cfg, &Array::from(cx, object.into_local()).unwrap()).fmt(f), + ESC::Date => format_date(cx, cfg, &Date::from(cx, object.into_local()).unwrap()).fmt(f), + ESC::Promise => format_promise(cx, cfg, &Promise::from(object.into_local()).unwrap()).fmt(f), + ESC::RegExp => format_regexp(cx, cfg, &RegExp::from(cx, object.into_local()).unwrap()).fmt(f), + ESC::Function => format_function(cx, cfg, &Function::from_object(cx, &self.object).unwrap()).fmt(f), + ESC::ArrayBuffer => format_array_buffer(cfg, &ArrayBuffer::from(object.into_local()).unwrap()).fmt(f), ESC::Error => match Exception::from_object(cx, &self.object) { Exception::Error(error) => f.write_str(&error.format()), _ => unreachable!("Expected Error"), }, - ESC::Object => { - write!( - f, - "{}", - format_plain_object(cx, cfg, &Object::from(object.into_local())) - ) - } + ESC::Object => format_plain_object(cx, cfg, &Object::from(object.into_local())).fmt(f), ESC::Other => { if let Some(view) = ArrayBufferView::from(cx.root_object(object.handle().get())) { 'view: { return match view.view_type() { - Type::Int8 => write!( - f, - "{}", - format_typed_array(cfg, &Int8Array::from(view.into_local()).unwrap()) - ), - Type::Uint8 => write!( - f, - "{}", - format_typed_array(cfg, &Uint8Array::from(view.into_local()).unwrap()) - ), - Type::Int16 => write!( - f, - "{}", - format_typed_array(cfg, &Int16Array::from(view.into_local()).unwrap()) - ), - Type::Uint16 => write!( - f, - "{}", - format_typed_array(cfg, &Uint16Array::from(view.into_local()).unwrap()) - ), - Type::Int32 => write!( - f, - "{}", - format_typed_array(cfg, &Int32Array::from(view.into_local()).unwrap()) - ), - Type::Uint32 => write!( - f, - "{}", - format_typed_array(cfg, &Uint32Array::from(view.into_local()).unwrap()) - ), - Type::Float32 => write!( - f, - "{}", - format_typed_array(cfg, &Float32Array::from(view.into_local()).unwrap()) - ), - Type::Float64 => write!( - f, - "{}", - format_typed_array(cfg, &Float64Array::from(view.into_local()).unwrap()) - ), - Type::Uint8Clamped => write!( - f, - "{}", - format_typed_array(cfg, &ClampedUint8Array::from(view.into_local()).unwrap()) - ), + Type::Int8 => format_typed_array(cfg, &Int8Array::from(view.into_local()).unwrap()).fmt(f), + Type::Uint8 => { + format_typed_array(cfg, &Uint8Array::from(view.into_local()).unwrap()).fmt(f) + } + Type::Int16 => { + format_typed_array(cfg, &Int16Array::from(view.into_local()).unwrap()).fmt(f) + } + Type::Uint16 => { + format_typed_array(cfg, &Uint16Array::from(view.into_local()).unwrap()).fmt(f) + } + Type::Int32 => { + format_typed_array(cfg, &Int32Array::from(view.into_local()).unwrap()).fmt(f) + } + Type::Uint32 => { + format_typed_array(cfg, &Uint32Array::from(view.into_local()).unwrap()).fmt(f) + } + Type::Float32 => { + format_typed_array(cfg, &Float32Array::from(view.into_local()).unwrap()).fmt(f) + } + Type::Float64 => { + format_typed_array(cfg, &Float64Array::from(view.into_local()).unwrap()).fmt(f) + } + Type::Uint8Clamped => { + format_typed_array(cfg, &ClampedUint8Array::from(view.into_local()).unwrap()).fmt(f) + } _ => break 'view, }; } @@ -192,8 +141,8 @@ impl Display for PlainObjectDisplay<'_> { for key in keys { f.write_str(&inner)?; - let value = self.object.get(self.cx, &key).unwrap(); - write_key_value(f, self.cx, self.cfg, &key, &value)?; + let desc = self.object.get_descriptor(self.cx, &key).unwrap(); + write_key_descriptor(f, self.cx, self.cfg, &key, &desc)?; write!(f, "{}", ",".color(colour))?; f.write_str(NEWLINE)?; } @@ -204,8 +153,8 @@ impl Display for PlainObjectDisplay<'_> { let len = length.clamp(0, 3); for (i, key) in keys.enumerate() { - let value = self.object.get(self.cx, &key).unwrap(); - write_key_value(f, self.cx, self.cfg, &key, &value)?; + let desc = self.object.get_descriptor(self.cx, &key).unwrap(); + write_key_descriptor(f, self.cx, self.cfg, &key, &desc)?; if i != len - 1 { write!(f, "{}", ",".color(colour))?; @@ -225,24 +174,22 @@ impl Display for PlainObjectDisplay<'_> { } } -fn write_key_value(f: &mut Formatter, cx: &Context, cfg: Config, key: &PropertyKey, value: &Value) -> fmt::Result { - write!( - f, - "{}{} {}", - format_key(cx, cfg, &key.to_owned_key(cx)), - ":".color(cfg.colours.object), - format_value(cx, cfg.depth(cfg.depth + 1).quoted(true), value) - ) +fn write_key_descriptor( + f: &mut Formatter, cx: &Context, cfg: Config, key: &PropertyKey, desc: &PropertyDescriptor, +) -> fmt::Result { + format_key(cx, cfg, &key.to_owned_key(cx)).fmt(f)?; + ": ".color(cfg.colours.object).fmt(f)?; + format_descriptor(cx, cfg, desc).fmt(f) } pub(crate) fn write_remaining(f: &mut Formatter, remaining: usize, inner: Option<&str>, colour: Color) -> fmt::Result { if remaining > 0 { if let Some(inner) = inner { - write!(f, "{}", inner)?; + f.write_str(inner)?; } match remaining.cmp(&1) { - Ordering::Equal => write!(f, "{}", "... 1 more item".color(colour))?, + Ordering::Equal => "... 1 more item".color(colour).fmt(f)?, Ordering::Greater => { let mut buffer = Buffer::new(); write!( @@ -256,7 +203,7 @@ pub(crate) fn write_remaining(f: &mut Formatter, remaining: usize, inner: Option _ => (), } if inner.is_some() { - write!(f, "{}", ",".color(colour))?; + ",".color(colour).fmt(f)?; } else { f.write_char(' ')?; } diff --git a/ion/src/object/array.rs b/ion/src/object/array.rs index 31437a90..c6d74e7c 100644 --- a/ion/src/object/array.rs +++ b/ion/src/object/array.rs @@ -10,7 +10,7 @@ use std::ops::{Deref, DerefMut}; use mozjs::jsapi::{GetArrayLength, HandleValueArray, IsArray, JSObject, NewArrayObject, NewArrayObject1}; use mozjs::jsval::{JSVal, ObjectValue}; -use crate::{Context, Local, Object, Value}; +use crate::{Context, Local, Object, PropertyDescriptor, Value}; use crate::conversions::{FromValue, ToValue}; use crate::flags::{IteratorFlags, PropertyFlags}; use crate::object::object::ObjectKeysIter; @@ -111,6 +111,12 @@ impl<'a> Array<'a> { self.arr.get_as(cx, index, strict, config) } + /// Gets the descriptor at the given index of the [Array]. + /// Returns [None] if there is no value at the given index. + pub fn get_descriptor<'cx>(&self, cx: &'cx Context, index: u32) -> Option> { + self.arr.get_descriptor(cx, index) + } + /// Sets the [Value] at the given index of the [Array]. /// Returns `false` if the element cannot be set. pub fn set(&self, cx: &Context, index: u32, value: &Value) -> bool { diff --git a/ion/src/object/descriptor.rs b/ion/src/object/descriptor.rs index 91e0fb97..8144ca8d 100644 --- a/ion/src/object/descriptor.rs +++ b/ion/src/object/descriptor.rs @@ -18,8 +18,12 @@ pub struct PropertyDescriptor<'pd> { } impl<'pd> PropertyDescriptor<'pd> { + pub fn empty(cx: &'pd Context) -> PropertyDescriptor<'pd> { + PropertyDescriptor::from(cx.root_property_descriptor(JSPropertyDescriptor::default())) + } + pub fn new(cx: &'pd Context, value: &Value, attrs: PropertyFlags) -> PropertyDescriptor<'pd> { - let mut desc = PropertyDescriptor::from(cx.root_property_descriptor(JSPropertyDescriptor::default())); + let mut desc = PropertyDescriptor::empty(cx); unsafe { SetDataPropertyDescriptor(desc.handle_mut().into(), value.handle().into(), attrs.bits() as u32) }; desc } @@ -29,7 +33,7 @@ impl<'pd> PropertyDescriptor<'pd> { ) -> PropertyDescriptor<'pd> { let getter = getter.to_object(cx); let setter = setter.to_object(cx); - let mut desc = PropertyDescriptor::from(cx.root_property_descriptor(JSPropertyDescriptor::default())); + let mut desc = PropertyDescriptor::empty(cx); unsafe { SetAccessorPropertyDescriptor( desc.handle_mut().into(), @@ -42,7 +46,7 @@ impl<'pd> PropertyDescriptor<'pd> { } pub fn from_object(cx: &'pd Context, object: &Object) -> Option> { - let mut desc = PropertyDescriptor::from(cx.root_property_descriptor(JSPropertyDescriptor::default())); + let mut desc = PropertyDescriptor::empty(cx); let desc_value = Value::object(cx, object); unsafe { ObjectToCompletePropertyDescriptor( diff --git a/ion/src/object/object.rs b/ion/src/object/object.rs index 0b2d938d..875664b4 100644 --- a/ion/src/object/object.rs +++ b/ion/src/object/object.rs @@ -14,13 +14,13 @@ use mozjs::jsapi::{ CurrentGlobalOrNull, ESClass, GetBuiltinClass, GetPropertyKeys, JS_DefineFunctionById, JS_DefineFunctions, JS_DefineFunctionsWithHelp, JS_DefineProperties, JS_DefinePropertyById2, JS_DeletePropertyById, JS_GetPropertyById, JS_HasOwnPropertyById, JS_HasPropertyById, JS_NewPlainObject, JS_SetPropertyById, JSFunctionSpec, - JSFunctionSpecWithHelp, JSObject, JSPropertySpec, Unbox, + JSFunctionSpecWithHelp, JSObject, JSPropertySpec, Unbox, JS_GetPropertyDescriptorById, }; use mozjs::jsapi::PropertyKey as JSPropertyKey; use mozjs::jsval::NullValue; use mozjs::rust::IdVector; -use crate::{Context, Exception, Function, Local, OwnedKey, PropertyKey, Value}; +use crate::{Context, Exception, Function, Local, OwnedKey, PropertyDescriptor, PropertyKey, Value}; use crate::conversions::{FromValue, ToPropertyKey, ToValue}; use crate::flags::{IteratorFlags, PropertyFlags}; use crate::function::NativeFunction; @@ -106,6 +106,36 @@ impl<'o> Object<'o> { self.get(cx, key).and_then(|val| T::from_value(cx, &val, strict, config).ok()) } + /// Gets the descriptor at the given key of the [Object]. + /// Returns [None] if the object does not contain the key. + pub fn get_descriptor<'cx, K: ToPropertyKey<'cx>>( + &self, cx: &'cx Context, key: K, + ) -> Option> { + let key = key.to_key(cx).unwrap(); + if self.has(cx, &key) { + let mut desc = PropertyDescriptor::empty(cx); + let mut holder = Object::null(cx); + let mut is_none = true; + unsafe { + JS_GetPropertyDescriptorById( + cx.as_ptr(), + self.handle().into(), + key.handle().into(), + desc.handle_mut().into(), + holder.handle_mut().into(), + &mut is_none, + ) + }; + if is_none { + None + } else { + Some(desc) + } + } else { + None + } + } + /// Sets the [Value] at the given key of the [Object]. /// /// Returns `false` if the property cannot be set. From 03c9bfd042ed65516a648c25546f6a0481979cf7 Mon Sep 17 00:00:00 2001 From: Redfire Date: Tue, 16 Jan 2024 00:57:44 +0800 Subject: [PATCH 09/12] Cleaned Up Display Formatting --- ion/src/format/array.rs | 8 ++++---- ion/src/format/boxed.rs | 2 +- ion/src/format/class.rs | 6 +++--- ion/src/format/date.rs | 2 +- ion/src/format/descriptor.rs | 4 +++- ion/src/format/key.rs | 8 ++++---- ion/src/format/mod.rs | 4 ++-- ion/src/format/object.rs | 14 +++++++------- ion/src/format/primitive.rs | 26 +++++++++++--------------- ion/src/format/promise.rs | 16 ++++++++-------- ion/src/format/regexp.rs | 2 +- ion/src/format/symbol.rs | 15 +++++++++------ ion/src/format/typedarray.rs | 12 ++++++------ ion/src/object/object.rs | 4 ++-- 14 files changed, 62 insertions(+), 61 deletions(-) diff --git a/ion/src/format/array.rs b/ion/src/format/array.rs index 06842a7e..ede6bca0 100644 --- a/ion/src/format/array.rs +++ b/ion/src/format/array.rs @@ -33,9 +33,9 @@ impl Display for ArrayDisplay<'_> { let length = self.array.len(self.cx); if length == 0 { - write!(f, "{}", "[]".color(colour)) + "[]".color(colour).fmt(f) } else { - write!(f, "{}", "[".color(colour))?; + "[".color(colour).fmt(f)?; let (remaining, inner) = if self.cfg.multiline { f.write_str(NEWLINE)?; @@ -75,10 +75,10 @@ impl Display for ArrayDisplay<'_> { f.write_str(&INDENT.repeat((self.cfg.indentation + self.cfg.depth) as usize))?; } - write!(f, "{}", "]".color(colour)) + "]".color(colour).fmt(f) } } else { - write!(f, "{}", "[Array]".color(colour)) + "[Array]".color(colour).fmt(f) } } } diff --git a/ion/src/format/boxed.rs b/ion/src/format/boxed.rs index 5debcdf4..cb4a200b 100644 --- a/ion/src/format/boxed.rs +++ b/ion/src/format/boxed.rs @@ -26,7 +26,7 @@ pub struct BoxedDisplay<'cx> { impl Display for BoxedDisplay<'_> { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { if let Some(primitive) = self.object.unbox_primitive(self.cx) { - write!(f, "{}", format_primitive(self.cx, self.cfg, &primitive)) + format_primitive(self.cx, self.cfg, &primitive).fmt(f) } else { unreachable!("Object is not boxed") } diff --git a/ion/src/format/class.rs b/ion/src/format/class.rs index c84ebc93..f9a700ce 100644 --- a/ion/src/format/class.rs +++ b/ion/src/format/class.rs @@ -30,8 +30,8 @@ impl Display for ClassObjectDisplay<'_> { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { let class = unsafe { get_object_class(self.object.handle().get()) }; let name = unsafe { CStr::from_ptr((*class).name) }.to_str().unwrap(); - - let string = format_plain_object(self.cx, self.cfg, self.object); - write!(f, "{} {}", name.color(self.cfg.colours.object), string) + name.color(self.cfg.colours.object).fmt(f)?; + f.write_str(" ")?; + format_plain_object(self.cx, self.cfg, self.object).fmt(f) } } diff --git a/ion/src/format/date.rs b/ion/src/format/date.rs index ce5b0da3..db487319 100644 --- a/ion/src/format/date.rs +++ b/ion/src/format/date.rs @@ -26,7 +26,7 @@ pub struct DateDisplay<'cx> { impl Display for DateDisplay<'_> { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { if let Some(date) = self.date.to_date(self.cx) { - write!(f, "{}", date.to_string().color(self.cfg.colours.date)) + date.to_string().color(self.cfg.colours.date).fmt(f) } else { panic!("Failed to unbox Date"); } diff --git a/ion/src/format/descriptor.rs b/ion/src/format/descriptor.rs index 5e9c1770..969498f9 100644 --- a/ion/src/format/descriptor.rs +++ b/ion/src/format/descriptor.rs @@ -5,9 +5,11 @@ */ use std::fmt::{Display, Formatter}; + use colored::Colorize; -use crate::format::{Config, format_value}; + use crate::{Context, PropertyDescriptor}; +use crate::format::{Config, format_value}; /// Formats a [descriptor](PropertyDescriptor) with the given [configuration](Config). pub fn format_descriptor<'cx>( diff --git a/ion/src/format/key.rs b/ion/src/format/key.rs index d9a47538..cd34983c 100644 --- a/ion/src/format/key.rs +++ b/ion/src/format/key.rs @@ -31,13 +31,13 @@ impl Display for KeyDisplay<'_> { match self.key { OwnedKey::Int(i) => { let mut buffer = Buffer::new(); - write!(f, "{}", buffer.format(*i).color(colours.number)) + buffer.format(*i).color(colours.number).fmt(f) } OwnedKey::String(str) => write!(f, "{0}{1}{0}", r#"""#.color(colours.string), str.color(colours.string)), OwnedKey::Symbol(sym) => { - write!(f, "{}", "[".color(colours.symbol))?; - write!(f, "{}", format_symbol(self.cx, self.cfg, sym))?; - write!(f, "{}", "]".color(colours.symbol)) + "[".color(colours.symbol).fmt(f)?; + format_symbol(self.cx, self.cfg, sym).fmt(f)?; + "]".color(colours.symbol).fmt(f) } OwnedKey::Void => unreachable!("Property key cannot be formatted."), } diff --git a/ion/src/format/mod.rs b/ion/src/format/mod.rs index aa64667e..dfe7242c 100644 --- a/ion/src/format/mod.rs +++ b/ion/src/format/mod.rs @@ -45,9 +45,9 @@ pub struct ValueDisplay<'cx> { impl Display for ValueDisplay<'_> { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { if self.value.handle().is_object() { - write!(f, "{}", format_object(self.cx, self.cfg, self.value.to_object(self.cx))) + format_object(self.cx, self.cfg, self.value.to_object(self.cx)).fmt(f) } else { - write!(f, "{}", format_primitive(self.cx, self.cfg, self.value)) + format_primitive(self.cx, self.cfg, self.value).fmt(f) } } } diff --git a/ion/src/format/object.rs b/ion/src/format/object.rs index 999f9d7a..8abe4756 100644 --- a/ion/src/format/object.rs +++ b/ion/src/format/object.rs @@ -100,7 +100,7 @@ impl Display for ObjectDisplay<'_> { } } - write!(f, "{}", format_class_object(cx, cfg, &self.object)) + format_class_object(cx, cfg, &self.object).fmt(f) } _ => { let source = self.object.as_value(cx).to_source(cx).to_owned(cx); @@ -131,9 +131,9 @@ impl Display for PlainObjectDisplay<'_> { let length = keys.len(); if length == 0 { - write!(f, "{}", "{}".color(colour)) + "{}".color(colour).fmt(f) } else { - write!(f, "{}", "{".color(colour))?; + "{".color(colour).fmt(f)?; if self.cfg.multiline { f.write_str(NEWLINE)?; @@ -143,7 +143,7 @@ impl Display for PlainObjectDisplay<'_> { f.write_str(&inner)?; let desc = self.object.get_descriptor(self.cx, &key).unwrap(); write_key_descriptor(f, self.cx, self.cfg, &key, &desc)?; - write!(f, "{}", ",".color(colour))?; + ",".color(colour).fmt(f)?; f.write_str(NEWLINE)?; } @@ -157,7 +157,7 @@ impl Display for PlainObjectDisplay<'_> { write_key_descriptor(f, self.cx, self.cfg, &key, &desc)?; if i != len - 1 { - write!(f, "{}", ",".color(colour))?; + ",".color(colour).fmt(f)?; f.write_char(' ')?; } } @@ -166,10 +166,10 @@ impl Display for PlainObjectDisplay<'_> { write_remaining(f, remaining, None, colour)?; } - write!(f, "{}", "}".color(colour)) + "}".color(colour).fmt(f) } } else { - write!(f, "{}", "[Object]".color(colour)) + "[Object]".color(colour).fmt(f) } } } diff --git a/ion/src/format/primitive.rs b/ion/src/format/primitive.rs index 016fa410..8ef9ff55 100644 --- a/ion/src/format/primitive.rs +++ b/ion/src/format/primitive.rs @@ -34,20 +34,20 @@ impl Display for PrimitiveDisplay<'_> { let value = self.value.handle(); if value.is_boolean() { - write!(f, "{}", value.to_boolean().to_string().color(colours.boolean)) + value.to_boolean().to_string().color(colours.boolean).fmt(f) } else if value.is_int32() { let int = value.to_int32(); let mut buffer = Buffer::new(); - write!(f, "{}", buffer.format(int).color(colours.number)) + buffer.format(int).color(colours.number).fmt(f) } else if value.is_double() { let number = value.to_double(); if number == f64::INFINITY { - write!(f, "{}", "Infinity".color(colours.number)) + "Infinity".color(colours.number).fmt(f) } else if number == f64::NEG_INFINITY { - write!(f, "{}", "-Infinity".color(colours.number)) + "-Infinity".color(colours.number).fmt(f) } else { - write!(f, "{}", number.to_string().color(colours.number)) + number.to_string().color(colours.number).fmt(f) } } else if value.is_string() { let str = crate::String::from_value(self.cx, self.value, true, ()).unwrap().to_owned(self.cx); @@ -57,22 +57,18 @@ impl Display for PrimitiveDisplay<'_> { f.write_str(&str) } } else if value.is_null() { - write!(f, "{}", "null".color(colours.null)) + "null".color(colours.null).fmt(f) } else if value.is_undefined() { - write!(f, "{}", "undefined".color(colours.undefined)) + "undefined".color(colours.undefined).fmt(f) } else if value.is_bigint() { let bi = BigInt::from(self.cx.root_bigint(value.to_bigint())); - write!( - f, - "{}{}", - bi.to_string(self.cx, 10).unwrap().to_owned(self.cx).color(colours.bigint), - "n".color(colours.bigint) - ) + bi.to_string(self.cx, 10).unwrap().to_owned(self.cx).color(colours.bigint).fmt(f)?; + "n".color(colours.bigint).fmt(f) } else if value.is_symbol() { let symbol = Symbol::from(self.cx.root_symbol(value.to_symbol())); - write!(f, "{}", format_symbol(self.cx, self.cfg, &symbol)) + format_symbol(self.cx, self.cfg, &symbol).fmt(f) } else if value.is_magic() { - write!(f, "{}", "".color(colours.boolean)) + "".color(colours.boolean).fmt(f) } else { unreachable!("Expected Primitive") } diff --git a/ion/src/format/promise.rs b/ion/src/format/promise.rs index 33b4419a..e3e25702 100644 --- a/ion/src/format/promise.rs +++ b/ion/src/format/promise.rs @@ -34,12 +34,12 @@ impl Display for PromiseDisplay<'_> { let state = self.promise.state(); let state = match state { - PromiseState::Pending => return write!(f, "{}", "Promise { }".color(colour)), + PromiseState::Pending => return "Promise { }".color(colour).fmt(f), PromiseState::Fulfilled => "".color(colour), PromiseState::Rejected => "".color(colour), }; - write!(f, "{}", "Promise {".color(colour))?; + "Promise {".color(colour).fmt(f)?; let result = self.promise.result(self.cx); if self.cfg.multiline { @@ -47,18 +47,18 @@ impl Display for PromiseDisplay<'_> { f.write_char('\n')?; f.write_str(&INDENT.repeat((self.cfg.indentation + self.cfg.depth + 1) as usize))?; - write!(f, "{}", state)?; + state.fmt(f)?; f.write_char(' ')?; - write!(f, "{}", result)?; - write!(f, "{}", "\n}".color(colour)) + result.fmt(f)?; + "\n}".color(colour).fmt(f) } else { let result = format_value(self.cx, self.cfg, &result); f.write_char(' ')?; - write!(f, "{}", state)?; + state.fmt(f)?; f.write_char(' ')?; - write!(f, "{}", result)?; - write!(f, "{}", " }".color(colour)) + result.fmt(f)?; + " }".color(colour).fmt(f) } } } diff --git a/ion/src/format/regexp.rs b/ion/src/format/regexp.rs index e394c1a8..438dfa17 100644 --- a/ion/src/format/regexp.rs +++ b/ion/src/format/regexp.rs @@ -26,6 +26,6 @@ pub struct RegExpDisplay<'cx> { impl Display for RegExpDisplay<'_> { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - write!(f, "{}", self.regexp.to_string(self.cx).color(self.cfg.colours.regexp)) + self.regexp.to_string(self.cx).color(self.cfg.colours.regexp).fmt(f) } } diff --git a/ion/src/format/symbol.rs b/ion/src/format/symbol.rs index 67523b9b..15dfd253 100644 --- a/ion/src/format/symbol.rs +++ b/ion/src/format/symbol.rs @@ -36,7 +36,10 @@ impl Display for SymbolDisplay<'_> { let code = self.symbol.code(); match code { - SymbolCode::WellKnown(code) => write!(f, "{}{}", "Symbol.".color(colour), code.identifier().color(colour)), + SymbolCode::WellKnown(code) => { + "Symbol.".color(colour).fmt(f)?; + code.identifier().color(colour).fmt(f) + } code => { let description = self .symbol @@ -45,14 +48,14 @@ impl Display for SymbolDisplay<'_> { .color(colour); match code { - SymbolCode::PrivateNameSymbol => return write!(f, "{}", description), - SymbolCode::InSymbolRegistry => write!(f, "{}", "Symbol.for(".color(colour),)?, - SymbolCode::UniqueSymbol => write!(f, "{}", "Symbol(".color(colour),)?, + SymbolCode::PrivateNameSymbol => return description.fmt(f), + SymbolCode::InSymbolRegistry => "Symbol.for(".color(colour).fmt(f)?, + SymbolCode::UniqueSymbol => "Symbol(".color(colour).fmt(f)?, _ => unreachable!(), } - write!(f, "{}", description)?; - write!(f, "{}", ")".color(colour)) + description.fmt(f)?; + ")".color(colour).fmt(f) } } } diff --git a/ion/src/format/typedarray.rs b/ion/src/format/typedarray.rs index 2e85adec..8f2bb5d0 100644 --- a/ion/src/format/typedarray.rs +++ b/ion/src/format/typedarray.rs @@ -25,7 +25,7 @@ pub struct ArrayBufferDisplay<'cx> { impl Display for ArrayBufferDisplay<'_> { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { let colour = self.cfg.colours.object; - write!(f, "{}", "ArrayBuffer {".color(colour))?; + "ArrayBuffer {".color(colour).fmt(f)?; let vec; let bytes = if self.buffer.is_shared() { @@ -60,7 +60,7 @@ impl Display for ArrayBufferDisplay<'_> { } f.write_char('>')?; - write!(f, "{}", ",".color(colour))?; + ",".color(colour).fmt(f)?; if bytes.len() < 8 { f.write_char(' ')?; } else { @@ -75,7 +75,7 @@ impl Display for ArrayBufferDisplay<'_> { f.write_str(NEWLINE)?; } - write!(f, "{}", "}".color(colour))?; + "}".color(colour).fmt(f)?; Ok(()) } @@ -128,8 +128,8 @@ where f.write_str(&indent)?; for (i, element) in elements.iter().enumerate() { - write!(f, "{}", element.to_string().color(self.cfg.colours.number))?; - write!(f, "{}", ",".color(colour))?; + element.to_string().color(self.cfg.colours.number).fmt(f)?; + ",".color(colour).fmt(f)?; if i != elements.len() - 1 { f.write_char(' ')?; @@ -141,7 +141,7 @@ where } f.write_str(NEWLINE)?; - write!(f, "{}", "}".color(colour))?; + "}".color(colour).fmt(f)?; Ok(()) } diff --git a/ion/src/object/object.rs b/ion/src/object/object.rs index 875664b4..bb079aac 100644 --- a/ion/src/object/object.rs +++ b/ion/src/object/object.rs @@ -13,8 +13,8 @@ use std::slice; use mozjs::jsapi::{ CurrentGlobalOrNull, ESClass, GetBuiltinClass, GetPropertyKeys, JS_DefineFunctionById, JS_DefineFunctions, JS_DefineFunctionsWithHelp, JS_DefineProperties, JS_DefinePropertyById2, JS_DeletePropertyById, JS_GetPropertyById, - JS_HasOwnPropertyById, JS_HasPropertyById, JS_NewPlainObject, JS_SetPropertyById, JSFunctionSpec, - JSFunctionSpecWithHelp, JSObject, JSPropertySpec, Unbox, JS_GetPropertyDescriptorById, + JS_GetPropertyDescriptorById, JS_HasOwnPropertyById, JS_HasPropertyById, JS_NewPlainObject, JS_SetPropertyById, + JSFunctionSpec, JSFunctionSpecWithHelp, JSObject, JSPropertySpec, Unbox, }; use mozjs::jsapi::PropertyKey as JSPropertyKey; use mozjs::jsval::NullValue; From fd62fb9c15b3300f6285442db148c4d566a0e54f Mon Sep 17 00:00:00 2001 From: Redfire Date: Tue, 16 Jan 2024 01:14:37 +0800 Subject: [PATCH 10/12] Fixed Getter/Setter Misdetection --- ion/src/object/descriptor.rs | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/ion/src/object/descriptor.rs b/ion/src/object/descriptor.rs index 8144ca8d..3da0c7cf 100644 --- a/ion/src/object/descriptor.rs +++ b/ion/src/object/descriptor.rs @@ -82,11 +82,19 @@ impl<'pd> PropertyDescriptor<'pd> { } pub fn getter<'cx>(&self, cx: &'cx Context) -> Option> { - self.handle().hasGetter_().then(|| Object::from(cx.root_object(self.handle().getter_))) + if self.handle().hasGetter_() && !self.handle().getter_.is_null() { + Some(Object::from(cx.root_object(self.handle().getter_))) + } else { + None + } } pub fn setter<'cx>(&self, cx: &'cx Context) -> Option> { - self.handle().hasSetter_().then(|| Object::from(cx.root_object(self.handle().setter_))) + if self.handle().hasSetter_() && !self.handle().setter_.is_null() { + Some(Object::from(cx.root_object(self.handle().setter_))) + } else { + None + } } pub fn value<'cx>(&self, cx: &'cx Context) -> Option> { From 7572f0d79f5f31ad77547965b46bf2748adddc97 Mon Sep 17 00:00:00 2001 From: Redfire Date: Tue, 16 Jan 2024 02:40:27 +0800 Subject: [PATCH 11/12] Added Symbol.toStringTag Generation Re-added Class Name Attribute Fixed Rest Argument Count --- ion-proc/src/attribute/class.rs | 51 ++++------------------- ion-proc/src/class/impl/mod.rs | 9 +++- ion-proc/src/class/struct.rs | 66 +++++++++++++----------------- ion-proc/src/function/parameter.rs | 2 +- 4 files changed, 44 insertions(+), 84 deletions(-) diff --git a/ion-proc/src/attribute/class.rs b/ion-proc/src/attribute/class.rs index 00c8a4e4..355b8af0 100644 --- a/ion-proc/src/attribute/class.rs +++ b/ion-proc/src/attribute/class.rs @@ -6,58 +6,21 @@ use syn::{LitStr, Result}; use syn::meta::ParseNestedMeta; -use syn::parse::{Parse, ParseStream}; use crate::attribute::{ArgumentError, ParseArgument, ParseArgumentWith, ParseAttribute}; use crate::attribute::name::Name; use crate::class::method::MethodKind; -mod keywords { - custom_keyword!(name); - custom_keyword!(class); -} - -#[allow(dead_code)] -pub(crate) struct ClassNameAttribute { - _kw: keywords::name, - _eq: Token![=], - pub(crate) name: LitStr, -} - -impl Parse for ClassNameAttribute { - fn parse(input: ParseStream) -> Result { - let lookahead = input.lookahead1(); - if lookahead.peek(keywords::name) { - Ok(ClassNameAttribute { - _kw: input.parse()?, - _eq: input.parse()?, - name: input.parse()?, - }) - } else { - Err(lookahead.error()) - } - } -} - // TODO: Add `inspectable` to provide `toString` and `toJSON` -#[allow(dead_code)] -pub(crate) enum ClassAttribute { - Name(ClassNameAttribute), - Class(keywords::class), +#[derive(Default)] +pub(crate) struct ClassAttribute { + pub(crate) name: Option, } -impl Parse for ClassAttribute { - fn parse(input: ParseStream) -> Result { - use ClassAttribute as CA; - - let lookahead = input.lookahead1(); - if lookahead.peek(keywords::name) { - Ok(CA::Name(input.parse()?)) - } else if lookahead.peek(keywords::class) { - Ok(CA::Class(input.parse()?)) - } else { - Err(lookahead.error()) - } +impl ParseAttribute for ClassAttribute { + fn parse(&mut self, meta: &ParseNestedMeta) -> Result<()> { + self.name.parse_argument(meta, "name", "Class")?; + Ok(()) } } diff --git a/ion-proc/src/class/impl/mod.rs b/ion-proc/src/class/impl/mod.rs index 993e35d8..c72d11d2 100644 --- a/ion-proc/src/class/impl/mod.rs +++ b/ion-proc/src/class/impl/mod.rs @@ -16,7 +16,7 @@ use crate::attribute::ParseAttribute; use crate::class::accessor::{get_accessor_name, impl_accessor, insert_accessor}; use crate::class::constructor::impl_constructor; use crate::class::method::{impl_method, Method, MethodKind, MethodReceiver}; -use crate::class::property::Property; +use crate::class::property::{Property, PropertyType}; use crate::class::r#impl::spec::PrototypeSpecs; mod spec; @@ -71,6 +71,13 @@ pub(super) fn impl_js_class_impl(r#impl: &mut ItemImpl) -> Result<[ItemImpl; 2]> _ => (), } } + specs.properties.0.push(Property { + ty: PropertyType::String, + ident: parse_quote!(__ION_TO_STRING_TAG), + names: vec![Name::Symbol( + parse_quote!(#ion::symbol::WellKnownSymbolCode::ToStringTag), + )], + }); let constructor = match constructor { Some(constructor) => constructor, diff --git a/ion-proc/src/class/struct.rs b/ion-proc/src/class/struct.rs index 3bf70bc8..9d76e29f 100644 --- a/ion-proc/src/class/struct.rs +++ b/ion-proc/src/class/struct.rs @@ -5,11 +5,13 @@ */ use proc_macro2::{Ident, Span, TokenStream}; -use syn::{Error, Fields, ImplItemFn, ItemImpl, ItemStruct, Member, parse2, Path, Result, Type}; +use syn::{Error, ImplItemFn, ItemImpl, ItemStruct, Member, parse2, Path, Result, Type}; use syn::punctuated::Punctuated; use syn::spanned::Spanned; +use crate::attribute::class::ClassAttribute; use crate::attribute::krate::crate_from_attributes; +use crate::attribute::ParseAttribute; use crate::utils::{new_token, path_ends_with}; pub(super) fn impl_js_class_struct(r#struct: &mut ItemStruct) -> Result<[ItemImpl; 6]> { @@ -40,6 +42,8 @@ pub(super) fn impl_js_class_struct(r#struct: &mut ItemStruct) -> Result<[ItemImp r#struct.attrs.push(parse_quote!(#[derive(#ion::Traceable)])); } + let attribute = ClassAttribute::from_attributes_mut("ion", &mut r#struct.attrs)?; + if !r#struct.generics.params.is_empty() { return Err(Error::new( r#struct.generics.span(), @@ -47,32 +51,23 @@ pub(super) fn impl_js_class_struct(r#struct: &mut ItemStruct) -> Result<[ItemImp )); } + let name = if let Some(name) = attribute.name { + name.value() + } else { + r#struct.ident.to_string() + }; + let ident = &r#struct.ident; let r#type: Type = parse2(quote_spanned!(ident.span() => #ident))?; - let super_field; - let super_type; - - let err = Err(Error::new( - r#struct.span(), - "Native Class Structs must have at least a reflector field.", - )); - match &r#struct.fields { - Fields::Named(fields) => match fields.named.first() { - Some(field) => { - super_field = Member::Named(field.ident.as_ref().unwrap().clone()); - super_type = field.ty.clone(); - } - None => return err, - }, - Fields::Unnamed(fields) => match fields.unnamed.first() { - Some(field) => { - super_field = parse_quote!(0); - super_type = field.ty.clone() - } - None => return err, - }, - Fields::Unit => return err, - } + + let (super_field, super_type) = if let Some(field) = r#struct.fields.iter().next() { + (Member::Named(field.ident.as_ref().unwrap().clone()), field.ty.clone()) + } else { + return Err(Error::new( + r#struct.span(), + "Native Class Structs must have at least a reflector field.", + )); + }; if let Type::Path(ty) = &super_type { if ty.path.segments.iter().any(|segment| !segment.arguments.is_empty()) { @@ -82,14 +77,7 @@ pub(super) fn impl_js_class_struct(r#struct: &mut ItemStruct) -> Result<[ItemImp return Err(Error::new(super_type.span(), "Superclass Type must be a path.")); } - class_impls( - ion, - r#struct.span(), - &ident.to_string(), - &r#type, - &super_field, - &super_type, - ) + class_impls(ion, r#struct.span(), &name, &r#type, &super_field, &super_type) } fn class_impls( @@ -113,7 +101,7 @@ fn class_impls( let operations = class_operations(span)?; let name = format!("{}\0", name); - let mut operations_native_class: ItemImpl = parse2(quote_spanned!(span => impl #r#type { + let mut class_impl: ItemImpl = parse2(quote_spanned!(span => impl #r#type { #(#operations)* pub const fn __ion_native_prototype_chain() -> #ion::class::PrototypeChain { @@ -149,8 +137,10 @@ fn class_impls( &ION_NATIVE_CLASS } + + pub const __ION_TO_STRING_TAG: &'static str = #name; }))?; - operations_native_class.attrs.push(parse_quote!(#[doc(hidden)])); + class_impl.attrs.push(parse_quote!(#[doc(hidden)])); Ok([ from_value, @@ -158,7 +148,7 @@ fn class_impls( derived_from, castable, native_object, - operations_native_class, + class_impl, ]) } @@ -181,7 +171,7 @@ fn impl_from_value(ion: &TokenStream, span: Span, r#type: &Type, mutable: bool) ) } -fn class_operations(span: Span) -> Result> { +fn class_operations(span: Span) -> Result<[ImplItemFn; 2]> { let finalise = parse2( quote_spanned!(span => unsafe extern "C" fn __ion_finalise_operation(_: *mut ::mozjs::jsapi::GCContext, this: *mut ::mozjs::jsapi::JSObject) { let mut value = ::mozjs::jsval::NullValue(); @@ -212,5 +202,5 @@ fn class_operations(span: Span) -> Result> { ), )?; - Ok(vec![finalise, trace]) + Ok([finalise, trace]) } diff --git a/ion-proc/src/function/parameter.rs b/ion-proc/src/function/parameter.rs index 9decb021..5de21820 100644 --- a/ion-proc/src/function/parameter.rs +++ b/ion-proc/src/function/parameter.rs @@ -182,7 +182,7 @@ impl Parameters { Err(e) => return Some(Err(e)), }; if let Type::Path(ty) = &*param.pat_ty.ty { - if !path_ends_with(&ty.path, "Opt") { + if !path_ends_with(&ty.path, "Opt") && !path_ends_with(&ty.path, "Rest") { nargs += 1; } } From 403dd73d432d319f0c873ea6e23ff5793e82e93a Mon Sep 17 00:00:00 2001 From: Redfire Date: Tue, 16 Jan 2024 16:35:03 +0800 Subject: [PATCH 12/12] Improved Accessor Formatting Added #[must_use] Attributes to DIsplay Wrappers Reduced Allocations in Indentation Formatting --- ion/src/format/array.rs | 15 ++++---- ion/src/format/boxed.rs | 3 +- ion/src/format/class.rs | 3 +- ion/src/format/config.rs | 1 + ion/src/format/date.rs | 3 +- ion/src/format/descriptor.rs | 64 ++++++++++++++++++++++++++++------ ion/src/format/function.rs | 3 +- ion/src/format/key.rs | 1 + ion/src/format/mod.rs | 20 ++++++++++- ion/src/format/object.rs | 31 ++++++++-------- ion/src/format/primitive.rs | 5 +-- ion/src/format/promise.rs | 7 ++-- ion/src/format/regexp.rs | 3 +- ion/src/format/symbol.rs | 1 + ion/src/format/typedarray.rs | 15 ++++---- ion/src/object/array.rs | 4 +++ ion/src/object/descriptor.rs | 14 +++++--- runtime/src/globals/console.rs | 8 ++--- 18 files changed, 141 insertions(+), 60 deletions(-) diff --git a/ion/src/format/array.rs b/ion/src/format/array.rs index ede6bca0..5d164aa8 100644 --- a/ion/src/format/array.rs +++ b/ion/src/format/array.rs @@ -10,16 +10,17 @@ use std::fmt::{Display, Formatter, Write}; use colored::Colorize; use crate::{Array, Context}; -use crate::format::{INDENT, NEWLINE}; +use crate::format::{indent_str, NEWLINE}; use crate::format::Config; use crate::format::descriptor::format_descriptor; use crate::format::object::write_remaining; -/// Formats an [JavaScript Array](Array) as a string using the given [configuration](Config). +/// Formats an [JavaScript Array](Array) using the given [configuration](Config). pub fn format_array<'cx>(cx: &'cx Context, cfg: Config, array: &'cx Array<'cx>) -> ArrayDisplay<'cx> { ArrayDisplay { cx, array, cfg } } +#[must_use] pub struct ArrayDisplay<'cx> { cx: &'cx Context, array: &'cx Array<'cx>, @@ -41,12 +42,12 @@ impl Display for ArrayDisplay<'_> { f.write_str(NEWLINE)?; let len = length.clamp(0, 100); - let inner = INDENT.repeat((self.cfg.indentation + self.cfg.depth + 1) as usize); + let inner = indent_str((self.cfg.indentation + self.cfg.depth + 1) as usize); for index in 0..len { - f.write_str(&inner)?; + inner.fmt(f)?; let desc = self.array.get_descriptor(self.cx, index).unwrap(); - format_descriptor(self.cx, self.cfg, &desc).fmt(f)?; + format_descriptor(self.cx, self.cfg, &desc, Some(self.array.as_object())).fmt(f)?; ",".color(colour).fmt(f)?; f.write_str(NEWLINE)?; } @@ -58,7 +59,7 @@ impl Display for ArrayDisplay<'_> { for index in 0..len { let desc = self.array.get_descriptor(self.cx, index).unwrap(); - format_descriptor(self.cx, self.cfg, &desc).fmt(f)?; + format_descriptor(self.cx, self.cfg, &desc, Some(self.array.as_object())).fmt(f)?; if index != len - 1 { ",".color(colour).fmt(f)?; @@ -72,7 +73,7 @@ impl Display for ArrayDisplay<'_> { write_remaining(f, remaining as usize, inner.as_deref(), colour)?; if self.cfg.multiline { - f.write_str(&INDENT.repeat((self.cfg.indentation + self.cfg.depth) as usize))?; + indent_str((self.cfg.indentation + self.cfg.depth) as usize).fmt(f)?; } "]".color(colour).fmt(f) diff --git a/ion/src/format/boxed.rs b/ion/src/format/boxed.rs index cb4a200b..0f7804b6 100644 --- a/ion/src/format/boxed.rs +++ b/ion/src/format/boxed.rs @@ -11,12 +11,13 @@ use crate::{Context, Object}; use crate::format::Config; use crate::format::primitive::format_primitive; -/// Formats a boxed primitive ([Object]) as a string using the given [configuration](Config). +/// Formats a boxed primitive ([Object]) using the given [configuration](Config). /// The supported boxed types are `Boolean`, `Number`, `String` and `BigInt`. pub fn format_boxed<'cx>(cx: &'cx Context, cfg: Config, object: &'cx Object<'cx>) -> BoxedDisplay<'cx> { BoxedDisplay { cx, object, cfg } } +#[must_use] pub struct BoxedDisplay<'cx> { cx: &'cx Context, object: &'cx Object<'cx>, diff --git a/ion/src/format/class.rs b/ion/src/format/class.rs index f9a700ce..633d7f69 100644 --- a/ion/src/format/class.rs +++ b/ion/src/format/class.rs @@ -15,11 +15,12 @@ use crate::{Context, Object}; use crate::format::Config; use crate::format::object::format_plain_object; -/// Formats a [JavaScript Object](Object), along with the name of its constructor, as a string with the given [configuration](Config). +/// Formats a [JavaScript Object](Object), along with the name of its constructor, with the given [configuration](Config). pub fn format_class_object<'cx>(cx: &'cx Context, cfg: Config, object: &'cx Object<'cx>) -> ClassObjectDisplay<'cx> { ClassObjectDisplay { cx, object, cfg } } +#[must_use] pub struct ClassObjectDisplay<'cx> { cx: &'cx Context, object: &'cx Object<'cx>, diff --git a/ion/src/format/config.rs b/ion/src/format/config.rs index 695bece3..18c5bd58 100644 --- a/ion/src/format/config.rs +++ b/ion/src/format/config.rs @@ -69,6 +69,7 @@ impl ColourConfig { /// Represents configuration for formatting #[derive(Clone, Copy, Debug)] +#[must_use] pub struct Config { pub colours: ColourConfig, pub iteration: IteratorFlags, diff --git a/ion/src/format/date.rs b/ion/src/format/date.rs index db487319..1eb4fc8e 100644 --- a/ion/src/format/date.rs +++ b/ion/src/format/date.rs @@ -12,11 +12,12 @@ use colored::Colorize; use crate::{Context, Date}; use crate::format::Config; -/// Formats a [JavaScript Date](Date) as a string using the given [configuration](Config). +/// Formats a [JavaScript Date](Date) using the given [configuration](Config). pub fn format_date<'cx>(cx: &'cx Context, cfg: Config, date: &'cx Date<'cx>) -> DateDisplay<'cx> { DateDisplay { cx, date, cfg } } +#[must_use] pub struct DateDisplay<'cx> { cx: &'cx Context, date: &'cx Date<'cx>, diff --git a/ion/src/format/descriptor.rs b/ion/src/format/descriptor.rs index 969498f9..7540558d 100644 --- a/ion/src/format/descriptor.rs +++ b/ion/src/format/descriptor.rs @@ -8,32 +8,76 @@ use std::fmt::{Display, Formatter}; use colored::Colorize; -use crate::{Context, PropertyDescriptor}; +use crate::{Context, Object, PropertyDescriptor}; use crate::format::{Config, format_value}; +use crate::format::object::format_object; +use crate::format::primitive::format_primitive; /// Formats a [descriptor](PropertyDescriptor) with the given [configuration](Config). pub fn format_descriptor<'cx>( - cx: &'cx Context, cfg: Config, desc: &'cx PropertyDescriptor<'cx>, + cx: &'cx Context, cfg: Config, desc: &'cx PropertyDescriptor<'cx>, object: Option<&'cx Object<'cx>>, ) -> DescriptorDisplay<'cx> { - DescriptorDisplay { cx, cfg, desc } + DescriptorDisplay { cx, cfg, desc, object } } +#[must_use] pub struct DescriptorDisplay<'cx> { cx: &'cx Context, cfg: Config, desc: &'cx PropertyDescriptor<'cx>, + object: Option<&'cx Object<'cx>>, } impl Display for DescriptorDisplay<'_> { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - match (self.desc.getter(self.cx), self.desc.setter(self.cx)) { - (Some(_), Some(_)) => "[Getter/Setter]".color(self.cfg.colours.function).fmt(f), - (Some(_), None) => "[Getter]".color(self.cfg.colours.function).fmt(f), - (None, Some(_)) => "[Setter]".color(self.cfg.colours.function).fmt(f), - (None, None) => match self.desc.value(self.cx) { + let color = self.cfg.colours.function; + + if let Some(getter) = self.desc.getter(self.cx) { + "[Getter".color(color).fmt(f)?; + if self.desc.setter(self.cx).is_some() { + "/Setter".color(color).fmt(f)?; + } + return if let Some(object) = self.object { + let value = match getter.call(self.cx, object, &[]) { + Ok(value) => value, + Err(report) => { + f.write_str(" { + report.stack = None; + report.format(self.cx).fmt(f)?; + } + None => f.write_str("unknown error")?, + } + f.write_str(">")?; + return "]".color(color).fmt(f); + } + }; + if value.handle().is_object() { + "] ".color(color).fmt(f)?; + format_object( + self.cx, + self.cfg.depth(self.cfg.depth + 1).quoted(true), + value.to_object(self.cx), + ) + .fmt(f) + } else { + ": ".color(color).fmt(f)?; + format_primitive(self.cx, self.cfg.quoted(true), &value).fmt(f)?; + "]".color(color).fmt(f) + } + } else { + "]".color(color).fmt(f) + }; + } + + if self.desc.setter(self.cx).is_some() { + "[Setter]".color(color).fmt(f) + } else { + match self.desc.value(self.cx) { Some(value) => format_value(self.cx, self.cfg.depth(self.cfg.depth + 1).quoted(true), &value).fmt(f), - None => unreachable!(), - }, + None => f.write_str(""), + } } } } diff --git a/ion/src/format/function.rs b/ion/src/format/function.rs index 197356a5..6013c1b9 100644 --- a/ion/src/format/function.rs +++ b/ion/src/format/function.rs @@ -13,7 +13,7 @@ use indent::indent_by; use crate::{Context, Function}; use crate::format::Config; -/// Formats a [JavaScript Function](Function) as a string, using the given [configuration](Config). +/// Formats a [function](Function), using the given [configuration](Config). /// /// ### Format /// ```js @@ -25,6 +25,7 @@ pub fn format_function<'cx>(cx: &'cx Context, cfg: Config, function: &'cx Functi FunctionDisplay { cx, function, cfg } } +#[must_use] pub struct FunctionDisplay<'cx> { cx: &'cx Context, function: &'cx Function<'cx>, diff --git a/ion/src/format/key.rs b/ion/src/format/key.rs index cd34983c..1f1f17ba 100644 --- a/ion/src/format/key.rs +++ b/ion/src/format/key.rs @@ -19,6 +19,7 @@ pub fn format_key<'cx>(cx: &'cx Context, cfg: Config, key: &'cx OwnedKey<'cx>) - KeyDisplay { cx, cfg, key } } +#[must_use] pub struct KeyDisplay<'cx> { cx: &'cx Context, cfg: Config, diff --git a/ion/src/format/mod.rs b/ion/src/format/mod.rs index dfe7242c..1194c6ae 100644 --- a/ion/src/format/mod.rs +++ b/ion/src/format/mod.rs @@ -4,8 +4,10 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +use std::borrow::Cow; use std::fmt; use std::fmt::{Display, Formatter}; +use std::str; pub use config::Config; @@ -31,11 +33,27 @@ pub mod typedarray; pub const INDENT: &str = " "; pub const NEWLINE: &str = "\n"; -/// Formats a [JavaScript Value](Value) as a string with the given [configuration](Config). +#[must_use] +pub fn indent_str(indentation: usize) -> Cow<'static, str> { + const MAX_INDENTS: usize = 128; + static INDENTS: &str = match str::from_utf8(&[b' '; MAX_INDENTS * 2]) { + Ok(indents) => indents, + _ => unreachable!(), + }; + + if indentation <= 128 { + Cow::Borrowed(&INDENTS[0..indentation * INDENT.len()]) + } else { + Cow::Owned(INDENT.repeat(indentation)) + } +} + +/// Formats a [JavaScript Value](Value) with the given [configuration](Config). pub fn format_value<'cx>(cx: &'cx Context, cfg: Config, value: &'cx Value<'cx>) -> ValueDisplay<'cx> { ValueDisplay { cx, value, cfg } } +#[must_use] pub struct ValueDisplay<'cx> { cx: &'cx Context, value: &'cx Value<'cx>, diff --git a/ion/src/format/object.rs b/ion/src/format/object.rs index 8abe4756..4e31afb2 100644 --- a/ion/src/format/object.rs +++ b/ion/src/format/object.rs @@ -14,7 +14,7 @@ use mozjs::jsapi::{ESClass, Type}; use crate::{Array, Context, Date, Exception, Function, Object, Promise, PropertyDescriptor, PropertyKey, RegExp}; use crate::conversions::ToValue; -use crate::format::{INDENT, NEWLINE}; +use crate::format::{indent_str, NEWLINE}; use crate::format::array::format_array; use crate::format::boxed::format_boxed; use crate::format::class::format_class_object; @@ -31,12 +31,13 @@ use crate::typedarray::{ Uint16Array, Uint32Array, Uint8Array, }; -/// Formats a [JavaScript Object](Object), depending on its class, as a string using the given [configuration](Config). +/// Formats a [JavaScript Object](Object), depending on its class, using the given [configuration](Config). /// The object is passed to more specific formatting functions, such as [format_array] and [format_date]. pub fn format_object<'cx>(cx: &'cx Context, cfg: Config, object: Object<'cx>) -> ObjectDisplay<'cx> { ObjectDisplay { cx, object, cfg } } +#[must_use] pub struct ObjectDisplay<'cx> { cx: &'cx Context, object: Object<'cx>, @@ -62,10 +63,10 @@ impl Display for ObjectDisplay<'_> { ESC::Function => format_function(cx, cfg, &Function::from_object(cx, &self.object).unwrap()).fmt(f), ESC::ArrayBuffer => format_array_buffer(cfg, &ArrayBuffer::from(object.into_local()).unwrap()).fmt(f), ESC::Error => match Exception::from_object(cx, &self.object) { - Exception::Error(error) => f.write_str(&error.format()), + Exception::Error(error) => error.format().fmt(f), _ => unreachable!("Expected Error"), }, - ESC::Object => format_plain_object(cx, cfg, &Object::from(object.into_local())).fmt(f), + ESC::Object => format_plain_object(cx, cfg, &self.object).fmt(f), ESC::Other => { if let Some(view) = ArrayBufferView::from(cx.root_object(object.handle().get())) { 'view: { @@ -102,20 +103,18 @@ impl Display for ObjectDisplay<'_> { format_class_object(cx, cfg, &self.object).fmt(f) } - _ => { - let source = self.object.as_value(cx).to_source(cx).to_owned(cx); - f.write_str(&source) - } + _ => self.object.as_value(cx).to_source(cx).to_owned(cx).fmt(f), } } } -/// Formats a [JavaScript Object](Object) as a string using the given [configuration](Config). +/// Formats a [JavaScript Object](Object) using the given [configuration](Config). /// Disregards the class of the object. pub fn format_plain_object<'cx>(cx: &'cx Context, cfg: Config, object: &'cx Object<'cx>) -> PlainObjectDisplay<'cx> { PlainObjectDisplay { cx, object, cfg } } +#[must_use] pub struct PlainObjectDisplay<'cx> { cx: &'cx Context, object: &'cx Object<'cx>, @@ -137,24 +136,24 @@ impl Display for PlainObjectDisplay<'_> { if self.cfg.multiline { f.write_str(NEWLINE)?; - let inner = INDENT.repeat((self.cfg.indentation + self.cfg.depth + 1) as usize); + let inner = indent_str((self.cfg.indentation + self.cfg.depth + 1) as usize); for key in keys { - f.write_str(&inner)?; + inner.fmt(f)?; let desc = self.object.get_descriptor(self.cx, &key).unwrap(); - write_key_descriptor(f, self.cx, self.cfg, &key, &desc)?; + write_key_descriptor(f, self.cx, self.cfg, &key, &desc, self.object)?; ",".color(colour).fmt(f)?; f.write_str(NEWLINE)?; } - f.write_str(&INDENT.repeat((self.cfg.indentation + self.cfg.depth) as usize))?; + indent_str((self.cfg.indentation + self.cfg.depth) as usize).fmt(f)?; } else { f.write_char(' ')?; let len = length.clamp(0, 3); for (i, key) in keys.enumerate() { let desc = self.object.get_descriptor(self.cx, &key).unwrap(); - write_key_descriptor(f, self.cx, self.cfg, &key, &desc)?; + write_key_descriptor(f, self.cx, self.cfg, &key, &desc, self.object)?; if i != len - 1 { ",".color(colour).fmt(f)?; @@ -175,11 +174,11 @@ impl Display for PlainObjectDisplay<'_> { } fn write_key_descriptor( - f: &mut Formatter, cx: &Context, cfg: Config, key: &PropertyKey, desc: &PropertyDescriptor, + f: &mut Formatter, cx: &Context, cfg: Config, key: &PropertyKey, desc: &PropertyDescriptor, object: &Object, ) -> fmt::Result { format_key(cx, cfg, &key.to_owned_key(cx)).fmt(f)?; ": ".color(cfg.colours.object).fmt(f)?; - format_descriptor(cx, cfg, desc).fmt(f) + format_descriptor(cx, cfg, desc, Some(object)).fmt(f) } pub(crate) fn write_remaining(f: &mut Formatter, remaining: usize, inner: Option<&str>, colour: Color) -> fmt::Result { diff --git a/ion/src/format/primitive.rs b/ion/src/format/primitive.rs index 8ef9ff55..3799a93b 100644 --- a/ion/src/format/primitive.rs +++ b/ion/src/format/primitive.rs @@ -16,12 +16,13 @@ use crate::conversions::FromValue; use crate::format::Config; use crate::format::symbol::format_symbol; -/// Formats a primitive value as a string using the given [configuration](Config). +/// Formats a primitive value using the given [configuration](Config). /// The supported types are `boolean`, `number`, `string`, `symbol`, `null` and `undefined`. pub fn format_primitive<'cx>(cx: &'cx Context, cfg: Config, value: &'cx Value<'cx>) -> PrimitiveDisplay<'cx> { PrimitiveDisplay { cx, value, cfg } } +#[must_use] pub struct PrimitiveDisplay<'cx> { cx: &'cx Context, value: &'cx Value<'cx>, @@ -54,7 +55,7 @@ impl Display for PrimitiveDisplay<'_> { if self.cfg.quoted { write!(f, "{0}{1}{0}", r#"""#.color(colours.string), str.color(colours.string)) } else { - f.write_str(&str) + str.fmt(f) } } else if value.is_null() { "null".color(colours.null).fmt(f) diff --git a/ion/src/format/promise.rs b/ion/src/format/promise.rs index e3e25702..3cfef1da 100644 --- a/ion/src/format/promise.rs +++ b/ion/src/format/promise.rs @@ -11,9 +11,9 @@ use colored::Colorize; use mozjs::jsapi::PromiseState; use crate::{Context, Promise}; -use crate::format::{Config, format_value, INDENT}; +use crate::format::{Config, format_value, indent_str}; -/// Formats a [Promise] as a string with the given [configuration](Config). +/// Formats a [Promise] with the given [configuration](Config). /// ### Format /// ```js /// Promise { <#state> <#result> } @@ -22,6 +22,7 @@ pub fn format_promise<'cx>(cx: &'cx Context, cfg: Config, promise: &'cx Promise) PromiseDisplay { cx, promise, cfg } } +#[must_use] pub struct PromiseDisplay<'cx> { cx: &'cx Context, promise: &'cx Promise<'cx>, @@ -46,7 +47,7 @@ impl Display for PromiseDisplay<'_> { let result = format_value(self.cx, self.cfg.depth(self.cfg.depth + 1), &result); f.write_char('\n')?; - f.write_str(&INDENT.repeat((self.cfg.indentation + self.cfg.depth + 1) as usize))?; + indent_str((self.cfg.indentation + self.cfg.depth + 1) as usize).fmt(f)?; state.fmt(f)?; f.write_char(' ')?; result.fmt(f)?; diff --git a/ion/src/format/regexp.rs b/ion/src/format/regexp.rs index 438dfa17..f3f821d4 100644 --- a/ion/src/format/regexp.rs +++ b/ion/src/format/regexp.rs @@ -13,11 +13,12 @@ use crate::Context; use crate::format::Config; use crate::object::RegExp; -/// Formats a [RegExp object](RegExp) as a string using the given [configuration](Config). +/// Formats a [RegExp object](RegExp) using the given [configuration](Config). pub fn format_regexp<'cx>(cx: &'cx Context, cfg: Config, regexp: &'cx RegExp<'cx>) -> RegExpDisplay<'cx> { RegExpDisplay { cx, regexp, cfg } } +#[must_use] pub struct RegExpDisplay<'cx> { cx: &'cx Context, regexp: &'cx RegExp<'cx>, diff --git a/ion/src/format/symbol.rs b/ion/src/format/symbol.rs index 15dfd253..a3cd2e7c 100644 --- a/ion/src/format/symbol.rs +++ b/ion/src/format/symbol.rs @@ -24,6 +24,7 @@ pub fn format_symbol<'cx>(cx: &'cx Context, cfg: Config, symbol: &'cx Symbol<'cx SymbolDisplay { cx, symbol, cfg } } +#[must_use] pub struct SymbolDisplay<'cx> { cx: &'cx Context, symbol: &'cx Symbol<'cx>, diff --git a/ion/src/format/typedarray.rs b/ion/src/format/typedarray.rs index 8f2bb5d0..e6b2e8e2 100644 --- a/ion/src/format/typedarray.rs +++ b/ion/src/format/typedarray.rs @@ -10,13 +10,14 @@ use std::fmt::{Display, Formatter, Write}; use colored::Colorize; use itoa::Buffer; -use crate::format::{Config, INDENT, NEWLINE}; +use crate::format::{Config, indent_str, NEWLINE}; use crate::typedarray::{ArrayBuffer, TypedArray, TypedArrayElement}; pub fn format_array_buffer<'cx>(cfg: Config, buffer: &'cx ArrayBuffer<'cx>) -> ArrayBufferDisplay<'cx> { ArrayBufferDisplay { buffer, cfg } } +#[must_use] pub struct ArrayBufferDisplay<'cx> { buffer: &'cx ArrayBuffer<'cx>, cfg: Config, @@ -35,12 +36,12 @@ impl Display for ArrayBufferDisplay<'_> { unsafe { self.buffer.as_slice() } }; - let indent = INDENT.repeat((self.cfg.indentation + self.cfg.depth + 1) as usize); + let indent = indent_str((self.cfg.indentation + self.cfg.depth + 1) as usize); if bytes.len() < 8 { f.write_char(' ')?; } else { f.write_str(NEWLINE)?; - f.write_str(&indent)?; + indent.fmt(f)?; } write!( @@ -65,7 +66,7 @@ impl Display for ArrayBufferDisplay<'_> { f.write_char(' ')?; } else { f.write_str(NEWLINE)?; - f.write_str(&indent)?; + indent.fmt(f)?; } write!(f, "byteLength: {}", bytes.len())?; @@ -123,9 +124,9 @@ where ") [".color(colour) )?; - let indent = INDENT.repeat((self.cfg.indentation + self.cfg.depth + 1) as usize); + let indent = indent_str((self.cfg.indentation + self.cfg.depth + 1) as usize); f.write_str(NEWLINE)?; - f.write_str(&indent)?; + indent.fmt(f)?; for (i, element) in elements.iter().enumerate() { element.to_string().color(self.cfg.colours.number).fmt(f)?; @@ -135,7 +136,7 @@ where f.write_char(' ')?; if (i + 1) % 16 == 0 { f.write_str(NEWLINE)?; - f.write_str(&indent)?; + indent.fmt(f)?; } } } diff --git a/ion/src/object/array.rs b/ion/src/object/array.rs index c6d74e7c..a92736c6 100644 --- a/ion/src/object/array.rs +++ b/ion/src/object/array.rs @@ -173,6 +173,10 @@ impl<'a> Array<'a> { unsafe { IsArray(cx.as_ptr(), object.handle().into(), &mut is_array) && is_array } } + pub fn as_object(&self) -> &Object<'a> { + &self.arr + } + pub fn into_local(self) -> Local<'a, *mut JSObject> { self.arr.into_local() } diff --git a/ion/src/object/descriptor.rs b/ion/src/object/descriptor.rs index 3da0c7cf..3f63dcfb 100644 --- a/ion/src/object/descriptor.rs +++ b/ion/src/object/descriptor.rs @@ -7,7 +7,7 @@ use std::ops::{Deref, DerefMut}; use mozjs::glue::{SetAccessorPropertyDescriptor, SetDataPropertyDescriptor}; -use mozjs::jsapi::{FromPropertyDescriptor, ObjectToCompletePropertyDescriptor}; +use mozjs::jsapi::{FromPropertyDescriptor, JS_GetObjectFunction, ObjectToCompletePropertyDescriptor}; use mozjs::jsapi::PropertyDescriptor as JSPropertyDescriptor; use crate::{Context, Function, Local, Object, Value}; @@ -81,17 +81,21 @@ impl<'pd> PropertyDescriptor<'pd> { self.handle().resolving_() } - pub fn getter<'cx>(&self, cx: &'cx Context) -> Option> { + pub fn getter<'cx>(&self, cx: &'cx Context) -> Option> { if self.handle().hasGetter_() && !self.handle().getter_.is_null() { - Some(Object::from(cx.root_object(self.handle().getter_))) + Some(Function::from( + cx.root_function(unsafe { JS_GetObjectFunction(self.handle().getter_) }), + )) } else { None } } - pub fn setter<'cx>(&self, cx: &'cx Context) -> Option> { + pub fn setter<'cx>(&self, cx: &'cx Context) -> Option> { if self.handle().hasSetter_() && !self.handle().setter_.is_null() { - Some(Object::from(cx.root_object(self.handle().setter_))) + Some(Function::from( + cx.root_function(unsafe { JS_GetObjectFunction(self.handle().setter_) }), + )) } else { None } diff --git a/runtime/src/globals/console.rs b/runtime/src/globals/console.rs index f92670e6..8eba85d6 100644 --- a/runtime/src/globals/console.rs +++ b/runtime/src/globals/console.rs @@ -18,7 +18,7 @@ use term_table::table_cell::{Alignment, TableCell}; use ion::{Context, Object, OwnedKey, Stack, Value}; use ion::conversions::FromValue; use ion::flags::PropertyFlags; -use ion::format::{format_value, INDENT}; +use ion::format::{format_value, indent_str}; use ion::format::Config as FormatConfig; use ion::format::key::format_key; use ion::format::primitive::format_primitive; @@ -40,11 +40,11 @@ thread_local! { } fn print_indent(is_stderr: bool) { - let indents = INDENTS.get(); + let indentation = INDENTS.get() as usize; if !is_stderr { - print!("{}", INDENT.repeat(indents as usize)); + print!("{}", indent_str(indentation)); } else { - eprint!("{}", INDENT.repeat(indents as usize)); + eprint!("{}", indent_str(indentation)); } }