mirror of
https://github.com/oxc-project/oxc.git
synced 2026-11-06 17:34:25 +00:00
feat(ecmascript): improve may_have_side_effects for instanceof (#9365)
This commit is contained in:
2 files changed
+77
-7
No files matched your search
@@ -1,7 +1,8 @@
|
||||
use oxc_ast::ast::*;
|
||||
|
||||
use crate::{
|
||||
is_global_reference::IsGlobalReference, to_numeric::ToNumeric, to_primitive::ToPrimitive,
|
||||
constant_evaluation::DetermineValueType, is_global_reference::IsGlobalReference,
|
||||
to_numeric::ToNumeric, to_primitive::ToPrimitive,
|
||||
};
|
||||
|
||||
/// Returns true if subtree changes application state.
|
||||
@@ -121,8 +122,26 @@ impl MayHaveSideEffects for BinaryExpression<'_> {
|
||||
self.left.may_have_side_effects(is_global_reference)
|
||||
|| self.right.may_have_side_effects(is_global_reference)
|
||||
}
|
||||
BinaryOperator::In | BinaryOperator::Instanceof => {
|
||||
// instanceof and in can throw `TypeError`
|
||||
BinaryOperator::Instanceof => {
|
||||
// When the following conditions are met, instanceof won't throw `TypeError`.
|
||||
// - the right hand side is a known global reference which is a function
|
||||
// - the left hand side is not a proxy
|
||||
if let Expression::Identifier(right_ident) = &self.right {
|
||||
let name = right_ident.name.as_str();
|
||||
// Any known global non-constructor functions can be allowed here.
|
||||
// But because non-constructor functions are not likely to be used, we ignore them.
|
||||
if is_known_global_constructor(name)
|
||||
&& is_global_reference.is_global_reference(right_ident) == Some(true)
|
||||
&& !self.left.value_type(is_global_reference).is_undetermined()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
// instanceof can throw `TypeError`
|
||||
true
|
||||
}
|
||||
BinaryOperator::In => {
|
||||
// in can throw `TypeError`
|
||||
true
|
||||
}
|
||||
BinaryOperator::Addition => {
|
||||
@@ -200,6 +219,56 @@ impl MayHaveSideEffects for BinaryExpression<'_> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Whether the name matches any known global constructors.
|
||||
///
|
||||
/// <https://tc39.es/ecma262/multipage/global-object.html#sec-constructor-properties-of-the-global-object>
|
||||
fn is_known_global_constructor(name: &str) -> bool {
|
||||
// technically, we need to exclude the constructors that are not supported by the target
|
||||
matches!(
|
||||
name,
|
||||
"AggregateError"
|
||||
| "Array"
|
||||
| "ArrayBuffer"
|
||||
| "BigInt"
|
||||
| "BigInt64Array"
|
||||
| "BitUint64Array"
|
||||
| "Boolean"
|
||||
| "DataView"
|
||||
| "Date"
|
||||
| "Error"
|
||||
| "EvalError"
|
||||
| "FinalizationRegistry"
|
||||
| "Float32Array"
|
||||
| "Float64Array"
|
||||
| "Function"
|
||||
| "Int8Array"
|
||||
| "Int16Array"
|
||||
| "Int32Array"
|
||||
| "Iterator"
|
||||
| "Map"
|
||||
| "Number"
|
||||
| "Object"
|
||||
| "Promise"
|
||||
| "Proxy"
|
||||
| "RangeError"
|
||||
| "ReferenceError"
|
||||
| "RegExp"
|
||||
| "Set"
|
||||
| "SharedArrayBuffer"
|
||||
| "String"
|
||||
| "Symbol"
|
||||
| "SyntaxError"
|
||||
| "TypeError"
|
||||
| "Uint8Array"
|
||||
| "Uint8ClampedArray"
|
||||
| "Uint16Array"
|
||||
| "Uint32Array"
|
||||
| "URIError"
|
||||
| "WeakMap"
|
||||
| "WeakSet"
|
||||
)
|
||||
}
|
||||
|
||||
impl MayHaveSideEffects for LogicalExpression<'_> {
|
||||
fn may_have_side_effects(&self, is_global_reference: &impl IsGlobalReference) -> bool {
|
||||
self.left.may_have_side_effects(is_global_reference)
|
||||
|
||||
@@ -526,13 +526,14 @@ fn test_binary_expressions() {
|
||||
test("1n % 0n", true); // `%` throws an error when the right operand is zero
|
||||
test("0n >>> 1n", true); // `>>>` throws an error even when both operands are bigint
|
||||
|
||||
test("[] instanceof 1", true); // throws an error
|
||||
test("[] instanceof { [Symbol.hasInstance]() { throw 'foo' } }", true);
|
||||
test_with_global_variables("[] instanceof Object", vec!["Object".to_string()], false);
|
||||
test_with_global_variables("a instanceof Object", vec!["Object".to_string()], true); // a maybe a proxy that has a side effectful "getPrototypeOf" trap
|
||||
|
||||
// b maybe not a object
|
||||
// b maybe a proxy that has a side effectful "has" trap
|
||||
test("a in b", true);
|
||||
// b maybe not a function
|
||||
// b[Symbol.hasInstance] may have a side effect
|
||||
// a maybe a proxy that has a side effectful "getPrototypeOf" trap
|
||||
test("a instanceof b", true);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
Reference in new issue
Block a user