Super Dev Mode: sourcemap URL fixes
Externally visible changes:
- put back old URL. (Needed for some Google integration tests.)
- changed sourcemap filename in URL's to [strong name]_sourcemap.json
(The '0' is not needed and it doesn't need to match the filename on
disk.)
- fixed a bug in a regular expression: The period in ".json" was not
escaped so it would match anything.
Refactoring:
- keep the code for URL-matching separate from the code for finding
sourcemap files on disk.
- Fixed the unit tests to report better errors. Inlined some constants
for readability.
Change-Id: I366b9b13b0026d922428ae3f1ae56abad9d07059
diff --git a/dev/codeserver/java/com/google/gwt/dev/codeserver/ModuleState.java b/dev/codeserver/java/com/google/gwt/dev/codeserver/ModuleState.java
index 9f5db6d..e107df4 100644
--- a/dev/codeserver/java/com/google/gwt/dev/codeserver/ModuleState.java
+++ b/dev/codeserver/java/com/google/gwt/dev/codeserver/ModuleState.java
@@ -38,6 +38,12 @@
* to recompile it and where the compiler output is.
*/
class ModuleState {
+
+ /**
+ * The suffix that the GWT compiler uses when writing a sourcemap file.
+ */
+ private static final String SOURCEMAP_FILE_SUFFIX = "_sourceMap0.json";
+
private final AtomicReference<CompileDir> current = new AtomicReference<CompileDir>();
private final Recompiler recompiler;
private final TreeLogger logger;
@@ -93,16 +99,18 @@
}
/**
- * Returns the source map file from the most recent recompile.
+ * Returns the source map file from the most recent recompile,
+ * assuming there is one permutation.
+ *
* @throws RuntimeException if unable
*/
- File findSourceMap() {
+ File findSourceMapForOnePermutation() {
File dir = findSymbolMapDir();
File[] sourceMapFiles = dir.listFiles(new FilenameFilter() {
@Override
public boolean accept(File dir, String name) {
- return name.matches(".*" + SourceHandler.SOURCEMAP_SUFFIX);
+ return name.endsWith(SOURCEMAP_FILE_SUFFIX);
}
});
@@ -122,10 +130,24 @@
}
/**
+ * Returns the source map file given a strong name.
+ *
+ * @throws RuntimeException if unable
+ */
+ public File findSourceMap(String strongName) {
+ File dir = findSymbolMapDir();
+ File file = new File(dir, strongName + SOURCEMAP_FILE_SUFFIX);
+ if (!file.isFile()) {
+ throw new RuntimeException("Sourcemap file doesn't exist for " + strongName);
+ }
+ return file;
+ }
+
+ /**
* Returns the symbols map folder for this modulename.
* @throws RuntimeException if unable
*/
- File findSymbolMapDir() {
+ private File findSymbolMapDir() {
String moduleName = recompiler.getModuleName();
File symbolMapsDir = current.get().findSymbolMapDir(moduleName);
if (symbolMapsDir == null) {
diff --git a/dev/codeserver/java/com/google/gwt/dev/codeserver/Recompiler.java b/dev/codeserver/java/com/google/gwt/dev/codeserver/Recompiler.java
index cd1fe49..1c42f2c 100644
--- a/dev/codeserver/java/com/google/gwt/dev/codeserver/Recompiler.java
+++ b/dev/codeserver/java/com/google/gwt/dev/codeserver/Recompiler.java
@@ -360,7 +360,7 @@
// Fix bug with SDM and Chrome 24+ where //@ sourceURL directives cause X-SourceMap header to be ignored
// Frustratingly, Chrome won't canonicalize a relative URL
overrideConfig(moduleDef, "includeSourceMapUrl", "http://" + serverPrefix +
- WebServer.sourceMapLocationForModule(moduleDef.getName()));
+ SourceHandler.sourceMapLocationTemplate(moduleDef.getName()));
// If present, set some config properties back to defaults.
// (Needed for Google's server-side linker.)
diff --git a/dev/codeserver/java/com/google/gwt/dev/codeserver/ReverseSourceMap.java b/dev/codeserver/java/com/google/gwt/dev/codeserver/ReverseSourceMap.java
index a000d71..2b81781 100644
--- a/dev/codeserver/java/com/google/gwt/dev/codeserver/ReverseSourceMap.java
+++ b/dev/codeserver/java/com/google/gwt/dev/codeserver/ReverseSourceMap.java
@@ -37,7 +37,7 @@
*/
static ReverseSourceMap load(TreeLogger logger, ModuleState moduleState) {
SourceMapConsumerV3 consumer = new SourceMapConsumerV3();
- String unparsed = Util.readFileAsString(moduleState.findSourceMap());
+ String unparsed = Util.readFileAsString(moduleState.findSourceMapForOnePermutation());
try {
consumer.parse(unparsed);
return new ReverseSourceMap(consumer);
diff --git a/dev/codeserver/java/com/google/gwt/dev/codeserver/SourceHandler.java b/dev/codeserver/java/com/google/gwt/dev/codeserver/SourceHandler.java
index 53b9d14..2982d5e 100644
--- a/dev/codeserver/java/com/google/gwt/dev/codeserver/SourceHandler.java
+++ b/dev/codeserver/java/com/google/gwt/dev/codeserver/SourceHandler.java
@@ -38,9 +38,9 @@
* >Source Map Spec</a>, such as Chrome.)
*
* <p>The debugger will first fetch the source map from
- * /sourcemaps/\{module name\}/gwtSourceMap.json. This file contains the names of Java
+ * /sourcemaps/[module name]/[strong name]_sourcemap.json. This file contains the names of Java
* source files to download. Each source file will have a path like
- * "/sourcemaps/\{module name\}/src/{filename}".</p>
+ * "/sourcemaps/[module name]/src/[filename]".</p>
*/
class SourceHandler {
@@ -50,9 +50,9 @@
static final String SOURCEMAP_PATH = "/sourcemaps/";
/**
- * The suffix of a source map location json file.
+ * The suffix that Super Dev Mode uses in source map URL's.
*/
- static final String SOURCEMAP_SUFFIX = "_sourceMap0.json";
+ private static final String SOURCEMAP_URL_SUFFIX = "_sourcemap.json";
/**
* Matches a valid source map json file request.
@@ -61,7 +61,7 @@
* StrongName_sourceMap0.json
*/
private static final Pattern SOURCEMAP_FILENAME_PATTERN = Pattern.compile(
- "^([\\dA-F]{32})" + SOURCEMAP_SUFFIX + "$");
+ "^(" + WebServer.STRONG_NAME + ")" + Pattern.quote(SOURCEMAP_URL_SUFFIX) + "$");
/**
* Matches a valid source map request.
@@ -87,6 +87,14 @@
return getModuleNameFromRequest(target) != null;
}
+ /**
+ * The template for the sourcemap location to give the compiler.
+ * It contains one template variable, __HASH__ for the strong name.
+ */
+ static String sourceMapLocationTemplate(String moduleName) {
+ return SOURCEMAP_PATH + moduleName + "/__HASH__" + SOURCEMAP_URL_SUFFIX;
+ }
+
void handle(String target, HttpServletRequest request, HttpServletResponse response)
throws IOException {
String moduleName = getModuleNameFromRequest(target);
@@ -99,6 +107,12 @@
if (rest.isEmpty()) {
sendDirectoryListPage(moduleName, response);
+ } else if (rest.equals("gwtSourceMap.json")) {
+ // This URL is no longer used by debuggers (we use the strong name) but is used for testing.
+ // It's useful not to need the strong name to download the sourcemap.
+ // (But this only works when there is one permutation.)
+ ModuleState moduleState = modules.get(moduleName);
+ sendSourceMap(moduleName, moduleState.findSourceMapForOnePermutation(), request, response);
} else if (rest.endsWith("/")) {
sendFileListPage(moduleName, rest, response);
} else if (rest.endsWith(".java")) {
@@ -106,7 +120,9 @@
} else {
String strongName = getStrongNameFromSourcemapFilename(rest);
if (strongName != null) {
- sendSourceMap(moduleName, strongName, request, response);
+ ModuleState moduleState = modules.get(moduleName);
+ File sourceMap = moduleState.findSourceMap(strongName).getAbsoluteFile();
+ sendSourceMap(moduleName, sourceMap, request, response);
} else {
response.sendError(HttpServletResponse.SC_NOT_FOUND);
logger.log(TreeLogger.WARN, "returned not found for request: " + target);
@@ -124,17 +140,11 @@
return matcher.matches() ? matcher.group(1) : null;
}
- private void sendSourceMap(String moduleName, String strongName, HttpServletRequest request,
+ private void sendSourceMap(String moduleName, File sourceMap, HttpServletRequest request,
HttpServletResponse response) throws IOException {
long startTime = System.currentTimeMillis();
- ModuleState moduleState = modules.get(moduleName);
-
- String sourceMapPath = moduleState.findSymbolMapDir().getAbsolutePath();
-
- File sourceMap = new File(sourceMapPath + "/" + strongName + SOURCEMAP_SUFFIX);
-
// Stream the file, substituting the sourceroot variable with the filename.
// (This is more efficient than parsing the file as JSON.)
@@ -269,6 +279,6 @@
private SourceMap loadSourceMap(String moduleName) {
ModuleState moduleState = modules.get(moduleName);
- return SourceMap.load(moduleState.findSourceMap());
+ return SourceMap.load(moduleState.findSourceMapForOnePermutation());
}
}
diff --git a/dev/codeserver/java/com/google/gwt/dev/codeserver/WebServer.java b/dev/codeserver/java/com/google/gwt/dev/codeserver/WebServer.java
index 66fe315..7fcc85c 100644
--- a/dev/codeserver/java/com/google/gwt/dev/codeserver/WebServer.java
+++ b/dev/codeserver/java/com/google/gwt/dev/codeserver/WebServer.java
@@ -82,6 +82,10 @@
private static final Pattern SAFE_CALLBACK =
Pattern.compile("([a-zA-Z_][a-zA-Z0-9_]*\\.)*[a-zA-Z_][a-zA-Z0-9_]*");
+ static final Pattern STRONG_NAME = Pattern.compile("[\\dA-F]{32}");
+
+ private static final Pattern CACHE_JS_FILE = Pattern.compile("/(" + STRONG_NAME + ").cache.js$");
+
private static final MimeTypes MIME_TYPES = new MimeTypes();
private final SourceHandler handler;
@@ -296,9 +300,12 @@
response.setHeader("Content-Encoding", "gzip");
}
- if (target.endsWith(".cache.js")) {
- String strongName = target.replaceFirst("^.*/(.+).cache.js$", "$1");
- response.setHeader("X-SourceMap", sourceMapLocationForModule(moduleName, strongName));
+ Matcher match = CACHE_JS_FILE.matcher(target);
+ if (match.matches()) {
+ String strongName = match.group(1);
+ String template = SourceHandler.sourceMapLocationTemplate(moduleName);
+ String sourceMapUrl = template.replace("__HASH__", strongName);
+ response.setHeader("X-SourceMap", sourceMapUrl);
}
response.setHeader("Access-Control-Allow-Origin", "*");
String mimeType = guessMimeType(target);
@@ -504,15 +511,6 @@
return result;
}
- static String sourceMapLocationForModule(String moduleName) {
- return sourceMapLocationForModule(moduleName, "__HASH__");
- }
-
- private static String sourceMapLocationForModule(String moduleName, String strongName) {
- return SourceHandler.SOURCEMAP_PATH + moduleName + "/" + strongName
- + SourceHandler.SOURCEMAP_SUFFIX;
- }
-
private static void setHandled(HttpServletRequest request) {
Request baseRequest = (request instanceof Request) ? (Request) request :
AbstractHttpConnection.getCurrentConnection().getRequest();
diff --git a/dev/codeserver/test/com/google/gwt/dev/codeserver/SourceHandlerTest.java b/dev/codeserver/test/com/google/gwt/dev/codeserver/SourceHandlerTest.java
index 1451b45..c1a89a4 100644
--- a/dev/codeserver/test/com/google/gwt/dev/codeserver/SourceHandlerTest.java
+++ b/dev/codeserver/test/com/google/gwt/dev/codeserver/SourceHandlerTest.java
@@ -1,8 +1,5 @@
package com.google.gwt.dev.codeserver;
-import static com.google.gwt.dev.codeserver.SourceHandler.SOURCEMAP_PATH;
-import static com.google.gwt.dev.codeserver.SourceHandler.SOURCEMAP_SUFFIX;
-
import com.google.gwt.dev.util.Util;
import static org.junit.Assert.assertEquals;
@@ -17,7 +14,6 @@
*/
public class SourceHandlerTest {
- private static final String VALID_MODULE_NAME = "myModule";
private static final String VALID_STRONG_NAME = Util.computeStrongName("foo-bar".getBytes());
/**
@@ -25,17 +21,14 @@
*/
@Test
public void testIsSourceMapRequest() {
- assertTrue(SourceHandler.isSourceMapRequest(SOURCEMAP_PATH + VALID_MODULE_NAME + "/"));
- assertTrue(SourceHandler.isSourceMapRequest(SOURCEMAP_PATH + VALID_MODULE_NAME + "/whatever"));
- assertTrue(SourceHandler.isSourceMapRequest(SOURCEMAP_PATH + VALID_MODULE_NAME + "/folder/"));
- assertTrue(SourceHandler.isSourceMapRequest(
- SOURCEMAP_PATH + VALID_MODULE_NAME + "/folder/file.ext"));
- assertTrue(SourceHandler.isSourceMapRequest(
- SOURCEMAP_PATH + VALID_MODULE_NAME + "/" + VALID_STRONG_NAME + SOURCEMAP_SUFFIX));
+ checkSourceMapRequest("/sourcemaps/myModule/");
+ checkSourceMapRequest("/sourcemaps/myModule/whatever");
+ checkSourceMapRequest("/sourcemaps/myModule/folder/");
+ checkSourceMapRequest("/sourcemaps/myModule/folder/file.ext");
+ checkSourceMapRequest("/sourcemaps/myModule/" + VALID_STRONG_NAME + "_sourcemap.json");
- assertFalse(SourceHandler.isSourceMapRequest(SOURCEMAP_PATH + VALID_MODULE_NAME));
- assertFalse(SourceHandler.isSourceMapRequest(
- "whatever" + SOURCEMAP_PATH + VALID_MODULE_NAME + "/"));
+ checkNotSourceMapRequest("/sourcemaps/myModule");
+ checkNotSourceMapRequest("whatever/sourcemaps/myModule/");
}
/**
@@ -43,21 +36,36 @@
*/
@Test
public void testGetModuleNameFromRequest() {
- assertEquals(VALID_MODULE_NAME, SourceHandler.getModuleNameFromRequest(
- SOURCEMAP_PATH + VALID_MODULE_NAME + "/"));
- assertEquals(VALID_MODULE_NAME, SourceHandler.getModuleNameFromRequest(
- SOURCEMAP_PATH + VALID_MODULE_NAME + "/" + VALID_STRONG_NAME + SOURCEMAP_SUFFIX));
+ assertEquals("myModule", SourceHandler.getModuleNameFromRequest(
+ "/sourcemaps/myModule/"));
+ assertEquals("myModule", SourceHandler.getModuleNameFromRequest(
+ "/sourcemaps/myModule/1234_sourcemap.json"));
}
/**
* Test {@link SourceHandler#getStrongNameFromSourcemapFilename(String)}
*/
@Test
- public void testGwtStrongNameFromSourcemapFilename() {
+ public void testGetStrongNameFromSourcemapFilename() {
assertEquals(VALID_STRONG_NAME, SourceHandler
- .getStrongNameFromSourcemapFilename(VALID_STRONG_NAME + SOURCEMAP_SUFFIX));
- assertNull(SourceHandler.getStrongNameFromSourcemapFilename("invalid_hash" + SOURCEMAP_SUFFIX));
- assertNull(SourceHandler.getStrongNameFromSourcemapFilename(
- "whatever/" + VALID_STRONG_NAME + SOURCEMAP_SUFFIX));
+ .getStrongNameFromSourcemapFilename(VALID_STRONG_NAME + "_sourcemap.json"));
+ checkNoStrongName("invalid_hash_sourcemap.json");
+ checkNoStrongName("whatever/" + VALID_STRONG_NAME + "_sourcemap.json");
+ checkNoStrongName(VALID_STRONG_NAME + "_sourcemap/json");
+ }
+
+ private void checkSourceMapRequest(String validUrl) {
+ assertTrue("should be a valid sourcemap URL but isn't: " + validUrl,
+ SourceHandler.isSourceMapRequest(validUrl));
+ }
+
+ private void checkNotSourceMapRequest(String validUrl) {
+ assertFalse("should not be a valid sourcemap URL but is: " + validUrl,
+ SourceHandler.isSourceMapRequest(validUrl));
+ }
+
+ private void checkNoStrongName(String rest) {
+ assertNull("shouldn't have returned a strong name for: " + rest,
+ SourceHandler.getStrongNameFromSourcemapFilename(rest));
}
}