-
Notifications
You must be signed in to change notification settings - Fork 1.1k
/
InlineFunctionsTest.java
3100 lines (2689 loc) · 91.2 KB
/
InlineFunctionsTest.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
/*
* Copyright 2008 The Closure Compiler Authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.javascript.jscomp;
/**
* Inline function tests.
* @author johnlenz@google.com (john lenz)
*/
public class InlineFunctionsTest extends CompilerTestCase {
boolean allowGlobalFunctionInlining;
boolean allowBlockInlining;
final boolean allowExpressionDecomposition = true;
final boolean allowFunctionExpressionInlining = true;
final boolean allowLocalFunctionInlining = true;
boolean assumeStrictThis;
boolean assumeMinimumCapture;
int maxSizeAfterInlining;
final static String EXTERNS =
"/** @nosideeffects */ function nochg(){}\n" +
"function chg(){}\n";
public InlineFunctionsTest() {
super(EXTERNS);
}
// Overridden by one test method that needs to disable this.
void maybeEnableInferConsts() {
enableInferConsts();
}
@Override
protected void setUp() throws Exception {
super.setUp();
maybeEnableInferConsts();
enableNormalize();
enableComputeSideEffects();
allowGlobalFunctionInlining = true;
allowBlockInlining = true;
assumeStrictThis = false;
assumeMinimumCapture = false;
maxSizeAfterInlining = CompilerOptions.UNLIMITED_FUN_SIZE_AFTER_INLINING;
}
@Override
protected CompilerPass getProcessor(Compiler compiler) {
compiler.resetUniqueNameId();
return new InlineFunctions(
compiler,
compiler.getUniqueNameIdSupplier(),
allowGlobalFunctionInlining,
allowLocalFunctionInlining,
allowBlockInlining,
assumeStrictThis,
assumeMinimumCapture,
maxSizeAfterInlining);
}
/**
* Returns the number of times the pass should be run before results are
* verified.
*/
@Override
protected int getNumRepetitions() {
// Some inlining can only be done in multiple passes.
return 3;
}
public void testInlineEmptyFunction1() {
// Empty function, no params.
test("function foo(){}" +
"foo();",
"void 0;");
}
public void testInlineEmptyFunction2() {
// Empty function, params with no side-effects.
test("function foo(){}" +
"foo(1, new Date, function(){});",
"void 0;");
}
public void testInlineEmptyFunction3() {
// Empty function, multiple references.
test("function foo(){}" +
"foo();foo();foo();",
"void 0;void 0;void 0");
}
public void testInlineEmptyFunction4() {
// Empty function, params with side-effects forces block inlining.
test("function foo(){}" +
"foo(x());",
"{var JSCompiler_inline_anon_param_0=x();}");
}
public void testInlineEmptyFunction5() {
// Empty function, call params with side-effects in expression can not
// be inlined.
allowBlockInlining = false;
testSame("function foo(){}" +
"foo(x());");
}
public void testInlineEmptyFunction6() {
setAcceptedLanguage(CompilerOptions.LanguageMode.ECMASCRIPT_2015);
test("if (window) { f(); function f() {} }",
"if (window) { void 0; }");
}
public void testInlineFunctions1() {
// As simple a test as we can get.
test("function foo(){ return 4 }" +
"foo();",
"4");
}
public void testInlineFunctions2() {
// inline simple constants
// NOTE: CD is not inlined.
test("var t;var AB=function(){return 4};" +
"function BC(){return 6;}" +
"CD=function(x){return x + 5};x=CD(3);y=AB();z=BC();",
"var t;CD=function(x){return x+5};x=CD(3);y=4;z=6"
);
}
public void testInlineFunctions3() {
// inline simple constants
test("var t;var AB=function(){return 4};" +
"function BC(){return 6;}" +
"var CD=function(x){return x + 5};x=CD(3);y=AB();z=BC();",
"var t;x=3+5;y=4;z=6");
}
public void testInlineFunctions4() {
// don't inline if there are multiple definitions (need DFA for that).
test("var t; var AB = function() { return 4 }; " +
"function BC() { return 6; }" +
"CD = 0;" +
"CD = function(x) { return x + 5 }; x = CD(3); y = AB(); z = BC();",
"var t;CD=0;CD=function(x){return x+5};x=CD(3);y=4;z=6");
}
public void testInlineFunctions5() {
// inline additions
test("var FOO_FN=function(x,y) { return \"de\" + x + \"nu\" + y };" +
"var a = FOO_FN(\"ez\", \"ts\")",
"var a=\"de\"+\"ez\"+\"nu\"+\"ts\"");
}
public void testInlineFunctions6() {
// more complex inlines
test("function BAR_FN(x, y, z) { return z(nochg(x + y)) }" +
"alert(BAR_FN(1, 2, baz))",
"alert(baz(nochg(1+2)))");
}
public void testInlineFunctions7() {
// inlines appearing multiple times
test("function FN(x,y,z){return x+x+y}" +
"var b=FN(1,2,3)",
"var b=1+1+2");
}
public void testInlineFunctions8() {
// check correct parenthesization
test("function MUL(x,y){return x*y}function ADD(x,y){return x+y}" +
"var a=1+MUL(2,3);var b=2*ADD(3,4)",
"var a=1+2*3;var b=2*(3+4)");
}
public void testInlineFunctions9() {
// don't inline if the input parameter is modified.
test("function INC(x){return x++}" +
"var y=INC(i)",
"var y;{var x$jscomp$inline_0=i;" +
"y=x$jscomp$inline_0++}");
}
public void testInlineFunctions10() {
test("function INC(x){return x++}" +
"var y=INC(i);y=INC(i)",
"var y;" +
"{var x$jscomp$inline_0=i;" +
"y=x$jscomp$inline_0++}" +
"{var x$jscomp$inline_2=i;" +
"y=x$jscomp$inline_2++}");
}
public void testInlineFunctions11() {
test("function f(x){return x}" +
"var y=f(i)",
"var y=i");
}
public void testInlineFunctions12() {
// don't inline if the input parameter has side-effects.
allowBlockInlining = false;
test("function f(x){return x}" +
"var y=f(i)",
"var y=i");
test(
"function f(x){return x} var y=f(i++)",
"var y = i++");
}
public void testInlineFunctions13() {
// inline as block if the input parameter has side-effects.
test("function f(x){return x}" +
"var y=f(i++)",
"var y=i++");
}
public void testInlineFunctions13a() {
// inline as block if the input parameter has side-effects.
test("function f(x){return random() || x}" +
"var y=f(i++)",
"var y;{var x$jscomp$inline_0=i++;y=random() || x$jscomp$inline_0}");
}
public void testInlineFunctions14() {
// don't remove functions that are referenced on other ways
test("function FOO(x){return x}var BAR=function(y){return y}" +
";b=FOO;a(BAR);x=FOO(1);y=BAR(2)",
"function FOO(x){return x}var BAR=function(y){return y}" +
";b=FOO;a(BAR);x=1;y=2");
}
public void testInlineFunctions15a() {
// closure factories: do inline into global scope.
test("function foo(){return function(a){return a+1}}" +
"var b=function(){return c};" +
"var d=b()+foo()",
"var d=c+function(a){return a+1}");
}
public void testInlineFunctions15b() {
assumeMinimumCapture = false;
// closure factories: don't inline closure with locals into global scope.
test("function foo(){var x;return function(a){return a+1}}" +
"var b=function(){return c};" +
"var d=b()+foo()",
"function foo(){var x;return function(a){return a+1}}" +
"var d=c+foo()");
assumeMinimumCapture = true;
test("function foo(){var x;return function(a){return a+1}}" +
"var b=function(){return c};" +
"var d=b()+foo()",
"var JSCompiler_inline_result$jscomp$0;" +
"{var x$jscomp$inline_1;" +
"JSCompiler_inline_result$jscomp$0=function(a$jscomp$inline_2) {" +
" return a$jscomp$inline_2+1};}" +
"var d=c+JSCompiler_inline_result$jscomp$0");
}
public void testInlineFunctions15c() {
assumeMinimumCapture = false;
// closure factories: don't inline into non-global scope.
test("function foo(){return function(a){return a+1}}" +
"var b=function(){return c};" +
"function _x(){ var d=b()+foo() }",
"function foo(){return function(a){return a+1}}" +
"function _x(){ var d=c+foo() }");
assumeMinimumCapture = true;
// closure factories: don't inline into non-global scope.
test("function foo(){return function(a){return a+1}}" +
"var b=function(){return c};" +
"function _x(){ var d=b()+foo() }",
"function _x(){var d=c+function(a){return a+1}}");
}
public void testInlineFunctions15d() {
assumeMinimumCapture = false;
// closure factories: don't inline functions with vars.
test("function foo(){var x; return function(a){return a+1}}" +
"var b=function(){return c};" +
"function _x(){ var d=b()+foo() }",
"function foo(){var x; return function(a){return a+1}}" +
"function _x(){ var d=c+foo() }");
assumeMinimumCapture = true;
// closure factories: inline functions with vars.
test("function foo(){var x; return function(a){return a+1}}" +
"var b=function(){return c};" +
"function _x(){ var d=b()+foo() }",
"function _x() {" +
" var JSCompiler_inline_result$jscomp$0;" +
" {" +
" var x$jscomp$inline_1;" +
" JSCompiler_inline_result$jscomp$0=function(a$jscomp$inline_2) {" +
" return a$jscomp$inline_2+1};" +
" }" +
" var d=c+JSCompiler_inline_result$jscomp$0" +
"}");
}
public void testInlineFunctions16a() {
assumeMinimumCapture = false;
testSame("function foo(b){return window.bar(function(){c(b)})}" +
"var d=foo(e)");
assumeMinimumCapture = true;
test(
"function foo(b){return window.bar(function(){c(b)})}" +
"var d=foo(e)",
"var d;{var b$jscomp$inline_0=e;" +
"d=window.bar(function(){c(b$jscomp$inline_0)})}");
}
public void testInlineFunctions16b() {
test("function foo(){return window.bar(function(){c()})}" +
"var d=foo(e)",
"var d=window.bar(function(){c()})");
}
public void testInlineFunctions17() {
// don't inline recursive functions
testSame("function foo(x){return x*x+foo(3)}var bar=foo(4)");
}
public void testInlineFunctions18() {
// TRICKY ... test nested inlines
allowBlockInlining = false;
test("function foo(a, b){return a+b}" +
"function bar(d){return c}" +
"var d=foo(bar(1),e)",
"var d=c+e");
}
public void testInlineFunctions19() {
// TRICKY ... test nested inlines
// with block inlining possible
test("function foo(a, b){return a+b}" +
"function bar(d){return c}" +
"var d=foo(bar(1),e)",
"var d=c+e;");
}
public void testInlineFunctions20() {
// Make sure both orderings work
allowBlockInlining = false;
test("function foo(a, b){return a+b}" +
"function bar(d){return c}" +
"var d=bar(foo(1,e));",
"var d=c");
}
public void testInlineFunctions21() {
// with block inlining possible
test("function foo(a, b){return a+b}" +
"function bar(d){return c}" +
"var d=bar(foo(1,e))",
"var d=c");
}
public void testInlineFunctions22() {
// Another tricky case ... test nested compiler inlines
test("function plex(a){if(a) return 0;else return 1;}" +
"function foo(a, b){return bar(a+b)}" +
"function bar(d){return plex(d)}" +
"var d=foo(1,2)",
"var d;{JSCompiler_inline_label_plex_1:{" +
"if(1+2){" +
"d=0;break JSCompiler_inline_label_plex_1}" +
"else{" +
"d=1;break JSCompiler_inline_label_plex_1}d=void 0}}");
}
public void testInlineFunctions23() {
// Test both orderings again
test("function complex(a){if(a) return 0;else return 1;}" +
"function bar(d){return complex(d)}" +
"function foo(a, b){return bar(a+b)}" +
"var d=foo(1,2)",
"var d;{JSCompiler_inline_label_complex_1:{" +
"if(1+2){" +
"d=0;break JSCompiler_inline_label_complex_1" +
"}else{" +
"d=1;break JSCompiler_inline_label_complex_1" +
"}d=void 0}}");
}
public void testInlineFunctions24() {
// Don't inline functions with 'arguments' or 'this'
testSame("function foo(x){return this}foo(1)");
}
public void testInlineFunctions25() {
testSame("function foo(){return arguments[0]}foo()");
}
public void testInlineFunctions26() {
// Don't inline external functions
testSame("function _foo(x){return x}_foo(1)");
}
public void testInlineFunctions27() {
test("var window = {}; function foo(){window.bar++; return 3;}" +
"var x = {y: 1, z: foo(2)};",
"var window={};" +
"var JSCompiler_inline_result$jscomp$0;" +
"{" +
" window.bar++;" +
" JSCompiler_inline_result$jscomp$0 = 3;" +
"}" +
"var x = {y: 1, z: JSCompiler_inline_result$jscomp$0};");
}
public void testInlineFunctions28() {
test("var window = {}; function foo(){window.bar++; return 3;}" +
"var x = {y: alert(), z: foo(2)};",
"var window = {};" +
"var JSCompiler_temp_const$jscomp$0 = alert();" +
"var JSCompiler_inline_result$jscomp$1;" +
"{" +
" window.bar++;" +
" JSCompiler_inline_result$jscomp$1 = 3;}" +
"var x = {" +
" y: JSCompiler_temp_const$jscomp$0," +
" z: JSCompiler_inline_result$jscomp$1" +
"};");
}
public void testInlineFunctions29() {
test("var window = {}; function foo(){window.bar++; return 3;}" +
"var x = {a: alert(), b: alert2(), c: foo(2)};",
"var window = {};" +
"var JSCompiler_temp_const$jscomp$1 = alert();" +
"var JSCompiler_temp_const$jscomp$0 = alert2();" +
"var JSCompiler_inline_result$jscomp$2;" +
"{" +
" window.bar++;" +
" JSCompiler_inline_result$jscomp$2 = 3;}" +
"var x = {" +
" a: JSCompiler_temp_const$jscomp$1," +
" b: JSCompiler_temp_const$jscomp$0," +
" c: JSCompiler_inline_result$jscomp$2" +
"};");
}
public void testInlineFunctions30() {
// As simple a test as we can get.
testSame("function foo(){ return eval() }" +
"foo();");
}
public void testInlineFunctions31() {
// Don't introduce a duplicate label in the same scope
test("function foo(){ lab:{4;} }" +
"lab:{foo();}",
"lab:{{JSCompiler_inline_label_0:{4}}}");
}
public void testMixedModeInlining1() {
// Base line tests, direct inlining
test("function foo(){return 1}" +
"foo();",
"1;");
}
public void testMixedModeInlining2() {
// Base line tests, block inlining. Block inlining is needed by
// possible-side-effect parameter.
test("function foo(){return 1}" +
"foo(x());",
"{var JSCompiler_inline_anon_param_0=x();1}");
}
public void testMixedModeInlining3() {
// Inline using both modes.
test("function foo(){return 1}" +
"foo();foo(x());",
"1;{var JSCompiler_inline_anon_param_0=x();1}");
}
public void testMixedModeInlining4() {
// Inline using both modes. Alternating. Second call of each type has
// side-effect-less parameter, this is thrown away.
test("function foo(){return 1}" +
"foo();foo(x());" +
"foo(1);foo(1,x());",
"1;{var JSCompiler_inline_anon_param_0=x();1}" +
"1;{var JSCompiler_inline_anon_param_4=x();1}");
}
public void testMixedModeInliningCosting1() {
// Inline using both modes. Costing estimates.
// Base line.
test(
"function foo(a,b){return a+b+a+b+4+5+6+7+8+9+1+2+3+4+5}" +
"foo(1,2);" +
"foo(2,3)",
"1+2+1+2+4+5+6+7+8+9+1+2+3+4+5;" +
"2+3+2+3+4+5+6+7+8+9+1+2+3+4+5");
}
public void testMixedModeInliningCosting2() {
// Don't inline here because the function definition can not be eliminated.
// TODO(johnlenz): Should we add constant removing to the unit test?
testSame(
"function foo(a,b){return a+b+a+b+4+5+6+7+8+9+1+2+3+4+5}" +
"foo(1,2);" +
"foo(2,3,x())");
}
public void testMixedModeInliningCosting3() {
// Do inline here because the function definition can be eliminated.
test(
"function foo(a,b){return a+b+a+b+4+5+6+7+8+9+1+2+3+10}" +
"foo(1,2);" +
"foo(2,3,x())",
"1+2+1+2+4+5+6+7+8+9+1+2+3+10;" +
"{var JSCompiler_inline_anon_param_2=x();" +
"2+3+2+3+4+5+6+7+8+9+1+2+3+10}");
}
public void testMixedModeInliningCosting4() {
// Threshold test.
testSame(
"function foo(a,b){return a+b+a+b+4+5+6+7+8+9+1+2+3+4+101}" +
"foo(1,2);" +
"foo(2,3,x())");
}
public void testNoInlineIfParametersModified1() {
// Assignment
test("function f(x){return x=1}f(undefined)",
"{var x$jscomp$inline_0=undefined;" +
"x$jscomp$inline_0=1}");
}
public void testNoInlineIfParametersModified2() {
test("function f(x){return (x)=1;}f(2)",
"{var x$jscomp$inline_0=2;" +
"x$jscomp$inline_0=1}");
}
public void testNoInlineIfParametersModified3() {
// Assignment variant.
test("function f(x){return x*=2}f(2)",
"{var x$jscomp$inline_0=2;" +
"x$jscomp$inline_0*=2}");
}
public void testNoInlineIfParametersModified4() {
// Assignment in if.
test("function f(x){return x?(x=2):0}f(2)",
"{var x$jscomp$inline_0=2;" +
"x$jscomp$inline_0?(" +
"x$jscomp$inline_0=2):0}");
}
public void testNoInlineIfParametersModified5() {
// Assignment in if, multiple params
test("function f(x,y){return x?(y=2):0}f(2,undefined)",
"{var y$jscomp$inline_1=undefined;2?(" +
"y$jscomp$inline_1=2):0}");
}
public void testNoInlineIfParametersModified6() {
test("function f(x,y){return x?(y=2):0}f(2)",
"{var y$jscomp$inline_1=void 0;2?(" +
"y$jscomp$inline_1=2):0}");
}
public void testNoInlineIfParametersModified7() {
// Increment
test("function f(a){return++a<++a}f(1)",
"{var a$jscomp$inline_0=1;" +
"++a$jscomp$inline_0<" +
"++a$jscomp$inline_0}");
}
public void testInlineIfParametersModified8() {
// OK, object parameter modified.
test("function f(a){return a.x=2}f(o)", "o.x=2");
}
public void testInlineIfParametersModified9() {
// OK, array parameter modified.
test("function f(a){return a[2]=2}f(o)", "o[2]=2");
}
public void testInlineNeverPartialSubtitution1() {
test("function f(z){return x.y.z;}f(1)",
"x.y.z");
}
public void testInlineNeverPartialSubtitution2() {
test("function f(z){return x.y[z];}f(a)",
"x.y[a]");
}
public void testInlineNeverMutateConstants() {
test("function f(x){return x=1}f(undefined)",
"{var x$jscomp$inline_0=undefined;" +
"x$jscomp$inline_0=1}");
}
public void testInlineNeverOverrideNewValues() {
test("function f(a){return++a<++a}f(1)",
"{var a$jscomp$inline_0=1;" +
"++a$jscomp$inline_0<++a$jscomp$inline_0}");
}
public void testInlineMutableArgsReferencedOnce() {
test(
"function foo(x){return x;}foo([])",
"[]");
}
public void testInlineMutableArgsReferencedOnce2() {
this.assumeMinimumCapture = true;
// Don't inline a mutable value that will be reused.
test(
"function foo(x){return function(){ return x; }} repeat(foo([]))",
"var JSCompiler_inline_result$jscomp$0;" +
"{ " +
"var x$jscomp$inline_1=[];" +
"JSCompiler_inline_result$jscomp$0=function(){return x$jscomp$inline_1}; " +
"}" +
"repeat(JSCompiler_inline_result$jscomp$0)");
}
public void testInlineMutableArgsReferencedOnce3() {
this.assumeMinimumCapture = true;
// Don't inline a mutable value that will be reused.
test(
"function f(a) {\n" +
" for(var i=0; i<0; i++) {\n" +
" g(a);\n" +
" }\n" +
"}\n" +
"f([]);",
"{" +
"var a$jscomp$inline_0=[];" +
"var i$jscomp$inline_1=0;" +
"for(;i$jscomp$inline_1<0;i$jscomp$inline_1++) {" +
" g(a$jscomp$inline_0)" +
"}" +
"}");
}
public void testNoInlineMutableArgs1() {
allowBlockInlining = false;
testSame("function foo(x){return x+x} foo([])");
}
public void testNoInlineMutableArgs2() {
allowBlockInlining = false;
testSame("function foo(x){return x+x} foo(new Date)");
}
public void testNoInlineMutableArgs3() {
allowBlockInlining = false;
testSame("function foo(x){return x+x} foo(true&&new Date)");
}
public void testNoInlineMutableArgs4() {
allowBlockInlining = false;
testSame("function foo(x){return x+x} foo({})");
}
public void testInlineBlockMutableArgs1() {
test("function foo(x){x+x}foo([])",
"{var x$jscomp$inline_0=[];" +
"x$jscomp$inline_0+x$jscomp$inline_0}");
}
public void testInlineBlockMutableArgs2() {
test("function foo(x){x+x}foo(new Date)",
"{var x$jscomp$inline_0=new Date;" +
"x$jscomp$inline_0+x$jscomp$inline_0}");
}
public void testInlineBlockMutableArgs3() {
test("function foo(x){x+x}foo(true&&new Date)",
"{var x$jscomp$inline_0=true&&new Date;" +
"x$jscomp$inline_0+x$jscomp$inline_0}");
}
public void testInlineBlockMutableArgs4() {
test("function foo(x){x+x}foo({})",
"{var x$jscomp$inline_0={};" +
"x$jscomp$inline_0+x$jscomp$inline_0}");
}
public void testShadowVariables1() {
// The Normalize pass now guarantees that that globals are never shadowed
// by locals.
// "foo" is inlined here as its parameter "a" doesn't conflict.
// "bar" is assigned a new name.
test("var a=0;" +
"function foo(a){return 3+a}" +
"function bar(){var a=foo(4)}" +
"bar();",
"var a=0;" +
"{var a$jscomp$inline_0=3+4}");
}
public void testShadowVariables2() {
// "foo" is inlined here as its parameter "a" doesn't conflict.
// "bar" is inlined as its uses global "a", and does introduce any new
// globals.
test("var a=0;" +
"function foo(a){return 3+a}" +
"function bar(){a=foo(4)}" +
"bar()",
"var a=0;" +
"{a=3+4}");
}
public void testShadowVariables3() {
// "foo" is inlined into exported "_bar", aliasing foo's "a".
test("var a=0;" +
"function foo(){var a=2;return 3+a}" +
"function _bar(){a=foo()}",
"var a=0;" +
"function _bar(){{var a$jscomp$inline_0=2;" +
"a=3+a$jscomp$inline_0}}");
}
public void testShadowVariables4() {
// "foo" is inlined.
// block access to global "a".
test("var a=0;" +
"function foo(){return 3+a}" +
"function _bar(a){a=foo(4)+a}",
"var a=0;function _bar(a$jscomp$1){" +
"a$jscomp$1=" +
"3+a+a$jscomp$1}");
}
public void testShadowVariables5() {
// Can't yet inline multiple statements functions into expressions
// (though some are possible using the COMMA operator).
allowBlockInlining = false;
testSame("var a=0;" +
"function foo(){var a=4;return 3+a}" +
"function _bar(a){a=foo(4)+a}");
}
public void testShadowVariables6() {
test("var a=0;" +
"function foo(){var a=4;return 3+a}" +
"function _bar(a){a=foo(4)}",
"var a=0;function _bar(a$jscomp$2){{" +
"var a$jscomp$inline_0=4;" +
"a$jscomp$2=3+a$jscomp$inline_0}}");
}
public void testShadowVariables7() {
assumeMinimumCapture = false;
test("var a=3;" +
"function foo(){return a}" +
"(function(){var a=5;(function(){foo()})()})()",
"var a=3;" +
"{var a$jscomp$inline_0=5;{a}}"
);
assumeMinimumCapture = true;
test("var a=3;" +
"function foo(){return a}" +
"(function(){var a=5;(function(){foo()})()})()",
"var a=3;" +
"{var a$jscomp$inline_1=5;{a}}"
);
}
public void testShadowVariables8() {
// this should be inlined
test("var a=0;" +
"function foo(){return 3}" +
"function _bar(){var a=foo()}",
"var a=0;" +
"function _bar(){var a=3}");
}
public void testShadowVariables9() {
// this should be inlined too [even if the global is not declared]
test("function foo(){return 3}" +
"function _bar(){var a=foo()}",
"function _bar(){var a=3}");
}
public void testShadowVariables10() {
// callee var must be renamed.
test("var a;function foo(){return a}" +
"function _bar(){var a=foo()}",
"var a;function _bar(){var a$jscomp$1=a}");
}
public void testShadowVariables11() {
// The call has a local variable
// which collides with the function being inlined
test("var a=0;var b=1;" +
"function foo(){return a+a}" +
"function _bar(){var a=foo();alert(a)}",
"var a=0;var b=1;" +
"function _bar(){var a$jscomp$1=a+a;" +
"alert(a$jscomp$1)}"
);
}
public void testShadowVariables12() {
// 2 globals colliding
test("var a=0;var b=1;" +
"function foo(){return a+b}" +
"function _bar(){var a=foo(),b;alert(a)}",
"var a=0;var b=1;" +
"function _bar(){var a$jscomp$1=a+b," +
"b$jscomp$1;" +
"alert(a$jscomp$1)}");
}
public void testShadowVariables13() {
// The only change is to remove the collision
test("var a=0;var b=1;" +
"function foo(){return a+a}" +
"function _bar(){var c=foo();alert(c)}",
"var a=0;var b=1;" +
"function _bar(){var c=a+a;alert(c)}");
}
public void testShadowVariables14() {
// There is a collision even though it is not read.
test("var a=0;var b=1;" +
"function foo(){return a+b}" +
"function _bar(){var c=foo(),b;alert(c)}",
"var a=0;var b=1;" +
"function _bar(){var c=a+b," +
"b$jscomp$1;alert(c)}");
}
public void testShadowVariables15() {
// Both parent and child reference a global
test("var a=0;var b=1;" +
"function foo(){return a+a}" +
"function _bar(){var c=foo();alert(c+a)}",
"var a=0;var b=1;" +
"function _bar(){var c=a+a;alert(c+a)}");
}
public void testShadowVariables16() {
assumeMinimumCapture = false;
// Inline functions defined as a child of the CALL node.
test("var a=3;" +
"function foo(){return a}" +
"(function(){var a=5;(function(){foo()})()})()",
"var a=3;" +
"{var a$jscomp$inline_0=5;{a}}"
);
assumeMinimumCapture = true;
// Inline functions defined as a child of the CALL node.
test("var a=3;" +
"function foo(){return a}" +
"(function(){var a=5;(function(){foo()})()})()",
"var a=3;" +
"{var a$jscomp$inline_1=5;{a}}"
);
}
public void testShadowVariables17() {
test("var a=0;" +
"function bar(){return a+a}" +
"function foo(){return bar()}" +
"function _goo(){var a=2;var x=foo();}",
"var a=0;" +
"function _goo(){var a$jscomp$1=2;var x=a+a}");
}
public void testShadowVariables18() {
test("var a=0;" +
"function bar(){return a+a}" +
"function foo(){var a=3;return bar()}" +
"function _goo(){var a=2;var x=foo();}",
"var a=0;" +
"function _goo(){var a$jscomp$2=2;var x;" +
"{var a$jscomp$inline_0=3;x=a+a}}");
}
public void testCostBasedInlining1() {
testSame(
"function foo(a){return a}" +
"foo=new Function(\"return 1\");" +
"foo(1)");
}
public void testCostBasedInlining2() {
// Baseline complexity tests.
// Single call, function not removed.
test(
"function foo(a){return a}" +
"var b=foo;" +
"function _t1(){return foo(1)}",
"function foo(a){return a}" +
"var b=foo;" +
"function _t1(){return 1}");
}
public void testCostBasedInlining3() {
// Two calls, function not removed.
test(
"function foo(a,b){return a+b}" +
"var b=foo;" +
"function _t1(){return foo(1,2)}" +
"function _t2(){return foo(2,3)}",
"function foo(a,b){return a+b}" +
"var b=foo;" +
"function _t1(){return 1+2}" +
"function _t2(){return 2+3}");
}
public void testCostBasedInlining4() {
// Two calls, function not removed.
// Here there isn't enough savings to justify inlining.
testSame(
"function foo(a,b){return a+b+a+b}" +
"var b=foo;" +
"function _t1(){return foo(1,2)}" +
"function _t2(){return foo(2,3)}");
}
public void testCostBasedInlining5() {
// Here there is enough savings to justify inlining.
test(
"function foo(a,b){return a+b+a+b}" +
"function _t1(){return foo(1,2)}" +
"function _t2(){return foo(2,3)}",
"function _t1(){return 1+2+1+2}" +
"function _t2(){return 2+3+2+3}");
}
public void testCostBasedInlining6() {
// Here we have a threshold test.
// Do inline here: