Author: maoziqing
Date: Tue Jul 14 13:50:32 2009
New Revision: 3570

Added:
   trunk/tests/com/google/caja/parser/quasiliteral/c.js
   trunk/tests/com/google/caja/parser/quasiliteral/foo/
   trunk/tests/com/google/caja/parser/quasiliteral/foo/b.js
Modified:
   trunk/src/com/google/caja/parser/quasiliteral/CajitaRewriter.java
   trunk/src/com/google/caja/parser/quasiliteral/RewriterMessageType.java
   trunk/src/com/google/caja/plugin/ExpressionSanitizerCaja.java
   trunk/tests/com/google/caja/parser/quasiliteral/CajitaRewriterTest.java

Log:
Static module inlining for Cajita.



Modified: trunk/src/com/google/caja/parser/quasiliteral/CajitaRewriter.java
==============================================================================
--- trunk/src/com/google/caja/parser/quasiliteral/CajitaRewriter.java (original) +++ trunk/src/com/google/caja/parser/quasiliteral/CajitaRewriter.java Tue Jul 14 13:50:32 2009
@@ -14,7 +14,13 @@

 package com.google.caja.parser.quasiliteral;

+import com.google.caja.lexer.CharProducer;
+import com.google.caja.lexer.ExternalReference;
 import com.google.caja.lexer.FilePosition;
+import com.google.caja.lexer.InputSource;
+import com.google.caja.lexer.JsLexer;
+import com.google.caja.lexer.JsTokenQueue;
+import com.google.caja.lexer.ParseException;
 import com.google.caja.parser.ParseTreeNode;
 import com.google.caja.parser.ParseTreeNodeContainer;
 import com.google.caja.parser.js.ArrayConstructor;
@@ -46,6 +52,7 @@
 import com.google.caja.parser.js.ObjectConstructor;
 import com.google.caja.parser.js.Operation;
 import com.google.caja.parser.js.Operator;
+import com.google.caja.parser.js.Parser;
 import com.google.caja.parser.js.Reference;
 import com.google.caja.parser.js.RegexpLiteral;
 import com.google.caja.parser.js.ReturnStmt;
@@ -58,14 +65,17 @@
 import com.google.caja.parser.js.TryStmt;
 import com.google.caja.parser.js.UncajoledModule;
 import com.google.caja.parser.js.UseSubsetDirective;
-import com.google.caja.util.Pair;
-import com.google.caja.util.SyntheticAttributeKey;
+import com.google.caja.plugin.PluginEnvironment;
+import com.google.caja.reporting.BuildInfo;
 import com.google.caja.reporting.MessagePart;
 import com.google.caja.reporting.MessageQueue;
-import com.google.caja.reporting.BuildInfo;
+import com.google.caja.util.Pair;
+import com.google.caja.util.SyntheticAttributeKey;

 import static com.google.caja.parser.js.SyntheticNodes.s;

+import java.net.URI;
+import java.net.URISyntaxException;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.LinkedHashSet;
@@ -87,6 +97,7 @@
= new SyntheticAttributeKey<Boolean>(Boolean.class, "translatedCode");

   private final BuildInfo buildInfo;
+  private final PluginEnvironment pluginEnv;

   /** Mark a tree as having been translated from another language. */
   private static void markTranslated(ParseTreeNode node) {
@@ -163,6 +174,59 @@
     return result;
   }

+  /**
+   * Retrieve the module source, cajole it and return a cajoled module.
+   * Return null if loading the module failed
+   */
+  public ParseTreeNode fetchStaticModule(
+      StringLiteral src, MessageQueue mq) {
+    String loc = src.getUnquotedValue();
+    if (!loc.toLowerCase().endsWith(".js")) {
+      loc = loc + ".js";
+    }
+
+    URI inputUri;
+    try {
+      inputUri = new URI(loc);
+    } catch (URISyntaxException ex) {
+      mq.addMessage(
+          RewriterMessageType.INVALID_MODULE_URI,
+          src.getFilePosition(),
+          MessagePart.Factory.valueOf(src.getUnquotedValue()));
+      return null;
+    }
+
+    ExternalReference er = new ExternalReference(
+        inputUri, src.getFilePosition());
+
+    CharProducer cp =
+      this.pluginEnv.loadExternalResource(er, "text/javascript");
+    if (cp == null) {
+      mq.addMessage(
+          RewriterMessageType.MODULE_NOT_FOUND,
+          src.getFilePosition(),
+          MessagePart.Factory.valueOf(src.getUnquotedValue()));
+      return null;
+    }
+
+ InputSource is = new InputSource(cp.getCurrentPosition().source().getUri());
+    try {
+      JsTokenQueue tq = new JsTokenQueue(new JsLexer(cp), is);
+      Block input = new Parser(tq, mq).parse();
+      tq.expectEmpty();
+
+      CajitaRewriter dcr = new CajitaRewriter(
+          buildInfo, this.pluginEnv, false /* logging */);
+      return dcr.expand(new UncajoledModule(input), mq);
+    } catch (ParseException e) {
+      mq.addMessage(
+          RewriterMessageType.PARSING_MODULE_FAILED,
+          src.getFilePosition(),
+          MessagePart.Factory.valueOf(src.getUnquotedValue()));
+      return null;
+    }
+  }
+
   // A NOTE ABOUT MATCHING MEMBER ACCESS EXPRESSIONS
// When we match the pattern like '@x...@y' or '@x...@y()' against a specimen,
   // the result is that 'y' is bound to the rightmost component, and 'x' is
@@ -425,6 +489,66 @@
       }
     },

+    // Loading a Module
+
+    new Rule() {
+      @Override
+      @RuleDescription(
+          name="loadmodule",
+          synopsis="rewrites the loader.load function.",
+          reason="",
+          matches="loader.load(@arg)",
+          substitutes="___.markFuncFreeze(function() {"
+            + "  function theModule(IMPORTS___) {"
+            + "    function instantiate___(___, IMPORTS___) {"
+            + "      @body;"
+            + "    }"
+            + "    ___.markFuncFreeze(instantiate___, 'instantiate___');"
+            + "    return instantiate___(___, IMPORTS___);"
+            + "  }"
+            + "  ___.markFuncOnly(theModule, 'theModule');"
+            + "  ___.setStatic(theModule, 'cajolerName', @cajolerName);"
+ + " ___.setStatic(theModule, 'cajolerVersion', @cajolerVersion);"
+            + "  ___.setStatic(theModule, 'cajolerDate', @cajoledDate);"
+            + "  return ___.primFreeze(theModule);"
+            + "})();")
+      public ParseTreeNode fire(
+          ParseTreeNode node, Scope scope, MessageQueue mq) {
+        Map<String, ParseTreeNode> bindings = match(node);
+        if (bindings != null && scope.isOuter("loader")) {
+          ParseTreeNode arg = bindings.get("arg");
+          if (arg instanceof StringLiteral) {
+            ParseTreeNode cajoledModule = fetchStaticModule(
+                (StringLiteral)arg, mq);
+
+            if (cajoledModule != null) {
+              ObjectConstructor oc =
+                ((CajoledModule)cajoledModule).getModuleBody();
+              FunctionConstructor fc =
+                ((FunctionConstructor)oc.getValue("instantiate"));
+
+              return substV(
+                  "body", fc.getBody(),
+                  "cajolerName", oc.getValue("cajolerName"),
+                  "cajolerVersion", oc.getValue("cajolerVersion"),
+                  "cajoledDate", oc.getValue("cajoledDate"));
+            }
+            else {
+ // error messages were logged in the function fetchStaticModule
+              return node;
+            }
+          }
+          else {
+            mq.addMessage(
+                RewriterMessageType.CANNOT_LOAD_A_DYNAMIC_MODULE,
+                node.getFilePosition());
+            return node;
+          }
+        }
+        return NONE;
+      }
+    },
+
////////////////////////////////////////////////////////////////////////
     // Module envelope
////////////////////////////////////////////////////////////////////////
@@ -2401,9 +2525,15 @@
   /**
    * Creates a Cajita rewriter
    */
-  public CajitaRewriter(BuildInfo buildInfo, boolean logging) {
+  public CajitaRewriter(
+      BuildInfo buildInfo, PluginEnvironment pluginEnv, boolean logging) {
     super(true, logging);
     this.buildInfo = buildInfo;
+    this.pluginEnv = pluginEnv;
     addRules(cajaRules);
+  }
+
+  public CajitaRewriter(BuildInfo buildInfo, boolean logging) {
+    this(buildInfo, PluginEnvironment.CLOSED_PLUGIN_ENVIRONMENT, logging);
   }
 }

Modified: trunk/src/com/google/caja/parser/quasiliteral/RewriterMessageType.java
==============================================================================
--- trunk/src/com/google/caja/parser/quasiliteral/RewriterMessageType.java (original) +++ trunk/src/com/google/caja/parser/quasiliteral/RewriterMessageType.java Tue Jul 14 13:50:32 2009
@@ -158,7 +158,27 @@
       "%s: Prototypical inheritance is not supported in Cajita. "
       + "The \"prototype\" property of a function is always "
       + "\"undefined\": %s, %s",
-      MessageLevel.LINT);
+      MessageLevel.LINT),
+
+  LOADING_MODULE_FAILED(
+          "%s: Loading module %s failed: %s",
+          MessageLevel.FATAL_ERROR),
+
+  INVALID_MODULE_URI(
+          "%s: Invalid URI for the module: %s",
+          MessageLevel.FATAL_ERROR),
+
+  MODULE_NOT_FOUND(
+          "%s: Module not found: %s",
+          MessageLevel.FATAL_ERROR),
+
+  PARSING_MODULE_FAILED(
+      "%s: Parsing module failed: %s",
+      MessageLevel.FATAL_ERROR),
+
+  CANNOT_LOAD_A_DYNAMIC_MODULE(
+      "%s: Cannot load a dynamic module",
+      MessageLevel.FATAL_ERROR);

   private final String formatString;
   private final MessageLevel level;

Modified: trunk/src/com/google/caja/plugin/ExpressionSanitizerCaja.java
==============================================================================
--- trunk/src/com/google/caja/plugin/ExpressionSanitizerCaja.java       
(original)
+++ trunk/src/com/google/caja/plugin/ExpressionSanitizerCaja.java Tue Jul 14 13:50:32 2009
@@ -65,7 +65,8 @@

   /** Visible for testing. */
   protected Rewriter newCajitaRewriter() {
-    return new CajitaRewriter(buildInfo, false);
+    return new CajitaRewriter(
+        buildInfo, meta.getPluginEnvironment(), false);
   }

   protected Rewriter newValijaRewriter() {

Modified: trunk/tests/com/google/caja/parser/quasiliteral/CajitaRewriterTest.java
==============================================================================
--- trunk/tests/com/google/caja/parser/quasiliteral/CajitaRewriterTest.java (original) +++ trunk/tests/com/google/caja/parser/quasiliteral/CajitaRewriterTest.java Tue Jul 14 13:50:32 2009
@@ -14,7 +14,10 @@

 package com.google.caja.parser.quasiliteral;

+import com.google.caja.lexer.CharProducer;
+import com.google.caja.lexer.ExternalReference;
 import com.google.caja.lexer.FilePosition;
+import com.google.caja.lexer.InputSource;
 import com.google.caja.lexer.ParseException;
 import com.google.caja.parser.ParseTreeNode;
 import com.google.caja.parser.ParseTreeNodes;
@@ -28,12 +31,14 @@
 import com.google.caja.parser.js.StringLiteral;
 import com.google.caja.parser.js.SyntheticNodes;
 import com.google.caja.parser.js.UncajoledModule;
+import com.google.caja.plugin.PluginEnvironment;
 import com.google.caja.reporting.MessageLevel;
 import com.google.caja.reporting.MessageType;
 import com.google.caja.reporting.TestBuildInfo;
 import com.google.caja.util.RhinoTestBed;

 import java.io.IOException;
+import java.net.URI;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
@@ -47,8 +52,30 @@
  */
 public class CajitaRewriterTest extends CommonJsRewriterTestCase {

+  protected class TestPluginEnvironment implements PluginEnvironment {
+    @Override
+    public CharProducer loadExternalResource(
+        ExternalReference ref, String mimeType) {
+      URI uri = ref.getUri();
+      uri = ref.getReferencePosition().source().getUri().resolve(uri);
+      if ("test".equals(uri.getScheme())) {
+        try {
+          InputSource is = new InputSource(uri);
+          return fromResource(uri.getPath().substring(1), is);
+        } catch (IOException e) {
+        }
+      }
+      return null;
+    }
+
+    @Override
+    public String rewriteUri(ExternalReference uri, String mimeType) {
+      return null;
+    }
+  }
+
   protected Rewriter defaultCajaRewriter =
-      new CajitaRewriter(new TestBuildInfo(), false);
+ new CajitaRewriter(new TestBuildInfo(), new TestPluginEnvironment(), false);

   @Override
   public void setUp() throws Exception {
@@ -2256,6 +2283,30 @@
         "var x;" +
         "try { x = toString; } catch (e) {}" +
"if (x) { cajita.fail('Inherited global properties are readable'); }");
+  }
+
+  /**
+   * Tests the securable module loading
+   */
+  public void testModule() throws Exception {
+    rewriteAndExecute(
+        "var r = loader.load('foo/b')({x: 6, y: 3}); "
+        + "assertEquals(11, r);");
+
+    rewriteAndExecute(
+        "var m = loader.load('foo/b');"
+        + "var s = m.cajolerName;"
+        + "assertEquals('com.google.caja', s);");
+
+    checkAddsMessage(
+        js(fromString("var m = loader.load('foo/c');")),
+        RewriterMessageType.MODULE_NOT_FOUND,
+        MessageLevel.FATAL_ERROR);
+
+    checkAddsMessage(
+        js(fromString("var s = 'c'; var m = loader.load(s);")),
+        RewriterMessageType.CANNOT_LOAD_A_DYNAMIC_MODULE,
+        MessageLevel.FATAL_ERROR);
   }

   @Override

Added: trunk/tests/com/google/caja/parser/quasiliteral/c.js
==============================================================================
--- (empty file)
+++ trunk/tests/com/google/caja/parser/quasiliteral/c.js Tue Jul 14 13:50:32 2009
@@ -0,0 +1 @@
+x + y
\ No newline at end of file

Added: trunk/tests/com/google/caja/parser/quasiliteral/foo/b.js
==============================================================================
--- (empty file)
+++ trunk/tests/com/google/caja/parser/quasiliteral/foo/b.js Tue Jul 14 13:50:32 2009
@@ -0,0 +1 @@
+loader.load('../c')({x: x + 1, y: y + 1});
\ No newline at end of file

Reply via email to