Goktug Gokdogan has uploaded a new change for review.
https://gwt-review.googlesource.com/3512
Change subject: Move StackTraceDeobfuscator from core.server.impl to
core.server.
......................................................................
Move StackTraceDeobfuscator from core.server.impl to core.server.
This patch moves StackTraceDeobfuscator to core.server as the API is
finalized.
Change-Id: Ic84f3b2210e3c40f0f7dc41bdeea9d13bf1a4dc6
---
D user/src/com/google/gwt/core/server/impl/StackTraceDeobfuscator.java
M user/src/com/google/gwt/junit/server/JUnitHostImpl.java
M user/src/com/google/gwt/logging/server/RemoteLoggingServiceImpl.java
M user/src/com/google/gwt/logging/server/RemoteLoggingServiceUtil.java
M user/src/com/google/gwt/logging/server/StackTraceDeobfuscator.java
M user/src/com/google/web/bindery/requestfactory/server/Logging.java
M user/test/com/google/gwt/user/server/rpc/LoggingRPCTestServiceImpl.java
7 files changed, 6 insertions(+), 442 deletions(-)
diff --git
a/user/src/com/google/gwt/core/server/impl/StackTraceDeobfuscator.java
b/user/src/com/google/gwt/core/server/impl/StackTraceDeobfuscator.java
deleted file mode 100644
index 2d62b5c..0000000
--- a/user/src/com/google/gwt/core/server/impl/StackTraceDeobfuscator.java
+++ /dev/null
@@ -1,436 +0,0 @@
-/*
- * Copyright 2013 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.core.server.impl;
-
-import
com.google.gwt.thirdparty.debugging.sourcemap.SourceMapConsumerFactory;
-import com.google.gwt.thirdparty.debugging.sourcemap.SourceMapping;
-import com.google.gwt.thirdparty.debugging.sourcemap.proto.Mapping;
-
-import java.io.BufferedReader;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.net.URL;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Map;
-import java.util.Scanner;
-import java.util.Set;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-/**
- * Deobfuscates stack traces on the server side. This class requires that
you have turned on
- * emulated stack traces, via <code><set-property
name="compiler.stackMode" value="emulated"
- * /></code> in your <code>.gwt.xml</code> module file for browsers
that don't support
- * sourcemaps or <code><set-property name="compiler.useSourceMaps"
value="true"/></code> for
- * browsers that support it (e.g. Chrome), and moved your symbol map files
to a location accessible
- * by your server-side code. You can use the GWT compiler
<code>-deploy</code> command line
- * argument to specify the location of the folder into which the generated
<code>symbolMaps</code>
- * directory is written. By default, the final <code>symbolMaps</code>
directory is
- * <code>war/WEB-INF/deploy/<i>yourmodulename</i>/symbolMaps/</code>.
- */
-public abstract class StackTraceDeobfuscator {
-
- /**
- * Creates a deobfuscator that loads symbol and source map files under
given resource path. Uses
- * StackTraceObfuscator's {@link ClassLoader}.
- */
- public static StackTraceDeobfuscator fromResource(String symbolMapsPath)
{
- final String basePath = symbolMapsPath.endsWith("/") ?
symbolMapsPath : symbolMapsPath + "/";
- final ClassLoader classLoader =
StackTraceDeobfuscator.class.getClassLoader();
- return new StackTraceDeobfuscator() {
- protected InputStream openInputStream(String fileName) throws
IOException {
- String filePath = basePath + fileName;
- InputStream inputStream =
classLoader.getResourceAsStream(filePath);
- if (inputStream == null) {
- throw new IOException("Missing resource: " + filePath);
- }
- return inputStream;
- }
- };
- }
-
- /**
- * Creates a deobfuscator that loads symbol and source map files from
the given directory.
- */
- public static StackTraceDeobfuscator fromFileSystem(final String
symbolMapsDirectory) {
- return new StackTraceDeobfuscator() {
- protected InputStream openInputStream(String fileName) throws
IOException {
- return new FileInputStream(new File(symbolMapsDirectory,
fileName));
- }
- };
- }
-
- /**
- * Creates a deobfuscator that loads symbol and source map files beneath
the given URL.
- */
- public static StackTraceDeobfuscator fromUrl(final URL urlPath) {
- return new StackTraceDeobfuscator() {
- protected InputStream openInputStream(String fileName) throws
IOException {
- return new URL(urlPath, fileName).openStream();
- }
- };
- }
-
- /**
- * A cache that maps obfuscated symbols to arbitrary non-null string
values. The cache can assume
- * each (strongName, symbol) pair always maps to the same value (never
goes invalid), but must
- * treat data as an opaque string.
- */
- private static class SymbolCache {
- // TODO(srogoff): This SymbolCache implementation never drops old
entries. If clients ever need
- // to cap memory usage even with lazy loading, consider making
SymbolCache an interface.
- // This could allow clients to pass their own implementation to the
StackTraceDeobfuscator
- // constructor, backed by a Guava Cache or other entry-evicting
mapping.
-
- private final ConcurrentHashMap<String, HashMap<String, String>>
symbolMaps;
-
- SymbolCache() {
- symbolMaps = new ConcurrentHashMap<String, HashMap<String,
String>>();
- }
-
- /**
- * Adds some symbol data to the cache for the given strong name.
- */
- void putAll(String strongName, Map<String, String> symbolMap) {
- if (strongName == null || symbolMap.size() == 0) {
- return;
- }
- symbolMaps.putIfAbsent(strongName, new HashMap<String, String>());
- HashMap<String, String> existingMap = symbolMaps.get(strongName);
- synchronized (existingMap) {
- existingMap.putAll(symbolMap);
- }
- }
-
- /**
- * Returns the data for each of the specified symbols that's currently
cached for the given
- * strong name. There will be no entry for symbols that are not in the
cache. If none of the
- * symbols are cached, an empty Map is returned.
- */
- Map<String, String> getAll(String strongName, Set<String> symbols) {
- Map<String, String> toReturn = new HashMap<String, String>();
- if (strongName == null || !symbolMaps.containsKey(strongName) ||
symbols.isEmpty()) {
- return toReturn;
- }
- HashMap<String, String> existingMap = symbolMaps.get(strongName);
- synchronized (existingMap) {
- for (String symbol : symbols) {
- if (existingMap.containsKey(symbol)) {
- toReturn.put(symbol, existingMap.get(symbol));
- }
- }
- }
- return toReturn;
- }
- }
-
- private static final Pattern JsniRefPattern =
Pattern.compile("@?([^:]+)::([^(]+)(\\((.*)\\))?");
- private static final Pattern fragmentIdPattern =
Pattern.compile(".*(\\d+)\\.js");
- private static final int LINE_NUMBER_UNKNOWN = -1;
- private static final String SYMBOL_DATA_UNKNOWN = "";
-
- private final Map<String, SourceMapping> sourceMaps = new
HashMap<String, SourceMapping>();
- private final SymbolCache symbolCache = new SymbolCache();
- private boolean lazyLoad = false;
-
- /**
- * If set to {@code true}, only symbols requested to be deobfuscated are
cached and the rest is
- * discarded. This provides a large memory savings at the expense of
occasional extra disk reads.
- * Note that, this will only have effect on symbol maps that haven't
been fully loaded yet.
- */
- public void setLazyLoad(boolean lazyLoad) {
- this.lazyLoad = lazyLoad;
- }
-
- /**
- * Replaces the stack traces in the given Throwable and its causes with
deobfuscated stack traces
- * wherever possible.
- *
- * @param throwable the Throwable that needs its stack trace to be
deobfuscated
- * @param strongName the GWT permutation strong name
- */
- public final void deobfuscateStackTrace(Throwable throwable, String
strongName) {
- throwable.setStackTrace(resymbolize(throwable.getStackTrace(),
strongName));
- if (throwable.getCause() != null) {
- deobfuscateStackTrace(throwable.getCause(), strongName);
- }
- }
-
- /**
- * Convenience method which resymbolizes an entire stack trace to extent
possible.
- *
- * @param st the stack trace to resymbolize
- * @param strongName the GWT permutation strong name
- * @return a best effort resymbolized stack trace
- */
- public final StackTraceElement[] resymbolize(StackTraceElement[] st,
String strongName) {
- if (st == null) {
- return null;
- }
- // Warm the symbol cache for all symbols in this stack trace.
- Set<String> requiredSymbols = new HashSet<String>();
- for (StackTraceElement ste : st) {
- requiredSymbols.add(ste.getMethodName());
- }
- loadSymbolMap(strongName, requiredSymbols);
-
- StackTraceElement[] newSt = new StackTraceElement[st.length];
- for (int i = 0; i < st.length; i++) {
- newSt[i] = resymbolize(st[i], strongName);
- }
- return newSt;
- }
-
- /**
- * Best effort resymbolization of a single stack trace element.
- *
- * @param ste the stack trace element to resymbolize
- * @param strongName the GWT permutation strong name
- * @return the best effort resymbolized stack trace element
- */
- public final StackTraceElement resymbolize(StackTraceElement ste, String
strongName) {
- String declaringClass = null;
- String methodName = null;
- String filename = null;
- int lineNumber = -1;
- int fragmentId = -1;
-
- String steFilename = ste.getFileName();
- String symbolData = loadOneSymbol(strongName, ste.getMethodName());
-
- boolean sourceMapCapable = false;
-
- int column = 1;
- // column information is encoded in filename after '@' for sourceMap
capable browsers
- if (steFilename != null) {
- int columnMarkerIndex = steFilename.indexOf("@");
- if (columnMarkerIndex != -1) {
- try {
- column =
Integer.parseInt(steFilename.substring(columnMarkerIndex + 1));
- sourceMapCapable = true;
- } catch (NumberFormatException nfe) {
- }
- steFilename = steFilename.substring(0, columnMarkerIndex);
- }
- }
-
- // first use symbolMap, then refine via sourceMap if possible
- if (!symbolData.isEmpty()) {
- // jsniIdent, className, memberName, sourceUri, sourceLine,
fragmentId
- String[] parts = symbolData.split(",");
- if (parts.length == 6) {
- String[] ref = parse(
- parts[0].substring(0, parts[0].lastIndexOf(')') + 1));
-
- if (ref != null) {
- declaringClass = ref[0];
- methodName = ref[1];
- } else {
- declaringClass = ste.getClassName();
- methodName = ste.getMethodName();
- }
-
- // parts[3] contains the source file URI or "Unknown"
- filename = "Unknown".equals(parts[3]) ? null
- : parts[3].substring(parts[3].lastIndexOf('/') + 1);
-
- lineNumber = ste.getLineNumber();
-
- /*
- * When lineNumber is LINE_NUMBER_UNKNOWN, either because
- * compiler.stackMode is not emulated or
- * compiler.emulatedStack.recordLineNumbers is false, use the
method
- * declaration line number from the symbol map.
- */
- if (lineNumber == LINE_NUMBER_UNKNOWN || (sourceMapCapable &&
column == -1)) {
- // Safari will send line numbers, with col == -1, we need to use
symbolMap in this case
- lineNumber = Integer.parseInt(parts[4]);
- }
-
- fragmentId = Integer.parseInt(parts[5]);
- }
- }
-
- // anonymous function, try to use <fragmentNum>.js:line to determine
fragment id
- if (fragmentId == -1 && steFilename != null) {
- // fragment identifier encoded in filename
- Matcher matcher = fragmentIdPattern.matcher(steFilename);
- if (matcher.matches()) {
- String fragment = matcher.group(1);
- try {
- fragmentId = Integer.parseInt(fragment);
- } catch (Exception e) {
- }
- } else if (steFilename.contains(strongName)) {
- // else it's <strongName>.cache.js which is the 0th fragment
- fragmentId = 0;
- }
- }
-
- int jsLineNumber = ste.getLineNumber();
-
- // try to refine location via sourcemap
- if (sourceMapCapable && fragmentId != -1 && column != -1) {
- SourceMapping sourceMapping = loadSourceMap(strongName, fragmentId);
- if (sourceMapping != null && ste.getLineNumber() > -1) {
- Mapping.OriginalMapping mappingForLine = sourceMapping
- .getMappingForLine(jsLineNumber, column);
- if (mappingForLine != null) {
-
- if (declaringClass == null ||
declaringClass.equals(ste.getClassName())) {
- declaringClass = mappingForLine.getOriginalFile();
- methodName = mappingForLine.getIdentifier();
- }
- filename = mappingForLine.getOriginalFile();
- lineNumber = mappingForLine.getLineNumber();
- }
- }
- }
-
- if (declaringClass != null) {
- return new StackTraceElement(declaringClass, methodName, filename,
lineNumber);
- }
-
- // If anything goes wrong, just return the unobfuscated element
- return ste;
- }
-
- protected InputStream getSourceMapInputStream(String
permutationStrongName, int fragmentNumber)
- throws IOException {
- return openInputStream(permutationStrongName + "_sourceMap" +
fragmentNumber + ".json");
- }
-
- /**
- * Retrieves a new {@link InputStream} for the given permutation strong
name. This implementation,
- * which subclasses may override, returns a {@link InputStream} for the
<code>
- * <i>permutation-strong-name</i>.symbolMap</code> file.
- *
- * @param permutationStrongName the GWT permutation strong name
- * @return a new {@link InputStream}
- */
- protected InputStream getSymbolMapInputStream(String
permutationStrongName) throws IOException {
- return openInputStream(permutationStrongName + ".symbolMap");
- }
-
- /**
- * Opens a new {@link InputStream} for a symbol or source map file.
- *
- * @param fileName name of the symbol or source map file
- * @return an input stream for reading the file (doesn't need to be
buffered).
- * @exception IOException if an I/O error occurs while creating the
input stream.
- */
- protected abstract InputStream openInputStream(String fileName) throws
IOException;
-
- private SourceMapping loadSourceMap(String permutationStrongName, int
fragmentId) {
- SourceMapping toReturn = sourceMaps.get(permutationStrongName +
fragmentId);
- if (toReturn == null) {
- try {
- String sourceMapString = loadStreamAsString(
- getSourceMapInputStream(permutationStrongName, fragmentId));
- toReturn = SourceMapConsumerFactory.parse(sourceMapString);
- sourceMaps.put(permutationStrongName + fragmentId, toReturn);
- } catch (Exception e) {
- }
- }
- return toReturn;
- }
-
- private String loadStreamAsString(InputStream stream) {
- return new Scanner(stream).useDelimiter("\\A").next();
- }
-
- private String loadOneSymbol(String strongName, String symbol) {
- Set<String> symbolSet = new HashSet<String>();
- symbolSet.add(symbol);
- Map<String, String> symbolMap = loadSymbolMap(strongName, symbolSet);
- return symbolMap.get(symbol);
- }
-
- /**
- * Returns a symbol map for the given strong name containing symbol data
for
- * all of the given required symbols. First checks the symbol cache, then
- * reads from disk if any symbol is missing. If a symbol cannot be
loaded for
- * some reason, it will be mapped to empty string.
- */
- private Map<String, String> loadSymbolMap(
- String strongName, Set<String> requiredSymbols) {
- Map<String, String> toReturn = symbolCache.getAll(strongName,
requiredSymbols);
- if (toReturn.size() == requiredSymbols.size()) {
- return toReturn;
- }
-
- Set<String> symbolsLeftToFind = new HashSet<String>(requiredSymbols);
- toReturn = new HashMap<String, String>();
- String line;
-
- try {
- BufferedReader bin = new BufferedReader(
- new InputStreamReader(getSymbolMapInputStream(strongName)));
- try {
- while ((line = bin.readLine()) != null &&
(symbolsLeftToFind.size() > 0 || !lazyLoad)) {
- if (line.charAt(0) == '#') {
- continue;
- }
- int idx = line.indexOf(',');
- String symbol = line.substring(0, idx);
- String symbolData = line.substring(idx + 1);
- if (requiredSymbols.contains(symbol) || !lazyLoad) {
- symbolsLeftToFind.remove(symbol);
- toReturn.put(symbol, symbolData);
- }
- }
- } finally {
- bin.close();
- }
- } catch (IOException e) {
- // If the symbol map isn't found or there's an I/O error reading the
file, the returned
- // mapping may contain some or all empty data (see below).
- }
- for (String symbol : symbolsLeftToFind) {
- // Store the empty string in the symbolCache to show we actually
looked on disk and couldn't
- // find the symbols. This avoids reading disk repeatedly for symbols
that can't be translated.
- toReturn.put(symbol, SYMBOL_DATA_UNKNOWN);
- }
-
- symbolCache.putAll(strongName, toReturn);
- return toReturn;
- }
-
- /**
- * Extracts the declaring class and method name from a JSNI ref, or null
if the information cannot
- * be extracted.
- *
- * @param refString symbol map reference string
- * @return a string array contains the declaring class and method name,
or null when the regex
- * match fails
- * @see com.google.gwt.dev.util.JsniRef
- */
- private String[] parse(String refString) {
- Matcher matcher = JsniRefPattern.matcher(refString);
- if (!matcher.matches()) {
- return null;
- }
- String className = matcher.group(1);
- String memberName = matcher.group(2);
- String[] toReturn = new String[]{className, memberName};
- return toReturn;
- }
-}
diff --git a/user/src/com/google/gwt/junit/server/JUnitHostImpl.java
b/user/src/com/google/gwt/junit/server/JUnitHostImpl.java
index 0577cbb..a7ba7b5 100644
--- a/user/src/com/google/gwt/junit/server/JUnitHostImpl.java
+++ b/user/src/com/google/gwt/junit/server/JUnitHostImpl.java
@@ -17,7 +17,7 @@
import static
com.google.gwt.user.client.rpc.RpcRequestBuilder.MODULE_BASE_HEADER;
-import com.google.gwt.core.server.impl.StackTraceDeobfuscator;
+import com.google.gwt.core.server.StackTraceDeobfuscator;
import com.google.gwt.junit.JUnitFatalLaunchException;
import com.google.gwt.junit.JUnitMessageQueue;
import com.google.gwt.junit.JUnitMessageQueue.ClientInfoExt;
diff --git
a/user/src/com/google/gwt/logging/server/RemoteLoggingServiceImpl.java
b/user/src/com/google/gwt/logging/server/RemoteLoggingServiceImpl.java
index 15cc374..924dd5b 100644
--- a/user/src/com/google/gwt/logging/server/RemoteLoggingServiceImpl.java
+++ b/user/src/com/google/gwt/logging/server/RemoteLoggingServiceImpl.java
@@ -16,7 +16,7 @@
package com.google.gwt.logging.server;
-import com.google.gwt.core.server.impl.StackTraceDeobfuscator;
+import com.google.gwt.core.server.StackTraceDeobfuscator;
import
com.google.gwt.logging.server.RemoteLoggingServiceUtil.RemoteLoggingException;
import com.google.gwt.logging.shared.RemoteLoggingService;
import com.google.gwt.user.server.rpc.RemoteServiceServlet;
diff --git
a/user/src/com/google/gwt/logging/server/RemoteLoggingServiceUtil.java
b/user/src/com/google/gwt/logging/server/RemoteLoggingServiceUtil.java
index 940cc95..027edd2 100644
--- a/user/src/com/google/gwt/logging/server/RemoteLoggingServiceUtil.java
+++ b/user/src/com/google/gwt/logging/server/RemoteLoggingServiceUtil.java
@@ -16,7 +16,7 @@
package com.google.gwt.logging.server;
-import com.google.gwt.core.server.impl.StackTraceDeobfuscator;
+import com.google.gwt.core.server.StackTraceDeobfuscator;
import java.util.logging.LogRecord;
import java.util.logging.Logger;
diff --git
a/user/src/com/google/gwt/logging/server/StackTraceDeobfuscator.java
b/user/src/com/google/gwt/logging/server/StackTraceDeobfuscator.java
index 11b3060..1493a69 100644
--- a/user/src/com/google/gwt/logging/server/StackTraceDeobfuscator.java
+++ b/user/src/com/google/gwt/logging/server/StackTraceDeobfuscator.java
@@ -26,7 +26,7 @@
* @deprecated Use com.google.gwt.core.server.StackTraceDeobfuscator
instead.
*/
@Deprecated
-public class StackTraceDeobfuscator extends
com.google.gwt.core.server.impl.StackTraceDeobfuscator {
+public class StackTraceDeobfuscator extends
com.google.gwt.core.server.StackTraceDeobfuscator {
protected File symbolMapsDirectory;
diff --git
a/user/src/com/google/web/bindery/requestfactory/server/Logging.java
b/user/src/com/google/web/bindery/requestfactory/server/Logging.java
index 485fb04..2b91b8c 100644
--- a/user/src/com/google/web/bindery/requestfactory/server/Logging.java
+++ b/user/src/com/google/web/bindery/requestfactory/server/Logging.java
@@ -16,7 +16,7 @@
package com.google.web.bindery.requestfactory.server;
-import com.google.gwt.core.server.impl.StackTraceDeobfuscator;
+import com.google.gwt.core.server.StackTraceDeobfuscator;
import com.google.gwt.logging.server.RemoteLoggingServiceUtil;
import
com.google.gwt.logging.server.RemoteLoggingServiceUtil.RemoteLoggingException;
import com.google.gwt.user.client.rpc.RpcRequestBuilder;
diff --git
a/user/test/com/google/gwt/user/server/rpc/LoggingRPCTestServiceImpl.java
b/user/test/com/google/gwt/user/server/rpc/LoggingRPCTestServiceImpl.java
index f08ac46..6facda3 100644
---
a/user/test/com/google/gwt/user/server/rpc/LoggingRPCTestServiceImpl.java
+++
b/user/test/com/google/gwt/user/server/rpc/LoggingRPCTestServiceImpl.java
@@ -16,7 +16,7 @@
package com.google.gwt.user.server.rpc;
-import com.google.gwt.core.server.impl.StackTraceDeobfuscator;
+import com.google.gwt.core.server.StackTraceDeobfuscator;
import com.google.gwt.junit.linker.JUnitSymbolMapsLinker;
import com.google.gwt.logging.server.RemoteLoggingServiceUtil;
import com.google.gwt.user.client.rpc.LoggingRPCTest;
--
To view, visit https://gwt-review.googlesource.com/3512
To unsubscribe, visit https://gwt-review.googlesource.com/settings
Gerrit-MessageType: newchange
Gerrit-Change-Id: Ic84f3b2210e3c40f0f7dc41bdeea9d13bf1a4dc6
Gerrit-PatchSet: 1
Gerrit-Project: gwt
Gerrit-Branch: master
Gerrit-Owner: Goktug Gokdogan <[email protected]>
--
http://groups.google.com/group/Google-Web-Toolkit-Contributors
---
You received this message because you are subscribed to the Google Groups "GWT Contributors" group.
To unsubscribe from this group and stop receiving emails from it, send an email
to [email protected].
For more options, visit https://groups.google.com/groups/opt_out.