Index: dart/compiler/java/com/google/dart/compiler/backend/js/ClosureJsBackend.java |
diff --git a/dart/compiler/java/com/google/dart/compiler/backend/js/ClosureJsBackend.java b/dart/compiler/java/com/google/dart/compiler/backend/js/ClosureJsBackend.java |
deleted file mode 100644 |
index c72c7f991872fbb21e179881667ee1cdefbb15c1..0000000000000000000000000000000000000000 |
--- a/dart/compiler/java/com/google/dart/compiler/backend/js/ClosureJsBackend.java |
+++ /dev/null |
@@ -1,664 +0,0 @@ |
-// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file |
-// for details. All rights reserved. Use of this source code is governed by a |
-// BSD-style license that can be found in the LICENSE file. |
- |
-package com.google.dart.compiler.backend.js; |
- |
-import com.google.common.base.Preconditions; |
-import com.google.common.collect.ImmutableList; |
-import com.google.common.collect.Lists; |
-import com.google.common.collect.Maps; |
-import com.google.common.collect.Sets; |
-import com.google.common.io.CharStreams; |
-import com.google.common.io.Closeables; |
-import com.google.common.io.LimitInputStream; |
-import com.google.dart.compiler.DartCompilationError; |
-import com.google.dart.compiler.DartCompilerContext; |
-import com.google.dart.compiler.DartSource; |
-import com.google.dart.compiler.LibrarySource; |
-import com.google.dart.compiler.Source; |
-import com.google.dart.compiler.ast.DartUnit; |
-import com.google.dart.compiler.ast.LibraryNode; |
-import com.google.dart.compiler.ast.LibraryUnit; |
-import com.google.dart.compiler.backend.js.ast.JsProgram; |
-import com.google.dart.compiler.common.SourceInfo; |
-import com.google.dart.compiler.metrics.CompilerMetrics; |
-import com.google.dart.compiler.resolver.CoreTypeProvider; |
-import com.google.javascript.jscomp.CheckLevel; |
-import com.google.javascript.jscomp.CompilationLevel; |
-import com.google.javascript.jscomp.Compiler; |
-import com.google.javascript.jscomp.CompilerInput; |
-import com.google.javascript.jscomp.CompilerOptions; |
-import com.google.javascript.jscomp.DiagnosticGroups; |
-import com.google.javascript.jscomp.JSError; |
-import com.google.javascript.jscomp.JSModule; |
-import com.google.javascript.jscomp.JSSourceFile; |
-import com.google.javascript.jscomp.PropertyRenamingPolicy; |
-import com.google.javascript.jscomp.Result; |
-import com.google.javascript.jscomp.SourceAst; |
-import com.google.javascript.jscomp.SourceMap.DetailLevel; |
-import com.google.javascript.jscomp.SourceMap.Format; |
-import com.google.javascript.jscomp.VariableRenamingPolicy; |
-import com.google.javascript.jscomp.WarningLevel; |
- |
-import java.io.BufferedInputStream; |
-import java.io.IOException; |
-import java.io.InputStream; |
-import java.io.Reader; |
-import java.io.StringReader; |
-import java.io.StringWriter; |
-import java.io.Writer; |
-import java.net.URI; |
-import java.util.Collection; |
-import java.util.HashMap; |
-import java.util.List; |
-import java.util.Map; |
-import java.util.logging.Level; |
-import java.util.logging.Logger; |
-import java.util.zip.ZipEntry; |
-import java.util.zip.ZipInputStream; |
- |
-/** |
- * A compiler backend that produces raw Javascript. |
- * @author johnlenz@google.com (John Lenz) |
- */ |
-public class ClosureJsBackend extends AbstractJsBackend { |
- private static final String EXTENSION_OPT_JS = "opt.js"; |
- private static final String EXTENSION_OPT_JS_SRC_MAP = "opt.js.map"; |
- |
- // A map of possible input sources to use when building the optimized output. |
- private Map<String, DartUnit> dartSrcToUnitMap = Maps.newHashMap(); |
- private long totalJsOutputCharCount; |
- |
- // Generate "readable" output for debugging |
- private final boolean generateHumanReadableOutput; |
- // Generate "good" instead of "best" output. |
- private final boolean fastOutput; |
- // TODO(johnlenz): Currently we can only support incremential builds |
- // if we aren't building source maps. |
- private final boolean incremental; |
- |
- // Validate the generated JavaScript |
- private final boolean validate; |
- |
- // Whether the generated code is "checked". |
- private final boolean checkedMode; |
- |
- public ClosureJsBackend() { |
- this(false, false); |
- } |
- |
- /** |
- * @param generateHumanReadableOutput - generates human readable javascript output. |
- */ |
- public ClosureJsBackend(boolean checkedMode, boolean generateHumanReadableOutput) { |
- // Current default settings |
- this(false, false, true, checkedMode, generateHumanReadableOutput); |
- } |
- |
- public ClosureJsBackend(boolean fastOutput, |
- boolean incremental, |
- boolean validate, |
- boolean checkedMode, |
- boolean generateHumanReadableOutput) { |
- this.fastOutput = fastOutput; |
- // can't currently produce a valid source map incrementally |
- this.incremental = incremental; |
- this.validate = validate; |
- this.checkedMode = checkedMode; |
- this.generateHumanReadableOutput = generateHumanReadableOutput; |
- } |
- |
- @Override |
- public boolean isOutOfDate(DartSource src, DartCompilerContext context) { |
- if (!incremental) { |
- return true; |
- } else { |
- return super.isOutOfDate(src, context); |
- } |
- } |
- |
- @Override |
- public void compileUnit(DartUnit unit, DartSource src, |
- DartCompilerContext context, CoreTypeProvider typeProvider) throws IOException { |
- if (!incremental) { |
- dartSrcToUnitMap.put(src.getName(), unit); |
- } else { |
- super.compileUnit(unit, src, context, typeProvider); |
- } |
- } |
- |
- private Map<String, CompilerInput> createClosureJsAst(Map<String,JsProgram> parts, Source source) { |
- String name = source.getName(); |
- Preconditions.checkState(name != null && !name.isEmpty(), "A source name is required"); |
- |
- Map<String, CompilerInput> translatedParts = new HashMap<String, CompilerInput>(); |
- for (Map.Entry<String,JsProgram> part : parts.entrySet()) { |
- String partName = part.getKey(); |
- String inputName = name + ':' + partName; |
- SourceAst sourceAst = new ClosureJsAst(part.getValue(), inputName, source, validate); |
- CompilerInput input = new CompilerInput(sourceAst, false); |
- translatedParts.put(part.getKey(), input); |
- } |
- return translatedParts; |
- } |
- |
- private class DepsWritingCallback implements DepsCallback { |
- private final DartCompilerContext context; |
- private final CoreTypeProvider typeProvider; |
- private final List<CompilerInput> inputs; |
- private final Map<String, Source> sourcesByName; |
- private final Map<DartUnit, Map<String, CompilerInput>> translatedUnits = Maps.newHashMap(); |
- |
- DepsWritingCallback( |
- DartCompilerContext context, |
- CoreTypeProvider typeProvider, |
- List<CompilerInput> inputs, |
- Map<String, Source> sourcesByName) { |
- this.context = context; |
- this.typeProvider = typeProvider; |
- this.inputs = inputs; |
- this.sourcesByName = sourcesByName; |
- } |
- |
- @Override |
- public void visitNative(LibraryUnit libUnit, LibraryNode node) |
- throws IOException { |
- String name = node.getText(); |
- DartSource nativeSrc = libUnit.getSource().getSourceFor(name); |
- StringWriter w = new StringWriter(); |
- Reader r = nativeSrc.getSourceReader(); |
- CharStreams.copy(r, w); |
- inputs.add(new CompilerInput(createSource(name, w), false)); |
- } |
- |
- @Override |
- public void visitPart(Part part) throws IOException { |
- DartSource src = part.unit.getSource(); |
- if (!incremental) { |
- Map<String, CompilerInput> translatedParts = translatedUnits.get(part.unit); |
- if (translatedParts == null) { |
- assert !sourcesByName.containsKey(src.getName()); |
- sourcesByName.put(src.getName(), src); |
- Preconditions.checkNotNull(part.unit, "src: " + src.getName()); |
- translatedParts = translateUnit(part.unit, src, context, typeProvider); |
- Preconditions.checkState(!translatedUnits.containsKey(part.unit)); |
- translatedUnits.put(part.unit, translatedParts); |
- } |
- inputs.add(translatedParts.get(part.part)); |
- return; |
- } |
- |
- Reader r = context.getArtifactReader(src, part.part, EXTENSION_JS); |
- if (r == null) { |
- return; |
- } |
- StringWriter w = new StringWriter(); |
- CharStreams.copy(r, w); |
- String inputName = src.getName() + ':' + part.part; |
- inputs.add(new CompilerInput(createSource(inputName, w), false)); |
- } |
- |
- private JSSourceFile createSource(String name, Writer w) { |
- return JSSourceFile.fromCode(name, w.toString()); |
- } |
- } |
- |
- private void packageAppOptimized(LibrarySource app, |
- Collection<LibraryUnit> libraries, |
- DartCompilerContext context, |
- CoreTypeProvider typeProvider) |
- throws IOException { |
- |
- List<CompilerInput> inputs = Lists.newLinkedList(); |
- Map<String, Source> sourcesByName = Maps.newHashMap(); |
- DepsWritingCallback callback = new DepsWritingCallback( |
- context, typeProvider, inputs, sourcesByName); |
- DependencyBuilder.build(context.getAppLibraryUnit(), callback); |
- |
- // Lastly, add the entry point. |
- inputs.add(getCompilerInputForEntry(context)); |
- |
- // Currently, there is only a single module, add all the sources to it. |
- JSModule mainModule = new JSModule("main"); |
- for (CompilerInput input : inputs) { |
- if (input != null) { |
- mainModule.add(input); |
- } |
- } |
- |
- Writer out = context.getArtifactWriter(app, "", getAppExtension()); |
- boolean failed = true; |
- try { |
- compileModule(app, context, mainModule, sourcesByName, out); |
- failed = false; |
- } finally { |
- Closeables.close(out, failed); |
- } |
- } |
- |
- private Map<String, CompilerInput> translateUnit( |
- DartUnit unit, DartSource src, DartCompilerContext context, CoreTypeProvider typeProvider) { |
- Map<String, JsProgram> parts = translateToJS(unit, context, typeProvider); |
- |
- // Translate the AST and cache it for later use. |
- return createClosureJsAst(parts, src); |
- } |
- |
- private CompilerInput getCompilerInputForEntry(DartCompilerContext context) |
- throws IOException { |
- StringWriter entry = new StringWriter(); |
- writeEntryPointCall(getMangledEntryPoint(context), entry); |
- return new CompilerInput( |
- JSSourceFile.fromCode("entry", entry.toString()), false); |
- } |
- |
- class MockSource implements Source { |
- private String sourceName; |
- |
- MockSource(String sourceName) { |
- this.sourceName = sourceName; |
- } |
- |
- @Override |
- public boolean exists() { |
- return true; |
- } |
- |
- @Override |
- public long getLastModified() { |
- return 0; |
- } |
- |
- @Override |
- public String getName() { |
- return sourceName; |
- } |
- |
- @Override |
- public Reader getSourceReader() { |
- return new StringReader(""); |
- } |
- |
- @Override |
- public URI getUri() { |
- return null; |
- } |
- } |
- |
- // Stub source info object for reporting errors coming from the Closure Compiler |
- static class JSErrorSourceInfo implements SourceInfo { |
- final JSError error; |
- final Source source; |
- |
- JSErrorSourceInfo(JSError error, Source source) { |
- this.error = error; |
- this.source = source; |
- } |
- |
- @Override |
- public Source getSource() { |
- return source; |
- } |
- |
- @Override |
- public int getSourceColumn() { |
- return error.getCharno(); |
- } |
- |
- @Override |
- public int getSourceLength() { |
- return -1; |
- } |
- |
- @Override |
- public int getSourceLine() { |
- return error.lineNumber; |
- } |
- |
- @Override |
- public int getSourceStart() { |
- return -1; |
- } |
- } |
- |
- private void compileModule( |
- LibrarySource src, DartCompilerContext context, |
- JSModule module, |
- Map<String, Source> sourcesByName, |
- Writer out) throws IOException { |
- // Turn off Closure Compiler logging |
- CompilerOptions options = getClosureCompilerOptions(context); |
- Logger.getLogger("com.google.javascript.jscomp").setLevel(Level.OFF); |
- |
- Compiler compiler = new Compiler(); |
- |
- List<JSSourceFile> externs = getDefaultExterns(); |
- List<JSModule> modules = Lists.newLinkedList(); |
- modules.add(module); |
- compiler.disableThreads(); |
- Result result = compiler.compileModules(externs, modules, options); |
- |
- if (processResults(src, context, compiler, result, module, out) != 0) { |
- for (JSError error : result.errors) { |
- // Use the real dart source object when we can. |
- Source source = sourcesByName.get(error.sourceName); |
- if (source == null) { |
- // This might be a compiler generate source, whatever it is |
- // report it. |
- source = new MockSource(error.sourceName); |
- } |
- System.err.println("error optimizing:" + error.toString()); |
- DartCompilationError dartError = |
- new DartCompilationError(new JSErrorSourceInfo(error, source), |
- ClosureJsErrorCode.INTERNAL_ERROR, |
- error.description); |
- context.onError(dartError); |
- } |
- } |
- |
- out.close(); |
- } |
- |
- /** |
- * Processes the results of the compile job, and returns an error code. |
- */ |
- private int processResults(LibrarySource src, DartCompilerContext context, Compiler compiler, |
- Result result, JSModule module, Writer out) |
- throws IOException { |
- if (result.success) { |
- // TODO(johnlenz): Append directly to the writer. |
- String output = compiler.toSource(module); |
- out.append(output); |
- out.append('\n'); |
- |
- if (generateSourceMap(context)) { |
- Writer srcMapOut = context.getArtifactWriter(src, "", getSourceMapExtension()); |
- boolean failed = true; |
- try { |
- compiler.getSourceMap().appendTo(srcMapOut, module.getName()); |
- failed = false; |
- } finally { |
- Closeables.close(srcMapOut, failed); |
- } |
- } |
- totalJsOutputCharCount = output.length(); |
- } |
- |
- // return 0 if no errors, the error count otherwise |
- return Math.min(result.errors.length, 0x7f); |
- } |
- |
- private CompilerOptions getClosureCompilerOptions(DartCompilerContext context) { |
- CompilerOptions options = new CompilerOptions(); |
- options.setCodingConvention(new ClosureJsCodingConvention()); |
- |
- // Set the optimization passes that we want. |
- if (fastOutput) { |
- options.smartNameRemoval = true; |
- options.collapseProperties = true; |
- options.removeUnusedPrototypeProperties = true; |
- options.removeUnusedVars = true; |
- options.setRenamingPolicy(VariableRenamingPolicy.ALL, PropertyRenamingPolicy.ALL_UNQUOTED); |
- // On by default |
- options.setReplaceIdGenerators(false); |
- } else { |
- CompilationLevel.ADVANCED_OPTIMIZATIONS.setOptionsForCompilationLevel(options); |
- options.setAssumeStrictThis(true); |
- // TODO(johnlenz): try out experimential inlining |
- // options.setAssumeClosuresOnlyCaptureReferences(true); |
- |
- // TODO(johnlenz): rewriteFunctionExpressions kills the Richards benchmark, |
- // it needs some better heuristics. |
- options.rewriteFunctionExpressions = false; |
- |
- // AliasKeywords has a runtime performance hit, disable it. |
- options.aliasKeywords = false; |
- |
- // slow for little value |
- options.setPropertyAffinity(false); |
- |
- // TODO(johnlenz): These passes use SimpleDefinitionFinder or equivalent, and operate |
- // based on property name, not object type. DisambiguateProperties helps but is not |
- // a complete fix even with complete type information. |
- // See http://code.google.com/p/closure-compiler/issues/detail?id=437. |
- // We need to develop a plan for how to deal with them. |
- |
- options.computeFunctionSideEffects = false; |
- options.devirtualizePrototypeMethods = true; |
- options.inlineGetters = true; |
- |
- // TODO(johnlenz): Some DOM definitions look like unused prototype property |
- // definitions because they are only referenced using dynamically generated |
- // names. |
- options.removeUnusedPrototypePropertiesInExterns = false; |
- } |
- |
- if (generateSourceMap(context)) { |
- options.sourceMapOutputPath = "placeholder"; // anything will do |
- options.sourceMapDetailLevel = DetailLevel.SYMBOLS; |
- options.sourceMapFormat = Format.V3; |
- } |
- |
- // Turn off the default checks. |
- |
- // Dart doesn't currently need the Closure Library checks |
- // or optimizations. |
- options.closurePass = false; |
- |
- // Disable type warnings as we don't provide any type information. |
- options.setInferTypes(false); |
- options.checkTypes = false; |
- options.setWarningLevel(DiagnosticGroups.CHECK_TYPES, CheckLevel.OFF); |
- |
- // Disable other checks, that don't make sense for generated code |
- options.setWarningLevel(DiagnosticGroups.GLOBAL_THIS, CheckLevel.OFF); |
- options.checkSuspiciousCode = false; |
- options.checkGlobalThisLevel = CheckLevel.OFF; |
- options.checkMissingReturn = CheckLevel.OFF; |
- options.checkGlobalNamesLevel = CheckLevel.OFF; |
- options.aggressiveVarCheck = CheckLevel.OFF; |
- options.setWarningLevel(DiagnosticGroups.DEPRECATED, CheckLevel.OFF); |
- |
- // Optionally turn on the checks that are useful to Dart |
- if (validate) { |
- options.checkSymbols = true; |
- options.setWarningLevel(DiagnosticGroups.ES5_STRICT, CheckLevel.ERROR); |
- // options.setAggressiveVarCheck(CheckLevel.ERROR); |
- } else { |
- // A lot of warnings don't make sense for generated code, or require type |
- // information. Turn them all off by default and make the ones we care |
- // about errors. |
- WarningLevel.QUIET.setOptionsForWarningLevel(options); |
- |
- options.checkSymbols = false; |
- options.setWarningLevel(DiagnosticGroups.ES5_STRICT, CheckLevel.OFF); |
- } |
- |
- // To ease debugging, try enabling these options: |
- if (generateHumanReadableOutput) { |
- options.prettyPrint = true; |
- options.generatePseudoNames = true; |
- options.printInputDelimiter = true; |
- options.inputDelimiter = "// Input %name%"; |
- } |
- // If those aren't enough, try disabling these: |
- // options.setRenamingPolicy(VariableRenamingPolicy.OFF, PropertyRenamingPolicy.OFF); |
- // options.coalesceVariableNames = false; |
- // options.setShadowVariables(false); |
- // options.inlineFunctions = false; |
- |
- /* |
- * NOTE: We turn this off because TypeErrors or anything that relies on a type name will fail |
- * due to the class renaming. |
- */ |
- if (checkedMode) { |
- options.setReplaceIdGenerators(false); |
- } |
- |
- return options; |
- } |
- |
- // The externs expected in externs.zip, in sorted order. |
- private static final List<String> DEFAULT_EXTERNS_NAMES = ImmutableList.of( |
- // JS externs |
- "es3.js", |
- "es5.js", |
- // "json.js", // TODO(johnlenz): add this. |
- |
- // Event APIs |
- "w3c_event.js", |
- "w3c_event3.js", |
- "gecko_event.js", |
- "ie_event.js", |
- "webkit_event.js", |
- |
- // DOM apis |
- "w3c_dom1.js", |
- "w3c_dom2.js", |
- "w3c_dom3.js", |
- "gecko_dom.js", |
- "ie_dom.js", |
- "webkit_dom.js", |
- |
- // CSS apis |
- "w3c_css.js", |
- "gecko_css.js", |
- "ie_css.js", |
- "webkit_css.js", |
- |
- // Top-level namespaces |
- "google.js", |
- |
- "deprecated.js", |
- "fileapi.js", |
- "flash.js", |
- "gears_symbols.js", |
- "gears_types.js", |
- "gecko_xml.js", |
- "html5.js", |
- "ie_vml.js", |
- "iphone.js", |
- "webstorage.js", |
- "w3c_anim_timing.js", |
- "w3c_css3d.js", |
- "w3c_elementtraversal.js", |
- "w3c_geolocation.js", |
- "w3c_indexeddb.js", |
- "w3c_navigation_timing.js", |
- "w3c_range.js", |
- "w3c_selectors.js", |
- "w3c_xml.js", |
- "window.js", |
- "webkit_notifications.js", |
- "webgl.js"); |
- |
- // Add a declarations for the V8 logging function. |
- private static final String UNIT_TEST_EXTERN_STUBS = "var write;"; |
- |
- private static final String CLOSURE_PRIMITIVES = "function JSCompiler_renameProperty() {};"; |
- |
- // TODO(johnlenz): include json.js in the default set of externs. |
- private static final String MISSING_EXTERNS = |
- "var JSON = {};\n" + |
- "/**\n" + |
- " * @param {string} jsonStr The string to parse.\n" + |
- " * @param {(function(string, *) : *)=} opt_reviver\n" + |
- " * @return {*} The JSON object.\n" + |
- " */\n" + |
- "JSON.parse = function(jsonStr, opt_reviver) {};\n" + |
- "\n" + |
- "/**\n" + |
- " * @param {*} jsonObj Input object.\n" + |
- " * @param {(Array.<string>|(function(string, *) : *)|null)=} opt_replacer\n" + |
- " * @param {(number|string)=} opt_space\n" + |
- " * @return {string} json string which represents jsonObj.\n" + |
- " */\n" + |
- "JSON.stringify = function(jsonObj, opt_replacer, opt_space) {};" + |
- "\n"; |
- |
- /** |
- * @return a mutable list |
- * @throws IOException |
- */ |
- private static List<JSSourceFile> getDefaultExterns() throws IOException { |
- Class<ClosureJsBackend> clazz = ClosureJsBackend.class; |
- InputStream input = clazz.getResourceAsStream( |
- "/com/google/javascript/jscomp/externs.zip"); |
- if (input == null) { |
- /* |
- * HACK - the open source version of the closure compiler maps the |
- * resource into a different location. |
- */ |
- input = clazz.getResourceAsStream("/externs.zip"); |
- } |
- ZipInputStream zip = new ZipInputStream(input); |
- Map<String, JSSourceFile> externsMap = Maps.newHashMap(); |
- for (ZipEntry entry = null; (entry = zip.getNextEntry()) != null; ) { |
- InputStream entryStream = new BufferedInputStream( |
- new LimitInputStream(zip, entry.getSize())); |
- externsMap.put(entry.getName(), |
- JSSourceFile.fromInputStream( |
- // Give the files an odd prefix, so that they do not conflict |
- // with the user's files. |
- "externs.zip//" + entry.getName(), |
- entryStream)); |
- } |
- |
- Preconditions.checkState( |
- externsMap.keySet().equals(Sets.newHashSet(DEFAULT_EXTERNS_NAMES)), |
- "Externs zip must match our hard-coded list of externs."); |
- |
- // Order matters, so the resources must be added to the result list |
- // in the expected order. |
- List<JSSourceFile> externs = Lists.newArrayList(); |
- for (String key : DEFAULT_EXTERNS_NAMES) { |
- externs.add(externsMap.get(key)); |
- } |
- |
- // Add methods used when running the unit tests. |
- externs.add(JSSourceFile.fromCode("missingExterns", MISSING_EXTERNS)); |
- |
- // Add methods used when running the unit tests. |
- externs.add(JSSourceFile.fromCode("unitTestStubs", UNIT_TEST_EXTERN_STUBS)); |
- |
- // Add methods used by Closure Compiler itself. |
- externs.add(JSSourceFile.fromCode("closureCompilerPrimitives", CLOSURE_PRIMITIVES)); |
- |
- return externs; |
- } |
- |
- @Override |
- public void packageApp(LibrarySource app, |
- Collection<LibraryUnit> libraries, |
- DartCompilerContext context, |
- CoreTypeProvider typeProvider) |
- throws IOException { |
- totalJsOutputCharCount = 0; |
- packageAppOptimized(app, libraries, context, typeProvider); |
- CompilerMetrics compilerMetrics = context.getCompilerMetrics(); |
- if (compilerMetrics != null) { |
- compilerMetrics.packagedJsApplication(totalJsOutputCharCount, -1); |
- } |
- } |
- |
- @Override |
- public String getAppExtension() { |
- return (incremental) ? EXTENSION_APP_JS : EXTENSION_OPT_JS; |
- } |
- |
- @Override |
- public String getSourceMapExtension() { |
- return (incremental) ? EXTENSION_APP_JS_SRC_MAP : EXTENSION_OPT_JS_SRC_MAP; |
- } |
- |
- @Override |
- protected boolean shouldOptimize() { |
- return (fastOutput) ? false : true; |
- } |
- |
- @Override |
- protected boolean generateClosureCompatibleCode() { |
- return true; |
- } |
-} |