Index: includes/common.inc
===================================================================
RCS file: /cvs/drupal/drupal/includes/common.inc,v
retrieving revision 1.1040
diff -u -p -r1.1040 common.inc
--- includes/common.inc	3 Nov 2009 06:47:22 -0000	1.1040
+++ includes/common.inc	4 Nov 2009 03:01:28 -0000
@@ -2683,6 +2683,7 @@ function drupal_attributes(array $attrib
  */
 function l($text, $path, array $options = array()) {
   global $language_url;
+  static $use_theme = NULL;
 
   // Merge in defaults.
   $options += array(
@@ -2702,6 +2703,35 @@ function l($text, $path, array $options 
     $options['attributes']['title'] = strip_tags($options['attributes']['title']);
   }
 
+  // Determine if rendering of the link is to be done with a theme function
+  // or the inline default. Inline is faster, but if the theme system has been
+  // loaded and a module or theme implements a preprocess or process function
+  // or overrides the theme_link() function, then invoke theme(). Preliminary
+  // benchmarks indicate that invoking theme() can slow down the l() function
+  // by 20% or more, and that some of the link-heavy Drupal pages spend more
+  // than 10% of the total page request time in the l() function.
+  if (!isset($use_theme) && function_exists('theme')) {
+    // Allow edge cases to prevent theme initialization and force inline link
+    // rendering.
+    if (variable_get('theme_link', TRUE)) {
+      drupal_theme_initialize();
+      $registry = theme_get_registry();
+      // We don't want to duplicate functionality that's in theme(), so any
+      // hint of a module or theme doing anything at all special with the 'link'
+      // theme hook should simply result in theme() being called. This includes
+      // the overriding of theme_link() with an alternate function or template,
+      // the presence of preprocess or process functions, or the presence of
+      // include files.
+      $use_theme = !isset($registry['link']['function']) || ($registry['link']['function'] != 'theme_link');
+      $use_theme = $use_theme || !empty($registry['link']['preprocess functions']) || !empty($registry['link']['process functions']) || !empty($registry['link']['includes']);
+    }
+    else {
+      $use_theme = FALSE;
+    }
+  }
+  if ($use_theme) {
+    return theme('link', array('text' => $text, 'path' => $path, 'options' => $options));
+  }
   return '<a href="' . check_plain(url($path, $options)) . '"' . drupal_attributes($options['attributes']) . '>' . ($options['html'] ? $text : check_plain($text)) . '</a>';
 }
 
@@ -5335,6 +5365,9 @@ function drupal_common_theme() {
     'status_messages' => array(
       'variables' => array('display' => NULL),
     ),
+    'link' => array(
+      'variables' => array('text' => NULL, 'path' => NULL, 'options' => array()),
+    ),
     'links' => array(
       'variables' => array('links' => NULL, 'attributes' => array('class' => array('links')), 'heading' => array()),
     ),
Index: includes/theme.inc
===================================================================
RCS file: /cvs/drupal/drupal/includes/theme.inc,v
retrieving revision 1.547
diff -u -p -r1.547 theme.inc
--- includes/theme.inc	3 Nov 2009 06:47:22 -0000	1.547
+++ includes/theme.inc	4 Nov 2009 03:01:29 -0000
@@ -1367,6 +1367,31 @@ function theme_status_messages($variable
 }
 
 /**
+ * Return a themed link.
+ *
+ * All Drupal code that outputs a link should call the l() function. That
+ * function performs some initial preprocessing, and then, if necessary,
+ * calls theme('link') for rendering the anchor tag.
+ *
+ * To optimize performance for sites that don't need custom theming of links,
+ * the l() function includes an inline copy of this function, and uses that copy
+ * if none of the enabled modules or the active theme implement any preprocess
+ * or process functions or override this theme implementation.
+ *
+ * @param $variables
+ *   An associative array containing the keys 'text', 'path', and 'options'.
+ *   See the l() function for information about these variables.
+ *
+ * @return
+ *   An HTML string containing a link to the given path.
+ *
+ * @see l()
+ */
+function theme_link($variables) {
+  return '<a href="' . check_plain(url($variables['path'], $variables['options'])) . '"' . drupal_attributes($variables['options']['attributes']) . '>' . ($variables['options']['html'] ? $variables['text'] : check_plain($variables['text'])) . '</a>';
+}
+
+/**
  * Return a themed set of links.
  *
  * @param $variables
Index: modules/simpletest/tests/common.test
===================================================================
RCS file: /cvs/drupal/drupal/modules/simpletest/tests/common.test,v
retrieving revision 1.86
diff -u -p -r1.86 common.test
--- modules/simpletest/tests/common.test	3 Nov 2009 06:47:23 -0000	1.86
+++ modules/simpletest/tests/common.test	4 Nov 2009 03:01:31 -0000
@@ -70,9 +70,24 @@ class CommonURLUnitTest extends DrupalUn
    * Confirm that invalid text given as $path is filtered.
    */
   function testLXSS() {
+    global $conf;
     $text = $this->randomName();
     $path = "<SCRIPT>alert('XSS')</SCRIPT>";
+    // We specifically want to test the default l() implementation, not the
+    // theme system. We don't want to permanently change the 'theme_link'
+    // configuration variable. Alternatively to messing with $conf in this way,
+    // we could make CommonURLUnitTest inherit from DrupalWebTestCase in order
+    // to give the test a proper sandbox for configuration variables and the
+    // theme system, but that seems like overkill just to test the unthemed
+    // version of l().
+    if (isset($conf['theme_link'])) {
+      $theme_link = $conf['theme_link'];
+    }
+    $conf['theme_link'] = FALSE;
     $link = l($text, $path);
+    if (isset($theme_link)) {
+      $conf['theme_link'] = $theme_link;
+    }
     $sanitized_path = check_url(url($path));
     $this->assertTrue(strpos($link, $sanitized_path) !== FALSE, t('XSS attack @path was filtered', array('@path' => $path)));
   }
