Author: johnh
Date: Sat Mar 26 05:18:14 2011
New Revision: 1085647
URL: http://svn.apache.org/viewvc?rev=1085647&view=rev
Log:
Reintroduce Closure Compiler to Shindig, and implement Java 1.5/1.6-specific
JsCompiler bindings.
This CL reintroduces a Closure-based JsCompiler implementation, and uses Paul
Lindner's Maven work to ensure
that it can be built for deployments utilizing Java 1.6. Java 1.5 gracefully
falls back to the build-time compilation mechanism
in ExportJsCompiler, which does not depend on a 1.6-specific package.
Added:
shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/js/JsServingPipelineModule.java
- copied, changed from r1085589,
shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/js/JavascriptModule.java
shindig/trunk/java/gadgets/src/main/java15/org/apache/shindig/gadgets/js/
shindig/trunk/java/gadgets/src/main/java15/org/apache/shindig/gadgets/js/JsCompilerModule.java
shindig/trunk/java/gadgets/src/main/java16/org/apache/shindig/gadgets/js/
shindig/trunk/java/gadgets/src/main/java16/org/apache/shindig/gadgets/js/JsCompilerModule.java
shindig/trunk/java/gadgets/src/main/java16/org/apache/shindig/gadgets/rewrite/
shindig/trunk/java/gadgets/src/main/java16/org/apache/shindig/gadgets/rewrite/js/
shindig/trunk/java/gadgets/src/main/java16/org/apache/shindig/gadgets/rewrite/js/ClosureJsCompiler.java
shindig/trunk/java/gadgets/src/test/java16/
shindig/trunk/java/gadgets/src/test/java16/org/
shindig/trunk/java/gadgets/src/test/java16/org/apache/
shindig/trunk/java/gadgets/src/test/java16/org/apache/shindig/
shindig/trunk/java/gadgets/src/test/java16/org/apache/shindig/gadgets/
shindig/trunk/java/gadgets/src/test/java16/org/apache/shindig/gadgets/rewrite/
shindig/trunk/java/gadgets/src/test/java16/org/apache/shindig/gadgets/rewrite/js/
shindig/trunk/java/gadgets/src/test/java16/org/apache/shindig/gadgets/rewrite/js/ClosureJsCompilerTest.java
Removed:
shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/js/JavascriptModule.java
Modified:
shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/DefaultGuiceModule.java
shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/rewrite/js/ExportJsCompiler.java
shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/rewrite/js/ExportJsCompilerTest.java
shindig/trunk/pom.xml
Modified:
shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/DefaultGuiceModule.java
URL:
http://svn.apache.org/viewvc/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/DefaultGuiceModule.java?rev=1085647&r1=1085646&r2=1085647&view=diff
==============================================================================
---
shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/DefaultGuiceModule.java
(original)
+++
shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/DefaultGuiceModule.java
Sat Mar 26 05:18:14 2011
@@ -34,7 +34,8 @@ import org.apache.shindig.gadgets.config
import org.apache.shindig.gadgets.http.AbstractHttpCache;
import org.apache.shindig.gadgets.http.HttpResponse;
import org.apache.shindig.gadgets.http.InvalidationHandler;
-import org.apache.shindig.gadgets.js.JavascriptModule;
+import org.apache.shindig.gadgets.js.JsCompilerModule;
+import org.apache.shindig.gadgets.js.JsServingPipelineModule;
import org.apache.shindig.gadgets.parse.ParseModule;
import org.apache.shindig.gadgets.preload.PreloadModule;
import org.apache.shindig.gadgets.render.RenderModule;
@@ -76,7 +77,8 @@ public class DefaultGuiceModule extends
install(new SubstituterModule());
install(new TemplateModule());
install(new UriModule());
- install(new JavascriptModule());
+ install(new JsCompilerModule());
+ install(new JsServingPipelineModule());
//
bind(Long.class).annotatedWith(Names.named("org.apache.shindig.serviceExpirationDurationMinutes")).toInstance(60l);
Copied:
shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/js/JsServingPipelineModule.java
(from r1085589,
shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/js/JavascriptModule.java)
URL:
http://svn.apache.org/viewvc/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/js/JsServingPipelineModule.java?p2=shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/js/JsServingPipelineModule.java&p1=shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/js/JavascriptModule.java&r1=1085589&r2=1085647&rev=1085647&view=diff
==============================================================================
---
shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/js/JavascriptModule.java
(original)
+++
shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/js/JsServingPipelineModule.java
Sat Mar 26 05:18:14 2011
@@ -25,13 +25,10 @@ import com.google.inject.Provides;
import java.util.List;
-import org.apache.shindig.gadgets.rewrite.js.ExportJsCompiler;
-import org.apache.shindig.gadgets.rewrite.js.JsCompiler;
-
/**
* Guice configuration for the Javascript serving pipeline.
*/
-public class JavascriptModule extends AbstractModule {
+public class JsServingPipelineModule extends AbstractModule {
@Override
protected void configure() {
@@ -40,12 +37,6 @@ public class JavascriptModule extends Ab
@Provides
@Inject
- public JsCompiler provideJsCompiler(ExportJsCompiler compiler) {
- return compiler;
- }
-
- @Provides
- @Inject
public List<JsProcessor> provideProcessors(
InjectJsInfoVariableProcessor injectJsInfoVariableProcessor,
JsLoadProcessor jsLoaderGeneratorProcessor,
Modified:
shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/rewrite/js/ExportJsCompiler.java
URL:
http://svn.apache.org/viewvc/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/rewrite/js/ExportJsCompiler.java?rev=1085647&r1=1085646&r2=1085647&view=diff
==============================================================================
---
shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/rewrite/js/ExportJsCompiler.java
(original)
+++
shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/rewrite/js/ExportJsCompiler.java
Sat Mar 26 05:18:14 2011
@@ -83,11 +83,6 @@ public class ExportJsCompiler extends De
return builder.build();
}
- @Override
- protected String getFeatureContent(JsUri jsUri, FeatureResource resource) {
- return resource.getDebugContent();
- }
-
private JsContent getExportsForFeature(JsUri jsUri, FeatureBundle bundle) {
List<String> exports = Lists.newArrayList();
Added:
shindig/trunk/java/gadgets/src/main/java15/org/apache/shindig/gadgets/js/JsCompilerModule.java
URL:
http://svn.apache.org/viewvc/shindig/trunk/java/gadgets/src/main/java15/org/apache/shindig/gadgets/js/JsCompilerModule.java?rev=1085647&view=auto
==============================================================================
---
shindig/trunk/java/gadgets/src/main/java15/org/apache/shindig/gadgets/js/JsCompilerModule.java
(added)
+++
shindig/trunk/java/gadgets/src/main/java15/org/apache/shindig/gadgets/js/JsCompilerModule.java
Sat Mar 26 05:18:14 2011
@@ -0,0 +1,45 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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 org.apache.shindig.gadgets.js;
+
+import com.google.inject.AbstractModule;
+import com.google.inject.Inject;
+import com.google.inject.Provides;
+
+import org.apache.shindig.gadgets.rewrite.js.JsCompiler;
+import org.apache.shindig.gadgets.rewrite.js.ExportJsCompiler;
+
+import java.util.List;
+
+/**
+ * Guice configuration for the Javascript compilation.
+ */
+public class JsCompilerModule extends AbstractModule {
+
+ @Override
+ protected void configure() {
+ // nothing to configure here
+ }
+
+ @Provides
+ @Inject
+ public JsCompiler provideJsCompiler(ExportJsCompiler compiler) {
+ return compiler;
+ }
+
+}
Added:
shindig/trunk/java/gadgets/src/main/java16/org/apache/shindig/gadgets/js/JsCompilerModule.java
URL:
http://svn.apache.org/viewvc/shindig/trunk/java/gadgets/src/main/java16/org/apache/shindig/gadgets/js/JsCompilerModule.java?rev=1085647&view=auto
==============================================================================
---
shindig/trunk/java/gadgets/src/main/java16/org/apache/shindig/gadgets/js/JsCompilerModule.java
(added)
+++
shindig/trunk/java/gadgets/src/main/java16/org/apache/shindig/gadgets/js/JsCompilerModule.java
Sat Mar 26 05:18:14 2011
@@ -0,0 +1,45 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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 org.apache.shindig.gadgets.js;
+
+import com.google.inject.AbstractModule;
+import com.google.inject.Inject;
+import com.google.inject.Provides;
+
+import org.apache.shindig.gadgets.rewrite.js.ExportJsCompiler;
+import org.apache.shindig.gadgets.rewrite.js.JsCompiler;
+
+import java.util.List;
+
+/**
+ * Guice configuration for JS compilation.
+ */
+public class JsCompilerModule extends AbstractModule {
+
+ @Override
+ protected void configure() {
+ // nothing to configure here
+ }
+
+ @Provides
+ @Inject
+ public JsCompiler provideJsCompiler(ExportJsCompiler compiler) {
+ return compiler;
+ }
+
+}
Added:
shindig/trunk/java/gadgets/src/main/java16/org/apache/shindig/gadgets/rewrite/js/ClosureJsCompiler.java
URL:
http://svn.apache.org/viewvc/shindig/trunk/java/gadgets/src/main/java16/org/apache/shindig/gadgets/rewrite/js/ClosureJsCompiler.java?rev=1085647&view=auto
==============================================================================
---
shindig/trunk/java/gadgets/src/main/java16/org/apache/shindig/gadgets/rewrite/js/ClosureJsCompiler.java
(added)
+++
shindig/trunk/java/gadgets/src/main/java16/org/apache/shindig/gadgets/rewrite/js/ClosureJsCompiler.java
Sat Mar 26 05:18:14 2011
@@ -0,0 +1,396 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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 org.apache.shindig.gadgets.rewrite.js;
+
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.base.Joiner;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+import com.google.inject.Inject;
+import com.google.javascript.jscomp.BasicErrorManager;
+import com.google.javascript.jscomp.CheckLevel;
+import com.google.javascript.jscomp.CompilationLevel;
+import com.google.javascript.jscomp.Compiler;
+import com.google.javascript.jscomp.CompilerOptions;
+import com.google.javascript.jscomp.JSError;
+import com.google.javascript.jscomp.JSSourceFile;
+import com.google.javascript.jscomp.Result;
+import com.google.javascript.jscomp.SourceMap;
+
+import org.apache.commons.lang.StringEscapeUtils;
+import org.apache.shindig.common.cache.Cache;
+import org.apache.shindig.common.cache.CacheProvider;
+import org.apache.shindig.common.util.HashUtil;
+import org.apache.shindig.gadgets.features.ApiDirective;
+import org.apache.shindig.gadgets.features.FeatureRegistry;
+import org.apache.shindig.gadgets.features.FeatureRegistry.FeatureBundle;
+import org.apache.shindig.gadgets.http.HttpResponse;
+import org.apache.shindig.gadgets.js.JsContent;
+import org.apache.shindig.gadgets.js.JsException;
+import org.apache.shindig.gadgets.js.JsResponse;
+import org.apache.shindig.gadgets.js.JsResponseBuilder;
+import org.apache.shindig.gadgets.rewrite.js.ExportJsCompiler;
+import org.apache.shindig.gadgets.rewrite.js.JsCompiler;
+import org.apache.shindig.gadgets.uri.JsUriManager.JsUri;
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.StringReader;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+
+public class ClosureJsCompiler implements JsCompiler {
+ // Based on Closure Library's goog.exportSymbol implementation.
+ private static final JsContent EXPORTSYMBOL_CODE =
+ new JsContent("var goog=goog||{};goog.exportSymbol=function(name,obj){"
+ + "var parts=name.split('.'),cur=window,part;"
+ + "for(;parts.length&&(part=parts.shift());){if(!parts.length){"
+ + "cur[part]=obj;}else{cur=cur[part]||(cur[part]={})}}};",
"[goog.exportSymbol]");
+
+ private static final JSSourceFile[] JSSOURCE_TYPE = new JSSourceFile[0];
+
+ @VisibleForTesting
+ static final String CACHE_NAME = "CompiledJs";
+
+ private final ExportJsCompiler exportCompiler;
+ private final CompilerOptions options;
+ private final Cache<String, ClosureResult> cache;
+ private ClosureResult lastResult;
+
+ @Inject
+ public ClosureJsCompiler(CacheProvider cacheProvider, FeatureRegistry
registry) {
+ this(newCompilerOptions(), cacheProvider, registry);
+ }
+
+ public ClosureJsCompiler(CompilerOptions options, CacheProvider
cacheProvider,
+ FeatureRegistry registry) {
+ this(options, new ExportJsCompiler(registry), cacheProvider);
+ }
+
+ @VisibleForTesting
+ ClosureJsCompiler(CompilerOptions options, ExportJsCompiler exportCompiler,
+ CacheProvider cacheProvider) {
+ // TODO: Consider using Provider<Compiler> here.
+ this.options = options;
+ this.cache = cacheProvider.createCache(CACHE_NAME);
+ this.exportCompiler = exportCompiler;
+ }
+
+ public static CompilerOptions newCompilerOptions() {
+ // Same as
google3/javascript/closure/builddefs:CLOSURE_COMPILER_FLAGS_FULL.
+ // Flags are used/preferred by Gmail.
+ CompilerOptions result = new CompilerOptions();
+
CompilationLevel.ADVANCED_OPTIMIZATIONS.setOptionsForCompilationLevel(result);
+ result.removeUnusedPrototypePropertiesInExterns = false;
+ // Avoid multiple declarations of tamings___ variables.
+ // TODO: Get rid of this deviation from standard flags.
+ result.checkSymbols = false;
+ result.sourceMapOutputPath = "create.out";
+ result.sourceMapDetailLevel = SourceMap.DetailLevel.ALL;
+
+ return result;
+ }
+
+ @VisibleForTesting
+ Compiler newCompiler() {
+ BasicErrorManager errorManager = new BasicErrorManager() {
+ @Override
+ protected void printSummary() { /* Do nothing */ }
+
+ @Override
+ public void println(CheckLevel arg0, JSError arg1) { /* Do nothing */ }
+ };
+ return new Compiler(errorManager);
+ }
+
+ private String toExternString(List<String> externs) {
+ StringBuilder builder = new StringBuilder();
+ for (String extern : externs) {
+ builder.append(extern).append(";\n");
+ }
+ return builder.toString();
+ }
+
+ public JsResponse compile(JsUri jsUri, Iterable<JsContent> content,
List<String> externs)
+ throws JsException {
+ JsResponse exportResponse = exportCompiler.compile(jsUri, content,
externs);
+ content = exportResponse.getAllJsContent();
+
+ String externStr = toExternString(externs);
+ String cacheKey = makeCacheKey(exportResponse.toJsString(), externStr,
jsUri);
+ ClosureResult cachedResult = cache.getElement(cacheKey);
+ if (cachedResult != null) {
+ lastResult = cachedResult;
+ return cachedResult.response;
+ }
+
+ JsResponseBuilder builder = new JsResponseBuilder();
+ String theExterns = null;
+
+ // Only run actual compiler if necessary.
+ if (!jsUri.isDebug() || options.isExternExportsEnabled()) {
+ List<JSSourceFile> allExterns = Lists.newArrayList();
+ allExterns.add(JSSourceFile.fromCode("externs", externStr));
+
+ List<JsContent> allContent = Lists.newLinkedList(content);
+ if (options.isExternExportsEnabled()) {
+ allContent.add(EXPORTSYMBOL_CODE);
+ }
+
+ Compiler actualCompiler = newCompiler();
+ Result result = actualCompiler.compile(
+ allExterns.toArray(JSSOURCE_TYPE),
+ convertToJsSource(allContent).toArray(JSSOURCE_TYPE),
+ options);
+
+ if (actualCompiler.hasErrors()) {
+ ImmutableList.Builder<String> errors = ImmutableList.builder();
+ for (JSError error : actualCompiler.getErrors()) {
+ errors.add(error.toString());
+ }
+ builder.setStatusCode(HttpResponse.SC_NOT_FOUND)
+ .addErrors(errors.build()).build();
+ ClosureResult errorResult = new ClosureResult(builder.build(), null);
+ cache.addElement(cacheKey, errorResult);
+ return errorResult.response;
+ }
+
+ String compiled = actualCompiler.toSource();
+ if (outputCorrelatedJs()) {
+ // Emit code correlated w/ original source.
+ // This operation is equivalent in final code to bundled-output,
+ // but is less efficient and should perhaps only be used in code
profiling.
+ SourceMappings sm = processSourceMap(result, allContent);
+ builder.appendAllJs(sm.mapCompiled(compiled));
+ } else {
+ builder.appendJs(compiled, "[compiled]");
+ }
+
+ theExterns = result.externExport;
+ } else {
+ // Otherwise, return original content and null exports.
+ builder.appendAllJs(content);
+ }
+
+ lastResult = new ClosureResult(builder.build(), theExterns);
+ cache.addElement(cacheKey, lastResult);
+ return lastResult.response;
+ }
+
+ // Override this method to return "true" for cases where individual chunks of
+ // compiled JS should be emitted as JsContent objects, each correlating
output JS
+ // with the original source file from which they came.
+ protected boolean outputCorrelatedJs() {
+ return false;
+ }
+
+ private List<JSSourceFile> convertToJsSource(Iterable<JsContent> content) {
+ Map<String, Integer> sourceMap = Maps.newHashMap();
+ List<JSSourceFile> sources = Lists.newLinkedList();
+ for (JsContent src : content) {
+ sources.add(JSSourceFile.fromCode(getUniqueSrc(src.getSource(),
sourceMap), src.get()));
+ }
+ return sources;
+ }
+
+ private static String getUniqueSrc(String source, Map<String, Integer>
sourceMap) {
+ Integer ix = sourceMap.get(source);
+ if (ix == null) {
+ ix = 0;
+ }
+ String ret = source + (ix > 0 ? ":" + ix : "");
+ sourceMap.put(source, ix + 1);
+ return ret;
+ }
+
+ private static String getRootSrc(String source) {
+ int colIx = source.lastIndexOf(":");
+ if (colIx == -1) {
+ return source;
+ }
+ return source.substring(0, colIx);
+ }
+
+ public Iterable<JsContent> getJsContent(JsUri jsUri, FeatureBundle bundle) {
+ jsUri = new JsUri(jsUri) {
+ @Override
+ public boolean isDebug() {
+ // Force debug JS in the raw JS content retrieved.
+ return true;
+ }
+ };
+ List<JsContent> builder =
Lists.newLinkedList(exportCompiler.getJsContent(jsUri, bundle));
+
+ if (options.isExternExportsEnabled()) {
+ List<String> exports =
Lists.newArrayList(bundle.getApis(ApiDirective.Type.JS, true));
+ Collections.sort(exports);
+ String prevExport = null;
+ for (String export : exports) {
+ if (!export.equals(prevExport)) {
+ builder.add(new JsContent("goog.exportSymbol('" +
StringEscapeUtils.escapeJavaScript(export) +
+ "', " + export + ");\n", "[export-symbol]"));
+ prevExport = export;
+ }
+ }
+ }
+ return builder;
+ }
+
+ public ClosureResult getLastResult() {
+ return this.lastResult;
+ }
+
+ protected String makeCacheKey(String code, String externs, JsUri uri) {
+ // TODO: include compilation options in the cache key
+ return Joiner.on(":").join(
+ HashUtil.checksum(code.getBytes()),
+ HashUtil.checksum(externs.getBytes()),
+ uri.getCompileMode(),
+ uri.isDebug());
+ }
+
+ public static class ClosureResult {
+ private final JsResponse response;
+ private final String externs;
+
+ public ClosureResult(JsResponse response, String externs) {
+ this.response = response;
+ this.externs = externs;
+ }
+
+ public String getExterns() {
+ return externs;
+ }
+ }
+
+ /**
+ * Pull the source map out of the given Closure Result, and convert
+ * it to a local SourceMappings object used to correlate compiled
+ * content with originating source.
+ * @param result Closure result object with source map.
+ * @param allInputs All inputs supplied to the compiler, in JsContent form.
+ * @return SourceMappings object correlating compiled with originating input.
+ */
+ private SourceMappings processSourceMap(Result result, List<JsContent>
allInputs)
+ throws JsException {
+ StringBuilder sb = new StringBuilder();
+ try {
+ result.sourceMap.appendTo(sb, "done");
+ return SourceMappings.parseV1(sb.toString(), allInputs);
+ } catch (Exception e) {
+ throw new JsException(HttpResponse.SC_INTERNAL_SERVER_ERROR,
+ "Parse error for source map: " + e);
+ }
+ }
+
+ private static class SourceMappings {
+ private final Map<String, JsContent> orig;
+ private final int[][] lines;
+ private final String[] mappings;
+
+ private SourceMappings(int[][] lines, String[] mappings, List<JsContent>
content) {
+ this.lines = lines;
+ this.mappings = mappings;
+ this.orig = Maps.newHashMap();
+ for (JsContent js : content) {
+ orig.put(js.getSource(), js);
+ }
+ }
+
+ private List<JsContent> mapCompiled(String compiled) {
+ List<JsContent> compiledOut = Lists.newLinkedList();
+ int codeStart = 0;
+ int codePos = 0;
+ int curMapping = -1;
+ for (int line = 0; line < lines.length; ++line) {
+ for (int col = 0; col < lines[line].length; ++col) {
+ int nextMapping = lines[line][col];
+ codePos++;
+ if (nextMapping != curMapping && curMapping != -1) {
+ JsContent sourceJs = orig.get(getRootSrc(mappings[curMapping]));
+ compiledOut.add(new JsContent(compiled.substring(codeStart,
codePos),
+ sourceJs.getSource(), sourceJs.getFeature()));
+ codeStart = codePos;
+ }
+ curMapping = nextMapping;
+ }
+ }
+ JsContent lastSource = orig.get(getRootSrc(mappings[curMapping]));
+ compiledOut.add(new JsContent(compiled.substring(codeStart, codePos + 1),
+ lastSource.getSource(), lastSource.getFeature()));
+ return compiledOut;
+ }
+
+ private static final String BEGIN_COMMENT = "/*";
+ private static final String END_COMMENT = "*/";
+ private static SourceMappings parseV1(String sourcemap, List<JsContent>
orig)
+ throws IOException, JSONException {
+ BufferedReader reader = new BufferedReader(new StringReader(sourcemap));
+ JSONObject summary = new JSONObject(stripComment(reader.readLine()));
+
+ int lineCount = summary.getInt("count");
+
+ // Read lines info.
+ int maxMappingIndex = 0;
+ int[][] lines = new int[lineCount][];
+ for (int i = 0; i < lineCount; ++i) {
+ String lineDescriptor = reader.readLine();
+ JSONArray lineArr = new JSONArray(lineDescriptor);
+ lines[i] = new int[lineArr.length()];
+ for (int j = 0; j < lineArr.length(); ++j) {
+ int mappingIndex = lineArr.getInt(j);
+ lines[i][j] = mappingIndex;
+ maxMappingIndex = Math.max(mappingIndex, maxMappingIndex);
+ }
+ }
+
+ // Bypass comment and unused file info for each line.
+ reader.readLine(); // comment
+ for (int i = 0; i < lineCount; ++i) {
+ reader.readLine();
+ }
+
+ // Read mappings objects.
+ reader.readLine(); // comment
+ String[] mappings = new String[maxMappingIndex + 1];
+ for (int i = 0; i <= maxMappingIndex; ++i) {
+ String mappingLine = reader.readLine();
+ JSONArray mappingObj = new JSONArray(mappingLine);
+ mappings[i] = mappingObj.getString(0);
+ }
+
+ return new SourceMappings(lines, mappings, orig);
+ }
+
+ private static String stripComment(String line) {
+ int begin = line.indexOf(BEGIN_COMMENT);
+ if (begin != -1) {
+ int end = line.indexOf(END_COMMENT, begin + 1);
+ if (end != -1) {
+ return line.substring(0, begin) + line.substring(end +
END_COMMENT.length());
+ }
+ }
+ return line;
+ }
+ }
+}
Modified:
shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/rewrite/js/ExportJsCompilerTest.java
URL:
http://svn.apache.org/viewvc/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/rewrite/js/ExportJsCompilerTest.java?rev=1085647&r1=1085646&r2=1085647&view=diff
==============================================================================
---
shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/rewrite/js/ExportJsCompilerTest.java
(original)
+++
shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/rewrite/js/ExportJsCompilerTest.java
Sat Mar 26 05:18:14 2011
@@ -97,7 +97,7 @@ public class ExportJsCompilerTest {
expect(result.getContext()).andReturn(RenderingContext.GADGET).anyTimes();
expect(result.getContainer()).andReturn(CONTAINER).anyTimes();
expect(result.getCompileMode()).andReturn(mode).anyTimes();
- expect(result.isDebug()).andReturn(false).anyTimes();
+ expect(result.isDebug()).andReturn(true).anyTimes();
replay(result);
return result;
}
@@ -108,7 +108,7 @@ public class ExportJsCompilerTest {
replay(result);
return result;
}
-
+
private FeatureResource mockResource(boolean external, String debContent,
String optContent) {
FeatureResource result = createMock(FeatureResource.class);
expect(result.getDebugContent()).andReturn(debContent).anyTimes();
Added:
shindig/trunk/java/gadgets/src/test/java16/org/apache/shindig/gadgets/rewrite/js/ClosureJsCompilerTest.java
URL:
http://svn.apache.org/viewvc/shindig/trunk/java/gadgets/src/test/java16/org/apache/shindig/gadgets/rewrite/js/ClosureJsCompilerTest.java?rev=1085647&view=auto
==============================================================================
---
shindig/trunk/java/gadgets/src/test/java16/org/apache/shindig/gadgets/rewrite/js/ClosureJsCompilerTest.java
(added)
+++
shindig/trunk/java/gadgets/src/test/java16/org/apache/shindig/gadgets/rewrite/js/ClosureJsCompilerTest.java
Sat Mar 26 05:18:14 2011
@@ -0,0 +1,238 @@
+// Copyright 2011 Google Inc. All Rights Reserved.
+package org.apache.shindig.gadgets.rewrite.js;
+
+import static org.easymock.EasyMock.createMock;
+import static org.easymock.EasyMock.expect;
+import static org.easymock.EasyMock.isA;
+import static org.easymock.EasyMock.replay;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Lists;
+import com.google.javascript.jscomp.Compiler;
+import com.google.javascript.jscomp.CompilerOptions;
+import com.google.javascript.jscomp.DiagnosticType;
+import com.google.javascript.jscomp.JSError;
+import com.google.javascript.jscomp.JSSourceFile;
+import com.google.javascript.jscomp.Result;
+
+import junit.framework.TestCase;
+
+import org.apache.shindig.common.cache.Cache;
+import org.apache.shindig.common.cache.CacheProvider;
+import org.apache.shindig.common.cache.NullCache;
+import org.apache.shindig.gadgets.JsCompileMode;
+import org.apache.shindig.gadgets.RenderingContext;
+import org.apache.shindig.gadgets.features.ApiDirective;
+import org.apache.shindig.gadgets.features.FeatureRegistry.FeatureBundle;
+import org.apache.shindig.gadgets.js.JsContent;
+import org.apache.shindig.gadgets.js.JsResponse;
+import org.apache.shindig.gadgets.rewrite.js.ExportJsCompiler;
+import org.apache.shindig.gadgets.uri.UriStatus;
+import org.apache.shindig.gadgets.uri.JsUriManager.JsUri;
+
+import java.util.List;
+
+public class ClosureJsCompilerTest extends TestCase {
+
+ private Compiler realCompMock;
+ private CompilerOptions realOptionsMock;
+ private Result realResultMock;
+ private ExportJsCompiler exportCompilerMock;
+ private JsResponse exportResponseMock;
+ private JsUri jsUriMock;
+ private CacheProvider cacheMock;
+ private ClosureJsCompiler compiler;
+
+ private final String ACTUAL_COMPILER_OUTPUT = "window.abc={};";
+ private final String EXPORT_COMPILER_STRING = "window['abc'] = {};";
+ private final Iterable<JsContent> EXPORT_COMPILER_CONTENTS =
+ newJsContents(EXPORT_COMPILER_STRING);
+
+ private final String CLOSURE_ACTUAL_COMPILER_OUTPUT = ACTUAL_COMPILER_OUTPUT;
+ private final String CLOSURE_EXPORT_COMPILER_OUTPUT = EXPORT_COMPILER_STRING;
+
+ private final List<String> EXPORTS = ImmutableList.of("foo", "bar");
+
+ private final String EXTERN = "extern";
+ private final String ERROR_NAME = "error";
+ private final JSError JS_ERROR = JSError.make(
+ "js", 12, 34, DiagnosticType.error(ERROR_NAME, "errDesc"));
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ cacheMock = new MockProvider();
+ exportResponseMock = mockJsResponse();
+ exportCompilerMock = mockExportJsCompiler(exportResponseMock);
+ }
+
+ public void testGetJsContentWithGoogSymbolExports() throws Exception {
+ realOptionsMock = mockRealJsCompilerOptions(true); // with
+ compiler = newClosureJsCompiler(null, realOptionsMock, exportCompilerMock,
cacheMock);
+ FeatureBundle bundle = mockBundle(EXPORTS);
+ Iterable<JsContent> actual = compiler.getJsContent(mockJsUri(false),
bundle);
+ assertEquals(EXPORT_COMPILER_STRING +
+ "goog.exportSymbol('bar', bar);\n" +
+ "goog.exportSymbol('foo', foo);\n",
+ getContent(actual));
+ }
+
+ public void testGetJsContentWithoutGoogSymbolExports() throws Exception {
+ realOptionsMock = mockRealJsCompilerOptions(false); // without
+ compiler = newClosureJsCompiler(null, realOptionsMock, exportCompilerMock,
cacheMock);
+ FeatureBundle bundle = mockBundle(EXPORTS);
+ Iterable<JsContent> actual = compiler.getJsContent(mockJsUri(false),
bundle);
+ assertEquals(EXPORT_COMPILER_STRING, getContent(actual));
+ }
+
+ public void testCompileSuccessOpt() throws Exception {
+ jsUriMock = mockJsUri(false); // opt
+ realResultMock = mockRealJsResult();
+ realCompMock = mockRealJsCompiler(null, realResultMock,
ACTUAL_COMPILER_OUTPUT);
+ realOptionsMock = mockRealJsCompilerOptions(false);
+ compiler = newClosureJsCompiler(realCompMock, realOptionsMock,
exportCompilerMock, cacheMock);
+ JsResponse actual = compiler.compile(jsUriMock, EXPORT_COMPILER_CONTENTS,
+ ImmutableList.of(EXTERN));
+ assertEquals(CLOSURE_ACTUAL_COMPILER_OUTPUT, actual.toJsString());
+ assertTrue(actual.getErrors().isEmpty());
+ }
+
+ public void testCompileSuccessDeb() throws Exception {
+ jsUriMock = mockJsUri(true); // debug
+ realResultMock = mockRealJsResult();
+ realCompMock = mockRealJsCompiler(null, realResultMock,
ACTUAL_COMPILER_OUTPUT);
+ realOptionsMock = mockRealJsCompilerOptions(false);
+ compiler = newClosureJsCompiler(realCompMock, realOptionsMock,
exportCompilerMock, cacheMock);
+ JsResponse actual = compiler.compile(jsUriMock, EXPORT_COMPILER_CONTENTS,
+ ImmutableList.of(EXTERN));
+ assertEquals(CLOSURE_EXPORT_COMPILER_OUTPUT, actual.toJsString());
+ assertTrue(actual.getErrors().isEmpty());
+ }
+
+ public void testCompileErrorOpt() throws Exception {
+ jsUriMock = mockJsUri(false); // opt
+ realCompMock = mockRealJsCompiler(JS_ERROR, realResultMock,
ACTUAL_COMPILER_OUTPUT);
+ realOptionsMock = mockRealJsCompilerOptions(true); // force compiler to run
+ compiler = newClosureJsCompiler(realCompMock, realOptionsMock,
exportCompilerMock, cacheMock);
+ JsResponse actual = compiler.compile(jsUriMock, EXPORT_COMPILER_CONTENTS,
+ ImmutableList.of(EXTERN));
+ assertTrue(actual.getErrors().get(0).contains(ERROR_NAME));
+ assertEquals(1, actual.getErrors().size());
+ }
+
+ public void testCompileErrorDeb() throws Exception {
+ jsUriMock = mockJsUri(true); // debug
+ realCompMock = mockRealJsCompiler(JS_ERROR, realResultMock,
ACTUAL_COMPILER_OUTPUT);
+ realOptionsMock = mockRealJsCompilerOptions(true); // force compiler to run
+ compiler = newClosureJsCompiler(realCompMock, realOptionsMock,
exportCompilerMock, cacheMock);
+ JsResponse actual = compiler.compile(jsUriMock, EXPORT_COMPILER_CONTENTS,
+ ImmutableList.of(EXTERN));
+ assertTrue(actual.getErrors().get(0).contains(ERROR_NAME));
+ assertEquals(1, actual.getErrors().size());
+ }
+
+ private ClosureJsCompiler newClosureJsCompiler(final Compiler realComp,
+ CompilerOptions realOptions, ExportJsCompiler exportComp, CacheProvider
cache) {
+ return new ClosureJsCompiler(realOptionsMock, exportComp, cache) {
+ @Override
+ Compiler newCompiler() {
+ return realComp;
+ }
+ };
+ }
+
+ private JsResponse mockJsResponse() {
+ JsResponse result = createMock(JsResponse.class);
+ expect(result.toJsString()).andReturn(EXPORT_COMPILER_STRING).anyTimes();
+
expect(result.getAllJsContent()).andReturn(EXPORT_COMPILER_CONTENTS).anyTimes();
+ replay(result);
+ return result;
+ }
+
+ @SuppressWarnings("unchecked")
+ private ExportJsCompiler mockExportJsCompiler(JsResponse res) {
+ ExportJsCompiler result = createMock(ExportJsCompiler.class);
+ expect(result.getJsContent(isA(JsUri.class), isA(FeatureBundle.class)))
+ .andReturn(EXPORT_COMPILER_CONTENTS).anyTimes();
+ expect(result.compile(isA(JsUri.class), isA(Iterable.class),
isA(List.class)))
+ .andReturn(res).anyTimes();
+ replay(result);
+ return result;
+ }
+
+ private Result mockRealJsResult() {
+ Result result = createMock(Result.class);
+ return result;
+ }
+
+ private Compiler mockRealJsCompiler(JSError error, Result res, String
toSource) {
+ Compiler result = createMock(Compiler.class);
+ expect(result.compile(isA(JSSourceFile[].class), isA(JSSourceFile[].class),
+ isA(CompilerOptions.class))).andReturn(res);
+ if (error != null) {
+ expect(result.hasErrors()).andReturn(true);
+ expect(result.getErrors()).andReturn(new JSError[] { error });
+ } else {
+ expect(result.hasErrors()).andReturn(false);
+ }
+ expect(result.getResult()).andReturn(res);
+ expect(result.toSource()).andReturn(toSource);
+ replay(result);
+ return result;
+ }
+
+ private CompilerOptions mockRealJsCompilerOptions(boolean
enableExternExports) {
+ CompilerOptions result = createMock(CompilerOptions.class);
+
expect(result.isExternExportsEnabled()).andReturn(enableExternExports).anyTimes();
+ replay(result);
+ return result;
+ }
+
+ private JsUri mockJsUri(boolean debug) {
+ JsUri result = createMock(JsUri.class);
+ expect(result.isDebug()).andReturn(debug).anyTimes();
+
expect(result.getCompileMode()).andReturn(JsCompileMode.ALL_RUN_TIME).anyTimes();
+
expect(result.getStatus()).andReturn(UriStatus.VALID_UNVERSIONED).anyTimes();
+ expect(result.getContainer()).andReturn("container").anyTimes();
+
expect(result.getContext()).andReturn(RenderingContext.CONFIGURED_GADGET).anyTimes();
+ expect(result.getRefresh()).andReturn(1000).anyTimes();
+ expect(result.isNoCache()).andReturn(false).anyTimes();
+ expect(result.getGadget()).andReturn("http://foo.com/g.xml").anyTimes();
+ expect(result.getLibs()).andReturn(ImmutableList.<String>of()).anyTimes();
+
expect(result.getLoadedLibs()).andReturn(ImmutableList.<String>of()).anyTimes();
+ expect(result.getOnload()).andReturn("foo").anyTimes();
+ expect(result.isJsload()).andReturn(true).anyTimes();
+ expect(result.isNohint()).andReturn(true).anyTimes();
+ expect(result.getOrigUri()).andReturn(null).anyTimes();
+ replay(result);
+ return result;
+ }
+
+ private FeatureBundle mockBundle(List<String> exports) {
+ FeatureBundle result = createMock(FeatureBundle.class);
+ expect(result.getApis(ApiDirective.Type.JS,
true)).andReturn(exports).anyTimes();
+ expect(result.getName()).andReturn(null).anyTimes();
+ replay(result);
+ return result;
+ }
+
+ private class MockProvider implements CacheProvider {
+ public <K, V> Cache<K, V> createCache(String name) {
+ return new NullCache<K, V>();
+ }
+ }
+
+ private String getContent(Iterable<JsContent> jsContent) {
+ StringBuilder sb = new StringBuilder();
+ for (JsContent js : jsContent) {
+ sb.append(js.get());
+ }
+ return sb.toString();
+ }
+
+ private static List<JsContent> newJsContents(String jsCode) {
+ List<JsContent> result = Lists.newArrayList();
+ result.add(new JsContent(jsCode, null));
+ return result;
+ }
+}
Modified: shindig/trunk/pom.xml
URL:
http://svn.apache.org/viewvc/shindig/trunk/pom.xml?rev=1085647&r1=1085646&r2=1085647&view=diff
==============================================================================
--- shindig/trunk/pom.xml (original)
+++ shindig/trunk/pom.xml Sat Mar 26 05:18:14 2011
@@ -1364,6 +1364,10 @@
<id>oauth</id>
<url>http://oauth.googlecode.com/svn/code/maven</url>
</repository>
+ <repository>
+ <id>com.google.javascript</id>
+ <url>http://oss.sonatype.org/content/groups/staging</url>
+ </repository>
<!-- for jstl-1.2 for now.. -->
<repository>
<id>java.net</id>