feat(es/compat): Support iterator protocol on loose mode (#6034)

This commit is contained in:
Donny/강동윤 2022-10-04 21:42:39 +09:00 committed by GitHub
parent 6749e6948e
commit 80da0981bd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 312 additions and 16 deletions

View File

@ -291,7 +291,8 @@ impl<'a, 'b, P: swc_ecma_visit::Fold> PassBuilder<'a, 'b, P> {
loose: self.loose
},
for_of: compat::es2015::for_of::Config {
assume_array: self.loose
assume_array: false,
loose: self.loose
},
spread: compat::es2015::spread::Config { loose: self.loose },
destructuring: compat::es2015::destructuring::Config {

View File

@ -0,0 +1,64 @@
{
"jsc": {
"parser": {
"syntax": "ecmascript",
"jsx": false
},
"target": "es5",
"loose": true,
"minify": {
"compress": {
"arguments": false,
"arrows": true,
"booleans": true,
"booleans_as_integers": false,
"collapse_vars": true,
"comparisons": true,
"computed_props": true,
"conditionals": true,
"dead_code": true,
"directives": false,
"drop_console": false,
"drop_debugger": true,
"evaluate": true,
"expression": false,
"hoist_funs": false,
"hoist_props": true,
"hoist_vars": false,
"if_return": true,
"join_vars": true,
"keep_classnames": false,
"keep_fargs": true,
"keep_fnames": false,
"keep_infinity": false,
"loops": true,
"negate_iife": true,
"properties": true,
"reduce_funcs": false,
"reduce_vars": false,
"side_effects": true,
"switches": true,
"typeofs": true,
"unsafe": false,
"unsafe_arrows": false,
"unsafe_comps": false,
"unsafe_Function": false,
"unsafe_math": false,
"unsafe_symbols": false,
"unsafe_methods": false,
"unsafe_proto": false,
"unsafe_regexp": false,
"unsafe_undefined": false,
"unused": true,
"const_to_let": true,
"pristine_globals": true
},
"mangle": false
}
},
"module": {
"type": "commonjs"
},
"minify": false,
"isModule": false
}

View File

@ -0,0 +1,4 @@
const m = new Map([[1, 2]])
for (const k of m.keys()) {
console.log(k)
}

View File

@ -1 +1 @@
export function test(list){var cur=list.findIndex(function(p){return 1==p});~cur||(cur=list.findIndex(function(p){return 0!==p}));for(var _i=0;_i<list.length;_i++)cur+=list[_i].value;return cur}
import _create_for_of_iterator_helper_loose from"@swc/helpers/src/_create_for_of_iterator_helper_loose.mjs";export function test(list){var cur=list.findIndex(function(p){return 1==p});~cur||(cur=list.findIndex(function(p){return 0!==p}));for(var _step,_iterator=_create_for_of_iterator_helper_loose(list);!(_step=_iterator()).done;)cur+=_step.value.value;return cur}

View File

@ -0,0 +1,11 @@
{
"jsc": {
"parser": {
"syntax": "ecmascript",
"jsx": false
},
"target": "es5",
"loose": true
},
"isModule": false
}

View File

@ -0,0 +1,4 @@
const m = new Map([[1, 2]])
for (const k of m.keys()) {
console.log(k)
}

View File

@ -0,0 +1,11 @@
import _create_for_of_iterator_helper_loose from "@swc/helpers/src/_create_for_of_iterator_helper_loose.mjs";
var m = new Map([
[
1,
2
]
]);
for(var _iterator = _create_for_of_iterator_helper_loose(m.keys()), _step; !(_step = _iterator()).done;){
var k = _step.value;
console.log(k);
}

View File

@ -246,6 +246,7 @@ where
pass,
ForOf,
es2015::for_of(es2015::for_of::Config {
loose,
assume_array: loose || assumptions.iterable_is_array
}),
true

View File

@ -0,0 +1,19 @@
function _createForOfIteratorHelperLoose(o, allowArrayLike) {
var it = typeof Symbol !== "undefined" && o[Symbol.iterator] || o["@@iterator"];
if (it) return (it = it.call(o)).next.bind(it);
// Fallback for engines without symbol support
if (
Array.isArray(o) ||
(it = unsupportedIterableToArray(o)) ||
(allowArrayLike && o && typeof o.length === "number")
) {
if (it) o = it;
var i = 0;
return function () {
if (i >= o.length) return { done: true };
return { done: false, value: o[i++] };
}
}
throw new TypeError("Invalid attempt to iterate non-iterable instance.\\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
}

View File

@ -356,6 +356,8 @@ define_helpers!(Helpers {
possible_constructor_return
),
create_for_of_iterator_helper_loose: (unsupported_iterable_to_array),
ts_decorate: (),
ts_generator: (),
ts_metadata: (),

View File

@ -4,7 +4,10 @@ use serde::Deserialize;
use swc_atoms::js_word;
use swc_common::{util::take::Take, Mark, Spanned, DUMMY_SP};
use swc_ecma_ast::*;
use swc_ecma_transforms_base::perf::{ParExplode, Parallel};
use swc_ecma_transforms_base::{
helper,
perf::{ParExplode, Parallel},
};
use swc_ecma_transforms_macros::parallel;
use swc_ecma_utils::{
alias_if_required, member_expr, prepend_stmt, private_ident, quote_ident, ExprFactory,
@ -57,6 +60,7 @@ pub fn for_of(c: Config) -> impl Fold + VisitMut {
#[derive(Debug, Clone, Copy, Default, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct Config {
pub loose: bool,
pub assume_array: bool,
}
@ -84,7 +88,7 @@ impl ForOf {
..
}: ForOfStmt,
) -> Stmt {
if right.is_array() || self.c.assume_array {
if right.is_array() || (self.c.assume_array && !self.c.loose) {
// Convert to normal for loop if rhs is array
//
// babel's output:
@ -199,6 +203,124 @@ impl ForOf {
};
}
// Loose mode
if self.c.loose {
let iterator = private_ident!("_iterator");
let step = private_ident!("_step");
let decls = vec![
VarDeclarator {
span: DUMMY_SP,
name: iterator.clone().into(),
init: Some(Box::new(Expr::Call(CallExpr {
span: DUMMY_SP,
callee: helper!(
create_for_of_iterator_helper_loose,
"createForOfIteratorHelperLoose"
),
args: vec![right.as_arg()],
type_args: Default::default(),
}))),
definite: Default::default(),
},
VarDeclarator {
span: DUMMY_SP,
name: step.clone().into(),
init: None,
definite: Default::default(),
},
];
let mut body = match *body {
Stmt::Block(b) => b,
_ => BlockStmt {
span: DUMMY_SP,
stmts: vec![*body],
},
};
match left {
VarDeclOrPat::VarDecl(var) => {
assert_eq!(
var.decls.len(),
1,
"Variable declarator of for of loop cannot contain multiple entries"
);
prepend_stmt(
&mut body.stmts,
VarDecl {
span: DUMMY_SP,
kind: var.kind,
declare: false,
decls: vec![VarDeclarator {
span: DUMMY_SP,
name: var.decls.into_iter().next().unwrap().name,
init: Some(step.clone().make_member(quote_ident!("value")).into()),
definite: false,
}],
}
.into(),
)
}
VarDeclOrPat::Pat(pat) => prepend_stmt(
&mut body.stmts,
AssignExpr {
span: DUMMY_SP,
left: pat.into(),
op: op!("="),
right: step.clone().make_member(quote_ident!("value")).into(),
}
.into_stmt(),
),
}
// !(_step = _iterator()).done;
let test = Box::new(Expr::Unary(UnaryExpr {
span: DUMMY_SP,
op: op!("!"),
arg: AssignExpr {
span: DUMMY_SP,
op: op!("="),
left: step.into(),
right: CallExpr {
span: DUMMY_SP,
callee: iterator.as_callee(),
args: vec![],
type_args: Default::default(),
}
.into(),
}
.make_member(quote_ident!("done"))
.into(),
}));
let stmt = Stmt::For(ForStmt {
span,
init: Some(
VarDecl {
span: DUMMY_SP,
kind: VarDeclKind::Var,
declare: false,
decls,
}
.into(),
),
test: Some(test),
update: None,
body: Box::new(Stmt::Block(body)),
});
return match label {
Some(label) => LabeledStmt {
span,
label,
body: Box::new(stmt),
}
.into(),
_ => stmt,
};
}
let var_span = left.span().apply_mark(Mark::fresh(Mark::root()));
let mut body = match *body {

View File

@ -293,7 +293,10 @@ try {
// for_of_as_array_for_of
test!(
syntax(),
|_| for_of(Config { assume_array: true }),
|_| for_of(Config {
assume_array: true,
..Default::default()
}),
for_of_as_array_for_of,
r#"
let elm;
@ -317,7 +320,10 @@ for(let _i = 0; _i < array.length; _i++){
// for_of_as_array_for_of_array_pattern
test!(
syntax(),
|_| for_of(Config { assume_array: true }),
|_| for_of(Config {
assume_array: true,
..Default::default()
}),
for_of_as_array_for_of_array_pattern,
r#"
let elm;
@ -340,7 +346,10 @@ for(let _i = 0; _i < array.length; _i++){
// regression_redeclare_array_8913
test!(
syntax(),
|_| for_of(Config { assume_array: true }),
|_| for_of(Config {
assume_array: true,
..Default::default()
}),
regression_redeclare_array_8913,
r#"
function f(...t) {
@ -364,7 +373,10 @@ function f(...t) {
// for_of_as_array_for_of_declaration_array_pattern
test!(
syntax(),
|_| for_of(Config { assume_array: true }),
|_| for_of(Config {
assume_array: true,
..Default::default()
}),
for_of_as_array_for_of_declaration_array_pattern,
r#"
for (const [elm] of array) {
@ -383,7 +395,10 @@ for(let _i = 0; _i < array.length; _i++){
// for_of_as_array_for_of_expression
test!(
syntax(),
|_| for_of(Config { assume_array: true }),
|_| for_of(Config {
assume_array: true,
..Default::default()
}),
for_of_as_array_for_of_expression,
r#"
let i;
@ -404,7 +419,10 @@ for(let _i = 0; _i < items.length; _i++){
// for_of_as_array_for_of_declaration
test!(
syntax(),
|_| for_of(Config { assume_array: true }),
|_| for_of(Config {
assume_array: true,
..Default::default()
}),
for_of_as_array_for_of_declaration,
r#"
for (const elm of array) {
@ -445,7 +463,10 @@ expect(results).toEqual([1, 2, 3]);
// for_of_as_array_for_of_static_declaration
test!(
syntax(),
|_| for_of(Config { assume_array: true }),
|_| for_of(Config {
assume_array: true,
..Default::default()
}),
for_of_as_array_for_of_static_declaration,
r#"
const array = [];
@ -469,7 +490,10 @@ for(let _i = 0; _i < array.length; _i++){
// for_of_as_array_for_of_static
test!(
syntax(),
|_| for_of(Config { assume_array: true }),
|_| for_of(Config {
assume_array: true,
..Default::default()
}),
for_of_as_array_for_of_static,
r#"
const array = [];
@ -495,7 +519,10 @@ for (let _i = 0; _i < array.length; _i++) {
// for_of_as_array_for_of_import_es2015
test!(
syntax(),
|_| for_of(Config { assume_array: true }),
|_| for_of(Config {
assume_array: true,
..Default::default()
}),
for_of_as_array_for_of_import_es2015,
r#"
import { array } from "foo";
@ -560,7 +587,10 @@ try {
// regression_if_label_3858
test!(
syntax(),
|_| for_of(Config { assume_array: true }),
|_| for_of(Config {
assume_array: true,
..Default::default()
}),
regression_if_label_3858,
r#"
if ( true )
@ -591,6 +621,7 @@ fn fixture(input: PathBuf) {
resolver(Mark::new(), top_level_mark, false),
for_of(Config {
assume_array: false,
..Default::default()
})
)
},

View File

@ -63,7 +63,10 @@ fn esm_to_amd(input: PathBuf) {
test!(
syntax(),
|t| chain!(
for_of(for_of::Config { assume_array: true }),
for_of(for_of::Config {
assume_array: true,
..Default::default()
}),
tr(Default::default(), false, t.comments.clone())
),
for_of_as_array_for_of_import_amd,

View File

@ -65,7 +65,10 @@ fn esm_to_cjs(input: PathBuf) {
test!(
syntax(),
|tester| chain!(
for_of(for_of::Config { assume_array: true }),
for_of(for_of::Config {
assume_array: true,
..Default::default()
}),
tr(Default::default(), false, tester.comments.clone())
),
for_of_as_array_for_of_import_commonjs,

View File

@ -0,0 +1,20 @@
import unsupportedIterableToArray from "./_unsupported_iterable_to_array.mjs";
export default function _createForOfIteratorHelperLoose(o, allowArrayLike) {
var it = typeof Symbol !== "undefined" && o[Symbol.iterator] || o["@@iterator"];
if (it) return (it = it.call(o)).next.bind(it);
// Fallback for engines without symbol support
if (
Array.isArray(o) ||
(it = unsupportedIterableToArray(o)) ||
(allowArrayLike && o && typeof o.length === "number")
) {
if (it) o = it;
var i = 0;
return function () {
if (i >= o.length) return { done: true };
return { done: false, value: o[i++] };
}
}
throw new TypeError("Invalid attempt to iterate non-iterable instance.\\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
}