diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/MethodInliner.java b/dev/core/src/com/google/gwt/dev/jjs/impl/MethodInliner.java
index eaab6ad..3b42619 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/impl/MethodInliner.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/MethodInliner.java
@@ -33,6 +33,7 @@ import com.google.gwt.dev.jjs.ast.JReturnStatement;
 import com.google.gwt.dev.jjs.ast.JStatement;
 import com.google.gwt.dev.jjs.ast.JThisRef;
 import com.google.gwt.dev.jjs.ast.JVisitor;
+import com.google.gwt.dev.jjs.ast.JNode;
 import com.google.gwt.dev.jjs.ast.js.JMultiExpression;
 
 import java.util.ArrayList;
@@ -493,7 +494,11 @@ public class MethodInliner {
   }
 
   public static boolean exec(JProgram program) {
-    return new MethodInliner(program).execImpl();
+    return exec(program, program);
+  }
+
+  public static boolean exec(JProgram program, JNode node) {
+    return new MethodInliner(program).execImpl(node);
   }
 
   private JMethod currentMethod;
@@ -503,11 +508,11 @@ public class MethodInliner {
     this.program = program;
   }
 
-  private boolean execImpl() {
+  private boolean execImpl(JNode node) {
     boolean madeChanges = false;
     while (true) {
       InliningVisitor inliner = new InliningVisitor();
-      inliner.accept(program);
+      inliner.accept(node);
       if (!inliner.didChange()) {
         break;
       }
diff --git a/dev/core/test/com/google/gwt/dev/jjs/impl/JJSOptimizerTestCase.java b/dev/core/test/com/google/gwt/dev/jjs/impl/JJSOptimizerTestCase.java
new file mode 100644
index 0000000..9a59d4e
--- /dev/null
+++ b/dev/core/test/com/google/gwt/dev/jjs/impl/JJSOptimizerTestCase.java
@@ -0,0 +1,195 @@
+/*
+ * Copyright 2008 Google Inc.
+ *
+ * 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.gwt.dev.jjs.impl;
+
+import com.google.gwt.core.ext.TreeLogger;
+import com.google.gwt.core.ext.UnableToCompleteException;
+import com.google.gwt.core.ext.linker.ArtifactSet;
+import com.google.gwt.dev.cfg.BindingProperty;
+import com.google.gwt.dev.cfg.ConfigurationProperty;
+import com.google.gwt.dev.cfg.ModuleDef;
+import com.google.gwt.dev.cfg.ModuleDefLoader;
+import com.google.gwt.dev.cfg.StaticPropertyOracle;
+import com.google.gwt.dev.javac.CompilationState;
+import com.google.gwt.dev.jdt.RebindOracle;
+import com.google.gwt.dev.jdt.RebindPermutationOracle;
+import com.google.gwt.dev.jdt.WebModeCompilerFrontEnd;
+import com.google.gwt.dev.jjs.JJSOptions;
+import com.google.gwt.dev.jjs.JavaToJavaScriptCompiler;
+import com.google.gwt.dev.jjs.ast.JNode;
+import com.google.gwt.dev.jjs.ast.JProgram;
+import com.google.gwt.dev.shell.StandardRebindOracle;
+import com.google.gwt.dev.util.log.PrintWriterTreeLogger;
+
+import junit.extensions.TestSetup;
+import junit.framework.Protectable;
+import junit.framework.TestCase;
+import junit.framework.TestResult;
+import junit.framework.TestSuite;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * Base test class for jjs optimization passes. This test case reporoduces
+ * the environment in which all optimizations are run.
+ * <p/>
+ * To create new optimization test, extend the class and use program field
+ * in setUp/test/tearDown methods. To have everything correctly initialized,
+ * create a static suite() method, which wraps your test suite into
+ * {@link JJSOptimizerTestDecorator}.
+ *
+ * @see com.google.gwt.dev.jjs.impl.MethodInlinerTest
+ */
+public abstract class JJSOptimizerTestCase extends TestCase {
+  /**
+   * Change this value if you want your tests to log to conole.
+   */
+  private static final boolean LOG_TO_CONSOLE = false;
+
+  private static final TreeLogger LOGGER = LOG_TO_CONSOLE ?
+      new PrintWriterTreeLogger() : TreeLogger.NULL;
+
+  protected JProgram program;
+
+  // This is private, because there's no need to
+  // publish our initialization details. All descendants should work with
+  // instance method.
+  private static JProgram staticJprogram;
+
+  @Override
+  protected void setUp() throws Exception {
+    super.setUp();
+    program = staticJprogram;
+  }
+
+
+  /**
+   * Helper method to compare node source with predefined expected data
+   * @param node the node, which source is checked
+   * @param expectedResourceName name of resource with expected source code.
+   * Th resource will be obtained through
+   * <code>getClass().getResourceAsStream()</code>
+   */
+  protected void assertExpectedText(JNode node,
+                                    String expectedResourceName)
+      throws IOException {
+    InputStream in = getClass().getResourceAsStream(expectedResourceName);
+    assertNotNull("Expected data " + expectedResourceName + " not found",
+      in);
+
+    ByteArrayOutputStream os = new ByteArrayOutputStream();
+    int ch;
+    while ((ch = in.read()) != -1) {
+      os.write(ch);
+    }
+    String expectedText = new String(os.toByteArray(), "UTF-8");
+    assertEquals(expectedText, node.toSource());
+  }
+
+
+  /**
+   * Decorate test, to be actually executed inside
+   * {@link JavaToJavaScriptCompiler#optimize(com.google.gwt.dev.jjs.ast.JProgram)}
+   * method.
+   */
+  protected static class JJSOptimizerTestDecorator extends TestSetup {
+    private final String moduleName;
+    private WebModeCompilerFrontEnd frontEnd;
+    private ModuleDef module;
+
+    public JJSOptimizerTestDecorator(TestSuite test, String moduleName) {
+      super(test);
+      this.moduleName = moduleName;
+    }
+
+    @Override
+    protected void setUp() throws Exception {
+      super.setUp();
+
+      module = ModuleDefLoader.loadFromClassPath(LOGGER, moduleName);
+      CompilationState compilationState = module.getCompilationState();
+      compilationState.compile(LOGGER);
+
+      TestRebindPermutationOracle rpo = new TestRebindPermutationOracle(module);
+      frontEnd = new WebModeCompilerFrontEnd(compilationState, rpo);
+    }
+
+    @Override
+    protected void tearDown() throws Exception {
+      staticJprogram = null;
+      super.tearDown();
+    }
+
+    @Override
+    public void basicRun(final TestResult result) {
+      result.runProtected(this, new Protectable() {
+        public void protect() throws Exception {
+          runProtected(result);
+        }
+      });
+    }
+
+    private void runProtected(final TestResult result)
+        throws UnableToCompleteException {
+      new JavaToJavaScriptCompiler(JJSOptimizerTestCase.LOGGER,
+          JJSOptimizerTestDecorator.this.frontEnd,
+          JJSOptimizerTestDecorator.this.module.getEntryPointTypeNames(),
+          new JJSOptions()) {
+        @Override
+        protected void optimize(final JProgram jprogram)
+            throws InterruptedException {
+          result.runProtected(JJSOptimizerTestDecorator.this, new Protectable() {
+            public void protect() throws Throwable {
+              JJSOptimizerTestCase.staticJprogram = jprogram;
+              fTest.run(result);
+            }
+          });
+        }
+      };
+    }
+
+  }
+
+  private static class TestRebindPermutationOracle
+      implements RebindPermutationOracle {
+    private RebindOracle rebindOracle;
+
+    public TestRebindPermutationOracle(ModuleDef module) {
+      BindingProperty userAgent = new BindingProperty("user.agent");
+      userAgent.addDefinedValue("gecko");
+      userAgent.setAllowedValues("gecko");
+
+      StaticPropertyOracle propertyOracle = new StaticPropertyOracle(
+          new BindingProperty[]{userAgent}, new String[]{"gecko"},
+          new ConfigurationProperty[0]);
+      rebindOracle = new StandardRebindOracle(module.getCompilationState(),
+          propertyOracle, module, module.getRules(), null, null,
+          new ArtifactSet());
+    }
+
+    public String[] getAllPossibleRebindAnswers(TreeLogger logger,
+                                                String requestTypeName)
+        throws UnableToCompleteException {
+      return new String[]{rebindOracle.rebind(logger, requestTypeName)};
+    }
+
+    public int getPermuationCount() {
+      return 2; // otherwise optimizations won' run
+    }
+  }
+}
diff --git a/dev/core/test/com/google/gwt/dev/jjs/impl/MethodInlinerTest.gwt.xml b/dev/core/test/com/google/gwt/dev/jjs/impl/MethodInlinerTest.gwt.xml
new file mode 100644
index 0000000..60ffdff
--- /dev/null
+++ b/dev/core/test/com/google/gwt/dev/jjs/impl/MethodInlinerTest.gwt.xml
@@ -0,0 +1,21 @@
+<!--
+  ~ Copyright 2008 Google Inc.
+  ~
+  ~ 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.
+  -->
+
+<module>
+  <inherits name="com.google.gwt.core.Core"/>
+  <source path="data"/>
+  <entry-point class="com.google.gwt.dev.jjs.impl.data.MethodInlinerTestData"/>
+</module>
\ No newline at end of file
diff --git a/dev/core/test/com/google/gwt/dev/jjs/impl/MethodInlinerTest.java b/dev/core/test/com/google/gwt/dev/jjs/impl/MethodInlinerTest.java
new file mode 100644
index 0000000..3c0b8af
--- /dev/null
+++ b/dev/core/test/com/google/gwt/dev/jjs/impl/MethodInlinerTest.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright 2008 Google Inc.
+ *
+ * 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.gwt.dev.jjs.impl;
+
+
+import com.google.gwt.dev.jjs.ast.JClassType;
+import com.google.gwt.dev.jjs.impl.data.MethodInlinerTestData;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+import java.io.IOException;
+
+/**
+ * Test for {@link MethodInliner} optimization pass.
+ */
+public class MethodInlinerTest extends JJSOptimizerTestCase {
+  private static final String MODULE_NAME = MethodInlinerTest.class.getName();
+  private JClassType fixtureClass;
+
+  @Override
+  protected void setUp() throws Exception {
+    super.setUp();
+
+    String fixtureName = getFixtureName();
+    fixtureClass = ((JClassType) program.getFromTypeMap(fixtureName));
+    assertNotNull("Fixture class: " + fixtureName + " not found", fixtureClass);
+  }
+
+  public void testBasicInline() throws Exception {
+    inline();
+    
+    assertExpectedText();
+  }
+
+  public void testTwoExpressionsCanBeInlined() throws Exception {
+    inline();
+
+    assertExpectedText();
+  }
+
+  public void testMoreThanTwoExpressionsCantBeInlined() throws Exception {
+    inline();
+
+    assertExpectedText();
+  }
+
+  private void assertExpectedText() throws IOException {
+    String expectedResource = "/" + fixtureClass.getName().replace('$', '/').
+        replace('.', '/') + ".txt";
+    assertExpectedText(fixtureClass, expectedResource);
+  }
+
+  private void inline() {
+    MethodInliner.exec(program, fixtureClass);
+  }
+
+  private String getFixtureName() {
+    String testName = getName();
+    if (testName.startsWith("test"))
+      testName = testName.substring("test".length());
+    return MethodInlinerTestData.class.getName() + "." + testName;
+  }
+
+  public static Test suite() {
+    TestSuite suite = new TestSuite();
+    suite.addTestSuite(MethodInlinerTest.class);
+
+    return new JJSOptimizerTestDecorator(suite, MODULE_NAME);
+  }
+}
diff --git a/dev/core/test/com/google/gwt/dev/jjs/impl/data/MethodInlinerTestData.java b/dev/core/test/com/google/gwt/dev/jjs/impl/data/MethodInlinerTestData.java
new file mode 100644
index 0000000..1a4e92e
--- /dev/null
+++ b/dev/core/test/com/google/gwt/dev/jjs/impl/data/MethodInlinerTestData.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright 2008 Google Inc.
+ *
+ * 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.gwt.dev.jjs.impl.data;
+
+/**
+ * Test data for MethodInliner test
+ */
+public class MethodInlinerTestData {
+  public void onModuleLoad() {}
+
+
+  public static class BasicInline {
+    public static int foo() {
+      return bar();
+    }
+
+    public static int bar() {
+      return 3;
+    }
+  }
+
+  public static class TwoExpressionsCanBeInlined {
+    public static int foo() {
+      return bar();
+    }
+
+    public static int bar() {
+      baz();
+      return 3;
+    }
+
+    public static void baz() {
+      for (int i = 0; i < 10; i++) {
+        i++;
+      }
+    }
+  }
+
+  public static class MoreThanTwoExpressionsCantBeInlined {
+    public static int foo() {
+      return bar();
+    }
+
+    public static int bar() {
+      baz();
+      baz();
+      return 3;
+    }
+
+    public static void baz() {
+      for (int i = 0; i < 10; i++) {
+        i++;
+      }
+    }
+  }
+}
diff --git a/dev/core/test/com/google/gwt/dev/jjs/impl/data/MethodInlinerTestData/BasicInline.txt b/dev/core/test/com/google/gwt/dev/jjs/impl/data/MethodInlinerTestData/BasicInline.txt
new file mode 100644
index 0000000..2aa0966
--- /dev/null
+++ b/dev/core/test/com/google/gwt/dev/jjs/impl/data/MethodInlinerTestData/BasicInline.txt
@@ -0,0 +1,24 @@
+static class MethodInlinerTestData$BasicInline extends Object {
+  public Class getClass(){
+    return MethodInlinerTestData$BasicInline.class;
+  }
+
+  public final MethodInlinerTestData$BasicInline MethodInlinerTestData$BasicInline(){
+    this.Object();
+    this.$init();
+    return this;
+  }
+
+  public static final MethodInlinerTestData$BasicInline new(){
+    return (new MethodInlinerTestData$BasicInline()).MethodInlinerTestData$BasicInline();
+  }
+
+  public static int foo(){
+    return 3;
+  }
+
+  public static int bar(){
+    return 3;
+  }
+
+}
\ No newline at end of file
diff --git a/dev/core/test/com/google/gwt/dev/jjs/impl/data/MethodInlinerTestData/MoreThanTwoExpressionsCantBeInlined.txt b/dev/core/test/com/google/gwt/dev/jjs/impl/data/MethodInlinerTestData/MoreThanTwoExpressionsCantBeInlined.txt
new file mode 100644
index 0000000..e6b87ef
--- /dev/null
+++ b/dev/core/test/com/google/gwt/dev/jjs/impl/data/MethodInlinerTestData/MoreThanTwoExpressionsCantBeInlined.txt
@@ -0,0 +1,32 @@
+static class MethodInlinerTestData$MoreThanTwoExpressionsCantBeInlined extends Object {
+  public Class getClass(){
+    return MethodInlinerTestData$MoreThanTwoExpressionsCantBeInlined.class;
+  }
+
+  public final MethodInlinerTestData$MoreThanTwoExpressionsCantBeInlined MethodInlinerTestData$MoreThanTwoExpressionsCantBeInlined(){
+    this.Object();
+    this.$init();
+    return this;
+  }
+
+  public static final MethodInlinerTestData$MoreThanTwoExpressionsCantBeInlined new(){
+    return (new MethodInlinerTestData$MoreThanTwoExpressionsCantBeInlined()).MethodInlinerTestData$MoreThanTwoExpressionsCantBeInlined();
+  }
+
+  public static int foo(){
+    return MethodInlinerTestData$MoreThanTwoExpressionsCantBeInlined.bar();
+  }
+
+  public static int bar(){
+    MethodInlinerTestData$MoreThanTwoExpressionsCantBeInlined.baz();
+    MethodInlinerTestData$MoreThanTwoExpressionsCantBeInlined.baz();
+    return 3;
+  }
+
+  public static void baz(){
+    for (int i = 0; i < 10; i++) {
+      i++;
+    }
+  }
+
+}
\ No newline at end of file
diff --git a/dev/core/test/com/google/gwt/dev/jjs/impl/data/MethodInlinerTestData/TwoExpressionsCanBeInlined.txt b/dev/core/test/com/google/gwt/dev/jjs/impl/data/MethodInlinerTestData/TwoExpressionsCanBeInlined.txt
new file mode 100644
index 0000000..af679c8
--- /dev/null
+++ b/dev/core/test/com/google/gwt/dev/jjs/impl/data/MethodInlinerTestData/TwoExpressionsCanBeInlined.txt
@@ -0,0 +1,31 @@
+static class MethodInlinerTestData$TwoExpressionsCanBeInlined extends Object {
+  public Class getClass(){
+    return MethodInlinerTestData$TwoExpressionsCanBeInlined.class;
+  }
+
+  public final MethodInlinerTestData$TwoExpressionsCanBeInlined MethodInlinerTestData$TwoExpressionsCanBeInlined(){
+    this.Object();
+    this.$init();
+    return this;
+  }
+
+  public static final MethodInlinerTestData$TwoExpressionsCanBeInlined new(){
+    return (new MethodInlinerTestData$TwoExpressionsCanBeInlined()).MethodInlinerTestData$TwoExpressionsCanBeInlined();
+  }
+
+  public static int foo(){
+    return (MethodInlinerTestData$TwoExpressionsCanBeInlined.baz(), 3);
+  }
+
+  public static int bar(){
+    MethodInlinerTestData$TwoExpressionsCanBeInlined.baz();
+    return 3;
+  }
+
+  public static void baz(){
+    for (int i = 0; i < 10; i++) {
+      i++;
+    }
+  }
+
+}
\ No newline at end of file
