/* * Copyright 2007 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; public class InlineSimpleMethodsTest extends CompilerTestCase { public InlineSimpleMethodsTest() { super("", false); } @Override protected void setUp() throws Exception { super.setUp(); super.enableLineNumberCheck(true); } @Override protected CompilerPass getProcessor(Compiler compiler) { return new InlineSimpleMethods(compiler); } /** * Helper for tests that expects definitions to remain unchanged, such * that {@code definitions+js} is converted to {@code definitions+expected}. */ private void testWithPrefix(String definitions, String js, String expected) { test(definitions + js, definitions + expected); } public void testSimpleInline1() { testWithPrefix("function Foo(){}" + "Foo.prototype.bar=function(){return this.baz};", "var x=(new Foo).bar();var y=(new Foo).bar();", "var x=(new Foo).baz;var y=(new Foo).baz"); } public void testSimpleInline2() { testWithPrefix("function Foo(){}" + "Foo.prototype={bar:function(){return this.baz}};", "var x=(new Foo).bar();var y=(new Foo).bar();", "var x=(new Foo).baz;var y=(new Foo).baz"); } public void testSimpleGetterInline1() { // TODO(johnlenz): Support this case. testSame("function Foo(){}" + "Foo.prototype={get bar(){return this.baz}};" + "var x=(new Foo).bar;var y=(new Foo).bar"); // Verify we are not confusing calling the result of an ES5 getter // with call the getter. testSame("function Foo(){}" + "Foo.prototype={get bar(){return this.baz}};" + "var x=(new Foo).bar();var y=(new Foo).bar()"); } public void testSimpleSetterInline1() { // Verify 'get' and 'set' are not confused. testSame("function Foo(){}" + "Foo.prototype={set bar(a){return this.baz}};" + "var x=(new Foo).bar;var y=(new Foo).bar"); testSame("function Foo(){}" + "Foo.prototype={set bar(a){return this.baz}};" + "var x=(new Foo).bar();var y=(new Foo).bar()"); } public void testSelfInline() { testWithPrefix("function Foo(){}" + "Foo.prototype.bar=function(){return this.baz};", "Foo.prototype.meth=function(){this.bar();}", "Foo.prototype.meth=function(){this.baz}"); } public void testCallWithArgs() { testWithPrefix("function Foo(){}" + "Foo.prototype.bar=function(){return this.baz};", "var x=(new Foo).bar(3,new Foo)", "var x=(new Foo).bar(3,new Foo)"); } public void testCallWithConstArgs() { testWithPrefix("function Foo(){}" + "Foo.prototype.bar=function(a){return this.baz};", "var x=(new Foo).bar(3, 4)", "var x=(new Foo).baz"); } public void testNestedProperties() { testWithPrefix("function Foo(){}" + "Foo.prototype.bar=function(){return this.baz.ooka};", "(new Foo).bar()", "(new Foo).baz.ooka"); } public void testSkipComplexMethods() { testWithPrefix("function Foo(){}" + "Foo.prototype.bar=function(){return this.baz};" + "Foo.prototype.condy=function(){return this.baz?this.baz:1};", "var x=(new Foo).argy()", "var x=(new Foo).argy()"); } public void testSkipConflictingMethods() { testWithPrefix("function Foo(){}" + "Foo.prototype.bar=function(){return this.baz};" + "Foo.prototype.bar=function(){return this.bazz};", "var x=(new Foo).bar()", "var x=(new Foo).bar()"); } public void testSameNamesDifferentDefinitions() { testWithPrefix("function A(){}" + "A.prototype.g=function(){return this.a};" + "function B(){}" + "B.prototype.g=function(){return this.b};", "var x=(new A).g();" + "var y=(new B).g();" + "var a=new A;" + "var ag=a.g();", "var x=(new A).g();" + "var y=(new B).g();" + "var a=new A;" + "var ag=a.g()"); } public void testSameNamesSameDefinitions() { testWithPrefix("function A(){}" + "A.prototype.g=function(){return this.a};" + "function B(){}" + "B.prototype.g=function(){return this.a};", "var x=(new A).g();" + "var y=(new B).g();" + "var a=new A;" + "var ag=a.g();", "var x=(new A).a;" + "var y=(new B).a;" + "var a=new A;" + "var ag=a.a"); } public void testConfusingNames() { testWithPrefix("function Foo(){}" + "Foo.prototype.bar=function(){return this.baz};", "function bar(){var bar=function(){};bar()}", "function bar(){var bar=function(){};bar()}"); } public void testConstantInline() { testWithPrefix("function Foo(){}" + "Foo.prototype.bar=function(){return 3};", "var f=new Foo;var x=f.bar()", "var f=new Foo;var x=3"); } public void testConstantArrayInline() { testWithPrefix("function Foo(){}" + "Foo.prototype.bar=function(){return[3,4]};", "var f=new Foo;var x=f.bar()", "var f=new Foo;var x=[3,4]"); } public void testConstantInlineWithSideEffects() { testWithPrefix("function Foo(){}" + "Foo.prototype.bar=function(){return 3};", "var x=(new Foo).bar()", "var x=(new Foo).bar()"); } public void testEmptyMethodInline() { testWithPrefix("function Foo(){}" + "Foo.prototype.bar=function(a){};", "var x=new Foo; x.bar();", "var x=new Foo"); } public void testEmptyMethodInlineWithSideEffects() { testWithPrefix("function Foo(){}" + "Foo.prototype.bar=function(){};", "(new Foo).bar();var y=new Foo;y.bar(new Foo)", "(new Foo).bar();var y=new Foo;y.bar(new Foo)"); } public void testEmptyMethodInlineInAssign1() { testWithPrefix("function Foo(){}" + "Foo.prototype.bar=function(){};", "var x=new Foo;var y=x.bar()", "var x=new Foo;var y=void 0"); } public void testEmptyMethodInlineInAssign2() { testWithPrefix("function Foo(){}" + "Foo.prototype.bar=function(){};", "var x=new Foo;var y=x.bar().toString()", "var x=new Foo;var y=(void 0).toString()"); } public void testNormalMethod() { testWithPrefix("function Foo(){}" + "Foo.prototype.bar=function(){var x=1};", "var x=new Foo;x.bar()", "var x=new Foo;x.bar()"); } public void testNoInlineOfExternMethods1() { testSame("var external={};external.charAt;", "external.charAt()", (DiagnosticType) null); } public void testNoInlineOfExternMethods2() { testSame("var external={};external.charAt=function(){};", "external.charAt()", (DiagnosticType) null); } public void testNoInlineOfExternMethods3() { testSame("var external={};external.bar=function(){};", "function Foo(){}Foo.prototype.bar=function(){};(new Foo).bar()", (DiagnosticType) null); } public void testNoInlineOfDangerousProperty() { testSame("function Foo(){this.bar=3}" + "Foo.prototype.bar=function(){};" + "var x=new Foo;var y=x.bar()"); } // Don't warn about argument naming conventions (this is done in another pass) // opt_ parameters must not be followed by non-optional parameters. // var_args must be last public void testNoWarn() { testSame("function Foo(){}" + "Foo.prototype.bar=function(opt_a,b){var x=1};" + "var x=new Foo;x.bar()"); testSame("function Foo(){}" + "Foo.prototype.bar=function(var_args,b){var x=1};" + "var x=new Foo;x.bar()"); } public void testObjectLit() { testSame("Foo.prototype.bar=function(){return this.baz_};" + "var blah={bar:function(){}};" + "(new Foo).bar()"); } public void testObjectLit2() { testSame("var blah={bar:function(){}};" + "(new Foo).bar()"); } public void testObjectLitExtern() { String externs = "window.bridge={_sip:function(){}};"; testSame(externs, "window.bridge._sip()", null); } public void testExternFunction() { String externs = "function emptyFunction() {}"; testSame(externs, "function Foo(){this.empty=emptyFunction}" + "(new Foo).empty()", null); } public void testIssue2508576_1() { // Method defined by an extern should be left alone. String externs = "function alert(a) {}"; testSame(externs, "({a:alert,b:alert}).a(\"a\")", null); } public void testIssue2508576_2() { // Anonymous object definition with a side-effect should be left alone. testSame("({a:function(){},b:x()}).a(\"a\")"); } public void testIssue2508576_3() { // Anonymous object definition without side-effect should be removed. test("({a:function(){},b:alert}).a(\"a\")", ""); } public void testAnonymousGet() { // Anonymous object definition without side-effect should be removed. testSame("({get a(){return function(){}},b:alert}).a(\"a\")"); testSame("({get a(){},b:alert}).a(\"a\")"); testSame("({get a(){},b:alert}).a"); } public void testAnonymousSet() { // Anonymous object definition without side-effect should be removed. testSame("({set a(b){return function(){}},b:alert}).a(\"a\")"); testSame("({set a(b){},b:alert}).a(\"a\")"); testSame("({set a(b){},b:alert}).a"); } }