Fix bug on OOP-style calling of exported map object.
This commit is contained in:
parent
ff2a23189a
commit
76e6c1b9e4
@ -15,6 +15,7 @@ Bug fixes
|
|||||||
* Shadowed variable exports are now handled correctly.
|
* Shadowed variable exports are now handled correctly.
|
||||||
* Shadowed constant definitions are now optimized correctly when propagated (e.g. `const X = 1; const X = 1 + 1 + 1; X` now evaluates to 3 instead of 0).
|
* Shadowed constant definitions are now optimized correctly when propagated (e.g. `const X = 1; const X = 1 + 1 + 1; X` now evaluates to 3 instead of 0).
|
||||||
* Identifiers and comma's in the middle of custom syntax now register correctly.
|
* Identifiers and comma's in the middle of custom syntax now register correctly.
|
||||||
|
* Exporting an object map from a module with closures defined on properties now works correctly when those properties are called to mimic method calls in OOP-style.
|
||||||
|
|
||||||
New features
|
New features
|
||||||
------------
|
------------
|
||||||
|
123
src/func/call.rs
123
src/func/call.rs
@ -768,24 +768,26 @@ impl Engine {
|
|||||||
|
|
||||||
// Linked to scripted function?
|
// Linked to scripted function?
|
||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
if let Some(fn_def) = fn_ptr.fn_def() {
|
let fn_def = fn_ptr.fn_def();
|
||||||
if fn_def.params.len() == args.len() {
|
#[cfg(feature = "no_function")]
|
||||||
return self
|
let fn_def = ();
|
||||||
|
|
||||||
|
match fn_def {
|
||||||
|
#[cfg(not(feature = "no_function"))]
|
||||||
|
Some(fn_def) if fn_def.params.len() == args.len() => self
|
||||||
.call_script_fn(
|
.call_script_fn(
|
||||||
global,
|
global,
|
||||||
caches,
|
caches,
|
||||||
&mut Scope::new(),
|
&mut Scope::new(),
|
||||||
None,
|
None,
|
||||||
fn_ptr.encapsulated_environ(),
|
fn_ptr.encapsulated_environ().map(|r| r.as_ref()),
|
||||||
fn_def,
|
fn_def,
|
||||||
args,
|
args,
|
||||||
true,
|
true,
|
||||||
fn_call_pos,
|
fn_call_pos,
|
||||||
)
|
)
|
||||||
.map(|v| (v, false));
|
.map(|v| (v, false)),
|
||||||
}
|
_ => {
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
let is_anon = fn_ptr.is_anonymous();
|
let is_anon = fn_ptr.is_anonymous();
|
||||||
#[cfg(feature = "no_function")]
|
#[cfg(feature = "no_function")]
|
||||||
@ -814,6 +816,8 @@ impl Engine {
|
|||||||
fn_call_pos,
|
fn_call_pos,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Handle obj.call(fn_ptr, ...)
|
// Handle obj.call(fn_ptr, ...)
|
||||||
KEYWORD_FN_PTR_CALL => {
|
KEYWORD_FN_PTR_CALL => {
|
||||||
@ -833,16 +837,27 @@ impl Engine {
|
|||||||
// FnPtr call on object
|
// FnPtr call on object
|
||||||
let fn_ptr = call_args[0].take().cast::<FnPtr>();
|
let fn_ptr = call_args[0].take().cast::<FnPtr>();
|
||||||
|
|
||||||
#[cfg(not(feature = "no_function"))]
|
|
||||||
let (fn_name, is_anon, fn_curry, _environ, fn_def) = {
|
let (fn_name, is_anon, fn_curry, _environ, fn_def) = {
|
||||||
|
#[cfg(not(feature = "no_function"))]
|
||||||
let is_anon = fn_ptr.is_anonymous();
|
let is_anon = fn_ptr.is_anonymous();
|
||||||
let (fn_name, fn_curry, environ, fn_def) = fn_ptr.take_data();
|
|
||||||
(fn_name, is_anon, fn_curry, environ, fn_def)
|
|
||||||
};
|
|
||||||
#[cfg(feature = "no_function")]
|
#[cfg(feature = "no_function")]
|
||||||
let (fn_name, is_anon, fn_curry, _environ) = {
|
let is_anon = false;
|
||||||
|
|
||||||
|
#[cfg(not(feature = "no_function"))]
|
||||||
|
let (fn_name, fn_curry, environ, fn_def) = fn_ptr.take_data();
|
||||||
|
#[cfg(feature = "no_function")]
|
||||||
let (fn_name, fn_curry, environ) = fn_ptr.take_data();
|
let (fn_name, fn_curry, environ) = fn_ptr.take_data();
|
||||||
(fn_name, false, fn_curry, environ)
|
|
||||||
|
(
|
||||||
|
fn_name,
|
||||||
|
is_anon,
|
||||||
|
fn_curry,
|
||||||
|
environ,
|
||||||
|
#[cfg(not(feature = "no_function"))]
|
||||||
|
fn_def,
|
||||||
|
#[cfg(feature = "no_function")]
|
||||||
|
(),
|
||||||
|
)
|
||||||
};
|
};
|
||||||
|
|
||||||
// Replace the first argument with the object pointer, adding the curried arguments
|
// Replace the first argument with the object pointer, adding the curried arguments
|
||||||
@ -855,15 +870,14 @@ impl Engine {
|
|||||||
args.extend(call_args.iter_mut());
|
args.extend(call_args.iter_mut());
|
||||||
|
|
||||||
// Linked to scripted function?
|
// Linked to scripted function?
|
||||||
|
match fn_def {
|
||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
if let Some(fn_def) = fn_def {
|
Some(fn_def) if fn_def.params.len() == args.len() => {
|
||||||
if fn_def.params.len() == args.len() {
|
|
||||||
// Check for data race.
|
// Check for data race.
|
||||||
#[cfg(not(feature = "no_closure"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
ensure_no_data_race(&fn_def.name, args, false)?;
|
ensure_no_data_race(&fn_def.name, args, false)?;
|
||||||
|
|
||||||
return self
|
self.call_script_fn(
|
||||||
.call_script_fn(
|
|
||||||
global,
|
global,
|
||||||
caches,
|
caches,
|
||||||
&mut Scope::new(),
|
&mut Scope::new(),
|
||||||
@ -874,17 +888,20 @@ impl Engine {
|
|||||||
true,
|
true,
|
||||||
fn_call_pos,
|
fn_call_pos,
|
||||||
)
|
)
|
||||||
.map(|v| (v, false));
|
.map(|v| (v, false))
|
||||||
}
|
}
|
||||||
}
|
_ => {
|
||||||
|
|
||||||
// Add the first argument with the object pointer
|
// Add the first argument with the object pointer
|
||||||
args.insert(0, target.as_mut());
|
args.insert(0, target.as_mut());
|
||||||
|
|
||||||
// Recalculate hash
|
// Recalculate hash
|
||||||
let new_hash = match is_anon {
|
let new_hash = match is_anon {
|
||||||
false if !is_valid_function_name(&fn_name) => {
|
false if !is_valid_function_name(&fn_name) => {
|
||||||
FnCallHashes::from_native_only(calc_fn_hash(None, &fn_name, args.len()))
|
FnCallHashes::from_native_only(calc_fn_hash(
|
||||||
|
None,
|
||||||
|
&fn_name,
|
||||||
|
args.len(),
|
||||||
|
))
|
||||||
}
|
}
|
||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
_ => FnCallHashes::from_script_and_native(
|
_ => FnCallHashes::from_script_and_native(
|
||||||
@ -892,7 +909,11 @@ impl Engine {
|
|||||||
calc_fn_hash(None, &fn_name, args.len()),
|
calc_fn_hash(None, &fn_name, args.len()),
|
||||||
),
|
),
|
||||||
#[cfg(feature = "no_function")]
|
#[cfg(feature = "no_function")]
|
||||||
_ => FnCallHashes::from_native_only(calc_fn_hash(None, &fn_name, args.len())),
|
_ => FnCallHashes::from_native_only(calc_fn_hash(
|
||||||
|
None,
|
||||||
|
&fn_name,
|
||||||
|
args.len(),
|
||||||
|
)),
|
||||||
};
|
};
|
||||||
|
|
||||||
// Map it to name(args) in function-call style
|
// Map it to name(args) in function-call style
|
||||||
@ -909,6 +930,8 @@ impl Engine {
|
|||||||
fn_call_pos,
|
fn_call_pos,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
KEYWORD_FN_PTR_CURRY => {
|
KEYWORD_FN_PTR_CURRY => {
|
||||||
if !target.is_fnptr() {
|
if !target.is_fnptr() {
|
||||||
return Err(self.make_type_mismatch_err::<FnPtr>(
|
return Err(self.make_type_mismatch_err::<FnPtr>(
|
||||||
@ -936,19 +959,16 @@ impl Engine {
|
|||||||
_ => {
|
_ => {
|
||||||
let mut fn_name = fn_name;
|
let mut fn_name = fn_name;
|
||||||
let _redirected;
|
let _redirected;
|
||||||
|
let mut _linked = None;
|
||||||
let mut _arg_values: FnArgsVec<_>;
|
let mut _arg_values: FnArgsVec<_>;
|
||||||
let mut call_args = call_args;
|
let mut call_args = call_args;
|
||||||
|
|
||||||
// Check if it is a map method call in OOP style
|
// Check if it is a map method call in OOP style
|
||||||
|
|
||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
if let Some(map) = target.read_lock::<crate::Map>() {
|
if let Some(map) = target.read_lock::<crate::Map>() {
|
||||||
if let Some(val) = map.get(fn_name) {
|
if let Some(val) = map.get(fn_name) {
|
||||||
if let Some(fn_ptr) = val.read_lock::<FnPtr>() {
|
if let Some(fn_ptr) = val.read_lock::<FnPtr>() {
|
||||||
#[cfg(not(feature = "no_function"))]
|
|
||||||
let is_anon = fn_ptr.is_anonymous();
|
|
||||||
#[cfg(feature = "no_function")]
|
|
||||||
let is_anon = false;
|
|
||||||
|
|
||||||
// Remap the function name
|
// Remap the function name
|
||||||
_redirected = fn_ptr.fn_name_raw().clone();
|
_redirected = fn_ptr.fn_name_raw().clone();
|
||||||
fn_name = &_redirected;
|
fn_name = &_redirected;
|
||||||
@ -962,6 +982,27 @@ impl Engine {
|
|||||||
.collect();
|
.collect();
|
||||||
call_args = &mut _arg_values;
|
call_args = &mut _arg_values;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Linked to scripted function?
|
||||||
|
#[cfg(not(feature = "no_function"))]
|
||||||
|
let fn_def = fn_ptr.fn_def();
|
||||||
|
#[cfg(feature = "no_function")]
|
||||||
|
let fn_def = ();
|
||||||
|
|
||||||
|
match fn_def {
|
||||||
|
#[cfg(not(feature = "no_function"))]
|
||||||
|
Some(fn_def) if fn_def.params.len() == call_args.len() => {
|
||||||
|
_linked = Some((
|
||||||
|
fn_def.clone(),
|
||||||
|
fn_ptr.encapsulated_environ().cloned(),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
#[cfg(not(feature = "no_function"))]
|
||||||
|
let is_anon = fn_ptr.is_anonymous();
|
||||||
|
#[cfg(feature = "no_function")]
|
||||||
|
let is_anon = false;
|
||||||
|
|
||||||
// Recalculate the hash based on the new function name and new arguments
|
// Recalculate the hash based on the new function name and new arguments
|
||||||
let args_len = call_args.len() + 1;
|
let args_len = call_args.len() + 1;
|
||||||
hash = match is_anon {
|
hash = match is_anon {
|
||||||
@ -982,8 +1023,30 @@ impl Engine {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
match _linked {
|
||||||
|
#[cfg(not(feature = "no_function"))]
|
||||||
|
Some((fn_def, environ)) => {
|
||||||
|
// Linked to scripted function
|
||||||
|
self.call_script_fn(
|
||||||
|
global,
|
||||||
|
caches,
|
||||||
|
&mut Scope::new(),
|
||||||
|
Some(target),
|
||||||
|
environ.as_deref(),
|
||||||
|
&*fn_def,
|
||||||
|
&mut call_args.iter_mut().collect::<FnArgsVec<_>>(),
|
||||||
|
true,
|
||||||
|
fn_call_pos,
|
||||||
|
)
|
||||||
|
.map(|v| (v, false))
|
||||||
|
}
|
||||||
|
#[cfg(feature = "no_function")]
|
||||||
|
Some(()) => unreachable!(),
|
||||||
|
None => {
|
||||||
// Attached object pointer in front of the arguments
|
// Attached object pointer in front of the arguments
|
||||||
let mut args = FnArgsVec::with_capacity(call_args.len() + 1);
|
let mut args = FnArgsVec::with_capacity(call_args.len() + 1);
|
||||||
args.push(target.as_mut());
|
args.push(target.as_mut());
|
||||||
@ -1002,6 +1065,8 @@ impl Engine {
|
|||||||
fn_call_pos,
|
fn_call_pos,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}?;
|
}?;
|
||||||
|
|
||||||
// Propagate the changed value back to the source if necessary
|
// Propagate the changed value back to the source if necessary
|
||||||
|
@ -313,7 +313,7 @@ impl FnPtr {
|
|||||||
caches,
|
caches,
|
||||||
&mut crate::Scope::new(),
|
&mut crate::Scope::new(),
|
||||||
this_ptr,
|
this_ptr,
|
||||||
self.encapsulated_environ(),
|
self.encapsulated_environ().map(|r| r.as_ref()),
|
||||||
fn_def,
|
fn_def,
|
||||||
args,
|
args,
|
||||||
true,
|
true,
|
||||||
@ -334,8 +334,8 @@ impl FnPtr {
|
|||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub(crate) fn encapsulated_environ(&self) -> Option<&EncapsulatedEnviron> {
|
pub(crate) fn encapsulated_environ(&self) -> Option<&Shared<EncapsulatedEnviron>> {
|
||||||
self.environ.as_deref()
|
self.environ.as_ref()
|
||||||
}
|
}
|
||||||
/// Set a reference to the [encapsulated environment][EncapsulatedEnviron].
|
/// Set a reference to the [encapsulated environment][EncapsulatedEnviron].
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
@ -350,8 +350,8 @@ impl FnPtr {
|
|||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub(crate) fn fn_def(&self) -> Option<&crate::ast::ScriptFnDef> {
|
pub(crate) fn fn_def(&self) -> Option<&Shared<crate::ast::ScriptFnDef>> {
|
||||||
self.fn_def.as_deref()
|
self.fn_def.as_ref()
|
||||||
}
|
}
|
||||||
/// Set a reference to the linked [`ScriptFnDef`][crate::ast::ScriptFnDef].
|
/// Set a reference to the linked [`ScriptFnDef`][crate::ast::ScriptFnDef].
|
||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
|
Loading…
Reference in New Issue
Block a user