Supports a new <when-linker-added> tag in GWT module files.

http://gwt-code-reviews.appspot.com/150801


git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@7672 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/dev/core/src/com/google/gwt/dev/CompilePerms.java b/dev/core/src/com/google/gwt/dev/CompilePerms.java
index cc1019e..0a0a5b6 100644
--- a/dev/core/src/com/google/gwt/dev/CompilePerms.java
+++ b/dev/core/src/com/google/gwt/dev/CompilePerms.java
@@ -333,7 +333,7 @@
 
     ModuleDef module = ModuleDefLoader.loadFromClassPath(logger, moduleName);
     PropertyPermutations allPermutations = new PropertyPermutations(
-        module.getProperties());
+        module.getProperties(), module.getActiveLinkerNames());
     int[] perms = options.getPermsToCompile();
     if (perms == null) {
       perms = new int[allPermutations.size()];
diff --git a/dev/core/src/com/google/gwt/dev/Link.java b/dev/core/src/com/google/gwt/dev/Link.java
index 8cf22f4..b585ccc 100644
--- a/dev/core/src/com/google/gwt/dev/Link.java
+++ b/dev/core/src/com/google/gwt/dev/Link.java
@@ -30,6 +30,7 @@
 import com.google.gwt.dev.cfg.BindingProperty;
 import com.google.gwt.dev.cfg.ModuleDef;
 import com.google.gwt.dev.cfg.ModuleDefLoader;
+import com.google.gwt.dev.cfg.PropertyPermutations;
 import com.google.gwt.dev.cfg.StaticPropertyOracle;
 import com.google.gwt.dev.jjs.JJSOptions;
 import com.google.gwt.dev.jjs.PermutationResult;
@@ -547,7 +548,7 @@
         Permutation[] perms = precomp.getPermutations();
         List<FileBackedObject<PermutationResult>> resultFiles = CompilePerms.makeResultFiles(
             compilerWorkDir, perms);
-        
+
         // Check that all files are present
         for (FileBackedObject<PermutationResult> file : resultFiles) {
           if (!file.getFile().exists()) {
@@ -582,7 +583,8 @@
   private boolean doLinkFinal(TreeLogger logger, File compilerWorkDir,
       ModuleDef module, JJSOptions precompileOptions)
       throws UnableToCompleteException {
-    int numPermutations = module.getProperties().numPermutations();
+    int numPermutations = new PropertyPermutations(module.getProperties(),
+        module.getActiveLinkerNames()).size();
     List<File> resultFiles = new ArrayList<File>(numPermutations);
     for (int i = 0; i < numPermutations; ++i) {
       File f = CompilePerms.makePermFilename(compilerWorkDir, i);
diff --git a/dev/core/src/com/google/gwt/dev/Precompile.java b/dev/core/src/com/google/gwt/dev/Precompile.java
index b2801f0..7a41eff 100644
--- a/dev/core/src/com/google/gwt/dev/Precompile.java
+++ b/dev/core/src/com/google/gwt/dev/Precompile.java
@@ -425,7 +425,7 @@
       JJSOptions jjsOptions, ModuleDef module, File genDir,
       File generatorResourcesDir, File dumpSignatureFile) {
     PropertyPermutations allPermutations = new PropertyPermutations(
-        module.getProperties());
+        module.getProperties(), module.getActiveLinkerNames());
     return precompile(logger, jjsOptions, module, 0, allPermutations, genDir,
         generatorResourcesDir, dumpSignatureFile);
   }
@@ -466,8 +466,8 @@
       ArtifactSet generatorArtifacts = new ArtifactSet();
       DistillerRebindPermutationOracle rpo = new DistillerRebindPermutationOracle(
           module, compilationState, generatorArtifacts,
-          new PropertyPermutations(module.getProperties()), genDir,
-          generatorResourcesDir);
+          new PropertyPermutations(module.getProperties(),
+              module.getActiveLinkerNames()), genDir, generatorResourcesDir);
       // Allow GC later.
       compilationState = null;
       if (dumpSignatureFile != null) {
@@ -624,7 +624,8 @@
         TreeLogger branch = logger.branch(TreeLogger.INFO,
             "Precompiling (minimal) module " + module.getName());
         Util.writeObjectAsFile(logger, precompilationFile, options);
-        int numPermutations = module.getProperties().numPermutations();
+        int numPermutations = new PropertyPermutations(module.getProperties(),
+            module.getActiveLinkerNames()).size();
         Util.writeStringAsFile(logger, new File(compilerWorkDir,
             PERM_COUNT_FILENAME), String.valueOf(numPermutations));
         branch.log(TreeLogger.INFO,
diff --git a/dev/core/src/com/google/gwt/dev/cfg/Condition.java b/dev/core/src/com/google/gwt/dev/cfg/Condition.java
index 3839abb..f44d2d7 100644
--- a/dev/core/src/com/google/gwt/dev/cfg/Condition.java
+++ b/dev/core/src/com/google/gwt/dev/cfg/Condition.java
@@ -15,10 +15,8 @@
  */
 package com.google.gwt.dev.cfg;
 
-import com.google.gwt.core.ext.PropertyOracle;
 import com.google.gwt.core.ext.TreeLogger;
 import com.google.gwt.core.ext.UnableToCompleteException;
-import com.google.gwt.core.ext.typeinfo.TypeOracle;
 import com.google.gwt.dev.util.collect.Sets;
 
 import java.io.Serializable;
@@ -36,28 +34,32 @@
     return Sets.create();
   }
 
-  public final boolean isTrue(TreeLogger logger, PropertyOracle propertyOracle,
-      TypeOracle typeOracle, String testType) throws UnableToCompleteException {
+  /**
+   * Test the condition with the given parameters. If <code>testType</code> is
+   * <code>null</code>, then this condition isn't being used to remap a type,
+   * and <code>typeOracle</code> can also be <code>null</code>.
+   */
+  public final boolean isTrue(TreeLogger logger, DeferredBindingQuery query)
+      throws UnableToCompleteException {
 
     boolean logDebug = logger.isLoggable(TreeLogger.DEBUG);
 
     if (logDebug) {
-      String startMsg = getEvalBeforeMessage(testType);
+      String startMsg = getEvalBeforeMessage(query.getTestType());
       logger = logger.branch(TreeLogger.DEBUG, startMsg, null);
     }
 
-    boolean result = doEval(logger, propertyOracle, typeOracle, testType);
+    boolean result = doEval(logger, query);
 
     if (logDebug) {
-      String afterMsg = getEvalAfterMessage(testType, result);
+      String afterMsg = getEvalAfterMessage(query.getTestType(), result);
       logger.log(TreeLogger.DEBUG, afterMsg, null);
     }
 
     return result;
   }
 
-  protected abstract boolean doEval(TreeLogger logger,
-      PropertyOracle propertyOracle, TypeOracle typeOracle, String testType)
+  protected abstract boolean doEval(TreeLogger logger, DeferredBindingQuery query)
       throws UnableToCompleteException;
 
   protected abstract String getEvalAfterMessage(String testType, boolean result);
diff --git a/dev/core/src/com/google/gwt/dev/cfg/ConditionAll.java b/dev/core/src/com/google/gwt/dev/cfg/ConditionAll.java
index 94a47cb..c53ac97 100644
--- a/dev/core/src/com/google/gwt/dev/cfg/ConditionAll.java
+++ b/dev/core/src/com/google/gwt/dev/cfg/ConditionAll.java
@@ -15,10 +15,8 @@
  */
 package com.google.gwt.dev.cfg;
 
-import com.google.gwt.core.ext.PropertyOracle;
 import com.google.gwt.core.ext.TreeLogger;
 import com.google.gwt.core.ext.UnableToCompleteException;
-import com.google.gwt.core.ext.typeinfo.TypeOracle;
 
 import java.util.Iterator;
 
@@ -31,11 +29,11 @@
   public ConditionAll() {
   }
 
-  protected boolean doEval(TreeLogger logger, PropertyOracle propertyOracle,
-      TypeOracle typeOracle, String testType) throws UnableToCompleteException {
+  protected boolean doEval(TreeLogger logger, DeferredBindingQuery query)
+      throws UnableToCompleteException {
     for (Iterator<Condition> iter = getConditions().iterator(); iter.hasNext();) {
       Condition condition = iter.next();
-      if (!condition.isTrue(logger, propertyOracle, typeOracle, testType)) {
+      if (!condition.isTrue(logger, query)) {
         return false;
       }
     }
diff --git a/dev/core/src/com/google/gwt/dev/cfg/ConditionAny.java b/dev/core/src/com/google/gwt/dev/cfg/ConditionAny.java
index edd34c0..2f474c2 100644
--- a/dev/core/src/com/google/gwt/dev/cfg/ConditionAny.java
+++ b/dev/core/src/com/google/gwt/dev/cfg/ConditionAny.java
@@ -15,10 +15,8 @@
  */
 package com.google.gwt.dev.cfg;
 
-import com.google.gwt.core.ext.PropertyOracle;
 import com.google.gwt.core.ext.TreeLogger;
 import com.google.gwt.core.ext.UnableToCompleteException;
-import com.google.gwt.core.ext.typeinfo.TypeOracle;
 
 import java.util.Iterator;
 
@@ -30,11 +28,11 @@
   public ConditionAny() {
   }
 
-  protected boolean doEval(TreeLogger logger, PropertyOracle propertyOracle,
-      TypeOracle typeOracle, String testType) throws UnableToCompleteException {
+  protected boolean doEval(TreeLogger logger, DeferredBindingQuery query)
+      throws UnableToCompleteException {
     for (Iterator<Condition> iter = getConditions().iterator(); iter.hasNext();) {
       Condition condition = iter.next();
-      if (condition.isTrue(logger, propertyOracle, typeOracle, testType)) {
+      if (condition.isTrue(logger, query)) {
         return true;
       }
     }
diff --git a/dev/core/src/com/google/gwt/dev/cfg/ConditionNone.java b/dev/core/src/com/google/gwt/dev/cfg/ConditionNone.java
index a1c1133..3e1b959 100644
--- a/dev/core/src/com/google/gwt/dev/cfg/ConditionNone.java
+++ b/dev/core/src/com/google/gwt/dev/cfg/ConditionNone.java
@@ -15,10 +15,8 @@
  */
 package com.google.gwt.dev.cfg;
 
-import com.google.gwt.core.ext.PropertyOracle;
 import com.google.gwt.core.ext.TreeLogger;
 import com.google.gwt.core.ext.UnableToCompleteException;
-import com.google.gwt.core.ext.typeinfo.TypeOracle;
 
 import java.util.Iterator;
 
@@ -31,11 +29,11 @@
   public ConditionNone() {
   }
 
-  protected boolean doEval(TreeLogger logger, PropertyOracle propertyOracle,
-      TypeOracle typeOracle, String testType) throws UnableToCompleteException {
+  protected boolean doEval(TreeLogger logger, DeferredBindingQuery query)
+      throws UnableToCompleteException {
     for (Iterator<Condition> iter = getConditions().iterator(); iter.hasNext();) {
       Condition condition = iter.next();
-      if (condition.isTrue(logger, propertyOracle, typeOracle, testType)) {
+      if (condition.isTrue(logger, query)) {
         return false;
       }
     }
diff --git a/dev/core/src/com/google/gwt/dev/cfg/ConditionWhenLinkerAdded.java b/dev/core/src/com/google/gwt/dev/cfg/ConditionWhenLinkerAdded.java
new file mode 100644
index 0000000..dd15934
--- /dev/null
+++ b/dev/core/src/com/google/gwt/dev/cfg/ConditionWhenLinkerAdded.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2010 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.cfg;
+
+import com.google.gwt.core.ext.TreeLogger;
+
+/**
+ * A condition that is true when the active linkers include the one specified.
+ */
+public class ConditionWhenLinkerAdded extends Condition {
+  private final String linkerName;
+
+  public ConditionWhenLinkerAdded(String linkerName) {
+    this.linkerName = linkerName;
+  }
+
+  @Override
+  public String toString() {
+    return "<when-linkers-include name='" + linkerName + "'/>";
+  }
+
+  @Override
+  protected boolean doEval(TreeLogger logger, DeferredBindingQuery query) {
+    return query.getLinkerNames().contains(linkerName);
+  }
+
+  @Override
+  protected String getEvalAfterMessage(String testType, boolean result) {
+    if (result) {
+      return "Yes, the requested linker is active";
+    } else {
+      return "No, the requested linker is not active";
+    }
+  }
+
+  @Override
+  protected String getEvalBeforeMessage(String testType) {
+    return toString();
+  }
+}
diff --git a/dev/core/src/com/google/gwt/dev/cfg/ConditionWhenPropertyIs.java b/dev/core/src/com/google/gwt/dev/cfg/ConditionWhenPropertyIs.java
index ac8205d..2148fcd 100644
--- a/dev/core/src/com/google/gwt/dev/cfg/ConditionWhenPropertyIs.java
+++ b/dev/core/src/com/google/gwt/dev/cfg/ConditionWhenPropertyIs.java
@@ -21,7 +21,6 @@
 import com.google.gwt.core.ext.SelectionProperty;
 import com.google.gwt.core.ext.TreeLogger;
 import com.google.gwt.core.ext.UnableToCompleteException;
-import com.google.gwt.core.ext.typeinfo.TypeOracle;
 import com.google.gwt.dev.util.collect.Sets;
 
 import java.util.Set;
@@ -50,8 +49,9 @@
     return "<when-property-is name='" + propName + "' value='" + value + "'/>";
   }
 
-  protected boolean doEval(TreeLogger logger, PropertyOracle propertyOracle,
-      TypeOracle typeOracle, String testType) throws UnableToCompleteException {
+  protected boolean doEval(TreeLogger logger, DeferredBindingQuery query)
+      throws UnableToCompleteException {
+    PropertyOracle propertyOracle = query.getPropertyOracle();
     String testValue;
     try {
       try {
diff --git a/dev/core/src/com/google/gwt/dev/cfg/ConditionWhenTypeAssignableTo.java b/dev/core/src/com/google/gwt/dev/cfg/ConditionWhenTypeAssignableTo.java
index 04d1df4..6bf00b9 100644
--- a/dev/core/src/com/google/gwt/dev/cfg/ConditionWhenTypeAssignableTo.java
+++ b/dev/core/src/com/google/gwt/dev/cfg/ConditionWhenTypeAssignableTo.java
@@ -15,7 +15,6 @@
  */
 package com.google.gwt.dev.cfg;
 
-import com.google.gwt.core.ext.PropertyOracle;
 import com.google.gwt.core.ext.TreeLogger;
 import com.google.gwt.core.ext.UnableToCompleteException;
 import com.google.gwt.core.ext.typeinfo.JClassType;
@@ -42,8 +41,10 @@
     return "<when-assignable class='" + assignableToTypeName + "'/>";
   }
 
-  protected boolean doEval(TreeLogger logger, PropertyOracle propertyOracle,
-      TypeOracle typeOracle, String testType) throws UnableToCompleteException {
+  protected boolean doEval(TreeLogger logger, DeferredBindingQuery query)
+      throws UnableToCompleteException {
+    TypeOracle typeOracle = query.getTypeOracle();
+    String testType = query.getTestType();
     JClassType fromType = typeOracle.findType(testType);
     if (fromType == null) {
       Util.logMissingTypeErrorWithHints(logger, testType);
diff --git a/dev/core/src/com/google/gwt/dev/cfg/ConditionWhenTypeIs.java b/dev/core/src/com/google/gwt/dev/cfg/ConditionWhenTypeIs.java
index 68204b5..495b5fc 100644
--- a/dev/core/src/com/google/gwt/dev/cfg/ConditionWhenTypeIs.java
+++ b/dev/core/src/com/google/gwt/dev/cfg/ConditionWhenTypeIs.java
@@ -15,9 +15,7 @@
  */
 package com.google.gwt.dev.cfg;
 
-import com.google.gwt.core.ext.PropertyOracle;
 import com.google.gwt.core.ext.TreeLogger;
-import com.google.gwt.core.ext.typeinfo.TypeOracle;
 
 /**
  * A deferred binding condition to determine whether the type being rebound is
@@ -35,9 +33,8 @@
     return "<when-type-is class='" + exactTypeName + "'/>";
   }
 
-  protected boolean doEval(TreeLogger logger, PropertyOracle propertyOracle,
-      TypeOracle typeOracle, String testType) {
-    return exactTypeName.equals(testType);
+  protected boolean doEval(TreeLogger logger, DeferredBindingQuery query) {
+    return exactTypeName.equals(query.getTestType());
   }
 
   protected String getEvalAfterMessage(String testType, boolean result) {
diff --git a/dev/core/src/com/google/gwt/dev/cfg/DeferredBindingQuery.java b/dev/core/src/com/google/gwt/dev/cfg/DeferredBindingQuery.java
new file mode 100644
index 0000000..503ca94
--- /dev/null
+++ b/dev/core/src/com/google/gwt/dev/cfg/DeferredBindingQuery.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright 2010 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.cfg;
+
+import com.google.gwt.core.ext.PropertyOracle;
+import com.google.gwt.core.ext.typeinfo.TypeOracle;
+
+import java.util.Set;
+
+/**
+ * A query into the deferred binding system. A {@link Condition} can be tested
+ * against such a query.
+ */
+public class DeferredBindingQuery {
+  private final Set<String> linkerNames;
+  private final PropertyOracle propertyOracle;
+  private final String testType;
+  private final TypeOracle typeOracle;
+
+  /**
+   * Construct a query for contexts where a type is not available. Such a query
+   * also does not need a type oracle.
+   */
+  public DeferredBindingQuery(PropertyOracle propertyOracle,
+      Set<String> linkerNames) {
+    this(propertyOracle, linkerNames, null, null);
+  }
+
+  /**
+   * Construct a fully general query, including a query type and type oracle.
+   */
+  public DeferredBindingQuery(PropertyOracle propertyOracle,
+      Set<String> linkerNames, TypeOracle typeOracle, String testType) {
+    assert propertyOracle != null;
+    assert linkerNames != null;
+
+    this.propertyOracle = propertyOracle;
+    this.linkerNames = linkerNames;
+    this.typeOracle = typeOracle;
+    this.testType = testType;
+  }
+
+  public Set<String> getLinkerNames() {
+    return linkerNames;
+  }
+
+  public PropertyOracle getPropertyOracle() {
+    return propertyOracle;
+  }
+
+  public String getTestType() {
+    return testType;
+  }
+
+  public TypeOracle getTypeOracle() {
+    return typeOracle;
+  }
+}
diff --git a/dev/core/src/com/google/gwt/dev/cfg/ModuleDef.java b/dev/core/src/com/google/gwt/dev/cfg/ModuleDef.java
index 5028e61..5db5c05 100644
--- a/dev/core/src/com/google/gwt/dev/cfg/ModuleDef.java
+++ b/dev/core/src/com/google/gwt/dev/cfg/ModuleDef.java
@@ -77,6 +77,8 @@
 
   private String activePrimaryLinker;
 
+  private final DefaultFilters defaultFilters;
+
   private final List<String> entryPointTypeNames = new ArrayList<String>();
 
   private final Set<File> gwtXmlFiles = new HashSet<File>();
@@ -110,9 +112,7 @@
   private final Map<String, String> servletClassNamesByPath = new HashMap<String, String>();
 
   private PathPrefixSet sourcePrefixSet = new PathPrefixSet();
-
   private final Styles styles = new Styles();
-  private final DefaultFilters defaultFilters;
 
   public ModuleDef(String name) {
     this.name = name;
@@ -133,10 +133,13 @@
 
     LinkerOrder order = clazz.getAnnotation(LinkerOrder.class);
     if (order.value() == Order.PRIMARY) {
+      if (activePrimaryLinker != null) {
+        activeLinkers.remove(activePrimaryLinker);
+      }
       activePrimaryLinker = name;
-    } else {
-      activeLinkers.add(name);
     }
+
+    activeLinkers.add(name);
   }
 
   public synchronized void addPublicPackage(String publicPackage,
@@ -249,6 +252,10 @@
     return null;
   }
 
+  public Set<String> getActiveLinkerNames() {
+    return new LinkedHashSet<String>(activeLinkers);
+  }
+
   public Set<Class<? extends Linker>> getActiveLinkers() {
     Set<Class<? extends Linker>> toReturn = new LinkedHashSet<Class<? extends Linker>>();
     for (String linker : activeLinkers) {
@@ -489,5 +496,4 @@
       throw new UnableToCompleteException();
     }
   }
-
 }
diff --git a/dev/core/src/com/google/gwt/dev/cfg/ModuleDefSchema.java b/dev/core/src/com/google/gwt/dev/cfg/ModuleDefSchema.java
index 9ca2cfc..c6ab34f 100644
--- a/dev/core/src/com/google/gwt/dev/cfg/ModuleDefSchema.java
+++ b/dev/core/src/com/google/gwt/dev/cfg/ModuleDefSchema.java
@@ -880,6 +880,8 @@
    * A limited number of conditional predicates based only on properties.
    */
   private class PropertyConditionSchema extends Schema {
+    protected final String __when_linker_added_1_name = null;
+
     protected final String __when_property_is_1_name = null;
 
     protected final String __when_property_is_2_value = null;
@@ -908,6 +910,14 @@
       return subSchema(cond);
     }
 
+    protected Schema __when_linker_added_begin(LinkerName linkerName) {
+      Condition cond = new ConditionWhenLinkerAdded(linkerName.name);
+      parentCondition.getConditions().add(cond);
+
+      // No children allowed.
+      return null;
+    }
+
     /*
      * We intentionally use the BindingProperty type here for tough-love on
      * module writers. It prevents them from trying to create property providers
@@ -1049,8 +1059,8 @@
   }
 
   /**
-   * Returns <code>true</code> if the string equals "true" or "yes" using a
-   * case insensitive comparison.
+   * Returns <code>true</code> if the string equals "true" or "yes" using a case
+   * insensitive comparison.
    */
   private static boolean toPrimitiveBoolean(String s) {
     return "yes".equalsIgnoreCase(s) || "true".equalsIgnoreCase(s);
diff --git a/dev/core/src/com/google/gwt/dev/cfg/Properties.java b/dev/core/src/com/google/gwt/dev/cfg/Properties.java
index be15e15..b3b6d86 100644
--- a/dev/core/src/com/google/gwt/dev/cfg/Properties.java
+++ b/dev/core/src/com/google/gwt/dev/cfg/Properties.java
@@ -74,15 +74,6 @@
     return map.values().iterator();
   }
 
-  /**
-   * Count the total number of permutations that this property set supports.
-   * This method can be expensive because it always recalculates the answer
-   * based on the current set of properties and values.
-   */
-  public int numPermutations() {
-    return new PropertyPermutations(this).size();
-  }
-
   private <T extends Property> T create(String name, boolean flag,
       boolean useFlagArgument, Class<T> clazz) {
     if (clazz == null) {
diff --git a/dev/core/src/com/google/gwt/dev/cfg/PropertyPermutations.java b/dev/core/src/com/google/gwt/dev/cfg/PropertyPermutations.java
index 682b2bf..be1a05a 100644
--- a/dev/core/src/com/google/gwt/dev/cfg/PropertyPermutations.java
+++ b/dev/core/src/com/google/gwt/dev/cfg/PropertyPermutations.java
@@ -40,12 +40,13 @@
    * Returns the list of all permutations. This method must return results in a
    * consistently sorted order over multiple invocations.
    */
-  private static List<String[]> allPermutationsOf(Properties properties) {
+  private static List<String[]> allPermutationsOf(Properties properties,
+      Set<String> activeLinkerNames) {
     BindingProperty[] bindingProperties = getOrderedPropertiesOf(properties);
 
     List<String[]> permutations = new ArrayList<String[]>();
     if (bindingProperties.length > 0) {
-      permute(bindingProperties, null, 0, permutations);
+      permute(bindingProperties, activeLinkerNames, null, 0, permutations);
     } else {
       permutations.add(new String[0]);
     }
@@ -93,8 +94,9 @@
         new BindingProperty[evaluationOrder.size()]);
   }
 
-  private static void permute(BindingProperty[] properties, String[] soFar,
-      int whichProp, List<String[]> permutations) {
+  private static void permute(BindingProperty[] properties,
+      Set<String> activeLinkerNames, String[] soFar, int whichProp,
+      List<String[]> permutations) {
     int lastProp = properties.length - 1;
 
     BindingProperty prop = properties[whichProp];
@@ -111,7 +113,8 @@
 
       for (Condition cond : prop.getConditionalValues().keySet()) {
         try {
-          if (cond.isTrue(TreeLogger.NULL, propertyOracle, null, null)) {
+          if (cond.isTrue(TreeLogger.NULL, new DeferredBindingQuery(
+              propertyOracle, activeLinkerNames))) {
             winner = cond;
           }
         } catch (UnableToCompleteException e) {
@@ -134,7 +137,8 @@
       nextStep[whichProp] = knownValue;
 
       if (whichProp < lastProp) {
-        permute(properties, nextStep, whichProp + 1, permutations);
+        permute(properties, activeLinkerNames, nextStep, whichProp + 1,
+            permutations);
       } else {
         // Finished this permutation.
         permutations.add(nextStep);
@@ -145,15 +149,10 @@
   private final Properties properties;
   private final List<String[]> values;
 
-  public PropertyPermutations(Properties properties) {
+  public PropertyPermutations(Properties properties,
+      Set<String> activeLinkerNames) {
     this.properties = properties;
-    this.values = allPermutationsOf(properties);
-  }
-
-  public PropertyPermutations(Properties properties, int firstPerm, int numPerms) {
-    this.properties = properties;
-    values = allPermutationsOf(properties).subList(firstPerm,
-        firstPerm + numPerms);
+    this.values = allPermutationsOf(properties, activeLinkerNames);
   }
 
   public PropertyPermutations(PropertyPermutations allPermutations,
diff --git a/dev/core/src/com/google/gwt/dev/cfg/Rule.java b/dev/core/src/com/google/gwt/dev/cfg/Rule.java
index 424a594..65d66b0 100644
--- a/dev/core/src/com/google/gwt/dev/cfg/Rule.java
+++ b/dev/core/src/com/google/gwt/dev/cfg/Rule.java
@@ -33,8 +33,9 @@
   public boolean isApplicable(TreeLogger logger,
       StandardGeneratorContext context, String typeName)
       throws UnableToCompleteException {
-    return rootCondition.isTrue(logger, context.getPropertyOracle(),
-        context.getTypeOracle(), typeName);
+    return rootCondition.isTrue(logger, new DeferredBindingQuery(
+        context.getPropertyOracle(), context.getActiveLinkerNames(),
+        context.getTypeOracle(), typeName));
   }
 
   public abstract String realize(TreeLogger logger,
diff --git a/dev/core/src/com/google/gwt/dev/javac/StandardGeneratorContext.java b/dev/core/src/com/google/gwt/dev/javac/StandardGeneratorContext.java
index 7c9ef59..0b2aff8 100644
--- a/dev/core/src/com/google/gwt/dev/javac/StandardGeneratorContext.java
+++ b/dev/core/src/com/google/gwt/dev/javac/StandardGeneratorContext.java
@@ -378,6 +378,10 @@
     }
   }
 
+  public Set<String> getActiveLinkerNames() {
+    return module.getActiveLinkerNames();
+  }
+
   public final PropertyOracle getPropertyOracle() {
     return propOracle;
   }
diff --git a/dev/core/src/com/google/gwt/dev/shell/ModuleSpacePropertyOracle.java b/dev/core/src/com/google/gwt/dev/shell/ModuleSpacePropertyOracle.java
index 4b0d6b9..a27a624 100644
--- a/dev/core/src/com/google/gwt/dev/shell/ModuleSpacePropertyOracle.java
+++ b/dev/core/src/com/google/gwt/dev/shell/ModuleSpacePropertyOracle.java
@@ -25,6 +25,7 @@
 import com.google.gwt.dev.cfg.BindingProperty;
 import com.google.gwt.dev.cfg.Condition;
 import com.google.gwt.dev.cfg.ConfigurationProperty;
+import com.google.gwt.dev.cfg.DeferredBindingQuery;
 import com.google.gwt.dev.cfg.Properties;
 import com.google.gwt.dev.cfg.Property;
 
@@ -32,6 +33,7 @@
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
 import java.util.SortedSet;
 import java.util.TreeSet;
 
@@ -41,6 +43,8 @@
  */
 public class ModuleSpacePropertyOracle implements PropertyOracle {
 
+  private final Set<String> activeLinkerNames;
+
   private final Map<String, String> prevAnswers = new HashMap<String, String>();
 
   private final Properties props;
@@ -49,12 +53,11 @@
 
   /**
    * Create a property oracle that computes its properties from a module.
-   * 
-   * @param props
-   * @param space
    */
-  public ModuleSpacePropertyOracle(Properties props, ModuleSpace space) {
+  public ModuleSpacePropertyOracle(Properties props,
+      Set<String> activeLinkerNames, ModuleSpace space) {
     this.space = space;
+    this.activeLinkerNames = activeLinkerNames;
     this.props = props;
   }
 
@@ -132,8 +135,7 @@
       for (String v : cprop.getDefinedValues()) {
         possibleValues.add(v);
       }
-      return new DefaultSelectionProperty(value, fallback, name,
-          possibleValues);
+      return new DefaultSelectionProperty(value, fallback, name, possibleValues);
     } else {
       throw new BadPropertyValueException(propertyName);
     }
@@ -145,7 +147,8 @@
     Condition winner = null;
     for (Condition cond : prop.getConditionalValues().keySet()) {
       try {
-        if (cond.isTrue(logger, this, null, null)) {
+        if (cond.isTrue(logger, new DeferredBindingQuery(this,
+            activeLinkerNames))) {
           winner = cond;
         }
       } catch (UnableToCompleteException e) {
diff --git a/dev/core/src/com/google/gwt/dev/shell/ShellModuleSpaceHost.java b/dev/core/src/com/google/gwt/dev/shell/ShellModuleSpaceHost.java
index b119bdd..f69db8f 100644
--- a/dev/core/src/com/google/gwt/dev/shell/ShellModuleSpaceHost.java
+++ b/dev/core/src/com/google/gwt/dev/shell/ShellModuleSpaceHost.java
@@ -88,7 +88,7 @@
     // Establish an environment for JavaScript property providers to run.
     //
     ModuleSpacePropertyOracle propOracle = new ModuleSpacePropertyOracle(
-        module.getProperties(), readySpace);
+        module.getProperties(), module.getActiveLinkerNames(), readySpace);
 
     // Set up the rebind oracle for the module.
     // It has to wait until now because we need to inject javascript.
diff --git a/dev/core/test/com/google/gwt/dev/cfg/ConditionTest.java b/dev/core/test/com/google/gwt/dev/cfg/ConditionTest.java
new file mode 100644
index 0000000..8c438ec
--- /dev/null
+++ b/dev/core/test/com/google/gwt/dev/cfg/ConditionTest.java
@@ -0,0 +1,112 @@
+/*
+ * Copyright 2009 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.cfg;
+
+import com.google.gwt.core.ext.PropertyOracle;
+import com.google.gwt.core.ext.TreeLogger;
+import com.google.gwt.core.ext.UnableToCompleteException;
+import com.google.gwt.core.ext.typeinfo.TypeOracle;
+import com.google.gwt.dev.javac.TypeOracleTestingUtils;
+
+import junit.framework.TestCase;
+
+import java.util.Arrays;
+import java.util.LinkedHashSet;
+import java.util.Set;
+
+/**
+ * Test the {@link Condition} class.
+ */
+public class ConditionTest extends TestCase {
+  private PropertyOracle propertyOracle;
+  private TypeOracle typeOracle;
+  private static Set<String> activeLinkerNames = new LinkedHashSet<String>(
+      Arrays.asList("linker1", "linker2", "xs"));
+
+  @Override
+  public void setUp() {
+    ModuleDef module = new ModuleDef("fake");
+
+    ConfigurationProperty conf1 = module.getProperties().createConfiguration(
+        "conf1", false);
+    conf1.setValue("value1");
+
+    BindingProperty binding1 = module.getProperties().createBinding("binding1");
+    binding1.addDefinedValue(new ConditionAll(), "true");
+    binding1.addDefinedValue(new ConditionAll(), "false");
+
+    propertyOracle = new StaticPropertyOracle(new BindingProperty[] {binding1},
+        new String[] {"true"}, new ConfigurationProperty[] {conf1});
+
+    typeOracle = TypeOracleTestingUtils.buildStandardTypeOracleWith(TreeLogger.NULL);
+  }
+
+  public void testBasics() throws UnableToCompleteException {
+    assertTrue(isTrue(new ConditionAll(), null));
+    assertFalse(isTrue(new ConditionAny(), null));
+    assertTrue(isTrue(new ConditionNone(), null));
+
+    assertTrue(isTrue(new ConditionWhenPropertyIs("conf1", "value1"), null));
+    assertFalse(isTrue(new ConditionWhenPropertyIs("conf1", "value2"), null));
+    assertTrue(isTrue(new ConditionWhenPropertyIs("binding1", "true"), null));
+    assertFalse(isTrue(new ConditionWhenPropertyIs("binding1", "false"), null));
+
+    assertTrue(isTrue(new ConditionWhenLinkerAdded("linker1"), null));
+    assertFalse(isTrue(new ConditionWhenLinkerAdded("bogoLinker"), null));
+
+    ConditionWhenTypeAssignableTo assignableToObject = new ConditionWhenTypeAssignableTo(
+        "java.lang.Object");
+    assertTrue(isTrue(assignableToObject, "java.lang.String"));
+    assertFalse(isTrue(new ConditionWhenTypeAssignableTo("java.lang.String"),
+        "java.lang.Object"));
+    ConditionWhenTypeIs isObject = new ConditionWhenTypeIs("java.lang.Object");
+    assertTrue(isTrue(isObject, "java.lang.Object"));
+    assertFalse(isTrue(isObject, "java.lang.String"));
+
+    {
+      ConditionAll all = new ConditionAll();
+      all.getConditions().add(assignableToObject);
+      all.getConditions().add(isObject);
+
+      assertTrue(isTrue(all, "java.lang.Object"));
+      assertFalse(isTrue(all, "java.lang.String"));
+    }
+
+    {
+      ConditionAny any = new ConditionAny();
+      any.getConditions().add(assignableToObject);
+      any.getConditions().add(isObject);
+
+      assertTrue(isTrue(any, "java.lang.Object"));
+      assertTrue(isTrue(any, "java.lang.String"));
+    }
+
+    {
+      ConditionNone none = new ConditionNone();
+      none.getConditions().add(assignableToObject);
+      none.getConditions().add(isObject);
+
+      assertFalse(isTrue(none, "java.lang.Object"));
+      assertFalse(isTrue(none, "java.lang.String"));
+    }
+  }
+
+  private boolean isTrue(Condition cond, String testType)
+      throws UnableToCompleteException {
+    return cond.isTrue(TreeLogger.NULL, new DeferredBindingQuery(
+        propertyOracle, activeLinkerNames, typeOracle, testType));
+  }
+}
\ No newline at end of file
diff --git a/dev/core/test/com/google/gwt/dev/cfg/ModuleDefTest.java b/dev/core/test/com/google/gwt/dev/cfg/ModuleDefTest.java
index 157703c..cd3baab 100644
--- a/dev/core/test/com/google/gwt/dev/cfg/ModuleDefTest.java
+++ b/dev/core/test/com/google/gwt/dev/cfg/ModuleDefTest.java
@@ -42,8 +42,14 @@
     }
 
     @Override
+    public ArtifactSet link(TreeLogger logger, LinkerContext context,
+        ArtifactSet artifacts, boolean onePermutation) {
+      return null;
+    }
+
+    @Override
     public ArtifactSet relink(TreeLogger logger, LinkerContext context,
-        ArtifactSet newArtifacts) throws UnableToCompleteException {
+        ArtifactSet newArtifacts) {
       return null;
     }
   }
@@ -95,7 +101,7 @@
 
     Class<?>[] expectedClasses = {
         FakeLinkerPre2.class, FakeLinkerPre.class, FakeLinkerPost.class,
-        FakeLinkerPost2.class};
+        FakeLinkerPost2.class, FakeLinkerPrimary.class};
     assertEquals(FakeLinkerPrimary.class, def.getActivePrimaryLinker());
     // Test iteration order
     assertEquals(Arrays.asList(expectedClasses),
@@ -118,7 +124,8 @@
     // Intentional duplication
     def.addLinker("post");
 
-    Class<?>[] expectedClasses = {FakeLinkerPre2.class, FakeLinkerPost2.class};
+    Class<?>[] expectedClasses = {
+        FakeLinkerPre2.class, FakeLinkerPost2.class, FakeLinkerPrimary2.class};
     assertEquals(FakeLinkerPrimary2.class, def.getActivePrimaryLinker());
     // Test iteration order
     assertEquals(Arrays.asList(expectedClasses),
@@ -154,4 +161,25 @@
       // OK
     }
   }
+
+  public void testTwoPrimaries() throws UnableToCompleteException {
+    ModuleDef def = new ModuleDef("fake");
+
+    def.defineLinker(TreeLogger.NULL, "pre", FakeLinkerPre.class);
+    def.defineLinker(TreeLogger.NULL, "post", FakeLinkerPost.class);
+    def.defineLinker(TreeLogger.NULL, "primary", FakeLinkerPrimary.class);
+    def.defineLinker(TreeLogger.NULL, "primary2", FakeLinkerPrimary2.class);
+
+    def.addLinker("pre");
+    def.addLinker("post");
+    def.addLinker("primary");
+    def.addLinker("primary2");
+
+    Class<?>[] expectedClasses = {
+        FakeLinkerPre.class, FakeLinkerPost.class, FakeLinkerPrimary2.class};
+    assertEquals(FakeLinkerPrimary2.class, def.getActivePrimaryLinker());
+    // Test iteration order
+    assertEquals(Arrays.asList(expectedClasses),
+        new ArrayList<Class<? extends Linker>>(def.getActiveLinkers()));
+  }
 }
diff --git a/dev/core/test/com/google/gwt/dev/util/test/PropertyPermutationsTest.java b/dev/core/test/com/google/gwt/dev/util/test/PropertyPermutationsTest.java
index 97aa1c6..de54d84 100644
--- a/dev/core/test/com/google/gwt/dev/util/test/PropertyPermutationsTest.java
+++ b/dev/core/test/com/google/gwt/dev/util/test/PropertyPermutationsTest.java
@@ -25,6 +25,7 @@
 import junit.framework.TestCase;
 
 import java.util.Iterator;
+import java.util.Set;
 
 /**
  * Tests the PropertyPermutations code.
@@ -56,7 +57,7 @@
     }
 
     try {
-      new PropertyPermutations(props);
+      new PropertyPermutations(props, md.getActiveLinkerNames());
       fail();
     } catch (IllegalStateException e) {
       // OK
@@ -75,7 +76,8 @@
 
     // Permutations and their values are in stable alphabetical order.
     //
-    PropertyPermutations perms = new PropertyPermutations(md.getProperties());
+    PropertyPermutations perms = new PropertyPermutations(md.getProperties(),
+        md.getActiveLinkerNames());
     String[] perm;
     Iterator<String[]> iter = perms.iterator();
 
@@ -107,7 +109,8 @@
 
     // String[]s and their values are in stable alphabetical order.
     //
-    PropertyPermutations perms = new PropertyPermutations(md.getProperties());
+    PropertyPermutations perms = new PropertyPermutations(md.getProperties(),
+        md.getActiveLinkerNames());
     String[] perm;
     Iterator<String[]> iter = perms.iterator();
 
@@ -171,7 +174,7 @@
       prop.setAllowedValues(cond, "true", "false");
     }
 
-    validateTwoDimensionPerm(props);
+    validateTwoDimensionPerm(props, md.getActiveLinkerNames());
   }
 
   public void testTwoDimensionPermWithExtension() {
@@ -198,7 +201,7 @@
       prop.addDefinedValue(cond, "true");
     }
 
-    validateTwoDimensionPerm(props);
+    validateTwoDimensionPerm(props, md.getActiveLinkerNames());
   }
 
   public void testTwoDimensionPermWithRestriction() {
@@ -227,11 +230,13 @@
       prop.setAllowedValues(cond, "false");
     }
 
-    validateTwoDimensionPerm(props);
+    validateTwoDimensionPerm(props, md.getActiveLinkerNames());
   }
 
-  private void validateTwoDimensionPerm(Properties props) {
-    PropertyPermutations perms = new PropertyPermutations(props);
+  private void validateTwoDimensionPerm(Properties props,
+      Set<String> activeLinkerNames) {
+    PropertyPermutations perms = new PropertyPermutations(props,
+        activeLinkerNames);
 
     assertEquals(6, perms.size());