@@ -6,6 +6,7 @@ module Language.PureScript.CodeGen.JS.Optimizer.Inliner
66 , inlineCommonValues
77 , inlineCommonOperators
88 , inlineFnComposition
9+ , inlineUnsafePartial
910 , etaConvert
1011 , unThunk
1112 , evaluateIifes
@@ -17,9 +18,11 @@ import Control.Monad.Supply.Class (MonadSupply, freshName)
1718
1819import Data.Maybe (fromMaybe)
1920import Data.Monoid ((<>))
21+import Data.String (IsString, fromString)
2022import Data.Text (Text)
2123import qualified Data.Text as T
2224
25+import Language.PureScript.PSString (PSString)
2326import Language.PureScript.CodeGen.JS.AST
2427import Language.PureScript.CodeGen.JS.Optimizer.Common
2528import qualified Language.PureScript.Constants as C
@@ -33,7 +36,6 @@ shouldInline (JSVar _ _) = True
3336shouldInline (JSNumericLiteral _ _) = True
3437shouldInline (JSStringLiteral _ _) = True
3538shouldInline (JSBooleanLiteral _ _) = True
36−shouldInline (JSAccessor _ _ val) = shouldInline val
3739shouldInline (JSIndexer _ index val) = shouldInline index && shouldInline val
3840shouldInline _ = False
3941
@@ -65,6 +67,8 @@ evaluateIifes = everywhereOnJS convert
6567 where
6668 convert :: JS -> JS
6769 convert (JSApp _ (JSFunction _ Nothing [] (JSBlock _ [JSReturn _ ret])) []) = ret
70+ convert (JSApp _ (JSFunction _ Nothing idents (JSBlock _ [JSReturn ss ret])) [])
71+ | not (any (`isReassigned` ret) idents) = replaceIdents (map (, JSVar ss C.undefined) idents) ret
6872 convert js = js
6973
7074inlineVariables :: JS -> JS
@@ -82,15 +86,17 @@ inlineCommonValues = everywhereOnJS convert
8286 where
8387 convert :: JS -> JS
8488 convert (JSApp ss fn [dict])
85− | isDict' [semiringNumber, semiringInt] dict && isFn fnZero fn = JSNumericLiteral ss (Left 0)
86− | isDict' [semiringNumber, semiringInt] dict && isFn fnOne fn = JSNumericLiteral ss (Left 1)
87− | isDict boundedBoolean dict && isFn fnBottom fn = JSBooleanLiteral ss False
88− | isDict boundedBoolean dict && isFn fnTop fn = JSBooleanLiteral ss True
89+ | isDict' [semiringNumber, semiringInt] dict && isDict fnZero fn = JSNumericLiteral ss (Left 0)
90+ | isDict' [semiringNumber, semiringInt] dict && isDict fnOne fn = JSNumericLiteral ss (Left 1)
91+ | isDict boundedBoolean dict && isDict fnBottom fn = JSBooleanLiteral ss False
92+ | isDict boundedBoolean dict && isDict fnTop fn = JSBooleanLiteral ss True
93+ convert (JSApp ss (JSApp _ fn [dict]) [x])
94+ | isDict ringInt dict && isDict fnNegate fn = JSBinary ss BitwiseOr (JSUnary ss Negate x) (JSNumericLiteral ss (Left 0))
8995 convert (JSApp ss (JSApp _ (JSApp _ fn [dict]) [x]) [y])
90− | isDict semiringInt dict && isFn fnAdd fn = intOp ss Add x y
91− | isDict semiringInt dict && isFn fnMultiply fn = intOp ss Multiply x y
92− | isDict euclideanRingInt dict && isFn fnDivide fn = intOp ss Divide x y
93− | isDict ringInt dict && isFn fnSubtract fn = intOp ss Subtract x y
96+ | isDict semiringInt dict && isDict fnAdd fn = intOp ss Add x y
97+ | isDict semiringInt dict && isDict fnMultiply fn = intOp ss Multiply x y
98+ | isDict euclideanRingInt dict && isDict fnDivide fn = intOp ss Divide x y
99+ | isDict ringInt dict && isDict fnSubtract fn = intOp ss Subtract x y
94100 convert other = other
95101 fnZero = (C.dataSemiring, C.zero)
96102 fnOne = (C.dataSemiring, C.one)
@@ -100,6 +106,7 @@ inlineCommonValues = everywhereOnJS convert
100106 fnDivide = (C.dataEuclideanRing, C.div)
101107 fnMultiply = (C.dataSemiring, C.mul)
102108 fnSubtract = (C.dataRing, C.sub)
109+ fnNegate = (C.dataRing, C.negate)
103110 intOp ss op x y = JSBinary ss BitwiseOr (JSBinary ss op x y) (JSNumericLiteral ss (Left 0))
104111
105112inlineCommonOperators :: JS -> JS
@@ -109,8 +116,6 @@ inlineCommonOperators = applyAll $
109116
110117 , binary ringNumber opSub Subtract
111118 , unary ringNumber opNegate Negate
112− , binary ringInt opSub Subtract
113− , unary ringInt opNegate Negate
114119
115120 , binary euclideanRingNumber opDiv Divide
116121 , binary euclideanRingInt opMod Modulus
@@ -167,29 +172,29 @@ inlineCommonOperators = applyAll $
167172 ] ++
168173 [ fn | i <- [0..10], fn <- [ mkFn i, runFn i ] ]
169174 where
170− binary :: (Text, Text) -> (Text, Text) -> BinaryOperator -> JS -> JS
175+ binary :: (Text, PSString) -> (Text, PSString) -> BinaryOperator -> JS -> JS
171176 binary dict fns op = everywhereOnJS convert
172177 where
173178 convert :: JS -> JS
174− convert (JSApp ss (JSApp _ (JSApp _ fn [dict']) [x]) [y]) | isDict dict dict' && isFn fns fn = JSBinary ss op x y
179+ convert (JSApp ss (JSApp _ (JSApp _ fn [dict']) [x]) [y]) | isDict dict dict' && isDict fns fn = JSBinary ss op x y
175180 convert other = other
176− binary' :: Text -> Text -> BinaryOperator -> JS -> JS
181+ binary' :: Text -> PSString -> BinaryOperator -> JS -> JS
177182 binary' moduleName opString op = everywhereOnJS convert
178183 where
179184 convert :: JS -> JS
180− convert (JSApp ss (JSApp _ fn [x]) [y]) | isFn (moduleName, opString) fn = JSBinary ss op x y
185+ convert (JSApp ss (JSApp _ fn [x]) [y]) | isDict (moduleName, opString) fn = JSBinary ss op x y
181186 convert other = other
182− unary :: (Text, Text) -> (Text, Text) -> UnaryOperator -> JS -> JS
187+ unary :: (Text, PSString) -> (Text, PSString) -> UnaryOperator -> JS -> JS
183188 unary dicts fns op = everywhereOnJS convert
184189 where
185190 convert :: JS -> JS
186− convert (JSApp ss (JSApp _ fn [dict']) [x]) | isDict dicts dict' && isFn fns fn = JSUnary ss op x
191+ convert (JSApp ss (JSApp _ fn [dict']) [x]) | isDict dicts dict' && isDict fns fn = JSUnary ss op x
187192 convert other = other
188− unary' :: Text -> Text -> UnaryOperator -> JS -> JS
193+ unary' :: Text -> PSString -> UnaryOperator -> JS -> JS
189194 unary' moduleName fnName op = everywhereOnJS convert
190195 where
191196 convert :: JS -> JS
192− convert (JSApp ss fn [x]) | isFn (moduleName, fnName) fn = JSUnary ss op x
197+ convert (JSApp ss fn [x]) | isDict (moduleName, fnName) fn = JSUnary ss op x
193198 convert other = other
194199 mkFn :: Int -> JS -> JS
195200 mkFn 0 = everywhereOnJS convert
@@ -213,7 +218,8 @@ inlineCommonOperators = applyAll $
213218
214219 isNFn :: Text -> Int -> JS -> Bool
215220 isNFn prefix n (JSVar _ name) = name == (prefix <> T.pack (show n))
216− isNFn prefix n (JSAccessor _ name (JSVar _ dataFunctionUncurried)) | dataFunctionUncurried == C.dataFunctionUncurried = name == (prefix <> T.pack (show n))
221+ isNFn prefix n (JSIndexer _ (JSStringLiteral _ name) (JSVar _ dataFunctionUncurried)) | dataFunctionUncurried == C.dataFunctionUncurried =
222+ name == fromString (T.unpack prefix <> show n)
217223 isNFn _ _ _ = False
218224
219225 runFn :: Int -> JS -> JS
@@ -234,20 +240,21 @@ inlineCommonOperators = applyAll $
234240 convert (JSApp _ (JSApp _ op' [x]) [y]) | p op' = f x y
235241 convert other = other
236242
237− isModFn :: (Text, Text) -> JS -> Bool
238− isModFn (m, op) (JSAccessor _ op' (JSVar _ m')) = m == m' && op == op'
243+ isModFn :: (Text, PSString) -> JS -> Bool
244+ isModFn (m, op) (JSIndexer _ (JSStringLiteral _ op') (JSVar _ m')) =
245+ m == m' && op == op'
239246 isModFn _ _ = False
240247
241− isModFnWithDict :: (Text, Text) -> JS -> Bool
242− isModFnWithDict (m, op) (JSApp _ (JSAccessor _ op' (JSVar _ m')) [JSVar _ _]) = m == m' && op == op'
248+ isModFnWithDict :: (Text, PSString) -> JS -> Bool
249+ isModFnWithDict (m, op) (JSApp _ (JSIndexer _ (JSStringLiteral _ op') (JSVar _ m')) [JSVar _ _]) =
250+ m == m' && op == op'
243251 isModFnWithDict _ _ = False
244252
245253-- (f <<< g $ x) = f (g x)
246254-- (f <<< g) = \x -> f (g x)
247−inlineFnComposition :: (MonadSupply m) => JS -> m JS
248−inlineFnComposition = everywhereOnJSTopDownM convert
249− where
250− convert :: (MonadSupply m) => JS -> m JS
255+inlineFnComposition :: forall m. MonadSupply m => JS -> m JS
256+inlineFnComposition = everywhereOnJSTopDownM convert where
257+ convert :: JS -> m JS
251258 convert (JSApp s1 (JSApp s2 (JSApp _ (JSApp _ fn [dict']) [x]) [y]) [z])
252259 | isFnCompose dict' fn = return $ JSApp s1 x [JSApp s2 y [z]]
253260 | isFnComposeFlipped dict' fn = return $ JSApp s2 y [JSApp s1 x [z]]
@@ -260,118 +267,127 @@ inlineFnComposition = everywhereOnJSTopDownM convert
260267 return $ JSFunction ss Nothing [arg] (JSBlock ss [JSReturn Nothing $ JSApp Nothing y [JSApp Nothing x [JSVar Nothing arg]]])
261268 convert other = return other
262269 isFnCompose :: JS -> JS -> Bool
263− isFnCompose dict' fn = isDict semigroupoidFn dict' && isFn fnCompose fn
270+ isFnCompose dict' fn = isDict semigroupoidFn dict' && isDict fnCompose fn
264271 isFnComposeFlipped :: JS -> JS -> Bool
265− isFnComposeFlipped dict' fn = isDict semigroupoidFn dict' && isFn fnComposeFlipped fn
266− fnCompose :: (Text, Text)
272+ isFnComposeFlipped dict' fn = isDict semigroupoidFn dict' && isDict fnComposeFlipped fn
273+ fnCompose :: forall a b. (IsString a, IsString b) => (a, b)
267274 fnCompose = (C.controlSemigroupoid, C.compose)
268− fnComposeFlipped :: (Text, Text)
275+ fnComposeFlipped :: forall a b. (IsString a, IsString b) => (a, b)
269276 fnComposeFlipped = (C.controlSemigroupoid, C.composeFlipped)
270277
271−semiringNumber :: (Text, Text)
278+inlineUnsafePartial :: JS -> JS
279+inlineUnsafePartial = everywhereOnJSTopDown convert where
280+ convert (JSApp ss (JSIndexer _ (JSStringLiteral _ unsafePartial) (JSVar _ partialUnsafe)) [ comp ])
281+ | unsafePartial == C.unsafePartial && partialUnsafe == C.partialUnsafe
282+ -- Apply to undefined here, the application should be optimized away
283+ -- if it is safe to do so
284+ = JSApp ss comp [ JSVar ss C.undefined ]
285+ convert other = other
286+
287+semiringNumber :: forall a b. (IsString a, IsString b) => (a, b)
272288semiringNumber = (C.dataSemiring, C.semiringNumber)
273289
274−semiringInt :: (Text, Text)
290+semiringInt :: forall a b. (IsString a, IsString b) => (a, b)
275291semiringInt = (C.dataSemiring, C.semiringInt)
276292
277−ringNumber :: (Text, Text)
293+ringNumber :: forall a b. (IsString a, IsString b) => (a, b)
278294ringNumber = (C.dataRing, C.ringNumber)
279295
280−ringInt :: (Text, Text)
296+ringInt :: forall a b. (IsString a, IsString b) => (a, b)
281297ringInt = (C.dataRing, C.ringInt)
282298
283−euclideanRingNumber :: (Text, Text)
299+euclideanRingNumber :: forall a b. (IsString a, IsString b) => (a, b)
284300euclideanRingNumber = (C.dataEuclideanRing, C.euclideanRingNumber)
285301
286−euclideanRingInt :: (Text, Text)
302+euclideanRingInt :: forall a b. (IsString a, IsString b) => (a, b)
287303euclideanRingInt = (C.dataEuclideanRing, C.euclideanRingInt)
288304
289−eqNumber :: (Text, Text)
305+eqNumber :: forall a b. (IsString a, IsString b) => (a, b)
290306eqNumber = (C.dataEq, C.eqNumber)
291307
292−eqInt :: (Text, Text)
308+eqInt :: forall a b. (IsString a, IsString b) => (a, b)
293309eqInt = (C.dataEq, C.eqInt)
294310
295−eqString :: (Text, Text)
311+eqString :: forall a b. (IsString a, IsString b) => (a, b)
296312eqString = (C.dataEq, C.eqString)
297313
298−eqChar :: (Text, Text)
314+eqChar :: forall a b. (IsString a, IsString b) => (a, b)
299315eqChar = (C.dataEq, C.eqChar)
300316
301−eqBoolean :: (Text, Text)
317+eqBoolean :: forall a b. (IsString a, IsString b) => (a, b)
302318eqBoolean = (C.dataEq, C.eqBoolean)
303319
304−ordBoolean :: (Text, Text)
320+ordBoolean :: forall a b. (IsString a, IsString b) => (a, b)
305321ordBoolean = (C.dataOrd, C.ordBoolean)
306322
307−ordNumber :: (Text, Text)
323+ordNumber :: forall a b. (IsString a, IsString b) => (a, b)
308324ordNumber = (C.dataOrd, C.ordNumber)
309325
310−ordInt :: (Text, Text)
326+ordInt :: forall a b. (IsString a, IsString b) => (a, b)
311327ordInt = (C.dataOrd, C.ordInt)
312328
313−ordString :: (Text, Text)
329+ordString :: forall a b. (IsString a, IsString b) => (a, b)
314330ordString = (C.dataOrd, C.ordString)
315331
316−ordChar :: (Text, Text)
332+ordChar :: forall a b. (IsString a, IsString b) => (a, b)
317333ordChar = (C.dataOrd, C.ordChar)
318334
319−semigroupString :: (Text, Text)
335+semigroupString :: forall a b. (IsString a, IsString b) => (a, b)
320336semigroupString = (C.dataSemigroup, C.semigroupString)
321337
322−boundedBoolean :: (Text, Text)
338+boundedBoolean :: forall a b. (IsString a, IsString b) => (a, b)
323339boundedBoolean = (C.dataBounded, C.boundedBoolean)
324340
325−heytingAlgebraBoolean :: (Text, Text)
341+heytingAlgebraBoolean :: forall a b. (IsString a, IsString b) => (a, b)
326342heytingAlgebraBoolean = (C.dataHeytingAlgebra, C.heytingAlgebraBoolean)
327343
328−semigroupoidFn :: (Text, Text)
344+semigroupoidFn :: forall a b. (IsString a, IsString b) => (a, b)
329345semigroupoidFn = (C.controlSemigroupoid, C.semigroupoidFn)
330346
331−opAdd :: (Text, Text)
347+opAdd :: forall a b. (IsString a, IsString b) => (a, b)
332348opAdd = (C.dataSemiring, C.add)
333349
334−opMul :: (Text, Text)
350+opMul :: forall a b. (IsString a, IsString b) => (a, b)
335351opMul = (C.dataSemiring, C.mul)
336352
337−opEq :: (Text, Text)
353+opEq :: forall a b. (IsString a, IsString b) => (a, b)
338354opEq = (C.dataEq, C.eq)
339355
340−opNotEq :: (Text, Text)
356+opNotEq :: forall a b. (IsString a, IsString b) => (a, b)
341357opNotEq = (C.dataEq, C.notEq)
342358
343−opLessThan :: (Text, Text)
359+opLessThan :: forall a b. (IsString a, IsString b) => (a, b)
344360opLessThan = (C.dataOrd, C.lessThan)
345361
346−opLessThanOrEq :: (Text, Text)
362+opLessThanOrEq :: forall a b. (IsString a, IsString b) => (a, b)
347363opLessThanOrEq = (C.dataOrd, C.lessThanOrEq)
348364
349−opGreaterThan :: (Text, Text)
365+opGreaterThan :: forall a b. (IsString a, IsString b) => (a, b)
350366opGreaterThan = (C.dataOrd, C.greaterThan)
351367
352−opGreaterThanOrEq :: (Text, Text)
368+opGreaterThanOrEq :: forall a b. (IsString a, IsString b) => (a, b)
353369opGreaterThanOrEq = (C.dataOrd, C.greaterThanOrEq)
354370
355−opAppend :: (Text, Text)
371+opAppend :: forall a b. (IsString a, IsString b) => (a, b)
356372opAppend = (C.dataSemigroup, C.append)
357373
358−opSub :: (Text, Text)
374+opSub :: forall a b. (IsString a, IsString b) => (a, b)
359375opSub = (C.dataRing, C.sub)
360376
361−opNegate :: (Text, Text)
377+opNegate :: forall a b. (IsString a, IsString b) => (a, b)
362378opNegate = (C.dataRing, C.negate)
363379
364−opDiv :: (Text, Text)
380+opDiv :: forall a b. (IsString a, IsString b) => (a, b)
365381opDiv = (C.dataEuclideanRing, C.div)
366382
367−opMod :: (Text, Text)
383+opMod :: forall a b. (IsString a, IsString b) => (a, b)
368384opMod = (C.dataEuclideanRing, C.mod)
369385
370−opConj :: (Text, Text)
386+opConj :: forall a b. (IsString a, IsString b) => (a, b)
371387opConj = (C.dataHeytingAlgebra, C.conj)
372388
373−opDisj :: (Text, Text)
389+opDisj :: forall a b. (IsString a, IsString b) => (a, b)
374390opDisj = (C.dataHeytingAlgebra, C.disj)
375391
376−opNot :: (Text, Text)
392+opNot :: forall a b. (IsString a, IsString b) => (a, b)
377393opNot = (C.dataHeytingAlgebra, C.not)