Remove backends.
R=zundel@google.com,brianwilkerson@google.com BUG= TEST= Review URL: https://chromiumcodereview.appspot.com//9479013 git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart@4771 260f80e4-7a28-3924-810f-c04153c831b5
This commit is contained in:
@@ -40,7 +40,6 @@
|
||||
'../third_party/args4j/2.0.12/args4j-2.0.12.jar',
|
||||
'../third_party/guava/r09/guava-r09.jar',
|
||||
'../third_party/json/r2_20080312/json.jar',
|
||||
'../third_party/rhino/1_7R3/js.jar',
|
||||
'../third_party/hamcrest/v1_3/hamcrest-core-1.3.0RC2.jar',
|
||||
'../third_party/hamcrest/v1_3/hamcrest-generator-1.3.0RC2.jar',
|
||||
'../third_party/hamcrest/v1_3/hamcrest-integration-1.3.0RC2.jar',
|
||||
@@ -135,7 +134,6 @@
|
||||
'action': [
|
||||
'<(PRODUCT_DIR)/dartc', 'api.dart',
|
||||
'--fatal-warnings', '--fatal-type-errors',
|
||||
'--deprecated-generate-code',
|
||||
'-out', '<(INTERMEDIATE_DIR)/<(_target_name)/api',
|
||||
],
|
||||
},
|
||||
|
||||
@@ -1,54 +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;
|
||||
|
||||
import com.google.dart.compiler.ast.DartUnit;
|
||||
import com.google.dart.compiler.ast.LibraryUnit;
|
||||
import com.google.dart.compiler.resolver.CoreTypeProvider;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Collection;
|
||||
|
||||
/**
|
||||
* Interface for compiler backends.
|
||||
*/
|
||||
public interface Backend {
|
||||
|
||||
/**
|
||||
* Determines whether compilation artifacts are out of date with respect to
|
||||
* this source.
|
||||
*/
|
||||
boolean isOutOfDate(DartSource src, DartCompilerContext context);
|
||||
|
||||
/**
|
||||
* Compile the given compilation unit.
|
||||
* @param context The listener through which compilation errors are reported
|
||||
* (not <code>null</code>)
|
||||
*/
|
||||
void compileUnit(DartUnit unit, DartSource src,
|
||||
DartCompilerContext context,
|
||||
CoreTypeProvider typeProvider)
|
||||
throws IOException;
|
||||
|
||||
/**
|
||||
* Package the given application.
|
||||
*
|
||||
* @param app The application library whose entry-point should be called
|
||||
* @param libraries The transitive set of libraries contained in this
|
||||
* application
|
||||
* @param context The listener through which compilation errors are reported
|
||||
* (not <code>null</code>)
|
||||
*/
|
||||
void packageApp(LibrarySource app,
|
||||
Collection<LibraryUnit> libraries,
|
||||
DartCompilerContext context,
|
||||
CoreTypeProvider typeProvider)
|
||||
throws IOException;
|
||||
|
||||
/**
|
||||
* The application extension for the backend.
|
||||
*/
|
||||
String getAppExtension();
|
||||
}
|
||||
@@ -30,10 +30,6 @@ public class CommandLineOptions {
|
||||
usage = "Batch mode (for unit testing)")
|
||||
private boolean batch = false;
|
||||
|
||||
@Option(name = "--deprecated-generate-code",
|
||||
usage = "Use deprecated code generation.\n Will be removed 1 March 2012.")
|
||||
private boolean deprecatedGenerateCode = false;
|
||||
|
||||
@Option(name = "--expose_core_impl", usage = "Automatic import of dart:coreimpl library")
|
||||
private boolean exposeCoreImpl = false;
|
||||
|
||||
@@ -49,23 +45,10 @@ public class CommandLineOptions {
|
||||
usage = "Turn off type optimizations\n (for debugging)")
|
||||
private boolean disableTypeOptimizations = false;
|
||||
|
||||
@Option(name = "--generate_source_maps",
|
||||
usage = "Generate source maps")
|
||||
private boolean generateSourceMaps = false;
|
||||
|
||||
@Option(name = "--dump_ast_format",
|
||||
usage = "Dump parse tree. Supported formats include console, text or dot")
|
||||
private String dumpAST = "";
|
||||
|
||||
@Option(name = "--coverage_type",
|
||||
usage = "Add instrumentation probes for collecting coverage. " +
|
||||
"Supported types include function, statement, branch and all")
|
||||
private String coverage = "";
|
||||
|
||||
@Option(name = "--human-readable-output",
|
||||
usage = "Write human readable javascript")
|
||||
private boolean generateHumanReadableOutput = false;
|
||||
|
||||
@Option(name = "--ignore-unrecognized-flags",
|
||||
usage = "Ignore unrecognized command line flags")
|
||||
private boolean ignoreUnrecognizedFlags = false;
|
||||
@@ -96,10 +79,6 @@ public class CommandLineOptions {
|
||||
usage = "Enable incremental compilation")
|
||||
private boolean incremental = false;
|
||||
|
||||
@Option(name = "--out",
|
||||
usage = "Write generated JavaScript to a file")
|
||||
private File outputFilename = null;
|
||||
|
||||
// TODO(zundel): -out is for backward compatibility until scripts are updated
|
||||
@Option(name = "--work", aliases = { "-out" },
|
||||
usage = "Directory to receive compiler output\n for future incremental builds")
|
||||
@@ -128,13 +107,6 @@ public class CommandLineOptions {
|
||||
@Argument
|
||||
private final List<String> sourceFiles = new ArrayList<String>();
|
||||
|
||||
/**
|
||||
* Returns whether the only analysis should be done, without code generation.
|
||||
*/
|
||||
public boolean checkOnly() {
|
||||
return !deprecatedGenerateCode;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return <code>true</code> to automatically import dart:coreimpl
|
||||
*/
|
||||
@@ -182,29 +154,10 @@ public class CommandLineOptions {
|
||||
return disableTypeOptimizations;
|
||||
}
|
||||
|
||||
public boolean generateSourceMaps() {
|
||||
return generateSourceMaps;
|
||||
}
|
||||
|
||||
public boolean generateHumanReadableOutput() {
|
||||
return generateHumanReadableOutput;
|
||||
}
|
||||
|
||||
public String dumpAST(){
|
||||
return dumpAST;
|
||||
}
|
||||
|
||||
public String getCoverageType(){
|
||||
return coverage;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the path to receive compiler output.
|
||||
*/
|
||||
public File getOutputFilename() {
|
||||
return outputFilename;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns <code>true</code> if the compiler should print it's help message.
|
||||
*/
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file
|
||||
// Copyright (c) 2012, 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.
|
||||
|
||||
@@ -11,8 +11,7 @@ import java.io.File;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* A configuration for the Dart compiler specifying which phases
|
||||
* and backends will be executed.
|
||||
* A configuration for the Dart compiler specifying which phases will be executed.
|
||||
*/
|
||||
public interface CompilerConfiguration {
|
||||
|
||||
@@ -23,8 +22,6 @@ public interface CompilerConfiguration {
|
||||
|
||||
List<DartCompilationPhase> getPhases();
|
||||
|
||||
List<Backend> getBackends();
|
||||
|
||||
/**
|
||||
* Indicates whether developer-mode runtime checks are needed.
|
||||
* @return true if developer-mode checks should be inserted, false if not
|
||||
@@ -61,26 +58,11 @@ public interface CompilerConfiguration {
|
||||
*/
|
||||
boolean incremental();
|
||||
|
||||
/**
|
||||
* The first backend that runs outputs to this filename if set.
|
||||
*/
|
||||
File getOutputFilename();
|
||||
|
||||
/**
|
||||
* The work directory where incremental build output is stored between invocations.
|
||||
*/
|
||||
File getOutputDirectory();
|
||||
|
||||
/**
|
||||
* Returns <code>true</code> if the compiler should not produce output.
|
||||
*/
|
||||
boolean checkOnly();
|
||||
|
||||
/**
|
||||
* Returns <code>true</code> if the compiler should expect an entry point to be defined.
|
||||
*/
|
||||
boolean expectEntryPoint();
|
||||
|
||||
/**
|
||||
* Returns the error formatting the compiler should print with
|
||||
*/
|
||||
|
||||
@@ -9,11 +9,9 @@ import com.google.common.collect.Sets;
|
||||
import com.google.common.collect.Sets.SetView;
|
||||
import com.google.common.io.CharStreams;
|
||||
import com.google.common.io.Closeables;
|
||||
import com.google.common.io.Files;
|
||||
import com.google.dart.compiler.CommandLineOptions.CompilerOptions;
|
||||
import com.google.dart.compiler.LibraryDeps.Dependency;
|
||||
import com.google.dart.compiler.UnitTestBatchRunner.Invocation;
|
||||
import com.google.dart.compiler.ast.CoverageInstrumenter;
|
||||
import com.google.dart.compiler.ast.DartDirective;
|
||||
import com.google.dart.compiler.ast.DartLibraryDirective;
|
||||
import com.google.dart.compiler.ast.DartNode;
|
||||
@@ -30,7 +28,6 @@ import com.google.dart.compiler.metrics.JvmMetrics;
|
||||
import com.google.dart.compiler.metrics.Tracer;
|
||||
import com.google.dart.compiler.metrics.Tracer.TraceEvent;
|
||||
import com.google.dart.compiler.parser.DartParser;
|
||||
import com.google.dart.compiler.parser.DartScanner.Location;
|
||||
import com.google.dart.compiler.parser.DartScannerParserContext;
|
||||
import com.google.dart.compiler.resolver.CompileTimeConstantResolver;
|
||||
import com.google.dart.compiler.resolver.CoreTypeProvider;
|
||||
@@ -55,7 +52,6 @@ import java.io.PrintStream;
|
||||
import java.io.Reader;
|
||||
import java.io.Writer;
|
||||
import java.net.URI;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
@@ -126,13 +122,10 @@ public class DartCompiler {
|
||||
private final DartCompilerMainContext context;
|
||||
private final CompilerConfiguration config;
|
||||
private final Map<URI, LibraryUnit> libraries = new LinkedHashMap<URI, LibraryUnit>();
|
||||
private boolean packageApp = false;
|
||||
private final boolean checkOnly;
|
||||
private CoreTypeProvider typeProvider;
|
||||
private final boolean incremental;
|
||||
private final boolean usePrecompiledDartLibs;
|
||||
private final List<DartCompilationPhase> phases;
|
||||
private final List<Backend> backends;
|
||||
private final LibrarySource coreLibrarySource;
|
||||
|
||||
private Compiler(LibrarySource app, List<LibrarySource> embedded, CompilerConfiguration config,
|
||||
@@ -140,9 +133,7 @@ public class DartCompiler {
|
||||
this.app = app;
|
||||
this.config = config;
|
||||
this.phases = config.getPhases();
|
||||
this.backends = config.getBackends();
|
||||
this.context = context;
|
||||
checkOnly = config.checkOnly();
|
||||
for (LibrarySource library : embedded) {
|
||||
if (SystemLibraryManager.isDartSpec(library.getName())) {
|
||||
embeddedLibraries.add(context.getSystemLibraryFor(library.getName()));
|
||||
@@ -164,16 +155,7 @@ public class DartCompiler {
|
||||
if (context.getErrorCount() > 0) {
|
||||
return;
|
||||
}
|
||||
if (!context.getFilesHaveChanged()) {
|
||||
for (Backend be : backends) {
|
||||
if(context.isOutOfDate(app, app, be.getAppExtension())) {
|
||||
packageApp = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
compileLibraries();
|
||||
packageApp();
|
||||
} catch (IOException e) {
|
||||
context.onError(new DartCompilationError(app, DartCompilerErrorCode.IO, e.getMessage()));
|
||||
} finally {
|
||||
@@ -459,33 +441,16 @@ public class DartCompiler {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (checkOnly) {
|
||||
TraceEvent backendEvent =
|
||||
Tracer.canTrace() ? Tracer.start(DartEventType.TIMESTAMP_OUTOFDATE,
|
||||
"src", dartSrc.getName()) : null;
|
||||
try {
|
||||
if (context.isOutOfDate(dartSrc, dartSrc, EXTENSION_TIMESTAMP)) {
|
||||
return true;
|
||||
}
|
||||
} finally {
|
||||
Tracer.end(backendEvent);
|
||||
}
|
||||
} else {
|
||||
// TODO(zundel): remove this case when code generation goes away
|
||||
for (Backend backend : backends) {
|
||||
TraceEvent backendEvent =
|
||||
Tracer.canTrace() ? Tracer.start(DartEventType.BACKEND_OUTOFDATE, "be", backend
|
||||
.getClass().getCanonicalName(), "src", dartSrc.getName()) : null;
|
||||
try {
|
||||
if (backend.isOutOfDate(dartSrc, context)) {
|
||||
return true;
|
||||
}
|
||||
} finally {
|
||||
Tracer.end(backendEvent);
|
||||
}
|
||||
}
|
||||
TraceEvent timestampEvent =
|
||||
Tracer.canTrace() ? Tracer.start(
|
||||
DartEventType.TIMESTAMP_OUTOFDATE,
|
||||
"src",
|
||||
dartSrc.getName()) : null;
|
||||
try {
|
||||
return context.isOutOfDate(dartSrc, dartSrc, EXTENSION_TIMESTAMP);
|
||||
} finally {
|
||||
Tracer.end(timestampEvent);
|
||||
}
|
||||
return false;
|
||||
} finally {
|
||||
Tracer.end(logEvent);
|
||||
}
|
||||
@@ -749,10 +714,6 @@ public class DartCompiler {
|
||||
// Dump the compiler parse tree if dump format is set in arguments
|
||||
BaseASTWriter astWriter = ASTWriterFactory.create(config);
|
||||
|
||||
// Coverage instrumenter
|
||||
CoverageInstrumenter coverageInstrumenter = CoverageInstrumenter.createInstance(config);
|
||||
coverageInstrumenter.process(libraries);
|
||||
|
||||
// The two following for loops can be parallelized.
|
||||
for (LibraryUnit lib : libraries.values()) {
|
||||
boolean persist = false;
|
||||
@@ -782,7 +743,6 @@ public class DartCompiler {
|
||||
Tracer.end(phaseEvent);
|
||||
}
|
||||
if (context.getErrorCount() > 0) {
|
||||
packageApp = false;
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -790,30 +750,11 @@ public class DartCompiler {
|
||||
// To help support the IDE, notify the listener that this unit is compiled.
|
||||
context.unitCompiled(unit);
|
||||
|
||||
if (!checkOnly) {
|
||||
// Run the unit through all the backends. This loop can also be
|
||||
// parallelized.
|
||||
for (Backend be : config.getBackends()) {
|
||||
TraceEvent backendEvent =
|
||||
Tracer.canTrace() ? Tracer.start(DartEventType.BACKEND_COMPILE,
|
||||
"be", be.getClass().getSimpleName(),
|
||||
"lib", lib.getName(),
|
||||
"unit", unit.getSourceName()) : null;
|
||||
try {
|
||||
be.compileUnit(unit, unit.getSource(), context, typeProvider);
|
||||
} finally {
|
||||
Tracer.end(backendEvent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Update deps.
|
||||
lib.getDeps(context).update(context, unit);
|
||||
|
||||
// We compiled something, so remember that this means we need to
|
||||
// persist the deps and package the app.
|
||||
// We analyzed something, so we need to persist the deps.
|
||||
persist = true;
|
||||
packageApp = true;
|
||||
}
|
||||
|
||||
// Persist the DEPS file.
|
||||
@@ -837,48 +778,6 @@ public class DartCompiler {
|
||||
writer.close();
|
||||
}
|
||||
|
||||
private void packageApp() throws IOException {
|
||||
TraceEvent logEvent = Tracer.canTrace() ? Tracer.start(DartEventType.PACKAGE_APP) : null;
|
||||
|
||||
CompilerMetrics compilerMetrics = context.getCompilerMetrics();
|
||||
if (compilerMetrics != null) {
|
||||
compilerMetrics.startPackageAppTime();
|
||||
}
|
||||
|
||||
try {
|
||||
// Package output for each backend.
|
||||
if (packageApp) {
|
||||
// When there's no entry-point in the application unit,
|
||||
// don't attempt to package it. This can happen when
|
||||
// compileUnit() is called on a library.
|
||||
if (context.getApplicationUnit().getEntryNode() == null) {
|
||||
if (config.expectEntryPoint()) {
|
||||
context.onError(new DartCompilationError(
|
||||
context.getApplicationUnit().getSource(), Location.NONE,
|
||||
DartCompilerErrorCode.NO_ENTRY_POINT));
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
for (Backend be : backends) {
|
||||
TraceEvent backendEvent =
|
||||
Tracer.canTrace() ? Tracer.start(DartEventType.BACKEND_PACKAGE_APP, "be", be
|
||||
.getClass().getSimpleName()) : null;
|
||||
try {
|
||||
be.packageApp(app, libraries.values(), context, typeProvider);
|
||||
} finally {
|
||||
Tracer.end(backendEvent);
|
||||
}
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
if (compilerMetrics != null) {
|
||||
compilerMetrics.endPackageAppTime();
|
||||
}
|
||||
Tracer.end(logEvent);
|
||||
}
|
||||
}
|
||||
|
||||
DartUnit parse(DartSource dartSrc, Set<String> libraryPrefixes, boolean diet) throws IOException {
|
||||
TraceEvent parseEvent =
|
||||
Tracer.canTrace() ? Tracer.start(DartEventType.PARSE, "src", dartSrc.getName()) : null;
|
||||
@@ -1092,12 +991,6 @@ public class DartCompiler {
|
||||
TraceEvent logEvent =
|
||||
Tracer.canTrace() ? Tracer.start(DartEventType.COMPILE_APP, "src", sourceFile.toString())
|
||||
: null;
|
||||
File outFile = config.getOutputFilename();
|
||||
if (outFile != null && config.getBackends().size() > 1) {
|
||||
// More than one backend is ambiguous.
|
||||
throw new IllegalArgumentException("Output filename "
|
||||
+ outFile + " specified. Only valid with a single backend.");
|
||||
}
|
||||
try {
|
||||
File outputDirectory = config.getOutputDirectory();
|
||||
DefaultDartArtifactProvider provider = new DefaultDartArtifactProvider(outputDirectory);
|
||||
@@ -1106,53 +999,7 @@ public class DartCompiler {
|
||||
|
||||
// Compile the Dart application and its dependencies.
|
||||
LibrarySource lib = new UrlLibrarySource(sourceFile);
|
||||
config = new DelegatingCompilerConfiguration(config) {
|
||||
@Override
|
||||
public boolean expectEntryPoint() {
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
String errorString = compileLib(lib, config, provider, listener);
|
||||
|
||||
// Write out a copy of the generated JS if specified by the user
|
||||
if (errorString == null && outFile != null && !config.getBackends().isEmpty()) {
|
||||
File dir = outFile.getParentFile();
|
||||
if (dir != null) {
|
||||
if (dir != null && !dir.exists()) {
|
||||
throw new IOException("Cannot create: " + outFile.getName()
|
||||
+ ". " + dir + " does not exist");
|
||||
}
|
||||
if (!dir.canWrite()) {
|
||||
throw new IOException("Cannot write " + outFile.getName() + " to "
|
||||
+ dir + ": Permission denied.");
|
||||
}
|
||||
} else {
|
||||
dir = new File (".");
|
||||
if (!dir.canWrite()) {
|
||||
throw new IOException("Cannot write " + outFile.getName() + " to "
|
||||
+ dir + ": Permission denied.");
|
||||
}
|
||||
}
|
||||
|
||||
if (!config.getCompilerOptions().checkOnly()) {
|
||||
Reader r = null;
|
||||
try {
|
||||
// HACK: there can be more than one backend. Since there isn't
|
||||
// an obvious way to tell which one the user meant, for now
|
||||
// just restrict the option to save the output if more than
|
||||
// one is active.
|
||||
r = provider.getArtifactReader(lib, "",
|
||||
config.getBackends().get(0).getAppExtension());
|
||||
String js = CharStreams.toString(r);
|
||||
if (r != null) {
|
||||
Files.write(js, outFile, Charset.defaultCharset());
|
||||
}
|
||||
} finally {
|
||||
Closeables.close(r, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
return errorString;
|
||||
} finally {
|
||||
Tracer.end(logEvent);
|
||||
@@ -1163,12 +1010,8 @@ public class DartCompiler {
|
||||
* Compiles the given library, translating all its source files, and those
|
||||
* of its imported libraries, transitively.
|
||||
*
|
||||
* If the specified library contains an entry-point method, then the application will be packaged
|
||||
* by each backend. Otherwise, only library artifacts will be generated.
|
||||
*
|
||||
* @param lib The library to be compiled (not <code>null</code>)
|
||||
* @param config The compiler configuration specifying the compilation phases
|
||||
* and backends
|
||||
* @param provider A mechanism for specifying where code should be generated
|
||||
* @param listener An object notified when compilation errors occur
|
||||
*/
|
||||
@@ -1212,7 +1055,8 @@ public class DartCompiler {
|
||||
if (!context.getFilesHaveChanged()) {
|
||||
return null;
|
||||
}
|
||||
if (config.checkOnly()) {
|
||||
// Write checking log.
|
||||
{
|
||||
Writer writer = provider.getArtifactWriter(lib, "", EXTENSION_LOG);
|
||||
boolean threw = true;
|
||||
try {
|
||||
@@ -1235,9 +1079,8 @@ public class DartCompiler {
|
||||
* instead of parsing the associated source from storage. Intended for
|
||||
* IDE use when modified buffers must be analyzed. AST nodes in the map may be
|
||||
* ignored if not referenced by {@code lib}. (May be null.)
|
||||
* @param config The compiler configuration (phases and backends
|
||||
* will not be used), but resolution and type-analysis will be
|
||||
* invoked
|
||||
* @param config The compiler configuration (phases will not be used), but resolution and
|
||||
* type-analysis will be invoked
|
||||
* @param provider A mechanism for specifying where code should be generated
|
||||
* @param listener An object notified when compilation errors occur
|
||||
* @throws NullPointerException if any of the arguments except {@code parsedUnits}
|
||||
|
||||
@@ -13,11 +13,10 @@ import java.io.Writer;
|
||||
import java.net.URI;
|
||||
|
||||
/**
|
||||
* An interface used internally by the {@link DartCompiler} and implementers of
|
||||
* {@link Backend} for determining where an artifact should be generated and
|
||||
* providing feedback during the compilation process. This is an internal
|
||||
* compiler construct and as such should not be instantiated or implemented by
|
||||
* those outside the compiler itself.
|
||||
* An interface used internally by the {@link DartCompiler} for determining where an artifact should
|
||||
* be generated and providing feedback during the compilation process. This is an internal compiler
|
||||
* construct and as such should not be instantiated or implemented by those outside the compiler
|
||||
* itself.
|
||||
*/
|
||||
public interface DartCompilerContext {
|
||||
|
||||
|
||||
@@ -5,28 +5,22 @@
|
||||
package com.google.dart.compiler;
|
||||
|
||||
import com.google.dart.compiler.CommandLineOptions.CompilerOptions;
|
||||
import com.google.dart.compiler.backend.js.JavascriptBackend;
|
||||
import com.google.dart.compiler.metrics.CompilerMetrics;
|
||||
import com.google.dart.compiler.resolver.CompileTimeConstantAnalyzer;
|
||||
import com.google.dart.compiler.resolver.Resolver;
|
||||
import com.google.dart.compiler.type.TypeAnalyzer;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* A configuration for the Dart compiler specifying which phases
|
||||
* and backends will be executed.
|
||||
* A configuration for the Dart compiler specifying which phases will be executed.
|
||||
*/
|
||||
public class DefaultCompilerConfiguration implements CompilerConfiguration {
|
||||
|
||||
private List<Backend> backends;
|
||||
|
||||
private final CompilerOptions compilerOptions;
|
||||
|
||||
private final CompilerMetrics compilerMetrics;
|
||||
@@ -34,66 +28,26 @@ public class DefaultCompilerConfiguration implements CompilerConfiguration {
|
||||
private final SystemLibraryManager systemLibraryManager;
|
||||
|
||||
/**
|
||||
* A default configuration with the {@link JavascriptBackend}
|
||||
* A default configuration.
|
||||
*/
|
||||
public DefaultCompilerConfiguration() {
|
||||
this(new JavascriptBackend());
|
||||
this(new CompilerOptions());
|
||||
}
|
||||
|
||||
/**
|
||||
* A new instance with the specified {@link CompilerOptions}
|
||||
* @throws FileNotFoundException
|
||||
* A new instance with the specified {@link CompilerOptions}.
|
||||
*/
|
||||
public DefaultCompilerConfiguration(CompilerOptions compilerOptions) {
|
||||
this (new JavascriptBackend(), compilerOptions);
|
||||
this(compilerOptions, new SystemLibraryManager());
|
||||
}
|
||||
|
||||
/**
|
||||
* A new instance with the specified {@link Backend}
|
||||
*/
|
||||
public DefaultCompilerConfiguration(Backend backend) {
|
||||
this(new CompilerOptions(), backend);
|
||||
}
|
||||
|
||||
/**
|
||||
* A new instance with the specified {@link Backend} and {@link CompilerOptions}
|
||||
*/
|
||||
public DefaultCompilerConfiguration(Backend backend, CompilerOptions compilerOptions) {
|
||||
this(compilerOptions, backend);
|
||||
}
|
||||
|
||||
/**
|
||||
* A new instance with the specified list of {@link Backend}
|
||||
*/
|
||||
public DefaultCompilerConfiguration(Backend... backends) {
|
||||
this(new CompilerOptions(), backends);
|
||||
}
|
||||
|
||||
/**
|
||||
* A new instance with the specified list of {@link Backend}
|
||||
*/
|
||||
public DefaultCompilerConfiguration(CompilerOptions compilerOptions, Backend... backends) {
|
||||
this(compilerOptions, new SystemLibraryManager(), backends);
|
||||
}
|
||||
|
||||
/**
|
||||
* A new instance with the specified options, system library manager, and default {@link Backend
|
||||
* backends}.
|
||||
* A new instance with the specified options and system library manager.
|
||||
*/
|
||||
public DefaultCompilerConfiguration(CompilerOptions compilerOptions,
|
||||
SystemLibraryManager libraryManager) throws FileNotFoundException {
|
||||
this(compilerOptions, libraryManager, new JavascriptBackend());
|
||||
}
|
||||
|
||||
/**
|
||||
* A new instance with the specified options, system library manager, and list of {@link Backend
|
||||
* backends}.
|
||||
*/
|
||||
public DefaultCompilerConfiguration(CompilerOptions compilerOptions, SystemLibraryManager libraryManager, Backend... backends) {
|
||||
this.backends = Arrays.asList(backends);
|
||||
SystemLibraryManager libraryManager) {
|
||||
this.compilerOptions = compilerOptions;
|
||||
this.compilerMetrics = compilerOptions.showMetrics() ?
|
||||
new CompilerMetrics() : null;
|
||||
this.compilerMetrics = compilerOptions.showMetrics() ? new CompilerMetrics() : null;
|
||||
this.systemLibraryManager = libraryManager;
|
||||
}
|
||||
|
||||
@@ -106,11 +60,6 @@ public class DefaultCompilerConfiguration implements CompilerConfiguration {
|
||||
return phases;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Backend> getBackends() {
|
||||
return backends;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean developerModeChecks() {
|
||||
return compilerOptions.developerModeChecks();
|
||||
@@ -146,26 +95,11 @@ public class DefaultCompilerConfiguration implements CompilerConfiguration {
|
||||
return compilerOptions.buildIncrementally();
|
||||
}
|
||||
|
||||
@Override
|
||||
public File getOutputFilename() {
|
||||
return compilerOptions.getOutputFilename();
|
||||
}
|
||||
|
||||
@Override
|
||||
public File getOutputDirectory() {
|
||||
return compilerOptions.getWorkDirectory();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checkOnly() {
|
||||
return compilerOptions.checkOnly();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean expectEntryPoint() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public LibrarySource getSystemLibraryFor(String importSpec) {
|
||||
URI systemUri;
|
||||
|
||||
@@ -33,11 +33,6 @@ public class DelegatingCompilerConfiguration implements CompilerConfiguration {
|
||||
return delegate.getPhases();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Backend> getBackends() {
|
||||
return delegate.getBackends();
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompilerMetrics getCompilerMetrics() {
|
||||
return delegate.getCompilerMetrics();
|
||||
@@ -68,26 +63,11 @@ public class DelegatingCompilerConfiguration implements CompilerConfiguration {
|
||||
return delegate.incremental();
|
||||
}
|
||||
|
||||
@Override
|
||||
public File getOutputFilename() {
|
||||
return delegate.getOutputFilename();
|
||||
}
|
||||
|
||||
@Override
|
||||
public File getOutputDirectory() {
|
||||
return delegate.getOutputDirectory();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checkOnly() {
|
||||
return delegate.checkOnly();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean expectEntryPoint() {
|
||||
return delegate.expectEntryPoint();
|
||||
}
|
||||
|
||||
@Override
|
||||
public LibrarySource getSystemLibraryFor(String importSpec) {
|
||||
return delegate.getSystemLibraryFor(importSpec);
|
||||
|
||||
@@ -8,12 +8,11 @@ import java.io.IOException;
|
||||
|
||||
/**
|
||||
* Abstract interface to library source.
|
||||
*
|
||||
* TODO(jgw): Consider requiring implementors to intern LibrarySource instances
|
||||
* so that users can depend upon reference equality to avoid cycles (or require
|
||||
* them to use .equals()). As it is, there are two pieces of code in
|
||||
* DartCompiler and one in JavascriptBackend that do this manually, and it's a
|
||||
* little tricky.
|
||||
*
|
||||
* TODO(jgw): Consider requiring implementors to intern LibrarySource instances so that users can
|
||||
* depend upon reference equality to avoid cycles (or require them to use .equals()). As it is,
|
||||
* there are two pieces of code in {@link DartCompiler} that do this manually, and it's a little
|
||||
* tricky.
|
||||
*/
|
||||
public interface LibrarySource extends Source {
|
||||
|
||||
|
||||
@@ -8,5 +8,5 @@ package com.google.dart.compiler;
|
||||
* Subsystem of compiler.
|
||||
*/
|
||||
public enum SubSystem {
|
||||
COMPILER, PARSER, STATIC_TYPE, RESOLVER, NORMALIZE, CODE_GEN, JS_BACKEND, CLOSURE_BACKEND
|
||||
COMPILER, PARSER, STATIC_TYPE, RESOLVER
|
||||
}
|
||||
@@ -1,807 +0,0 @@
|
||||
package com.google.dart.compiler.ast;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.net.URI;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import com.google.dart.compiler.common.SourceInfo;
|
||||
import com.google.dart.compiler.CommandLineOptions.CompilerOptions;
|
||||
import com.google.dart.compiler.CompilerConfiguration;
|
||||
|
||||
/**
|
||||
* CoverageInstrumenter contains specialized instrumenters to perform specific
|
||||
* instrumentation for obtaining coverage of different program entities
|
||||
*/
|
||||
public class CoverageInstrumenter {
|
||||
private List<BaseInstrumenter> instrumenters;
|
||||
private static final String[] ignoredLibs = { "corelib", "corelib_impl",
|
||||
"dom", "html", "htmlimpl", "base", "touch", "view", "utilslib",
|
||||
"observable", "layout.dart", "unittest", "dartest" };
|
||||
|
||||
private static final String functionCoverage = "function";
|
||||
private static final String statementCoverage = "statement";
|
||||
private static final String branchCoverage = "branch";
|
||||
private static final String allCoverage = "all";
|
||||
|
||||
// Only createInstance should be used to instantiate this class
|
||||
private CoverageInstrumenter() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Method to instantiate a CoverageInstrumenter containing multiple
|
||||
* instrumenters created based on command line flags.
|
||||
*
|
||||
* @param config
|
||||
* @return instance of this class
|
||||
*/
|
||||
public static CoverageInstrumenter createInstance(
|
||||
CompilerConfiguration config) {
|
||||
|
||||
CompilerOptions compilerOptions = config.getCompilerOptions();
|
||||
String coverageTypes = compilerOptions.getCoverageType();
|
||||
|
||||
CoverageInstrumenter instr = new CoverageInstrumenter();
|
||||
if (!"".equals(coverageTypes)) {
|
||||
String outDir = compilerOptions.getWorkDirectory()
|
||||
.getAbsolutePath();
|
||||
instr.createInstrumenters(coverageTypes, outDir);
|
||||
}
|
||||
return instr;
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs instrumentation of all classes
|
||||
*
|
||||
* @param libraries
|
||||
*/
|
||||
public void process(Map<URI, LibraryUnit> libraries) {
|
||||
|
||||
if (instrumenters == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Instrument all dart units
|
||||
for (LibraryUnit lib : libraries.values()) {
|
||||
for (DartUnit unit : lib.getUnits()) {
|
||||
exec(unit);
|
||||
}
|
||||
}
|
||||
|
||||
// Populate totals and initialize coverage
|
||||
for (LibraryUnit lib : libraries.values()) {
|
||||
for (DartUnit unit : lib.getUnits()) {
|
||||
init(unit);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs all instrumenters on a particular Dart unit
|
||||
*
|
||||
* @param unit
|
||||
*/
|
||||
private void exec(DartUnit unit) {
|
||||
if (isIgnored(unit)) {
|
||||
return;
|
||||
}
|
||||
System.out.println("Instrumenting " + unit.getSourceName() + ", lib:"
|
||||
+ unit.getLibrary().getName());
|
||||
for (BaseInstrumenter instrumenter : instrumenters) {
|
||||
instrumenter.accept(unit);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Ignores a Dart unit based on the library it belongs to
|
||||
*
|
||||
* @param unit
|
||||
* @return true is the unit should be ignored
|
||||
*/
|
||||
private boolean isIgnored(DartUnit unit) {
|
||||
LibraryUnit lu = unit.getLibrary();
|
||||
if (lu != null) {
|
||||
String libName = lu.getName();
|
||||
for (String ignoredLib : ignoredLibs) {
|
||||
if (ignoredLib.equals(libName)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs the TotalSettingInstrumenter on the supplied Dart unit, which adds
|
||||
* totals of all coverable program entities
|
||||
*
|
||||
* @param unit
|
||||
*/
|
||||
private void init(DartUnit unit) {
|
||||
if (isIgnored(unit)) {
|
||||
return;
|
||||
}
|
||||
new TotalSettingInstrumenter().accept(unit);
|
||||
}
|
||||
|
||||
/**
|
||||
* Factory method to create instrumenters based on supplied command line
|
||||
* configuration
|
||||
*
|
||||
* @param coverageTypes
|
||||
* @param outDir
|
||||
*/
|
||||
private void createInstrumenters(String coverageTypes, String outDir) {
|
||||
BaseInstrumenter.setOutDir(outDir);
|
||||
instrumenters = new ArrayList<BaseInstrumenter>();
|
||||
if (coverageTypes.length() > 0) {
|
||||
for (String covType : coverageTypes.split(",")) {
|
||||
if (allCoverage.equals(covType)) {
|
||||
instrumenters.add(new StatementInstrumenter());
|
||||
instrumenters.add(new FunctionInstrumenter());
|
||||
instrumenters.add(new BranchInstrumenter());
|
||||
}
|
||||
if (statementCoverage.equals(covType)) {
|
||||
instrumenters.add(new StatementInstrumenter());
|
||||
}
|
||||
if (functionCoverage.equals(covType)) {
|
||||
instrumenters.add(new FunctionInstrumenter());
|
||||
}
|
||||
if (branchCoverage.equals(covType)) {
|
||||
instrumenters.add(new BranchInstrumenter());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The Base Instrumenter class that provides some common functionalities to
|
||||
* all Instrumenters
|
||||
*/
|
||||
private static class BaseInstrumenter extends DartModVisitor {
|
||||
// TODO: Switch to DartNodeTraverser
|
||||
private static String outputDir;
|
||||
protected DartUnit currentUnit;
|
||||
protected static Set<String> unitsVisited = new HashSet<String>();
|
||||
protected static Map<String, Integer> numFunctionsMap =
|
||||
new HashMap<String, Integer>();
|
||||
protected static Map<String, Integer> numStatementsMap =
|
||||
new HashMap<String, Integer>();
|
||||
protected static Map<String, Integer> numBranchesMap =
|
||||
new HashMap<String, Integer>();
|
||||
|
||||
public static void setOutDir(String outDir) {
|
||||
outputDir = outDir;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean visit(DartUnit x, DartContext ctx) {
|
||||
currentUnit = x;
|
||||
unitsVisited.add(x.getSourceName());
|
||||
return super.visit(x, ctx);
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepend a function call to a Dart block, which is a body of a
|
||||
* function.
|
||||
*
|
||||
* @param oldBody
|
||||
* @param functionStmt
|
||||
* @return
|
||||
*/
|
||||
protected DartBlock prependToFnBody(DartBlock oldBody,
|
||||
DartExprStmt functionStmt) {
|
||||
List<DartStatement> blockStmts = new ArrayList<DartStatement>();
|
||||
blockStmts.add(functionStmt);
|
||||
if (oldBody != null) {
|
||||
List<DartStatement> stmts = oldBody.getStatements();
|
||||
if (stmts != null) {
|
||||
blockStmts.addAll(stmts);
|
||||
}
|
||||
}
|
||||
DartBlock newDB = new DartBlock(blockStmts);
|
||||
if (oldBody != null) {
|
||||
newDB.setSourceInfo(oldBody.getSourceInfo());
|
||||
}
|
||||
return newDB;
|
||||
}
|
||||
|
||||
protected void replaceFunction(DartContext ctx, DartFunction x,
|
||||
DartBlock newFnBody) {
|
||||
DartFunction newFn = new DartFunction(x.getParams(), newFnBody,
|
||||
x.getReturnTypeNode());
|
||||
newFn.setSourceInfo(x.getSourceInfo());
|
||||
ctx.replaceMe(newFn);
|
||||
}
|
||||
|
||||
protected DartExprStmt createFunctionCall(String functionName,
|
||||
List<DartExpression> args, DartNode oldNode) {
|
||||
DartIdentifier funcName = new DartIdentifier(functionName);
|
||||
DartUnqualifiedInvocation funcInvocation = new DartUnqualifiedInvocation(
|
||||
funcName, args);
|
||||
DartExprStmt functionStmt = new DartExprStmt(funcInvocation);
|
||||
if (oldNode != null) {
|
||||
SourceInfo sourceInfo = oldNode.getSourceInfo();
|
||||
setInstrumentedSourceInfo(functionStmt, sourceInfo);
|
||||
setInstrumentedSourceInfo(funcInvocation, sourceInfo);
|
||||
setInstrumentedSourceInfo(funcName, sourceInfo);
|
||||
for (DartExpression arg : args) {
|
||||
setInstrumentedSourceInfo(arg, sourceInfo);
|
||||
}
|
||||
}
|
||||
return functionStmt;
|
||||
}
|
||||
|
||||
void setInstrumentedSourceInfo(DartNode node, SourceInfo info) {
|
||||
node.setInstrumentedNode(true);
|
||||
node.setSourceInfo(info);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* TotalSettingInstrumenter instruments the main function to initialize
|
||||
* coverage variables and set totals for covered entities
|
||||
*/
|
||||
private class TotalSettingInstrumenter extends BaseInstrumenter {
|
||||
|
||||
@Override
|
||||
public boolean visit(DartFunction x, DartContext ctx) {
|
||||
DartNode parent = x.getParent();
|
||||
if (parent instanceof DartMethodDefinition) {
|
||||
String funcName = ((DartMethodDefinition) parent).getName()
|
||||
.toString();
|
||||
if ("main".equals(funcName)) {
|
||||
DartBlock newFnBody = x.getBody();
|
||||
|
||||
for (String unitName : unitsVisited) {
|
||||
int numFunctions = nullCheckingUnBox(numFunctionsMap
|
||||
.get(unitName));
|
||||
int numStatements = nullCheckingUnBox(numStatementsMap
|
||||
.get(unitName));
|
||||
int numBranches = nullCheckingUnBox(numBranchesMap
|
||||
.get(unitName));
|
||||
List<DartExpression> args = new ArrayList<DartExpression>();
|
||||
args.add(DartStringLiteral.get(unitName));
|
||||
args.add(DartIntegerLiteral.get(BigInteger
|
||||
.valueOf(numFunctions)));
|
||||
args.add(DartIntegerLiteral.get(BigInteger
|
||||
.valueOf(numStatements)));
|
||||
args.add(DartIntegerLiteral.get(BigInteger
|
||||
.valueOf(numBranches)));
|
||||
DartExprStmt callCovTotals = createFunctionCall(
|
||||
"setCoverageTotals", args, newFnBody);
|
||||
callCovTotals.setInstrumentedNode(true);
|
||||
newFnBody = prependToFnBody(newFnBody, callCovTotals);
|
||||
}
|
||||
|
||||
replaceFunction(ctx, x, newFnBody);
|
||||
}
|
||||
}
|
||||
return super.visit(x, ctx);
|
||||
}
|
||||
|
||||
int nullCheckingUnBox(Integer value) {
|
||||
int ret = 0;
|
||||
if (value != null) {
|
||||
ret = value.intValue();
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* FunctionInstrumenter adds instrumentation to track function coverage
|
||||
*/
|
||||
private class FunctionInstrumenter extends BaseInstrumenter {
|
||||
|
||||
private int numFunctions;
|
||||
|
||||
/**
|
||||
* This visitor method transforms:
|
||||
*
|
||||
* myFunction(){ stmts; ... }
|
||||
*
|
||||
* to:
|
||||
*
|
||||
* myFunction(){ coverFunction('unit.dart', 'myFunction'); stmts; ... }
|
||||
*
|
||||
* TODO: Cover closures as well
|
||||
*/
|
||||
@Override
|
||||
public boolean visit(DartFunction x, DartContext ctx) {
|
||||
DartNode parent = x.getParent();
|
||||
if (parent instanceof DartMethodDefinition) {
|
||||
String funcName = ((DartMethodDefinition) parent).getName()
|
||||
.toString();
|
||||
DartBlock oldBody = x.getBody();
|
||||
List<DartExpression> covArgs = new ArrayList<DartExpression>();
|
||||
covArgs.add(DartStringLiteral.get(currentUnit.getSourceName()));
|
||||
covArgs.add(DartStringLiteral.get(funcName));
|
||||
DartExprStmt callCovFunc = createFunctionCall("coverFunction",
|
||||
covArgs, oldBody);
|
||||
DartBlock newFnBody = prependToFnBody(oldBody, callCovFunc);
|
||||
replaceFunction(ctx, x, newFnBody);
|
||||
numFunctions++;
|
||||
}
|
||||
return super.visit(x, ctx);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize function counter
|
||||
*/
|
||||
@Override
|
||||
public boolean visit(DartUnit x, DartContext ctx) {
|
||||
numFunctions = 0;
|
||||
return super.visit(x, ctx);
|
||||
}
|
||||
|
||||
/**
|
||||
* Populate number of functions for current unit
|
||||
*/
|
||||
@Override
|
||||
public void endVisit(DartUnit x, DartContext ctx) {
|
||||
numFunctionsMap.put(currentUnit.getSourceName(), numFunctions);
|
||||
super.endVisit(x, ctx);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* StatementInstrumenter adds instrumentation before each statement to
|
||||
* capture runtime coverage of that statement
|
||||
*/
|
||||
private class StatementInstrumenter extends BaseInstrumenter {
|
||||
private int numStatements;
|
||||
|
||||
/**
|
||||
* Initialize statement counter
|
||||
*/
|
||||
@Override
|
||||
public boolean visit(DartUnit x, DartContext ctx) {
|
||||
numStatements = 0;
|
||||
return super.visit(x, ctx);
|
||||
}
|
||||
|
||||
/**
|
||||
* Populate number of statements for current unit
|
||||
*/
|
||||
@Override
|
||||
public void endVisit(DartUnit x, DartContext ctx) {
|
||||
numStatementsMap.put(currentUnit.getSourceName(), numStatements);
|
||||
super.endVisit(x, ctx);
|
||||
}
|
||||
|
||||
/**
|
||||
* This visitor method transforms:
|
||||
*
|
||||
* { stmts; ... }
|
||||
*
|
||||
* to:
|
||||
*
|
||||
* { coverStatement('unit.dart', lineNum1); stmt1;
|
||||
* coverStatement('unit.dart', lineNum2); stmt2;
|
||||
* ... }
|
||||
*
|
||||
*/
|
||||
@Override
|
||||
public boolean visit(DartBlock x, DartContext ctx) {
|
||||
List<DartStatement> newStmts = new ArrayList<DartStatement>();
|
||||
for (DartStatement stmt : x.getStatements()) {
|
||||
if (!stmt.isInstrumentedNode()) {
|
||||
List<DartExpression> covArgs = new ArrayList<DartExpression>();
|
||||
covArgs.add(DartStringLiteral.get(currentUnit
|
||||
.getSourceName()));
|
||||
covArgs.add(DartIntegerLiteral.get(BigInteger.valueOf(stmt
|
||||
.getSourceLine())));
|
||||
DartExprStmt callCoverStmt = createFunctionCall(
|
||||
"coverStatement", covArgs, stmt);
|
||||
newStmts.add(callCoverStmt);
|
||||
}
|
||||
newStmts.add(stmt);
|
||||
numStatements++;
|
||||
}
|
||||
DartBlock newBlock = new DartBlock(newStmts);
|
||||
ctx.replaceMe(newBlock);
|
||||
return super.visit(x, ctx);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* BranchInstrumenter captures runtime execution of each branch in the
|
||||
* program by adding instrumentation at each branch point
|
||||
*/
|
||||
private class BranchInstrumenter extends BaseInstrumenter {
|
||||
private int numBranches;
|
||||
|
||||
@Override
|
||||
public boolean visit(DartUnit x, DartContext ctx) {
|
||||
numBranches = 0;
|
||||
return super.visit(x, ctx);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void endVisit(DartUnit x, DartContext ctx) {
|
||||
numBranchesMap.put(currentUnit.getSourceName(), numBranches);
|
||||
super.endVisit(x, ctx);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* This visitor method transforms:
|
||||
*
|
||||
* if { stmts; ... }
|
||||
* else { ... }
|
||||
*
|
||||
* to:
|
||||
*
|
||||
* if { coverBranch('unit.dart', lineNum1, startLineNum); stmts; ... }
|
||||
* else { coverBranch('unit.dart', lineNum1, startLineNum); ... }
|
||||
*
|
||||
* A synthetic else is added to track the non-if branch if there is no else
|
||||
*
|
||||
* Post-order instrumentation is needed to handle nested ifs and
|
||||
* synthetic else
|
||||
*/
|
||||
@Override
|
||||
public void endVisit(DartIfStatement x, DartContext ctx) {
|
||||
DartIfStatement newIfStmt;
|
||||
DartStatement thenStmt = x.getThenStatement();
|
||||
DartStatement elseStmt = x.getElseStatement();
|
||||
|
||||
List<DartStatement> newThen = doCallCoverBranch(thenStmt);
|
||||
numBranches++;
|
||||
if (thenStmt instanceof DartBlock) {
|
||||
List<DartStatement> stmts = ((DartBlock) thenStmt)
|
||||
.getStatements();
|
||||
if (stmts != null) {
|
||||
newThen.addAll(stmts);
|
||||
}
|
||||
} else {
|
||||
newThen.add(thenStmt);
|
||||
}
|
||||
|
||||
if (elseStmt instanceof DartIfStatement) {
|
||||
newIfStmt = new DartIfStatement(x.getCondition(),
|
||||
new DartBlock(newThen), elseStmt);
|
||||
} else {
|
||||
List<DartStatement> newElse;
|
||||
if (elseStmt != null) {
|
||||
newElse = doCallCoverBranch(elseStmt);
|
||||
if (elseStmt instanceof DartBlock) {
|
||||
List<DartStatement> stmts = ((DartBlock) elseStmt)
|
||||
.getStatements();
|
||||
if (stmts != null) {
|
||||
newElse.addAll(stmts);
|
||||
}
|
||||
} else {
|
||||
newElse.add(elseStmt);
|
||||
}
|
||||
} else {
|
||||
// Although else block is missing, there is a branch which
|
||||
// doesn't cover the IfStatement
|
||||
// So, we need to add a synthetic else block to track that
|
||||
newElse = doCallCoverBranch(x);
|
||||
}
|
||||
newIfStmt = new DartIfStatement(x.getCondition(),
|
||||
new DartBlock(newThen), new DartBlock(newElse));
|
||||
numBranches++;
|
||||
}
|
||||
newIfStmt.setSourceInfo(x.getSourceInfo());
|
||||
ctx.replaceMe(newIfStmt);
|
||||
super.endVisit(x, ctx);
|
||||
}
|
||||
|
||||
/**
|
||||
* This method instruments all non-empty switch case blocks and the
|
||||
* default block. It inserts a synthetic default statement if one
|
||||
* doesn't exist.
|
||||
* This visitor method transforms:
|
||||
*
|
||||
* switch(var a) {
|
||||
* case a: ...;
|
||||
* case b: ...;
|
||||
* }
|
||||
*
|
||||
* to:
|
||||
*
|
||||
* switch(var a) {
|
||||
* case a: coverBranch('unit.dart', lineNum1, startLineNum1); ...;
|
||||
* case b: coverBranch('unit.dart', lineNum2, startLineNum2); ...;
|
||||
* default: coverBranch('unit.dart', lineNum3, startLineNum3); ...;
|
||||
* }
|
||||
*
|
||||
* The existing code ignores empty switch cases.
|
||||
*
|
||||
* For tracking branch on empty cases, this logic won't work directly
|
||||
* since instrumenting empty cases will throw a FallThroughError at
|
||||
* runtime. For achieving this, the switch needs to be transformed into
|
||||
* an if. Its slightly complicated but here is what I think the
|
||||
* transformation should be:
|
||||
*
|
||||
* switch(expr) {
|
||||
* case 'a': // empty switch case
|
||||
* case 'b': do1(); break;
|
||||
* case 'c': do2(); break;
|
||||
* default: doDefault();
|
||||
* }
|
||||
*
|
||||
* should be transformed to:
|
||||
*
|
||||
* var tmp = expr;
|
||||
* if( (tmp == 'a' && coverBranch(..)) || (tmp == 'b' &&
|
||||
* coverBranch(..))) {
|
||||
* do1();
|
||||
* } else if (tmp == 'a' && coverBranch(..)) {
|
||||
* do2();
|
||||
* } else {
|
||||
* coverBranch(..);
|
||||
* doDefault();
|
||||
* }
|
||||
*
|
||||
* However, I don't see much value in covering empty cases and hence
|
||||
* this was written like described.
|
||||
*/
|
||||
@Override
|
||||
public void endVisit(DartSwitchStatement x, DartContext ctx) {
|
||||
List<DartSwitchMember> newSwitchMembers =
|
||||
new ArrayList<DartSwitchMember>();
|
||||
boolean hasDefault = false;
|
||||
for (DartSwitchMember swMember : x.getMembers()) {
|
||||
List<DartStatement> stmts = doCallCoverBranch(swMember);
|
||||
List<DartStatement> oldStmts = swMember.getStatements();
|
||||
if (oldStmts == null || oldStmts.size() == 0) {
|
||||
newSwitchMembers.add(swMember);
|
||||
continue; // Ignore empty cases
|
||||
} else {
|
||||
stmts.addAll(oldStmts);
|
||||
}
|
||||
|
||||
assert (swMember instanceof DartCase ||
|
||||
swMember instanceof DartDefault);
|
||||
|
||||
DartSwitchMember newMember;
|
||||
if (swMember instanceof DartCase) {
|
||||
newMember = new DartCase(((DartCase) swMember).getExpr(),
|
||||
swMember.getLabel(), stmts);
|
||||
} else {
|
||||
hasDefault = true;
|
||||
newMember = new DartDefault(swMember.getLabel(), stmts);
|
||||
}
|
||||
setInstrumentedSourceInfo(newMember, swMember);
|
||||
newSwitchMembers.add(newMember);
|
||||
numBranches++;
|
||||
}
|
||||
|
||||
if (!hasDefault) {
|
||||
List<DartStatement> statements = doCallCoverBranch(x);
|
||||
DartSwitchMember defaultMember = new DartDefault(null,
|
||||
statements);
|
||||
newSwitchMembers.add(defaultMember);
|
||||
numBranches++;
|
||||
}
|
||||
|
||||
DartSwitchStatement newSwitch = new DartSwitchStatement(
|
||||
x.getExpression(), newSwitchMembers);
|
||||
newSwitch.setSourceInfo(x.getSourceInfo());
|
||||
|
||||
ctx.replaceMe(newSwitch);
|
||||
super.visit(x, ctx);
|
||||
}
|
||||
|
||||
/**
|
||||
* The instrumentation is similar to if and switch.
|
||||
* A synthetic finally block is inserted if not present.
|
||||
*/
|
||||
|
||||
@Override
|
||||
public void endVisit(DartTryStatement x, DartContext ctx) {
|
||||
DartBlock tryBlock = x.getTryBlock(), finallyBlock = x
|
||||
.getFinallyBlock();
|
||||
List<DartCatchBlock> catchBlocks = x.getCatchBlocks();
|
||||
|
||||
List<DartStatement> tryStmts = doCallCoverBranch(tryBlock);
|
||||
if (tryBlock != null && tryBlock.getStatements() != null) {
|
||||
tryStmts.addAll(tryBlock.getStatements());
|
||||
}
|
||||
DartBlock newTryBlock = new DartBlock(tryStmts);
|
||||
setInstrumentedSourceInfo(newTryBlock, tryBlock.getSourceInfo());
|
||||
numBranches++;
|
||||
|
||||
List<DartCatchBlock> newCatchBlocks = null;
|
||||
if (catchBlocks != null) {
|
||||
newCatchBlocks = new ArrayList<DartCatchBlock>();
|
||||
for (DartCatchBlock cBlock : catchBlocks) {
|
||||
DartBlock oldBlock = cBlock.getBlock();
|
||||
|
||||
List<DartStatement> cBlockStmts = doCallCoverBranch(cBlock);
|
||||
if (oldBlock != null && oldBlock.getStatements() != null) {
|
||||
cBlockStmts.addAll(oldBlock.getStatements());
|
||||
}
|
||||
DartBlock newBlock = new DartBlock(cBlockStmts);
|
||||
setInstrumentedSourceInfo(newBlock,
|
||||
oldBlock.getSourceInfo());
|
||||
|
||||
DartCatchBlock newCatchBlock = new DartCatchBlock(newBlock,
|
||||
cBlock.getException(), cBlock.getStackTrace());
|
||||
setInstrumentedSourceInfo(newCatchBlock,
|
||||
cBlock.getSourceInfo());
|
||||
|
||||
newCatchBlocks.add(newCatchBlock);
|
||||
numBranches++;
|
||||
}
|
||||
}
|
||||
|
||||
DartBlock newFinallyBlock = null;
|
||||
if(finallyBlock != null){
|
||||
List<DartStatement> finallyStmts = doCallCoverBranch(finallyBlock);
|
||||
if(finallyBlock.getStatements() != null){
|
||||
finallyStmts.addAll(finallyBlock.getStatements());
|
||||
}
|
||||
newFinallyBlock = new DartBlock(finallyStmts);
|
||||
setInstrumentedSourceInfo(newFinallyBlock,
|
||||
finallyBlock.getSourceInfo());
|
||||
} else {
|
||||
List<DartStatement> finallyStmts = doCallCoverBranch(x);
|
||||
newFinallyBlock = new DartBlock(finallyStmts);
|
||||
setInstrumentedSourceInfo(newFinallyBlock, x.getSourceInfo());
|
||||
}
|
||||
numBranches++;
|
||||
|
||||
DartTryStatement newTry = new DartTryStatement(tryBlock,
|
||||
newCatchBlocks, newFinallyBlock);
|
||||
ctx.replaceMe(newTry);
|
||||
super.endVisit(x, ctx);
|
||||
}
|
||||
|
||||
/**
|
||||
* Rewrite all loops in this block
|
||||
*
|
||||
* While loop is transformed from this:
|
||||
*
|
||||
* while(cond) { stmts; }
|
||||
*
|
||||
* to:
|
||||
*
|
||||
* loopBranchBefore('unit.dart', loopLine, loopStart);
|
||||
* while(cond) {
|
||||
* loopBranchInside('unit.dart', loopLine, loopStart);
|
||||
* stmts;
|
||||
* }
|
||||
* coverLoopBranch('unit.dart', loopLine, loopStart);
|
||||
*
|
||||
* We track two branches for every loop - one that goes inside and one that
|
||||
* doesn't execute the loop at all. This is tracked by resetting/setting
|
||||
* a variable from loopBranchBefore and loopBranchInside, which is checked
|
||||
* in coverLoopBranch to decide which branch was taken.
|
||||
*/
|
||||
@Override
|
||||
public void endVisit(DartBlock x, DartContext ctx) {
|
||||
List<DartStatement> blockStmts = new ArrayList<DartStatement>();
|
||||
for(DartStatement stmt : x.getStatements()) {
|
||||
if(stmt instanceof DartForInStatement) {
|
||||
doLoopBefore(stmt, blockStmts);
|
||||
|
||||
DartForInStatement oldForIn = (DartForInStatement) stmt;
|
||||
DartStatement setup = null;
|
||||
if(oldForIn.introducesVariable()){
|
||||
setup = oldForIn.getVariableStatement();
|
||||
} else {
|
||||
setup = new DartExprStmt(oldForIn.getIdentifier());
|
||||
}
|
||||
DartBlock body = doLoopInside(stmt, oldForIn.getBody());
|
||||
DartForInStatement newForIn =
|
||||
new DartForInStatement(setup, oldForIn.getIterable(), body);
|
||||
setInstrumentedSourceInfo(newForIn, oldForIn.getSourceInfo());
|
||||
|
||||
doLoopAfter(stmt, blockStmts);
|
||||
numBranches+=2;
|
||||
} else if (stmt instanceof DartForStatement) {
|
||||
doLoopBefore(stmt, blockStmts);
|
||||
|
||||
DartForStatement oldFor = (DartForStatement) stmt;
|
||||
DartBlock body = doLoopInside(stmt, oldFor.getBody());
|
||||
DartForStatement newFor = new DartForStatement(oldFor.getInit(),
|
||||
oldFor.getCondition(), oldFor.getIncrement(), body);
|
||||
setInstrumentedSourceInfo(newFor, oldFor.getSourceInfo());
|
||||
|
||||
doLoopAfter(stmt, blockStmts);
|
||||
numBranches+=2;
|
||||
} else if (stmt instanceof DartWhileStatement) {
|
||||
doLoopBefore(stmt, blockStmts);
|
||||
|
||||
DartWhileStatement oldWhile = (DartWhileStatement) stmt;
|
||||
DartBlock body = doLoopInside(stmt, oldWhile.getBody());
|
||||
DartWhileStatement newWhile =
|
||||
new DartWhileStatement(oldWhile.getCondition(), body);
|
||||
setInstrumentedSourceInfo(newWhile, oldWhile.getSourceInfo());
|
||||
|
||||
doLoopAfter(stmt, blockStmts);
|
||||
numBranches+=2;
|
||||
} else if (stmt instanceof DartDoWhileStatement) {
|
||||
doLoopBefore(stmt, blockStmts);
|
||||
|
||||
DartDoWhileStatement oldDoWhile = (DartDoWhileStatement) stmt;
|
||||
DartBlock body = doLoopInside(stmt, oldDoWhile.getBody());
|
||||
DartDoWhileStatement newDoWhile =
|
||||
new DartDoWhileStatement(oldDoWhile.getCondition(), body);
|
||||
setInstrumentedSourceInfo(newDoWhile, oldDoWhile.getSourceInfo());
|
||||
|
||||
doLoopAfter(stmt, blockStmts);
|
||||
numBranches+=2;
|
||||
} else {
|
||||
blockStmts.add(stmt);
|
||||
}
|
||||
}
|
||||
|
||||
super.visit(x, ctx);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds function call to track coverage of a branch represented by
|
||||
* blockNode
|
||||
*
|
||||
* @param blockNode
|
||||
* @return
|
||||
*/
|
||||
private List<DartStatement> doCallCoverBranch(DartNode blockNode) {
|
||||
List<DartStatement> newBlockStmts = new ArrayList<DartStatement>();
|
||||
List<DartExpression> covArgs = makeCoverageArgs(blockNode);
|
||||
DartExprStmt callCoverBranch = createFunctionCall("coverBranch",
|
||||
covArgs, blockNode);
|
||||
newBlockStmts.add(callCoverBranch);
|
||||
setInstrumentedSourceInfo(callCoverBranch,
|
||||
blockNode.getSourceInfo());
|
||||
return newBlockStmts;
|
||||
}
|
||||
|
||||
private void doLoopBefore(DartNode loopNode, List<DartStatement> stmts) {
|
||||
List<DartExpression> args = makeCoverageArgs(loopNode);
|
||||
DartExprStmt callLoopBefore =
|
||||
createFunctionCall("loopBranchBefore", args, loopNode);
|
||||
stmts.add(callLoopBefore);
|
||||
}
|
||||
|
||||
private void doLoopAfter(DartNode loopNode, List<DartStatement> stmts) {
|
||||
List<DartExpression> args = makeCoverageArgs(loopNode);
|
||||
args.add(DartIntegerLiteral.get(BigInteger.valueOf(
|
||||
loopNode.getSourceStart() + loopNode.getSourceLength())));
|
||||
DartExprStmt callLoopAfter =
|
||||
createFunctionCall("coverLoopBranch", args, loopNode);
|
||||
stmts.add(callLoopAfter);
|
||||
}
|
||||
|
||||
private DartBlock doLoopInside(DartNode loopNode, DartStatement body) {
|
||||
List<DartStatement> newBlockStmts = new ArrayList<DartStatement>();
|
||||
List<DartExpression> covArgs = makeCoverageArgs(loopNode);
|
||||
DartExprStmt callCoverBranch = createFunctionCall("loopBranchInside",
|
||||
covArgs, loopNode);
|
||||
newBlockStmts.add(callCoverBranch);
|
||||
setInstrumentedSourceInfo(callCoverBranch,
|
||||
loopNode.getSourceInfo());
|
||||
|
||||
if(body instanceof DartBlock) {
|
||||
newBlockStmts.addAll(((DartBlock) body).getStatements());
|
||||
} else {
|
||||
newBlockStmts.add(body);
|
||||
}
|
||||
|
||||
return new DartBlock(newBlockStmts);
|
||||
}
|
||||
|
||||
private List<DartExpression> makeCoverageArgs(DartNode blockNode) {
|
||||
List<DartExpression> covArgs = new ArrayList<DartExpression>();
|
||||
covArgs.add(DartStringLiteral.get(currentUnit.getSourceName()));
|
||||
covArgs.add(DartIntegerLiteral.get(BigInteger.valueOf(blockNode
|
||||
.getSourceLine())));
|
||||
covArgs.add(DartIntegerLiteral.get(BigInteger.valueOf(blockNode
|
||||
.getSourceStart())));
|
||||
return covArgs;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -18,7 +18,6 @@ import java.util.List;
|
||||
public abstract class DartNode extends AbstractNode implements DartVisitable {
|
||||
|
||||
private DartNode parent;
|
||||
private boolean isInstrumentedNode;
|
||||
|
||||
public final String toSource() {
|
||||
DefaultTextOutput out = new DefaultTextOutput(false);
|
||||
@@ -152,12 +151,4 @@ public abstract class DartNode extends AbstractNode implements DartVisitable {
|
||||
public String getObjectIdentifier(){
|
||||
return super.toString();
|
||||
}
|
||||
|
||||
public boolean isInstrumentedNode() {
|
||||
return isInstrumentedNode;
|
||||
}
|
||||
|
||||
public void setInstrumentedNode(boolean isInstrumentedNode) {
|
||||
this.isInstrumentedNode = isInstrumentedNode;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,18 +1,17 @@
|
||||
// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file
|
||||
// Copyright (c) 2012, 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.ast.viz;
|
||||
|
||||
import com.google.dart.compiler.ast.DartNode;
|
||||
import com.google.dart.compiler.ast.DartUnit;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStreamWriter;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import com.google.common.io.Closeables;
|
||||
import com.google.dart.compiler.ast.DartNode;
|
||||
import com.google.dart.compiler.ast.DartUnit;
|
||||
|
||||
/**
|
||||
* Write the AST in to System out.
|
||||
*/
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file
|
||||
// Copyright (c) 2012, 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.
|
||||
|
||||
@@ -110,10 +110,6 @@ public class DotWriter extends BaseASTWriter {
|
||||
style.append(", color=blue");
|
||||
}
|
||||
|
||||
if(node.isInstrumentedNode()){
|
||||
style.append(", style=filled, fillcolor=yellow");
|
||||
}
|
||||
|
||||
return style.toString();
|
||||
}
|
||||
|
||||
|
||||
@@ -1,99 +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.dart;
|
||||
|
||||
import com.google.common.io.CharStreams;
|
||||
import com.google.common.io.Closeables;
|
||||
import com.google.dart.compiler.Backend;
|
||||
import com.google.dart.compiler.DartCompilerContext;
|
||||
import com.google.dart.compiler.DartSource;
|
||||
import com.google.dart.compiler.LibrarySource;
|
||||
import com.google.dart.compiler.ast.DartToSourceVisitor;
|
||||
import com.google.dart.compiler.ast.DartUnit;
|
||||
import com.google.dart.compiler.ast.LibraryUnit;
|
||||
import com.google.dart.compiler.resolver.CoreTypeProvider;
|
||||
import com.google.dart.compiler.util.DefaultTextOutput;
|
||||
import com.google.dart.compiler.util.TextOutput;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.Reader;
|
||||
import java.io.Writer;
|
||||
import java.util.Collection;
|
||||
|
||||
/**
|
||||
* A compiler backend that produces optimized Dart.
|
||||
*/
|
||||
public class DartBackend implements Backend {
|
||||
|
||||
public static final String EXTENSION_DART = "opt.dart";
|
||||
|
||||
private static void packageLibs(Collection<LibraryUnit> libraries,
|
||||
Writer w,
|
||||
DartCompilerContext context)
|
||||
throws IOException {
|
||||
for (LibraryUnit libUnit : libraries) {
|
||||
for (DartUnit unit : libUnit.getUnits()) {
|
||||
DartSource src = unit.getSource();
|
||||
if (src != null) {
|
||||
Reader r = context.getArtifactReader(src, "", EXTENSION_DART);
|
||||
boolean failed = true;
|
||||
try {
|
||||
CharStreams.copy(r, w);
|
||||
failed = false;
|
||||
} finally {
|
||||
Closeables.close(r, failed);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isOutOfDate(DartSource src, DartCompilerContext context) {
|
||||
return context.isOutOfDate(src, src, EXTENSION_DART);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void compileUnit(DartUnit unit, DartSource src,
|
||||
DartCompilerContext context, CoreTypeProvider typeProvider) throws IOException {
|
||||
// Generate Javascript output.
|
||||
TextOutput out = new DefaultTextOutput(false);
|
||||
DartToSourceVisitor srcGenerator = new DartToSourceVisitor(out);
|
||||
srcGenerator.accept(unit);
|
||||
Writer w = context.getArtifactWriter(src, "", EXTENSION_DART);
|
||||
boolean failed = true;
|
||||
try {
|
||||
w.write(out.toString());
|
||||
failed = false;
|
||||
} finally {
|
||||
Closeables.close(w, failed);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void packageApp(LibrarySource app,
|
||||
Collection<LibraryUnit> libraries,
|
||||
DartCompilerContext context,
|
||||
CoreTypeProvider typeProvider) throws IOException {
|
||||
Writer out = context.getArtifactWriter(app, "", EXTENSION_DART);
|
||||
boolean failed = true;
|
||||
try {
|
||||
// Emit the concatenated Javascript sources in dependency order.
|
||||
packageLibs(libraries, out, context);
|
||||
|
||||
// Emit entry point call.
|
||||
// TODO: How does a dart app start?
|
||||
// out.write(app.getEntryMethod() + "();");
|
||||
failed = false;
|
||||
} finally {
|
||||
Closeables.close(out, failed);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getAppExtension() {
|
||||
return EXTENSION_DART;
|
||||
}
|
||||
}
|
||||
@@ -1,518 +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.collect.ArrayListMultimap;
|
||||
import com.google.common.collect.HashMultimap;
|
||||
import com.google.common.collect.HashMultiset;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Maps;
|
||||
import com.google.common.collect.Multimap;
|
||||
import com.google.common.collect.Multimaps;
|
||||
import com.google.common.collect.Multiset;
|
||||
import com.google.common.io.Closeables;
|
||||
import com.google.dart.compiler.Backend;
|
||||
import com.google.dart.compiler.CommandLineOptions.CompilerOptions;
|
||||
import com.google.dart.compiler.DartCompilerContext;
|
||||
import com.google.dart.compiler.DartSource;
|
||||
import com.google.dart.compiler.ast.DartClass;
|
||||
import com.google.dart.compiler.ast.DartNode;
|
||||
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.JsBlock;
|
||||
import com.google.dart.compiler.backend.js.ast.JsProgram;
|
||||
import com.google.dart.compiler.metrics.DartEventType;
|
||||
import com.google.dart.compiler.metrics.Tracer.TraceEvent;
|
||||
import com.google.dart.compiler.metrics.Tracer;
|
||||
import com.google.dart.compiler.resolver.ClassElement;
|
||||
import com.google.dart.compiler.resolver.CoreTypeProvider;
|
||||
import com.google.dart.compiler.resolver.MethodElement;
|
||||
import com.google.dart.compiler.type.InterfaceType;
|
||||
import com.google.dart.compiler.util.DefaultTextOutput;
|
||||
import com.google.dart.compiler.util.TextOutput;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.Writer;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.PriorityQueue;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Methods common to the ClosureJsBackend and JavascriptBackend.
|
||||
* @author johnlenz@google.com (John Lenz)
|
||||
*/
|
||||
public abstract class AbstractJsBackend implements Backend {
|
||||
|
||||
public static final String EXTENSION_JS = "js";
|
||||
public static final String EXTENSION_APP_JS = "app.js";
|
||||
|
||||
private static final String ROOT_PART_NAME = "";
|
||||
private static final String STATICS_PART_NAME = "$statics$";
|
||||
private static final String SEPARATOR_PART_NAME = "$seperator$";
|
||||
|
||||
protected final DartMangler mangler = new DollarMangler();
|
||||
|
||||
protected static class Part {
|
||||
final LibraryUnit lib;
|
||||
final DartUnit unit;
|
||||
final String part;
|
||||
final ClassElement element;
|
||||
final ClassElement superElement;
|
||||
|
||||
public Part(LibraryUnit lib, DartUnit unit, String part,
|
||||
ClassElement element, ClassElement superElement) {
|
||||
this.lib = lib;
|
||||
this.unit = unit;
|
||||
this.element = element;
|
||||
this.part = part;
|
||||
this.superElement = superElement;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
if (element != null) {
|
||||
return element.hashCode();
|
||||
}
|
||||
final int prime = 31;
|
||||
int result = 1;
|
||||
result = prime * result + ((part == null) ? 0 : part.hashCode());
|
||||
result = prime * result + ((unit == null) ? 0 : unit.hashCode());
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if (obj == null) {
|
||||
return false;
|
||||
}
|
||||
if (getClass() != obj.getClass()) {
|
||||
return false;
|
||||
}
|
||||
Part other = (Part) obj;
|
||||
if (element != null) {
|
||||
return element.equals(other.element);
|
||||
}
|
||||
return unit.equals(other.unit) && part.equals(other.part);
|
||||
}
|
||||
}
|
||||
|
||||
protected static interface DepsCallback {
|
||||
void visitNative(LibraryUnit libUnit, LibraryNode node) throws IOException;
|
||||
void visitPart(Part part) throws IOException;
|
||||
}
|
||||
|
||||
protected static class DependencyBuilder {
|
||||
private final List<Part> parts;
|
||||
private final Part staticSeparator = new Part(null, null, SEPARATOR_PART_NAME, null, null);
|
||||
|
||||
static void build(LibraryUnit libUnit, DepsCallback callback) throws IOException {
|
||||
DependencyBuilder builder = new DependencyBuilder();
|
||||
builder.gatherParts(libUnit);
|
||||
builder.sortParts();
|
||||
builder.writeParts(callback);
|
||||
}
|
||||
|
||||
private DependencyBuilder() {
|
||||
this.parts = new ArrayList<Part>();
|
||||
}
|
||||
|
||||
private void gatherParts(LibraryUnit libUnit) {
|
||||
gatherParts(libUnit, new HashSet<LibraryUnit>());
|
||||
}
|
||||
|
||||
private void gatherParts(LibraryUnit libUnit, Set<LibraryUnit> seenLibs) {
|
||||
// Avoid cycles.
|
||||
if (seenLibs.contains(libUnit)) {
|
||||
return;
|
||||
}
|
||||
seenLibs.add(libUnit);
|
||||
|
||||
// Visit dependencies first.
|
||||
for (LibraryUnit importUnit : libUnit.getImports()) {
|
||||
gatherParts(importUnit, seenLibs);
|
||||
}
|
||||
|
||||
for (DartUnit unit : libUnit.getUnits()) {
|
||||
DartSource src = unit.getSource();
|
||||
if (src != null) {
|
||||
// get the list of source source parts
|
||||
gatherUnitParts(libUnit, unit);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void gatherUnitParts(LibraryUnit libUnit, DartUnit unit) {
|
||||
List<DartNode> nodes = ((DartUnit)unit.getNormalizedNode()).getTopLevelNodes();
|
||||
for (DartNode node : nodes) {
|
||||
DartNode norm = node.getNormalizedNode();
|
||||
if (norm instanceof DartClass) {
|
||||
DartClass clasz = (DartClass)norm;
|
||||
ClassElement selfElement = clasz.getSymbol();
|
||||
InterfaceType superType = selfElement.getSupertype();
|
||||
ClassElement superElement = null;
|
||||
if (superType != null) {
|
||||
superElement = superType.getElement();
|
||||
assert(superElement != null);
|
||||
}
|
||||
|
||||
parts.add(new Part(libUnit, unit,
|
||||
clasz.getClassName(), selfElement, superElement));
|
||||
}
|
||||
}
|
||||
|
||||
parts.add(new Part(libUnit, unit, ROOT_PART_NAME, null, null)); // top-level bits
|
||||
parts.add(new Part(libUnit, unit, STATICS_PART_NAME, null, null)); // static initializer bits
|
||||
}
|
||||
|
||||
/**
|
||||
* @param items The list of items to sort.
|
||||
* @param deps A map of dependencies between items.
|
||||
* @return A list of items in dependency order.
|
||||
*/
|
||||
private static <T> List<T> topologicalStableSort(
|
||||
List<T> items, Multimap<T, T> deps) {
|
||||
final Map<T, Integer> originalIndex = Maps.newHashMap();
|
||||
for (int i = 0; i < items.size(); i++) {
|
||||
originalIndex.put(items.get(i), i);
|
||||
}
|
||||
|
||||
PriorityQueue<T> inDegreeZero = new PriorityQueue<T>(items.size(),
|
||||
new Comparator<T>() {
|
||||
@Override
|
||||
public int compare(T a, T b) {
|
||||
return originalIndex.get(a).intValue() -
|
||||
originalIndex.get(b).intValue();
|
||||
}
|
||||
});
|
||||
List<T> result = Lists.newArrayList();
|
||||
|
||||
Multiset<T> inDegree = HashMultiset.create();
|
||||
Multimap<T, T> reverseDeps = ArrayListMultimap.create();
|
||||
Multimaps.invertFrom(deps, reverseDeps);
|
||||
|
||||
// First, add all the inputs with in-degree 0.
|
||||
for (T item : items) {
|
||||
Collection<T> itemDeps = deps.get(item);
|
||||
inDegree.add(item, itemDeps.size());
|
||||
if (itemDeps.isEmpty()) {
|
||||
inDegreeZero.add(item);
|
||||
}
|
||||
}
|
||||
|
||||
// Then, iterate to a fixed point over the reverse dependency graph.
|
||||
while (!inDegreeZero.isEmpty()) {
|
||||
T item = inDegreeZero.remove();
|
||||
result.add(item);
|
||||
for (T inWaiting : reverseDeps.get(item)) {
|
||||
inDegree.remove(inWaiting, 1);
|
||||
if (inDegree.count(inWaiting) == 0) {
|
||||
inDegreeZero.add(inWaiting);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build a map of dependencies between Parts.
|
||||
* @param parts The parts to build dependencies from.
|
||||
*/
|
||||
private Multimap<Part, Part> buildDependencyMap(List<Part> parts) {
|
||||
// Add a Part to act as separator between class initialization
|
||||
// and static initialization. Statics may depend on the classes
|
||||
// being properly setup.
|
||||
parts.add(staticSeparator);
|
||||
|
||||
final Map<ClassElement, Part> elementToPartMap = Maps.newHashMap();
|
||||
for (Part part : parts) {
|
||||
if (part.element != null) {
|
||||
Part previous = elementToPartMap.put(part.element, part);
|
||||
assert(previous == null);
|
||||
}
|
||||
}
|
||||
|
||||
// Get the direct dependencies.
|
||||
final Multimap<Part, Part> deps = HashMultimap.create();
|
||||
for (Part part : parts) {
|
||||
if (part.superElement != null) {
|
||||
Part superPart = elementToPartMap.get(part.superElement);
|
||||
assert(superPart != null);
|
||||
deps.put(part, superPart);
|
||||
}
|
||||
|
||||
// Don't add a dependency on itself.
|
||||
if (part != staticSeparator) {
|
||||
if (part.part.equals(STATICS_PART_NAME)) {
|
||||
// Push all statics after classes.
|
||||
deps.put(part, staticSeparator);
|
||||
} else {
|
||||
// All classes before statics.
|
||||
deps.put(staticSeparator, part);
|
||||
}
|
||||
}
|
||||
}
|
||||
return deps;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sort the parts based on their dependencies.
|
||||
*/
|
||||
private void sortParts() {
|
||||
List<Part> unsortedParts = parts;
|
||||
|
||||
Multimap<Part, Part> deps = buildDependencyMap(unsortedParts);
|
||||
List<Part> sortParts = topologicalStableSort(unsortedParts, deps);
|
||||
|
||||
parts.clear();
|
||||
parts.addAll(sortParts);
|
||||
}
|
||||
|
||||
private long writeParts(DepsCallback callback)
|
||||
throws IOException {
|
||||
long charsWritten = 0;
|
||||
Set<LibraryUnit> seenLibs = new HashSet<LibraryUnit>();
|
||||
for (Part part : parts) {
|
||||
writePart(part, callback, seenLibs);
|
||||
}
|
||||
return charsWritten;
|
||||
}
|
||||
|
||||
private void writePart(Part part, DepsCallback callback, Set<LibraryUnit> seenLibs)
|
||||
throws IOException {
|
||||
|
||||
// Don't try to do anything with the fake separator part.
|
||||
if (part == staticSeparator) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Avoid cycles.
|
||||
if (!seenLibs.contains(part.lib)) {
|
||||
seenLibs.add(part.lib);
|
||||
// Prepend all native JS for this library.
|
||||
for (LibraryNode node : part.lib.getNativePaths()) {
|
||||
callback.visitNative(part.lib, node);
|
||||
}
|
||||
}
|
||||
|
||||
callback.visitPart(part);
|
||||
}
|
||||
}
|
||||
|
||||
protected Map<String, JsProgram> translateToJS(DartUnit unit, DartCompilerContext context,
|
||||
CoreTypeProvider typeProvider) {
|
||||
TraceEvent logEvent =
|
||||
Tracer.canTrace() ? Tracer.start(DartEventType.TRANSLATE_TO_JS, "unit",
|
||||
unit.getSourceName()) : null;
|
||||
|
||||
try {
|
||||
TraceEvent normalizeEvent =
|
||||
Tracer.canTrace() ? Tracer.start(DartEventType.JS_NORMALIZE, "unit",
|
||||
unit.getSourceName()) : null;
|
||||
try {
|
||||
// Normalize front-end AST for back-end consumption.
|
||||
unit = (DartUnit) (new Normalizer()).exec(unit, typeProvider).getNormalizedNode();
|
||||
} finally {
|
||||
Tracer.end(normalizeEvent);
|
||||
}
|
||||
|
||||
// TODO(floitsch.: Make namer configurable.
|
||||
JsNamer namer = new JsPrettyNamer();
|
||||
|
||||
Map<String, JsProgram> parts = new LinkedHashMap<String, JsProgram>();
|
||||
|
||||
List<DartNode> topNodes = unit.getTopLevelNodes();
|
||||
|
||||
// Generate an id for this unit that can be used to make globally unique
|
||||
// identifiers.
|
||||
String baseUnitId = generateBaseUnitId(unit);
|
||||
int partIndex = 0;
|
||||
|
||||
// Translate the AST to JS.
|
||||
JsProgram nonClassStatements = new JsProgram(baseUnitId + partIndex++);
|
||||
GenerateJavascriptAST nonClassGenerator = null;
|
||||
TranslationContext nonClassTranslationContext = null;
|
||||
|
||||
JsProgram staticInitStatements = new JsProgram(baseUnitId + partIndex++);
|
||||
JsBlock staticInitBlock = staticInitStatements.getGlobalBlock();
|
||||
|
||||
for (DartNode node : topNodes) {
|
||||
node = node.getNormalizedNode();
|
||||
if (node instanceof DartClass) {
|
||||
// TODO: Don't write out *.js for interfaces -- there are a lot of them
|
||||
TraceEvent nodeEvent =
|
||||
Tracer.canTrace() ? Tracer.start(DartEventType.TRANSLATE_NODE, "unit",
|
||||
unit.getSourceName(), "node", node.getSymbol().getOriginalSymbolName()) : null;
|
||||
try {
|
||||
// Translate the AST to JS.
|
||||
JsProgram program = new JsProgram(baseUnitId + partIndex++);
|
||||
|
||||
TranslationContext translationContext = TranslationContext.createContext(unit, program,
|
||||
mangler, node);
|
||||
|
||||
// Generate the Javascript AST.
|
||||
GenerateJavascriptAST generator =
|
||||
new GenerateJavascriptAST(unit, typeProvider, context);
|
||||
generator.translateNode(translationContext, node, staticInitBlock);
|
||||
|
||||
TraceEvent namerEvent =
|
||||
Tracer.canTrace() ? Tracer.start(DartEventType.NAMER, "unit",
|
||||
unit.getSourceName()) : null;
|
||||
try {
|
||||
namer.exec(program);
|
||||
} finally {
|
||||
Tracer.end(namerEvent);
|
||||
}
|
||||
|
||||
parts.put(((DartClass) node).getClassName(), program);
|
||||
} finally {
|
||||
Tracer.end(nodeEvent);
|
||||
}
|
||||
} else {
|
||||
|
||||
if (nonClassGenerator == null) {
|
||||
// This block generates the AST used to generate for all non-class nodes at once
|
||||
// and is saved for subsequent iterations.
|
||||
TraceEvent genInitEvent =
|
||||
Tracer.canTrace() ? Tracer.start(DartEventType.GEN_AST_INIT, "unit",
|
||||
unit.getSourceName()) : null;
|
||||
try {
|
||||
nonClassTranslationContext = TranslationContext.createContext(unit,
|
||||
nonClassStatements, mangler, null);
|
||||
nonClassGenerator = new GenerateJavascriptAST(unit, typeProvider, context);
|
||||
} finally {
|
||||
Tracer.end(genInitEvent);
|
||||
}
|
||||
}
|
||||
|
||||
nonClassGenerator.translateNode(nonClassTranslationContext, node, staticInitBlock);
|
||||
}
|
||||
}
|
||||
|
||||
TraceEvent namerEvent =
|
||||
Tracer.canTrace() ? Tracer.start(DartEventType.NAMER, "unit", unit.getSourceName())
|
||||
: null;
|
||||
try {
|
||||
namer.exec(nonClassStatements);
|
||||
} finally {
|
||||
Tracer.end(namerEvent);
|
||||
}
|
||||
|
||||
// Out-of-date checks rely on the root JS file existing, even if it is empty
|
||||
parts.put(ROOT_PART_NAME, nonClassStatements);
|
||||
|
||||
// Only add static parts if they are not empty
|
||||
for (int i = 0; i < staticInitStatements.getFragmentCount(); ++i) {
|
||||
if (!staticInitStatements.getFragmentBlock(i).getStatements().isEmpty()) {
|
||||
parts.put(STATICS_PART_NAME, staticInitStatements);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return parts;
|
||||
} finally {
|
||||
Tracer.end(logEvent);
|
||||
}
|
||||
}
|
||||
|
||||
private static String generateBaseUnitId(DartUnit unit) {
|
||||
MessageDigest md;
|
||||
try {
|
||||
md = MessageDigest.getInstance("MD5");
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
throw new AssertionError("Could not find MD5 digest");
|
||||
}
|
||||
StringBuilder sb = new StringBuilder();
|
||||
byte[] md5 = md.digest(unit.getSource().getUri().toString().getBytes());
|
||||
// Only use the first 6 hex characters of the md5.
|
||||
for (int i = 0; i < 3; i++) {
|
||||
sb.append(Integer.toHexString((md5[i] & 0xf0) >> 4));
|
||||
sb.append(Integer.toHexString(md5[i] & 0xf));
|
||||
}
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
protected int writeEntryPointCall(String entry, Writer out) throws IOException {
|
||||
// Emit entry point call.
|
||||
// TODO: Actually validate that this method exists.
|
||||
// Small hack: the V8 arguments object is not an instance of Array. [].concat(arguments)
|
||||
// copies the elements of the arguments and returns a proper array.
|
||||
// However in Rhino this operation simply creates an array with 'arguments' as the first (and
|
||||
// only) element. By calling "arguments.slice()" on it, a new array is returned, and the
|
||||
// concatenation works again.
|
||||
// TODO: Use a more robust check to test that the argument is
|
||||
// array.
|
||||
String entryPointCall = "RunEntry(" + entry + ", this.arguments ?" +
|
||||
" (this.arguments.slice ? [].concat(this.arguments.slice())" +
|
||||
" : this.arguments) : []);";
|
||||
out.write(entryPointCall);
|
||||
return entryPointCall.length();
|
||||
}
|
||||
|
||||
protected String getMangledEntryPoint(DartCompilerContext context) {
|
||||
MethodElement entry = context.getApplicationUnit().getElement().getEntryPoint();
|
||||
if (entry == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return mangler.mangleEntryPoint(entry, context.getApplicationUnit().getElement());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isOutOfDate(DartSource src, DartCompilerContext context) {
|
||||
return context.isOutOfDate(src, src, EXTENSION_JS);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void compileUnit(DartUnit unit, DartSource src, DartCompilerContext context,
|
||||
CoreTypeProvider typeProvider) throws IOException {
|
||||
// Translate the AST to JS.
|
||||
Map<String, JsProgram> parts = translateToJS(unit, context, typeProvider);
|
||||
String srcName = src.getName();
|
||||
|
||||
for (Map.Entry<String, JsProgram> entry : parts.entrySet()) {
|
||||
// Generate Javascript output.
|
||||
TextOutput out = new DefaultTextOutput(false);
|
||||
JsToStringGenerationVisitor srcGenerator;
|
||||
String name = entry.getKey();
|
||||
boolean failed = true;
|
||||
Writer w;
|
||||
|
||||
JsProgram program = entry.getValue();
|
||||
JsBlock globalBlock = program.getGlobalBlock();
|
||||
|
||||
TraceEvent srcEvent =
|
||||
Tracer.canTrace() ? Tracer.start(DartEventType.JS_SOURCE_GEN, "src", srcName, "name",
|
||||
name) : null;
|
||||
try {
|
||||
srcGenerator = new JsSourceGenerationVisitor(out);
|
||||
|
||||
srcGenerator.accept(globalBlock);
|
||||
w = context.getArtifactWriter(src, name, EXTENSION_JS);
|
||||
try {
|
||||
w.write(out.toString());
|
||||
failed = false;
|
||||
} finally {
|
||||
Closeables.close(w, failed);
|
||||
}
|
||||
} finally {
|
||||
Tracer.end(srcEvent);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,253 +0,0 @@
|
||||
// Copyright 2011, the Dart project authors. All rights reserved.
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following
|
||||
// disclaimer in the documentation and/or other materials provided
|
||||
// with the distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived
|
||||
// from this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
package com.google.dart.compiler.backend.js;
|
||||
|
||||
import com.google.dart.compiler.InternalCompilerException;
|
||||
import com.google.dart.compiler.backend.js.ast.JsArrayAccess;
|
||||
import com.google.dart.compiler.backend.js.ast.JsArrayLiteral;
|
||||
import com.google.dart.compiler.backend.js.ast.JsBinaryOperation;
|
||||
import com.google.dart.compiler.backend.js.ast.JsBooleanLiteral;
|
||||
import com.google.dart.compiler.backend.js.ast.JsConditional;
|
||||
import com.google.dart.compiler.backend.js.ast.JsContext;
|
||||
import com.google.dart.compiler.backend.js.ast.JsExpression;
|
||||
import com.google.dart.compiler.backend.js.ast.JsFunction;
|
||||
import com.google.dart.compiler.backend.js.ast.JsInvocation;
|
||||
import com.google.dart.compiler.backend.js.ast.JsName;
|
||||
import com.google.dart.compiler.backend.js.ast.JsNameRef;
|
||||
import com.google.dart.compiler.backend.js.ast.JsNew;
|
||||
import com.google.dart.compiler.backend.js.ast.JsNullLiteral;
|
||||
import com.google.dart.compiler.backend.js.ast.JsNumberLiteral;
|
||||
import com.google.dart.compiler.backend.js.ast.JsObjectLiteral;
|
||||
import com.google.dart.compiler.backend.js.ast.JsPostfixOperation;
|
||||
import com.google.dart.compiler.backend.js.ast.JsPrefixOperation;
|
||||
import com.google.dart.compiler.backend.js.ast.JsPropertyInitializer;
|
||||
import com.google.dart.compiler.backend.js.ast.JsRegExp;
|
||||
import com.google.dart.compiler.backend.js.ast.JsStringLiteral;
|
||||
import com.google.dart.compiler.backend.js.ast.JsThisRef;
|
||||
import com.google.dart.compiler.backend.js.ast.JsVisitor;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Stack;
|
||||
|
||||
/**
|
||||
* Implements actual cloning logic. We rely on the JsExpressions to provide
|
||||
* traversal logic. The {@link #stack} field is used to accumulate
|
||||
* already-cloned JsExpression instances. One gotcha that falls out of this is
|
||||
* that argument lists are on the stack in reverse order, so lists should be
|
||||
* constructed via inserts, rather than appends.
|
||||
*/
|
||||
public class Cloner extends JsVisitor {
|
||||
protected final Stack<JsExpression> stack = new Stack<JsExpression>();
|
||||
private boolean successful = true;
|
||||
|
||||
/**
|
||||
* @param expression
|
||||
* @return Return a clone of the expression tree
|
||||
*/
|
||||
public static JsExpression clone(JsExpression expression) {
|
||||
Cloner c = new Cloner();
|
||||
c.accept(expression);
|
||||
return c.getExpression();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void endVisit(JsArrayAccess x, JsContext ctx) {
|
||||
JsArrayAccess newExpression = new JsArrayAccess();
|
||||
newExpression.setIndexExpr(stack.pop());
|
||||
newExpression.setArrayExpr(stack.pop());
|
||||
stack.push(newExpression);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void endVisit(JsArrayLiteral x, JsContext ctx) {
|
||||
JsArrayLiteral toReturn = new JsArrayLiteral();
|
||||
List<JsExpression> expressions = toReturn.getExpressions();
|
||||
int size = x.getExpressions().size();
|
||||
while (size-- > 0) {
|
||||
expressions.add(0, stack.pop());
|
||||
}
|
||||
stack.push(toReturn);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void endVisit(JsBinaryOperation x, JsContext ctx) {
|
||||
JsBinaryOperation toReturn = new JsBinaryOperation(x.getOperator());
|
||||
toReturn.setArg2(stack.pop());
|
||||
toReturn.setArg1(stack.pop());
|
||||
stack.push(toReturn);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void endVisit(JsBooleanLiteral x, JsContext ctx) {
|
||||
stack.push(x);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void endVisit(JsConditional x, JsContext ctx) {
|
||||
JsConditional toReturn = new JsConditional();
|
||||
toReturn.setElseExpression(stack.pop());
|
||||
toReturn.setThenExpression(stack.pop());
|
||||
toReturn.setTestExpression(stack.pop());
|
||||
stack.push(toReturn);
|
||||
}
|
||||
|
||||
/**
|
||||
* The only functions that would get be visited are those being used as
|
||||
* first-class objects.
|
||||
*/
|
||||
@Override
|
||||
public void endVisit(JsFunction x, JsContext ctx) {
|
||||
// Set a flag to indicate that we cannot continue, and push a null so
|
||||
// we don't run out of elements on the stack.
|
||||
successful = false;
|
||||
stack.push(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Cloning the invocation allows us to modify it without damaging other call
|
||||
* sites.
|
||||
*/
|
||||
@Override
|
||||
public void endVisit(JsInvocation x, JsContext ctx) {
|
||||
JsInvocation toReturn = new JsInvocation();
|
||||
List<JsExpression> params = toReturn.getArguments();
|
||||
int size = x.getArguments().size();
|
||||
while (size-- > 0) {
|
||||
params.add(0, stack.pop());
|
||||
}
|
||||
toReturn.setQualifier(stack.pop());
|
||||
stack.push(toReturn);
|
||||
}
|
||||
|
||||
/**
|
||||
* Do a deep clone of a JsNameRef. Because JsNameRef chains are shared
|
||||
* throughout the AST, you can't just go and change their qualifiers when
|
||||
* re-writing an invocation.
|
||||
*/
|
||||
@Override
|
||||
public void endVisit(JsNameRef x, JsContext ctx) {
|
||||
JsNameRef toReturn;
|
||||
JsName name = x.getName();
|
||||
if (name != null) {
|
||||
toReturn = new JsNameRef(name);
|
||||
} else {
|
||||
toReturn = new JsNameRef(x.getIdent());
|
||||
}
|
||||
|
||||
if (x.getQualifier() != null) {
|
||||
toReturn.setQualifier(stack.pop());
|
||||
}
|
||||
stack.push(toReturn);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void endVisit(JsNew x, JsContext ctx) {
|
||||
int size = x.getArguments().size();
|
||||
List<JsExpression> arguments = new ArrayList<JsExpression>(size);
|
||||
while (size-- > 0) {
|
||||
arguments.add(0, stack.pop());
|
||||
}
|
||||
JsNew toReturn = new JsNew(stack.pop());
|
||||
toReturn.getArguments().addAll(arguments);
|
||||
stack.push(toReturn);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void endVisit(JsNullLiteral x, JsContext ctx) {
|
||||
stack.push(x);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void endVisit(JsNumberLiteral x, JsContext ctx) {
|
||||
stack.push(x);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void endVisit(JsObjectLiteral x, JsContext ctx) {
|
||||
JsObjectLiteral toReturn = new JsObjectLiteral();
|
||||
List<JsPropertyInitializer> inits = toReturn.getPropertyInitializers();
|
||||
|
||||
int size = x.getPropertyInitializers().size();
|
||||
while (size-- > 0) {
|
||||
/*
|
||||
* JsPropertyInitializers are the only non-JsExpression objects that we
|
||||
* care about, so we just go ahead and create the objects in the loop,
|
||||
* rather than expecting it to be on the stack and having to perform
|
||||
* narrowing casts at all stack.pop() invocations.
|
||||
*/
|
||||
JsPropertyInitializer newInit = new JsPropertyInitializer();
|
||||
newInit.setValueExpr(stack.pop());
|
||||
newInit.setLabelExpr(stack.pop());
|
||||
|
||||
inits.add(0, newInit);
|
||||
}
|
||||
stack.push(toReturn);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void endVisit(JsPostfixOperation x, JsContext ctx) {
|
||||
JsPostfixOperation toReturn = new JsPostfixOperation(x.getOperator());
|
||||
toReturn.setArg(stack.pop());
|
||||
stack.push(toReturn);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void endVisit(JsPrefixOperation x, JsContext ctx) {
|
||||
JsPrefixOperation toReturn = new JsPrefixOperation(x.getOperator());
|
||||
toReturn.setArg(stack.pop());
|
||||
stack.push(toReturn);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void endVisit(JsRegExp x, JsContext ctx) {
|
||||
stack.push(x);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void endVisit(JsStringLiteral x, JsContext ctx) {
|
||||
stack.push(x);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void endVisit(JsThisRef x, JsContext ctx) {
|
||||
stack.push(new JsThisRef());
|
||||
}
|
||||
|
||||
public JsExpression getExpression() {
|
||||
return (successful && checkStack()) ? stack.peek() : null;
|
||||
}
|
||||
|
||||
private boolean checkStack() {
|
||||
if (stack.size() > 1) {
|
||||
throw new InternalCompilerException("Too many expressions on stack");
|
||||
}
|
||||
|
||||
return stack.size() == 1;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,143 +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.dart.compiler.parser.Token;
|
||||
import com.google.dart.compiler.resolver.ClassElement;
|
||||
import com.google.dart.compiler.resolver.Element;
|
||||
import com.google.dart.compiler.resolver.FieldElement;
|
||||
import com.google.dart.compiler.resolver.LibraryElement;
|
||||
import com.google.dart.compiler.resolver.MethodElement;
|
||||
|
||||
/**
|
||||
* Mangles dart identifiers so that they don't conflict with JavaScript identifiers.
|
||||
* This complements the JsScope infrastructure. The JsScope infrastructure deals with obfuscatable
|
||||
* identifiers, whereas the DartMangler deals with non-obfuscatable identifiers.
|
||||
*/
|
||||
public interface DartMangler {
|
||||
public static final String NEGATE_OPERATOR_NAME = "negate";
|
||||
|
||||
/**
|
||||
* Mangles the given className, so that it does not clash with any global variable or other
|
||||
* mangled identifiers.
|
||||
* @return a String that identifies the class, and does not clash with any global variable.
|
||||
*/
|
||||
public String mangleClassName(ClassElement classElement);
|
||||
@Deprecated
|
||||
public String mangleClassNameHack(LibraryElement library, String str);
|
||||
|
||||
/**
|
||||
* Mangles the given constructor, so that it does not clash with any built-in JS property,
|
||||
* initializers, factories or other mangled fields.<br/>
|
||||
* The given LibraryElement is used if the constructor is library private.
|
||||
* @return a String that identifies the constructor, and does not clash with any global variable.
|
||||
*/
|
||||
public String mangleConstructor(String constructorName, LibraryElement currentLibrary);
|
||||
|
||||
/**
|
||||
* Returns a mangled identifier for the given method.
|
||||
*/
|
||||
public String mangleNativeMethod(MethodElement methodElement);
|
||||
|
||||
/**
|
||||
* Mangles the given initializer, so that it does not clash with any built-in JS property,
|
||||
* factories, constructors or other mangled fields.<br/>
|
||||
* The given LibraryElement is used if the initializer is library private.
|
||||
* @return a String that identifies the initializer, and does not clash with any global variable.
|
||||
*/
|
||||
public String createInitializerSyntax(String constructorName, LibraryElement currentLibrary);
|
||||
|
||||
/**
|
||||
* Mangles the given factory, so that it does not clash with any built-in JS property,
|
||||
* constructors, initializers or other mangled fields.<br/>
|
||||
* Note that factories may be unrelated to the containing class. A class <code>A</code> can
|
||||
* have a factory method returning an instance of class <code>B</code>. In this case
|
||||
* <code>className</code> equals <code>B</code>.<br>
|
||||
* The given LibraryElement is used if the factory is library private.
|
||||
*
|
||||
* @return a String that identifies the factory, and does not clash with any global variable.
|
||||
*/
|
||||
public String createFactorySyntax(String className, String constructorName,
|
||||
LibraryElement currentlibrary);
|
||||
|
||||
|
||||
/**
|
||||
* <p>Returns a name for the given closure. The returned name does not
|
||||
* clash with any global variable or other mangled identifiers.</p>
|
||||
* The closure is identified by the closureIdentifier. Manglers are allowed to discard the
|
||||
* closureName completely.
|
||||
*
|
||||
* @param closureIdentifier must be a valid identifier of the form [a-zA-Z]+[0-9]*
|
||||
* @param closureName the closures short readable name. May be null.
|
||||
* @return a String that identifies the hoisted closure, and does not clash with any global
|
||||
* variable.
|
||||
*/
|
||||
public String createHoistedFunctionName(Element holder,
|
||||
Element classMemberElement,
|
||||
String closureIdentifier,
|
||||
String closureName);
|
||||
|
||||
/**
|
||||
* Mangles the given field, so that it does not clash with any built-in JS property or other
|
||||
* mangled fields or methods.<br/>
|
||||
* The given LibraryElement is used if the field is library private.
|
||||
* @return a String that identifies the member, and does not clash with built-in JS properties.
|
||||
*/
|
||||
public String mangleField(FieldElement field, LibraryElement currentLibrary);
|
||||
|
||||
/**
|
||||
* Mangles the given method, so that it does not clash with any built-in JS property or other
|
||||
* mangled fields or methods.<br/>
|
||||
* The given LibraryElement is used if the method is library private.
|
||||
* @return a String that identifies the member, and does not clash with built-in JS properties.
|
||||
*/
|
||||
public String mangleMethod(MethodElement method, LibraryElement currentLibrary);
|
||||
public String mangleMethod(String methodName, LibraryElement currentLibrary);
|
||||
|
||||
/**
|
||||
* Mangles the given method to its $named form.
|
||||
* @return a String that identifies the named form of the member.
|
||||
*/
|
||||
public String mangleNamedMethod(MethodElement method, LibraryElement currentLibrary);
|
||||
public String mangleNamedMethod(String methodName, LibraryElement currentLibrary);
|
||||
|
||||
/**
|
||||
* Mangles the given method to its _$lookupRTT form.
|
||||
* @return a String that identifies the named form of the member.
|
||||
*/
|
||||
public String mangleRttLookupMethod(MethodElement method, LibraryElement currentLibrary);
|
||||
public String mangleRttLookupMethod(String methodName, LibraryElement currentLibrary);
|
||||
|
||||
/**
|
||||
* Mangles the given method, so that it does not clash with any built-in JS property or other
|
||||
* mangled fields or methods. This method is different than mangleMethod, as it returns
|
||||
* the fully qualified mangled name.
|
||||
* @return a String that identifies the entry, and does not clash with built-in JS properties.
|
||||
*/
|
||||
public String mangleEntryPoint(MethodElement method, LibraryElement library);
|
||||
|
||||
/**
|
||||
* @return the JavaScript property identifier for the given operation.
|
||||
*/
|
||||
public String createOperatorSyntax(Token token);
|
||||
|
||||
/**
|
||||
* @return the JavaScript property identifier for the given operation.
|
||||
*/
|
||||
public String createOperatorSyntax(String operation);
|
||||
|
||||
/**
|
||||
* @return the JavaScript getter property for the given member.
|
||||
*/
|
||||
public String createGetterSyntax(String member, LibraryElement currentLibrary);
|
||||
public String createGetterSyntax(FieldElement member, LibraryElement currentLibrary);
|
||||
public String createGetterSyntax(MethodElement member, LibraryElement currentLibrary);
|
||||
|
||||
/**
|
||||
* @return the JavaScript setter property for the given member.
|
||||
*/
|
||||
public String createSetterSyntax(String member, LibraryElement currentLibrary);
|
||||
public String createSetterSyntax(FieldElement member, LibraryElement currentLibrary);
|
||||
}
|
||||
@@ -1,424 +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.collect.ImmutableSet;
|
||||
import com.google.dart.compiler.InternalCompilerException;
|
||||
import com.google.dart.compiler.ast.LibraryUnit;
|
||||
import com.google.dart.compiler.parser.Token;
|
||||
import com.google.dart.compiler.resolver.ClassElement;
|
||||
import com.google.dart.compiler.resolver.Element;
|
||||
import com.google.dart.compiler.resolver.ElementKind;
|
||||
import com.google.dart.compiler.resolver.FieldElement;
|
||||
import com.google.dart.compiler.resolver.LibraryElement;
|
||||
import com.google.dart.compiler.resolver.MethodElement;
|
||||
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Mangles classes and members (including constructors and operators).
|
||||
* These must be accessible from outside the compilation unit and must therefore have a
|
||||
* predictable mangling.
|
||||
*
|
||||
* <p>Functions, constructors and initializers to become top-level functions. They
|
||||
* cannot conflict with other members, but must not conflict with predefined JavaScript globals.
|
||||
*
|
||||
* <p>Other members (fields, operators, methods) are always accessed through an object. The mangler
|
||||
* only needs to guard against conflicts with predefined JavaScript properties (like "prototype"
|
||||
* and "__proto__").
|
||||
*/
|
||||
public class DollarMangler implements DartMangler {
|
||||
// TODO(floitsch): get rid of the blacklisted libraries.
|
||||
// Libraries, where class-names must not include the library-name.
|
||||
private static final Set<String> BLACKLISTED_LIBRARIES = ImmutableSet.<String>of("corelib",
|
||||
"corelib_impl", "dom");
|
||||
|
||||
// Helpers for getters/setters/operator name mangling.
|
||||
private static final String OPERATOR_SUFFIX = "$operator";
|
||||
private static final String GETTER_SUFFIX = "$getter";
|
||||
private static final String SETTER_SUFFIX = "$setter";
|
||||
private static final String FIELD_SUFFIX = "$field";
|
||||
private static final String METHOD_SUFFIX = "$member";
|
||||
private static final String NAMED_SUFFIX = "$named";
|
||||
private static final String CONSTRUCTOR_SUFFIX = "$Constructor";
|
||||
private static final String FACTORY_SUFFIX = "$Factory";
|
||||
private static final String INITIALIZER_SUFFIX = "$Initializer";
|
||||
|
||||
private static final String CLASS_SUFFIX = "$Dart";
|
||||
private static final String HOISTED_METHOD_SUFFIX = "$Hoisted";
|
||||
private static final String HOISTED_OPERATOR_SUFFIX = "$HoistedOperator";
|
||||
private static final String HOISTED_CONSTRUCTOR_SUFFIX = "$HoistedConstructor";
|
||||
private static final String HOISTED_STATIC_SUFFIX = "$HoistedStatic";
|
||||
private static final String DYNAMIC_CLASS_NAME = "$_Dynamic_";
|
||||
private static final String RTT_LOOKUP_NAME = "_$lookupRTT";
|
||||
|
||||
private static final String NATIVE_PREFIX = "native_";
|
||||
|
||||
private boolean isLibraryPrivate(String id) {
|
||||
return (id.length() > 0) && (id.charAt(0) == '_');
|
||||
}
|
||||
|
||||
private String attachSuffix(String name, String suffix,
|
||||
boolean isLibraryPrivate, LibraryElement currentLibrary) {
|
||||
if (isLibraryPrivate) {
|
||||
return name + '$' + mangleLibraryName(currentLibrary) + suffix + '_';
|
||||
}
|
||||
return name + suffix;
|
||||
}
|
||||
|
||||
private String attachSuffix(String name, String suffix, LibraryElement currentLibrary) {
|
||||
return attachSuffix(name, suffix, isLibraryPrivate(name), currentLibrary);
|
||||
}
|
||||
|
||||
private String mangleLibraryName(LibraryElement element) {
|
||||
LibraryUnit library = element.getLibraryUnit();
|
||||
if (isInBlacklist(library)) {
|
||||
return "";
|
||||
}
|
||||
|
||||
// TODO(floitsch): Replace this libraryName + md5(source) with something more enhanced.
|
||||
|
||||
String libName = library.getName();
|
||||
if (libName == null || libName.isEmpty()) {
|
||||
libName = "unnamed";
|
||||
}
|
||||
|
||||
// If the libraryName is a path, cut off everything before the last slash.
|
||||
int nameStart = libName.lastIndexOf('/') + 1;
|
||||
|
||||
// add space for md5 + trailing '$' (and possible leading 'l').
|
||||
StringBuilder sb = new StringBuilder(libName.length() + 8);
|
||||
|
||||
// see if we have a leading number, in which case need to prepend an alpha char
|
||||
final char startChar = libName.charAt(nameStart);
|
||||
if ('0' <= startChar && startChar <= '9') {
|
||||
sb.append('l');
|
||||
}
|
||||
sb.append(libName.substring(nameStart));
|
||||
|
||||
// Replace all non-word chars ([^a-z_A-Z0-9]) with "_".
|
||||
replaceNonWordChars(sb);
|
||||
|
||||
MessageDigest md;
|
||||
try {
|
||||
md = MessageDigest.getInstance("MD5");
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
throw new AssertionError("Could not find MD5 digest");
|
||||
}
|
||||
byte[] md5 = md.digest(library.getSource().getUri().toString().getBytes());
|
||||
// Only use the first 6 hex characters of the md5.
|
||||
for (int i = 0; i < 3; i++) {
|
||||
sb.append(Integer.toHexString((md5[i] & 0xf0) >> 4));
|
||||
sb.append(Integer.toHexString(md5[i] & 0xf));
|
||||
}
|
||||
|
||||
sb.append("$");
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/*
|
||||
* Replace all non-word characters with an underscore
|
||||
*/
|
||||
private void replaceNonWordChars(StringBuilder sb) {
|
||||
/*
|
||||
* This code is implemented as a more efficient implementation than using
|
||||
* String.replaceAll("\\W", "_")
|
||||
*/
|
||||
final int len = sb.length();
|
||||
for (int idx = 0; idx < len; idx++) {
|
||||
final char ch = sb.charAt(idx);
|
||||
if (!((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') || (ch >= '0' && ch <= '9'))) {
|
||||
sb.setCharAt(idx, '_');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String mangleClassName(ClassElement classElement) {
|
||||
String className = !classElement.isDynamic() ? classElement.getName() : DYNAMIC_CLASS_NAME;
|
||||
return mangleClassNameHack(classElement.getLibrary(), className);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Deprecated
|
||||
public String mangleClassNameHack(LibraryElement library, String className) {
|
||||
String libraryToken = library == null ? "" : mangleLibraryName(library);
|
||||
return libraryToken + className + CLASS_SUFFIX;
|
||||
}
|
||||
|
||||
private boolean isInBlacklist(LibraryUnit libraryUnit) {
|
||||
if (libraryUnit.getName() == null) {
|
||||
return false;
|
||||
}
|
||||
if (BLACKLISTED_LIBRARIES.contains(libraryUnit.getName())) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Libraries that use the new syntax (with no name) will have a URI for a name, e.g.:
|
||||
// file://blah/blah/coreimpl.dart
|
||||
for (String name : BLACKLISTED_LIBRARIES) {
|
||||
if (libraryUnit.getName().endsWith(name)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String mangleConstructor(String constructorName, LibraryElement currentLibrary) {
|
||||
return attachSuffix(constructorName, CONSTRUCTOR_SUFFIX, currentLibrary);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String createInitializerSyntax(String constructorName, LibraryElement currentLibrary) {
|
||||
return attachSuffix(constructorName, INITIALIZER_SUFFIX, currentLibrary);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String createFactorySyntax(String className, String constructor,
|
||||
LibraryElement currentLibrary) {
|
||||
// We can't just return the constructor + suffix.
|
||||
// A class might have factories for different classes.
|
||||
String factoryName;
|
||||
className = className.equals("<dynamic>") ? DYNAMIC_CLASS_NAME : className;
|
||||
if (constructor.equals("")) {
|
||||
factoryName = className + "$";
|
||||
} else {
|
||||
factoryName = className + "$" + constructor + "$" + className.length();
|
||||
}
|
||||
|
||||
return attachSuffix(factoryName, FACTORY_SUFFIX, isLibraryPrivate(constructor), currentLibrary);
|
||||
}
|
||||
|
||||
private boolean containsDollar(String str) {
|
||||
return str.indexOf('$') >= 0;
|
||||
}
|
||||
|
||||
private String createHoistedFunctionName(String holderName,
|
||||
String elementName,
|
||||
String closureIdentifier,
|
||||
String closureName,
|
||||
String suffix) {
|
||||
assert closureIdentifier.indexOf('$') == -1;
|
||||
boolean containsDollar = containsDollar(holderName)
|
||||
|| containsDollar(elementName)
|
||||
|| (closureName != null && containsDollar(closureName));
|
||||
String result;
|
||||
if (closureName != null) {
|
||||
// Return a mangled id of the form:
|
||||
// class$method$id$closure$$Hoisted, or
|
||||
// class$method$id$closure$12_23_45$Hoisted (if any of the strings contains dollars).
|
||||
result = holderName + "$" + elementName + "$" + closureIdentifier + "$" + closureName + "$";
|
||||
if (containsDollar) {
|
||||
result += holderName.length() + "_" + elementName.length() + "_"
|
||||
+ closureIdentifier.length();
|
||||
}
|
||||
} else {
|
||||
// Return a mangled id of the form:
|
||||
// class$method$id$$Hoisted, or
|
||||
// class$method$id$12_23$Hoisted (if any of the strings contains dollars).
|
||||
result = holderName + "$" + elementName + "$" + closureIdentifier + "$";
|
||||
if (containsDollar) {
|
||||
result += holderName.length() + "_" + holderName.length();
|
||||
}
|
||||
}
|
||||
return result + suffix;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String createHoistedFunctionName(Element holder,
|
||||
Element element,
|
||||
String closureIdentifier,
|
||||
String closureName) {
|
||||
String holderName = "";
|
||||
switch (ElementKind.of(holder)) {
|
||||
case CLASS:
|
||||
holderName = mangleClassName((ClassElement) holder);
|
||||
break;
|
||||
|
||||
case LIBRARY:
|
||||
holderName = mangleLibraryName((LibraryElement) holder);
|
||||
break;
|
||||
}
|
||||
|
||||
String name = element.getName();
|
||||
String suffix;
|
||||
switch (ElementKind.of(element)) {
|
||||
case METHOD:
|
||||
if (element.getModifiers().isOperator()) {
|
||||
suffix = HOISTED_OPERATOR_SUFFIX;
|
||||
if (!name.equals(NEGATE_OPERATOR_NAME)) {
|
||||
name = Token.lookup(name).name();
|
||||
}
|
||||
} else {
|
||||
suffix = HOISTED_METHOD_SUFFIX;
|
||||
}
|
||||
break;
|
||||
|
||||
case CONSTRUCTOR:
|
||||
suffix = HOISTED_CONSTRUCTOR_SUFFIX;
|
||||
break;
|
||||
|
||||
default:
|
||||
// Otherwise we are in a static initializer.
|
||||
suffix = HOISTED_STATIC_SUFFIX;
|
||||
}
|
||||
return createHoistedFunctionName(holderName, name, closureIdentifier, closureName,
|
||||
suffix);
|
||||
}
|
||||
|
||||
private String createFieldOrMethodBaseName(Element field, boolean accessor) {
|
||||
String prefix = "";
|
||||
Element enclosing = field.getEnclosingElement();
|
||||
if (ElementKind.of(enclosing).equals(ElementKind.LIBRARY)) {
|
||||
prefix = mangleLibraryName((LibraryElement) enclosing);
|
||||
} else if (!accessor && field.getModifiers().isStatic()) {
|
||||
prefix = mangleClassName((ClassElement) enclosing);
|
||||
}
|
||||
return prefix + field.getName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String mangleField(FieldElement field, LibraryElement currentLibrary) {
|
||||
return attachSuffix(createFieldOrMethodBaseName(field, false), FIELD_SUFFIX,
|
||||
isLibraryPrivate(field.getName()), currentLibrary);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String mangleMethod(MethodElement method, LibraryElement currentLibrary) {
|
||||
String methodName = method.getName();
|
||||
if (method.getModifiers().isOperator()) {
|
||||
methodName = createOperatorSyntax(methodName);
|
||||
} else if (method.getModifiers().isGetter()) {
|
||||
methodName = createGetterSyntax(methodName, currentLibrary);
|
||||
} else if (method.getModifiers().isSetter()) {
|
||||
methodName = createSetterSyntax(methodName, currentLibrary);
|
||||
} else {
|
||||
methodName = attachSuffix(methodName, METHOD_SUFFIX, currentLibrary);
|
||||
}
|
||||
|
||||
String prefix = "";
|
||||
if (ElementKind.of(method.getEnclosingElement()).equals(ElementKind.LIBRARY)) {
|
||||
prefix = mangleLibraryName((LibraryElement) method.getEnclosingElement());
|
||||
}
|
||||
return prefix + methodName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String mangleNamedMethod(MethodElement method, LibraryElement currentLibrary) {
|
||||
// There can be no named shims for operators, getters, or setters.
|
||||
String methodName = method.getName();
|
||||
methodName = attachSuffix(methodName, NAMED_SUFFIX, currentLibrary);
|
||||
|
||||
String prefix = "";
|
||||
if (ElementKind.of(method.getEnclosingElement()).equals(ElementKind.LIBRARY)) {
|
||||
prefix = mangleLibraryName((LibraryElement) method.getEnclosingElement());
|
||||
}
|
||||
return prefix + methodName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String mangleNamedMethod(String methodName, LibraryElement currentLibrary) {
|
||||
return attachSuffix(methodName, NAMED_SUFFIX, currentLibrary);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String mangleEntryPoint(MethodElement method, LibraryElement library) {
|
||||
Element holder = method.getEnclosingElement();
|
||||
switch (ElementKind.of(holder)) {
|
||||
case CLASS:
|
||||
String mangledClassName = mangleClassName((ClassElement) holder);
|
||||
return mangledClassName + "." + mangleMethod(method.getName(), library);
|
||||
|
||||
case LIBRARY:
|
||||
return mangleMethod(method, library);
|
||||
}
|
||||
throw new InternalCompilerException("Unknown entry point kind" + method);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String createGetterSyntax(FieldElement field, LibraryElement currentLibrary) {
|
||||
return attachSuffix(createFieldOrMethodBaseName(field, true), GETTER_SUFFIX,
|
||||
isLibraryPrivate(field.getName()), currentLibrary);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String createGetterSyntax(MethodElement field, LibraryElement currentLibrary) {
|
||||
return attachSuffix(createFieldOrMethodBaseName(field, true), GETTER_SUFFIX,
|
||||
isLibraryPrivate(field.getName()), currentLibrary);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String createSetterSyntax(FieldElement field, LibraryElement currentLibrary) {
|
||||
return attachSuffix(createFieldOrMethodBaseName(field, true), SETTER_SUFFIX,
|
||||
isLibraryPrivate(field.getName()), currentLibrary);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String mangleMethod(String methodName, LibraryElement currentLibrary) {
|
||||
return attachSuffix(methodName, METHOD_SUFFIX, currentLibrary);
|
||||
}
|
||||
|
||||
private static String getNegateOperator() {
|
||||
return NEGATE_OPERATOR_NAME + OPERATOR_SUFFIX;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String createOperatorSyntax(Token token) {
|
||||
return token.name() + OPERATOR_SUFFIX;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String createOperatorSyntax(String operation) {
|
||||
if (operation.equals(NEGATE_OPERATOR_NAME)) {
|
||||
return getNegateOperator();
|
||||
}
|
||||
return Token.lookup(operation).name() + OPERATOR_SUFFIX;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String createGetterSyntax(String member, LibraryElement currentLibrary) {
|
||||
return attachSuffix(member, GETTER_SUFFIX, currentLibrary);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String createSetterSyntax(String member, LibraryElement currentLibrary) {
|
||||
return attachSuffix(member, SETTER_SUFFIX, currentLibrary);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String mangleNativeMethod(MethodElement element) {
|
||||
String elementName = element.getName();
|
||||
String encodedName = null;
|
||||
if (element.getModifiers().isOperator()) {
|
||||
if ("negate".equals(elementName)) {
|
||||
encodedName = elementName;
|
||||
} else {
|
||||
encodedName = Token.lookup(elementName).name();
|
||||
}
|
||||
} else if (element.getModifiers().isGetter()) {
|
||||
encodedName = "get$" + elementName;
|
||||
} else if (element.getModifiers().isSetter()) {
|
||||
encodedName = "set$" + elementName;
|
||||
} else {
|
||||
encodedName = elementName;
|
||||
}
|
||||
String holderName = element.getEnclosingElement().getName();
|
||||
return NATIVE_PREFIX + holderName + "_" + encodedName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String mangleRttLookupMethod(MethodElement method, LibraryElement currentLibrary) {
|
||||
return mangleNamedMethod(method, currentLibrary) + RTT_LOOKUP_NAME;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String mangleRttLookupMethod(String methodName, LibraryElement currentLibrary) {
|
||||
return mangleNamedMethod(methodName, currentLibrary) + RTT_LOOKUP_NAME;
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,214 +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.dart.compiler.ast.DartClass;
|
||||
import com.google.dart.compiler.ast.DartContext;
|
||||
import com.google.dart.compiler.ast.DartField;
|
||||
import com.google.dart.compiler.ast.DartFunction;
|
||||
import com.google.dart.compiler.ast.DartFunctionExpression;
|
||||
import com.google.dart.compiler.ast.DartLabel;
|
||||
import com.google.dart.compiler.ast.DartMethodDefinition;
|
||||
import com.google.dart.compiler.ast.DartParameter;
|
||||
import com.google.dart.compiler.ast.DartVariable;
|
||||
import com.google.dart.compiler.backend.js.ast.JsFunction;
|
||||
import com.google.dart.compiler.backend.js.ast.JsName;
|
||||
import com.google.dart.compiler.backend.js.ast.JsScope;
|
||||
import com.google.dart.compiler.common.Symbol;
|
||||
import com.google.dart.compiler.resolver.ConstructorElement;
|
||||
import com.google.dart.compiler.resolver.Elements;
|
||||
import com.google.dart.compiler.resolver.FieldElement;
|
||||
import com.google.dart.compiler.resolver.LibraryElement;
|
||||
import com.google.dart.compiler.resolver.MethodElement;
|
||||
|
||||
import java.util.Deque;
|
||||
import java.util.LinkedList;
|
||||
|
||||
/**
|
||||
* This visitor generates Javascript scopes and names for all the Dart nodes, filling in the
|
||||
* node->name map in 'names'.
|
||||
*/
|
||||
class GenerateNamesAndScopes extends NormalizedVisitor {
|
||||
|
||||
/**
|
||||
* A JsScope used to manage fields and methods. A MemberJsScope can become
|
||||
* parentless.
|
||||
*/
|
||||
private static class MemberJsScope extends JsScope {
|
||||
private MemberJsScope(JsScope parent, String description) {
|
||||
super(parent, description);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void detachFromParent() {
|
||||
super.detachFromParent();
|
||||
}
|
||||
}
|
||||
|
||||
private final Deque<JsScope> scopes = new LinkedList<JsScope>();
|
||||
private DartClass currentClass = null;
|
||||
private int labelUniqifier = 0; // to resolve label name collisions.
|
||||
private int varUniqifier = 0; // to resolve variable name collisions.
|
||||
|
||||
private final TranslationContext translationContext;
|
||||
private final LibraryElement unitLibrary;
|
||||
|
||||
private JsScope getGlobalScope() {
|
||||
return translationContext.getProgram().getScope();
|
||||
}
|
||||
|
||||
public GenerateNamesAndScopes(TranslationContext data, LibraryElement unitLibrary) {
|
||||
this.translationContext = data;
|
||||
this.unitLibrary = unitLibrary;
|
||||
scopes.push(getGlobalScope());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean visit(DartClass x, DartContext ctx) {
|
||||
assert currentClass == null;
|
||||
// Global variables are declared lazily. We don't declare the class now.
|
||||
currentClass = x;
|
||||
// We add the member scope into the hierarchy, so that the resolution works on unqualified
|
||||
// identifiers. Once the resolution is done, we can rip out the scope from the hierarchy.
|
||||
scopes.push(new MemberJsScope(scopes.peek(), x.getClassName()));
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean visit(DartField x, DartContext ctx) {
|
||||
FieldElement element = x.getSymbol();
|
||||
String mangledFieldName = translationContext.getMangler().mangleField(element, unitLibrary);
|
||||
JsName fieldName = declare(x.getSymbol(), mangledFieldName, element.getName());
|
||||
fieldName.setObfuscatable(false);
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean generateConstructorName(DartMethodDefinition x) {
|
||||
ConstructorElement element = (ConstructorElement) x.getSymbol();
|
||||
String name = translationContext.getMangler().mangleConstructor(element.getName(), unitLibrary);
|
||||
JsName jsName = function(x.getSymbol(), name, element.getName(), x.getFunction());
|
||||
// Constructors are globally accessible.
|
||||
jsName.setObfuscatable(false);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean visit(DartMethodDefinition x, DartContext ctx) {
|
||||
MethodElement element = x.getSymbol();
|
||||
if (Elements.isNonFactoryConstructor(element)) {
|
||||
return generateConstructorName(x);
|
||||
}
|
||||
if (x.getModifiers().isFactory()) {
|
||||
String className = ((ConstructorElement) element).getConstructorType().getName();
|
||||
String name = translationContext.getMangler().createFactorySyntax(className, element.getName(), unitLibrary);
|
||||
JsName jsName = function(x.getSymbol(), name, element.getName(), x.getFunction());
|
||||
// Factories are globally accessible.
|
||||
jsName.setObfuscatable(false);
|
||||
return true;
|
||||
}
|
||||
|
||||
String mangledName = translationContext.getMangler().mangleMethod(element, unitLibrary);
|
||||
JsName methodName = function(x.getSymbol(), mangledName, element.getName(), x.getFunction());
|
||||
methodName.setObfuscatable(false);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean visit(DartFunctionExpression x, DartContext ctx) {
|
||||
function(x.getSymbol(), x.getFunctionName(), x.getFunctionName(), x.getFunction());
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean visit(DartParameter x, DartContext ctx) {
|
||||
// TODO(ngeoffray): A parameter in a function type does not have a symbol.
|
||||
if (x.getSymbol() != null) {
|
||||
declareExclusively(x.getSymbol(), x.getParameterName());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean visit(DartVariable x, DartContext ctx) {
|
||||
declareExclusively(x.getSymbol(), x.getVariableName());
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean visit(DartLabel x, DartContext ctx) {
|
||||
declareExclusively(x.getSymbol(), String.format("L%X", labelUniqifier++));
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void endVisit(DartMethodDefinition x, DartContext ctx) {
|
||||
scopes.pop();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void endVisit(DartFunctionExpression x, DartContext ctx) {
|
||||
scopes.pop();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void endVisit(DartClass x, DartContext ctx) {
|
||||
currentClass = null;
|
||||
// Rip out the member scope. Members are always accessed through an object and don't clash
|
||||
// with other variables.
|
||||
GenerateNamesAndScopes.MemberJsScope memberScope = (GenerateNamesAndScopes.MemberJsScope) scopes.pop();
|
||||
memberScope.rebaseChildScopes(memberScope.getParent());
|
||||
memberScope.detachFromParent();
|
||||
translationContext.getMemberScopes().put(x.getSymbol(), memberScope);
|
||||
}
|
||||
|
||||
private JsName function(Symbol symbol, String name, String originalName, DartFunction func) {
|
||||
JsName jsName = name != null ? declareExclusively(symbol, name, originalName) : null;
|
||||
JsFunction jsFunc = new JsFunction(scopes.peek(), jsName);
|
||||
jsFunc.setFromDart(true);
|
||||
scopes.push(jsFunc.getScope());
|
||||
translationContext.getMethods().put(func, jsFunc);
|
||||
return jsName;
|
||||
}
|
||||
|
||||
private JsName declare(Symbol x, String name, String originalName) {
|
||||
return declareInScope(scopes.peek(), x, name, originalName);
|
||||
}
|
||||
|
||||
private JsName declareExclusively(Symbol x, String name, String originalName) {
|
||||
return declareExclusivelyInScope(scopes.peek(), x, name, originalName);
|
||||
}
|
||||
|
||||
private JsName declareExclusively(Symbol x, String name) {
|
||||
return declareExclusivelyInScope(scopes.peek(), x, name, name);
|
||||
}
|
||||
|
||||
private static final int BIG_PRIME_UNDER_0XFFFFF = 985531;
|
||||
|
||||
/**
|
||||
* Create a unique name for this variable in this scope.
|
||||
*
|
||||
* Try to keep this from being a linear scan of the namespace, and keep
|
||||
* it under 5 hex digits (over 1,000,000 unique suffixes).
|
||||
*
|
||||
*/
|
||||
private JsName declareExclusivelyInScope(JsScope scope, Symbol x,
|
||||
String name, String originalName) {
|
||||
String mappedName = name;
|
||||
int offset = 0;
|
||||
while (scope.findExistingName(mappedName) != null) {
|
||||
mappedName = String.format("%s_%X", mappedName, varUniqifier);
|
||||
varUniqifier = (varUniqifier + offset++) % BIG_PRIME_UNDER_0XFFFFF;
|
||||
}
|
||||
return declareInScope(scope, x, mappedName, originalName);
|
||||
}
|
||||
|
||||
private JsName declareInScope(JsScope scope, Symbol x, String name, String originalName) {
|
||||
JsName jsName = scope.declareName(name, name, originalName);
|
||||
jsName.getClass(); // Fast null check.
|
||||
x.getClass(); // Fast null check.
|
||||
translationContext.getNames().setName(x, jsName);
|
||||
return jsName;
|
||||
}
|
||||
}
|
||||
@@ -1,181 +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.io.CharStreams;
|
||||
import com.google.common.io.Closeables;
|
||||
import com.google.dart.compiler.DartCompilerContext;
|
||||
import com.google.dart.compiler.DartSource;
|
||||
import com.google.dart.compiler.LibrarySource;
|
||||
import com.google.dart.compiler.ast.LibraryNode;
|
||||
import com.google.dart.compiler.ast.LibraryUnit;
|
||||
import com.google.dart.compiler.backend.js.analysis.TreeShaker;
|
||||
import com.google.dart.compiler.metrics.CompilerMetrics;
|
||||
import com.google.dart.compiler.resolver.CoreTypeProvider;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.Reader;
|
||||
import java.io.Writer;
|
||||
import java.util.Collection;
|
||||
|
||||
/**
|
||||
* A compiler backend that produces raw Javascript.
|
||||
*/
|
||||
public class JavascriptBackend extends AbstractJsBackend {
|
||||
|
||||
public static final String EXTENSION_APP_JS_COMPLETE = EXTENSION_APP_JS + ".complete";
|
||||
|
||||
/**
|
||||
* Wraps an Appendable and keeps track of the current offset as line/columns.
|
||||
*/
|
||||
static class CountingAppendable implements Appendable {
|
||||
|
||||
private int line = 0;
|
||||
private int column = 0;
|
||||
private Appendable out;
|
||||
private long charCount = 0;
|
||||
|
||||
CountingAppendable(Appendable out) {
|
||||
this.out = out;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Appendable append(CharSequence csq) throws IOException {
|
||||
incCount(csq, 0, csq.length());
|
||||
return out.append(csq);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Appendable append(char c) throws IOException {
|
||||
incCount(c);
|
||||
return out.append(c);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Appendable append(CharSequence csq, int start, int end)
|
||||
throws IOException {
|
||||
incCount(csq, start, end);
|
||||
return out.append(csq, start, end);
|
||||
}
|
||||
|
||||
private void incCount(CharSequence cs, int start, int end) {
|
||||
for (int i = 0; i < cs.length(); i++) {
|
||||
incCount(cs.charAt(i));
|
||||
}
|
||||
}
|
||||
|
||||
private void incCount(char c) {
|
||||
++charCount;
|
||||
if (c == '\n') {
|
||||
line++;
|
||||
column = 0;
|
||||
} else {
|
||||
column++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static class DepsWritingCallback implements DepsCallback {
|
||||
private final DartCompilerContext context;
|
||||
private CountingAppendable out;
|
||||
DepsWritingCallback(
|
||||
DartCompilerContext context,
|
||||
CountingAppendable out) {
|
||||
this.out = out;
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitNative(LibraryUnit libUnit, LibraryNode node)
|
||||
throws IOException {
|
||||
DartSource nativeSrc = libUnit.getSource().getSourceFor(node.getText());
|
||||
Reader r = nativeSrc.getSourceReader();
|
||||
long charsWrittenForFile = CharStreams.copy(r, out);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitPart(Part part) throws IOException {
|
||||
DartSource src = part.unit.getSource();
|
||||
assert(src != null);
|
||||
Reader r = context.getArtifactReader(src, part.part, EXTENSION_JS);
|
||||
if (r == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
long partSize = 0;
|
||||
boolean failed = true;
|
||||
try {
|
||||
partSize = CharStreams.copy(r, out);
|
||||
failed = false;
|
||||
} finally {
|
||||
Closeables.close(r, failed);
|
||||
}
|
||||
}
|
||||
|
||||
public long getCharsWritten() {
|
||||
return out.charCount;
|
||||
}
|
||||
}
|
||||
|
||||
private static long packageLibs(Writer w,
|
||||
DartCompilerContext context) throws IOException {
|
||||
final CountingAppendable out = new CountingAppendable(w);
|
||||
|
||||
DepsWritingCallback callback = new DepsWritingCallback(context, out);
|
||||
DependencyBuilder.build(context.getAppLibraryUnit(), callback);
|
||||
return callback.getCharsWritten();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void packageApp(LibrarySource app,
|
||||
Collection<LibraryUnit> libraries,
|
||||
DartCompilerContext context,
|
||||
CoreTypeProvider typeProvider)
|
||||
throws IOException {
|
||||
|
||||
LibraryUnit appLibraryUnit = context.getAppLibraryUnit();
|
||||
boolean hasEntryPoint = appLibraryUnit.getElement().getEntryPoint() != null;
|
||||
|
||||
String completeArtifactName = EXTENSION_APP_JS;
|
||||
if (hasEntryPoint) {
|
||||
// Apps with entry points will be reduced so we write into a different file name
|
||||
completeArtifactName = EXTENSION_APP_JS_COMPLETE;
|
||||
}
|
||||
|
||||
Writer out = context.getArtifactWriter(app, "", completeArtifactName);
|
||||
long outputFileSize = 0;
|
||||
boolean failed = true;
|
||||
try {
|
||||
// Emit the concatenated Javascript sources in dependency order.
|
||||
outputFileSize = packageLibs(out, context);
|
||||
outputFileSize += writeEntryPointCall(getMangledEntryPoint(context), out);
|
||||
failed = false;
|
||||
} finally {
|
||||
Closeables.close(out, failed);
|
||||
}
|
||||
|
||||
|
||||
if (hasEntryPoint) {
|
||||
Writer artifactWriter = context.getArtifactWriter(app, "", EXTENSION_APP_JS);
|
||||
try {
|
||||
failed = true;
|
||||
outputFileSize = TreeShaker.reduce(app, context, completeArtifactName, artifactWriter);
|
||||
failed = false;
|
||||
} finally {
|
||||
Closeables.close(artifactWriter, failed);
|
||||
}
|
||||
}
|
||||
|
||||
CompilerMetrics compilerMetrics = context.getCompilerMetrics();
|
||||
if (compilerMetrics != null) {
|
||||
compilerMetrics.packagedJsApplication(outputFileSize, -1);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getAppExtension() {
|
||||
return EXTENSION_APP_JS;
|
||||
}
|
||||
}
|
||||
@@ -1,114 +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.dart.compiler.backend.js.ast.JsArrayAccess;
|
||||
import com.google.dart.compiler.backend.js.ast.JsArrayLiteral;
|
||||
import com.google.dart.compiler.backend.js.ast.JsContext;
|
||||
import com.google.dart.compiler.backend.js.ast.JsExpression;
|
||||
import com.google.dart.compiler.backend.js.ast.JsFunction;
|
||||
import com.google.dart.compiler.backend.js.ast.JsInvocation;
|
||||
import com.google.dart.compiler.backend.js.ast.JsNameRef;
|
||||
import com.google.dart.compiler.backend.js.ast.JsNew;
|
||||
import com.google.dart.compiler.backend.js.ast.JsObjectLiteral;
|
||||
import com.google.dart.compiler.backend.js.ast.JsVisitable;
|
||||
import com.google.dart.compiler.backend.js.ast.JsVisitor;
|
||||
|
||||
/**
|
||||
* Searches for method invocations in constructor expressions that would not
|
||||
* normally be surrounded by parentheses.
|
||||
*/
|
||||
public class JsConstructExpressionVisitor extends JsVisitor {
|
||||
|
||||
public static boolean exec(JsExpression expression) {
|
||||
if (JsPrecedenceVisitor.exec(expression) < JsPrecedenceVisitor.PRECEDENCE_NEW) {
|
||||
return true;
|
||||
}
|
||||
JsConstructExpressionVisitor visitor = new JsConstructExpressionVisitor();
|
||||
visitor.accept(expression);
|
||||
return visitor.containsInvocation;
|
||||
}
|
||||
|
||||
private boolean containsInvocation = false;
|
||||
|
||||
private JsConstructExpressionVisitor() {
|
||||
}
|
||||
|
||||
/**
|
||||
* We only look at the array expression since the index has its own scope.
|
||||
*/
|
||||
@Override
|
||||
public boolean visit(JsArrayAccess x, JsContext ctx) {
|
||||
accept(x.getArrayExpr());
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Array literals have their own scoping.
|
||||
*/
|
||||
@Override
|
||||
public boolean visit(JsArrayLiteral x, JsContext ctx) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Functions have their own scoping.
|
||||
*/
|
||||
@Override
|
||||
public boolean visit(JsFunction x, JsContext ctx) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean visit(JsInvocation x, JsContext ctx) {
|
||||
containsInvocation = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean visit(JsNameRef x, JsContext ctx) {
|
||||
if (!x.isLeaf()) {
|
||||
accept(x.getQualifier());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* New constructs bind to the nearest set of parentheses.
|
||||
*/
|
||||
@Override
|
||||
public boolean visit(JsNew x, JsContext ctx) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Object literals have their own scope.
|
||||
*/
|
||||
@Override
|
||||
public boolean visit(JsObjectLiteral x, JsContext ctx) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* We only look at nodes that would not normally be surrounded by parentheses.
|
||||
*/
|
||||
@Override
|
||||
protected <T extends JsVisitable> T doAccept(T node) {
|
||||
// Assign to Object to prevent 'inconvertible types' compile errors due
|
||||
// to http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6548436
|
||||
// reproducible in jdk1.6.0_02.
|
||||
Object o = node;
|
||||
if (o instanceof JsExpression) {
|
||||
JsExpression expression = (JsExpression) o;
|
||||
int precedence = JsPrecedenceVisitor.exec(expression);
|
||||
// Only visit expressions that won't automatically be surrounded by
|
||||
// parentheses
|
||||
if (precedence < JsPrecedenceVisitor.PRECEDENCE_NEW) {
|
||||
return node;
|
||||
}
|
||||
}
|
||||
return super.doAccept(node);
|
||||
}
|
||||
}
|
||||
@@ -1,49 +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.dart.compiler.ErrorCode;
|
||||
import com.google.dart.compiler.ErrorSeverity;
|
||||
import com.google.dart.compiler.SubSystem;
|
||||
|
||||
/**
|
||||
* {@link ErrorCode}s for JavaScript backend.
|
||||
*/
|
||||
public enum JsErrorCode implements ErrorCode {
|
||||
INTERNAL_ERROR("internal error: %s");
|
||||
private final ErrorSeverity severity;
|
||||
private final String message;
|
||||
|
||||
/**
|
||||
* Initialize a newly created error code to have the given message and ERROR severity.
|
||||
*/
|
||||
private JsErrorCode(String message) {
|
||||
this(ErrorSeverity.ERROR, message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize a newly created error code to have the given severity and message.
|
||||
*/
|
||||
private JsErrorCode(ErrorSeverity severity, String message) {
|
||||
this.severity = severity;
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
public String getMessage() {
|
||||
return message;
|
||||
}
|
||||
|
||||
public ErrorSeverity getErrorSeverity() {
|
||||
return severity;
|
||||
}
|
||||
|
||||
public SubSystem getSubSystem() {
|
||||
return SubSystem.JS_BACKEND;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean needsRecompilation() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -1,134 +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.dart.compiler.backend.js.ast.JsArrayAccess;
|
||||
import com.google.dart.compiler.backend.js.ast.JsArrayLiteral;
|
||||
import com.google.dart.compiler.backend.js.ast.JsBinaryOperation;
|
||||
import com.google.dart.compiler.backend.js.ast.JsConditional;
|
||||
import com.google.dart.compiler.backend.js.ast.JsContext;
|
||||
import com.google.dart.compiler.backend.js.ast.JsExprStmt;
|
||||
import com.google.dart.compiler.backend.js.ast.JsExpression;
|
||||
import com.google.dart.compiler.backend.js.ast.JsFunction;
|
||||
import com.google.dart.compiler.backend.js.ast.JsInvocation;
|
||||
import com.google.dart.compiler.backend.js.ast.JsNameRef;
|
||||
import com.google.dart.compiler.backend.js.ast.JsNew;
|
||||
import com.google.dart.compiler.backend.js.ast.JsObjectLiteral;
|
||||
import com.google.dart.compiler.backend.js.ast.JsPostfixOperation;
|
||||
import com.google.dart.compiler.backend.js.ast.JsPrefixOperation;
|
||||
import com.google.dart.compiler.backend.js.ast.JsRegExp;
|
||||
import com.google.dart.compiler.backend.js.ast.JsVisitor;
|
||||
|
||||
/**
|
||||
* Determines if an expression statement needs to be surrounded by parentheses.
|
||||
*
|
||||
* The statement or the left-most expression needs to be surrounded by
|
||||
* parentheses if the left-most expression is an object literal or a function
|
||||
* object. Function declarations do not need parentheses.
|
||||
*
|
||||
* For example the following require parentheses:<br>
|
||||
* <ul>
|
||||
* <li>{ key : 'value'}</li>
|
||||
* <li>{ key : 'value'}.key</li>
|
||||
* <li>function () {return 1;}()</li>
|
||||
* <li>function () {return 1;}.prototype</li>
|
||||
* </ul>
|
||||
*
|
||||
* The following do not require parentheses:<br>
|
||||
* <ul>
|
||||
* <li>var x = { key : 'value'}</li>
|
||||
* <li>"string" + { key : 'value'}.key</li>
|
||||
* <li>function func() {}</li>
|
||||
* <li>function() {}</li>
|
||||
* </ul>
|
||||
*/
|
||||
public class JsFirstExpressionVisitor extends JsVisitor {
|
||||
|
||||
public static boolean exec(JsExprStmt statement) {
|
||||
JsFirstExpressionVisitor visitor = new JsFirstExpressionVisitor();
|
||||
JsExpression expression = statement.getExpression();
|
||||
// Pure function declarations do not need parentheses
|
||||
if (expression instanceof JsFunction) {
|
||||
return false;
|
||||
}
|
||||
visitor.accept(statement.getExpression());
|
||||
return visitor.needsParentheses;
|
||||
}
|
||||
|
||||
private boolean needsParentheses = false;
|
||||
|
||||
private JsFirstExpressionVisitor() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean visit(JsArrayAccess x, JsContext ctx) {
|
||||
accept(x.getArrayExpr());
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean visit(JsArrayLiteral x, JsContext ctx) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean visit(JsBinaryOperation x, JsContext ctx) {
|
||||
accept(x.getArg1());
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean visit(JsConditional x, JsContext ctx) {
|
||||
accept(x.getTestExpression());
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean visit(JsFunction x, JsContext ctx) {
|
||||
needsParentheses = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean visit(JsInvocation x, JsContext ctx) {
|
||||
accept(x.getQualifier());
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean visit(JsNameRef x, JsContext ctx) {
|
||||
if (!x.isLeaf()) {
|
||||
accept(x.getQualifier());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean visit(JsNew x, JsContext ctx) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean visit(JsObjectLiteral x, JsContext ctx) {
|
||||
needsParentheses = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean visit(JsPostfixOperation x, JsContext ctx) {
|
||||
accept(x.getArg());
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean visit(JsPrefixOperation x, JsContext ctx) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean visit(JsRegExp x, JsContext ctx) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -1,87 +0,0 @@
|
||||
// Copyright 2011, the Dart project authors. All rights reserved.
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following
|
||||
// disclaimer in the documentation and/or other materials provided
|
||||
// with the distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived
|
||||
// from this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
package com.google.dart.compiler.backend.js;
|
||||
|
||||
import com.google.dart.compiler.backend.js.ast.JsName;
|
||||
import com.google.dart.compiler.backend.js.ast.JsProgram;
|
||||
import com.google.dart.compiler.backend.js.ast.JsScope;
|
||||
import com.google.dart.compiler.common.Symbol;
|
||||
import com.google.dart.compiler.resolver.ClassElement;
|
||||
import com.google.dart.compiler.resolver.ElementKind;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* A helper class for managing global names.
|
||||
* @author johnlenz@google.com (John Lenz)
|
||||
*/
|
||||
class JsNameProvider {
|
||||
private final DartMangler mangler;
|
||||
private Map<Symbol, JsName> names = new HashMap<Symbol, JsName>();
|
||||
private JsScope globalScope;
|
||||
|
||||
JsNameProvider(JsProgram program, DartMangler mangler) {
|
||||
this.globalScope = program.getScope();
|
||||
this.mangler = mangler;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the JsName for the given element. If the element is global and
|
||||
* hasn't been declared yet, it is done now.
|
||||
*/
|
||||
JsName getName(Symbol symbol) {
|
||||
JsName jsName = names.get(symbol);
|
||||
if (jsName != null) {
|
||||
assert !jsName.getShortIdent().equals("Object$Dart");
|
||||
return jsName;
|
||||
}
|
||||
assert ElementKind.of(symbol).equals(ElementKind.CLASS)
|
||||
|| ElementKind.of(symbol).equals(ElementKind.FUNCTION_TYPE_ALIAS)
|
||||
: "Only classes or typedefs can be lazily declared. Undeclared: "
|
||||
+ symbol.getOriginalSymbolName();
|
||||
ClassElement classElement = (ClassElement) symbol;
|
||||
String name = classElement.getName();
|
||||
String nativeName = classElement.getNativeName();
|
||||
if (nativeName == null) {
|
||||
String mangledClassName = mangler.mangleClassName(classElement);
|
||||
jsName = globalScope.declareName(mangledClassName, mangledClassName, name);
|
||||
} else {
|
||||
jsName = globalScope.declareName(nativeName);
|
||||
}
|
||||
// Class names are globally accessible.
|
||||
jsName.setObfuscatable(false);
|
||||
names.put(symbol, jsName);
|
||||
assert !jsName.getShortIdent().equals("Object$Dart") : "unexpected " + ((ClassElement) symbol).getNode().getSource().getName();
|
||||
return jsName;
|
||||
}
|
||||
|
||||
void setName(Symbol symbol, JsName name) {
|
||||
names.put(symbol, name);
|
||||
}
|
||||
}
|
||||
@@ -1,23 +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.dart.compiler.backend.js.ast.JsProgram;
|
||||
|
||||
/**
|
||||
* A namer runs through a program and renames the short names of JsNames.
|
||||
* Namers must assign short names that don't clash and that are valid
|
||||
* JS-identifiers. Nested JsScopes must not shadow JsNames from outer
|
||||
* scopes.
|
||||
* If a JsName is marked as non-obfuscatable then it must retain its short
|
||||
* name.
|
||||
*/
|
||||
public interface JsNamer {
|
||||
/**
|
||||
* Names the shortNames of all JsNames of the program so that they are valid
|
||||
* JS-identifiers and that there are no clashes and no shadowing.
|
||||
*/
|
||||
public void exec(JsProgram program);
|
||||
}
|
||||
@@ -1,112 +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.dart.compiler.backend.js.ast.JsBinaryOperation;
|
||||
import com.google.dart.compiler.backend.js.ast.JsBinaryOperator;
|
||||
import com.google.dart.compiler.backend.js.ast.JsContext;
|
||||
import com.google.dart.compiler.backend.js.ast.JsExpression;
|
||||
import com.google.dart.compiler.backend.js.ast.JsModVisitor;
|
||||
import com.google.dart.compiler.backend.js.ast.JsPostfixOperation;
|
||||
import com.google.dart.compiler.backend.js.ast.JsPrefixOperation;
|
||||
import com.google.dart.compiler.backend.js.ast.JsProgram;
|
||||
import com.google.dart.compiler.backend.js.ast.JsUnaryOperation;
|
||||
import com.google.dart.compiler.backend.js.ast.JsUnaryOperator;
|
||||
|
||||
/**
|
||||
* Fixes any semantic errors introduced by JS AST gen.
|
||||
*
|
||||
* <ul>
|
||||
* <li>Creating clinit calls can put comma expressions as lvalues; the modifying
|
||||
* operation must be moved inside the comma expression to the last argument.</li>
|
||||
* </ul>
|
||||
*/
|
||||
public class JsNormalizer {
|
||||
|
||||
/**
|
||||
* Resolves any unresolved JsNameRefs.
|
||||
*/
|
||||
private static class JsNormalizing extends JsModVisitor {
|
||||
|
||||
@Override
|
||||
public void endVisit(JsBinaryOperation x, JsContext ctx) {
|
||||
maybeShuffleModifyingBinary(x, ctx);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void endVisit(JsPostfixOperation x, JsContext ctx) {
|
||||
maybeShuffleModifyingUnary(x, ctx);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void endVisit(JsPrefixOperation x, JsContext ctx) {
|
||||
maybeShuffleModifyingUnary(x, ctx);
|
||||
}
|
||||
|
||||
/**
|
||||
* Due to the way clinits are constructed, you can end up with a comma
|
||||
* operation as the argument to a modifying operation, which is illegal.
|
||||
* Juggle things to put the operator inside of the comma expression.
|
||||
*/
|
||||
private void maybeShuffleModifyingBinary(JsBinaryOperation x, JsContext ctx) {
|
||||
JsBinaryOperator myOp = x.getOperator();
|
||||
JsExpression lhs = x.getArg1();
|
||||
|
||||
if (myOp.isAssignment() && (lhs instanceof JsBinaryOperation)) {
|
||||
// Find the rightmost comma operation
|
||||
JsBinaryOperation curLhs = (JsBinaryOperation) lhs;
|
||||
assert (curLhs.getOperator() == JsBinaryOperator.COMMA);
|
||||
while (curLhs.getArg2() instanceof JsBinaryOperation) {
|
||||
curLhs = (JsBinaryOperation) curLhs.getArg2();
|
||||
assert (curLhs.getOperator() == JsBinaryOperator.COMMA);
|
||||
}
|
||||
// curLhs is now the rightmost comma operation; slide our operation in
|
||||
x.setArg1(curLhs.getArg2());
|
||||
curLhs.setArg2(x);
|
||||
// replace myself with the comma expression
|
||||
ctx.replaceMe(lhs);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Due to the way clinits are constructed, you can end up with a comma
|
||||
* operation as the argument to a modifying operation, which is illegal.
|
||||
* Juggle things to put the operator inside of the comma expression.
|
||||
*/
|
||||
private void maybeShuffleModifyingUnary(JsUnaryOperation x, JsContext ctx) {
|
||||
JsUnaryOperator myOp = x.getOperator();
|
||||
JsExpression arg = x.getArg();
|
||||
if (myOp.isModifying() && (arg instanceof JsBinaryOperation)) {
|
||||
// Find the rightmost comma operation
|
||||
JsBinaryOperation curArg = (JsBinaryOperation) arg;
|
||||
assert (curArg.getOperator() == JsBinaryOperator.COMMA);
|
||||
while (curArg.getArg2() instanceof JsBinaryOperation) {
|
||||
curArg = (JsBinaryOperation) curArg.getArg2();
|
||||
assert (curArg.getOperator() == JsBinaryOperator.COMMA);
|
||||
}
|
||||
// curArg is now the rightmost comma operation; slide our operation in
|
||||
x.setArg(curArg.getArg2());
|
||||
curArg.setArg2(x);
|
||||
// replace myself with the comma expression
|
||||
ctx.replaceMe(arg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void exec(JsProgram program) {
|
||||
new JsNormalizer(program).execImpl();
|
||||
}
|
||||
|
||||
private final JsProgram program;
|
||||
|
||||
private JsNormalizer(JsProgram program) {
|
||||
this.program = program;
|
||||
}
|
||||
|
||||
private void execImpl() {
|
||||
JsNormalizing normalizer = new JsNormalizing();
|
||||
normalizer.accept(program);
|
||||
}
|
||||
}
|
||||
@@ -1,92 +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;
|
||||
|
||||
/**
|
||||
* Indicates inability to parse JavaScript source.
|
||||
*/
|
||||
public class JsParserException extends Exception {
|
||||
|
||||
/**
|
||||
* Represents the location of a parser exception.
|
||||
*/
|
||||
public static class SourceDetail {
|
||||
private final String fileName;
|
||||
private final int line;
|
||||
private final int lineOffset;
|
||||
private final String lineSource;
|
||||
|
||||
public SourceDetail(int line, String lineSource, int lineOffset, String fileName) {
|
||||
this.line = line;
|
||||
this.lineSource = lineSource;
|
||||
this.lineOffset = lineOffset;
|
||||
this.fileName = fileName;
|
||||
}
|
||||
|
||||
public String getFileName() {
|
||||
return fileName;
|
||||
}
|
||||
|
||||
public int getLine() {
|
||||
return line;
|
||||
}
|
||||
|
||||
public int getLineOffset() {
|
||||
return lineOffset;
|
||||
}
|
||||
|
||||
public String getLineSource() {
|
||||
return lineSource;
|
||||
}
|
||||
}
|
||||
|
||||
private static String createMessageWithDetail(String msg, SourceDetail sourceDetail) {
|
||||
if (sourceDetail == null) {
|
||||
return msg;
|
||||
}
|
||||
StringBuffer sb = new StringBuffer();
|
||||
sb.append(sourceDetail.getFileName());
|
||||
sb.append('(');
|
||||
sb.append(sourceDetail.getLine());
|
||||
sb.append(')');
|
||||
sb.append(": ");
|
||||
sb.append(msg);
|
||||
if (sourceDetail.getLineSource() != null) {
|
||||
sb.append("\n> ");
|
||||
sb.append(sourceDetail.getLineSource());
|
||||
sb.append("\n> ");
|
||||
for (int i = 0, n = sourceDetail.getLineOffset(); i < n; ++i) {
|
||||
sb.append('-');
|
||||
}
|
||||
sb.append('^');
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
private final SourceDetail sourceDetail;
|
||||
|
||||
public JsParserException(String msg) {
|
||||
this(msg, null);
|
||||
}
|
||||
|
||||
public JsParserException(String msg, int line, String lineSource, int lineOffset, String fileName) {
|
||||
this(msg, new SourceDetail(line, lineSource, lineOffset, fileName));
|
||||
}
|
||||
|
||||
public JsParserException(String msg, SourceDetail sourceDetail) {
|
||||
super(createMessageWithDetail(msg, sourceDetail));
|
||||
this.sourceDetail = sourceDetail;
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides additional source detail in some cases.
|
||||
*
|
||||
* @return additional detail regarding the error, or <code>null</code> if no
|
||||
* additional detail is available
|
||||
*/
|
||||
public SourceDetail getSourceDetail() {
|
||||
return sourceDetail;
|
||||
}
|
||||
}
|
||||
@@ -1,317 +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.dart.compiler.backend.js.ast.JsArrayAccess;
|
||||
import com.google.dart.compiler.backend.js.ast.JsArrayLiteral;
|
||||
import com.google.dart.compiler.backend.js.ast.JsBinaryOperation;
|
||||
import com.google.dart.compiler.backend.js.ast.JsBlock;
|
||||
import com.google.dart.compiler.backend.js.ast.JsBooleanLiteral;
|
||||
import com.google.dart.compiler.backend.js.ast.JsBreak;
|
||||
import com.google.dart.compiler.backend.js.ast.JsCase;
|
||||
import com.google.dart.compiler.backend.js.ast.JsCatch;
|
||||
import com.google.dart.compiler.backend.js.ast.JsConditional;
|
||||
import com.google.dart.compiler.backend.js.ast.JsContext;
|
||||
import com.google.dart.compiler.backend.js.ast.JsContinue;
|
||||
import com.google.dart.compiler.backend.js.ast.JsDebugger;
|
||||
import com.google.dart.compiler.backend.js.ast.JsDefault;
|
||||
import com.google.dart.compiler.backend.js.ast.JsDoWhile;
|
||||
import com.google.dart.compiler.backend.js.ast.JsEmpty;
|
||||
import com.google.dart.compiler.backend.js.ast.JsExprStmt;
|
||||
import com.google.dart.compiler.backend.js.ast.JsExpression;
|
||||
import com.google.dart.compiler.backend.js.ast.JsFor;
|
||||
import com.google.dart.compiler.backend.js.ast.JsForIn;
|
||||
import com.google.dart.compiler.backend.js.ast.JsFunction;
|
||||
import com.google.dart.compiler.backend.js.ast.JsIf;
|
||||
import com.google.dart.compiler.backend.js.ast.JsInvocation;
|
||||
import com.google.dart.compiler.backend.js.ast.JsLabel;
|
||||
import com.google.dart.compiler.backend.js.ast.JsNameRef;
|
||||
import com.google.dart.compiler.backend.js.ast.JsNew;
|
||||
import com.google.dart.compiler.backend.js.ast.JsNullLiteral;
|
||||
import com.google.dart.compiler.backend.js.ast.JsNumberLiteral;
|
||||
import com.google.dart.compiler.backend.js.ast.JsObjectLiteral;
|
||||
import com.google.dart.compiler.backend.js.ast.JsParameter;
|
||||
import com.google.dart.compiler.backend.js.ast.JsPostfixOperation;
|
||||
import com.google.dart.compiler.backend.js.ast.JsPrefixOperation;
|
||||
import com.google.dart.compiler.backend.js.ast.JsProgram;
|
||||
import com.google.dart.compiler.backend.js.ast.JsPropertyInitializer;
|
||||
import com.google.dart.compiler.backend.js.ast.JsRegExp;
|
||||
import com.google.dart.compiler.backend.js.ast.JsReturn;
|
||||
import com.google.dart.compiler.backend.js.ast.JsStringLiteral;
|
||||
import com.google.dart.compiler.backend.js.ast.JsSwitch;
|
||||
import com.google.dart.compiler.backend.js.ast.JsThisRef;
|
||||
import com.google.dart.compiler.backend.js.ast.JsThrow;
|
||||
import com.google.dart.compiler.backend.js.ast.JsTry;
|
||||
import com.google.dart.compiler.backend.js.ast.JsVars;
|
||||
import com.google.dart.compiler.backend.js.ast.JsVisitor;
|
||||
import com.google.dart.compiler.backend.js.ast.JsWhile;
|
||||
import com.google.dart.compiler.backend.js.ast.JsVars.JsVar;
|
||||
|
||||
/**
|
||||
* Precedence indices from "JavaScript - The Definitive Guide" 4th Edition (page
|
||||
* 57)
|
||||
*
|
||||
* Precedence 17 is for indivisible primaries that either don't have children,
|
||||
* or provide their own delimiters.
|
||||
*
|
||||
* Precedence 16 is for really important things that have their own AST classes.
|
||||
*
|
||||
* Precedence 15 is for the new construct.
|
||||
*
|
||||
* Precedence 14 is for unary operators.
|
||||
*
|
||||
* Precedences 12 through 4 are for non-assigning binary operators.
|
||||
*
|
||||
* Precedence 3 is for the tertiary conditional.
|
||||
*
|
||||
* Precedence 2 is for assignments.
|
||||
*
|
||||
* Precedence 1 is for comma operations.
|
||||
*/
|
||||
class JsPrecedenceVisitor extends JsVisitor {
|
||||
|
||||
static final int PRECEDENCE_NEW = 15;
|
||||
|
||||
public static int exec(JsExpression expression) {
|
||||
JsPrecedenceVisitor visitor = new JsPrecedenceVisitor();
|
||||
visitor.accept(expression);
|
||||
if (visitor.answer < 0) {
|
||||
throw new RuntimeException("Precedence must be >= 0!");
|
||||
}
|
||||
return visitor.answer;
|
||||
}
|
||||
|
||||
private int answer = -1;
|
||||
|
||||
private JsPrecedenceVisitor() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean visit(JsArrayAccess x, JsContext ctx) {
|
||||
answer = 16;
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean visit(JsArrayLiteral x, JsContext ctx) {
|
||||
answer = 17; // primary
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean visit(JsBinaryOperation x, JsContext ctx) {
|
||||
answer = x.getOperator().getPrecedence();
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean visit(JsBlock x, JsContext ctx) {
|
||||
throw new RuntimeException("Only expressions have precedence.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean visit(JsBooleanLiteral x, JsContext ctx) {
|
||||
answer = 17; // primary
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean visit(JsBreak x, JsContext ctx) {
|
||||
throw new RuntimeException("Only expressions have precedence.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean visit(JsCase x, JsContext ctx) {
|
||||
throw new RuntimeException("Only expressions have precedence.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean visit(JsCatch x, JsContext ctx) {
|
||||
throw new RuntimeException("Only expressions have precedence.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean visit(JsConditional x, JsContext ctx) {
|
||||
answer = 3;
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean visit(JsContinue x, JsContext ctx) {
|
||||
throw new RuntimeException("Only expressions have precedence.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean visit(JsDebugger x, JsContext ctx) {
|
||||
throw new RuntimeException("Only expressions have precedence.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean visit(JsDefault x, JsContext ctx) {
|
||||
throw new RuntimeException("Only expressions have precedence.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean visit(JsDoWhile x, JsContext ctx) {
|
||||
throw new RuntimeException("Only expressions have precedence.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean visit(JsEmpty x, JsContext ctx) {
|
||||
throw new RuntimeException("Only expressions have precedence.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean visit(JsExprStmt x, JsContext ctx) {
|
||||
throw new RuntimeException("Only expressions have precedence.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean visit(JsFor x, JsContext ctx) {
|
||||
throw new RuntimeException("Only expressions have precedence.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean visit(JsForIn x, JsContext ctx) {
|
||||
throw new RuntimeException("Only expressions have precedence.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean visit(JsFunction x, JsContext ctx) {
|
||||
answer = 17; // primary
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean visit(JsIf x, JsContext ctx) {
|
||||
throw new RuntimeException("Only expressions have precedence.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean visit(JsInvocation x, JsContext ctx) {
|
||||
answer = 16;
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean visit(JsLabel x, JsContext ctx) {
|
||||
throw new RuntimeException("Only expressions have precedence.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean visit(JsNameRef x, JsContext ctx) {
|
||||
if (x.isLeaf()) {
|
||||
answer = 17; // primary
|
||||
} else {
|
||||
answer = 16; // property access
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean visit(JsNew x, JsContext ctx) {
|
||||
answer = PRECEDENCE_NEW;
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean visit(JsNullLiteral x, JsContext ctx) {
|
||||
answer = 17; // primary
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean visit(JsNumberLiteral x, JsContext ctx) {
|
||||
answer = 17; // primary
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean visit(JsObjectLiteral x, JsContext ctx) {
|
||||
answer = 17; // primary
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean visit(JsParameter x, JsContext ctx) {
|
||||
throw new RuntimeException("Only expressions have precedence.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean visit(JsPostfixOperation x, JsContext ctx) {
|
||||
answer = x.getOperator().getPrecedence();
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean visit(JsPrefixOperation x, JsContext ctx) {
|
||||
answer = x.getOperator().getPrecedence();
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean visit(JsProgram x, JsContext ctx) {
|
||||
throw new RuntimeException("Only expressions have precedence.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean visit(JsPropertyInitializer x, JsContext ctx) {
|
||||
answer = 17; // primary
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean visit(JsRegExp x, JsContext ctx) {
|
||||
answer = 17; // primary
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean visit(JsReturn x, JsContext ctx) {
|
||||
throw new RuntimeException("Only expressions have precedence.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean visit(JsStringLiteral x, JsContext ctx) {
|
||||
answer = 17; // primary
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean visit(JsSwitch x, JsContext ctx) {
|
||||
throw new RuntimeException("Only expressions have precedence.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean visit(JsThisRef x, JsContext ctx) {
|
||||
answer = 17; // primary
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean visit(JsThrow x, JsContext ctx) {
|
||||
throw new RuntimeException("Only expressions have precedence.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean visit(JsTry x, JsContext ctx) {
|
||||
throw new RuntimeException("Only expressions have precedence.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean visit(JsVar x, JsContext ctx) {
|
||||
throw new RuntimeException("Only expressions have precedence.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean visit(JsVars x, JsContext ctx) {
|
||||
throw new RuntimeException("Only expressions have precedence.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean visit(JsWhile x, JsContext ctx) {
|
||||
throw new RuntimeException("Only expressions have precedence.");
|
||||
}
|
||||
}
|
||||
@@ -1,127 +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.dart.compiler.backend.js.ast.JsName;
|
||||
import com.google.dart.compiler.backend.js.ast.JsProgram;
|
||||
import com.google.dart.compiler.backend.js.ast.JsRootScope;
|
||||
import com.google.dart.compiler.backend.js.ast.JsScope;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.IdentityHashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* A namer that uses short, readable idents to maximize reability.
|
||||
*/
|
||||
public class JsPrettyNamer implements JsNamer {
|
||||
|
||||
public JsPrettyNamer() {
|
||||
this.program = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void exec(JsProgram program) {
|
||||
new JsPrettyNamer(program).execImpl();
|
||||
}
|
||||
|
||||
/**
|
||||
* Communicates to a parent scope all the idents used by all child scopes.
|
||||
*/
|
||||
private Set<String> childIdents = null;
|
||||
|
||||
private final JsProgram program;
|
||||
|
||||
/**
|
||||
* A map containing the next integer to try as an identifier suffix for a
|
||||
* given JsScope.
|
||||
*/
|
||||
private IdentityHashMap<JsScope, HashMap<String, Integer>> startIdentForScope =
|
||||
new IdentityHashMap<JsScope, HashMap<String, Integer>>();
|
||||
|
||||
protected JsPrettyNamer(JsProgram program) {
|
||||
this.program = program;
|
||||
}
|
||||
|
||||
private void execImpl() {
|
||||
visit(program.getRootScope());
|
||||
}
|
||||
|
||||
private boolean isLegal(JsScope scope, Set<String> childIdents, String newIdent) {
|
||||
if (JsReservedIdentifiers.isKeyword(newIdent)) {
|
||||
return false;
|
||||
}
|
||||
if (childIdents.contains(newIdent)) {
|
||||
// one of my children already claimed this ident
|
||||
return false;
|
||||
}
|
||||
/*
|
||||
* Never obfuscate a name into an identifier that conflicts with an existing
|
||||
* unobfuscatable name! It's okay if it conflicts with an existing
|
||||
* obfuscatable name; that name will get obfuscated out of the way.
|
||||
*/
|
||||
return (scope.findExistingUnobfuscatableName(newIdent) == null);
|
||||
}
|
||||
|
||||
private void visit(JsScope scope) {
|
||||
HashMap<String, Integer> startIdent = startIdentForScope.get(scope);
|
||||
if (startIdent == null) {
|
||||
startIdent = new HashMap<String, Integer>();
|
||||
startIdentForScope.put(scope, startIdent);
|
||||
}
|
||||
|
||||
// Save off the childIdents which is currently being computed for my parent.
|
||||
Set<String> myChildIdents = childIdents;
|
||||
|
||||
/*
|
||||
* Visit my children first. Reset childIdents so that my children will get a
|
||||
* clean slate: I do not communicate to my children.
|
||||
*/
|
||||
childIdents = new HashSet<String>();
|
||||
List<JsScope> children = scope.getChildren();
|
||||
for (Iterator<JsScope> it = children.iterator(); it.hasNext();) {
|
||||
visit(it.next());
|
||||
}
|
||||
|
||||
JsRootScope rootScope = program.getRootScope();
|
||||
if (scope == rootScope) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Visit all my idents.
|
||||
for (Iterator<JsName> it = scope.getAllNames(); it.hasNext();) {
|
||||
JsName name = it.next();
|
||||
if (!name.isObfuscatable()) {
|
||||
// Unobfuscatable names become themselves.
|
||||
name.setShortIdent(name.getIdent());
|
||||
continue;
|
||||
}
|
||||
|
||||
String newIdent = name.getShortIdent();
|
||||
if (!isLegal(scope, childIdents, newIdent)) {
|
||||
String checkIdent;
|
||||
|
||||
// Start searching using a suffix hint stored in the scope.
|
||||
// We still do a search in case there is a collision with
|
||||
// a user-provided identifier
|
||||
Integer s = startIdent.get(newIdent);
|
||||
int suffix = (s == null) ? 0 : s.intValue();
|
||||
do {
|
||||
checkIdent = newIdent + "_" + suffix++;
|
||||
} while (!isLegal(scope, childIdents, checkIdent));
|
||||
startIdent.put(newIdent, suffix);
|
||||
name.setShortIdent(checkIdent);
|
||||
} else {
|
||||
// nothing to do; the short name is already good
|
||||
}
|
||||
childIdents.add(name.getShortIdent());
|
||||
}
|
||||
myChildIdents.addAll(childIdents);
|
||||
childIdents = myChildIdents;
|
||||
}
|
||||
}
|
||||
@@ -1,158 +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.dart.compiler.backend.js.ast.JsBlock;
|
||||
import com.google.dart.compiler.backend.js.ast.JsBreak;
|
||||
import com.google.dart.compiler.backend.js.ast.JsContext;
|
||||
import com.google.dart.compiler.backend.js.ast.JsDebugger;
|
||||
import com.google.dart.compiler.backend.js.ast.JsDoWhile;
|
||||
import com.google.dart.compiler.backend.js.ast.JsEmpty;
|
||||
import com.google.dart.compiler.backend.js.ast.JsExprStmt;
|
||||
import com.google.dart.compiler.backend.js.ast.JsFor;
|
||||
import com.google.dart.compiler.backend.js.ast.JsForIn;
|
||||
import com.google.dart.compiler.backend.js.ast.JsIf;
|
||||
import com.google.dart.compiler.backend.js.ast.JsLabel;
|
||||
import com.google.dart.compiler.backend.js.ast.JsReturn;
|
||||
import com.google.dart.compiler.backend.js.ast.JsStatement;
|
||||
import com.google.dart.compiler.backend.js.ast.JsSwitch;
|
||||
import com.google.dart.compiler.backend.js.ast.JsThrow;
|
||||
import com.google.dart.compiler.backend.js.ast.JsTry;
|
||||
import com.google.dart.compiler.backend.js.ast.JsVars;
|
||||
import com.google.dart.compiler.backend.js.ast.JsVisitor;
|
||||
import com.google.dart.compiler.backend.js.ast.JsWhile;
|
||||
|
||||
/**
|
||||
* Determines if a statement at the end of a block requires a semicolon.
|
||||
*
|
||||
* For example, the following statements require semicolons:<br>
|
||||
* <ul>
|
||||
* <li>if (cond);</li>
|
||||
* <li>while (cond);</li>
|
||||
* </ul>
|
||||
*
|
||||
* The following do not require semicolons:<br>
|
||||
* <ul>
|
||||
* <li>return 1</li>
|
||||
* <li>do {} while(true)</li>
|
||||
* </ul>
|
||||
*/
|
||||
public class JsRequiresSemiVisitor extends JsVisitor {
|
||||
|
||||
public static boolean exec(JsStatement lastStatement) {
|
||||
JsRequiresSemiVisitor visitor = new JsRequiresSemiVisitor();
|
||||
visitor.accept(lastStatement);
|
||||
return visitor.needsSemicolon;
|
||||
}
|
||||
|
||||
private boolean needsSemicolon = false;
|
||||
|
||||
private JsRequiresSemiVisitor() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean visit(JsBlock x, JsContext ctx) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean visit(JsBreak x, JsContext ctx) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean visit(JsDebugger x, JsContext ctx) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean visit(JsDoWhile x, JsContext ctx) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean visit(JsEmpty x, JsContext ctx) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean visit(JsExprStmt x, JsContext ctx) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean visit(JsFor x, JsContext ctx) {
|
||||
if (x.getBody() instanceof JsEmpty) {
|
||||
needsSemicolon = true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean visit(JsForIn x, JsContext ctx) {
|
||||
if (x.getBody() instanceof JsEmpty) {
|
||||
needsSemicolon = true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean visit(JsIf x, JsContext ctx) {
|
||||
JsStatement thenStmt = x.getThenStmt();
|
||||
JsStatement elseStmt = x.getElseStmt();
|
||||
JsStatement toCheck = thenStmt;
|
||||
if (elseStmt != null) {
|
||||
toCheck = elseStmt;
|
||||
}
|
||||
if (toCheck instanceof JsEmpty) {
|
||||
needsSemicolon = true;
|
||||
} else {
|
||||
// Must recurse to determine last statement (possible if-else chain).
|
||||
accept(toCheck);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean visit(JsLabel x, JsContext ctx) {
|
||||
if (x.getStmt() instanceof JsEmpty) {
|
||||
needsSemicolon = true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean visit(JsReturn x, JsContext ctx) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean visit(JsSwitch x, JsContext ctx) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean visit(JsThrow x, JsContext ctx) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean visit(JsTry x, JsContext ctx) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean visit(JsVars x, JsContext ctx) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean visit(JsWhile x, JsContext ctx) {
|
||||
if (x.getBody() instanceof JsEmpty) {
|
||||
needsSemicolon = true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -1,223 +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 java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Determines whether or not a particular string is a JavaScript keyword or not.
|
||||
*/
|
||||
public class JsReservedIdentifiers {
|
||||
|
||||
private static Set<String> javaScriptKeywords;
|
||||
private static Set<String> reservedGlobalSymbols;
|
||||
private static Set<String> reservedPropertySymbols;
|
||||
|
||||
static {
|
||||
javaScriptKeywords = new HashSet<String>();
|
||||
reservedGlobalSymbols = new HashSet<String>();
|
||||
reservedPropertySymbols = new HashSet<String>();
|
||||
initJavaScriptKeywords();
|
||||
initReservedGlobalSymbols();
|
||||
initReservedPropertySymbols();
|
||||
}
|
||||
|
||||
public static boolean isKeyword(String s) {
|
||||
return javaScriptKeywords.contains(s);
|
||||
}
|
||||
|
||||
private static void initJavaScriptKeywords() {
|
||||
String[] keywords = new String[] {
|
||||
// These are current keywords
|
||||
"break", "delete", "function", "return", "typeof", "case", "do", "if", "switch", "var",
|
||||
"catch", "else", "in", "this", "void", "continue", "false", "instanceof", "throw",
|
||||
"while", "debugger", "finally", "new", "true", "with", "default", "for",
|
||||
"null", "try",
|
||||
|
||||
// These are future keywords
|
||||
"abstract", "double", "goto", "native", "static", "boolean", "enum", "implements",
|
||||
"package", "super", "byte", "export", "import", "private", "synchronized", "char",
|
||||
"extends", "int", "protected", "throws", "class", "final", "interface", "public",
|
||||
"transient", "const", "float", "long", "short", "volatile"
|
||||
};
|
||||
|
||||
for (int i = 0; i < keywords.length; i++) {
|
||||
javaScriptKeywords.add(keywords[i]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return a set containing all known reserved global identifiers. This set must not be modified.
|
||||
*/
|
||||
public static Set<String> getReservedGlobalSymbols() {
|
||||
return reservedGlobalSymbols;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the string s can not be used as a global identifier. The check includes
|
||||
* JavaScript keywords (as they must not be used either).
|
||||
* @param s
|
||||
* @return true if the given String must not be used as a global identifier.
|
||||
*/
|
||||
public static boolean isReservedGlobalSymbol(String s) {
|
||||
return isKeyword(s) || reservedGlobalSymbols.contains(s);
|
||||
}
|
||||
|
||||
private static void initReservedGlobalSymbols() {
|
||||
// Section references are from Ecma-262
|
||||
// (http://www.ecma-international.org/publications/files/ECMA-ST/Ecma-262.pdf)
|
||||
String[] commonBuiltins = new String[] {
|
||||
// 15.1.1 Value Properties of the Global Object
|
||||
"NaN", "Infinity", "undefined",
|
||||
|
||||
// 15.1.2 Function Properties of the Global Object
|
||||
"eval", "parseInt", "parseFloat", "isNan", "isFinite",
|
||||
|
||||
// 15.1.3 URI Handling Function Properties
|
||||
"decodeURI", "decodeURIComponent",
|
||||
"encodeURI",
|
||||
"encodeURIComponent",
|
||||
|
||||
// 15.1.4 Constructor Properties of the Global Object
|
||||
"Object", "Function", "Array", "String", "Boolean", "Number", "Date",
|
||||
"RegExp", "Error", "EvalError", "RangeError", "ReferenceError",
|
||||
"SyntaxError", "TypeError", "URIError",
|
||||
|
||||
// 15.1.5 Other Properties of the Global Object
|
||||
"Math",
|
||||
|
||||
// 10.1.6 Activation Object
|
||||
"arguments",
|
||||
|
||||
// B.2 Additional Properties (non-normative)
|
||||
"escape", "unescape",
|
||||
|
||||
// Window props (https://developer.mozilla.org/en/DOM/window)
|
||||
"applicationCache", "closed", "Components", "content", "controllers",
|
||||
"crypto", "defaultStatus", "dialogArguments", "directories",
|
||||
"document", "frameElement", "frames", "fullScreen", "globalStorage",
|
||||
"history", "innerHeight", "innerWidth", "length",
|
||||
"location", "locationbar", "localStorage", "menubar",
|
||||
"mozInnerScreenX", "mozInnerScreenY", "mozScreenPixelsPerCssPixel",
|
||||
"name", "navigator", "opener", "outerHeight", "outerWidth",
|
||||
"pageXOffset", "pageYOffset", "parent", "personalbar", "pkcs11",
|
||||
"returnValue", "screen", "scrollbars", "scrollMaxX", "scrollMaxY",
|
||||
"self", "sessionStorage", "sidebar", "status", "statusbar", "toolbar",
|
||||
"top", "window",
|
||||
|
||||
// Window methods (https://developer.mozilla.org/en/DOM/window)
|
||||
"alert", "addEventListener", "atob", "back", "blur", "btoa",
|
||||
"captureEvents", "clearInterval", "clearTimeout", "close", "confirm",
|
||||
"disableExternalCapture", "dispatchEvent", "dump",
|
||||
"enableExternalCapture", "escape", "find", "focus", "forward",
|
||||
"GeckoActiveXObject", "getAttention", "getAttentionWithCycleCount",
|
||||
"getComputedStyle", "getSelection", "home", "maximize", "minimize",
|
||||
"moveBy", "moveTo", "open", "openDialog", "postMessage", "print",
|
||||
"prompt", "QueryInterface", "releaseEvents", "removeEventListener",
|
||||
"resizeBy", "resizeTo", "restore", "routeEvent", "scroll", "scrollBy",
|
||||
"scrollByLines", "scrollByPages", "scrollTo", "setInterval",
|
||||
"setResizeable", "setTimeout", "showModalDialog", "sizeToContent",
|
||||
"stop", "uuescape", "updateCommands", "XPCNativeWrapper",
|
||||
"XPCSafeJSOjbectWrapper",
|
||||
|
||||
// Mozilla Window event handlers, same cite
|
||||
"onabort", "onbeforeunload", "onchange", "onclick", "onclose",
|
||||
"oncontextmenu", "ondragdrop", "onerror", "onfocus", "onhashchange",
|
||||
"onkeydown", "onkeypress", "onkeyup", "onload", "onmousedown",
|
||||
"onmousemove", "onmouseout", "onmouseover", "onmouseup",
|
||||
"onmozorientation", "onpaint", "onreset", "onresize", "onscroll",
|
||||
"onselect", "onsubmit", "onunload",
|
||||
|
||||
// Safari Web Content Guide
|
||||
// http://developer.apple.com/library/safari/#documentation/AppleApplications/Reference/SafariWebContent/SafariWebContent.pdf
|
||||
// WebKit Window member data, from WebKit DOM Reference
|
||||
// (http://developer.apple.com/safari/library/documentation/AppleApplications/Reference/WebKitDOMRef/DOMWindow_idl/Classes/DOMWindow/index.html)
|
||||
// TODO(fredsa) Many, many more functions and member data to add
|
||||
"ontouchcancel", "ontouchend", "ontouchmove", "ontouchstart",
|
||||
"ongesturestart", "ongesturechange", "ongestureend",
|
||||
|
||||
// extra window methods
|
||||
"uneval",
|
||||
|
||||
// keywords https://developer.mozilla.org/en/New_in_JavaScript_1.7,
|
||||
// https://developer.mozilla.org/en/New_in_JavaScript_1.8.1
|
||||
"getPrototypeOf", "let", "yield",
|
||||
|
||||
// "future reserved words"
|
||||
"abstract", "int", "short", "boolean", "interface", "static", "byte",
|
||||
"long", "char", "final", "native", "synchronized", "float", "package",
|
||||
"throws", "goto", "private", "transient", "implements", "protected",
|
||||
"volatile", "double", "public",
|
||||
|
||||
// IE methods
|
||||
// (http://msdn.microsoft.com/en-us/library/ms535873(VS.85).aspx#)
|
||||
"attachEvent", "clientInformation", "clipboardData", "createPopup",
|
||||
"dialogHeight", "dialogLeft", "dialogTop", "dialogWidth",
|
||||
"onafterprint", "onbeforedeactivate", "onbeforeprint",
|
||||
"oncontrolselect", "ondeactivate", "onhelp", "onresizeend",
|
||||
|
||||
// Common browser-defined identifiers not defined in ECMAScript
|
||||
"event", "external", "Debug", "Enumerator", "Global", "Image",
|
||||
"ActiveXObject", "VBArray", "Components",
|
||||
|
||||
// Functions commonly defined on Object
|
||||
"toString", "getClass", "constructor", "prototype", "valueOf",
|
||||
|
||||
// Client-side JavaScript identifiers, which are needed for linkers
|
||||
// that don't ensure GWT's window != $wnd, document != $doc, etc.
|
||||
// Taken from the Rhino book, pg 715
|
||||
"Anchor", "Applet", "Attr", "Canvas", "CanvasGradient",
|
||||
"CanvasPattern", "CanvasRenderingContext2D", "CDATASection",
|
||||
"CharacterData", "Comment", "CSS2Properties", "CSSRule",
|
||||
"CSSStyleSheet", "Document", "DocumentFragment", "DocumentType",
|
||||
"DOMException", "DOMImplementation", "DOMParser", "Element", "Event",
|
||||
"ExternalInterface", "FlashPlayer", "Form", "Frame", "History",
|
||||
"HTMLCollection", "HTMLDocument", "HTMLElement", "IFrame", "Image",
|
||||
"Input", "JSObject", "KeyEvent", "Link", "Location", "MimeType",
|
||||
"MouseEvent", "Navigator", "Node", "NodeList", "Option", "Plugin",
|
||||
"ProcessingInstruction", "Range", "RangeException", "Screen", "Select",
|
||||
"Table", "TableCell", "TableRow", "TableSelection", "Text", "TextArea",
|
||||
"UIEvent", "Window", "XMLHttpRequest", "XMLSerializer",
|
||||
"XPathException", "XPathResult", "XSLTProcessor",
|
||||
|
||||
// These keywords trigger the loading of the java-plugin. For the
|
||||
// next-generation plugin, this results in starting a new Java process.
|
||||
"java", "Packages", "netscape", "sun", "JavaObject", "JavaClass",
|
||||
"JavaArray", "JavaMember",
|
||||
|
||||
// GWT-defined identifiers
|
||||
"$wnd", "$doc", "$entry", "$moduleName", "$moduleBase", "$gwt_version", "$sessionId",
|
||||
|
||||
// Identifiers used by JsStackEmulator; later set to obfuscatable
|
||||
"$stack", "$stackDepth", "$location",
|
||||
|
||||
// TODO: prove why this is necessary or remove it
|
||||
"call"
|
||||
};
|
||||
for (int i = 0; i < commonBuiltins.length; i++) {
|
||||
reservedGlobalSymbols.add(commonBuiltins[i]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the given string can not be used as property symbol. The check excludes
|
||||
* keywords as JavaScript allow keywords as properties.
|
||||
* @param s
|
||||
* @return true if the given string must not be used as a property.
|
||||
*/
|
||||
public static boolean isReservedPropertySymbol(String s) {
|
||||
return reservedPropertySymbols.contains(s);
|
||||
}
|
||||
|
||||
private static void initReservedPropertySymbols() {
|
||||
// TODO(floitsch): fill in reserved property symbols.
|
||||
reservedPropertySymbols.add("__PROTO__");
|
||||
reservedPropertySymbols.add("prototype");
|
||||
}
|
||||
|
||||
private JsReservedIdentifiers() {
|
||||
}
|
||||
}
|
||||
@@ -1,39 +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.dart.compiler.backend.js.ast.JsBlock;
|
||||
import com.google.dart.compiler.backend.js.ast.JsContext;
|
||||
import com.google.dart.compiler.backend.js.ast.JsProgram;
|
||||
import com.google.dart.compiler.backend.js.ast.JsProgramFragment;
|
||||
import com.google.dart.compiler.util.TextOutput;
|
||||
|
||||
/**
|
||||
* Generates JavaScript source from an AST.
|
||||
*/
|
||||
public class JsSourceGenerationVisitor extends JsToStringGenerationVisitor {
|
||||
|
||||
public JsSourceGenerationVisitor(TextOutput out) {
|
||||
super(out);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean visit(JsProgram x, JsContext ctx) {
|
||||
// Descend naturally.
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean visit(JsProgramFragment x, JsContext ctx) {
|
||||
// Descend naturally.
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean visit(JsBlock x, JsContext ctx) {
|
||||
printJsBlock(x, false, true);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,21 +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.dart.compiler.ast.DartContext;
|
||||
import com.google.dart.compiler.ast.DartNode;
|
||||
import com.google.dart.compiler.ast.DartVisitable;
|
||||
import com.google.dart.compiler.ast.DartVisitor;
|
||||
|
||||
/**
|
||||
* A visitor that always visits the normalized node.
|
||||
*/
|
||||
class NormalizedVisitor extends DartVisitor {
|
||||
@Override
|
||||
protected void doTraverse(DartVisitable visitable, DartContext ctx) {
|
||||
DartNode node = (DartNode) visitable;
|
||||
super.doTraverse(node.getNormalizedNode(), ctx);
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,533 +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.collect.Lists;
|
||||
import com.google.common.collect.Maps;
|
||||
import com.google.common.collect.Sets;
|
||||
import com.google.dart.compiler.ast.DartBlock;
|
||||
import com.google.dart.compiler.ast.DartCatchBlock;
|
||||
import com.google.dart.compiler.ast.DartClass;
|
||||
import com.google.dart.compiler.ast.DartClassMember;
|
||||
import com.google.dart.compiler.ast.DartContext;
|
||||
import com.google.dart.compiler.ast.DartExpression;
|
||||
import com.google.dart.compiler.ast.DartField;
|
||||
import com.google.dart.compiler.ast.DartForInStatement;
|
||||
import com.google.dart.compiler.ast.DartForStatement;
|
||||
import com.google.dart.compiler.ast.DartFunction;
|
||||
import com.google.dart.compiler.ast.DartFunctionExpression;
|
||||
import com.google.dart.compiler.ast.DartFunctionObjectInvocation;
|
||||
import com.google.dart.compiler.ast.DartIdentifier;
|
||||
import com.google.dart.compiler.ast.DartInitializer;
|
||||
import com.google.dart.compiler.ast.DartMethodDefinition;
|
||||
import com.google.dart.compiler.ast.DartNode;
|
||||
import com.google.dart.compiler.ast.DartParameter;
|
||||
import com.google.dart.compiler.ast.DartThisExpression;
|
||||
import com.google.dart.compiler.ast.DartTypeParameter;
|
||||
import com.google.dart.compiler.ast.DartUnit;
|
||||
import com.google.dart.compiler.ast.DartVariable;
|
||||
import com.google.dart.compiler.backend.js.ast.JsName;
|
||||
import com.google.dart.compiler.backend.js.ast.JsScope;
|
||||
import com.google.dart.compiler.common.Symbol;
|
||||
import com.google.dart.compiler.resolver.Element;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.Deque;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Information relating to a methods scope, symbols, and closures
|
||||
*/
|
||||
class ScopeRootInfo {
|
||||
/**
|
||||
* Defines a relationship between a DartSymbol and a scope.
|
||||
*/
|
||||
static class DartScope {
|
||||
/**
|
||||
* A simple class for storing information about a symbol, specifically
|
||||
* whether the symbol is referenced by a closure.
|
||||
*/
|
||||
static class DartSymbolInfo {
|
||||
private final DartScope owningScope;
|
||||
private boolean referencedFromClosure = false;
|
||||
|
||||
public DartSymbolInfo(DartScope owningScope) {
|
||||
this.owningScope = owningScope;
|
||||
}
|
||||
|
||||
public DartScope getOwningScope() {
|
||||
return owningScope;
|
||||
}
|
||||
|
||||
public boolean isReferencedFromClosure() {
|
||||
return referencedFromClosure;
|
||||
}
|
||||
|
||||
public void setReferencedFromClosure(boolean referencedFromClosure) {
|
||||
this.referencedFromClosure = referencedFromClosure;
|
||||
}
|
||||
}
|
||||
|
||||
private final DartScope parent;
|
||||
private Map<Symbol, DartScope.DartSymbolInfo> symbols = Maps.newLinkedHashMap();
|
||||
private Map<JsScope, JsName> jsAliasNames = Maps.newHashMap();
|
||||
// The scope's depth from the initial method definition scope.
|
||||
private final int depth;
|
||||
|
||||
public DartScope(DartScope parent) {
|
||||
this.parent = parent;
|
||||
this.depth = (parent != null) ? parent.getDepth() + 1 : 0;
|
||||
}
|
||||
|
||||
public void declare(Symbol symbol) {
|
||||
symbols.put(symbol, new DartSymbolInfo(this));
|
||||
}
|
||||
|
||||
public int getDepth() {
|
||||
return depth;
|
||||
}
|
||||
|
||||
public DartScope getParent() {
|
||||
return parent;
|
||||
}
|
||||
|
||||
public DartScope findSymbolScope(Symbol symbol) {
|
||||
DartScope current = this;
|
||||
while (current != null) {
|
||||
DartScope.DartSymbolInfo info = current.getSymbolInfo(symbol);
|
||||
if (info != null) {
|
||||
return current;
|
||||
}
|
||||
current = current.getParent();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public DartScope.DartSymbolInfo getSymbolInfo(Symbol symbol) {
|
||||
return this.symbols.get(symbol);
|
||||
}
|
||||
|
||||
public Map<Symbol, DartScope.DartSymbolInfo> getSymbols() {
|
||||
return symbols;
|
||||
}
|
||||
|
||||
public boolean definesClosureReferencedSymbols() {
|
||||
for (DartScope.DartSymbolInfo symbol : symbols.values()) {
|
||||
if (symbol.isReferencedFromClosure()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private String getScopeAliasName() {
|
||||
return "dartc_scp$" + depth;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the alias-object-JsName of this DartScope in the given JsScope. If none exists yet,
|
||||
* creates a new one.
|
||||
*
|
||||
* @param jsScope
|
||||
* @return the JsName representing the alias object for this DartScope.
|
||||
*/
|
||||
public JsName getAliasForJsScope(JsScope jsScope) {
|
||||
JsName result = findAliasForJsScope(jsScope);
|
||||
if (result == null) {
|
||||
result = jsScope.declareFreshName(getScopeAliasName());
|
||||
jsAliasNames.put(jsScope, result);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the alias-object-JsName of this DartScope in the given JsScope. If none exists yet,
|
||||
* null is returned.
|
||||
*
|
||||
* @param jsScope
|
||||
* @return the JsName representing the alias object for this DartScope.
|
||||
*/
|
||||
public JsName findAliasForJsScope(JsScope jsScope) {
|
||||
return jsAliasNames.get(jsScope);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A simple class to keep track of the scope referenced by a closure,
|
||||
* also whether the closure references "this".
|
||||
*/
|
||||
public static class ClosureInfo {
|
||||
final Set<DartScope> referencedScopes = Sets.newHashSet();
|
||||
|
||||
boolean referencesThis = false;
|
||||
|
||||
public List<DartScope> getSortedReferencedScopeList() {
|
||||
ArrayList<DartScope> sortedScopes = Lists.newArrayList(
|
||||
referencedScopes);
|
||||
Collections.sort(sortedScopes, new Comparator<DartScope>(){
|
||||
@Override
|
||||
public int compare(DartScope s1, DartScope s2) {
|
||||
return s1.getDepth() - s2.getDepth();
|
||||
}});
|
||||
return sortedScopes;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A generic helper class for visiting DartScope definitions.
|
||||
*/
|
||||
static private abstract class DartScopesVisitor extends NormalizedVisitor {
|
||||
|
||||
// A place to store the constructor initializer list to the initializers
|
||||
// can be visited within the scope of the constructor's parameters.
|
||||
private List<DartInitializer> pendingConstructorInitList = null;
|
||||
|
||||
@Override
|
||||
public boolean visit(DartUnit x, DartContext ctx) {
|
||||
return enterScope(x, ctx);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean visit(DartClass x, DartContext ctx) {
|
||||
return enterScope(x, ctx);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean visit(DartMethodDefinition x, DartContext ctx) {
|
||||
this.pendingConstructorInitList = x.getInitializers();
|
||||
accept(x.getFunction());
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void endVisit(DartMethodDefinition x, DartContext ctx) {
|
||||
assert this.pendingConstructorInitList == null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean visit(DartFunctionExpression x, DartContext ctx) {
|
||||
// For function statements, the function's name belong in the outer scope.
|
||||
// For function expressions, it is part of the function's own scope.
|
||||
if (!x.isStatement()) {
|
||||
return enterScope(x, ctx);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean visit(DartFunction x, DartContext ctx) {
|
||||
boolean enter = enterScope(x, ctx);
|
||||
if (enter) {
|
||||
// Save and clear the cached init lists before processing
|
||||
// any function as default parameters or in init list.
|
||||
List<DartInitializer> inits = pendingConstructorInitList;
|
||||
pendingConstructorInitList = null;
|
||||
acceptList(x.getParams());
|
||||
if (inits != null) {
|
||||
acceptList(inits);
|
||||
}
|
||||
if (x.getBody() != null) {
|
||||
accept(x.getBody());
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean visit(DartBlock x, DartContext ctx) {
|
||||
return enterScope(x, ctx);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean visit(DartCatchBlock x, DartContext ctx) {
|
||||
return enterScope(x, ctx);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean visit(DartForInStatement x, DartContext ctx) {
|
||||
return enterScope(x, ctx);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean visit(DartForStatement x, DartContext ctx) {
|
||||
return enterScope(x, ctx);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void endVisit(DartUnit x, DartContext ctx) {
|
||||
exitScope(x, ctx);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void endVisit(DartClass x, DartContext ctx) {
|
||||
exitScope(x, ctx);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void endVisit(DartFunctionExpression x, DartContext ctx) {
|
||||
if (!x.isStatement()) {
|
||||
exitScope(x, ctx);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void endVisit(DartFunction x, DartContext ctx) {
|
||||
exitScope(x, ctx);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void endVisit(DartBlock x, DartContext ctx) {
|
||||
exitScope(x, ctx);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void endVisit(DartCatchBlock x, DartContext ctx) {
|
||||
exitScope(x, ctx);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void endVisit(DartForStatement x, DartContext ctx) {
|
||||
exitScope(x, ctx);
|
||||
}
|
||||
|
||||
abstract boolean enterScope(DartNode x, DartContext ctx);
|
||||
abstract void exitScope(DartNode x, DartContext ctx);
|
||||
}
|
||||
|
||||
/**
|
||||
* Build a set of ClosureInfo objects for the method,
|
||||
* find and mark any variable referenced by a closure.
|
||||
*/
|
||||
private static class ClosureRefenceMapBuilder extends ScopeRootInfo.DartScopesVisitor {
|
||||
private final Map<DartNode, DartScope> scopes;
|
||||
private Deque<DartScope> scopeStack = Lists.newLinkedList();
|
||||
private final Map<DartFunction, ScopeRootInfo.ClosureInfo> closures = Maps.newHashMap();
|
||||
private Deque<DartFunction> closureStack = Lists.newLinkedList();
|
||||
private final boolean respectInlinableModifier;
|
||||
|
||||
ClosureRefenceMapBuilder(Map<DartNode, DartScope> scopes, boolean respectInlinableModifier) {
|
||||
this.scopes = scopes;
|
||||
this.respectInlinableModifier = respectInlinableModifier;
|
||||
}
|
||||
|
||||
@Override
|
||||
boolean enterScope(DartNode x, DartContext ctx) {
|
||||
scopeStack.push(scopes.get(x));
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
void exitScope(DartNode x, DartContext ctx) {
|
||||
scopeStack.pop();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean visit(DartFunctionObjectInvocation x, DartContext ctx) {
|
||||
DartExpression target = x.getTarget();
|
||||
if (target instanceof DartFunctionExpression) {
|
||||
DartFunctionExpression functionExpression = (DartFunctionExpression) target;
|
||||
if (respectInlinableModifier &&
|
||||
functionExpression.getSymbol().getModifiers().isInlinable()) {
|
||||
acceptList(x.getArgs());
|
||||
return traverseFunction(functionExpression.getFunction(), ctx);
|
||||
}
|
||||
}
|
||||
return super.visit(x, ctx);
|
||||
}
|
||||
|
||||
// Inlined from DartFunction#traverse(DartVisitor, DartContext).
|
||||
private boolean traverseFunction(DartFunction function, DartContext ctx) {
|
||||
for (DartParameter parameter : function.getParams()) {
|
||||
doTraverse(parameter, ctx);
|
||||
}
|
||||
if (function.getBody() != null) {
|
||||
doTraverse(function.getBody(), ctx);
|
||||
}
|
||||
// Ignore the return type.
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean visit(DartFunction x, DartContext ctx) {
|
||||
this.closures.put(x, new ClosureInfo());
|
||||
this.closureStack.push(x);
|
||||
return super.visit(x, ctx);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void endVisit(DartFunction x, DartContext ctx) {
|
||||
closureStack.pop();
|
||||
super.endVisit(x, ctx);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void endVisit(DartIdentifier x, DartContext ctx) {
|
||||
processSymbol(x.getTargetSymbol());
|
||||
super.endVisit(x, ctx);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void endVisit(DartThisExpression x, DartContext ctx) {
|
||||
for (DartFunction closure : closureStack) {
|
||||
ScopeRootInfo.ClosureInfo info = closures.get(closure);
|
||||
info.referencesThis = true;
|
||||
}
|
||||
}
|
||||
|
||||
private void processSymbol(Symbol targetSymbol) {
|
||||
if (targetSymbol != null) {
|
||||
DartNode node = targetSymbol.getNode();
|
||||
if (node instanceof DartClassMember<?>) {
|
||||
// Special case: implicit instance/static member references.
|
||||
DartClassMember<?> member = (DartClassMember<?>) node;
|
||||
if (!member.getModifiers().isStatic()) {
|
||||
// A member reference implies a reference to the current "this"
|
||||
// object, the closure will need to pass it through.
|
||||
for (DartFunction closure : closureStack) {
|
||||
ScopeRootInfo.ClosureInfo info = closures.get(closure);
|
||||
info.referencesThis = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (closureStack.size() > 0) {
|
||||
DartScope currentScope = this.scopeStack.peek();
|
||||
DartScope symbolScope = currentScope.findSymbolScope(targetSymbol);
|
||||
if (symbolScope != null) {
|
||||
boolean referencedFromClosure = false;
|
||||
for (DartFunction closure : closureStack) {
|
||||
DartScope closureScope = scopes.get(closure);
|
||||
// For each of the closures, determine if the value is defined
|
||||
// outside outside the current closure, if so record its use.
|
||||
if (symbolScope.getDepth() < closureScope.getDepth()) {
|
||||
ScopeRootInfo.ClosureInfo info = closures.get(closure);
|
||||
info.referencedScopes.add(symbolScope);
|
||||
referencedFromClosure = true;
|
||||
}
|
||||
}
|
||||
if (referencedFromClosure) {
|
||||
symbolScope.getSymbolInfo(targetSymbol)
|
||||
.setReferencedFromClosure(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Build the DartScope objects for a method.
|
||||
*/
|
||||
static private class MethodScopeMapBuilder extends ScopeRootInfo.DartScopesVisitor {
|
||||
private Map<DartNode, DartScope> scopes = Maps.newLinkedHashMap();
|
||||
private Deque<DartScope> scopeStack = Lists.newLinkedList();
|
||||
|
||||
@Override
|
||||
boolean enterScope(DartNode x, DartContext ctx) {
|
||||
scopeStack.push(new DartScope(scopeStack.peek()));
|
||||
scopes.put(x, scopeStack.peek());
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
void exitScope(DartNode x, DartContext ctx) {
|
||||
scopeStack.pop();
|
||||
}
|
||||
|
||||
// TODO(johnlenz): Handle catch exception declarations.
|
||||
|
||||
@Override
|
||||
public void endVisit(DartParameter x, DartContext ctx) {
|
||||
DartScope currentScope = scopeStack.peek();
|
||||
currentScope.declare(x.getSymbol());
|
||||
super.endVisit(x, ctx);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void endVisit(DartVariable x, DartContext ctx) {
|
||||
DartScope currentScope = scopeStack.peek();
|
||||
currentScope.declare(x.getSymbol());
|
||||
super.endVisit(x, ctx);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean visit(DartFunctionExpression x, DartContext ctx) {
|
||||
DartScope currentScope = scopeStack.peek();
|
||||
boolean visit = super.visit(x, ctx);
|
||||
if (!x.isStatement()) {
|
||||
// Declare the symbol in the new scope
|
||||
currentScope = scopeStack.peek();
|
||||
}
|
||||
currentScope.declare(x.getSymbol());
|
||||
return visit;
|
||||
}
|
||||
}
|
||||
|
||||
private final DartClassMember<?> classMember;
|
||||
private final Map<Symbol, DartScope.DartSymbolInfo> symbols = Maps.newHashMap();
|
||||
private final Map<DartFunction, ScopeRootInfo.ClosureInfo> closures;
|
||||
private final Map<DartNode, DartScope> scopes;
|
||||
private int closureIds = 0;
|
||||
|
||||
static ScopeRootInfo makeScopeInfo(DartMethodDefinition x, boolean respectInlinableModifier) {
|
||||
return makeScopeInfoImpl(x, respectInlinableModifier);
|
||||
}
|
||||
|
||||
static ScopeRootInfo makeScopeInfo(DartField x, boolean respectInlinableModifier) {
|
||||
return makeScopeInfoImpl(x, respectInlinableModifier);
|
||||
}
|
||||
|
||||
private static ScopeRootInfo makeScopeInfoImpl(DartClassMember<?> x,
|
||||
boolean respectInlinableModifier) {
|
||||
ScopeRootInfo.MethodScopeMapBuilder scopeBuilder = new MethodScopeMapBuilder();
|
||||
scopeBuilder.accept(x);
|
||||
ScopeRootInfo.ClosureRefenceMapBuilder closureBuilder = new ClosureRefenceMapBuilder(
|
||||
scopeBuilder.scopes, respectInlinableModifier);
|
||||
closureBuilder.accept(x);
|
||||
return new ScopeRootInfo(x, scopeBuilder.scopes, closureBuilder.closures);
|
||||
}
|
||||
|
||||
ScopeRootInfo(
|
||||
DartClassMember<?> x, Map<DartNode, DartScope> scopes,
|
||||
Map<DartFunction, ScopeRootInfo.ClosureInfo> closures) {
|
||||
this.classMember = x;
|
||||
this.closures = closures;
|
||||
this.scopes = scopes;
|
||||
for (DartScope scope : scopes.values()) {
|
||||
this.symbols.putAll(scope.getSymbols());
|
||||
}
|
||||
}
|
||||
|
||||
Element getContainingElement() {
|
||||
return classMember.getSymbol();
|
||||
}
|
||||
|
||||
DartClassMember<?> getContainingClassMember() {
|
||||
return classMember;
|
||||
}
|
||||
|
||||
DartScope.DartSymbolInfo getSymbolInfo(Symbol targetSymbol) {
|
||||
return symbols.get(targetSymbol);
|
||||
}
|
||||
|
||||
public ScopeRootInfo.ClosureInfo getClosureInfo(DartFunction x) {
|
||||
return closures.get(x);
|
||||
}
|
||||
|
||||
public DartScope getScope(DartNode x) {
|
||||
return scopes.get(x);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return A name for a closure unique within this method.
|
||||
*/
|
||||
public String getNextClosureName() {
|
||||
return "c" + closureIds++;
|
||||
}
|
||||
}
|
||||
@@ -1,78 +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.dart.compiler.ast.DartFunction;
|
||||
import com.google.dart.compiler.ast.DartNode;
|
||||
import com.google.dart.compiler.ast.DartUnit;
|
||||
import com.google.dart.compiler.backend.js.ast.JsFunction;
|
||||
import com.google.dart.compiler.backend.js.ast.JsProgram;
|
||||
import com.google.dart.compiler.backend.js.ast.JsScope;
|
||||
import com.google.dart.compiler.resolver.Element;
|
||||
import com.google.dart.compiler.resolver.LibraryElement;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Information generated by {@link GenerateNamesAndScopes} and consumed by
|
||||
* {@link GenerateJavascriptAST}.
|
||||
*/
|
||||
public class TranslationContext {
|
||||
private final DartMangler mangler;
|
||||
private final Map<Element, JsScope> memberScopes = new HashMap<Element, JsScope>();
|
||||
private final Map<DartFunction, JsFunction> methods = new HashMap<DartFunction, JsFunction>();
|
||||
private final JsNameProvider names;
|
||||
private final JsProgram program;
|
||||
|
||||
private TranslationContext(JsProgram program, DartMangler mangler) {
|
||||
this.program = program;
|
||||
this.mangler = mangler;
|
||||
this.names = new JsNameProvider(program, mangler);
|
||||
}
|
||||
|
||||
public DartMangler getMangler() {
|
||||
return mangler;
|
||||
}
|
||||
|
||||
public Map<Element, JsScope> getMemberScopes() {
|
||||
return memberScopes;
|
||||
}
|
||||
|
||||
public Map<DartFunction, JsFunction> getMethods() {
|
||||
return methods;
|
||||
}
|
||||
|
||||
public JsNameProvider getNames() {
|
||||
return names;
|
||||
}
|
||||
|
||||
public JsProgram getProgram() {
|
||||
return program;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param unit Unit to create translation context for
|
||||
* @param program
|
||||
* @param mangler
|
||||
* @param filterNode If not null, create names for this node only.
|
||||
* @return
|
||||
*/
|
||||
public static TranslationContext createContext(DartUnit unit, JsProgram program,
|
||||
DartMangler mangler, DartNode filterNode) {
|
||||
TranslationContext translationContext = new TranslationContext(program, mangler);
|
||||
LibraryElement unitLibrary = unit.getLibrary().getElement();
|
||||
GenerateNamesAndScopes visitor = new GenerateNamesAndScopes(translationContext, unitLibrary);
|
||||
if (filterNode != null) {
|
||||
visitor.accept(filterNode);
|
||||
} else {
|
||||
visitor.accept(unit);
|
||||
}
|
||||
return translationContext;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -1,40 +0,0 @@
|
||||
// Copyright 2011, the Dart project authors. All rights reserved.
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following
|
||||
// disclaimer in the documentation and/or other materials provided
|
||||
// with the distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived
|
||||
// from this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
package com.google.dart.compiler.backend.js;
|
||||
|
||||
import com.google.dart.compiler.ast.DartClassMember;
|
||||
import com.google.dart.compiler.backend.js.ast.JsName;
|
||||
|
||||
/**
|
||||
* @author johnlenz@google.com (John Lenz)
|
||||
*/
|
||||
public interface TraversalContextProvider {
|
||||
DartClassMember<?> getCurrentClassMember();
|
||||
|
||||
JsName createTemporary();
|
||||
}
|
||||
@@ -1,21 +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;
|
||||
|
||||
/**
|
||||
* An unchecked wrapper exception to interop with Rhino.
|
||||
*/
|
||||
class UncheckedJsParserException extends RuntimeException {
|
||||
|
||||
private final JsParserException parserException;
|
||||
|
||||
public UncheckedJsParserException(JsParserException parserException) {
|
||||
this.parserException = parserException;
|
||||
}
|
||||
|
||||
public JsParserException getParserException() {
|
||||
return parserException;
|
||||
}
|
||||
}
|
||||
@@ -1,262 +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.analysis;
|
||||
|
||||
import org.mozilla.javascript.Token;
|
||||
import org.mozilla.javascript.ast.AstNode;
|
||||
import org.mozilla.javascript.ast.FunctionNode;
|
||||
import org.mozilla.javascript.ast.Name;
|
||||
import org.mozilla.javascript.ast.NewExpression;
|
||||
import org.mozilla.javascript.ast.NodeVisitor;
|
||||
import org.mozilla.javascript.ast.PropertyGet;
|
||||
import org.mozilla.javascript.ast.Scope;
|
||||
import org.mozilla.javascript.ast.Symbol;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Computes the set of dependencies that a given AstNode node has.
|
||||
*/
|
||||
class DependencyComputer {
|
||||
/**
|
||||
* Visitor for determining what dependencies an AstNode has.
|
||||
*/
|
||||
class DependencyComputingVisitor implements NodeVisitor {
|
||||
/**
|
||||
* Adds a dependency on the given identifier. If the identifier is virtual then then a
|
||||
* dependency is only added if the enclosing "class" has been instantiated.
|
||||
*/
|
||||
private void addDependency(String identifier, boolean isVirtual) {
|
||||
List<JavascriptElement> members = namesToElements.get(identifier);
|
||||
if (members != null) {
|
||||
for (JavascriptElement member : members) {
|
||||
if (isVirtual && member.isVirtual()) {
|
||||
JavascriptElement enclosingElement = member.getEnclosingElement();
|
||||
if (enclosingElement != null && enclosingElement.isInstantiated()) {
|
||||
dependencies.add(member);
|
||||
}
|
||||
} else {
|
||||
if (!member.isVirtual()) {
|
||||
if (member.getEnclosingElement() != null) {
|
||||
dependencies.add(member.getEnclosingElement());
|
||||
}
|
||||
}
|
||||
dependencies.add(member);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Record that we saw a new of a given identifier.
|
||||
*/
|
||||
private void addInstantiation(String identifier) {
|
||||
List<JavascriptElement> instantiatedClasses = namesToElements.get(identifier);
|
||||
if (instantiatedClasses != null) {
|
||||
for (JavascriptElement instantiatedClass : instantiatedClasses) {
|
||||
instantiatedClass.setInstantiated(true);
|
||||
|
||||
/*
|
||||
* Whenever we see an instantiation we must check it and any super type for members that
|
||||
* match the virtual names seen to date and we must ensure that the corresponding inherits
|
||||
* get emitted.
|
||||
*/
|
||||
while (instantiatedClass != null) {
|
||||
JavascriptElement inheritsInvocation = instantiatedClass.getInheritsInvocation();
|
||||
if (inheritsInvocation != null) {
|
||||
dependencies.add(inheritsInvocation);
|
||||
}
|
||||
|
||||
for (JavascriptElement member : instantiatedClass.getMembers()) {
|
||||
if (virtualNames.contains(member.getName())) {
|
||||
dependencies.add(member);
|
||||
}
|
||||
}
|
||||
|
||||
instantiatedClass = instantiatedClass.getInheritsElement();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void addStaticDependency(String identifier) {
|
||||
addDependency(identifier, false);
|
||||
}
|
||||
|
||||
private void addVirtualDependency(String identifier) {
|
||||
virtualNames.add(identifier);
|
||||
addDependency(identifier, true);
|
||||
}
|
||||
|
||||
Symbol findSymbol(Scope scope, String name) {
|
||||
if (scope == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
Symbol symbol = scope.getSymbol(name);
|
||||
if (symbol == null) {
|
||||
Scope parentScope = scope.getParentScope();
|
||||
if (parentScope == null && (scope != scope.getAstRoot())) {
|
||||
return findSymbol(scope.getAstRoot(), name);
|
||||
} else {
|
||||
return findSymbol(parentScope, name);
|
||||
}
|
||||
}
|
||||
|
||||
return symbol;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the name is a local or parameter name. However, if the name is on the
|
||||
* right hand side of a property get then we don't consider this name to be a local variable.
|
||||
*/
|
||||
boolean isLocalVariableOrParameter(Name name) {
|
||||
Scope definingScope = name.getDefiningScope();
|
||||
Scope enclosingScope = name.getEnclosingScope();
|
||||
if (definingScope != null) {
|
||||
Symbol symbol = definingScope.getSymbol(name.getIdentifier());
|
||||
if (definingScope.getType() == Token.FUNCTION) {
|
||||
if (symbol != null && (symbol.getDeclType() == Token.VAR)
|
||||
|| (symbol.getDeclType() == Token.LP)) {
|
||||
|
||||
if (name.getParent().getType() == Token.GETPROP) {
|
||||
PropertyGet propertyGet = (PropertyGet) name.getParent();
|
||||
return propertyGet.getRight() != name;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns <code>true</code> if the name is associated with a native
|
||||
* function or a top level function.
|
||||
*/
|
||||
private boolean isNativeOrTopLevelFunction(Scope enclosingScope, String name) {
|
||||
List<JavascriptElement> list = namesToElements.get(name);
|
||||
if (list == null || list.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Symbol symbol = findSymbol(enclosingScope, name);
|
||||
if (symbol != null
|
||||
&& (symbol.getDeclType() == Token.FUNCTION || symbol.getDeclType() == Token.VAR)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
JavascriptElement javascriptElement = list.get(0);
|
||||
return javascriptElement.isNative();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a static name if the {@link PropertyGet} matches the pattern x.y or
|
||||
* x.prototype.y or <code>null</code> if it does not.
|
||||
*/
|
||||
String maybeGetStaticName(PropertyGet propertyGet) {
|
||||
AstNode right = propertyGet.getRight();
|
||||
int rightType = right.getType();
|
||||
if (rightType != Token.NAME) {
|
||||
return null;
|
||||
}
|
||||
|
||||
AstNode left = propertyGet.getLeft();
|
||||
int leftType = left.getType();
|
||||
if (leftType == Token.NAME) {
|
||||
String qualifier = ((Name) left).getIdentifier();
|
||||
|
||||
if (isNativeOrTopLevelFunction(left.getEnclosingScope(), qualifier)) {
|
||||
String targetName = ((Name) right).getIdentifier();
|
||||
String qualifiedName = qualifier + "." + targetName;
|
||||
if ("prototype".equals(targetName)) {
|
||||
return qualifiedName;
|
||||
} else if (namesToElements.containsKey(qualifiedName)) {
|
||||
return qualifiedName;
|
||||
}
|
||||
}
|
||||
} else if (leftType == Token.GETPROP) {
|
||||
PropertyGet leftPropGet = (PropertyGet) left;
|
||||
String handleSpecialCase = maybeGetStaticName(leftPropGet);
|
||||
if (handleSpecialCase != null && handleSpecialCase.endsWith("prototype")) {
|
||||
handleSpecialCase = handleSpecialCase + "." + ((Name) right).getIdentifier();
|
||||
if (namesToElements.containsKey(handleSpecialCase)) {
|
||||
return handleSpecialCase;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean visit(AstNode node) {
|
||||
switch (node.getType()) {
|
||||
case Token.GETPROP:
|
||||
PropertyGet propertyGet = (PropertyGet) node;
|
||||
String staticName = maybeGetStaticName(propertyGet);
|
||||
if (staticName != null) {
|
||||
addStaticDependency(staticName);
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
|
||||
case Token.FUNCTION:
|
||||
FunctionNode functionNode = (FunctionNode) node;
|
||||
functionNode.getBody().visit(this);
|
||||
// Don't process the parameters
|
||||
return false;
|
||||
|
||||
case Token.NAME:
|
||||
Name name = (Name) node;
|
||||
if (!isLocalVariableOrParameter(name)) {
|
||||
String identifier = name.getIdentifier();
|
||||
addVirtualDependency(identifier);
|
||||
}
|
||||
break;
|
||||
|
||||
case Token.NEW:
|
||||
NewExpression newExpression = (NewExpression) node;
|
||||
Name target = (Name) newExpression.getTarget();
|
||||
if (target != null) {
|
||||
addInstantiation(target.getIdentifier());
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private final List<JavascriptElement> dependencies = new ArrayList<JavascriptElement>();
|
||||
private final Map<String, List<JavascriptElement>> namesToElements;
|
||||
|
||||
/**
|
||||
* Names that have been referenced using virtual syntax, i.e. not using A.prototype.foo, but
|
||||
* as foo or this.foo, etc.
|
||||
*/
|
||||
private final Set<String> virtualNames = new HashSet<String>();
|
||||
|
||||
public DependencyComputer(Map<String, List<JavascriptElement>> namesToElements) {
|
||||
this.namesToElements = namesToElements;
|
||||
}
|
||||
|
||||
public List<JavascriptElement> computeDependencies(AstNode node) {
|
||||
// Clear the dependencies so this object can be reused.
|
||||
dependencies.clear();
|
||||
node.visit(new DependencyComputingVisitor());
|
||||
return dependencies;
|
||||
}
|
||||
}
|
||||
@@ -1,130 +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.analysis;
|
||||
|
||||
import org.mozilla.javascript.ast.AstNode;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* JavaScript element that could be any subtype of {@link AstNode}. In the cases where the element
|
||||
* is a function any members that are hung off of its prototype chain are also recorded as members.
|
||||
*/
|
||||
class JavascriptElement {
|
||||
private final JavascriptElement enclosingElement;
|
||||
private JavascriptElement inheritsElement;
|
||||
private JavascriptElement inheritsInvocation;
|
||||
private boolean instantiated;
|
||||
private final boolean isVirtual;
|
||||
private List<JavascriptElement> members = null;
|
||||
private final String name;
|
||||
private final AstNode node;
|
||||
private final String qualifiedName;
|
||||
|
||||
public JavascriptElement(JavascriptElement enclosingElement, boolean isVirtual,
|
||||
String qualifiedName, String name, AstNode node) {
|
||||
this.enclosingElement = enclosingElement;
|
||||
this.isVirtual = isVirtual;
|
||||
this.qualifiedName = qualifiedName;
|
||||
this.name = name;
|
||||
this.node = node;
|
||||
if (enclosingElement != null) {
|
||||
enclosingElement.addMember(this);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param javascriptElement
|
||||
*/
|
||||
private void addMember(JavascriptElement javascriptElement) {
|
||||
if (members == null) {
|
||||
members = new LinkedList<JavascriptElement>();
|
||||
}
|
||||
members.add(javascriptElement);
|
||||
}
|
||||
|
||||
public JavascriptElement getEnclosingElement() {
|
||||
return enclosingElement;
|
||||
}
|
||||
|
||||
public JavascriptElement getInheritsElement() {
|
||||
return inheritsElement;
|
||||
}
|
||||
|
||||
public JavascriptElement getInheritsInvocation() {
|
||||
return inheritsInvocation;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the length of this element in characters.
|
||||
*/
|
||||
public int getLength() {
|
||||
return node.getLength();
|
||||
}
|
||||
|
||||
public List<JavascriptElement> getMembers() {
|
||||
if (members == null) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
return members;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public AstNode getNode() {
|
||||
return node;
|
||||
}
|
||||
|
||||
public int getOffset() {
|
||||
return node.getAbsolutePosition();
|
||||
}
|
||||
|
||||
public String getQualifiedName() {
|
||||
return qualifiedName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns <code>true</code> if the element was used in a new expression. For now, we assume that
|
||||
* any javascript native element is instantiated.
|
||||
*/
|
||||
public boolean isInstantiated() {
|
||||
// TODO: Assume that natives are always instantiated for now
|
||||
return instantiated || isNative();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns <code>true</code> if the element was referenced from a top-level expression but was
|
||||
* not declared. For example, we would synthesize a native member if the following top-level
|
||||
* expression is found "Array.prototype.XXX = function(){}". Array is a native JS object.
|
||||
*/
|
||||
public boolean isNative() {
|
||||
return getNode() == null;
|
||||
}
|
||||
|
||||
public boolean isVirtual() {
|
||||
return isVirtual;
|
||||
}
|
||||
|
||||
public void setInherits(JavascriptElement inherits) {
|
||||
this.inheritsElement = inherits;
|
||||
}
|
||||
|
||||
public void setInheritsNode(AstNode inheritsNode) {
|
||||
this.inheritsInvocation = new JavascriptElement(null,false, "", "", inheritsNode);
|
||||
}
|
||||
|
||||
public void setInstantiated(boolean instantiated) {
|
||||
this.instantiated = instantiated;
|
||||
|
||||
if (inheritsElement != null) {
|
||||
inheritsElement.setInstantiated(instantiated);
|
||||
}
|
||||
}
|
||||
}
|
||||
-283
@@ -1,283 +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.analysis;
|
||||
|
||||
import org.mozilla.javascript.Token;
|
||||
import org.mozilla.javascript.ast.Assignment;
|
||||
import org.mozilla.javascript.ast.AstNode;
|
||||
import org.mozilla.javascript.ast.ExpressionStatement;
|
||||
import org.mozilla.javascript.ast.FunctionCall;
|
||||
import org.mozilla.javascript.ast.FunctionNode;
|
||||
import org.mozilla.javascript.ast.Name;
|
||||
import org.mozilla.javascript.ast.NodeVisitor;
|
||||
import org.mozilla.javascript.ast.VariableDeclaration;
|
||||
import org.mozilla.javascript.ast.VariableInitializer;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Deque;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Indexes the top level declarations and expressions contained in a JavaScript AST tree. Where top
|
||||
* level expressions can be global variables, method definitions or invocations. Note that method
|
||||
* bodies are not processed.
|
||||
*/
|
||||
class TopLevelElementIndexer implements NodeVisitor {
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private static final String RUN_ENTRY_METHOD_NAME = "RunEntry";
|
||||
|
||||
/**
|
||||
* Collects {@link Name} {@link AstNode}s.
|
||||
*/
|
||||
static class NameLocator implements NodeVisitor {
|
||||
private static final int PROTOTYPE_DEFAULT_INDEX = -1;
|
||||
private final Deque<String> identifiers = new LinkedList<String>();
|
||||
private int prototypeIndex = PROTOTYPE_DEFAULT_INDEX;
|
||||
|
||||
public String getEnclosingTypeName() {
|
||||
return identifiers.getFirst();
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return identifiers.getLast();
|
||||
}
|
||||
|
||||
public Collection<String> getPossibleNames() {
|
||||
List<String> names = new ArrayList<String>(2);
|
||||
if (hasPrototypeInName()) {
|
||||
names.add(identifiers.getLast());
|
||||
}
|
||||
names.add(getQualifiedName());
|
||||
return names;
|
||||
}
|
||||
|
||||
public String getQualifiedName() {
|
||||
StringBuffer sb = new StringBuffer();
|
||||
Iterator<String> iterator = identifiers.iterator();
|
||||
while (iterator.hasNext()) {
|
||||
sb.append(iterator.next());
|
||||
if (iterator.hasNext()) {
|
||||
sb.append(".");
|
||||
}
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
public boolean hasPrototypeInName() {
|
||||
return prototypeIndex != PROTOTYPE_DEFAULT_INDEX;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean visit(AstNode node) {
|
||||
if (node.getType() == Token.NAME) {
|
||||
Name name = (Name) node;
|
||||
String identifier = name.getIdentifier();
|
||||
if ("prototype".equals(identifier)) {
|
||||
prototypeIndex = identifiers.size();
|
||||
}
|
||||
identifiers.add(identifier);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public static void printGlobals(List<AstNode> globals) {
|
||||
System.out.println("Globals");
|
||||
for (AstNode global : globals) {
|
||||
System.out.println("--------");
|
||||
System.out.println(global.toSource());
|
||||
System.out.println();
|
||||
}
|
||||
}
|
||||
|
||||
public static void printNamesToElements(Map<String, List<JavascriptElement>> namesToElements) {
|
||||
long rttCost = 0;
|
||||
long namedCost = 0;
|
||||
long ctorCost = 0;
|
||||
|
||||
Set<Entry<String, List<JavascriptElement>>> entrySet = namesToElements.entrySet();
|
||||
for (Entry<String, List<JavascriptElement>> entry : entrySet) {
|
||||
System.out.println("--------");
|
||||
System.out.println("Name: " + entry.getKey());
|
||||
for (JavascriptElement javascriptElement : entry.getValue()) {
|
||||
AstNode node = javascriptElement.getNode();
|
||||
if (node == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
System.out.println("Type: " + Token.typeToName(node.getType()));
|
||||
if (javascriptElement.getInheritsElement() != null) {
|
||||
System.out.println("Inherits: " + javascriptElement.getInheritsElement());
|
||||
}
|
||||
|
||||
try {
|
||||
System.out.println(node.toSource());
|
||||
} catch (Exception e) {
|
||||
System.out.println("Failed to print node source code");
|
||||
}
|
||||
|
||||
String name = javascriptElement.getName();
|
||||
if (name.endsWith("$named")) {
|
||||
namedCost += node.getLength();
|
||||
} else if (name.endsWith("$addTo") || name.endsWith("$lookupRTT")
|
||||
|| name.endsWith("$RTTimplements")) {
|
||||
rttCost += node.getLength();
|
||||
} else if (name.endsWith("$Constructor") || name.endsWith("$Initializer")) {
|
||||
ctorCost += node.getLength();
|
||||
}
|
||||
}
|
||||
System.out.println();
|
||||
}
|
||||
|
||||
System.out.println(ctorCost +
|
||||
" characters worth of $Constructor and $Initializer methode declarations");
|
||||
System.out.println(namedCost + " characters worth of $named methods declarations");
|
||||
System.out.println(rttCost + " characters worth of RTT method declarations");
|
||||
}
|
||||
|
||||
private final List<AstNode> entryPoints = new ArrayList<AstNode>();
|
||||
private final List<AstNode> globals;
|
||||
|
||||
private final Map<String, List<JavascriptElement>> namesToElements;
|
||||
|
||||
public TopLevelElementIndexer(Map<String, List<JavascriptElement>> namesToElements,
|
||||
List<AstNode> globals) {
|
||||
this.globals = globals;
|
||||
this.namesToElements = namesToElements;
|
||||
}
|
||||
|
||||
private void addElement(String identifier, JavascriptElement javascriptElement) {
|
||||
List<JavascriptElement> list = namesToElements.get(identifier);
|
||||
if (list == null) {
|
||||
list = new ArrayList<JavascriptElement>(1);
|
||||
namesToElements.put(identifier, list);
|
||||
}
|
||||
list.add(javascriptElement);
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes an AST node that does not define a method. If the node is an invocation to a
|
||||
* RunEntry method or an inherits method update the entry points and element inheritance
|
||||
* hierarchy accordingly. Otherwise we just add this node to the globals block and continue.
|
||||
*/
|
||||
private void processGlobal(AstNode node) {
|
||||
if (node.getType() == Token.EXPR_RESULT) {
|
||||
ExpressionStatement expressionStatement = (ExpressionStatement) node;
|
||||
AstNode expression = expressionStatement.getExpression();
|
||||
if (expression.getType() == Token.CALL) {
|
||||
FunctionCall functionCall = (FunctionCall) expression;
|
||||
AstNode target = functionCall.getTarget();
|
||||
if (target.getType() == Token.NAME) {
|
||||
Name targetName = (Name) target;
|
||||
if (RUN_ENTRY_METHOD_NAME.equals(targetName.getIdentifier())) {
|
||||
// This is a call to an entry point so add it to the known set of entry points.
|
||||
entryPoints.add(node);
|
||||
return;
|
||||
} else if ("$inherits".equals(targetName.getIdentifier())) {
|
||||
// This is an inherits call so update the element's inheritance hierarchy.
|
||||
List<AstNode> arguments = functionCall.getArguments();
|
||||
assert (arguments.size() == 2);
|
||||
assert (arguments.get(0).getType() == Token.NAME);
|
||||
assert (arguments.get(1).getType() == Token.NAME);
|
||||
Name subtype = (Name) arguments.get(0);
|
||||
Name supertype = (Name) arguments.get(1);
|
||||
List<JavascriptElement> subTypeFunctions = namesToElements.get(subtype.getIdentifier());
|
||||
assert (subTypeFunctions != null && subTypeFunctions.size() == 1);
|
||||
JavascriptElement subtypeFunction = subTypeFunctions.get(0);
|
||||
subtypeFunction.setInheritsNode(node);
|
||||
subtypeFunction.setInherits(namesToElements.get(supertype.getIdentifier()).get(0));
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
globals.add(node);
|
||||
}
|
||||
|
||||
public List<AstNode> getEntryPoints() {
|
||||
return entryPoints;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean visit(AstNode node) {
|
||||
if (node == node.getAstRoot()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
switch (node.getType()) {
|
||||
case Token.EXPR_VOID:
|
||||
case Token.EXPR_RESULT:
|
||||
ExpressionStatement expressionStatement = (ExpressionStatement) node;
|
||||
AstNode expression = expressionStatement.getExpression();
|
||||
if (expression.getType() == Token.ASSIGN) {
|
||||
Assignment assignment = (Assignment) expression;
|
||||
NameLocator nameLocator = new NameLocator();
|
||||
assignment.getLeft().visit(nameLocator);
|
||||
String enclosingTypeName = nameLocator.getEnclosingTypeName();
|
||||
JavascriptElement enclosingElement = null;
|
||||
if (enclosingTypeName != null) {
|
||||
List<JavascriptElement> list = namesToElements.get(enclosingTypeName);
|
||||
if (list == null) {
|
||||
// TODO: Assume that this is a native object for now, we could have a whitelist of
|
||||
// native objects if we wanted to.
|
||||
enclosingElement =
|
||||
new JavascriptElement(null, false, enclosingTypeName, nameLocator.getName(),
|
||||
null);
|
||||
addElement(enclosingTypeName, enclosingElement);
|
||||
} else {
|
||||
assert (list != null && list.size() == 1);
|
||||
enclosingElement = list.get(0);
|
||||
}
|
||||
}
|
||||
|
||||
JavascriptElement javascriptElement =
|
||||
new JavascriptElement(enclosingElement, nameLocator.hasPrototypeInName(),
|
||||
nameLocator.getQualifiedName(), nameLocator.getName(), node);
|
||||
for (String identifier : nameLocator.getPossibleNames()) {
|
||||
addElement(identifier, javascriptElement);
|
||||
}
|
||||
} else {
|
||||
processGlobal(node);
|
||||
}
|
||||
break;
|
||||
|
||||
case Token.FUNCTION:
|
||||
FunctionNode functionNode = (FunctionNode) node;
|
||||
String name = functionNode.getName();
|
||||
if (name != null && !name.isEmpty()) {
|
||||
addElement(name, new JavascriptElement(null, false, name, name, node));
|
||||
}
|
||||
break;
|
||||
|
||||
case Token.VAR:
|
||||
VariableDeclaration variableDeclaration = (VariableDeclaration) node;
|
||||
List<VariableInitializer> variables = variableDeclaration.getVariables();
|
||||
for (VariableInitializer variable : variables) {
|
||||
AstNode target = variable.getTarget();
|
||||
if (target.getType() == Token.NAME) {
|
||||
Name variableName = (Name) target;
|
||||
addElement(variableName.getIdentifier(), new JavascriptElement(null, false,
|
||||
variableName.getIdentifier(), variableName.getIdentifier(), node));
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
processGlobal(node);
|
||||
break;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -1,191 +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.analysis;
|
||||
|
||||
import com.google.common.io.CharStreams;
|
||||
import com.google.common.io.Closeables;
|
||||
import com.google.dart.compiler.DartCompilerContext;
|
||||
import com.google.dart.compiler.LibrarySource;
|
||||
|
||||
import org.mozilla.javascript.EvaluatorException;
|
||||
import org.mozilla.javascript.Parser;
|
||||
import org.mozilla.javascript.ast.AstNode;
|
||||
import org.mozilla.javascript.ast.AstRoot;
|
||||
import org.mozilla.javascript.ast.NodeVisitor;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.Reader;
|
||||
import java.io.Writer;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* A JavaScript tree shaker that is specialized for the output produced by
|
||||
* dartc.
|
||||
*/
|
||||
public class TreeShaker {
|
||||
private static class VisitorIOException extends RuntimeException {
|
||||
public VisitorIOException(IOException e) {
|
||||
super(e);
|
||||
}
|
||||
|
||||
@Override
|
||||
public IOException getCause() {
|
||||
return (IOException) super.getCause();
|
||||
}
|
||||
}
|
||||
|
||||
private static final class OutputFileWriter implements NodeVisitor {
|
||||
private final Set<AstNode> nodesToEmit;
|
||||
private final Writer outputFile;
|
||||
private final Reader inputFile;
|
||||
private long lastReadPosition = 0;
|
||||
private long outputSize = 0;
|
||||
|
||||
private OutputFileWriter(Set<AstNode> nodesToEmit, Writer outputFile, Reader inputFile) {
|
||||
this.nodesToEmit = nodesToEmit;
|
||||
this.outputFile = outputFile;
|
||||
this.inputFile = inputFile;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean visit(AstNode node) {
|
||||
if (node.getAstRoot() == node) {
|
||||
return true;
|
||||
}
|
||||
|
||||
try {
|
||||
if (nodesToEmit.contains(node)) {
|
||||
int nodePosition = node.getAbsolutePosition();
|
||||
inputFile.skip(nodePosition - lastReadPosition);
|
||||
|
||||
char[] buffer = new char[node.getLength()];
|
||||
int charsRead = inputFile.read(buffer);
|
||||
assert (charsRead == buffer.length);
|
||||
outputFile.write(buffer);
|
||||
outputFile.write("\n");
|
||||
outputSize += charsRead + 1;
|
||||
lastReadPosition = nodePosition + node.getLength();
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new VisitorIOException(e);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public long getOutputSize() {
|
||||
return outputSize;
|
||||
}
|
||||
}
|
||||
|
||||
private static final boolean DEBUG = false;
|
||||
|
||||
/**
|
||||
* Returns the set of {@link AstNode}s that should be emitted into the final
|
||||
* JS code.
|
||||
*/
|
||||
private static Set<AstNode> computeNodesToEmit(AstRoot root) {
|
||||
List<AstNode> globals = new ArrayList<AstNode>();
|
||||
Map<String, List<JavascriptElement>> namesToElements =
|
||||
new HashMap<String, List<JavascriptElement>>();
|
||||
TopLevelElementIndexer declVisitor = new TopLevelElementIndexer(namesToElements, globals);
|
||||
root.visit(declVisitor);
|
||||
|
||||
if (DEBUG) {
|
||||
TopLevelElementIndexer.printNamesToElements(namesToElements);
|
||||
TopLevelElementIndexer.printGlobals(globals);
|
||||
}
|
||||
|
||||
List<AstNode> worklist = new ArrayList<AstNode>();
|
||||
for (AstNode global : globals) {
|
||||
worklist.add(global);
|
||||
}
|
||||
|
||||
worklist.addAll(declVisitor.getEntryPoints());
|
||||
DependencyComputer dependencyComputer = new DependencyComputer(namesToElements);
|
||||
final Set<AstNode> nodesProcessed = new LinkedHashSet<AstNode>();
|
||||
while (!worklist.isEmpty()) {
|
||||
AstNode node = worklist.remove(worklist.size() - 1);
|
||||
if (!nodesProcessed.add(node)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (DEBUG) {
|
||||
try {
|
||||
System.out.println(node.toSource());
|
||||
System.out.println("Dependencies:");
|
||||
} catch (Exception e) {
|
||||
// Ignore exceptions thrown by rhino's toSource method...
|
||||
}
|
||||
}
|
||||
|
||||
List<JavascriptElement> dependencies = dependencyComputer.computeDependencies(node);
|
||||
for (JavascriptElement dependency : dependencies) {
|
||||
if (dependency.isNative() || nodesProcessed.contains(dependency.getNode())) {
|
||||
// Skip natives since they don't have a node in the AST
|
||||
continue;
|
||||
}
|
||||
|
||||
if (DEBUG) {
|
||||
System.out.println("\t" + dependency.getQualifiedName());
|
||||
}
|
||||
|
||||
worklist.add(dependency.getNode());
|
||||
}
|
||||
}
|
||||
return nodesProcessed;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reduce the input JS file by following the conservative "call graph" and
|
||||
* pruning dead code.
|
||||
*/
|
||||
public static long reduce(LibrarySource app, DartCompilerContext context,
|
||||
String completeArtifactName, Writer outputFile) throws IOException {
|
||||
Reader inputFile = context.getArtifactReader(app, "", completeArtifactName);
|
||||
// Mark beyond the expected length so we can reset back to zero
|
||||
AstRoot root = null;
|
||||
boolean failed = true;
|
||||
try {
|
||||
Parser parser = new Parser();
|
||||
root = parser.parse(inputFile, "", 1);
|
||||
failed = false;
|
||||
} catch (EvaluatorException e) {
|
||||
/*
|
||||
* This can happen if we generate bad JS code. For example, the negative
|
||||
* tests may cause invalid control flow constructs to be generated. In
|
||||
* this case we will swallow the exception and simply copy the input file
|
||||
* to the output file.
|
||||
*/
|
||||
Closeables.close(inputFile, failed);
|
||||
inputFile = context.getArtifactReader(app, "", completeArtifactName);
|
||||
return CharStreams.copy(inputFile, outputFile);
|
||||
} finally {
|
||||
Closeables.close(inputFile, failed);
|
||||
}
|
||||
|
||||
final Set<AstNode> nodesProcessed = computeNodesToEmit(root);
|
||||
|
||||
// Need to get a new reader since we don't cache the stream
|
||||
failed = true;
|
||||
inputFile = context.getArtifactReader(app, "", completeArtifactName);
|
||||
OutputFileWriter outputFileWriter =
|
||||
new OutputFileWriter(nodesProcessed, outputFile, inputFile);
|
||||
try {
|
||||
root.visit(outputFileWriter);
|
||||
failed = false;
|
||||
return outputFileWriter.getOutputSize();
|
||||
} catch (VisitorIOException e) {
|
||||
throw e.getCause();
|
||||
} finally {
|
||||
Closeables.close(inputFile, failed);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,15 +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.ast;
|
||||
|
||||
/**
|
||||
* An interface that describes the boolean evaluation of an expression.
|
||||
*/
|
||||
public interface CanBooleanEval {
|
||||
|
||||
boolean isBooleanFalse();
|
||||
|
||||
boolean isBooleanTrue();
|
||||
}
|
||||
@@ -1,15 +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.ast;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Implemented by JavaScript objects that accept arguments.
|
||||
*/
|
||||
public interface HasArguments {
|
||||
|
||||
List<JsExpression> getArguments();
|
||||
}
|
||||
@@ -1,15 +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.ast;
|
||||
|
||||
/**
|
||||
* Implemented by JavaScript objects with conditional execution.
|
||||
*/
|
||||
public interface HasCondition {
|
||||
|
||||
JsExpression getCondition();
|
||||
|
||||
void setCondition(JsExpression condition);
|
||||
}
|
||||
@@ -1,14 +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.ast;
|
||||
|
||||
import com.google.dart.compiler.common.HasSymbol;
|
||||
|
||||
/**
|
||||
* Implemented by JavaScript objects that have a name.
|
||||
*/
|
||||
public interface HasName extends HasSymbol {
|
||||
JsName getName();
|
||||
}
|
||||
@@ -1,68 +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.ast;
|
||||
|
||||
/**
|
||||
* Represents a javascript expression for array access.
|
||||
*/
|
||||
public final class JsArrayAccess extends JsExpression {
|
||||
|
||||
private JsExpression arrayExpr;
|
||||
private JsExpression indexExpr;
|
||||
|
||||
public JsArrayAccess() {
|
||||
super();
|
||||
}
|
||||
|
||||
public JsArrayAccess(JsExpression arrayExpr, JsExpression indexExpr) {
|
||||
this.arrayExpr = arrayExpr;
|
||||
this.indexExpr = indexExpr;
|
||||
}
|
||||
|
||||
public JsExpression getArrayExpr() {
|
||||
return arrayExpr;
|
||||
}
|
||||
|
||||
public JsExpression getIndexExpr() {
|
||||
return indexExpr;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasSideEffects() {
|
||||
return arrayExpr.hasSideEffects() || indexExpr.hasSideEffects();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDefinitelyNotNull() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDefinitelyNull() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public void setArrayExpr(JsExpression arrayExpr) {
|
||||
this.arrayExpr = arrayExpr;
|
||||
}
|
||||
|
||||
public void setIndexExpr(JsExpression indexExpr) {
|
||||
this.indexExpr = indexExpr;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void traverse(JsVisitor v, JsContext ctx) {
|
||||
if (v.visit(this, ctx)) {
|
||||
arrayExpr = v.accept(arrayExpr);
|
||||
indexExpr = v.accept(indexExpr);
|
||||
}
|
||||
v.endVisit(this, ctx);
|
||||
}
|
||||
|
||||
@Override
|
||||
public NodeKind getKind() {
|
||||
return NodeKind.ARRAY_ACCESS;
|
||||
}
|
||||
}
|
||||
@@ -1,67 +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.ast;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Represents a JavaScript expression for array literals.
|
||||
*/
|
||||
public final class JsArrayLiteral extends JsLiteral {
|
||||
|
||||
private final List<JsExpression> exprs = new ArrayList<JsExpression>();
|
||||
|
||||
public JsArrayLiteral() {
|
||||
super();
|
||||
}
|
||||
|
||||
public List<JsExpression> getExpressions() {
|
||||
return exprs;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasSideEffects() {
|
||||
for (JsExpression expr : exprs) {
|
||||
if (expr.hasSideEffects()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isBooleanFalse() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isBooleanTrue() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDefinitelyNotNull() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDefinitelyNull() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void traverse(JsVisitor v, JsContext ctx) {
|
||||
if (v.visit(this, ctx)) {
|
||||
v.acceptWithInsertRemove(exprs);
|
||||
}
|
||||
v.endVisit(this, ctx);
|
||||
}
|
||||
|
||||
@Override
|
||||
public NodeKind getKind() {
|
||||
return NodeKind.ARRAY;
|
||||
}
|
||||
}
|
||||
@@ -1,107 +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.ast;
|
||||
|
||||
/**
|
||||
* Represents a JavaScript binary operation.
|
||||
*/
|
||||
public final class JsBinaryOperation extends JsExpression {
|
||||
|
||||
private JsExpression arg1;
|
||||
private JsExpression arg2;
|
||||
private final JsBinaryOperator op;
|
||||
|
||||
public JsBinaryOperation(JsBinaryOperator op) {
|
||||
this(op, null, null);
|
||||
}
|
||||
|
||||
public JsBinaryOperation(JsBinaryOperator op, JsExpression arg1, JsExpression arg2) {
|
||||
this.op = op;
|
||||
this.arg1 = arg1;
|
||||
this.arg2 = arg2;
|
||||
}
|
||||
|
||||
public JsExpression getArg1() {
|
||||
return arg1;
|
||||
}
|
||||
|
||||
public JsExpression getArg2() {
|
||||
return arg2;
|
||||
}
|
||||
|
||||
public JsBinaryOperator getOperator() {
|
||||
return op;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasSideEffects() {
|
||||
return op.isAssignment() || arg1.hasSideEffects() || arg2.hasSideEffects();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDefinitelyNotNull() {
|
||||
// Precarious coding, but none of these can have null results.
|
||||
if (op.getPrecedence() > 5) {
|
||||
return true;
|
||||
}
|
||||
if (op == JsBinaryOperator.OR) {
|
||||
if (arg1 instanceof CanBooleanEval) {
|
||||
if (((CanBooleanEval) arg1).isBooleanTrue()) {
|
||||
assert arg1.isDefinitelyNotNull();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
// AND and OR can return nulls
|
||||
if (op.isAssignment()) {
|
||||
if (op == JsBinaryOperator.ASG) {
|
||||
return arg2.isDefinitelyNotNull();
|
||||
} else {
|
||||
// All other ASG's are math ops.
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (op == JsBinaryOperator.COMMA) {
|
||||
return arg2.isDefinitelyNotNull();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDefinitelyNull() {
|
||||
if (op == JsBinaryOperator.AND) {
|
||||
return arg1.isDefinitelyNull();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public void setArg1(JsExpression arg1) {
|
||||
this.arg1 = arg1;
|
||||
}
|
||||
|
||||
public void setArg2(JsExpression arg2) {
|
||||
this.arg2 = arg2;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void traverse(JsVisitor v, JsContext ctx) {
|
||||
if (v.visit(this, ctx)) {
|
||||
if (op.isAssignment()) {
|
||||
arg1 = v.acceptLvalue(arg1);
|
||||
} else {
|
||||
arg1 = v.accept(arg1);
|
||||
}
|
||||
arg2 = v.accept(arg2);
|
||||
}
|
||||
v.endVisit(this, ctx);
|
||||
}
|
||||
|
||||
@Override
|
||||
public NodeKind getKind() {
|
||||
return NodeKind.BINARY_OP;
|
||||
}
|
||||
}
|
||||
@@ -1,120 +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.ast;
|
||||
|
||||
/**
|
||||
* Represents the operator in a JavaScript binary operation.
|
||||
*/
|
||||
public enum JsBinaryOperator implements JsOperator {
|
||||
|
||||
/*
|
||||
* Precedence indices from "JavaScript - The Definitive Guide" 4th Edition
|
||||
* (page 57)
|
||||
*
|
||||
*
|
||||
* Precedence 15 is for really important things that have their own AST
|
||||
* classes.
|
||||
*
|
||||
* Precedence 14 is for unary operators.
|
||||
*/
|
||||
|
||||
MUL("*", 13, LEFT | INFIX), DIV("/", 13, LEFT | INFIX), MOD("%", 13, LEFT
|
||||
| INFIX),
|
||||
|
||||
ADD("+", 12, LEFT | INFIX), SUB("-", 12, LEFT | INFIX),
|
||||
|
||||
SHL("<<", 11, LEFT | INFIX), SHR(">>", 11, LEFT | INFIX), SHRU(">>>", 11,
|
||||
LEFT | INFIX),
|
||||
|
||||
LT("<", 10, LEFT | INFIX), LTE("<=", 10, LEFT | INFIX), GT(">", 10, LEFT
|
||||
| INFIX), GTE(">=", 10, LEFT | INFIX), INSTANCEOF("instanceof", 10, LEFT
|
||||
| INFIX), INOP("in", 10, LEFT | INFIX),
|
||||
|
||||
EQ("==", 9, LEFT | INFIX), NEQ("!=", 9, LEFT | INFIX), REF_EQ("===", 9, LEFT
|
||||
| INFIX), REF_NEQ("!==", 9, LEFT | INFIX),
|
||||
|
||||
BIT_AND("&", 8, LEFT | INFIX),
|
||||
|
||||
BIT_XOR("^", 7, LEFT | INFIX),
|
||||
|
||||
BIT_OR("|", 6, LEFT | INFIX),
|
||||
|
||||
AND("&&", 5, LEFT | INFIX),
|
||||
|
||||
OR("||", 4, LEFT | INFIX),
|
||||
|
||||
// Precedence 3 is for the condition operator.
|
||||
|
||||
// These assignment operators are right-associative.
|
||||
ASG("=", 2, INFIX), ASG_ADD("+=", 2, INFIX), ASG_SUB("-=", 2, INFIX), ASG_MUL(
|
||||
"*=", 2, INFIX), ASG_DIV("/=", 2, INFIX), ASG_MOD("%=", 2, INFIX), ASG_SHL(
|
||||
"<<=", 2, INFIX), ASG_SHR(">>=", 2, INFIX), ASG_SHRU(">>>=", 2, INFIX), ASG_BIT_AND(
|
||||
"&=", 2, INFIX), ASG_BIT_OR("|=", 2, INFIX), ASG_BIT_XOR("^=", 2, INFIX),
|
||||
|
||||
COMMA(",", 1, LEFT | INFIX);
|
||||
|
||||
private final int mask;
|
||||
private final int precedence;
|
||||
private final String symbol;
|
||||
|
||||
private JsBinaryOperator(String symbol, int precedence, int mask) {
|
||||
this.symbol = symbol;
|
||||
this.precedence = precedence;
|
||||
this.mask = mask;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getPrecedence() {
|
||||
return precedence;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getSymbol() {
|
||||
return symbol;
|
||||
}
|
||||
|
||||
public boolean isAssignment() {
|
||||
/*
|
||||
* Beware, flaky! Maybe I should have added Yet Another Field to
|
||||
* BinaryOperator?
|
||||
*/
|
||||
return (getPrecedence() == ASG.getPrecedence());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isKeyword() {
|
||||
return this == INSTANCEOF || this == INOP;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isLeftAssociative() {
|
||||
return (mask & LEFT) != 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isPrecedenceLessThan(JsOperator other) {
|
||||
return precedence < other.getPrecedence();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isValidInfix() {
|
||||
return (mask & INFIX) != 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isValidPostfix() {
|
||||
return (mask & POSTFIX) != 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isValidPrefix() {
|
||||
return (mask & PREFIX) != 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return symbol;
|
||||
}
|
||||
}
|
||||
@@ -1,44 +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.ast;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Represents a JavaScript block statement.
|
||||
*/
|
||||
public class JsBlock extends JsStatement {
|
||||
|
||||
private final List<JsStatement> stmts = new ArrayList<JsStatement>();
|
||||
|
||||
public JsBlock() {
|
||||
}
|
||||
|
||||
public JsBlock(JsStatement stmt) {
|
||||
stmts.add(stmt);
|
||||
}
|
||||
|
||||
public List<JsStatement> getStatements() {
|
||||
return stmts;
|
||||
}
|
||||
|
||||
public boolean isGlobalBlock() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void traverse(JsVisitor v, JsContext ctx) {
|
||||
if (v.visit(this, ctx)) {
|
||||
v.acceptWithInsertRemove(stmts);
|
||||
}
|
||||
v.endVisit(this, ctx);
|
||||
}
|
||||
|
||||
@Override
|
||||
public NodeKind getKind() {
|
||||
return NodeKind.BLOCK;
|
||||
}
|
||||
}
|
||||
@@ -1,53 +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.ast;
|
||||
|
||||
/**
|
||||
* Represents a JavaScript literal boolean expression.
|
||||
*/
|
||||
public final class JsBooleanLiteral extends JsValueLiteral {
|
||||
|
||||
private final boolean value;
|
||||
|
||||
// Should be interned by JsProgram
|
||||
JsBooleanLiteral(boolean value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public boolean getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isBooleanFalse() {
|
||||
return value == false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isBooleanTrue() {
|
||||
return value == true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDefinitelyNotNull() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDefinitelyNull() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void traverse(JsVisitor v, JsContext ctx) {
|
||||
v.visit(this, ctx);
|
||||
v.endVisit(this, ctx);
|
||||
}
|
||||
|
||||
@Override
|
||||
public NodeKind getKind() {
|
||||
return NodeKind.BOOLEAN;
|
||||
}
|
||||
}
|
||||
@@ -1,46 +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.ast;
|
||||
|
||||
/**
|
||||
* Represents the JavaScript break statement.
|
||||
*/
|
||||
public final class JsBreak extends JsStatement {
|
||||
|
||||
private final JsNameRef label;
|
||||
|
||||
public JsBreak() {
|
||||
this(null);
|
||||
}
|
||||
|
||||
public JsBreak(JsNameRef label) {
|
||||
super();
|
||||
this.label = label;
|
||||
}
|
||||
|
||||
public JsNameRef getLabel() {
|
||||
return label;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void traverse(JsVisitor v, JsContext ctx) {
|
||||
if (v.visit(this, ctx)) {
|
||||
if (label != null) {
|
||||
v.accept(label);
|
||||
}
|
||||
}
|
||||
v.endVisit(this, ctx);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean unconditionalControlBreak() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public NodeKind getKind() {
|
||||
return NodeKind.BREAK;
|
||||
}
|
||||
}
|
||||
@@ -1,39 +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.ast;
|
||||
|
||||
/**
|
||||
* Represents the JavaScript case statement.
|
||||
*/
|
||||
public final class JsCase extends JsSwitchMember {
|
||||
|
||||
private JsExpression caseExpr;
|
||||
|
||||
public JsCase() {
|
||||
super();
|
||||
}
|
||||
|
||||
public JsExpression getCaseExpr() {
|
||||
return caseExpr;
|
||||
}
|
||||
|
||||
public void setCaseExpr(JsExpression caseExpr) {
|
||||
this.caseExpr = caseExpr;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void traverse(JsVisitor v, JsContext ctx) {
|
||||
if (v.visit(this, ctx)) {
|
||||
caseExpr = v.accept(caseExpr);
|
||||
v.acceptWithInsertRemove(stmts);
|
||||
}
|
||||
v.endVisit(this, ctx);
|
||||
}
|
||||
|
||||
@Override
|
||||
public NodeKind getKind() {
|
||||
return NodeKind.CASE;
|
||||
}
|
||||
}
|
||||
@@ -1,66 +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.ast;
|
||||
|
||||
/**
|
||||
* Represents a JavaScript catch clause.
|
||||
*/
|
||||
public class JsCatch extends JsNode implements HasCondition {
|
||||
|
||||
protected final JsCatchScope scope;
|
||||
private JsBlock body;
|
||||
private JsExpression condition;
|
||||
private JsParameter param;
|
||||
|
||||
public JsCatch(JsScope parent, String ident) {
|
||||
super();
|
||||
assert (parent != null);
|
||||
scope = new JsCatchScope(parent, ident);
|
||||
param = new JsParameter(scope.findExistingName(ident));
|
||||
}
|
||||
|
||||
public JsBlock getBody() {
|
||||
return body;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JsExpression getCondition() {
|
||||
return condition;
|
||||
}
|
||||
|
||||
public JsParameter getParameter() {
|
||||
return param;
|
||||
}
|
||||
|
||||
public JsScope getScope() {
|
||||
return scope;
|
||||
}
|
||||
|
||||
public void setBody(JsBlock body) {
|
||||
this.body = body;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setCondition(JsExpression condition) {
|
||||
this.condition = condition;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void traverse(JsVisitor v, JsContext ctx) {
|
||||
if (v.visit(this, ctx)) {
|
||||
param = v.accept(param);
|
||||
if (condition != null) {
|
||||
condition = v.accept(condition);
|
||||
}
|
||||
body = v.accept(body);
|
||||
}
|
||||
v.endVisit(this, ctx);
|
||||
}
|
||||
|
||||
@Override
|
||||
public NodeKind getKind() {
|
||||
return NodeKind.CATCH;
|
||||
}
|
||||
}
|
||||
@@ -1,75 +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.ast;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.NoSuchElementException;
|
||||
|
||||
/**
|
||||
* A special scope used only for catch blocks. It only holds a single symbol:
|
||||
* the catch argument's name.
|
||||
*/
|
||||
public class JsCatchScope extends JsScope {
|
||||
|
||||
private final JsName name;
|
||||
|
||||
public JsCatchScope(JsScope parent, String ident) {
|
||||
super(parent, "Catch scope");
|
||||
this.name = new JsName(this, ident, ident, ident);
|
||||
}
|
||||
|
||||
@Override
|
||||
public JsName declareName(String ident) {
|
||||
// Declare into parent scope!
|
||||
return getParent().declareName(ident);
|
||||
}
|
||||
|
||||
@Override
|
||||
public JsName declareName(String ident, String shortIdent) {
|
||||
// Declare into parent scope!
|
||||
return getParent().declareName(ident, shortIdent);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<JsName> getAllNames() {
|
||||
return new Iterator<JsName>() {
|
||||
private boolean didIterate = false;
|
||||
|
||||
@Override
|
||||
public boolean hasNext() {
|
||||
return !didIterate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JsName next() {
|
||||
if (didIterate) {
|
||||
throw new NoSuchElementException();
|
||||
}
|
||||
didIterate = true;
|
||||
return name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
protected JsName doCreateName(String ident, String shortIdent, String originalName) {
|
||||
throw new UnsupportedOperationException("Cannot create a name in a catch scope");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected JsName findExistingNameNoRecurse(String ident) {
|
||||
if (name.getIdent().equals(ident)) {
|
||||
return name;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,78 +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.ast;
|
||||
|
||||
/**
|
||||
* Represents a JavaScript conditional expression.
|
||||
*/
|
||||
public final class JsConditional extends JsExpression {
|
||||
|
||||
private JsExpression elseExpr;
|
||||
private JsExpression testExpr;
|
||||
private JsExpression thenExpr;
|
||||
|
||||
public JsConditional() {
|
||||
}
|
||||
|
||||
public JsConditional(JsExpression testExpr, JsExpression thenExpr, JsExpression elseExpr) {
|
||||
this.testExpr = testExpr;
|
||||
this.thenExpr = thenExpr;
|
||||
this.elseExpr = elseExpr;
|
||||
}
|
||||
|
||||
public JsExpression getElseExpression() {
|
||||
return elseExpr;
|
||||
}
|
||||
|
||||
public JsExpression getTestExpression() {
|
||||
return testExpr;
|
||||
}
|
||||
|
||||
public JsExpression getThenExpression() {
|
||||
return thenExpr;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasSideEffects() {
|
||||
return testExpr.hasSideEffects() || thenExpr.hasSideEffects() || elseExpr.hasSideEffects();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDefinitelyNotNull() {
|
||||
return thenExpr.isDefinitelyNotNull() && elseExpr.isDefinitelyNotNull();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDefinitelyNull() {
|
||||
return thenExpr.isDefinitelyNull() && elseExpr.isDefinitelyNull();
|
||||
}
|
||||
|
||||
public void setElseExpression(JsExpression elseExpr) {
|
||||
this.elseExpr = elseExpr;
|
||||
}
|
||||
|
||||
public void setTestExpression(JsExpression testExpr) {
|
||||
this.testExpr = testExpr;
|
||||
}
|
||||
|
||||
public void setThenExpression(JsExpression thenExpr) {
|
||||
this.thenExpr = thenExpr;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void traverse(JsVisitor v, JsContext ctx) {
|
||||
if (v.visit(this, ctx)) {
|
||||
testExpr = v.accept(testExpr);
|
||||
thenExpr = v.accept(thenExpr);
|
||||
elseExpr = v.accept(elseExpr);
|
||||
}
|
||||
v.endVisit(this, ctx);
|
||||
}
|
||||
|
||||
@Override
|
||||
public NodeKind getKind() {
|
||||
return NodeKind.CONDITIONAL;
|
||||
}
|
||||
}
|
||||
@@ -1,27 +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.ast;
|
||||
|
||||
/**
|
||||
* The context in which a JsNode visitation occurs. This represents the set of
|
||||
* possible operations a JsVisitor subclass can perform on the currently visited
|
||||
* node.
|
||||
*/
|
||||
public interface JsContext {
|
||||
|
||||
boolean canInsert();
|
||||
|
||||
boolean canRemove();
|
||||
|
||||
void insertAfter(JsVisitable node);
|
||||
|
||||
void insertBefore(JsVisitable node);
|
||||
|
||||
boolean isLvalue();
|
||||
|
||||
void removeMe();
|
||||
|
||||
void replaceMe(JsVisitable node);
|
||||
}
|
||||
@@ -1,46 +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.ast;
|
||||
|
||||
/**
|
||||
* Represents the JavaScript continue statement.
|
||||
*/
|
||||
public final class JsContinue extends JsStatement {
|
||||
|
||||
private final JsNameRef label;
|
||||
|
||||
public JsContinue() {
|
||||
this(null);
|
||||
}
|
||||
|
||||
public JsContinue(JsNameRef label) {
|
||||
super();
|
||||
this.label = label;
|
||||
}
|
||||
|
||||
public JsNameRef getLabel() {
|
||||
return label;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void traverse(JsVisitor v, JsContext ctx) {
|
||||
if (v.visit(this, ctx)) {
|
||||
if (label != null) {
|
||||
v.accept(label);
|
||||
}
|
||||
}
|
||||
v.endVisit(this, ctx);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean unconditionalControlBreak() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public NodeKind getKind() {
|
||||
return NodeKind.CONTINUE;
|
||||
}
|
||||
}
|
||||
@@ -1,25 +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.ast;
|
||||
|
||||
/**
|
||||
* Represents a JavaScript debugger statement.
|
||||
*/
|
||||
public class JsDebugger extends JsStatement {
|
||||
|
||||
public JsDebugger() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void traverse(JsVisitor v, JsContext ctx) {
|
||||
v.visit(this, ctx);
|
||||
v.endVisit(this, ctx);
|
||||
}
|
||||
|
||||
@Override
|
||||
public NodeKind getKind() {
|
||||
return NodeKind.DEBUGGER;
|
||||
}
|
||||
}
|
||||
@@ -1,28 +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.ast;
|
||||
|
||||
/**
|
||||
* Represents the default option in a JavaScript swtich statement.
|
||||
*/
|
||||
public final class JsDefault extends JsSwitchMember {
|
||||
|
||||
public JsDefault() {
|
||||
super();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void traverse(JsVisitor v, JsContext ctx) {
|
||||
if (v.visit(this, ctx)) {
|
||||
v.acceptWithInsertRemove(stmts);
|
||||
}
|
||||
v.endVisit(this, ctx);
|
||||
}
|
||||
|
||||
@Override
|
||||
public NodeKind getKind() {
|
||||
return NodeKind.DEFAULT;
|
||||
}
|
||||
}
|
||||
@@ -1,54 +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.ast;
|
||||
|
||||
/**
|
||||
* Represents a JavaScript do..while statement.
|
||||
*/
|
||||
public class JsDoWhile extends JsStatement {
|
||||
|
||||
private JsStatement body;
|
||||
private JsExpression condition;
|
||||
|
||||
public JsDoWhile() {
|
||||
super();
|
||||
}
|
||||
|
||||
public JsDoWhile(JsExpression condition, JsStatement body) {
|
||||
super();
|
||||
this.condition = condition;
|
||||
this.body = body;
|
||||
}
|
||||
|
||||
public JsStatement getBody() {
|
||||
return body;
|
||||
}
|
||||
|
||||
public JsExpression getCondition() {
|
||||
return condition;
|
||||
}
|
||||
|
||||
public void setBody(JsStatement body) {
|
||||
this.body = body;
|
||||
}
|
||||
|
||||
public void setCondition(JsExpression condition) {
|
||||
this.condition = condition;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void traverse(JsVisitor v, JsContext ctx) {
|
||||
if (v.visit(this, ctx)) {
|
||||
body = v.accept(body);
|
||||
condition = v.accept(condition);
|
||||
}
|
||||
v.endVisit(this, ctx);
|
||||
}
|
||||
|
||||
@Override
|
||||
public NodeKind getKind() {
|
||||
return NodeKind.DO;
|
||||
}
|
||||
}
|
||||
@@ -1,26 +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.ast;
|
||||
|
||||
/**
|
||||
* Represents an empty statement in JavaScript.
|
||||
*/
|
||||
public class JsEmpty extends JsStatement {
|
||||
|
||||
// Interned by JsProgram
|
||||
JsEmpty() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void traverse(JsVisitor v, JsContext ctx) {
|
||||
v.visit(this, ctx);
|
||||
v.endVisit(this, ctx);
|
||||
}
|
||||
|
||||
@Override
|
||||
public NodeKind getKind() {
|
||||
return NodeKind.EMPTY;
|
||||
}
|
||||
}
|
||||
@@ -1,36 +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.ast;
|
||||
|
||||
/**
|
||||
* Represents a JavaScript expression statement.
|
||||
*/
|
||||
public final class JsExprStmt extends JsStatement {
|
||||
|
||||
private JsExpression expr;
|
||||
|
||||
public JsExprStmt(JsExpression expr) {
|
||||
super();
|
||||
this.expr = expr;
|
||||
this.setSourceInfo(expr);
|
||||
}
|
||||
|
||||
public JsExpression getExpression() {
|
||||
return expr;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void traverse(JsVisitor v, JsContext ctx) {
|
||||
if (v.visit(this, ctx)) {
|
||||
expr = v.accept(expr);
|
||||
}
|
||||
v.endVisit(this, ctx);
|
||||
}
|
||||
|
||||
@Override
|
||||
public NodeKind getKind() {
|
||||
return NodeKind.EXPR_STMT;
|
||||
}
|
||||
}
|
||||
@@ -1,52 +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.ast;
|
||||
|
||||
import com.google.dart.compiler.common.SourceInfo;
|
||||
|
||||
/**
|
||||
* An abstract base class for all JavaScript expressions.
|
||||
*/
|
||||
public abstract class JsExpression extends JsNode {
|
||||
|
||||
protected JsExpression() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether the expression can cause side effects.
|
||||
*/
|
||||
public abstract boolean hasSideEffects();
|
||||
|
||||
/**
|
||||
* True if the target expression is definitely not null.
|
||||
*/
|
||||
public abstract boolean isDefinitelyNotNull();
|
||||
|
||||
/**
|
||||
* True if the target expression is definitely null.
|
||||
*/
|
||||
public abstract boolean isDefinitelyNull();
|
||||
|
||||
/**
|
||||
* Determines whether or not this expression is a leaf, such as a
|
||||
* {@link JsNameRef}, {@link JsBooleanLiteral}, and so on. Leaf expressions
|
||||
* never need to be parenthesized.
|
||||
*/
|
||||
public boolean isLeaf() {
|
||||
// Conservatively say that it isn't a leaf.
|
||||
// Individual subclasses can speak for themselves if they are a leaf.
|
||||
return false;
|
||||
}
|
||||
|
||||
public JsExprStmt makeStmt() {
|
||||
return new JsExprStmt(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public JsExpression setSourceRef(SourceInfo info) {
|
||||
super.setSourceRef(info);
|
||||
return this;
|
||||
}
|
||||
}
|
||||
@@ -1,105 +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.ast;
|
||||
|
||||
import com.google.dart.compiler.common.SourceInfo;
|
||||
|
||||
/**
|
||||
* A <code>for</code> statement. If specified at all, the initializer part is
|
||||
* either a declaration of one or more variables, in which case
|
||||
* {@link #getInitVars()} is used, or an expression, in which case
|
||||
* {@link #getInitExpr()} is used. In the latter case, the comma operator is
|
||||
* often used to create a compound expression.
|
||||
*
|
||||
* <p>
|
||||
* Note that any of the parts of the <code>for</code> loop header can be
|
||||
* <code>null</code>, although the body will never be null.
|
||||
*/
|
||||
public class JsFor extends JsStatement {
|
||||
|
||||
private JsStatement body;
|
||||
private JsExpression condition;
|
||||
private JsExpression incrExpr;
|
||||
private JsExpression initExpr;
|
||||
private JsVars initVars;
|
||||
|
||||
public JsFor() {
|
||||
super();
|
||||
}
|
||||
|
||||
public JsStatement getBody() {
|
||||
return body;
|
||||
}
|
||||
|
||||
public JsExpression getCondition() {
|
||||
return condition;
|
||||
}
|
||||
|
||||
public JsExpression getIncrExpr() {
|
||||
return incrExpr;
|
||||
}
|
||||
|
||||
public JsExpression getInitExpr() {
|
||||
return initExpr;
|
||||
}
|
||||
|
||||
public JsVars getInitVars() {
|
||||
return initVars;
|
||||
}
|
||||
|
||||
public void setBody(JsStatement body) {
|
||||
this.body = body;
|
||||
}
|
||||
|
||||
public void setCondition(JsExpression condition) {
|
||||
this.condition = condition;
|
||||
}
|
||||
|
||||
public void setIncrExpr(JsExpression incrExpr) {
|
||||
this.incrExpr = incrExpr;
|
||||
}
|
||||
|
||||
public void setInitExpr(JsExpression initExpr) {
|
||||
this.initExpr = initExpr;
|
||||
}
|
||||
|
||||
public void setInitVars(JsVars initVars) {
|
||||
this.initVars = initVars;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void traverse(JsVisitor v, JsContext ctx) {
|
||||
if (v.visit(this, ctx)) {
|
||||
assert (!(initExpr != null && initVars != null));
|
||||
|
||||
if (initExpr != null) {
|
||||
initExpr = v.accept(initExpr);
|
||||
} else if (initVars != null) {
|
||||
initVars = v.accept(initVars);
|
||||
}
|
||||
|
||||
if (condition != null) {
|
||||
condition = v.accept(condition);
|
||||
}
|
||||
|
||||
if (incrExpr != null) {
|
||||
incrExpr = v.accept(incrExpr);
|
||||
}
|
||||
body = v.accept(body);
|
||||
}
|
||||
v.endVisit(this, ctx);
|
||||
}
|
||||
|
||||
@Override
|
||||
public JsFor setSourceRef(SourceInfo info) {
|
||||
super.setSourceRef(info);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public NodeKind getKind() {
|
||||
return NodeKind.FOR;
|
||||
}
|
||||
}
|
||||
@@ -1,71 +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.ast;
|
||||
|
||||
/**
|
||||
* Represents a JavaScript for..in statement.
|
||||
*/
|
||||
public class JsForIn extends JsStatement {
|
||||
|
||||
private JsStatement body;
|
||||
private JsExpression iterExpr;
|
||||
private JsExpression objExpr;
|
||||
|
||||
// Optional: the name of a new iterator variable to introduce
|
||||
private final JsName iterVarName;
|
||||
|
||||
public JsForIn() {
|
||||
this(null);
|
||||
}
|
||||
|
||||
public JsForIn(JsName iterVarName) {
|
||||
this.iterVarName = iterVarName;
|
||||
}
|
||||
|
||||
public JsStatement getBody() {
|
||||
return body;
|
||||
}
|
||||
|
||||
public JsExpression getIterExpr() {
|
||||
return iterExpr;
|
||||
}
|
||||
|
||||
public JsName getIterVarName() {
|
||||
return iterVarName;
|
||||
}
|
||||
|
||||
public JsExpression getObjExpr() {
|
||||
return objExpr;
|
||||
}
|
||||
|
||||
public void setBody(JsStatement body) {
|
||||
this.body = body;
|
||||
}
|
||||
|
||||
public void setIterExpr(JsExpression iterExpr) {
|
||||
this.iterExpr = iterExpr;
|
||||
}
|
||||
|
||||
public void setObjExpr(JsExpression objExpr) {
|
||||
this.objExpr = objExpr;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void traverse(JsVisitor v, JsContext ctx) {
|
||||
if (v.visit(this, ctx)) {
|
||||
if (iterExpr != null) {
|
||||
iterExpr = v.acceptLvalue(iterExpr);
|
||||
}
|
||||
objExpr = v.accept(objExpr);
|
||||
body = v.accept(body);
|
||||
}
|
||||
v.endVisit(this, ctx);
|
||||
}
|
||||
|
||||
@Override
|
||||
public NodeKind getKind() {
|
||||
return NodeKind.FOR_IN;
|
||||
}
|
||||
}
|
||||
@@ -1,225 +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.ast;
|
||||
|
||||
import com.google.dart.compiler.common.SourceInfo;
|
||||
import com.google.dart.compiler.common.Symbol;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Represents a JavaScript function expression.
|
||||
*/
|
||||
public final class JsFunction extends JsLiteral implements HasName {
|
||||
|
||||
private static void trace(String title, String code) {
|
||||
System.out.println("---------------------------");
|
||||
System.out.println(title + ":");
|
||||
System.out.println("---------------------------");
|
||||
System.out.println(code);
|
||||
}
|
||||
|
||||
protected JsBlock body;
|
||||
protected final List<JsParameter> params = new ArrayList<JsParameter>();
|
||||
protected final JsScope scope;
|
||||
private boolean artificiallyRescued;
|
||||
private boolean executeOnce;
|
||||
private boolean fromDart;
|
||||
private JsFunction impliedExecute;
|
||||
private JsName name;
|
||||
private boolean trace = false;
|
||||
private boolean traceFirst = true;
|
||||
private boolean hoisted = false;
|
||||
private boolean constructor = false;
|
||||
|
||||
/**
|
||||
* Creates an anonymous function.
|
||||
*/
|
||||
public JsFunction(JsScope parent) {
|
||||
this(parent, null, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a function that is not derived from Dart source.
|
||||
*/
|
||||
public JsFunction(JsScope parent, JsName name) {
|
||||
this(parent, name, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a named function, possibly derived from Dart source.
|
||||
*/
|
||||
public JsFunction(JsScope parent, JsName name, boolean fromDart) {
|
||||
assert (parent != null);
|
||||
this.fromDart = fromDart;
|
||||
setName(name);
|
||||
String scopeName = (name == null) ? "<anonymous>" : name.getIdent();
|
||||
scopeName = "function " + scopeName;
|
||||
this.scope = new JsScope(parent, scopeName);
|
||||
}
|
||||
|
||||
public JsBlock getBody() {
|
||||
return body;
|
||||
}
|
||||
|
||||
/**
|
||||
* If true, this indicates that only the first invocation of the function will
|
||||
* have any effects. Subsequent invocations may be considered to be no-op
|
||||
* calls whose return value is ignored.
|
||||
*/
|
||||
public boolean getExecuteOnce() {
|
||||
return executeOnce;
|
||||
}
|
||||
|
||||
public JsFunction getImpliedExecute() {
|
||||
return impliedExecute;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JsName getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Symbol getSymbol() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public List<JsParameter> getParameters() {
|
||||
return params;
|
||||
}
|
||||
|
||||
public JsScope getScope() {
|
||||
return scope;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasSideEffects() {
|
||||
// If there's a name, the name is assigned to.
|
||||
return name != null;
|
||||
}
|
||||
|
||||
public boolean isArtificiallyRescued() {
|
||||
return artificiallyRescued;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isBooleanFalse() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isBooleanTrue() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDefinitelyNotNull() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDefinitelyNull() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean isFromDart() {
|
||||
return fromDart;
|
||||
}
|
||||
|
||||
public void setArtificiallyRescued(boolean rescued) {
|
||||
this.artificiallyRescued = rescued;
|
||||
}
|
||||
|
||||
public void setBody(JsBlock body) {
|
||||
this.body = body;
|
||||
}
|
||||
|
||||
public void setExecuteOnce(boolean executeOnce) {
|
||||
this.executeOnce = executeOnce;
|
||||
}
|
||||
|
||||
public void setFromDart(boolean fromDart) {
|
||||
this.fromDart = fromDart;
|
||||
}
|
||||
|
||||
public void setImpliedExecute(JsFunction impliedExecute) {
|
||||
this.impliedExecute = impliedExecute;
|
||||
}
|
||||
|
||||
public void setName(JsName name) {
|
||||
this.name = name;
|
||||
if (name != null) {
|
||||
if (isFromDart()) {
|
||||
name.setStaticRef(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void setTrace() {
|
||||
this.trace = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void traverse(JsVisitor v, JsContext ctx) {
|
||||
String before = null;
|
||||
if (trace && v instanceof JsModVisitor) {
|
||||
before = this.toSource();
|
||||
if (traceFirst) {
|
||||
traceFirst = false;
|
||||
trace("SCRIPT INITIAL", before);
|
||||
}
|
||||
}
|
||||
if (v.visit(this, ctx)) {
|
||||
v.acceptWithInsertRemove(params);
|
||||
body = v.accept(body);
|
||||
}
|
||||
v.endVisit(this, ctx);
|
||||
if (trace && v instanceof JsModVisitor) {
|
||||
String after = this.toSource();
|
||||
if (!after.equals(before)) {
|
||||
String title = v.getClass().getSimpleName();
|
||||
trace(title, after);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void setHoisted() {
|
||||
hoisted = true;
|
||||
}
|
||||
|
||||
/** Whether the function has been hoisted */
|
||||
public boolean isHoisted() {
|
||||
return hoisted;
|
||||
}
|
||||
|
||||
/**
|
||||
* Rebase the function to a new scope.
|
||||
* @param newScopeParent The scope to add the function to.
|
||||
*/
|
||||
public void rebaseScope(JsScope newScopeParent) {
|
||||
this.scope.rebase(newScopeParent);
|
||||
}
|
||||
|
||||
@Override
|
||||
public JsFunction setSourceRef(SourceInfo info) {
|
||||
super.setSourceRef(info);
|
||||
return this;
|
||||
}
|
||||
|
||||
public boolean isConstructor() {
|
||||
return this.constructor;
|
||||
}
|
||||
|
||||
public boolean setIsConstructor(boolean constructor) {
|
||||
return this.constructor = constructor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public NodeKind getKind() {
|
||||
return NodeKind.FUNCTION;
|
||||
}
|
||||
}
|
||||
@@ -1,19 +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.ast;
|
||||
|
||||
/**
|
||||
* Represnts a JavaScript block in the global scope.
|
||||
*/
|
||||
public class JsGlobalBlock extends JsBlock {
|
||||
|
||||
public JsGlobalBlock() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isGlobalBlock() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -1,65 +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.ast;
|
||||
|
||||
/**
|
||||
* Represents a JavaScript if statement.
|
||||
*/
|
||||
public final class JsIf extends JsStatement {
|
||||
|
||||
private JsExpression ifExpr;
|
||||
private JsStatement thenStmt;
|
||||
private JsStatement elseStmt;
|
||||
|
||||
public JsIf() {
|
||||
}
|
||||
|
||||
public JsIf(JsExpression ifExpr, JsStatement thenStmt, JsStatement elseStmt) {
|
||||
this.ifExpr = ifExpr;
|
||||
this.thenStmt = thenStmt;
|
||||
this.elseStmt = elseStmt;
|
||||
}
|
||||
|
||||
public JsStatement getElseStmt() {
|
||||
return elseStmt;
|
||||
}
|
||||
|
||||
public JsExpression getIfExpr() {
|
||||
return ifExpr;
|
||||
}
|
||||
|
||||
public JsStatement getThenStmt() {
|
||||
return thenStmt;
|
||||
}
|
||||
|
||||
public void setElseStmt(JsStatement elseStmt) {
|
||||
this.elseStmt = elseStmt;
|
||||
}
|
||||
|
||||
public void setIfExpr(JsExpression ifExpr) {
|
||||
this.ifExpr = ifExpr;
|
||||
}
|
||||
|
||||
public void setThenStmt(JsStatement thenStmt) {
|
||||
this.thenStmt = thenStmt;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void traverse(JsVisitor v, JsContext ctx) {
|
||||
if (v.visit(this, ctx)) {
|
||||
ifExpr = v.accept(ifExpr);
|
||||
thenStmt = v.accept(thenStmt);
|
||||
if (elseStmt != null) {
|
||||
elseStmt = v.accept(elseStmt);
|
||||
}
|
||||
}
|
||||
v.endVisit(this, ctx);
|
||||
}
|
||||
|
||||
@Override
|
||||
public NodeKind getKind() {
|
||||
return NodeKind.IF;
|
||||
}
|
||||
}
|
||||
@@ -1,62 +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.ast;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Represents a JavaScript invocation.
|
||||
*/
|
||||
public final class JsInvocation extends JsExpression implements HasArguments {
|
||||
|
||||
private final List<JsExpression> args = new ArrayList<JsExpression>();
|
||||
private JsExpression qualifier;
|
||||
|
||||
public JsInvocation() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<JsExpression> getArguments() {
|
||||
return args;
|
||||
}
|
||||
|
||||
public JsExpression getQualifier() {
|
||||
return qualifier;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasSideEffects() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDefinitelyNotNull() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDefinitelyNull() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public void setQualifier(JsExpression qualifier) {
|
||||
this.qualifier = qualifier;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void traverse(JsVisitor v, JsContext ctx) {
|
||||
if (v.visit(this, ctx)) {
|
||||
qualifier = v.accept(qualifier);
|
||||
v.acceptList(args);
|
||||
}
|
||||
v.endVisit(this, ctx);
|
||||
}
|
||||
|
||||
@Override
|
||||
public NodeKind getKind() {
|
||||
return NodeKind.INVOKE;
|
||||
}
|
||||
}
|
||||
@@ -1,52 +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.ast;
|
||||
|
||||
import com.google.dart.compiler.common.Symbol;
|
||||
|
||||
/**
|
||||
* Represents a JavaScript label statement.
|
||||
*/
|
||||
public class JsLabel extends JsStatement implements HasName {
|
||||
|
||||
private final JsName label;
|
||||
|
||||
private JsStatement stmt;
|
||||
|
||||
public JsLabel(JsName label) {
|
||||
this.label = label;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JsName getName() {
|
||||
return label;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Symbol getSymbol() {
|
||||
return label;
|
||||
}
|
||||
|
||||
public JsStatement getStmt() {
|
||||
return stmt;
|
||||
}
|
||||
|
||||
public void setStmt(JsStatement stmt) {
|
||||
this.stmt = stmt;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void traverse(JsVisitor v, JsContext ctx) {
|
||||
if (v.visit(this, ctx)) {
|
||||
stmt = v.accept(stmt);
|
||||
}
|
||||
v.endVisit(this, ctx);
|
||||
}
|
||||
|
||||
@Override
|
||||
public NodeKind getKind() {
|
||||
return NodeKind.LABEL;
|
||||
}
|
||||
}
|
||||
@@ -1,14 +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.ast;
|
||||
|
||||
/**
|
||||
* A JavaScript string literal expression.
|
||||
*/
|
||||
public abstract class JsLiteral extends JsExpression implements CanBooleanEval {
|
||||
|
||||
protected JsLiteral() {
|
||||
}
|
||||
}
|
||||
@@ -1,189 +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.ast;
|
||||
|
||||
import com.google.dart.compiler.util.Hack;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* A visitor for iterating through and modifying an AST.
|
||||
*/
|
||||
public class JsModVisitor extends JsVisitor {
|
||||
|
||||
private class ListContext<T extends JsVisitable> implements JsContext {
|
||||
|
||||
private List<T> collection;
|
||||
private int index;
|
||||
private boolean removed;
|
||||
private boolean replaced;
|
||||
|
||||
@Override
|
||||
public boolean canInsert() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canRemove() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void insertAfter(JsVisitable node) {
|
||||
checkRemoved();
|
||||
collection.add(index + 1, Hack.<T>cast(node));
|
||||
didChange = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void insertBefore(JsVisitable node) {
|
||||
checkRemoved();
|
||||
collection.add(index++, Hack.<T>cast(node));
|
||||
didChange = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isLvalue() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeMe() {
|
||||
checkState();
|
||||
collection.remove(index--);
|
||||
didChange = removed = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void replaceMe(JsVisitable node) {
|
||||
checkState();
|
||||
checkReplacement(collection.get(index), node);
|
||||
collection.set(index, Hack.<T>cast(node));
|
||||
didChange = replaced = true;
|
||||
}
|
||||
|
||||
protected void traverse(List<T> collection) {
|
||||
this.collection = collection;
|
||||
for (index = 0; index < collection.size(); ++index) {
|
||||
removed = replaced = false;
|
||||
doTraverse(collection.get(index), this);
|
||||
}
|
||||
}
|
||||
|
||||
private void checkRemoved() {
|
||||
if (removed) {
|
||||
throw new RuntimeException("Node was already removed");
|
||||
}
|
||||
}
|
||||
|
||||
private void checkState() {
|
||||
checkRemoved();
|
||||
if (replaced) {
|
||||
throw new RuntimeException("Node was already replaced");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class LvalueContext extends NodeContext<JsExpression> {
|
||||
@Override
|
||||
public boolean isLvalue() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
private class NodeContext<T extends JsVisitable> implements JsContext {
|
||||
private T node;
|
||||
private boolean replaced;
|
||||
|
||||
@Override
|
||||
public boolean canInsert() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canRemove() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void insertAfter(JsVisitable node) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void insertBefore(JsVisitable node) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isLvalue() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeMe() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void replaceMe(JsVisitable node) {
|
||||
if (replaced) {
|
||||
throw new RuntimeException("Node was already replaced");
|
||||
}
|
||||
checkReplacement(this.node, node);
|
||||
this.node = Hack.<T>cast(node);
|
||||
didChange = replaced = true;
|
||||
}
|
||||
|
||||
protected T traverse(T node) {
|
||||
this.node = node;
|
||||
replaced = false;
|
||||
doTraverse(node, this);
|
||||
return this.node;
|
||||
}
|
||||
}
|
||||
|
||||
protected static <T extends JsVisitable> void checkReplacement(T origNode, T newNode) {
|
||||
if (newNode == null) {
|
||||
throw new RuntimeException("Cannot replace with null");
|
||||
}
|
||||
if (newNode == origNode) {
|
||||
throw new RuntimeException("The replacement is the same as the original");
|
||||
}
|
||||
}
|
||||
|
||||
protected boolean didChange = false;
|
||||
|
||||
@Override
|
||||
public boolean didChange() {
|
||||
return didChange;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected <T extends JsVisitable> T doAccept(T node) {
|
||||
return new NodeContext<T>().traverse(node);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected <T extends JsVisitable> void doAcceptList(List<T> collection) {
|
||||
NodeContext<T> ctx = new NodeContext<T>();
|
||||
for (int i = 0, c = collection.size(); i < c; ++i) {
|
||||
ctx.traverse(collection.get(i));
|
||||
if (ctx.replaced) {
|
||||
collection.set(i, ctx.node);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected JsExpression doAcceptLvalue(JsExpression expr) {
|
||||
return new LvalueContext().traverse(expr);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected <T extends JsVisitable> void doAcceptWithInsertRemove(List<T> collection) {
|
||||
new ListContext<T>().traverse(collection);
|
||||
}
|
||||
}
|
||||
@@ -1,114 +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.ast;
|
||||
|
||||
import com.google.dart.compiler.ast.DartNode;
|
||||
import com.google.dart.compiler.common.Symbol;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* An abstract base class for named JavaScript objects.
|
||||
*/
|
||||
public class JsName implements Symbol, Serializable {
|
||||
private final JsScope enclosing;
|
||||
private final String ident;
|
||||
private boolean isObfuscatable;
|
||||
private String shortIdent;
|
||||
private String originalName;
|
||||
|
||||
/**
|
||||
* A back-reference to the JsNode that the JsName refers to.
|
||||
*/
|
||||
private JsNode staticRef;
|
||||
|
||||
/**
|
||||
* @param ident the unmangled ident to use for this name
|
||||
*/
|
||||
JsName(JsScope enclosing, String ident, String shortIdent, String originalName) {
|
||||
this.enclosing = enclosing;
|
||||
this.ident = ident;
|
||||
this.shortIdent = shortIdent;
|
||||
if (originalName != null) {
|
||||
this.originalName = originalName;
|
||||
}
|
||||
this.isObfuscatable = true;
|
||||
}
|
||||
|
||||
public JsScope getEnclosing() {
|
||||
return enclosing;
|
||||
}
|
||||
|
||||
public String getIdent() {
|
||||
return ident;
|
||||
}
|
||||
|
||||
public String getShortIdent() {
|
||||
return shortIdent;
|
||||
}
|
||||
|
||||
public String getOriginalName() {
|
||||
return originalName;
|
||||
}
|
||||
|
||||
public JsNode getStaticRef() {
|
||||
return staticRef;
|
||||
}
|
||||
|
||||
public boolean isObfuscatable() {
|
||||
return isObfuscatable;
|
||||
}
|
||||
|
||||
public JsNameRef makeRef() {
|
||||
return new JsNameRef(this);
|
||||
}
|
||||
|
||||
public void setObfuscatable(boolean isObfuscatable) {
|
||||
this.isObfuscatable = isObfuscatable;
|
||||
}
|
||||
|
||||
public void setShortIdent(String shortIdent) {
|
||||
this.shortIdent = shortIdent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Should never be called except on immutable stuff.
|
||||
*/
|
||||
public void setStaticRef(JsNode node) {
|
||||
this.staticRef = node;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return ident;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getOriginalSymbolName() {
|
||||
return getOriginalName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return ident.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if (!(obj instanceof JsName)) {
|
||||
return false;
|
||||
}
|
||||
JsName other = (JsName) obj;
|
||||
return ident.equals(other.ident) && enclosing == other.enclosing;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DartNode getNode() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
}
|
||||
@@ -1,120 +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.ast;
|
||||
|
||||
import com.google.dart.compiler.common.Symbol;
|
||||
|
||||
/**
|
||||
* Represents a JavaScript expression that references a name.
|
||||
*/
|
||||
public final class JsNameRef extends JsExpression implements CanBooleanEval, HasName {
|
||||
|
||||
private String ident;
|
||||
private JsName name;
|
||||
private JsExpression qualifier;
|
||||
|
||||
public JsNameRef(JsName name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public JsNameRef(String ident) {
|
||||
this.ident = ident;
|
||||
}
|
||||
|
||||
public String getIdent() {
|
||||
return (name == null) ? ident : name.getIdent();
|
||||
}
|
||||
|
||||
@Override
|
||||
public JsName getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Symbol getSymbol() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public JsExpression getQualifier() {
|
||||
return qualifier;
|
||||
}
|
||||
|
||||
public String getShortIdent() {
|
||||
return (name == null) ? ident : name.getShortIdent();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasSideEffects() {
|
||||
if (qualifier == null) {
|
||||
return false;
|
||||
}
|
||||
if (!qualifier.isDefinitelyNotNull()) {
|
||||
// Could trigger NPE.
|
||||
return true;
|
||||
}
|
||||
return qualifier.hasSideEffects();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isBooleanFalse() {
|
||||
return isDefinitelyNull();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isBooleanTrue() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDefinitelyNotNull() {
|
||||
// TODO: look for single-assignment of stuff from Java?
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDefinitelyNull() {
|
||||
if (name != null) {
|
||||
return (name.getEnclosing().getProgram().getUndefinedLiteral().getName() == name);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isLeaf() {
|
||||
if (qualifier == null) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isResolved() {
|
||||
return name != null;
|
||||
}
|
||||
|
||||
public void resolve(JsName name) {
|
||||
this.name = name;
|
||||
this.ident = null;
|
||||
}
|
||||
|
||||
public void setQualifier(JsExpression qualifier) {
|
||||
this.qualifier = qualifier;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void traverse(JsVisitor v, JsContext ctx) {
|
||||
if (v.visit(this, ctx)) {
|
||||
if (qualifier != null) {
|
||||
qualifier = v.accept(qualifier);
|
||||
}
|
||||
}
|
||||
v.endVisit(this, ctx);
|
||||
}
|
||||
|
||||
@Override
|
||||
public NodeKind getKind() {
|
||||
return NodeKind.NAME_REF;
|
||||
}
|
||||
}
|
||||
@@ -1,61 +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.ast;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Represents the JavaScript new expression.
|
||||
*/
|
||||
public final class JsNew extends JsExpression implements HasArguments {
|
||||
|
||||
private final List<JsExpression> args = new ArrayList<JsExpression>();
|
||||
private JsExpression ctorExpr;
|
||||
|
||||
public JsNew(JsExpression ctorExpr) {
|
||||
this.ctorExpr = ctorExpr;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<JsExpression> getArguments() {
|
||||
return args;
|
||||
}
|
||||
|
||||
public JsExpression getConstructorExpression() {
|
||||
return ctorExpr;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasSideEffects() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDefinitelyNotNull() {
|
||||
// Sadly, in JS it can be!
|
||||
// TODO: analysis could probably determine most instances cannot be null.
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDefinitelyNull() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void traverse(JsVisitor v, JsContext ctx) {
|
||||
if (v.visit(this, ctx)) {
|
||||
ctorExpr = v.accept(ctorExpr);
|
||||
v.acceptList(args);
|
||||
}
|
||||
v.endVisit(this, ctx);
|
||||
}
|
||||
|
||||
@Override
|
||||
public NodeKind getKind() {
|
||||
return NodeKind.NEW;
|
||||
}
|
||||
}
|
||||
@@ -1,51 +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.ast;
|
||||
|
||||
import com.google.dart.compiler.backend.js.JsSourceGenerationVisitor;
|
||||
import com.google.dart.compiler.backend.js.JsToStringGenerationVisitor;
|
||||
import com.google.dart.compiler.common.AbstractNode;
|
||||
import com.google.dart.compiler.common.SourceInfo;
|
||||
import com.google.dart.compiler.util.DefaultTextOutput;
|
||||
|
||||
/**
|
||||
* Base class for all JS AST elements.
|
||||
*/
|
||||
public abstract class JsNode extends AbstractNode implements JsVisitable {
|
||||
|
||||
protected JsNode() {
|
||||
}
|
||||
|
||||
// Causes source generation to delegate to the one visitor
|
||||
public final String toSource() {
|
||||
DefaultTextOutput out = new DefaultTextOutput(false);
|
||||
JsSourceGenerationVisitor v = new JsSourceGenerationVisitor(out);
|
||||
v.accept(this);
|
||||
return out.toString();
|
||||
}
|
||||
|
||||
// Causes source generation to delegate to the one visitor
|
||||
@Override
|
||||
public final String toString() {
|
||||
DefaultTextOutput out = new DefaultTextOutput(false);
|
||||
JsToStringGenerationVisitor v = new JsToStringGenerationVisitor(out);
|
||||
v.accept(this);
|
||||
return out.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public SourceInfo getSourceInfo() {
|
||||
return this;
|
||||
}
|
||||
|
||||
public JsNode setSourceRef(SourceInfo info) {
|
||||
if (info != null) {
|
||||
this.setSourceInfo(info);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public abstract NodeKind getKind();
|
||||
}
|
||||
@@ -1,46 +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.ast;
|
||||
|
||||
/**
|
||||
* A JavaScript null literal.
|
||||
*/
|
||||
public final class JsNullLiteral extends JsValueLiteral {
|
||||
|
||||
// Should only be instantiated in JsProgram
|
||||
JsNullLiteral() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isBooleanFalse() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isBooleanTrue() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDefinitelyNotNull() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDefinitelyNull() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void traverse(JsVisitor v, JsContext ctx) {
|
||||
v.visit(this, ctx);
|
||||
v.endVisit(this, ctx);
|
||||
}
|
||||
|
||||
@Override
|
||||
public NodeKind getKind() {
|
||||
return NodeKind.NULL;
|
||||
}
|
||||
}
|
||||
@@ -1,53 +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.ast;
|
||||
|
||||
/**
|
||||
* Represents a JavaScript literal decimal expression.
|
||||
*/
|
||||
public final class JsNumberLiteral extends JsValueLiteral {
|
||||
|
||||
private final double value;
|
||||
|
||||
// Should be interned by JsProgram
|
||||
JsNumberLiteral(double value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public double getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isBooleanFalse() {
|
||||
return value == 0.0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isBooleanTrue() {
|
||||
return value != 0.0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDefinitelyNotNull() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDefinitelyNull() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void traverse(JsVisitor v, JsContext ctx) {
|
||||
v.visit(this, ctx);
|
||||
v.endVisit(this, ctx);
|
||||
}
|
||||
|
||||
@Override
|
||||
public NodeKind getKind() {
|
||||
return NodeKind.NUMBER;
|
||||
}
|
||||
}
|
||||
@@ -1,66 +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.ast;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* A JavaScript object literal.
|
||||
*/
|
||||
public final class JsObjectLiteral extends JsLiteral {
|
||||
|
||||
private final List<JsPropertyInitializer> props = new ArrayList<JsPropertyInitializer>();
|
||||
|
||||
public JsObjectLiteral() {
|
||||
}
|
||||
|
||||
public List<JsPropertyInitializer> getPropertyInitializers() {
|
||||
return props;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasSideEffects() {
|
||||
for (JsPropertyInitializer prop : props) {
|
||||
if (prop.hasSideEffects()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isBooleanFalse() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isBooleanTrue() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDefinitelyNotNull() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDefinitelyNull() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void traverse(JsVisitor v, JsContext ctx) {
|
||||
if (v.visit(this, ctx)) {
|
||||
v.acceptWithInsertRemove(props);
|
||||
}
|
||||
v.endVisit(this, ctx);
|
||||
}
|
||||
|
||||
@Override
|
||||
public NodeKind getKind() {
|
||||
return NodeKind.OBJECT;
|
||||
}
|
||||
}
|
||||
@@ -1,32 +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.ast;
|
||||
|
||||
/**
|
||||
* A JavaScript operator.
|
||||
*/
|
||||
public interface JsOperator {
|
||||
|
||||
int INFIX = 0x02;
|
||||
int LEFT = 0x01;
|
||||
int POSTFIX = 0x04;
|
||||
int PREFIX = 0x08;
|
||||
|
||||
int getPrecedence();
|
||||
|
||||
String getSymbol();
|
||||
|
||||
boolean isKeyword();
|
||||
|
||||
boolean isLeftAssociative();
|
||||
|
||||
boolean isPrecedenceLessThan(JsOperator other);
|
||||
|
||||
boolean isValidInfix();
|
||||
|
||||
boolean isValidPostfix();
|
||||
|
||||
boolean isValidPrefix();
|
||||
}
|
||||
@@ -1,41 +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.ast;
|
||||
|
||||
import com.google.dart.compiler.common.Symbol;
|
||||
|
||||
/**
|
||||
* A JavaScript parameter.
|
||||
*/
|
||||
public final class JsParameter extends JsNode implements HasName {
|
||||
|
||||
private final JsName name;
|
||||
|
||||
public JsParameter(JsName name) {
|
||||
this.name = name;
|
||||
name.setStaticRef(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public JsName getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Symbol getSymbol() {
|
||||
return name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void traverse(JsVisitor v, JsContext ctx) {
|
||||
v.visit(this, ctx);
|
||||
v.endVisit(this, ctx);
|
||||
}
|
||||
|
||||
@Override
|
||||
public NodeKind getKind() {
|
||||
return NodeKind.PARAMETER;
|
||||
}
|
||||
}
|
||||
@@ -1,42 +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.ast;
|
||||
|
||||
/**
|
||||
* A JavaScript postfix operation.
|
||||
*/
|
||||
public final class JsPostfixOperation extends JsUnaryOperation {
|
||||
|
||||
public JsPostfixOperation(JsUnaryOperator op) {
|
||||
this(op, null);
|
||||
}
|
||||
|
||||
public JsPostfixOperation(JsUnaryOperator op, JsExpression arg) {
|
||||
super(op, arg);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDefinitelyNotNull() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDefinitelyNull() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void traverse(JsVisitor v, JsContext ctx) {
|
||||
if (v.visit(this, ctx)) {
|
||||
super.traverse(v, ctx);
|
||||
}
|
||||
v.endVisit(this, ctx);
|
||||
}
|
||||
|
||||
@Override
|
||||
public NodeKind getKind() {
|
||||
return NodeKind.POSTFIX_OP;
|
||||
}
|
||||
}
|
||||
@@ -1,69 +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.ast;
|
||||
|
||||
/**
|
||||
* A JavaScript prefix operation.
|
||||
*/
|
||||
public final class JsPrefixOperation extends JsUnaryOperation implements CanBooleanEval {
|
||||
|
||||
public JsPrefixOperation(JsUnaryOperator op) {
|
||||
this(op, null);
|
||||
}
|
||||
|
||||
public JsPrefixOperation(JsUnaryOperator op, JsExpression arg) {
|
||||
super(op, arg);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isBooleanFalse() {
|
||||
if (getOperator() == JsUnaryOperator.VOID) {
|
||||
return true;
|
||||
}
|
||||
if (getOperator() == JsUnaryOperator.NOT && getArg() instanceof CanBooleanEval) {
|
||||
CanBooleanEval eval = (CanBooleanEval) getArg();
|
||||
return eval.isBooleanTrue();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isBooleanTrue() {
|
||||
if (getOperator() == JsUnaryOperator.NOT && getArg() instanceof CanBooleanEval) {
|
||||
CanBooleanEval eval = (CanBooleanEval) getArg();
|
||||
return eval.isBooleanFalse();
|
||||
}
|
||||
if (getOperator() == JsUnaryOperator.TYPEOF) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDefinitelyNotNull() {
|
||||
if (getOperator() == JsUnaryOperator.TYPEOF) {
|
||||
return true;
|
||||
}
|
||||
return getOperator() != JsUnaryOperator.VOID;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDefinitelyNull() {
|
||||
return getOperator() == JsUnaryOperator.VOID;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void traverse(JsVisitor v, JsContext ctx) {
|
||||
if (v.visit(this, ctx)) {
|
||||
super.traverse(v, ctx);
|
||||
}
|
||||
v.endVisit(this, ctx);
|
||||
}
|
||||
|
||||
@Override
|
||||
public NodeKind getKind() {
|
||||
return NodeKind.PREFIX_OP;
|
||||
}
|
||||
}
|
||||
@@ -1,172 +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.ast;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* A JavaScript program.
|
||||
*/
|
||||
public final class JsProgram extends JsNode {
|
||||
|
||||
private final JsStatement debuggerStmt;
|
||||
private final JsEmpty emptyStmt;
|
||||
private final JsBooleanLiteral falseLiteral;
|
||||
private JsProgramFragment[] fragments;
|
||||
private final Map<String, JsFunction> indexedFunctions = new HashMap<String, JsFunction>();
|
||||
private final JsNullLiteral nullLiteral;
|
||||
private final Map<Double, JsNumberLiteral> numberLiteralMap =
|
||||
new HashMap<Double, JsNumberLiteral>();
|
||||
private final JsScope objectScope;
|
||||
private final JsRootScope rootScope;
|
||||
private final Map<String, JsStringLiteral> stringLiteralMap =
|
||||
new HashMap<String, JsStringLiteral>();
|
||||
private final JsScope topScope;
|
||||
private final JsBooleanLiteral trueLiteral;
|
||||
|
||||
/**
|
||||
* Constructs a JavaScript program object.
|
||||
*/
|
||||
public JsProgram(String unitId) {
|
||||
rootScope = new JsRootScope(this);
|
||||
topScope = new JsScope(rootScope, "Global", unitId);
|
||||
objectScope = new JsScope(rootScope, "Object");
|
||||
setFragmentCount(1);
|
||||
|
||||
debuggerStmt = new JsDebugger();
|
||||
emptyStmt = new JsEmpty();
|
||||
falseLiteral = new JsBooleanLiteral(false);
|
||||
nullLiteral = new JsNullLiteral();
|
||||
trueLiteral = new JsBooleanLiteral(true);
|
||||
}
|
||||
|
||||
public JsBooleanLiteral getBooleanLiteral(boolean truth) {
|
||||
if (truth) {
|
||||
return getTrueLiteral();
|
||||
}
|
||||
return getFalseLiteral();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the {@link JsStatement} to use whenever parsed source include a
|
||||
* <code>debugger</code> statement.
|
||||
*/
|
||||
public JsStatement getDebuggerStmt() {
|
||||
return debuggerStmt;
|
||||
}
|
||||
|
||||
public JsEmpty getEmptyStmt() {
|
||||
return emptyStmt;
|
||||
}
|
||||
|
||||
public JsBooleanLiteral getFalseLiteral() {
|
||||
return falseLiteral;
|
||||
}
|
||||
|
||||
public JsBlock getFragmentBlock(int fragment) {
|
||||
if (fragment < 0 || fragment >= fragments.length) {
|
||||
throw new IllegalArgumentException("Invalid fragment: " + fragment);
|
||||
}
|
||||
return fragments[fragment].getGlobalBlock();
|
||||
}
|
||||
|
||||
public int getFragmentCount() {
|
||||
return this.fragments.length;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the one and only global block.
|
||||
*/
|
||||
public JsBlock getGlobalBlock() {
|
||||
return getFragmentBlock(0);
|
||||
}
|
||||
|
||||
public JsFunction getIndexedFunction(String name) {
|
||||
return indexedFunctions.get(name);
|
||||
}
|
||||
|
||||
public JsNullLiteral getNullLiteral() {
|
||||
return nullLiteral;
|
||||
}
|
||||
|
||||
public JsNumberLiteral getNumberLiteral(double value) {
|
||||
JsNumberLiteral lit = numberLiteralMap.get(value);
|
||||
if (lit == null) {
|
||||
lit = new JsNumberLiteral(value);
|
||||
numberLiteralMap.put(value, lit);
|
||||
}
|
||||
|
||||
return lit;
|
||||
}
|
||||
|
||||
public JsScope getObjectScope() {
|
||||
return objectScope;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the quasi-mythical root scope. This is not the same as the top scope;
|
||||
* all unresolvable identifiers wind up here, because they are considered
|
||||
* external to the program.
|
||||
*/
|
||||
public JsRootScope getRootScope() {
|
||||
return rootScope;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the top level scope. This is the scope of all the statements in the
|
||||
* main program.
|
||||
*/
|
||||
public JsScope getScope() {
|
||||
return topScope;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates or retrieves a JsStringLiteral from an interned object pool.
|
||||
*/
|
||||
public JsStringLiteral getStringLiteral(String value) {
|
||||
JsStringLiteral lit = stringLiteralMap.get(value);
|
||||
if (lit == null) {
|
||||
lit = new JsStringLiteral(value);
|
||||
stringLiteralMap.put(value, lit);
|
||||
}
|
||||
return lit;
|
||||
}
|
||||
|
||||
public JsBooleanLiteral getTrueLiteral() {
|
||||
return trueLiteral;
|
||||
}
|
||||
|
||||
public JsNameRef getUndefinedLiteral() {
|
||||
return new JsNameRef("$Dart$Null");
|
||||
}
|
||||
|
||||
public void setFragmentCount(int fragments) {
|
||||
this.fragments = new JsProgramFragment[fragments];
|
||||
for (int i = 0; i < fragments; i++) {
|
||||
this.fragments[i] = new JsProgramFragment();
|
||||
}
|
||||
}
|
||||
|
||||
public void setIndexedFunctions(Map<String, JsFunction> indexedFunctions) {
|
||||
this.indexedFunctions.clear();
|
||||
this.indexedFunctions.putAll(indexedFunctions);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void traverse(JsVisitor v, JsContext ctx) {
|
||||
if (v.visit(this, ctx)) {
|
||||
for (JsProgramFragment fragment : fragments) {
|
||||
v.accept(fragment);
|
||||
}
|
||||
}
|
||||
v.endVisit(this, ctx);
|
||||
}
|
||||
|
||||
@Override
|
||||
public NodeKind getKind() {
|
||||
return NodeKind.PROGRAM;
|
||||
}
|
||||
}
|
||||
@@ -1,34 +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.ast;
|
||||
|
||||
/**
|
||||
* One independently loadable fragment of a {@link JsProgram}.
|
||||
*/
|
||||
public class JsProgramFragment extends JsNode {
|
||||
|
||||
private final JsGlobalBlock globalBlock;
|
||||
|
||||
public JsProgramFragment() {
|
||||
this.globalBlock = new JsGlobalBlock();
|
||||
}
|
||||
|
||||
public JsBlock getGlobalBlock() {
|
||||
return globalBlock;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void traverse(JsVisitor v, JsContext ctx) {
|
||||
if (v.visit(this, ctx)) {
|
||||
v.accept(globalBlock);
|
||||
}
|
||||
v.endVisit(this, ctx);
|
||||
}
|
||||
|
||||
@Override
|
||||
public NodeKind getKind() {
|
||||
return NodeKind.PROGRAM_FRAGMENT;
|
||||
}
|
||||
}
|
||||
@@ -1,56 +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.ast;
|
||||
|
||||
/**
|
||||
* Used in object literals to specify property values by name.
|
||||
*/
|
||||
public class JsPropertyInitializer extends JsNode {
|
||||
|
||||
private JsExpression labelExpr;
|
||||
private JsExpression valueExpr;
|
||||
|
||||
public JsPropertyInitializer() {
|
||||
}
|
||||
|
||||
public JsPropertyInitializer(JsExpression labelExpr, JsExpression valueExpr) {
|
||||
this.labelExpr = labelExpr;
|
||||
this.valueExpr = valueExpr;
|
||||
}
|
||||
|
||||
public JsExpression getLabelExpr() {
|
||||
return labelExpr;
|
||||
}
|
||||
|
||||
public JsExpression getValueExpr() {
|
||||
return valueExpr;
|
||||
}
|
||||
|
||||
public boolean hasSideEffects() {
|
||||
return labelExpr.hasSideEffects() || valueExpr.hasSideEffects();
|
||||
}
|
||||
|
||||
public void setLabelExpr(JsExpression labelExpr) {
|
||||
this.labelExpr = labelExpr;
|
||||
}
|
||||
|
||||
public void setValueExpr(JsExpression valueExpr) {
|
||||
this.valueExpr = valueExpr;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void traverse(JsVisitor v, JsContext ctx) {
|
||||
if (v.visit(this, ctx)) {
|
||||
labelExpr = v.accept(labelExpr);
|
||||
valueExpr = v.accept(valueExpr);
|
||||
}
|
||||
v.endVisit(this, ctx);
|
||||
}
|
||||
|
||||
@Override
|
||||
public NodeKind getKind() {
|
||||
return NodeKind.PROPERTY_INIT;
|
||||
}
|
||||
}
|
||||
@@ -1,64 +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.ast;
|
||||
|
||||
/**
|
||||
* A JavaScript regular expression.
|
||||
*/
|
||||
public final class JsRegExp extends JsValueLiteral {
|
||||
|
||||
private String flags;
|
||||
private String pattern;
|
||||
|
||||
public JsRegExp() {
|
||||
}
|
||||
|
||||
public String getFlags() {
|
||||
return flags;
|
||||
}
|
||||
|
||||
public String getPattern() {
|
||||
return pattern;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isBooleanFalse() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isBooleanTrue() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDefinitelyNotNull() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDefinitelyNull() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public void setFlags(String suffix) {
|
||||
this.flags = suffix;
|
||||
}
|
||||
|
||||
public void setPattern(String re) {
|
||||
this.pattern = re;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void traverse(JsVisitor v, JsContext ctx) {
|
||||
v.visit(this, ctx);
|
||||
v.endVisit(this, ctx);
|
||||
}
|
||||
|
||||
@Override
|
||||
public NodeKind getKind() {
|
||||
return NodeKind.REGEXP;
|
||||
}
|
||||
}
|
||||
@@ -1,48 +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.ast;
|
||||
|
||||
/**
|
||||
* A JavaScript return statement.
|
||||
*/
|
||||
public final class JsReturn extends JsStatement {
|
||||
|
||||
private JsExpression expr;
|
||||
|
||||
public JsReturn() {
|
||||
}
|
||||
|
||||
public JsReturn(JsExpression expr) {
|
||||
this.expr = expr;
|
||||
}
|
||||
|
||||
public JsExpression getExpr() {
|
||||
return expr;
|
||||
}
|
||||
|
||||
public void setExpr(JsExpression expr) {
|
||||
this.expr = expr;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void traverse(JsVisitor v, JsContext ctx) {
|
||||
if (v.visit(this, ctx)) {
|
||||
if (expr != null) {
|
||||
expr = v.accept(expr);
|
||||
}
|
||||
}
|
||||
v.endVisit(this, ctx);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean unconditionalControlBreak() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public NodeKind getKind() {
|
||||
return NodeKind.RETURN;
|
||||
}
|
||||
}
|
||||
@@ -1,50 +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.ast;
|
||||
|
||||
import com.google.dart.compiler.backend.js.JsReservedIdentifiers;
|
||||
|
||||
/**
|
||||
* The root scope is the parent of every scope. All identifiers in this scope
|
||||
* are not obfuscatable. This scope is prefilled with reserved global
|
||||
* JavaScript symbols.
|
||||
*/
|
||||
public final class JsRootScope extends JsScope {
|
||||
|
||||
private final JsProgram program;
|
||||
|
||||
public JsRootScope(JsProgram program) {
|
||||
super("Root");
|
||||
this.program = program;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JsProgram getProgram() {
|
||||
return program;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected JsName doCreateName(String ident, String shortIdent, String originalName) {
|
||||
JsName name = super.doCreateName(ident, shortIdent, originalName);
|
||||
name.setObfuscatable(false);
|
||||
return name;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected JsName findExistingNameNoRecurse(String ident) {
|
||||
JsName name = super.findExistingNameNoRecurse(ident);
|
||||
if (name == null) {
|
||||
if (JsReservedIdentifiers.getReservedGlobalSymbols().contains(ident)) {
|
||||
/*
|
||||
* Lazily add JsNames for reserved identifiers. Since a JsName for a reserved global symbol
|
||||
* must report a legitimate enclosing scope, we can't simply have a shared set of symbol
|
||||
* names.
|
||||
*/
|
||||
name = doCreateName(ident, ident, ident);
|
||||
}
|
||||
}
|
||||
return name;
|
||||
}
|
||||
}
|
||||
@@ -1,301 +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.ast;
|
||||
|
||||
import com.google.common.collect.Interner;
|
||||
import com.google.common.collect.Interners;
|
||||
import com.google.dart.compiler.util.Lists;
|
||||
import com.google.dart.compiler.util.Maps;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Collections;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* A scope is a factory for creating and allocating
|
||||
* {@link com.google.dart.compiler.backend.js.ast.JsName}s. A JavaScript AST is
|
||||
* built in terms of abstract name objects without worrying about obfuscation,
|
||||
* keyword/identifier blacklisting, and so on.
|
||||
*
|
||||
* <p>
|
||||
*
|
||||
* Scopes are associated with
|
||||
* {@link com.google.dart.compiler.backend.js.ast.JsFunction}s, but the two are
|
||||
* not equivalent. Functions <i>have</i> scopes, but a scope does not
|
||||
* necessarily have an associated Function. Examples of this include the
|
||||
* {@link com.google.dart.compiler.backend.js.ast.JsRootScope} and synthetic
|
||||
* scopes that might be created by a client.
|
||||
*
|
||||
* <p>
|
||||
*
|
||||
* Scopes can have parents to provide constraints when allocating actual
|
||||
* identifiers for names. Specifically, names in child scopes are chosen such
|
||||
* that they do not conflict with names in their parent scopes. The ultimate
|
||||
* parent is usually the global scope (see
|
||||
* {@link com.google.dart.compiler.backend.js.ast.JsProgram#getRootScope()}),
|
||||
* but parentless scopes are useful for managing names that are always accessed
|
||||
* with a qualifier and could therefore never be confused with the global scope
|
||||
* hierarchy.
|
||||
*/
|
||||
public class JsScope implements Serializable {
|
||||
|
||||
private List<JsScope> children = Collections.emptyList();
|
||||
private final String description;
|
||||
private Map<String, JsName> names = Collections.emptyMap();
|
||||
private JsScope parent;
|
||||
protected int tempIndex = 0;
|
||||
private final String scopeId;
|
||||
private static Interner<String> interner = Interners.newWeakInterner();
|
||||
|
||||
/*
|
||||
* Create a scope with parent.
|
||||
*/
|
||||
public JsScope(JsScope parent, String description) {
|
||||
this(parent, description, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a scope with parent.
|
||||
*/
|
||||
public JsScope(JsScope parent, String description, String scopeId) {
|
||||
assert (parent != null);
|
||||
this.scopeId = scopeId;
|
||||
this.description = interner.intern(description);
|
||||
this.parent = parent;
|
||||
parent.children = Lists.add(parent.children, this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Rebase the function to a new scope.
|
||||
* @param newParent The scope to add the function to.
|
||||
*/
|
||||
public void rebase(JsScope newParent) {
|
||||
detachFromParent();
|
||||
parent = newParent;
|
||||
parent.children = Lists.add(parent.children, this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Rebase the function's children to a new scope.
|
||||
* @param newParent
|
||||
*/
|
||||
public void rebaseChildScopes(JsScope newParent) {
|
||||
if (newParent == this) {
|
||||
return;
|
||||
}
|
||||
parent.children = Lists.addAll(parent.children, children);
|
||||
for (JsScope child : children) {
|
||||
child.parent = newParent;
|
||||
}
|
||||
children = Collections.emptyList();
|
||||
}
|
||||
|
||||
/**
|
||||
* Subclasses can detach and become parentless.
|
||||
*/
|
||||
protected void detachFromParent() {
|
||||
JsScope oldParent = parent;
|
||||
|
||||
oldParent.children = Lists.remove(
|
||||
parent.children, oldParent.children.indexOf(this));
|
||||
|
||||
parent = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Subclasses can be parentless.
|
||||
*/
|
||||
protected JsScope(String description) {
|
||||
this.description = description;
|
||||
this.parent = null;
|
||||
this.scopeId = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a name object associated with the specified ident in this scope,
|
||||
* creating it if necessary.<br/>
|
||||
* If the JsName does not exist yet, a new JsName is created. The ident,
|
||||
* short name, and original name of the newly created JsName are equal to
|
||||
* the given ident.
|
||||
*
|
||||
* @param ident An identifier that is unique within this scope.
|
||||
*/
|
||||
public JsName declareName(String ident) {
|
||||
JsName name = findExistingNameNoRecurse(ident);
|
||||
if (name != null) {
|
||||
return name;
|
||||
}
|
||||
return doCreateName(ident, ident, ident);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new variable with an unique ident in this scope.
|
||||
* The generated JsName is guaranteed to have an identifier (but not short
|
||||
* name) that does not clash with any existing variables in the scope.
|
||||
* Future declarations of variables might however clash with the temporary
|
||||
* (unless they use this function).
|
||||
*/
|
||||
public JsName declareFreshName(String shortName) {
|
||||
String ident = shortName;
|
||||
int counter = 0;
|
||||
while (findExistingNameNoRecurse(ident) != null) {
|
||||
ident = shortName + "_" + counter++;
|
||||
}
|
||||
return doCreateName(ident, shortName, shortName);
|
||||
}
|
||||
|
||||
String getNextTempName() {
|
||||
// TODO(ngeoffray): Decide on a convention for temporary variables
|
||||
// introduced by the compiler.
|
||||
return "tmp$" + (scopeId != null ? scopeId + "$" : "") + tempIndex++;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a temporary variable with an unique name in this scope.
|
||||
* The generated temporary is guaranteed to have an identifier (but not short
|
||||
* name) that does not clash with any existing variables in the scope.
|
||||
* Future declarations of variables might however clash with the temporary.
|
||||
*/
|
||||
public JsName declareTemporary() {
|
||||
return declareFreshName(getNextTempName());
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a name object associated with the specified ident in this scope,
|
||||
* creating it if necessary.<br/>
|
||||
* If the JsName does not exist yet, a new JsName is created with the given
|
||||
* ident, short name and original name.
|
||||
*
|
||||
* @param ident An identifier that is unique within this scope.
|
||||
* @param shortIdent A "pretty" name that does not have to be unique.
|
||||
* @throws IllegalArgumentException if ident already exists in this scope but
|
||||
* the requested short name does not match the existing short name.
|
||||
*/
|
||||
public JsName declareName(String ident, String shortIdent) {
|
||||
return declareName(ident, shortIdent, ident);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a name object associated with the specified ident in this scope,
|
||||
* creating it if necessary.<br/>
|
||||
* If the JsName does not exist yet, a new JsName is created. The original
|
||||
* name stored in the JsName is equal to the (unmangled) specified originalName.
|
||||
*
|
||||
* @param ident An identifier that is unique within this scope.
|
||||
* @param shortIdent A "pretty" name that does not have to be unique.
|
||||
* @param originalName The original name in the source.
|
||||
* @throws IllegalArgumentException if ident already exists in this scope but
|
||||
* the requested short name does not match the existing short name,
|
||||
* or the original name does not match the existing original name.
|
||||
*/
|
||||
public JsName declareName(String ident, String shortIdent, String originalName) {
|
||||
JsName name = findExistingNameNoRecurse(ident);
|
||||
if (name != null) {
|
||||
if (!name.getShortIdent().equals(shortIdent)
|
||||
|| !nullableEquals(name.getOriginalName(), originalName)) {
|
||||
throw new IllegalArgumentException("Requested short name " + shortIdent
|
||||
+ " conflicts with preexisting short name " + name.getShortIdent() + " for identifier "
|
||||
+ ident);
|
||||
}
|
||||
return name;
|
||||
}
|
||||
return doCreateName(ident, shortIdent, originalName);
|
||||
}
|
||||
|
||||
boolean nullableEquals(String s1, String s2) {
|
||||
return (s1 == null) ? (s2 == null) : s1.equals(s2);
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempts to find the name object for the specified ident, searching in this
|
||||
* scope, and if not found, in the parent scopes.
|
||||
*
|
||||
* @return <code>null</code> if the identifier has no associated name
|
||||
*/
|
||||
public final JsName findExistingName(String ident) {
|
||||
JsName name = findExistingNameNoRecurse(ident);
|
||||
if (name == null && parent != null) {
|
||||
return parent.findExistingName(ident);
|
||||
}
|
||||
return name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempts to find an unobfuscatable name object for the specified ident,
|
||||
* searching in this scope, and if not found, in the parent scopes.
|
||||
*
|
||||
* @return <code>null</code> if the identifier has no associated name
|
||||
*/
|
||||
public final JsName findExistingUnobfuscatableName(String ident) {
|
||||
JsName name = findExistingNameNoRecurse(ident);
|
||||
if (name != null && name.isObfuscatable()) {
|
||||
name = null;
|
||||
}
|
||||
if (name == null && parent != null) {
|
||||
return parent.findExistingUnobfuscatableName(ident);
|
||||
}
|
||||
return name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an iterator for all the names defined by this scope.
|
||||
*/
|
||||
public Iterator<JsName> getAllNames() {
|
||||
return names.values().iterator();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of this scope's child scopes.
|
||||
*/
|
||||
public final List<JsScope> getChildren() {
|
||||
return children;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the parent scope of this scope, or <code>null</code> if this is the
|
||||
* root scope.
|
||||
*/
|
||||
public final JsScope getParent() {
|
||||
return parent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the associated program.
|
||||
*/
|
||||
public JsProgram getProgram() {
|
||||
assert (parent != null) : "Subclasses must override getProgram() if they do not set a parent";
|
||||
return parent.getProgram();
|
||||
}
|
||||
|
||||
@Override
|
||||
public final String toString() {
|
||||
if (parent != null) {
|
||||
return description + "->" + parent;
|
||||
} else {
|
||||
return description;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new name in this scope.
|
||||
*/
|
||||
protected JsName doCreateName(String ident, String shortIdent, String originalName) {
|
||||
JsName name = new JsName(this, ident, shortIdent, originalName);
|
||||
names = Maps.putOrdered(names, ident, name);
|
||||
return name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempts to find the name object for the specified ident, searching in this
|
||||
* scope only.
|
||||
*
|
||||
* @return <code>null</code> if the identifier has no associated name
|
||||
*/
|
||||
protected JsName findExistingNameNoRecurse(String ident) {
|
||||
return names.get(ident);
|
||||
}
|
||||
}
|
||||
@@ -1,22 +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.ast;
|
||||
|
||||
/**
|
||||
* Abstract base class for JavaScript statement objects.
|
||||
*/
|
||||
public abstract class JsStatement extends JsNode {
|
||||
|
||||
protected JsStatement() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if this statement definitely causes an abrupt change in flow
|
||||
* control.
|
||||
*/
|
||||
public boolean unconditionalControlBreak() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -1,53 +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.ast;
|
||||
|
||||
/**
|
||||
* A JavaScript string literal expression.
|
||||
*/
|
||||
public final class JsStringLiteral extends JsValueLiteral {
|
||||
|
||||
private final String value;
|
||||
|
||||
// These only get created by JsProgram so that they can be interned.
|
||||
JsStringLiteral(String value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public String getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isBooleanFalse() {
|
||||
return value.length() == 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isBooleanTrue() {
|
||||
return value.length() != 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDefinitelyNotNull() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDefinitelyNull() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void traverse(JsVisitor v, JsContext ctx) {
|
||||
v.visit(this, ctx);
|
||||
v.endVisit(this, ctx);
|
||||
}
|
||||
|
||||
@Override
|
||||
public NodeKind getKind() {
|
||||
return NodeKind.STRING;
|
||||
}
|
||||
}
|
||||
@@ -1,47 +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.ast;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* A JavaScript switch statement.
|
||||
*/
|
||||
public class JsSwitch extends JsStatement {
|
||||
|
||||
private final List<JsSwitchMember> cases = new ArrayList<JsSwitchMember>();
|
||||
private JsExpression expr;
|
||||
|
||||
public JsSwitch() {
|
||||
super();
|
||||
}
|
||||
|
||||
public List<JsSwitchMember> getCases() {
|
||||
return cases;
|
||||
}
|
||||
|
||||
public JsExpression getExpr() {
|
||||
return expr;
|
||||
}
|
||||
|
||||
public void setExpr(JsExpression expr) {
|
||||
this.expr = expr;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void traverse(JsVisitor v, JsContext ctx) {
|
||||
if (v.visit(this, ctx)) {
|
||||
expr = v.accept(expr);
|
||||
v.acceptWithInsertRemove(cases);
|
||||
}
|
||||
v.endVisit(this, ctx);
|
||||
}
|
||||
|
||||
@Override
|
||||
public NodeKind getKind() {
|
||||
return NodeKind.SWITCH;
|
||||
}
|
||||
}
|
||||
@@ -1,24 +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.ast;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* A member/case in a JavaScript switch object.
|
||||
*/
|
||||
public abstract class JsSwitchMember extends JsNode {
|
||||
|
||||
protected final List<JsStatement> stmts = new ArrayList<JsStatement>();
|
||||
|
||||
protected JsSwitchMember() {
|
||||
super();
|
||||
}
|
||||
|
||||
public List<JsStatement> getStmts() {
|
||||
return stmts;
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user