diff --git a/src/ast/nodes/ForInStatement.js b/src/ast/nodes/ForInStatement.js new file mode 100644 index 0000000..002dd0a --- /dev/null +++ b/src/ast/nodes/ForInStatement.js @@ -0,0 +1,15 @@ +import Node from '../Node.js'; +import { STRING } from '../values.js'; + +export default class ForInStatement extends Node { + initialise ( scope ) { + super.initialise( scope ); + + // special case + if ( this.left.type === 'VariableDeclaration' ) { + for ( const proxy of this.left.declarations[0].proxies.values() ) { + proxy.possibleValues.add( STRING ); + } + } + } +} diff --git a/src/ast/nodes/ForOfStatement.js b/src/ast/nodes/ForOfStatement.js new file mode 100644 index 0000000..201d491 --- /dev/null +++ b/src/ast/nodes/ForOfStatement.js @@ -0,0 +1,15 @@ +import Node from '../Node.js'; +import { UNKNOWN } from '../values.js'; + +export default class ForOfStatement extends Node { + initialise ( scope ) { + super.initialise( scope ); + + // special case + if ( this.left.type === 'VariableDeclaration' ) { + for ( const proxy of this.left.declarations[0].proxies.values() ) { + proxy.possibleValues.add( UNKNOWN ); + } + } + } +} diff --git a/src/ast/nodes/index.js b/src/ast/nodes/index.js index c276a34..3cca1ad 100644 --- a/src/ast/nodes/index.js +++ b/src/ast/nodes/index.js @@ -11,6 +11,8 @@ import ExportAllDeclaration from './ExportAllDeclaration.js'; import ExportDefaultDeclaration from './ExportDefaultDeclaration.js'; import ExportNamedDeclaration from './ExportNamedDeclaration.js'; import ExpressionStatement from './ExpressionStatement.js'; +import ForInStatement from './ForInStatement.js'; +import ForOfStatement from './ForOfStatement.js'; import FunctionDeclaration from './FunctionDeclaration.js'; import FunctionExpression from './FunctionExpression.js'; import Identifier from './Identifier.js'; @@ -43,6 +45,8 @@ export default { ExportDefaultDeclaration, ExportNamedDeclaration, ExpressionStatement, + ForInStatement, + ForOfStatement, FunctionDeclaration, FunctionExpression, Identifier, diff --git a/test/form/effect-in-for-of-loop/_config.js b/test/form/effect-in-for-of-loop/_config.js new file mode 100644 index 0000000..79abe74 --- /dev/null +++ b/test/form/effect-in-for-of-loop/_config.js @@ -0,0 +1,3 @@ +module.exports = { + description: 'includes effects in for-of loop (#870)' +} diff --git a/test/form/effect-in-for-of-loop/_expected/amd.js b/test/form/effect-in-for-of-loop/_expected/amd.js new file mode 100644 index 0000000..57223a0 --- /dev/null +++ b/test/form/effect-in-for-of-loop/_expected/amd.js @@ -0,0 +1,19 @@ +define(function () { 'use strict'; + + const items = [{}, {}, {}]; + + function x () { + for ( const item of items.children ) { + item.foo = 'bar'; + } + } + + x(); + + assert.deepEqual( items, [ + { foo: 'bar' }, + { foo: 'bar' }, + { foo: 'bar' } + ]); + +}); diff --git a/test/form/effect-in-for-of-loop/_expected/cjs.js b/test/form/effect-in-for-of-loop/_expected/cjs.js new file mode 100644 index 0000000..fd94a9d --- /dev/null +++ b/test/form/effect-in-for-of-loop/_expected/cjs.js @@ -0,0 +1,17 @@ +'use strict'; + +const items = [{}, {}, {}]; + +function x () { + for ( const item of items.children ) { + item.foo = 'bar'; + } +} + +x(); + +assert.deepEqual( items, [ + { foo: 'bar' }, + { foo: 'bar' }, + { foo: 'bar' } +]); diff --git a/test/form/effect-in-for-of-loop/_expected/es.js b/test/form/effect-in-for-of-loop/_expected/es.js new file mode 100644 index 0000000..cabffc4 --- /dev/null +++ b/test/form/effect-in-for-of-loop/_expected/es.js @@ -0,0 +1,15 @@ +const items = [{}, {}, {}]; + +function x () { + for ( const item of items.children ) { + item.foo = 'bar'; + } +} + +x(); + +assert.deepEqual( items, [ + { foo: 'bar' }, + { foo: 'bar' }, + { foo: 'bar' } +]); diff --git a/test/form/effect-in-for-of-loop/_expected/iife.js b/test/form/effect-in-for-of-loop/_expected/iife.js new file mode 100644 index 0000000..96e91ea --- /dev/null +++ b/test/form/effect-in-for-of-loop/_expected/iife.js @@ -0,0 +1,20 @@ +(function () { + 'use strict'; + + const items = [{}, {}, {}]; + + function x () { + for ( const item of items.children ) { + item.foo = 'bar'; + } + } + + x(); + + assert.deepEqual( items, [ + { foo: 'bar' }, + { foo: 'bar' }, + { foo: 'bar' } + ]); + +}()); diff --git a/test/form/effect-in-for-of-loop/_expected/umd.js b/test/form/effect-in-for-of-loop/_expected/umd.js new file mode 100644 index 0000000..83a6d30 --- /dev/null +++ b/test/form/effect-in-for-of-loop/_expected/umd.js @@ -0,0 +1,23 @@ +(function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' ? factory() : + typeof define === 'function' && define.amd ? define(factory) : + (factory()); +}(this, (function () { 'use strict'; + + const items = [{}, {}, {}]; + + function x () { + for ( const item of items.children ) { + item.foo = 'bar'; + } + } + + x(); + + assert.deepEqual( items, [ + { foo: 'bar' }, + { foo: 'bar' }, + { foo: 'bar' } + ]); + +}))); diff --git a/test/form/effect-in-for-of-loop/main.js b/test/form/effect-in-for-of-loop/main.js new file mode 100644 index 0000000..401dcf2 --- /dev/null +++ b/test/form/effect-in-for-of-loop/main.js @@ -0,0 +1,23 @@ +const items = [{}, {}, {}]; + +function x () { + for ( const item of items.children ) { + item.foo = 'bar'; + } +} + +x(); + +function y () { + for ( const item of items.children ) { + // do nothing + } +} + +y(); + +assert.deepEqual( items, [ + { foo: 'bar' }, + { foo: 'bar' }, + { foo: 'bar' } +]);