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:
scheglov@google.com
2012-02-29 23:50:01 +00:00
parent e2e9ca6c7c
commit c81fcbafde
156 changed files with 64 additions and 20548 deletions
-2
View File
@@ -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;
}
}
@@ -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);
}
}
}
@@ -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