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