diff --git a/.idea/inspectionProfiles/idea_default.xml b/.idea/inspectionProfiles/idea_default.xml index 6c2f8ad45ac..23bae163cd7 100644 --- a/.idea/inspectionProfiles/idea_default.xml +++ b/.idea/inspectionProfiles/idea_default.xml @@ -410,9 +410,6 @@ diff --git a/.idea/libraries/js_libs.xml b/.idea/libraries/js_libs.xml index 54a91f2d97b..814aa1befec 100644 --- a/.idea/libraries/js_libs.xml +++ b/.idea/libraries/js_libs.xml @@ -2,15 +2,15 @@ - + - + \ No newline at end of file diff --git a/compiler/cli/src/org/jetbrains/jet/cli/js/K2JSCompiler.java b/compiler/cli/src/org/jetbrains/jet/cli/js/K2JSCompiler.java index ea1c42db269..a8b71d9e984 100644 --- a/compiler/cli/src/org/jetbrains/jet/cli/js/K2JSCompiler.java +++ b/compiler/cli/src/org/jetbrains/jet/cli/js/K2JSCompiler.java @@ -22,6 +22,7 @@ import com.google.common.base.Predicates; import com.google.common.collect.Iterables; import com.intellij.openapi.Disposable; import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.io.FileUtil; import com.intellij.psi.PsiFile; import jet.Function0; import org.jetbrains.annotations.NotNull; @@ -40,6 +41,7 @@ import org.jetbrains.k2js.config.*; import org.jetbrains.k2js.facade.K2JSTranslator; import org.jetbrains.k2js.facade.MainCallParameters; +import java.io.File; import java.util.List; import static org.jetbrains.jet.cli.common.messages.CompilerMessageLocation.NO_LOCATION; @@ -63,21 +65,15 @@ public class K2JSCompiler extends CLICompiler sourceFiles; - @Override public boolean isHelp() { return help; @@ -84,22 +81,16 @@ public class K2JSCompilerArguments extends CompilerArguments { @Override public String getSrc() { - if (sourceFiles != null) { - return sourceFiles.toString(); - } - return srcdir; + throw new IllegalStateException(); } public MainCallParameters createMainCallParameters() { - if (mainCall != null) { - if (mainCall.equals("main")) { - return MainCallParameters.mainWithoutArguments(); - } - if (mainCall.equals("mainWithArgs")) { - // TODO should we pass the arguments to the compiler? - return MainCallParameters.mainWithArguments(new ArrayList()); - } + if ("noCall".equals(main)) { + return MainCallParameters.noCall(); + } + else { + // TODO should we pass the arguments to the compiler? + return MainCallParameters.mainWithoutArguments(); } - return MainCallParameters.noCall(); } } diff --git a/idea/src/org/jetbrains/jet/plugin/compiler/K2JSCompiler.java b/idea/src/org/jetbrains/jet/plugin/compiler/K2JSCompiler.java index 500318810b5..14360d671d7 100644 --- a/idea/src/org/jetbrains/jet/plugin/compiler/K2JSCompiler.java +++ b/idea/src/org/jetbrains/jet/plugin/compiler/K2JSCompiler.java @@ -17,27 +17,29 @@ package org.jetbrains.jet.plugin.compiler; import com.google.common.collect.Lists; +import com.intellij.openapi.application.AccessToken; import com.intellij.openapi.application.ApplicationManager; -import com.intellij.openapi.compiler.CompileContext; -import com.intellij.openapi.compiler.CompileScope; -import com.intellij.openapi.compiler.CompilerMessageCategory; -import com.intellij.openapi.compiler.TranslatingCompiler; +import com.intellij.openapi.application.ReadAction; +import com.intellij.openapi.compiler.*; import com.intellij.openapi.module.Module; -import com.intellij.openapi.project.Project; +import com.intellij.openapi.roots.ModuleOrderEntry; import com.intellij.openapi.roots.ModuleRootManager; +import com.intellij.openapi.roots.OrderEntry; import com.intellij.openapi.util.Pair; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.util.ArrayUtil; import com.intellij.util.Chunk; +import com.intellij.util.StringBuilderSpinAllocator; +import gnu.trove.THashSet; import jet.Function1; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.jet.plugin.JetFileType; -import org.jetbrains.jet.plugin.k2jsrun.K2JSRunnerUtils; import org.jetbrains.jet.plugin.project.JsModuleDetector; import java.io.PrintStream; import java.util.ArrayList; +import java.util.Set; import static org.jetbrains.jet.plugin.compiler.CompilerUtils.invokeExecMethod; import static org.jetbrains.jet.plugin.compiler.CompilerUtils.outputCompilerMessagesAndHandleExitCode; @@ -46,7 +48,6 @@ import static org.jetbrains.jet.plugin.compiler.CompilerUtils.outputCompilerMess * @author Pavel Talanov */ public final class K2JSCompiler implements TranslatingCompiler { - @Override public boolean isCompilableFile(VirtualFile file, CompileContext context) { if (!(file.getFileType() instanceof JetFileType)) { @@ -115,16 +116,9 @@ public final class K2JSCompiler implements TranslatingCompiler { @NotNull private static Integer doExec(@NotNull CompileContext context, @NotNull CompilerEnvironment environment, @NotNull PrintStream out, @NotNull Module module) throws Exception { - VirtualFile[] roots = ModuleRootManager.getInstance(module).getSourceRoots(); - if (roots.length != 1) { - context.addMessage(CompilerMessageCategory.ERROR, "K2JSCompiler does not support multiple module source roots.", null, -1, -1); - return -1; - } - VirtualFile outDir = context.getModuleOutputDirectory(module); - String outFile = outDir == null ? null : K2JSRunnerUtils.constructPathToGeneratedFile(context.getProject(), outDir.getPath()); - - String[] commandLineArgs = constructArguments(context.getProject(), outFile, roots[0]); + String outFile = outDir == null ? null : outDir.getPath() + "/" + module.getName() + ".js"; + String[] commandLineArgs = constructArguments(module, outFile); Object rc = invokeExecMethod(environment, out, context, commandLineArgs, "org.jetbrains.jet.cli.js.K2JSCompiler"); if (outDir != null && !ApplicationManager.getApplication().isUnitTestMode()) { @@ -134,30 +128,96 @@ public final class K2JSCompiler implements TranslatingCompiler { } @NotNull - private static String[] constructArguments(@NotNull Project project, @Nullable String outFile, @NotNull VirtualFile srcDir) { + private static String[] constructArguments(@NotNull Module module, @Nullable String outFile) { + VirtualFile[] sourceFiles = getSourceFiles(module); + ArrayList args = Lists.newArrayList("-tags", "-verbose", "-version"); - addPathToSourcesDir(args, srcDir); + addPathToSourcesDir(sourceFiles, args); addOutputPath(outFile, args); - addLibLocationAndTarget(project, args); + addLibLocationAndTarget(module, args); return ArrayUtil.toStringArray(args); } - private static void addLibLocationAndTarget(@NotNull Project project, @NotNull ArrayList args) { - Pair libLocationAndTarget = JsModuleDetector.getLibLocationAndTargetForProject(project); - if (libLocationAndTarget.first != null) { - args.add("-libzip"); - args.add(libLocationAndTarget.first); + // we cannot use OrderEnumerator because it has critical bug — try https://gist.github.com/2953261, processor will never be called for module dependency + // we don't use context.getCompileScope().getAffectedModules() because we want to know about linkage type (well, we ignore scope right now, but in future...) + private static void collectModuleDependencies(Module dependentModule, Set modules) { + for (OrderEntry entry : ModuleRootManager.getInstance(dependentModule).getOrderEntries()) { + if (entry instanceof ModuleOrderEntry) { + ModuleOrderEntry moduleEntry = (ModuleOrderEntry) entry; + if (!moduleEntry.getScope().isForProductionCompile()) { + continue; + } + + Module module = moduleEntry.getModule(); + if (module == null) { + continue; + } + + if (modules.add(module) && moduleEntry.isExported()) { + collectModuleDependencies(module, modules); + } + } } + } + + private static VirtualFile[] getSourceFiles(@NotNull Module module) { + return CompilerManager.getInstance(module.getProject()).createModuleCompileScope(module, false) + .getFiles(JetFileType.INSTANCE, true); + } + + private static void addLibLocationAndTarget(@NotNull Module module, @NotNull ArrayList args) { + Pair libLocationAndTarget = JsModuleDetector.getLibLocationAndTargetForProject(module); + + StringBuilder sb = StringBuilderSpinAllocator.alloc(); + AccessToken token = ReadAction.start(); + try { + THashSet modules = new THashSet(); + collectModuleDependencies(module, modules); + if (!modules.isEmpty()) { + for (Module dependency : modules) { + sb.append('@').append(dependency.getName()).append(','); + + for (VirtualFile file : getSourceFiles(dependency)) { + sb.append(file.getPath()).append(','); + } + } + } + + if (libLocationAndTarget.first != null) { + for (String file : libLocationAndTarget.first) { + sb.append(file).append(','); + } + } + + if (sb.length() > 0) { + args.add("-libraryFiles"); + args.add(sb.substring(0, sb.length() - 1)); + } + } + finally { + token.finish(); + StringBuilderSpinAllocator.dispose(sb); + } + if (libLocationAndTarget.second != null) { args.add("-target"); args.add(libLocationAndTarget.second); } } - private static void addPathToSourcesDir(@NotNull ArrayList args, @NotNull VirtualFile srcDir) { - String srcPath = srcDir.getPath(); - args.add("-srcdir"); - args.add(srcPath); + private static void addPathToSourcesDir(@NotNull VirtualFile[] sourceFiles, @NotNull ArrayList args) { + args.add("-sourceFiles"); + + StringBuilder sb = StringBuilderSpinAllocator.alloc(); + try { + for (VirtualFile file : sourceFiles) { + sb.append(file.getPath()).append(','); + } + args.add(sb.substring(0, sb.length() - 1)); + } + finally { + StringBuilderSpinAllocator.dispose(sb); + } } private static void addOutputPath(@Nullable String outFile, @NotNull ArrayList args) { diff --git a/idea/src/org/jetbrains/jet/plugin/project/IDEAConfig.java b/idea/src/org/jetbrains/jet/plugin/project/IDEAConfig.java index db29e72d794..2afd9df2842 100644 --- a/idea/src/org/jetbrains/jet/plugin/project/IDEAConfig.java +++ b/idea/src/org/jetbrains/jet/plugin/project/IDEAConfig.java @@ -19,15 +19,15 @@ package org.jetbrains.jet.plugin.project; import com.intellij.openapi.project.Project; import org.jetbrains.annotations.NotNull; import org.jetbrains.k2js.config.EcmaVersion; -import org.jetbrains.k2js.config.ZippedLibrarySourcesConfig; +import org.jetbrains.k2js.config.LibrarySourcesConfig; import static org.jetbrains.jet.plugin.project.JsModuleDetector.getLibLocationAndTargetForProject; /** * @author Pavel Talanov */ -public final class IDEAConfig extends ZippedLibrarySourcesConfig { +public final class IDEAConfig extends LibrarySourcesConfig { public IDEAConfig(@NotNull Project project) { - super(project, getLibLocationAndTargetForProject(project).first, EcmaVersion.defaultVersion()); + super(project, "default", getLibLocationAndTargetForProject(project).first, EcmaVersion.defaultVersion()); } } diff --git a/idea/src/org/jetbrains/jet/plugin/project/JsModuleDetector.java b/idea/src/org/jetbrains/jet/plugin/project/JsModuleDetector.java index 796791c1179..526be3aa494 100644 --- a/idea/src/org/jetbrains/jet/plugin/project/JsModuleDetector.java +++ b/idea/src/org/jetbrains/jet/plugin/project/JsModuleDetector.java @@ -36,10 +36,6 @@ public final class JsModuleDetector { private JsModuleDetector() { } - public static boolean isJsProject(@NotNull Project project) { - return getJSModule(project) != null; - } - public static boolean isJsModule(@NotNull Module module) { return K2JSModuleComponent.getInstance(module).isJavaScriptModule(); } @@ -57,15 +53,22 @@ public final class JsModuleDetector { } @NotNull - public static Pair getLibLocationAndTargetForProject(@NotNull Project project) { + public static Pair getLibLocationAndTargetForProject(@NotNull Project project) { Module module = getJSModule(project); if (module == null) { return Pair.empty(); } + else { + return getLibLocationAndTargetForProject(module); + } + } + + @NotNull + public static Pair getLibLocationAndTargetForProject(@NotNull Module module) { K2JSModuleComponent jsModuleComponent = K2JSModuleComponent.getInstance(module); String pathToJavaScriptLibrary = jsModuleComponent.getPathToJavaScriptLibrary(); String basePath = ModuleRootManager.getInstance(module).getContentRoots()[0].getPath(); - return Pair.create(basePath + pathToJavaScriptLibrary, jsModuleComponent.getEcmaVersion().toString()); + return Pair.create(new String[] {basePath + pathToJavaScriptLibrary}, jsModuleComponent.getEcmaVersion().toString()); } @Nullable diff --git a/js/js.libraries/src/core/annotations.kt b/js/js.libraries/src/core/annotations.kt index 15e9185634a..f1a1793fc7a 100644 --- a/js/js.libraries/src/core/annotations.kt +++ b/js/js.libraries/src/core/annotations.kt @@ -3,4 +3,6 @@ package js; native public annotation class native(name : String = "") {} native -public annotation class library(name : String = "") {} \ No newline at end of file +public annotation class library(name : String = "") {} +native +public annotation class enumerable() {} \ No newline at end of file diff --git a/js/js.libraries/src/core/debug.kt b/js/js.libraries/src/core/debug.kt index 54000cc79b6..a542b0f5b2d 100644 --- a/js/js.libraries/src/core/debug.kt +++ b/js/js.libraries/src/core/debug.kt @@ -1,11 +1,15 @@ package js.debug -import js.* +import js.noImpl + +// https://developer.mozilla.org/en/DOM/console +native trait Console { + native fun dir(o: Any): Unit = noImpl + native fun error(vararg o: Any?): Unit = noImpl + native fun info(vararg o: Any?): Unit = noImpl + native fun log(vararg o: Any?): Unit = noImpl + native fun warn(vararg o: Any?): Unit = noImpl +} native -val console : consoleClass = js.noImpl - -native -class consoleClass() { - fun log(message : Any?) : Unit = js.noImpl -} \ No newline at end of file +val console:Console = noImpl \ No newline at end of file diff --git a/js/js.libraries/src/core/javautil.kt b/js/js.libraries/src/core/javautil.kt index 414cd77f702..7a57c6c54d5 100644 --- a/js/js.libraries/src/core/javautil.kt +++ b/js/js.libraries/src/core/javautil.kt @@ -12,12 +12,11 @@ public trait Comparator { library public trait Iterator { - open fun next() : T = js.noImpl - open fun hasNext() : Boolean = js.noImpl - open fun remove() : Unit = js.noImpl + open public fun next() : T = js.noImpl + open public fun hasNext() : Boolean = js.noImpl + open public fun remove() : Unit = js.noImpl } - val Collections = object { library("collectionsMax") public fun max(col : Collection, comp : Comparator) : T = js.noImpl @@ -55,120 +54,74 @@ val Collections = object { } library -public open class ArrayList() : java.util.List { - public override fun size() : Int = js.noImpl - public override fun isEmpty() : Boolean = js.noImpl - public override fun contains(o : Any?) : Boolean = js.noImpl - public override fun iterator() : Iterator = js.noImpl - // public override fun indexOf(o : Any?) : Int = js.noImpl - // public override fun lastIndexOf(o : Any?) : Int = js.noImpl - // public override fun toArray() : Array = js.noImpl - // public override fun toArray(a : Array) : Array = js.noImpl - public override fun get(index : Int) : E = js.noImpl - public override fun set(index : Int, element : E) : E = js.noImpl - public override fun add(e : E) : Boolean = js.noImpl - public override fun add(index : Int, element : E) : Unit = js.noImpl - library("removeByIndex") - public override fun remove(index : Int) : E = js.noImpl - public override fun remove(o : Any?) : Boolean = js.noImpl - public override fun clear() : Unit = js.noImpl - public override fun addAll(c : java.util.Collection) : Boolean = js.noImpl - // public override fun addAll(index : Int, c : java.util.Collection) : Boolean = js.noImpl -} - -library -public trait Collection : java.lang.Iterable { - open public fun size() : Int - open public fun isEmpty() : Boolean - open public fun contains(o : Any?) : Boolean - override public fun iterator() : java.util.Iterator - // open public fun toArray() : Array - // open public fun toArray(a : Array) : Array - open public fun add(e : E) : Boolean - open public fun remove(o : Any?) : Boolean +public trait Collection: Iterable { + open public fun size(): Int + open public fun isEmpty(): Boolean + open public fun contains(o: Any?): Boolean + override public fun iterator(): Iterator + public fun toArray(): Array + // open public fun toArray(a : Array) : Array + open public fun add(e: E): Boolean + open public fun remove(o: Any?): Boolean //open public fun containsAll(c : java.util.Collection<*>) : Boolean - open public fun addAll(c : java.util.Collection) : Boolean + open public fun addAll(c: Collection): Boolean //open public fun removeAll(c : java.util.Collection<*>) : Boolean //open public fun retainAll(c : java.util.Collection<*>) : Boolean - open public fun clear() : Unit + open public fun clear(): Unit } library -public abstract open class AbstractCollection() : Collection { +public abstract class AbstractCollection() : Collection { + override public fun toArray(): Array = js.noImpl + + override public fun isEmpty(): Boolean = js.noImpl + override public fun contains(o: Any?): Boolean = js.noImpl + override public fun iterator(): Iterator = js.noImpl + + override public fun add(e: E): Boolean = js.noImpl + override public fun remove(o: Any?): Boolean = js.noImpl + + override public fun addAll(c: Collection): Boolean = js.noImpl + + override public fun clear(): Unit = js.noImpl + override public fun size(): Int = js.noImpl } library -public abstract open class AbstractList() : AbstractCollection(), List { - public override fun isEmpty() : Boolean = js.noImpl - public override fun contains(o : Any?) : Boolean = js.noImpl - public override fun iterator() : Iterator = js.noImpl - // public override fun indexOf(o : Any?) : Int = js.noImpl - // public override fun lastIndexOf(o : Any?) : Int = js.noImpl - // public override fun toArray() : Array = js.noImpl - // public override fun toArray(a : Array) : Array = js.noImpl - public override fun set(index : Int, element : E) : E = js.noImpl - public override fun add(e : E) : Boolean = js.noImpl - public override fun add(index : Int, element : E) : Unit = js.noImpl - library("removeByIndex") - public override fun remove(index : Int) : E = js.noImpl - public override fun remove(o : Any?) : Boolean = js.noImpl - public override fun clear() : Unit = js.noImpl - public override fun addAll(c : java.util.Collection) : Boolean = js.noImpl - // public override fun addAll(index : Int, c : java.util.Collection) : Boolean = js.noImpl +public trait List: Collection { + public fun get(index: Int): E + public fun set(index: Int, element: E): E + + public fun add(index: Int, element: E): Unit + public fun remove(index: Int): E + + public fun indexOf(o: E?): Int } library -public trait List : Collection { - override public fun size() : Int - override public fun isEmpty() : Boolean - override public fun contains(o : Any?) : Boolean - override public fun iterator() : java.util.Iterator - // override public fun toArray() : Array - // Simulate Java's array covariance - // override public fun toArray(a : Array) : Array - override public fun add(e : E) : Boolean - override public fun remove(o : Any?) : Boolean - // override public fun containsAll(c : java.util.Collection<*>) : Boolean - override public fun addAll(c : java.util.Collection) : Boolean - // open public fun addAll(index : Int, c : java.util.Collection) : Boolean - // override public fun removeAll(c : java.util.Collection<*>) : Boolean - // override public fun retainAll(c : java.util.Collection<*>) : Boolean - override public fun clear() : Unit - open public fun get(index : Int) : E - open public fun set(index : Int, element : E) : E - open public fun add(index : Int, element : E) : Unit - open public fun remove(index : Int) : E - // open public fun indexOf(o : Any?) : Int - // open public fun lastIndexOf(o : Any?) : Int +public abstract class AbstractList(): AbstractCollection(), List { + override public fun get(index: Int): E = js.noImpl + override public fun set(index: Int, element: E): E = js.noImpl + + library("addAt") + override public public fun add(index: Int, element: E): Unit = js.noImpl + + library("removeAt") + override public fun remove(index: Int): E = js.noImpl + + override public fun indexOf(o: E?): Int = js.noImpl +} + +library +public open class ArrayList() : AbstractList() { } library public trait Set : Collection { - override public fun size() : Int - override public fun isEmpty() : Boolean - override public fun contains(o : Any?) : Boolean - override public fun iterator() : java.util.Iterator - // override public fun toArray() : Array - // override public fun toArray(a : Array) : Array - override public fun add(e : E) : Boolean - override public fun remove(o : Any?) : Boolean - //override public fun containsAll(c : java.util.Collection<*>) : Boolean - override public fun addAll(c : java.util.Collection) : Boolean - //override public fun retainAll(c : java.util.Collection<*>) : Boolean - //override public fun removeAll(c : java.util.Collection<*>) : Boolean - override public fun clear() : Unit } library -public open class HashSet() : java.util.Set { - public override fun iterator() : java.util.Iterator = js.noImpl - public override fun size() : Int = js.noImpl - public override fun isEmpty() : Boolean = js.noImpl - public override fun contains(o : Any?) : Boolean = js.noImpl - public override fun add(e : E) : Boolean = js.noImpl - public override fun remove(o : Any?) : Boolean = js.noImpl - public override fun clear() : Unit = js.noImpl - override fun addAll(c : java.util.Collection) : Boolean = js.noImpl +public open class HashSet(): AbstractCollection(), java.util.Set { } library @@ -186,23 +139,23 @@ public trait Map { open public fun values() : java.util.Collection open public fun entrySet() : java.util.Set> -// open public fun equals(o : Any?) : Boolean -// open public fun hashCode() : Int +// open public fun equals(o : Any?) : Boolean +// open public fun hashCode() : Int trait Entry { open public fun getKey() : K open public fun getValue() : V open public fun setValue(value : V) : V -// open public fun equals(o : Any?) : Boolean -// open public fun hashCode() : Int +// open public fun equals(o : Any?) : Boolean +// open public fun hashCode() : Int } } library -public open class HashMap() : java.util.Map { +public open class HashMap() : Map { public override fun size() : Int = js.noImpl public override fun isEmpty() : Boolean = js.noImpl - public override fun get(key : Any?) : V = js.noImpl + public override fun get(key : Any?) : V? = js.noImpl public override fun containsKey(key : Any?) : Boolean = js.noImpl public override fun put(key : K, value : V) : V = js.noImpl public override fun putAll(m : java.util.Map) : Unit = js.noImpl @@ -215,37 +168,23 @@ public open class HashMap() : java.util.Map { } library -public open class LinkedList() : List { - public override fun iterator() : java.util.Iterator = js.noImpl - public override fun isEmpty() : Boolean = js.noImpl - public override fun contains(o : Any?) : Boolean = js.noImpl - public override fun size() : Int = js.noImpl - public override fun add(e : E) : Boolean = js.noImpl - public override fun remove(o : Any?) : Boolean = js.noImpl - public override fun addAll(c : java.util.Collection) : Boolean = js.noImpl - public override fun clear() : Unit = js.noImpl - public override fun get(index : Int) : E = js.noImpl - public override fun set(index : Int, element : E) : E = js.noImpl - public override fun add(index : Int, element : E) : Unit = js.noImpl - public override fun remove(index : Int) : E = js.noImpl - public fun poll() : E? = js.noImpl - public fun peek() : E? = js.noImpl - public fun offer(e : E) : Boolean = js.noImpl +public open class LinkedList(): AbstractList() { + public override fun get(index: Int): E = js.noImpl + public override fun set(index: Int, element: E): E = js.noImpl + public override fun add(index: Int, element: E): Unit = js.noImpl + public fun poll(): E? = js.noImpl + public fun peek(): E? = js.noImpl + public fun offer(e: E): Boolean = js.noImpl } library public class StringBuilder() : Appendable { - override fun append(c: Char): Appendable? = js.noImpl - override fun append(csq: CharSequence?): Appendable? = js.noImpl - override fun append(csq: CharSequence?, start: Int, end: Int): Appendable? = js.noImpl + override public fun append(c: Char): Appendable? = js.noImpl + override public fun append(csq: CharSequence?): Appendable? = js.noImpl + override public fun append(csq: CharSequence?, start: Int, end: Int): Appendable? = js.noImpl public fun append(obj : Any?) : StringBuilder = js.noImpl public fun toString() : String = js.noImpl } library -public class NoSuchElementException() : Exception() {} - -public trait Enumeration { - open fun hasMoreElements(): Boolean - open fun nextElement(): E? -} +public class NoSuchElementException() : Exception() {} \ No newline at end of file diff --git a/js/js.libraries/src/core/json.kt b/js/js.libraries/src/core/json.kt index 9a60614639c..f61f1904724 100644 --- a/js/js.libraries/src/core/json.kt +++ b/js/js.libraries/src/core/json.kt @@ -21,4 +21,13 @@ library("jsonFromTuples") public fun json2(pairs : Array>) : Json = js.noImpl library("jsonAddProperties") -public fun Json.add(other : Json) : Json = js.noImpl \ No newline at end of file +public fun Json.add(other : Json) : Json = js.noImpl + +native +public trait JsonClass { + public fun stringify(o: Any): String = noImpl + public fun parse(text: String): T = noImpl +} + +native +public val JSON:JsonClass = noImpl \ No newline at end of file diff --git a/js/js.tests/test/org/jetbrains/k2js/test/BasicTest.java b/js/js.tests/test/org/jetbrains/k2js/test/BasicTest.java index 3adf4798ba5..453e3b3e3e3 100644 --- a/js/js.tests/test/org/jetbrains/k2js/test/BasicTest.java +++ b/js/js.tests/test/org/jetbrains/k2js/test/BasicTest.java @@ -43,6 +43,8 @@ public abstract class BasicTest extends TestWithEnvironment { private static final String CASES = "cases/"; private static final String OUT = "out/"; private static final String EXPECTED = "expected/"; + + public static final String JSLINT_LIB = pathToTestFilesRoot() + "jslint.js"; @NotNull private String mainDirectory = ""; diff --git a/js/js.tests/test/org/jetbrains/k2js/test/SingleFileTranslationTest.java b/js/js.tests/test/org/jetbrains/k2js/test/SingleFileTranslationTest.java index 3f09631dfaf..4d3dc54ca1f 100644 --- a/js/js.tests/test/org/jetbrains/k2js/test/SingleFileTranslationTest.java +++ b/js/js.tests/test/org/jetbrains/k2js/test/SingleFileTranslationTest.java @@ -42,7 +42,7 @@ public abstract class SingleFileTranslationTest extends BasicTest { runFunctionOutputTest(EcmaVersion.all(), kotlinFilename, namespaceName, functionName, expectedResult); } - protected void runFunctionOutputTest(@NotNull EnumSet ecmaVersions, @NotNull String kotlinFilename, + protected void runFunctionOutputTest(@NotNull Iterable ecmaVersions, @NotNull String kotlinFilename, @NotNull String namespaceName, @NotNull String functionName, @NotNull Object expectedResult) throws Exception { @@ -50,7 +50,7 @@ public abstract class SingleFileTranslationTest extends BasicTest { runRhinoTests(kotlinFilename, ecmaVersions, new RhinoFunctionResultChecker(namespaceName, functionName, expectedResult)); } - public void checkFooBoxIsTrue(@NotNull String filename, @NotNull EnumSet ecmaVersions) throws Exception { + public void checkFooBoxIsTrue(@NotNull String filename, @NotNull Iterable ecmaVersions) throws Exception { runFunctionOutputTest(ecmaVersions, filename, "foo", "box", true); } @@ -58,7 +58,7 @@ public abstract class SingleFileTranslationTest extends BasicTest { checkFooBoxIsTrue(getTestName(true) + ".kt", EcmaVersion.all()); } - protected void fooBoxTest(@NotNull EnumSet ecmaVersions) throws Exception { + protected void fooBoxTest(@NotNull Iterable ecmaVersions) throws Exception { checkFooBoxIsTrue(getTestName(true) + ".kt", ecmaVersions); } @@ -84,7 +84,7 @@ public abstract class SingleFileTranslationTest extends BasicTest { runRhinoTests(kotlinFilename, ecmaVersions, new RhinoSystemOutputChecker(expectedResult)); } - protected void performTestWithMain(@NotNull EnumSet ecmaVersions, + protected void performTestWithMain(@NotNull Iterable ecmaVersions, @NotNull String testName, @NotNull String testId, @NotNull String... args) throws Exception { diff --git a/js/js.tests/test/org/jetbrains/k2js/test/config/TestConfig.java b/js/js.tests/test/org/jetbrains/k2js/test/config/TestConfig.java index f3832533022..bd2db405a78 100644 --- a/js/js.tests/test/org/jetbrains/k2js/test/config/TestConfig.java +++ b/js/js.tests/test/org/jetbrains/k2js/test/config/TestConfig.java @@ -28,8 +28,11 @@ import java.util.List; /** * @author Pavel Talanov */ -public class TestConfig extends Config { +public final class TestConfig extends Config { + //NOTE: hard-coded in kotlin-lib files + @NotNull + public static final String TEST_MODULE_NAME = "JS_TESTS"; @NotNull private final List jsLibFiles; @NotNull @@ -37,7 +40,7 @@ public class TestConfig extends Config { public TestConfig(@NotNull Project project, @NotNull EcmaVersion version, @NotNull List files, @NotNull BindingContext context) { - super(project, version); + super(project, TEST_MODULE_NAME, version); jsLibFiles = files; libraryContext = context; } diff --git a/js/js.tests/test/org/jetbrains/k2js/test/rhino/FunctionWithScope.java b/js/js.tests/test/org/jetbrains/k2js/test/rhino/FunctionWithScope.java new file mode 100644 index 00000000000..228c914c824 --- /dev/null +++ b/js/js.tests/test/org/jetbrains/k2js/test/rhino/FunctionWithScope.java @@ -0,0 +1,44 @@ +/* + * Copyright 2010-2012 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.k2js.test.rhino; + +import org.jetbrains.annotations.NotNull; +import org.mozilla.javascript.Function; +import org.mozilla.javascript.Scriptable; + +/** + * @author Sergey Simonchik + */ +class FunctionWithScope { + private final Function fun; + private final Scriptable scope; + + FunctionWithScope(@NotNull Function function, @NotNull Scriptable scope) { + this.fun = function; + this.scope = scope; + } + + @NotNull + public Function getFunction() { + return fun; + } + + @NotNull + public Scriptable getScope() { + return scope; + } +} diff --git a/js/js.tests/test/org/jetbrains/k2js/test/rhino/RhinoFunctionManager.java b/js/js.tests/test/org/jetbrains/k2js/test/rhino/RhinoFunctionManager.java new file mode 100644 index 00000000000..a4f460b18ab --- /dev/null +++ b/js/js.tests/test/org/jetbrains/k2js/test/rhino/RhinoFunctionManager.java @@ -0,0 +1,106 @@ +/* + * Copyright 2010-2012 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.k2js.test.rhino; + +import com.google.common.base.Supplier; +import com.intellij.openapi.diagnostic.Logger; +import org.jetbrains.annotations.NotNull; +import org.mozilla.javascript.Context; +import org.mozilla.javascript.Function; +import org.mozilla.javascript.Script; +import org.mozilla.javascript.Scriptable; + +/** + * @author Sergey Simonchik + */ +class RhinoFunctionManager { + private static final Logger LOG = Logger.getInstance(RhinoFunctionManager.class); + + private final ThreadLocal threadLocalFunction = new ThreadLocal() { + @Override + protected FunctionWithScope initialValue() { + if (script == null) { + synchronized (threadLocalFunction) { + if (script == null) { + script = compileScript(9); + } + } + } + return extractFunctionWithScope(script); + } + }; + + private volatile Script script; + + private final Supplier scriptSourceProvider; + private final String functionName; + + public RhinoFunctionManager(@NotNull Supplier scriptSourceProvider, + @NotNull String functionName) { + this.scriptSourceProvider = scriptSourceProvider; + this.functionName = functionName; + } + + private Script compileScript(int optimizationLevel) { + long startNano = System.nanoTime(); + Context context = Context.enter(); + try { + context.setOptimizationLevel(optimizationLevel); + String scriptSource = scriptSourceProvider.get(); + return context.compileString(scriptSource, "<" + functionName + " script>", 1, null); + } + finally { + Context.exit(); + LOG.info(formatMessage(startNano, functionName + " script rhino compilation")); + } + } + + @NotNull + private FunctionWithScope extractFunctionWithScope(@NotNull Script script) { + long startNano = System.nanoTime(); + Context context = Context.enter(); + try { + Scriptable scope = context.initStandardObjects(); + script.exec(context, scope); + Object jsLintObj = scope.get(functionName, scope); + if (jsLintObj instanceof Function) { + Function jsLint = (Function) jsLintObj; + return new FunctionWithScope(jsLint, scope); + } + else { + throw new RuntimeException(functionName + " is undefined or not a function."); + } + } + finally { + Context.exit(); + LOG.info(formatMessage(startNano, functionName + " function extraction")); + } + } + + private static String formatMessage(long startTimeNano, @NotNull String actionName) { + long nanoDuration = System.nanoTime() - startTimeNano; + return String.format("[%s] %s took %.2f ms", + Thread.currentThread().getName(), + actionName, + nanoDuration / 1000000.0); + } + + @NotNull + public FunctionWithScope getFunctionWithScope() { + return threadLocalFunction.get(); + } +} diff --git a/js/js.tests/test/org/jetbrains/k2js/test/rhino/RhinoFunctionNativeObjectResultChecker.java b/js/js.tests/test/org/jetbrains/k2js/test/rhino/RhinoFunctionNativeObjectResultChecker.java index 59eba9537cc..198f6a5cea8 100644 --- a/js/js.tests/test/org/jetbrains/k2js/test/rhino/RhinoFunctionNativeObjectResultChecker.java +++ b/js/js.tests/test/org/jetbrains/k2js/test/rhino/RhinoFunctionNativeObjectResultChecker.java @@ -17,6 +17,7 @@ package org.jetbrains.k2js.test.rhino; import org.jetbrains.annotations.Nullable; +import org.mozilla.javascript.Context; import org.mozilla.javascript.NativeJavaObject; /** @@ -33,13 +34,13 @@ public class RhinoFunctionNativeObjectResultChecker extends RhinoFunctionResultC } @Override - protected void assertResultValid(Object result) { + protected void assertResultValid(Object result, Context context) { if (result instanceof NativeJavaObject) { NativeJavaObject nativeJavaObject = (NativeJavaObject) result; Object unwrap = nativeJavaObject.unwrap(); - super.assertResultValid(unwrap); + super.assertResultValid(unwrap, context); } else { - super.assertResultValid(result); + super.assertResultValid(result, context); } } } diff --git a/js/js.tests/test/org/jetbrains/k2js/test/rhino/RhinoFunctionResultChecker.java b/js/js.tests/test/org/jetbrains/k2js/test/rhino/RhinoFunctionResultChecker.java index 55d4f847bf7..cd9886f9707 100644 --- a/js/js.tests/test/org/jetbrains/k2js/test/rhino/RhinoFunctionResultChecker.java +++ b/js/js.tests/test/org/jetbrains/k2js/test/rhino/RhinoFunctionResultChecker.java @@ -17,6 +17,8 @@ package org.jetbrains.k2js.test.rhino; import org.jetbrains.annotations.Nullable; +import org.jetbrains.k2js.test.config.TestConfig; +import org.jetbrains.k2js.translate.context.Namer; import org.mozilla.javascript.Context; import org.mozilla.javascript.Scriptable; @@ -46,11 +48,12 @@ public class RhinoFunctionResultChecker implements RhinoResultChecker { public void runChecks(Context context, Scriptable scope) throws Exception { Object result = evaluateFunction(context, scope); flushSystemOut(context, scope); - assertResultValid(result); + assertResultValid(result, context); } - protected void assertResultValid(Object result) { - assertEquals("Result of " + namespaceName + "." + functionName + "() is not what expected!", expectedResult, result); + protected void assertResultValid(Object result, Context context) { + String ecmaVersion = context.getLanguageVersion() == Context.VERSION_1_8 ? "ecma5" : "ecma3"; + assertEquals("Result of " + namespaceName + "." + functionName + "() is not what expected (" + ecmaVersion + ")!", expectedResult, result); String report = namespaceName + "." + functionName + "() = " + Context.toString(result); System.out.println(report); } @@ -60,10 +63,14 @@ public class RhinoFunctionResultChecker implements RhinoResultChecker { } private String functionCallString() { - String result = functionName + "()"; + StringBuilder sb = new StringBuilder(); if (namespaceName != null) { - result = "Kotlin.defs." + namespaceName + "." + result; + sb.append("Kotlin.modules." + TestConfig.TEST_MODULE_NAME); + if (namespaceName != Namer.getRootNamespaceName()) { + sb.append('.').append(namespaceName); + } + sb.append('.'); } - return result; + return sb.append(functionName).append("()").toString(); } } diff --git a/js/js.tests/test/org/jetbrains/k2js/test/rhino/RhinoUtils.java b/js/js.tests/test/org/jetbrains/k2js/test/rhino/RhinoUtils.java index bb2dc39d996..46f9abd6c13 100644 --- a/js/js.tests/test/org/jetbrains/k2js/test/rhino/RhinoUtils.java +++ b/js/js.tests/test/org/jetbrains/k2js/test/rhino/RhinoUtils.java @@ -17,15 +17,19 @@ package org.jetbrains.k2js.test.rhino; import closurecompiler.internal.com.google.common.collect.Maps; +import com.google.common.base.Supplier; +import com.google.common.collect.Sets; +import com.intellij.openapi.util.io.FileUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.k2js.config.EcmaVersion; import org.jetbrains.k2js.facade.K2JSTranslator; -import org.mozilla.javascript.Context; -import org.mozilla.javascript.Scriptable; -import org.mozilla.javascript.ScriptableObject; +import org.jetbrains.k2js.test.BasicTest; +import org.mozilla.javascript.*; +import java.io.File; import java.io.FileReader; +import java.io.IOException; import java.util.List; import java.util.Map; import java.util.Set; @@ -37,6 +41,28 @@ import static org.jetbrains.k2js.test.BasicTest.pathToTestFilesRoot; * @author Pavel Talanov */ public final class RhinoUtils { + @NotNull + private static final Set IGNORED_JSLINT_WARNINGS = Sets.newHashSet(); + + static { + // todo dart ast bug + IGNORED_JSLINT_WARNINGS.add("Unexpected space between '}' and '('."); + // don't read JS, use kotlin and idea debugger ;) + IGNORED_JSLINT_WARNINGS + .add("Wrap an immediate function invocation in parentheses to assist the reader in understanding that the expression is the result of a function, and not the function itself."); + } + + @NotNull + private static final RhinoFunctionManager functionManager = new RhinoFunctionManager( + new Supplier() { + @Override + public String get() { + return fileToString(BasicTest.JSLINT_LIB); + } + }, + "JSLINT" + ); + public static final String KOTLIN_JS_LIB_COMMON = pathToTestFilesRoot() + "kotlin_lib.js"; private static final String KOTLIN_JS_LIB_ECMA_3 = pathToTestFilesRoot() + "kotlin_lib_ecma3.js"; @@ -46,6 +72,15 @@ public final class RhinoUtils { } + private static String fileToString(String file) { + try { + return FileUtil.loadFile(new File(file)); + } + catch (IOException e) { + throw new RuntimeException(e); + } + } + private static void runFileWithRhino(@NotNull String inputFile, @NotNull Context context, @NotNull Scriptable scope) throws Exception { @@ -75,6 +110,8 @@ public final class RhinoUtils { runFileWithRhino(filename, context, scope); } checker.runChecks(context, scope); + + lintIt(context, fileNames.get(fileNames.size() - 1)); } finally { Context.exit(); @@ -147,4 +184,61 @@ public final class RhinoUtils { static void flushSystemOut(@NotNull Context context, @NotNull Scriptable scope) { context.evaluateString(scope, K2JSTranslator.FLUSH_SYSTEM_OUT, "test", 0, null); } + + private static void lintIt(Context context, String fileName) throws IOException { + if (Boolean.valueOf(System.getProperty("test.lint.skip"))) { + return; + } + + NativeObject options = new NativeObject(); + // todo fix dart ast? + options.defineProperty("white", true, ScriptableObject.READONLY); + // vars, http://uxebu.com/blog/2010/04/02/one-var-statement-for-one-variable/ + options.defineProperty("vars", true, ScriptableObject.READONLY); + NativeArray globals = new NativeArray(new Object[] {"Kotlin"}); + options.defineProperty("predef", globals, ScriptableObject.READONLY); + + Object[] args = {FileUtil.loadFile(new File(fileName)), options}; + FunctionWithScope functionWithScope = functionManager.getFunctionWithScope(); + Function function = functionWithScope.getFunction(); + Scriptable scope = functionWithScope.getScope(); + Object status = function.call(context, scope, scope, args); + Boolean noErrors = (Boolean) Context.jsToJava(status, Boolean.class); + if (!noErrors) { + Object errors = function.get("errors", scope); + if (errors == null) { + return; + } + + System.out.println(fileName); + for (Object errorObj : ((NativeArray) errors)) { + if (!(errorObj instanceof NativeObject)) { + continue; + } + + NativeObject e = (NativeObject) errorObj; + int line = toInt(e.get("line")); + int character = toInt(e.get("character")); + if (line < 0 || character < 0) { + continue; + } + Object reasonObj = e.get("reason"); + if (reasonObj instanceof String) { + String reason = (String) reasonObj; + if (IGNORED_JSLINT_WARNINGS.contains(reason)) { + continue; + } + + System.out.println(line + ":" + character + " " + reason); + } + } + } + } + + private static int toInt(Object obj) { + if (obj instanceof Number) { + return ((Number) obj).intValue(); + } + return -1; + } } diff --git a/js/js.tests/test/org/jetbrains/k2js/test/semantics/ArrayListTest.java b/js/js.tests/test/org/jetbrains/k2js/test/semantics/ArrayListTest.java index 4a2f0b6ff1f..8f8551acee7 100644 --- a/js/js.tests/test/org/jetbrains/k2js/test/semantics/ArrayListTest.java +++ b/js/js.tests/test/org/jetbrains/k2js/test/semantics/ArrayListTest.java @@ -51,6 +51,10 @@ public final class ArrayListTest extends JavaClassesTest { fooBoxTest(); } + public void testToArray() throws Exception { + fooBoxTest(); + } + public void testIndexOOB() throws Exception { try { fooBoxTest(); diff --git a/js/js.tests/test/org/jetbrains/k2js/test/semantics/ExtensionFunctionTest.java b/js/js.tests/test/org/jetbrains/k2js/test/semantics/ExtensionFunctionTest.java index 643c15cb047..c5bbaa32be5 100644 --- a/js/js.tests/test/org/jetbrains/k2js/test/semantics/ExtensionFunctionTest.java +++ b/js/js.tests/test/org/jetbrains/k2js/test/semantics/ExtensionFunctionTest.java @@ -74,4 +74,8 @@ public final class ExtensionFunctionTest extends SingleFileTranslationTest { public void testExtensionPropertyOnClassWithExplicitAndImplicitReceiver() throws Exception { fooBoxTest(); } + + public void testExtensionFunctionCalledFromFor() throws Exception { + fooBoxTest(); + } } diff --git a/js/js.tests/test/org/jetbrains/k2js/test/semantics/MultiFileTest.java b/js/js.tests/test/org/jetbrains/k2js/test/semantics/MultiFileTest.java index 5dc8927264a..a7e5d11246f 100644 --- a/js/js.tests/test/org/jetbrains/k2js/test/semantics/MultiFileTest.java +++ b/js/js.tests/test/org/jetbrains/k2js/test/semantics/MultiFileTest.java @@ -34,4 +34,8 @@ public final class MultiFileTest extends MultipleFilesTranslationTest { public void testClassesInheritedFromOtherFile() throws Exception { checkFooBoxIsTrue("classesInheritedFromOtherFile"); } + + public void testClassOfTheSameNameInAnotherPackage() throws Exception { + checkFooBoxIsTrue("classOfTheSameNameInAnotherPackage"); + } } diff --git a/js/js.tests/test/org/jetbrains/k2js/test/semantics/ObjectTest.java b/js/js.tests/test/org/jetbrains/k2js/test/semantics/ObjectTest.java index 51eed910ebd..141fec3b0b6 100644 --- a/js/js.tests/test/org/jetbrains/k2js/test/semantics/ObjectTest.java +++ b/js/js.tests/test/org/jetbrains/k2js/test/semantics/ObjectTest.java @@ -16,8 +16,11 @@ package org.jetbrains.k2js.test.semantics; +import org.jetbrains.k2js.config.EcmaVersion; import org.jetbrains.k2js.test.SingleFileTranslationTest; +import java.util.EnumSet; + /** * @author Pavel Talanov */ @@ -41,9 +44,13 @@ public final class ObjectTest extends SingleFileTranslationTest { fooBoxTest(); } + public void testObjectInObject() throws Exception { + fooBoxTest(EnumSet.noneOf(EcmaVersion.class)); + } + public void testObjectInheritingFromATrait() throws Exception { fooBoxTest(); } -} \ No newline at end of file +} diff --git a/js/js.tests/test/org/jetbrains/k2js/test/semantics/PropertyAccessTest.java b/js/js.tests/test/org/jetbrains/k2js/test/semantics/PropertyAccessTest.java index 1e01ecba839..5e49b03936b 100644 --- a/js/js.tests/test/org/jetbrains/k2js/test/semantics/PropertyAccessTest.java +++ b/js/js.tests/test/org/jetbrains/k2js/test/semantics/PropertyAccessTest.java @@ -16,10 +16,14 @@ package org.jetbrains.k2js.test.semantics; +import com.google.common.collect.Lists; +import org.jetbrains.annotations.NotNull; import org.jetbrains.k2js.config.EcmaVersion; import org.jetbrains.k2js.test.SingleFileTranslationTest; +import org.jetbrains.k2js.test.utils.JsTestUtils; import java.util.EnumSet; +import java.util.List; /** * @author Pavel Talanov @@ -79,4 +83,23 @@ public final class PropertyAccessTest extends SingleFileTranslationTest { public void testInitInstanceProperties() throws Exception { fooBoxTest(EnumSet.of(EcmaVersion.v5)); } + + public void testEnumerable() throws Exception { + fooBoxTest(JsTestUtils.successOnEcmaV5()); + } + + public void testOverloadedOverriddenFunctionPropertyName() throws Exception { + //fooBoxTest(JsTestUtils.successOnEcmaV5()); + //fooBoxTest(); + } + + @Override + @NotNull + protected List additionalJSFiles(@NotNull EcmaVersion ecmaVersion) { + List result = Lists.newArrayList(super.additionalJSFiles(ecmaVersion)); + if (getName().equals("testEnumerable")) { + result.add(pathToTestFiles() + "enumerate.js"); + } + return result; + } } diff --git a/js/js.tests/test/org/jetbrains/k2js/test/semantics/StdLibTestSupport.java b/js/js.tests/test/org/jetbrains/k2js/test/semantics/StdLibTestSupport.java index 6c2f14ba2ad..1381212893f 100644 --- a/js/js.tests/test/org/jetbrains/k2js/test/semantics/StdLibTestSupport.java +++ b/js/js.tests/test/org/jetbrains/k2js/test/semantics/StdLibTestSupport.java @@ -79,7 +79,7 @@ abstract class StdLibTestSupport extends SingleFileTranslationTest { K2JSCompiler compiler = new K2JSCompiler(); K2JSCompilerArguments arguments = new K2JSCompilerArguments(); arguments.outputFile = getOutputFilePath(getTestName(false) + ".compiler.kt", version); - arguments.sourceFiles = files; + arguments.sourceFiles = files.toArray(new String[files.size()]); arguments.verbose = true; System.out.println("Compiling with version: " + version + " to: " + arguments.outputFile); ExitCode answer = compiler.exec(System.out, arguments); diff --git a/js/js.tests/test/org/jetbrains/k2js/test/utils/JsTestUtils.java b/js/js.tests/test/org/jetbrains/k2js/test/utils/JsTestUtils.java index dd103c1a1b0..2f3165fc4e2 100644 --- a/js/js.tests/test/org/jetbrains/k2js/test/utils/JsTestUtils.java +++ b/js/js.tests/test/org/jetbrains/k2js/test/utils/JsTestUtils.java @@ -24,6 +24,7 @@ import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.util.ArrayList; +import java.util.EnumSet; import java.util.List; /** @@ -34,6 +35,11 @@ public final class JsTestUtils { private JsTestUtils() { } + @NotNull + public static EnumSet successOnEcmaV5() { + return EnumSet.of(EcmaVersion.v5); + } + @NotNull public static String convertFileNameToDotJsFile(@NotNull String filename, @NotNull EcmaVersion ecmaVersion) { String postFix = "_" + ecmaVersion.toString() + ".js"; diff --git a/js/js.translator/lib/rhino-1.7R3-src.jar b/js/js.translator/lib/rhino-1.7R3-src.jar deleted file mode 100644 index 6baf004ef6b..00000000000 Binary files a/js/js.translator/lib/rhino-1.7R3-src.jar and /dev/null differ diff --git a/js/js.translator/lib/rhino-1.7R3.jar b/js/js.translator/lib/rhino-1.7R3.jar deleted file mode 100644 index 878b0d9422b..00000000000 Binary files a/js/js.translator/lib/rhino-1.7R3.jar and /dev/null differ diff --git a/js/js.translator/lib/rhino-1.7R4-sources.jar b/js/js.translator/lib/rhino-1.7R4-sources.jar new file mode 100644 index 00000000000..f9691cf8969 Binary files /dev/null and b/js/js.translator/lib/rhino-1.7R4-sources.jar differ diff --git a/js/js.translator/lib/rhino-1.7R4.jar b/js/js.translator/lib/rhino-1.7R4.jar new file mode 100644 index 00000000000..6f0dafbbc78 Binary files /dev/null and b/js/js.translator/lib/rhino-1.7R4.jar differ diff --git a/js/js.translator/src/org/jetbrains/k2js/analyze/AnalyzerFacadeForJS.java b/js/js.translator/src/org/jetbrains/k2js/analyze/AnalyzerFacadeForJS.java index 3f956e7c2ec..3b5a10dafb1 100644 --- a/js/js.translator/src/org/jetbrains/k2js/analyze/AnalyzerFacadeForJS.java +++ b/js/js.translator/src/org/jetbrains/k2js/analyze/AnalyzerFacadeForJS.java @@ -18,7 +18,6 @@ package org.jetbrains.k2js.analyze; import com.google.common.base.Predicate; import com.google.common.base.Predicates; -import com.google.common.collect.Sets; import com.intellij.openapi.project.Project; import com.intellij.psi.PsiFile; import org.jetbrains.annotations.NotNull; @@ -41,10 +40,10 @@ import org.jetbrains.jet.lang.resolve.scopes.WritableScope; import org.jetbrains.jet.lang.types.lang.JetStandardClasses; import org.jetbrains.k2js.config.Config; +import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.List; -import java.util.Set; import static org.jetbrains.jet.lang.resolve.DescriptorUtils.isRootNamespace; @@ -136,7 +135,7 @@ public final class AnalyzerFacadeForJS { @NotNull public static Collection withJsLibAdded(@NotNull Collection files, @NotNull Config config) { - Set allFiles = Sets.newHashSet(); + Collection allFiles = new ArrayList(); allFiles.addAll(files); allFiles.addAll(config.getLibFiles()); return allFiles; diff --git a/js/js.translator/src/org/jetbrains/k2js/config/ClassPathLibraryDefintionsConfig.java b/js/js.translator/src/org/jetbrains/k2js/config/ClassPathLibraryDefintionsConfig.java index 41133aebb07..5ed0995de7e 100644 --- a/js/js.translator/src/org/jetbrains/k2js/config/ClassPathLibraryDefintionsConfig.java +++ b/js/js.translator/src/org/jetbrains/k2js/config/ClassPathLibraryDefintionsConfig.java @@ -16,11 +16,11 @@ package org.jetbrains.k2js.config; -import org.jetbrains.annotations.NotNull; import com.intellij.openapi.project.Project; +import org.jetbrains.annotations.NotNull; import org.jetbrains.jet.lang.psi.JetFile; -import java.util.*; +import java.util.List; /** * A Config implementation which is configured with a directory to find the standard library names from @@ -28,8 +28,8 @@ import java.util.*; public class ClassPathLibraryDefintionsConfig extends Config { public static final String META_INF_SERVICES_FILE = "META-INF/services/org.jetbrains.kotlin.js.libraryDefinitions"; - public ClassPathLibraryDefintionsConfig(@NotNull Project project, @NotNull EcmaVersion version) { - super(project, version); + public ClassPathLibraryDefintionsConfig(@NotNull Project project, @NotNull String moduleId, @NotNull EcmaVersion version) { + super(project, moduleId, version); } @NotNull diff --git a/js/js.translator/src/org/jetbrains/k2js/config/Config.java b/js/js.translator/src/org/jetbrains/k2js/config/Config.java index fd7c43ae621..43e21dcd01c 100644 --- a/js/js.translator/src/org/jetbrains/k2js/config/Config.java +++ b/js/js.translator/src/org/jetbrains/k2js/config/Config.java @@ -36,7 +36,7 @@ public abstract class Config { @NotNull public static Config getEmptyConfig(@NotNull Project project, @NotNull EcmaVersion ecmaVersion) { - return new Config(project, ecmaVersion) { + return new Config(project, "main", ecmaVersion) { @NotNull @Override protected List generateLibFiles() { @@ -133,9 +133,13 @@ public abstract class Config { @NotNull private final EcmaVersion target; - public Config(@NotNull Project project, @NotNull EcmaVersion ecmaVersion) { + @NotNull + private final String moduleId; + + public Config(@NotNull Project project, @NotNull String moduleId, @NotNull EcmaVersion ecmaVersion) { this.project = project; this.target = ecmaVersion; + this.moduleId = moduleId; } @NotNull @@ -148,6 +152,11 @@ public abstract class Config { return target; } + @NotNull + public String getModuleId() { + return moduleId; + } + @NotNull protected abstract List generateLibFiles(); diff --git a/js/js.translator/src/org/jetbrains/k2js/config/ZippedLibrarySourcesConfig.java b/js/js.translator/src/org/jetbrains/k2js/config/LibrarySourcesConfig.java similarity index 54% rename from js/js.translator/src/org/jetbrains/k2js/config/ZippedLibrarySourcesConfig.java rename to js/js.translator/src/org/jetbrains/k2js/config/LibrarySourcesConfig.java index 723e0171ba7..bbf31d8dc8a 100644 --- a/js/js.translator/src/org/jetbrains/k2js/config/ZippedLibrarySourcesConfig.java +++ b/js/js.translator/src/org/jetbrains/k2js/config/LibrarySourcesConfig.java @@ -18,6 +18,7 @@ package org.jetbrains.k2js.config; import com.google.common.collect.Lists; import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.Key; import com.intellij.openapi.util.io.FileUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -27,6 +28,7 @@ import org.jetbrains.k2js.utils.JetFileUtils; import java.io.File; import java.io.IOException; import java.io.InputStream; +import java.util.ArrayList; import java.util.Collections; import java.util.Enumeration; import java.util.List; @@ -36,33 +38,63 @@ import java.util.zip.ZipFile; /** * @author Pavel Talanov */ -public class ZippedLibrarySourcesConfig extends Config { - @Nullable - protected final String pathToLibZip; +public class LibrarySourcesConfig extends Config { + public static final Key EXTERNAL_MODULE_NAME = new Key("externalModule"); + public static final String UNKNOWN_EXTERNAL_MODULE_NAME = ""; - public ZippedLibrarySourcesConfig(@NotNull Project project, @Nullable String pathToZip, @NotNull EcmaVersion ecmaVersion) { - super(project, ecmaVersion); - pathToLibZip = pathToZip; + @Nullable + private final String[] files; + + public LibrarySourcesConfig(@NotNull Project project, + @NotNull String moduleId, + @Nullable String[] files, + @NotNull EcmaVersion ecmaVersion) { + super(project, moduleId, ecmaVersion); + this.files = files; } @NotNull @Override public List generateLibFiles() { - if (pathToLibZip == null) { + if (files == null) { return Collections.emptyList(); } - try { - File file = new File(pathToLibZip); - ZipFile zipFile = new ZipFile(file); + + List jetFiles = new ArrayList(); + String moduleName = UNKNOWN_EXTERNAL_MODULE_NAME; + for (String path : files) { + File file = new File(path); try { - return traverseArchive(zipFile); + String name = file.getName(); + if (path.charAt(0) == '@') { + moduleName = path.substring(1); + continue; + } + + if (name.endsWith(".jar") || name.endsWith(".zip")) { + jetFiles.addAll(readZip(file)); + } + else { + JetFile psiFile = JetFileUtils.createPsiFile(path, FileUtil.loadFile(file), getProject()); + psiFile.putUserData(EXTERNAL_MODULE_NAME, moduleName); + jetFiles.add(psiFile); + } } - finally { - zipFile.close(); + catch (IOException e) { + throw new RuntimeException(e); } } - catch (IOException e) { - return Collections.emptyList(); + + return jetFiles; + } + + private List readZip(File file) throws IOException { + ZipFile zipFile = new ZipFile(file); + try { + return traverseArchive(zipFile); + } + finally { + zipFile.close(); } } @@ -76,6 +108,7 @@ public class ZippedLibrarySourcesConfig extends Config { InputStream stream = file.getInputStream(entry); String text = FileUtil.loadTextAndClose(stream); JetFile jetFile = JetFileUtils.createPsiFile(entry.getName(), text, getProject()); + jetFile.putUserData(EXTERNAL_MODULE_NAME, UNKNOWN_EXTERNAL_MODULE_NAME); result.add(jetFile); } } diff --git a/js/js.translator/src/org/jetbrains/k2js/facade/K2JSTranslator.java b/js/js.translator/src/org/jetbrains/k2js/facade/K2JSTranslator.java index eac933e6357..88b6a423dcc 100644 --- a/js/js.translator/src/org/jetbrains/k2js/facade/K2JSTranslator.java +++ b/js/js.translator/src/org/jetbrains/k2js/facade/K2JSTranslator.java @@ -32,7 +32,6 @@ import org.jetbrains.k2js.utils.JetFileUtils; import java.io.IOException; import java.util.Arrays; -import java.util.Collection; import java.util.List; import static org.jetbrains.k2js.facade.FacadeUtils.parseString; @@ -97,9 +96,7 @@ public final class K2JSTranslator { throws TranslationException { JetStandardLibrary.initialize(config.getProject()); BindingContext bindingContext = AnalyzerFacadeForJS.analyzeFilesAndCheckErrors(filesToTranslate, config); - Collection files = AnalyzerFacadeForJS.withJsLibAdded(filesToTranslate, config); - - return Translation.generateAst(bindingContext, Lists.newArrayList(files), mainCallParameters, config.getTarget(), rawStatements); + return Translation.generateAst(bindingContext, filesToTranslate, mainCallParameters, config, rawStatements); } @NotNull diff --git a/js/js.translator/src/org/jetbrains/k2js/facade/MainCallParameters.java b/js/js.translator/src/org/jetbrains/k2js/facade/MainCallParameters.java index d7c5bc8cf73..326d8683868 100644 --- a/js/js.translator/src/org/jetbrains/k2js/facade/MainCallParameters.java +++ b/js/js.translator/src/org/jetbrains/k2js/facade/MainCallParameters.java @@ -25,7 +25,6 @@ import java.util.List; * @author Pavel Talanov */ public abstract class MainCallParameters { - @NotNull public static MainCallParameters noCall() { return new MainCallParameters() { diff --git a/js/js.translator/src/org/jetbrains/k2js/translate/context/DynamicContext.java b/js/js.translator/src/org/jetbrains/k2js/translate/context/DynamicContext.java index ed0028287ac..aa1d08c6496 100644 --- a/js/js.translator/src/org/jetbrains/k2js/translate/context/DynamicContext.java +++ b/js/js.translator/src/org/jetbrains/k2js/translate/context/DynamicContext.java @@ -53,8 +53,13 @@ public final class DynamicContext { @NotNull public TemporaryVariable declareTemporary(@NotNull JsExpression initExpression) { + return declareTemporary(initExpression, false); + } + + @NotNull + public TemporaryVariable declareTemporary(@NotNull JsExpression initExpression, boolean initialize) { JsName temporaryName = currentScope.declareTemporary(); - JsVars temporaryDeclaration = newVar(temporaryName, /*no init expression in var statement*/ null); + JsVars temporaryDeclaration = newVar(temporaryName, initialize ? initExpression : null); addVarDeclaration(jsBlock(), temporaryDeclaration); return new TemporaryVariable(temporaryName, initExpression); } diff --git a/js/js.translator/src/org/jetbrains/k2js/translate/context/Namer.java b/js/js.translator/src/org/jetbrains/k2js/translate/context/Namer.java index 52da0995874..871ddbadedd 100644 --- a/js/js.translator/src/org/jetbrains/k2js/translate/context/Namer.java +++ b/js/js.translator/src/org/jetbrains/k2js/translate/context/Namer.java @@ -32,14 +32,13 @@ public final class Namer { private static final String INITIALIZE_METHOD_NAME = "initialize"; private static final String CLASS_OBJECT_NAME = "createClass"; private static final String TRAIT_OBJECT_NAME = "createTrait"; - private static final String NAMESPACE_OBJECT_NAME = "createNamespace"; private static final String OBJECT_OBJECT_NAME = "createObject"; private static final String SETTER_PREFIX = "set_"; private static final String GETTER_PREFIX = "get_"; private static final String BACKING_FIELD_PREFIX = "$"; private static final String SUPER_METHOD_NAME = "super_init"; private static final String KOTLIN_OBJECT_NAME = "Kotlin"; - private static final String ROOT_NAMESPACE = "Root"; + private static final String ROOT_NAMESPACE = "_"; private static final String RECEIVER_PARAMETER_NAME = "receiver"; private static final String CLASSES_OBJECT_NAME = "classes"; private static final String THROW_NPE_FUN_NAME = "throwNPE"; @@ -112,7 +111,7 @@ public final class Namer { @NotNull private final JsName traitName; @NotNull - private final JsName namespaceName; + private final JsExpression definePackage; @NotNull private final JsName objectName; @@ -122,17 +121,24 @@ public final class Namer { @NotNull private final JsPropertyInitializer writablePropertyDescriptorField; + @NotNull + private final JsPropertyInitializer enumerablePropertyDescriptorField; + private Namer(@NotNull JsScope rootScope) { kotlinName = rootScope.declareName(KOTLIN_OBJECT_NAME); kotlinScope = new JsScope(rootScope, "Kotlin standard object"); traitName = kotlinScope.declareName(TRAIT_OBJECT_NAME); - namespaceName = kotlinScope.declareName(NAMESPACE_OBJECT_NAME); + + definePackage = kotlin("definePackage"); + className = kotlinScope.declareName(CLASS_OBJECT_NAME); objectName = kotlinScope.declareName(OBJECT_OBJECT_NAME); isTypeName = kotlinScope.declareName("isType"); - writablePropertyDescriptorField = new JsPropertyInitializer(new JsNameRef("writable"), rootScope.getProgram().getTrueLiteral()); + JsProgram program = rootScope.getProgram(); + writablePropertyDescriptorField = new JsPropertyInitializer(program.getStringLiteral("writable"), program.getTrueLiteral()); + enumerablePropertyDescriptorField = new JsPropertyInitializer(program.getStringLiteral("enumerable"), program.getTrueLiteral()); } @NotNull @@ -146,8 +152,8 @@ public final class Namer { } @NotNull - public JsExpression namespaceCreationMethodReference() { - return kotlin(namespaceName); + public JsExpression packageDefinitionMethodReference() { + return definePackage; } @NotNull @@ -164,14 +170,17 @@ public final class Namer { @NotNull private JsExpression kotlin(@NotNull JsName name) { - JsNameRef reference = name.makeRef(); - reference.setQualifier(kotlinName.makeRef()); - return reference; + return kotlin(name.makeRef()); + } + + @NotNull + public JsExpression kotlin(@NotNull String name) { + return kotlin(kotlinScope.declareName(name)); } @NotNull private JsExpression kotlin(@NotNull JsExpression reference) { - setQualifier(reference, kotlinName.makeRef()); + setQualifier(reference, kotlinObject()); return reference; } @@ -190,6 +199,11 @@ public final class Namer { return writablePropertyDescriptorField; } + @NotNull + public JsPropertyInitializer enumerablePropertyDescriptorField() { + return enumerablePropertyDescriptorField; + } + @NotNull /*package*/ JsScope getKotlinScope() { return kotlinScope; diff --git a/js/js.translator/src/org/jetbrains/k2js/translate/context/StaticContext.java b/js/js.translator/src/org/jetbrains/k2js/translate/context/StaticContext.java index 3027ae32f33..d933bbb2ba0 100644 --- a/js/js.translator/src/org/jetbrains/k2js/translate/context/StaticContext.java +++ b/js/js.translator/src/org/jetbrains/k2js/translate/context/StaticContext.java @@ -18,13 +18,17 @@ package org.jetbrains.k2js.translate.context; import com.google.common.collect.Maps; import com.google.dart.compiler.backend.js.ast.*; +import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiFile; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.jet.lang.descriptors.*; import org.jetbrains.jet.lang.resolve.BindingContext; +import org.jetbrains.jet.lang.resolve.BindingContextUtils; import org.jetbrains.jet.lang.resolve.DescriptorUtils; import org.jetbrains.jet.lang.types.lang.JetStandardLibrary; import org.jetbrains.k2js.config.EcmaVersion; +import org.jetbrains.k2js.config.LibrarySourcesConfig; import org.jetbrains.k2js.translate.context.generator.Generator; import org.jetbrains.k2js.translate.context.generator.Rule; import org.jetbrains.k2js.translate.intrinsic.Intrinsics; @@ -105,6 +109,11 @@ public final class StaticContext { return ecmaVersion == EcmaVersion.v5; } + @NotNull + public EcmaVersion getEcmaVersion() { + return ecmaVersion; + } + @NotNull public JsProgram getProgram() { return program; @@ -153,6 +162,14 @@ public final class StaticContext { } private final class NameGenerator extends Generator { + private JsName declareName(DeclarationDescriptor descriptor, String name) { + NamingScope scope = getEnclosingScope(descriptor); + // ecma 5 property name never declares as obfuscatable: + // 1) property cannot be overloaded, so, name collision is not possible + // 2) main reason: if property doesn't have any custom accessor, value holder will have the same name as accessor, so, the same name will be declared more than once + return isEcma5() ? scope.declareUnobfuscatableName(name) : scope.declareObfuscatableName(name); + } + public NameGenerator() { Rule namesForStandardClasses = new Rule() { @Override @@ -171,8 +188,16 @@ public final class StaticContext { if (!(descriptor instanceof NamespaceDescriptor)) { return null; } - String nameForNamespace = getNameForNamespace((NamespaceDescriptor) descriptor); - return getRootScope().declareUnobfuscatableName(nameForNamespace); + + String name; + if (DescriptorUtils.isRootNamespace((NamespaceDescriptor) descriptor)) { + name = Namer.getRootNamespaceName(); + } + else { + name = descriptor.getName().getName(); + } + + return getRootScope().declareUnobfuscatableName(name); } }; Rule memberDeclarationsInsideParentsScope = new Rule() { @@ -180,6 +205,9 @@ public final class StaticContext { @Nullable public JsName apply(@NotNull DeclarationDescriptor descriptor) { NamingScope namingScope = getEnclosingScope(descriptor); + if (descriptor instanceof ClassDescriptor) { + return namingScope.declareUnobfuscatableName(descriptor.getName().getName()); + } return namingScope.declareObfuscatableName(descriptor.getName().getName()); } }; @@ -203,11 +231,9 @@ public final class StaticContext { boolean isGetter = descriptor instanceof PropertyGetterDescriptor; PropertyAccessorDescriptor accessorDescriptor = (PropertyAccessorDescriptor) descriptor; String propertyName = accessorDescriptor.getCorrespondingProperty().getName().getName(); - String accessorName = Namer.getNameForAccessor(propertyName, isGetter, !accessorDescriptor.getReceiverParameter().exists() && isEcma5()); - NamingScope enclosingScope = getEnclosingScope(descriptor); - return isEcma5() - ? enclosingScope.declareUnobfuscatableName(accessorName) - : enclosingScope.declareObfuscatableName(accessorName); + String accessorName = Namer.getNameForAccessor(propertyName, isGetter, + !accessorDescriptor.getReceiverParameter().exists() && isEcma5()); + return declareName(descriptor, accessorName); } }; @@ -232,19 +258,12 @@ public final class StaticContext { return null; } - //TODO: move somewhere - NamingScope enclosingScope = getEnclosingScope(descriptor); - if (isEcma5()) { - String name = descriptor.getName().getName(); - if (JsDescriptorUtils.isAsPrivate((PropertyDescriptor) descriptor)) { - name = '_' + name; - } + String name = descriptor.getName().getName(); + if (!isEcma5() || JsDescriptorUtils.isAsPrivate((PropertyDescriptor) descriptor)) { + name = Namer.getKotlinBackingFieldName(name); + } - return enclosingScope.declareUnobfuscatableName(name); - } - else { - return enclosingScope.declareObfuscatableName(Namer.getKotlinBackingFieldName(descriptor.getName().getName())); - } + return declareName(descriptor, name); } }; //TODO: hack! @@ -386,14 +405,50 @@ public final class StaticContext { Rule namespaceLevelDeclarationsHaveEnclosingNamespacesNamesAsQualifier = new Rule() { @Override public JsNameRef apply(@NotNull DeclarationDescriptor descriptor) { - DeclarationDescriptor containingDeclaration = getContainingDeclaration(descriptor); - if (!(containingDeclaration instanceof NamespaceDescriptor)) { + DeclarationDescriptor containingDescriptor = getContainingDeclaration(descriptor); + if (!(containingDescriptor instanceof NamespaceDescriptor)) { return null; } - JsName containingDeclarationName = getNameForDescriptor(containingDeclaration); - JsNameRef qualifier = containingDeclarationName.makeRef(); - qualifier.setQualifier(getQualifierForDescriptor(containingDeclaration)); - return qualifier; + + final JsNameRef result = new JsNameRef(getNameForDescriptor(containingDescriptor)); + if (DescriptorUtils.isRootNamespace((NamespaceDescriptor) containingDescriptor)) { + return result; + } + + JsNameRef qualifier = result; + while ((containingDescriptor = getContainingDeclaration(containingDescriptor)) instanceof NamespaceDescriptor && + !DescriptorUtils.isRootNamespace((NamespaceDescriptor) containingDescriptor)) { + JsNameRef ref = getNameForDescriptor(containingDescriptor).makeRef(); + qualifier.setQualifier(ref); + qualifier = ref; + } + + PsiElement element = BindingContextUtils.descriptorToDeclaration(bindingContext, descriptor); + if (element == null && descriptor instanceof PropertyAccessorDescriptor) { + element = BindingContextUtils.descriptorToDeclaration(bindingContext, ((PropertyAccessorDescriptor) descriptor) + .getCorrespondingProperty()); + } + + if (element != null) { + PsiFile file = element.getContainingFile(); + String moduleName = file.getUserData(LibrarySourcesConfig.EXTERNAL_MODULE_NAME); + if (LibrarySourcesConfig.UNKNOWN_EXTERNAL_MODULE_NAME.equals(moduleName)) { + return null; + } + else if (moduleName != null) { + qualifier.setQualifier(new JsArrayAccess(namer.kotlin("modules"), program.getStringLiteral(moduleName))); + } + else if (result == qualifier && result.getIdent().equals("kotlin")) { + // todo WebDemoExamples2Test#testBuilder, package "kotlin" from kotlin/js/js.libraries/src/stdlib/JUMaps.kt must be inlined + return qualifier; + } + } + + if (qualifier.getQualifier() == null) { + qualifier.setQualifier(new JsNameRef(Namer.getRootNamespaceName())); + } + + return result; } }; Rule constructorHaveTheSameQualifierAsTheClass = new Rule() { @@ -449,10 +504,7 @@ public final class StaticContext { Rule topLevelNamespaceHaveNoQualifier = new Rule() { @Override public Boolean apply(@NotNull DeclarationDescriptor descriptor) { - if (!(descriptor instanceof NamespaceDescriptor)) { - return null; - } - if (DescriptorUtils.isTopLevelNamespace((NamespaceDescriptor) descriptor)) { + if (descriptor instanceof NamespaceDescriptor && DescriptorUtils.isRootNamespace((NamespaceDescriptor) descriptor)) { return true; } return null; diff --git a/js/js.translator/src/org/jetbrains/k2js/translate/context/TranslationContext.java b/js/js.translator/src/org/jetbrains/k2js/translate/context/TranslationContext.java index a55a17687e9..26e8f33ec4c 100644 --- a/js/js.translator/src/org/jetbrains/k2js/translate/context/TranslationContext.java +++ b/js/js.translator/src/org/jetbrains/k2js/translate/context/TranslationContext.java @@ -24,6 +24,7 @@ import org.jetbrains.jet.lang.descriptors.CallableDescriptor; import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor; import org.jetbrains.jet.lang.psi.JetExpression; import org.jetbrains.jet.lang.resolve.BindingContext; +import org.jetbrains.k2js.config.EcmaVersion; import org.jetbrains.k2js.translate.intrinsic.Intrinsics; import java.util.Map; @@ -58,6 +59,10 @@ public final class TranslationContext { return staticContext.isEcma5(); } + public boolean isNotEcma3() { + return staticContext.getEcmaVersion() != EcmaVersion.v3; + } + private TranslationContext(@NotNull StaticContext staticContext, @NotNull DynamicContext dynamicContext, @NotNull AliasingContext context) { @@ -152,6 +157,11 @@ public final class TranslationContext { return dynamicContext.declareTemporary(initExpression); } + @NotNull + public TemporaryVariable declareTemporary(@NotNull JsExpression initExpression, boolean initialize) { + return dynamicContext.declareTemporary(initExpression, initialize); + } + @NotNull public Namer namer() { return staticContext.getNamer(); diff --git a/js/js.translator/src/org/jetbrains/k2js/translate/declaration/ClassAliasingMap.java b/js/js.translator/src/org/jetbrains/k2js/translate/declaration/ClassAliasingMap.java new file mode 100644 index 00000000000..c7312044a4b --- /dev/null +++ b/js/js.translator/src/org/jetbrains/k2js/translate/declaration/ClassAliasingMap.java @@ -0,0 +1,10 @@ +package org.jetbrains.k2js.translate.declaration; + +import com.google.dart.compiler.backend.js.ast.JsNameRef; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.jet.lang.psi.JetClass; + +public interface ClassAliasingMap { + @Nullable + JsNameRef get(JetClass declaration, JetClass referencedDeclaration); +} diff --git a/js/js.translator/src/org/jetbrains/k2js/translate/declaration/ClassDeclarationTranslator.java b/js/js.translator/src/org/jetbrains/k2js/translate/declaration/ClassDeclarationTranslator.java index 5c90678041e..f00caac0bd9 100644 --- a/js/js.translator/src/org/jetbrains/k2js/translate/declaration/ClassDeclarationTranslator.java +++ b/js/js.translator/src/org/jetbrains/k2js/translate/declaration/ClassDeclarationTranslator.java @@ -16,28 +16,29 @@ package org.jetbrains.k2js.translate.declaration; -import com.google.common.collect.BiMap; -import com.google.common.collect.HashBiMap; -import com.google.common.collect.Lists; import com.google.dart.compiler.backend.js.ast.*; import com.google.dart.compiler.util.AstUtil; +import gnu.trove.THashMap; +import gnu.trove.TLinkable; +import gnu.trove.TLinkableAdaptor; +import gnu.trove.TLinkedList; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.jet.lang.descriptors.ClassDescriptor; -import org.jetbrains.jet.lang.descriptors.NamespaceDescriptor; +import org.jetbrains.jet.lang.descriptors.Modality; import org.jetbrains.jet.lang.psi.JetClass; import org.jetbrains.k2js.translate.context.Namer; import org.jetbrains.k2js.translate.context.TranslationContext; import org.jetbrains.k2js.translate.general.AbstractTranslator; -import org.jetbrains.k2js.translate.general.Translation; -import org.jetbrains.k2js.translate.utils.BindingUtils; -import org.jetbrains.k2js.translate.utils.ClassSortingUtils; +import org.jetbrains.k2js.translate.utils.JsAstUtils; import java.util.ArrayList; import java.util.List; -import static org.jetbrains.k2js.translate.utils.JsAstUtils.*; -import static org.jetbrains.k2js.translate.utils.JsDescriptorUtils.getAllClassesDefinedInNamespace; +import static com.google.dart.compiler.backend.js.ast.JsVars.JsVar; +import static org.jetbrains.k2js.translate.general.Translation.translateClassDeclaration; +import static org.jetbrains.k2js.translate.utils.BindingUtils.getClassDescriptor; +import static org.jetbrains.k2js.translate.utils.JsAstUtils.newBlock; /** * @author Pavel Talanov @@ -45,115 +46,170 @@ import static org.jetbrains.k2js.translate.utils.JsDescriptorUtils.getAllClasses * Generates a big block where are all the classes(objects representing them) are created. */ public final class ClassDeclarationTranslator extends AbstractTranslator { + private int localNameCounter; @NotNull - private final List descriptors; - @NotNull - private final BiMap localToGlobalClassName; + private final THashMap openClassToItem = new THashMap(); + + private final TLinkedList openList = new TLinkedList(); + private final List finalList = new ArrayList(); + @NotNull private final JsFunction dummyFunction; - @Nullable - private JsName declarationsObject = null; - @Nullable - private JsStatement declarationsStatement = null; - public ClassDeclarationTranslator(@NotNull List descriptors, - @NotNull TranslationContext context) { + private final JsName declarationsObject; + private final JsVar classesVar; + + public ClassDeclarationTranslator(@NotNull TranslationContext context) { super(context); - this.descriptors = descriptors; - this.localToGlobalClassName = HashBiMap.create(); - this.dummyFunction = new JsFunction(context.jsScope()); + + dummyFunction = new JsFunction(context.jsScope()); + declarationsObject = context().jsScope().declareName(Namer.nameForClassesVariable()); + classesVar = new JsVars.JsVar(declarationsObject); + } + + private final class OpenClassRefProvider implements ClassAliasingMap { + @Override + @Nullable + public JsNameRef get(JetClass declaration, JetClass referencedDeclaration) { + ListItem item = openClassToItem.get(declaration); + // class declared in library + if (item == null) { + return null; + } + + addAfter(item, openClassToItem.get(referencedDeclaration)); + return item.label; + } + + private void addAfter(@NotNull ListItem item, @NotNull ListItem referencedItem) { + for (TLinkable link = item.getNext(); link != null; link = link.getNext()) { + if (link == referencedItem) { + return; + } + } + + openList.remove(referencedItem); + openList.addBefore((ListItem) item.getNext(), referencedItem); + } + } + + private final class FinalClassRefProvider implements ClassAliasingMap { + @Override + public JsNameRef get(JetClass declaration, JetClass referencedDeclaration) { + ListItem item = openClassToItem.get(declaration); + return item == null ? null : item.label; + } + } + + private static class ListItem extends TLinkableAdaptor { + private final JetClass declaration; + private final JsNameRef label; + + private JsExpression translatedDeclaration; + + private ListItem(JetClass declaration, JsNameRef label) { + this.declaration = declaration; + this.label = label; + } + } + + @NotNull + public JsVars getDeclarationsStatement() { + JsVars vars = new JsVars(); + vars.add(classesVar); + return vars; } public void generateDeclarations() { - declarationsObject = context().jsScope().declareName(Namer.nameForClassesVariable()); - assert declarationsObject != null; - declarationsStatement = - newVar(declarationsObject, generateDummyFunctionInvocation()); - } + JsObjectLiteral valueLiteral = new JsObjectLiteral(); + JsVars vars = new JsVars(); + List propertyInitializers = valueLiteral.getPropertyInitializers(); - @NotNull - public JsName getDeclarationsObjectName() { - assert declarationsObject != null : "Should run generateDeclarations first"; - return declarationsObject; - } + generateOpenClassDeclarations(vars, propertyInitializers); + generateFinalClassDeclarations(vars, propertyInitializers); - @NotNull - public JsStatement getDeclarationsStatement() { - assert declarationsStatement != null : "Should run generateDeclarations first"; - return declarationsStatement; - } - - @NotNull - private JsInvocation generateDummyFunctionInvocation() { - List classDeclarations = generateClassDeclarationStatements(); - classDeclarations.add(new JsReturn(generateReturnedObjectLiteral())); - dummyFunction.setBody(newBlock(classDeclarations)); - return AstUtil.newInvocation(dummyFunction); - } - - @NotNull - private JsObjectLiteral generateReturnedObjectLiteral() { - JsObjectLiteral returnedValueLiteral = new JsObjectLiteral(); - for (JsName localName : localToGlobalClassName.keySet()) { - returnedValueLiteral.getPropertyInitializers().add(classEntry(localName)); + if (vars.isEmpty()) { + classesVar.setInitExpr(valueLiteral); + return; } - return returnedValueLiteral; + + List result = new ArrayList(); + result.add(vars); + result.add(new JsReturn(valueLiteral)); + dummyFunction.setBody(newBlock(result)); + classesVar.setInitExpr(AstUtil.newInvocation(dummyFunction)); } - @NotNull - private JsPropertyInitializer classEntry(@NotNull JsName localName) { - return new JsPropertyInitializer(localToGlobalClassName.get(localName).makeRef(), localName.makeRef()); - } - - @NotNull - private List generateClassDeclarationStatements() { - List classDeclarations = new ArrayList(); - for (JetClass jetClass : getClassDeclarations()) { - classDeclarations.add(generateDeclaration(jetClass)); + private void generateOpenClassDeclarations(@NotNull JsVars vars, @NotNull List propertyInitializers) { + ClassAliasingMap classAliasingMap = new OpenClassRefProvider(); + // first pass: set up list order + for (ListItem item : openList) { + item.translatedDeclaration = translateClassDeclaration(item.declaration, classAliasingMap, context()); } - return classDeclarations; - } - - - @NotNull - private List getClassDeclarations() { - List classes = new ArrayList(); - for (ClassDescriptor classDescriptor : descriptors) { - classes.add(BindingUtils.getClassForDescriptor(bindingContext(), classDescriptor)); + // second pass: generate + for (ListItem item : openList) { + generate(item, propertyInitializers, item.translatedDeclaration, vars); } - return ClassSortingUtils.sortUsingInheritanceOrder(classes, bindingContext()); } - @NotNull - private JsStatement generateDeclaration(@NotNull JetClass declaration) { - JsName localClassName = generateLocalAlias(declaration); - JsExpression classDeclarationExpression = - Translation.translateClassDeclaration(declaration, localToGlobalClassName.inverse(), context()); - return newVar(localClassName, classDeclarationExpression); - } - - @NotNull - private JsName generateLocalAlias(@NotNull JetClass declaration) { - JsName globalClassName = context().getNameForElement(declaration); - JsName localAlias = dummyFunction.getScope().declareTemporary(); - localToGlobalClassName.put(localAlias, globalClassName); - return localAlias; - } - - @NotNull - public List classDeclarationsForNamespace(@NotNull NamespaceDescriptor namespaceDescriptor) { - List result = Lists.newArrayList(); - for (ClassDescriptor classDescriptor : getAllClassesDefinedInNamespace(namespaceDescriptor)) { - result.add(getClassNameToClassObject(classDescriptor)); + private void generateFinalClassDeclarations(@NotNull JsVars vars, @NotNull List propertyInitializers) { + ClassAliasingMap classAliasingMap = new FinalClassRefProvider(); + for (ListItem item : finalList) { + generate(item, propertyInitializers, translateClassDeclaration(item.declaration, classAliasingMap, context()), vars); } - return result; + } + + private static void generate(@NotNull ListItem item, + @NotNull List propertyInitializers, + @NotNull JsExpression definition, + @NotNull JsVars vars) { + JsExpression value; + if (item.label.getName() == null) { + value = definition; + } + else { + assert item.label.getName() != null; + vars.add(new JsVar(item.label.getName(), definition)); + value = item.label; + } + + propertyInitializers.add(new JsPropertyInitializer(item.label, value)); } @NotNull - private JsPropertyInitializer getClassNameToClassObject(@NotNull ClassDescriptor classDescriptor) { - JsName className = context().getNameForDescriptor(classDescriptor); - JsNameRef alreadyDefinedClassReference = qualified(className, getDeclarationsObjectName().makeRef()); - return new JsPropertyInitializer(className.makeRef(), alreadyDefinedClassReference); + public JsPropertyInitializer translateAndGetClassNameToClassObject(@NotNull JetClass declaration) { + ClassDescriptor descriptor = getClassDescriptor(context().bindingContext(), declaration); + + JsNameRef labelRef; + String label = 'c' + Integer.toString(localNameCounter++, 36); + boolean isFinal = descriptor.getModality() == Modality.FINAL; + if (isFinal) { + labelRef = new JsNameRef(label); + } + else { + labelRef = dummyFunction.getScope().declareName(label).makeRef(); + } + + ListItem item = new ListItem(declaration, labelRef); + if (isFinal) { + finalList.add(item); + } + else { + openList.add(item); + openClassToItem.put(declaration, item); + } + + JsNameRef qualifiedLabelRef = new JsNameRef(labelRef.getIdent()); + qualifiedLabelRef.setQualifier(declarationsObject.makeRef()); + JsExpression value; + if (context().isEcma5()) { + value = JsAstUtils.createDataDescriptor(qualifiedLabelRef, false, context()); + } + else { + value = qualifiedLabelRef; + } + + return new JsPropertyInitializer(context().program().getStringLiteral(descriptor.getName().getName()), value); } } diff --git a/js/js.translator/src/org/jetbrains/k2js/translate/declaration/ClassTranslator.java b/js/js.translator/src/org/jetbrains/k2js/translate/declaration/ClassTranslator.java index c5ab497e195..0b3167d85af 100644 --- a/js/js.translator/src/org/jetbrains/k2js/translate/declaration/ClassTranslator.java +++ b/js/js.translator/src/org/jetbrains/k2js/translate/declaration/ClassTranslator.java @@ -23,6 +23,7 @@ import org.jetbrains.annotations.Nullable; import org.jetbrains.jet.lang.descriptors.ClassDescriptor; import org.jetbrains.jet.lang.descriptors.ClassKind; import org.jetbrains.jet.lang.descriptors.PropertyDescriptor; +import org.jetbrains.jet.lang.psi.JetClass; import org.jetbrains.jet.lang.psi.JetClassOrObject; import org.jetbrains.jet.lang.psi.JetObjectLiteralExpression; import org.jetbrains.jet.lang.psi.JetParameter; @@ -31,12 +32,11 @@ import org.jetbrains.k2js.translate.context.TranslationContext; import org.jetbrains.k2js.translate.general.AbstractTranslator; import org.jetbrains.k2js.translate.general.Translation; import org.jetbrains.k2js.translate.initializer.InitializerUtils; +import org.jetbrains.k2js.translate.utils.BindingUtils; import org.jetbrains.k2js.translate.utils.JsAstUtils; import java.util.ArrayList; -import java.util.Collections; import java.util.List; -import java.util.Map; import static com.google.dart.compiler.util.AstUtil.newSequence; import static org.jetbrains.k2js.translate.utils.BindingUtils.getClassDescriptor; @@ -64,8 +64,8 @@ public final class ClassTranslator extends AbstractTranslator { @NotNull public static JsExpression generateClassCreationExpression(@NotNull JetClassOrObject classDeclaration, - @NotNull Map aliasingMap, - @NotNull TranslationContext context) { + @NotNull ClassAliasingMap aliasingMap, + @NotNull TranslationContext context) { return (new ClassTranslator(classDeclaration, aliasingMap, context)).translateClassOrObjectCreation(); } @@ -73,13 +73,13 @@ public final class ClassTranslator extends AbstractTranslator { public static JsExpression generateClassCreationExpression(@NotNull JetClassOrObject classDeclaration, @NotNull TranslationContext context) { - return (new ClassTranslator(classDeclaration, Collections.emptyMap(), context)).translateClassOrObjectCreation(); + return (new ClassTranslator(classDeclaration, null, context)).translateClassOrObjectCreation(); } @NotNull public static JsExpression generateObjectLiteralExpression(@NotNull JetObjectLiteralExpression objectLiteralExpression, @NotNull TranslationContext context) { - return (new ClassTranslator(objectLiteralExpression.getObjectDeclaration(), Collections.emptyMap(), context)) + return (new ClassTranslator(objectLiteralExpression.getObjectDeclaration(), null, context)) .translateObjectLiteralExpression(); } @@ -95,12 +95,12 @@ public final class ClassTranslator extends AbstractTranslator { @NotNull private final ClassDescriptor descriptor; - @NotNull - private final Map aliasingMap; + @Nullable + private final ClassAliasingMap aliasingMap; private ClassTranslator(@NotNull JetClassOrObject classDeclaration, - @NotNull Map aliasingMap, - @NotNull TranslationContext context) { + @Nullable ClassAliasingMap aliasingMap, + @NotNull TranslationContext context) { super(context.newDeclaration(classDeclaration)); this.aliasingMap = aliasingMap; this.descriptor = getClassDescriptor(context.bindingContext(), classDeclaration); @@ -160,7 +160,7 @@ public final class ClassTranslator extends AbstractTranslator { if (!isTrait()) { JsFunction initializer = Translation.generateClassInitializerMethod(classDeclaration, classDeclarationContext); if (context().isEcma5()) { - jsClassDeclaration.getArguments().add(isObject() ? initializer : JsAstUtils.encloseFunction(initializer)); + jsClassDeclaration.getArguments().add(initializer.getName() == null ? initializer : JsAstUtils.encloseFunction(initializer)); } else { propertyList.add(InitializerUtils.generateInitializeMethod(initializer)); @@ -209,8 +209,7 @@ public final class ClassTranslator extends AbstractTranslator { private void addTraits(@NotNull List superclassReferences, @NotNull List superclassDescriptors) { - for (ClassDescriptor superClassDescriptor : - superclassDescriptors) { + for (ClassDescriptor superClassDescriptor : superclassDescriptors) { assert (superClassDescriptor.getKind() == ClassKind.TRAIT) : "Only traits are expected here"; superclassReferences.add(getClassReference(superClassDescriptor)); } @@ -227,12 +226,16 @@ public final class ClassTranslator extends AbstractTranslator { @NotNull private JsExpression getClassReference(@NotNull ClassDescriptor superClassDescriptor) { - //NOTE: aliasing here is needed for the declaration generation step - JsName name = context().getNameForDescriptor(superClassDescriptor); - JsName alias = aliasingMap.get(name); - if (alias != null) { - return alias.makeRef(); + // aliasing here is needed for the declaration generation step + if (aliasingMap != null) { + JsNameRef name = aliasingMap.get(BindingUtils.getClassForDescriptor(bindingContext(), superClassDescriptor), + (JetClass) classDeclaration); + if (name != null) { + return name; + } } + + // from library return getQualifiedReference(context(), superClassDescriptor); } diff --git a/js/js.translator/src/org/jetbrains/k2js/translate/declaration/DeclarationBodyVisitor.java b/js/js.translator/src/org/jetbrains/k2js/translate/declaration/DeclarationBodyVisitor.java index 683b7427ce1..c7e533db00e 100644 --- a/js/js.translator/src/org/jetbrains/k2js/translate/declaration/DeclarationBodyVisitor.java +++ b/js/js.translator/src/org/jetbrains/k2js/translate/declaration/DeclarationBodyVisitor.java @@ -19,6 +19,7 @@ package org.jetbrains.k2js.translate.declaration; import com.google.dart.compiler.backend.js.ast.JsExpression; import com.google.dart.compiler.backend.js.ast.JsPropertyInitializer; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import org.jetbrains.jet.lang.descriptors.FunctionDescriptor; import org.jetbrains.jet.lang.descriptors.NamespaceDescriptor; import org.jetbrains.jet.lang.descriptors.PropertyDescriptor; @@ -39,6 +40,17 @@ import static org.jetbrains.k2js.translate.utils.BindingUtils.*; * @author Pavel Talanov */ public final class DeclarationBodyVisitor extends TranslatorVisitor> { + @Nullable + private final ClassDeclarationTranslator classDeclarationTranslator; + + public DeclarationBodyVisitor() { + classDeclarationTranslator = null; + } + + public DeclarationBodyVisitor(ClassDeclarationTranslator classDeclarationTranslator) { + this.classDeclarationTranslator = classDeclarationTranslator; + } + @NotNull public List traverseClass(@NotNull JetClassOrObject jetClass, @NotNull TranslationContext context) { @@ -51,7 +63,7 @@ public final class DeclarationBodyVisitor extends TranslatorVisitor traverseNamespace(@NotNull NamespaceDescriptor namespace, - @NotNull TranslationContext context) { + @NotNull TranslationContext context) { List properties = new ArrayList(); for (JetDeclaration declaration : getDeclarationsForNamespace(context.bindingContext(), namespace)) { properties.addAll(declaration.accept(this, context)); @@ -62,7 +74,11 @@ public final class DeclarationBodyVisitor extends TranslatorVisitor visitClass(@NotNull JetClass expression, @NotNull TranslationContext context) { - return Collections.emptyList(); + if (classDeclarationTranslator == null) { + return Collections.emptyList(); + } + + return Collections.singletonList(classDeclarationTranslator.translateAndGetClassNameToClassObject(expression)); } @Override @@ -71,10 +87,9 @@ public final class DeclarationBodyVisitor extends TranslatorVisitor getAllClasses() { - List result = Lists.newArrayList(); - for (NamespaceDescriptor namespaceDescriptor : namespaceDescriptors) { - result.addAll(getAllClassesDefinedInNamespace(namespaceDescriptor)); - } - return result; + classDeclarationTranslator = new ClassDeclarationTranslator(context); } @NotNull private List translate() { - List result = classesDeclarations(); - result.addAll(namespacesDeclarations()); - return result; - } - - @NotNull - private List classesDeclarations() { - List result = Lists.newArrayList(); - classDeclarationTranslator.generateDeclarations(); + List result = new ArrayList(); result.add(classDeclarationTranslator.getDeclarationsStatement()); + namespacesDeclarations(result); + classDeclarationTranslator.generateDeclarations(); return result; } - @NotNull - private List namespacesDeclarations() { - List result = Lists.newArrayList(); + private void namespacesDeclarations(List statements) { List namespaceTranslators = getTranslatorsForNonEmptyNamespaces(); - result.addAll(declarationStatements(namespaceTranslators, context())); - result.addAll(initializeStatements(namespaceTranslators)); - return result; + declarationStatements(namespaceTranslators, statements); + initializeStatements(namespaceTranslators, statements); } @NotNull @@ -102,34 +83,31 @@ public final class NamespaceDeclarationTranslator extends AbstractTranslator { return namespaceTranslators; } - @NotNull - private static List declarationStatements(@NotNull List namespaceTranslators, TranslationContext context) { - List result = Lists.newArrayList(); - - JsNameRef defs = JsAstUtils.qualified(context.jsScope().declareName("defs"), context.namer().kotlinObject()); - for (NamespaceTranslator translator : namespaceTranslators) { - JsVars vars = translator.getDeclarationAsVar(); - - JsVars.JsVar var = vars.iterator().next(); - JsNameRef ref = new JsNameRef(var.getName()); - ref.setQualifier(defs); - - result.add(vars); - result.add(JsAstUtils.assignment(ref, new JsNameRef(var.getName())).makeStmt()); + private void declarationStatements(@NotNull List namespaceTranslators, + @NotNull List statements) { + JsObjectLiteral objectLiteral = new JsObjectLiteral(); + JsNameRef packageMapNameRef = context().jsScope().declareName("_").makeRef(); + JsExpression packageMapValue; + if (context().isNotEcma3()) { + packageMapValue = AstUtil.newInvocation(JsAstUtils.CREATE_OBJECT, context().program().getNullLiteral(), objectLiteral); + } + else { + packageMapValue = objectLiteral; + } + statements.add(JsAstUtils.newVar(packageMapNameRef.getName(), packageMapValue)); + + for (NamespaceTranslator translator : namespaceTranslators) { + translator.addNamespaceDeclaration(objectLiteral.getPropertyInitializers()); } - return result; } - @NotNull - private List initializeStatements(@NotNull List namespaceTranslators) { - List result = Lists.newArrayList(); - for (NamespaceDescriptor descriptor : filterNonEmptyNamespaces(namespaceDescriptors)) { - JsNameRef initializeMethodReference = Namer.initializeMethodReference(); - JsNameRef fqNamespaceNameRef = TranslationUtils.getQualifiedReference(context(), descriptor); - setQualifier(initializeMethodReference, fqNamespaceNameRef); - result.add(AstUtil.newInvocation(initializeMethodReference).makeStmt()); + private static void initializeStatements(@NotNull List namespaceTranslators, + @NotNull List statements) { + for (NamespaceTranslator translator : namespaceTranslators) { + for (JsExpression expression : translator.getInitializers()) { + statements.add(expression.makeStmt()); + } } - return result; } @NotNull @@ -144,10 +122,10 @@ public final class NamespaceDeclarationTranslator extends AbstractTranslator { } @NotNull - private static List filterNonEmptyNamespaces(@NotNull List namespaceDescriptors) { + private List filterNonEmptyNamespaces(@NotNull List namespaceDescriptors) { List result = Lists.newArrayList(); for (NamespaceDescriptor descriptor : namespaceDescriptors) { - if (!JsDescriptorUtils.isNamespaceEmpty(descriptor)) { + if (!JsDescriptorUtils.isNamespaceEmpty(descriptor, context().bindingContext())) { result.add(descriptor); } } diff --git a/js/js.translator/src/org/jetbrains/k2js/translate/declaration/NamespaceTranslator.java b/js/js.translator/src/org/jetbrains/k2js/translate/declaration/NamespaceTranslator.java index 3149932a5ec..6a92dd3340b 100644 --- a/js/js.translator/src/org/jetbrains/k2js/translate/declaration/NamespaceTranslator.java +++ b/js/js.translator/src/org/jetbrains/k2js/translate/declaration/NamespaceTranslator.java @@ -25,14 +25,14 @@ import org.jetbrains.jet.lang.resolve.DescriptorUtils; import org.jetbrains.k2js.translate.context.TranslationContext; import org.jetbrains.k2js.translate.general.AbstractTranslator; import org.jetbrains.k2js.translate.general.Translation; -import org.jetbrains.k2js.translate.initializer.InitializerUtils; +import org.jetbrains.k2js.translate.utils.JsAstUtils; import org.jetbrains.k2js.translate.utils.JsDescriptorUtils; +import org.jetbrains.k2js.translate.utils.TranslationUtils; import java.util.ArrayList; import java.util.List; import static org.jetbrains.k2js.translate.utils.JsAstUtils.newObjectLiteral; -import static org.jetbrains.k2js.translate.utils.JsAstUtils.newVar; /** * @author Pavel.Talanov @@ -48,6 +48,9 @@ public final class NamespaceTranslator extends AbstractTranslator { @NotNull private final ClassDeclarationTranslator classDeclarationTranslator; + @NotNull + private final List initializers = new ArrayList(); + /*package*/ NamespaceTranslator(@NotNull NamespaceDescriptor descriptor, @NotNull ClassDeclarationTranslator classDeclarationTranslator, @NotNull TranslationContext context) { @@ -57,82 +60,81 @@ public final class NamespaceTranslator extends AbstractTranslator { this.classDeclarationTranslator = classDeclarationTranslator; } - @NotNull - public JsVars getDeclarationAsVar() { - return newVar(namespaceName, getNamespaceDeclaration()); + public List getInitializers() { + return initializers; } @NotNull public JsPropertyInitializer getDeclarationAsInitializer() { + addNamespaceInitializer(); return new JsPropertyInitializer(namespaceName.makeRef(), getNamespaceDeclaration()); } + public void addNamespaceDeclaration(List list) { + addNamespaceInitializer(); + + if (DescriptorUtils.isRootNamespace(descriptor)) { + list.addAll(getFunctionsAndClasses()); + return; + } + + JsExpression value = getNamespaceDeclaration(); + if (context().isNotEcma3()) { + value = JsAstUtils.createDataDescriptor(value, false, context()); + } + + list.add(new JsPropertyInitializer(namespaceName.makeRef(), value)); + } + @NotNull private JsInvocation getNamespaceDeclaration() { JsInvocation namespaceDeclaration = namespaceCreateMethodInvocation(); - addNamespaceInitalizersAndProperties(namespaceDeclaration); - namespaceDeclaration.getArguments().add(getClassesAndNestedNamespaces()); + addIfNeed(getFunctionsAndClasses(), namespaceDeclaration.getArguments()); + addIfNeed(getNestedNamespaceDeclarations(), namespaceDeclaration.getArguments()); return namespaceDeclaration; } - private void addNamespaceInitalizersAndProperties(@NotNull JsInvocation namespaceDeclaration) { + private void addNamespaceInitializer() { JsFunction initializer = Translation.generateNamespaceInitializerMethod(descriptor, context()); - List properties = new DeclarationBodyVisitor().traverseNamespace(descriptor, context()); - if (context().isEcma5()) { - addEcma5InitializersAndProperties(namespaceDeclaration, initializer, properties); - } - else { - addEcma3InitializersAndProperties(namespaceDeclaration, initializer, properties); + if (!initializer.getBody().getStatements().isEmpty()) { + JsNameRef call = new JsNameRef("call"); + call.setQualifier(initializer); + JsInvocation invocation = new JsInvocation(); + invocation.setQualifier(call); + invocation.getArguments().add(TranslationUtils.getQualifiedReference(context(), descriptor)); + initializers.add(invocation); } } - private static void addEcma3InitializersAndProperties(@NotNull JsInvocation namespaceDeclaration, - @NotNull JsFunction initializer, - @NotNull List properties) { - List propertyList = new ArrayList(); - propertyList.add(InitializerUtils.generateInitializeMethod(initializer)); - propertyList.addAll(properties); - namespaceDeclaration.getArguments().add(newObjectLiteral(propertyList)); - } - - private static void addEcma5InitializersAndProperties(@NotNull JsInvocation namespaceDeclaration, - @NotNull JsFunction initializer, - @NotNull List properties) { - namespaceDeclaration.getArguments().add(initializer); - namespaceDeclaration.getArguments().add(newObjectLiteral(properties)); + private List getFunctionsAndClasses() { + return new DeclarationBodyVisitor(classDeclarationTranslator).traverseNamespace(descriptor, context()); } @NotNull private JsInvocation namespaceCreateMethodInvocation() { - return AstUtil.newInvocation(context().namer().namespaceCreationMethodReference()); + return AstUtil.newInvocation(context().namer().packageDefinitionMethodReference()); } - @NotNull - private JsObjectLiteral getClassesAndNestedNamespaces() { - JsObjectLiteral classesAndNestedNamespaces = new JsObjectLiteral(); - classesAndNestedNamespaces.getPropertyInitializers() - .addAll(getClassesDefined()); - classesAndNestedNamespaces.getPropertyInitializers() - .addAll(getNestedNamespaceDeclarations()); - return classesAndNestedNamespaces; - } - - @NotNull - private List getClassesDefined() { - return classDeclarationTranslator.classDeclarationsForNamespace(descriptor); + private void addIfNeed(@NotNull List declarations, @NotNull List expressions) { + // ecma5 expects strict number of arguments, but ecma3 doesn't + if (!declarations.isEmpty()) { + expressions.add(newObjectLiteral(declarations)); + } + else if (context().isNotEcma3()) { + expressions.add(context().program().getNullLiteral()); + } } @NotNull private List getNestedNamespaceDeclarations() { - if (DescriptorUtils.isRootNamespace(descriptor)) { - return Lists.newArrayList(); - } List result = Lists.newArrayList(); - List nestedNamespaces = JsDescriptorUtils.getNestedNamespaces(descriptor); + List nestedNamespaces = JsDescriptorUtils.getNestedNamespaces(descriptor, context().bindingContext()); for (NamespaceDescriptor nestedNamespace : nestedNamespaces) { NamespaceTranslator nestedNamespaceTranslator = new NamespaceTranslator(nestedNamespace, classDeclarationTranslator, context()); result.add(nestedNamespaceTranslator.getDeclarationAsInitializer()); + + initializers.addAll(nestedNamespaceTranslator.getInitializers()); } return result; } diff --git a/js/js.translator/src/org/jetbrains/k2js/translate/expression/WhenTranslator.java b/js/js.translator/src/org/jetbrains/k2js/translate/expression/WhenTranslator.java index 3ae42af63d9..06c016582a7 100644 --- a/js/js.translator/src/org/jetbrains/k2js/translate/expression/WhenTranslator.java +++ b/js/js.translator/src/org/jetbrains/k2js/translate/expression/WhenTranslator.java @@ -116,12 +116,11 @@ public final class WhenTranslator extends AbstractTranslator { return statementToExecute; } JsExpression condition = translateConditions(entry); - return new JsIf(condition, addDummyBreak(statementToExecute), null); + return new JsIf(condition, addDummyBreakIfNeed(statementToExecute), null); } @NotNull JsStatement withReturnValueCaptured(@NotNull JsNode node) { - return convertToStatement(LastExpressionMutator.mutateLastExpression(node, new AssignToExpressionMutator(result.reference()))); } @@ -172,8 +171,8 @@ public final class WhenTranslator extends AbstractTranslator { } @NotNull - private static JsBlock addDummyBreak(@NotNull JsStatement statement) { - return AstUtil.newBlock(statement, new JsBreak()); + private static JsStatement addDummyBreakIfNeed(@NotNull JsStatement statement) { + return statement instanceof JsReturn ? statement : AstUtil.newBlock(statement, new JsBreak()); } @NotNull diff --git a/js/js.translator/src/org/jetbrains/k2js/translate/expression/foreach/IteratorForTranslator.java b/js/js.translator/src/org/jetbrains/k2js/translate/expression/foreach/IteratorForTranslator.java index d6e28144f7c..8f70fdc5e87 100644 --- a/js/js.translator/src/org/jetbrains/k2js/translate/expression/foreach/IteratorForTranslator.java +++ b/js/js.translator/src/org/jetbrains/k2js/translate/expression/foreach/IteratorForTranslator.java @@ -31,9 +31,7 @@ import org.jetbrains.k2js.translate.context.TranslationContext; import org.jetbrains.k2js.translate.general.Translation; import org.jetbrains.k2js.translate.reference.CallBuilder; -import static org.jetbrains.k2js.translate.utils.BindingUtils.getHasNextCallable; -import static org.jetbrains.k2js.translate.utils.BindingUtils.getIteratorFunction; -import static org.jetbrains.k2js.translate.utils.BindingUtils.getNextFunction; +import static org.jetbrains.k2js.translate.utils.BindingUtils.*; import static org.jetbrains.k2js.translate.utils.JsAstUtils.convertToBlock; import static org.jetbrains.k2js.translate.utils.JsAstUtils.newVar; import static org.jetbrains.k2js.translate.utils.PsiUtils.getLoopBody; @@ -104,7 +102,7 @@ public final class IteratorForTranslator extends ForTranslator { // kotlin iterator define hasNext as property, but java util as function, our js side expects as property private static boolean isJavaUtilIterator(CallableDescriptor descriptor) { - final DeclarationDescriptor declaration = descriptor.getContainingDeclaration(); + DeclarationDescriptor declaration = descriptor.getContainingDeclaration(); return declaration != null && declaration.getName().getName().equals("Iterator"); } diff --git a/js/js.translator/src/org/jetbrains/k2js/translate/general/Translation.java b/js/js.translator/src/org/jetbrains/k2js/translate/general/Translation.java index f784f264771..ea77ec00559 100644 --- a/js/js.translator/src/org/jetbrains/k2js/translate/general/Translation.java +++ b/js/js.translator/src/org/jetbrains/k2js/translate/general/Translation.java @@ -19,8 +19,10 @@ package org.jetbrains.k2js.translate.general; import com.google.dart.compiler.backend.js.JsNamer; import com.google.dart.compiler.backend.js.JsPrettyNamer; import com.google.dart.compiler.backend.js.ast.*; +import com.google.dart.compiler.util.AstUtil; import com.intellij.openapi.util.text.StringUtil; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import org.jetbrains.jet.lang.descriptors.ClassDescriptor; import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor; import org.jetbrains.jet.lang.descriptors.FunctionDescriptor; @@ -28,7 +30,7 @@ import org.jetbrains.jet.lang.descriptors.NamespaceDescriptor; import org.jetbrains.jet.lang.psi.*; import org.jetbrains.jet.lang.resolve.BindingContext; import org.jetbrains.jet.lang.types.lang.JetStandardLibrary; -import org.jetbrains.k2js.config.EcmaVersion; +import org.jetbrains.k2js.config.Config; import org.jetbrains.k2js.facade.MainCallParameters; import org.jetbrains.k2js.facade.exceptions.MainFunctionNotFoundException; import org.jetbrains.k2js.facade.exceptions.TranslationException; @@ -36,6 +38,7 @@ import org.jetbrains.k2js.facade.exceptions.TranslationInternalException; import org.jetbrains.k2js.facade.exceptions.UnsupportedFeatureException; import org.jetbrains.k2js.translate.context.StaticContext; import org.jetbrains.k2js.translate.context.TranslationContext; +import org.jetbrains.k2js.translate.declaration.ClassAliasingMap; import org.jetbrains.k2js.translate.declaration.ClassTranslator; import org.jetbrains.k2js.translate.declaration.NamespaceDeclarationTranslator; import org.jetbrains.k2js.translate.expression.ExpressionVisitor; @@ -52,7 +55,6 @@ import org.jetbrains.k2js.translate.utils.dangerous.DangerousTranslator; import java.util.ArrayList; import java.util.Collections; import java.util.List; -import java.util.Map; import static org.jetbrains.jet.plugin.JetMainDetector.getMainFunction; import static org.jetbrains.k2js.translate.utils.BindingUtils.getFunctionDescriptor; @@ -83,9 +85,9 @@ public final class Translation { @NotNull public static JsExpression translateClassDeclaration(@NotNull JetClass classDeclaration, - @NotNull Map aliasingMap, + @NotNull ClassAliasingMap classAliasingMap, @NotNull TranslationContext context) { - return ClassTranslator.generateClassCreationExpression(classDeclaration, aliasingMap, context); + return ClassTranslator.generateClassCreationExpression(classDeclaration, classAliasingMap, context); } @NotNull @@ -148,10 +150,10 @@ public final class Translation { @NotNull public static JsProgram generateAst(@NotNull BindingContext bindingContext, @NotNull List files, @NotNull MainCallParameters mainCallParameters, - @NotNull EcmaVersion ecmaVersion, List rawStatements) + @NotNull Config config, List rawStatements) throws TranslationException { try { - return doGenerateAst(bindingContext, files, mainCallParameters, ecmaVersion, rawStatements); + return doGenerateAst(bindingContext, files, mainCallParameters, config, rawStatements); } catch (UnsupportedOperationException e) { throw new UnsupportedFeatureException("Unsupported feature used.", e); @@ -164,10 +166,10 @@ public final class Translation { @NotNull private static JsProgram doGenerateAst(@NotNull BindingContext bindingContext, @NotNull List files, @NotNull MainCallParameters mainCallParameters, - @NotNull EcmaVersion ecmaVersion, List rawStatements) throws MainFunctionNotFoundException { + @NotNull Config config, List rawStatements) throws MainFunctionNotFoundException { //TODO: move some of the code somewhere JetStandardLibrary standardLibrary = JetStandardLibrary.getInstance(); - StaticContext staticContext = StaticContext.generateStaticContext(standardLibrary, bindingContext, ecmaVersion); + StaticContext staticContext = StaticContext.generateStaticContext(standardLibrary, bindingContext, config.getTarget()); JsProgram program = staticContext.getProgram(); JsBlock block = program.getGlobalBlock(); @@ -177,8 +179,13 @@ public final class Translation { TranslationContext context = TranslationContext.rootContext(staticContext); statements.addAll(translateFiles(files, context)); + defineModule(statements, context, config); + if (mainCallParameters.shouldBeGenerated()) { - statements.add(generateCallToMain(context, files, mainCallParameters.arguments())); + JsStatement statement = generateCallToMain(context, files, mainCallParameters.arguments()); + if (statement != null) { + statements.add(statement); + } } generateTestCalls(context, files, block, rawStatements); JsNamer namer = new JsPrettyNamer(); @@ -186,12 +193,20 @@ public final class Translation { return context.program(); } - @NotNull + private static void defineModule(@NotNull List statements, + @NotNull TranslationContext context, + @NotNull Config config) { + statements.add(AstUtil.newInvocation(context.namer().kotlin("defineModule"), + context.program().getStringLiteral(config.getModuleId()), + context.jsScope().declareName("_").makeRef()).makeStmt()); + } + + @Nullable private static JsStatement generateCallToMain(@NotNull TranslationContext context, @NotNull List files, @NotNull List arguments) throws MainFunctionNotFoundException { JetNamedFunction mainFunction = getMainFunction(files); if (mainFunction == null) { - throw new MainFunctionNotFoundException("Main function was not found. Please check compiler arguments"); + return null; } JsInvocation translatedCall = generateInvocation(context, mainFunction); setArguments(context, arguments, translatedCall); @@ -214,12 +229,12 @@ public final class Translation { JsAstUtils.setArguments(translatedCall, Collections.singletonList(arrayLiteral)); } - @NotNull private static void generateTestCalls(@NotNull TranslationContext context, @NotNull List files, @NotNull JsBlock block, List rawStatements) { ClassDescriptor lastClassDescriptor = null; + boolean declaredVar = false; List functions = JetTestFunctionDetector.findTestFunctions(context.bindingContext(), files); for (JetNamedFunction function : functions) { FunctionDescriptor functionDescriptor = getFunctionDescriptor(context.bindingContext(), function); @@ -228,9 +243,16 @@ public final class Translation { if (containingDeclaration instanceof ClassDescriptor) { ClassDescriptor classDescriptor = (ClassDescriptor) containingDeclaration; String className = getQualifiedName(classDescriptor); + if (lastClassDescriptor != classDescriptor) { + lastClassDescriptor = classDescriptor; + String prefix = ""; + if (!declaredVar) { + prefix = "var "; + declaredVar = true; + } + rawStatements.add(prefix + "_testCase = new Kotlin.main." + className + "();"); + } rawStatements.add("QUnit.test( \"" + className + "." + funName + "()\" , function() {"); - String prefix = " var "; - rawStatements.add(prefix + "_testCase = new Kotlin.defs." + className + "();"); //rawStatements.add(" expect(0);"); rawStatements.add(" _testCase." + funName + "();"); } else { diff --git a/js/js.translator/src/org/jetbrains/k2js/translate/operation/UnaryOperationTranslator.java b/js/js.translator/src/org/jetbrains/k2js/translate/operation/UnaryOperationTranslator.java index 8d5bba28ff9..8777a23c258 100644 --- a/js/js.translator/src/org/jetbrains/k2js/translate/operation/UnaryOperationTranslator.java +++ b/js/js.translator/src/org/jetbrains/k2js/translate/operation/UnaryOperationTranslator.java @@ -19,6 +19,7 @@ package org.jetbrains.k2js.translate.operation; 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.JsExpression; +import com.google.dart.compiler.backend.js.ast.JsNameRef; import org.jetbrains.annotations.NotNull; import org.jetbrains.jet.lang.psi.JetUnaryExpression; import org.jetbrains.jet.lexer.JetTokens; @@ -62,9 +63,9 @@ public final class UnaryOperationTranslator { @NotNull private static JsExpression translateExclExclOperator(@NotNull JetUnaryExpression expression, @NotNull TranslationContext context) { - JsExpression translatedExpression = translateAsExpression(getBaseExpression(expression), context); - JsBinaryOperation notNullCheck = notNullCheck(context, translatedExpression); - return new JsConditional(notNullCheck, translatedExpression, context.namer().throwNPEFunctionCall()); + JsNameRef cachedValue = context.declareTemporary(translateAsExpression(getBaseExpression(expression), context), true).reference(); + JsBinaryOperation notNullCheck = notNullCheck(context, cachedValue); + return new JsConditional(notNullCheck, cachedValue, context.namer().throwNPEFunctionCall()); } @NotNull diff --git a/js/js.translator/src/org/jetbrains/k2js/translate/reference/CallBuilder.java b/js/js.translator/src/org/jetbrains/k2js/translate/reference/CallBuilder.java index 4e26d07b6b4..4cd670eaf15 100644 --- a/js/js.translator/src/org/jetbrains/k2js/translate/reference/CallBuilder.java +++ b/js/js.translator/src/org/jetbrains/k2js/translate/reference/CallBuilder.java @@ -107,8 +107,10 @@ public final class CallBuilder { private CallTranslator finish() { if (resolvedCall == null) { assert descriptor != null; - resolvedCall = ResolvedCallImpl.create(ResolutionCandidate.create(descriptor, false), - TemporaryBindingTrace.create(new BindingTraceContext())); //todo + resolvedCall = ResolvedCallImpl.create(ResolutionCandidate.create(descriptor, descriptor.getExpectedThisObject(), + descriptor.getReceiverParameter(), + ExplicitReceiverKind.THIS_OBJECT, false), + TemporaryBindingTrace.create(new BindingTraceContext())); } if (descriptor == null) { descriptor = resolvedCall.getCandidateDescriptor().getOriginal(); diff --git a/js/js.translator/src/org/jetbrains/k2js/translate/reference/CallTranslator.java b/js/js.translator/src/org/jetbrains/k2js/translate/reference/CallTranslator.java index ca42397faaf..ff97abb9815 100644 --- a/js/js.translator/src/org/jetbrains/k2js/translate/reference/CallTranslator.java +++ b/js/js.translator/src/org/jetbrains/k2js/translate/reference/CallTranslator.java @@ -213,7 +213,7 @@ public final class CallTranslator extends AbstractTranslator { @NotNull private JsInvocation generateCallMethodInvocation() { - JsNameRef callMethodNameRef = AstUtil.newQualifiedNameRef("call"); + JsNameRef callMethodNameRef = new JsNameRef("call"); JsInvocation callMethodInvocation = new JsInvocation(); callMethodInvocation.setQualifier(callMethodNameRef); setQualifier(callMethodInvocation, callParameters.getFunctionReference()); diff --git a/js/js.translator/src/org/jetbrains/k2js/translate/utils/AnnotationsUtils.java b/js/js.translator/src/org/jetbrains/k2js/translate/utils/AnnotationsUtils.java index 303ca774236..a6256535d82 100644 --- a/js/js.translator/src/org/jetbrains/k2js/translate/utils/AnnotationsUtils.java +++ b/js/js.translator/src/org/jetbrains/k2js/translate/utils/AnnotationsUtils.java @@ -19,6 +19,7 @@ package org.jetbrains.k2js.translate.utils; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.jet.lang.descriptors.ClassDescriptor; +import org.jetbrains.jet.lang.descriptors.ClassKind; import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor; import org.jetbrains.jet.lang.descriptors.annotations.AnnotationDescriptor; import org.jetbrains.jet.lang.resolve.DescriptorUtils; @@ -31,6 +32,8 @@ import static org.jetbrains.k2js.translate.utils.JsDescriptorUtils.getContaining */ public final class AnnotationsUtils { + private static final String ENUMERABLE = "js.enumerable"; + private AnnotationsUtils() { } @@ -69,10 +72,14 @@ public final class AnnotationsUtils { @Nullable private static AnnotationDescriptor getAnnotationByName(@NotNull DeclarationDescriptor descriptor, - @NotNull PredefinedAnnotation annotation) { + @NotNull PredefinedAnnotation annotation) { + return getAnnotationByName(descriptor, annotation.getFQName()); + } + + @Nullable + private static AnnotationDescriptor getAnnotationByName(@NotNull DeclarationDescriptor descriptor, @NotNull String fqn) { for (AnnotationDescriptor annotationDescriptor : descriptor.getAnnotations()) { - String annotationClassFQName = getAnnotationClassFQName(annotationDescriptor); - if (annotationClassFQName.equals(annotation.getFQName())) { + if (getAnnotationClassFQName(annotationDescriptor).equals(fqn)) { return annotationDescriptor; } } @@ -91,6 +98,16 @@ public final class AnnotationsUtils { return hasAnnotationOrInsideAnnotatedClass(descriptor, PredefinedAnnotation.NATIVE); } + public static boolean isEnumerable(@NotNull DeclarationDescriptor descriptor) { + if (getAnnotationByName(descriptor, ENUMERABLE) != null) { + return true; + } + ClassDescriptor containingClass = getContainingClass(descriptor); + return containingClass != null && + (getAnnotationByName(containingClass, ENUMERABLE) != null || + (containingClass.getKind().equals(ClassKind.OBJECT) && containingClass.getName().isSpecial())); + } + public static boolean isLibraryObject(@NotNull DeclarationDescriptor descriptor) { return hasAnnotationOrInsideAnnotatedClass(descriptor, PredefinedAnnotation.LIBRARY); } @@ -105,14 +122,15 @@ public final class AnnotationsUtils { } public static boolean hasAnnotationOrInsideAnnotatedClass(@NotNull DeclarationDescriptor descriptor, - @NotNull PredefinedAnnotation annotation) { - if (getAnnotationByName(descriptor, annotation) != null) { + @NotNull PredefinedAnnotation annotation) { + return hasAnnotationOrInsideAnnotatedClass(descriptor, annotation.getFQName()); + } + + private static boolean hasAnnotationOrInsideAnnotatedClass(@NotNull DeclarationDescriptor descriptor, @NotNull String fqn) { + if (getAnnotationByName(descriptor, fqn) != null) { return true; } ClassDescriptor containingClass = getContainingClass(descriptor); - if (containingClass == null) { - return false; - } - return (getAnnotationByName(containingClass, annotation) != null); + return containingClass != null && getAnnotationByName(containingClass, fqn) != null; } } diff --git a/js/js.translator/src/org/jetbrains/k2js/translate/utils/BindingUtils.java b/js/js.translator/src/org/jetbrains/k2js/translate/utils/BindingUtils.java index 10c0775ab09..9ef7572e550 100644 --- a/js/js.translator/src/org/jetbrains/k2js/translate/utils/BindingUtils.java +++ b/js/js.translator/src/org/jetbrains/k2js/translate/utils/BindingUtils.java @@ -16,8 +16,8 @@ package org.jetbrains.k2js.translate.utils; -import com.google.common.collect.Sets; import com.intellij.psi.PsiElement; +import com.intellij.util.containers.OrderedSet; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.jet.lang.descriptors.*; @@ -110,7 +110,7 @@ public final class BindingUtils { public static List getDeclarationsForNamespace(@NotNull BindingContext bindingContext, @NotNull NamespaceDescriptor namespace) { List declarations = new ArrayList(); - for (DeclarationDescriptor descriptor : getContainedDescriptorsWhichAreNotPredefined(namespace)) { + for (DeclarationDescriptor descriptor : getContainedDescriptorsWhichAreNotPredefined(namespace, bindingContext)) { if (descriptor instanceof NamespaceDescriptor) { continue; } @@ -321,7 +321,7 @@ public final class BindingUtils { @NotNull public static Set getAllNonNativeNamespaceDescriptors(@NotNull BindingContext context, @NotNull List files) { - Set descriptorSet = Sets.newHashSet(); + Set descriptorSet = new OrderedSet(); for (JetFile file : files) { //TODO: can't be NamespaceDescriptor namespaceDescriptor = getNamespaceDescriptor(context, file); diff --git a/js/js.translator/src/org/jetbrains/k2js/translate/utils/ClassSortingUtils.java b/js/js.translator/src/org/jetbrains/k2js/translate/utils/ClassSortingUtils.java deleted file mode 100644 index d522d241a33..00000000000 --- a/js/js.translator/src/org/jetbrains/k2js/translate/utils/ClassSortingUtils.java +++ /dev/null @@ -1,88 +0,0 @@ -/* - * Copyright 2010-2012 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.jetbrains.k2js.translate.utils; - -import com.google.common.collect.Lists; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.jet.lang.descriptors.ClassDescriptor; -import org.jetbrains.jet.lang.psi.JetClass; -import org.jetbrains.jet.lang.resolve.BindingContext; - -import java.util.ArrayList; -import java.util.List; - -import static org.jetbrains.k2js.translate.utils.JsDescriptorUtils.getSuperclassDescriptors; - - -//TODO: can optimise using less dumb implementation -//TODO: pass list of descriptors here, not the list of jet classes - -/** - * @author Pavel Talanov - */ -public final class ClassSortingUtils { - - private ClassSortingUtils() { - } - - @NotNull - public static List sortUsingInheritanceOrder(@NotNull List elements, - @NotNull BindingContext bindingContext) { - List descriptors = descriptorsFromClasses(elements, bindingContext); - PartiallyOrderedSet partiallyOrderedSet - = new PartiallyOrderedSet(descriptors, inheritanceOrder()); - List sortedClasses = descriptorsToClasses(partiallyOrderedSet.partiallySortedElements(), bindingContext); - assert elements.size() == sortedClasses.size(); - return sortedClasses; - } - - @NotNull - private static PartiallyOrderedSet.Order inheritanceOrder() { - return new PartiallyOrderedSet.Order() { - @Override - public boolean firstDependsOnSecond(@NotNull ClassDescriptor first, @NotNull ClassDescriptor second) { - return isDerivedClass(first, second); - } - }; - } - - private static boolean isDerivedClass(@NotNull ClassDescriptor ancestor, @NotNull ClassDescriptor derived) { - return (getSuperclassDescriptors(derived).contains(ancestor)); - } - - @NotNull - private static List descriptorsToClasses(@NotNull List descriptors, - @NotNull BindingContext bindingContext) { - List sortedClasses = Lists.newArrayList(); - for (ClassDescriptor descriptor : descriptors) { - sortedClasses.add(BindingUtils.getClassForDescriptor(bindingContext, descriptor)); - } - return sortedClasses; - } - - - @NotNull - private static List descriptorsFromClasses(@NotNull List classesToSort, - @NotNull BindingContext bindingContext) { - List descriptorList = new ArrayList(); - for (JetClass jetClass : classesToSort) { - descriptorList.add(BindingUtils.getClassDescriptor(bindingContext, jetClass)); - } - return descriptorList; - } - -} diff --git a/js/js.translator/src/org/jetbrains/k2js/translate/utils/JsAstUtils.java b/js/js.translator/src/org/jetbrains/k2js/translate/utils/JsAstUtils.java index fbbc442187f..f9a479d7bfd 100644 --- a/js/js.translator/src/org/jetbrains/k2js/translate/utils/JsAstUtils.java +++ b/js/js.translator/src/org/jetbrains/k2js/translate/utils/JsAstUtils.java @@ -21,6 +21,8 @@ import com.google.dart.compiler.backend.js.ast.*; import com.google.dart.compiler.util.AstUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor; +import org.jetbrains.jet.lang.descriptors.FunctionDescriptor; import org.jetbrains.jet.lang.descriptors.PropertyDescriptor; import org.jetbrains.k2js.translate.context.TranslationContext; @@ -31,11 +33,13 @@ import java.util.*; */ public final class JsAstUtils { private static final JsNameRef DEFINE_PROPERTY = new JsNameRef("defineProperty"); + public static final JsNameRef CREATE_OBJECT = new JsNameRef("create"); private static final JsNameRef EMPTY_REF = new JsNameRef(""); static { JsNameRef globalObjectReference = new JsNameRef("Object"); DEFINE_PROPERTY.setQualifier(globalObjectReference); + CREATE_OBJECT.setQualifier(globalObjectReference); } private JsAstUtils() { @@ -289,20 +293,36 @@ public final class JsAstUtils { @NotNull TranslationContext context) { return AstUtil.newInvocation(DEFINE_PROPERTY, new JsThisRef(), context.program().getStringLiteral(context.getNameForDescriptor(descriptor).getIdent()), - createPropertyDataDescriptor(descriptor.isVar(), value, context)); + createPropertyDataDescriptor(descriptor.isVar(), descriptor, value, context)); } @NotNull - public static JsObjectLiteral createPropertyDataDescriptor(boolean writable, + public static JsObjectLiteral createPropertyDataDescriptor(@NotNull FunctionDescriptor descriptor, @NotNull JsExpression value, @NotNull TranslationContext context) { - JsObjectLiteral jsPropertyDescriptor = new JsObjectLiteral(); - List meta = jsPropertyDescriptor.getPropertyInitializers(); - meta.add(new JsPropertyInitializer(context.program().getStringLiteral("value"), value)); + return createPropertyDataDescriptor(descriptor.getModality().isOverridable(), descriptor, value, context); + } + + @NotNull + public static JsObjectLiteral createDataDescriptor(@NotNull JsExpression value, boolean writable, @NotNull TranslationContext context) { + JsObjectLiteral dataDescriptor = new JsObjectLiteral(); + dataDescriptor.getPropertyInitializers().add(new JsPropertyInitializer(context.program().getStringLiteral("value"), value)); if (writable) { - meta.add(context.namer().writablePropertyDescriptorField()); + dataDescriptor.getPropertyInitializers().add(context.namer().writablePropertyDescriptorField()); } - return jsPropertyDescriptor; + return dataDescriptor; + } + + @NotNull + private static JsObjectLiteral createPropertyDataDescriptor(boolean writable, + @NotNull DeclarationDescriptor descriptor, + @NotNull JsExpression value, + @NotNull TranslationContext context) { + JsObjectLiteral dataDescriptor = createDataDescriptor(value, writable, context); + if (AnnotationsUtils.isEnumerable(descriptor)) { + dataDescriptor.getPropertyInitializers().add(context.namer().enumerablePropertyDescriptorField()); + } + return dataDescriptor; } @NotNull diff --git a/js/js.translator/src/org/jetbrains/k2js/translate/utils/JsDescriptorUtils.java b/js/js.translator/src/org/jetbrains/k2js/translate/utils/JsDescriptorUtils.java index 59860ed1ea6..c6d5d40d412 100644 --- a/js/js.translator/src/org/jetbrains/k2js/translate/utils/JsDescriptorUtils.java +++ b/js/js.translator/src/org/jetbrains/k2js/translate/utils/JsDescriptorUtils.java @@ -17,15 +17,19 @@ package org.jetbrains.k2js.translate.utils; import com.google.common.collect.Lists; +import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiFile; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.jet.lang.descriptors.*; +import org.jetbrains.jet.lang.resolve.BindingContext; +import org.jetbrains.jet.lang.resolve.BindingContextUtils; import org.jetbrains.jet.lang.resolve.name.Name; import org.jetbrains.jet.lang.resolve.scopes.JetScope; import org.jetbrains.jet.lang.resolve.scopes.receivers.ReceiverDescriptor; import org.jetbrains.jet.lang.types.JetType; import org.jetbrains.jet.lang.types.expressions.OperatorConventions; -import org.jetbrains.k2js.translate.context.Namer; +import org.jetbrains.k2js.config.LibrarySourcesConfig; import java.util.ArrayList; import java.util.Collection; @@ -140,16 +144,6 @@ public final class JsDescriptorUtils { return (functionDescriptor.getReceiverParameter().exists()); } - @NotNull - public static String getNameForNamespace(@NotNull NamespaceDescriptor descriptor) { - if (descriptor.getContainingDeclaration() instanceof ModuleDescriptor) { - return Namer.getRootNamespaceName(); - } - else { - return descriptor.getName().getName(); - } - } - //TODO: why callable descriptor @Nullable public static DeclarationDescriptor getExpectedThisDescriptor(@NotNull CallableDescriptor callableDescriptor) { @@ -193,22 +187,12 @@ public final class JsDescriptorUtils { } @NotNull - public static List getAllClassesDefinedInNamespace(@NotNull NamespaceDescriptor namespaceDescriptor) { - List classDescriptors = Lists.newArrayList(); - for (DeclarationDescriptor descriptor : getContainedDescriptorsWhichAreNotPredefined(namespaceDescriptor)) { - if (descriptor instanceof ClassDescriptor) { - classDescriptors.add((ClassDescriptor)descriptor); - } - } - return classDescriptors; - } - - @NotNull - public static List getNestedNamespaces(@NotNull NamespaceDescriptor namespaceDescriptor) { + public static List getNestedNamespaces(@NotNull NamespaceDescriptor namespaceDescriptor, + @NotNull BindingContext context) { List result = Lists.newArrayList(); - for (DeclarationDescriptor descriptor : getContainedDescriptorsWhichAreNotPredefined(namespaceDescriptor)) { + for (DeclarationDescriptor descriptor : getContainedDescriptorsWhichAreNotPredefined(namespaceDescriptor, context)) { if (descriptor instanceof NamespaceDescriptor) { - result.add((NamespaceDescriptor)descriptor); + result.add((NamespaceDescriptor) descriptor); } } return result; @@ -227,10 +211,22 @@ public final class JsDescriptorUtils { } @NotNull - public static List getContainedDescriptorsWhichAreNotPredefined(@NotNull NamespaceDescriptor namespace) { + public static List getContainedDescriptorsWhichAreNotPredefined(@NotNull NamespaceDescriptor namespace, + @NotNull BindingContext context) { List result = Lists.newArrayList(); for (DeclarationDescriptor descriptor : namespace.getMemberScope().getAllDescriptors()) { if (!AnnotationsUtils.isPredefinedObject(descriptor)) { + // namespace may be defined in multiple files + if (!(descriptor instanceof NamespaceDescriptor)) { + PsiElement psiElement = BindingContextUtils.descriptorToDeclaration(context, descriptor); + if (psiElement != null) { + PsiFile file = psiElement.getContainingFile(); + if (file.getUserData(LibrarySourcesConfig.EXTERNAL_MODULE_NAME) != null) { + continue; + } + } + } + result.add(descriptor); } } @@ -238,11 +234,11 @@ public final class JsDescriptorUtils { } //TODO: at the moment this check is very ineffective - public static boolean isNamespaceEmpty(@NotNull NamespaceDescriptor namespace) { - List containedDescriptors = getContainedDescriptorsWhichAreNotPredefined(namespace); + public static boolean isNamespaceEmpty(@NotNull NamespaceDescriptor namespace, @NotNull BindingContext context) { + List containedDescriptors = getContainedDescriptorsWhichAreNotPredefined(namespace, context); for (DeclarationDescriptor descriptor : containedDescriptors) { if (descriptor instanceof NamespaceDescriptor) { - if (!isNamespaceEmpty((NamespaceDescriptor)descriptor)) { + if (!isNamespaceEmpty((NamespaceDescriptor) descriptor, context)) { return false; } } diff --git a/js/js.translator/src/org/jetbrains/k2js/translate/utils/PartiallyOrderedSet.java b/js/js.translator/src/org/jetbrains/k2js/translate/utils/PartiallyOrderedSet.java deleted file mode 100644 index 79be5ba4890..00000000000 --- a/js/js.translator/src/org/jetbrains/k2js/translate/utils/PartiallyOrderedSet.java +++ /dev/null @@ -1,121 +0,0 @@ -/* - * Copyright 2010-2012 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.jetbrains.k2js.translate.utils; - -import com.google.common.collect.Lists; -import com.google.common.collect.Maps; -import org.jetbrains.annotations.NotNull; - -import java.util.Collection; -import java.util.List; -import java.util.Map; - -/** - * @author Pavel Talanov - *

- * This is very inefficient but simple implementation of partially orderered set. - * Feel free to replace with library implementation. - */ -public final class PartiallyOrderedSet { - - private class Arc { - @NotNull - public final Element from; - @NotNull - public final Element to; - - private Arc(@NotNull Element from, @NotNull Element to) { - this.from = from; - this.to = to; - } - } - - public interface Order { - boolean firstDependsOnSecond(@NotNull Element first, @NotNull Element second); - } - - @NotNull - private final List arcs = Lists.newArrayList(); - @NotNull - private final Map incomingArcs = Maps.newHashMap(); - @NotNull - private final List elementsWithZeroIncoming = Lists.newArrayList(); - - public PartiallyOrderedSet(@NotNull Collection elements, @NotNull Order order) { - elementsWithZeroIncoming.addAll(elements); - for (@NotNull Element first : elements) { - for (@NotNull Element second : elements) { - if (order.firstDependsOnSecond(first, second)) { - arcs.add(new Arc(first, second)); - increaseIncomingCount(second); - } - } - } - } - - private void increaseIncomingCount(@NotNull Element element) { - if (!incomingArcs.containsKey(element)) { - incomingArcs.put(element, 1); - elementsWithZeroIncoming.remove(element); - } - else { - Integer count = incomingArcs.get(element); - incomingArcs.put(element, count + 1); - } - } - - private void decreaseIncomingCount(@NotNull Element element) { - assert incomingArcs.containsKey(element); - Integer count = incomingArcs.get(element); - if (count == 1) { - incomingArcs.remove(element); - elementsWithZeroIncoming.add(element); - } - else { - incomingArcs.put(element, count - 1); - } - } - - @NotNull - public List partiallySortedElements() { - List result = Lists.newArrayList(); - while (!elementsWithZeroIncoming.isEmpty()) { - result.add(getNextElement()); - } - return result; - } - - @NotNull - private Element getNextElement() { - Element elementWithZeroIncoming = getElementWithZeroIncoming(); - for (Arc arc : arcs) { - if (arc.from == elementWithZeroIncoming) { - decreaseIncomingCount(arc.to); - } - } - return elementWithZeroIncoming; - } - - @NotNull - private Element getElementWithZeroIncoming() { - int indexOfLast = elementsWithZeroIncoming.size() - 1; - Element element = elementsWithZeroIncoming.get(indexOfLast); - elementsWithZeroIncoming.remove(indexOfLast); - return element; - } - -} diff --git a/js/js.translator/src/org/jetbrains/k2js/translate/utils/PredefinedAnnotation.java b/js/js.translator/src/org/jetbrains/k2js/translate/utils/PredefinedAnnotation.java index e2e52dbea34..661b799e862 100644 --- a/js/js.translator/src/org/jetbrains/k2js/translate/utils/PredefinedAnnotation.java +++ b/js/js.translator/src/org/jetbrains/k2js/translate/utils/PredefinedAnnotation.java @@ -22,8 +22,6 @@ import org.jetbrains.annotations.NotNull; * @author Pavel Talanov */ public enum PredefinedAnnotation { - - LIBRARY("js.library"), NATIVE("js.native"); diff --git a/js/js.translator/src/org/jetbrains/k2js/translate/utils/PsiUtils.java b/js/js.translator/src/org/jetbrains/k2js/translate/utils/PsiUtils.java index 4a505f325f3..e4f08b3a55a 100644 --- a/js/js.translator/src/org/jetbrains/k2js/translate/utils/PsiUtils.java +++ b/js/js.translator/src/org/jetbrains/k2js/translate/utils/PsiUtils.java @@ -23,7 +23,6 @@ import org.jetbrains.annotations.Nullable; import org.jetbrains.jet.lang.psi.*; import org.jetbrains.jet.lexer.JetToken; import org.jetbrains.jet.lexer.JetTokens; -import org.jetbrains.k2js.translate.context.Namer; import java.util.Collections; import java.util.List; @@ -143,17 +142,6 @@ public final class PsiUtils { return nameAsDeclaration; } - @NotNull - public static String getNamespaceName(@NotNull JetFile psiFile) { - JetNamespaceHeader namespaceHeader = psiFile.getNamespaceHeader(); - String name = namespaceHeader.getName(); - assert name != null : "NamespaceHeader must have a name"; - if (name.isEmpty()) { - return Namer.getRootNamespaceName(); - } - return name; - } - @NotNull public static JetExpression getLoopRange(@NotNull JetForExpression expression) { JetExpression rangeExpression = expression.getLoopRange(); diff --git a/js/js.translator/src/org/jetbrains/k2js/translate/utils/TranslationUtils.java b/js/js.translator/src/org/jetbrains/k2js/translate/utils/TranslationUtils.java index bfa8dbd6463..30a5d686e18 100644 --- a/js/js.translator/src/org/jetbrains/k2js/translate/utils/TranslationUtils.java +++ b/js/js.translator/src/org/jetbrains/k2js/translate/utils/TranslationUtils.java @@ -43,7 +43,7 @@ import static org.jetbrains.k2js.translate.utils.JsDescriptorUtils.getExpectedRe */ public final class TranslationUtils { - private static JsNameRef UNDEFINED_LITERAL = AstUtil.newQualifiedNameRef("undefined"); + private static final JsNameRef UNDEFINED_LITERAL = AstUtil.newQualifiedNameRef("undefined"); private TranslationUtils() { } @@ -64,11 +64,7 @@ public final class TranslationUtils { @NotNull private static JsPropertyInitializer translateExtensionFunctionAsEcma5PropertyDescriptor(@NotNull JsFunction function, @NotNull FunctionDescriptor descriptor, @NotNull TranslationContext context) { - JsObjectLiteral meta = new JsObjectLiteral(); - meta.getPropertyInitializers().add(new JsPropertyInitializer(context.program().getStringLiteral("value"), function)); - if (descriptor.getModality().isOverridable()) { - meta.getPropertyInitializers().add(context.namer().writablePropertyDescriptorField()); - } + JsObjectLiteral meta = JsAstUtils.createDataDescriptor(function, descriptor.getModality().isOverridable(), context); return new JsPropertyInitializer(context.getNameForDescriptor(descriptor).makeRef(), meta); } diff --git a/js/js.translator/testFiles/extensionFunction/cases/extensionFunctionCalledFromFor.kt b/js/js.translator/testFiles/extensionFunction/cases/extensionFunctionCalledFromFor.kt new file mode 100644 index 00000000000..98f24fe5da3 --- /dev/null +++ b/js/js.translator/testFiles/extensionFunction/cases/extensionFunctionCalledFromFor.kt @@ -0,0 +1,33 @@ +package foo + +class SimpleEnumerator { + private var counter = 0 + + fun getNext(): String { + counter++; + return counter.toString() + } + + fun hasMoreElements(): Boolean = counter < 1 +} + +class SimpleEnumeratorWrapper(private val enumerator: SimpleEnumerator) { + val hasNext: Boolean + get() = enumerator.hasMoreElements() + + fun next() = enumerator.getNext() +} + +fun SimpleEnumerator.iterator(): SimpleEnumeratorWrapper { + return SimpleEnumeratorWrapper(this) +} + +fun box(): Boolean { + var o = "" + val enumerator = SimpleEnumerator() + for (s in enumerator) { + o += s; + } + + return o == "1" +} \ No newline at end of file diff --git a/js/js.translator/testFiles/java/arrayList/cases/toArray.kt b/js/js.translator/testFiles/java/arrayList/cases/toArray.kt new file mode 100644 index 00000000000..65da1d0d93f --- /dev/null +++ b/js/js.translator/testFiles/java/arrayList/cases/toArray.kt @@ -0,0 +1,13 @@ +package foo + +import java.util.ArrayList + +fun box() : Boolean { + var i = 0 + val list = ArrayList() + while (i++ < 3) { + list.add(i) + } + val array = list.toArray() + return array[0] == 1 && array[1] == 2 && array[2] == 3 +} \ No newline at end of file diff --git a/js/js.translator/testFiles/jslint.js b/js/js.translator/testFiles/jslint.js new file mode 100644 index 00000000000..db95cfc73bf --- /dev/null +++ b/js/js.translator/testFiles/jslint.js @@ -0,0 +1,6400 @@ +// jslint.js +// 2012-05-09 + +// Copyright (c) 2002 Douglas Crockford (www.JSLint.com) + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: + +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. + +// The Software shall be used for Good, not Evil. + +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +// WARNING: JSLint will hurt your feelings. + +// JSLINT is a global function. It takes two parameters. + +// var myResult = JSLINT(source, option); + +// The first parameter is either a string or an array of strings. If it is a +// string, it will be split on '\n' or '\r'. If it is an array of strings, it +// is assumed that each string represents one line. The source can be a +// JavaScript text, or HTML text, or a JSON text, or a CSS text. + +// The second parameter is an optional object of options that control the +// operation of JSLINT. Most of the options are booleans: They are all +// optional and have a default value of false. One of the options, predef, +// can be an array of names, which will be used to declare global variables, +// or an object whose keys are used as global names, with a boolean value +// that determines if they are assignable. + +// If it checks out, JSLINT returns true. Otherwise, it returns false. + +// If false, you can inspect JSLINT.errors to find out the problems. +// JSLINT.errors is an array of objects containing these properties: + +// { +// line : The line (relative to 0) at which the lint was found +// character : The character (relative to 0) at which the lint was found +// reason : The problem +// evidence : The text line in which the problem occurred +// raw : The raw message before the details were inserted +// a : The first detail +// b : The second detail +// c : The third detail +// d : The fourth detail +// } + +// If a stopping error was found, a null will be the last element of the +// JSLINT.errors array. A stopping error means that JSLint was not confident +// enough to continue. It does not necessarily mean that the error was +// especially heinous. + +// You can request a data structure that contains JSLint's results. + +// var myData = JSLINT.data(); + +// It returns a structure with this form: + +// { +// errors: [ +// { +// line: NUMBER, +// character: NUMBER, +// reason: STRING, +// evidence: STRING +// } +// ], +// functions: [ +// { +// name: STRING, +// line: NUMBER, +// last: NUMBER, +// params: [ +// { +// string: STRING +// } +// ], +// closure: [ +// STRING +// ], +// var: [ +// STRING +// ], +// exception: [ +// STRING +// ], +// outer: [ +// STRING +// ], +// unused: [ +// STRING +// ], +// undef: [ +// STRING +// ], +// global: [ +// STRING +// ], +// label: [ +// STRING +// ] +// } +// ], +// globals: [ +// STRING +// ], +// member: { +// STRING: NUMBER +// }, +// urls: [ +// STRING +// ], +// json: BOOLEAN +// } + +// Empty arrays will not be included. + +// You can request a Function Report, which shows all of the functions +// and the parameters and vars that they use. This can be used to find +// implied global variables and other problems. The report is in HTML and +// can be inserted in an HTML . It should be given the result of the +// JSLINT.data function. + +// var myReport = JSLINT.report(data); + +// You can request an HTML error report. + +// var myErrorReport = JSLINT.error_report(data); + +// You can request a properties report, which produces a list of the program's +// properties in the form of a /*properties*/ declaration. + +// var myPropertyReport = properties_report(JSLINT.property); + +// You can obtain the parse tree that JSLint constructed while parsing. The +// latest tree is kept in JSLINT.tree. A nice stringication can be produced +// with + +// JSON.stringify(JSLINT.tree, [ +// 'string', 'arity', 'name', 'first', +// 'second', 'third', 'block', 'else' +// ], 4)); + +// JSLint provides three directives. They look like slashstar comments, and +// allow for setting options, declaring global variables, and establishing a +// set of allowed property names. + +// These directives respect function scope. + +// The jslint directive is a special comment that can set one or more options. +// The current option set is + +// anon true, if the space may be omitted in anonymous function declarations +// bitwise true, if bitwise operators should be allowed +// browser true, if the standard browser globals should be predefined +// cap true, if upper case HTML should be allowed +// 'continue' true, if the continuation statement should be tolerated +// css true, if CSS workarounds should be tolerated +// debug true, if debugger statements should be allowed +// devel true, if logging should be allowed (console, alert, etc.) +// eqeq true, if == should be allowed +// es5 true, if ES5 syntax should be allowed +// evil true, if eval should be allowed +// forin true, if for in statements need not filter +// fragment true, if HTML fragments should be allowed +// indent the indentation factor +// maxerr the maximum number of errors to allow +// maxlen the maximum length of a source line +// newcap true, if constructor names capitalization is ignored +// node true, if Node.js globals should be predefined +// nomen true, if names may have dangling _ +// on true, if HTML event handlers should be allowed +// passfail true, if the scan should stop on first error +// plusplus true, if increment/decrement should be allowed +// properties true, if all property names must be declared with /*properties*/ +// regexp true, if the . should be allowed in regexp literals +// rhino true, if the Rhino environment globals should be predefined +// undef true, if variables can be declared out of order +// unparam true, if unused parameters should be tolerated +// sloppy true, if the 'use strict'; pragma is optional +// stupid true, if really stupid practices are tolerated +// sub true, if all forms of subscript notation are tolerated +// vars true, if multiple var statements per function should be allowed +// white true, if sloppy whitespace is tolerated +// windows true, if MS Windows-specific globals should be predefined + +// For example: + +/*jslint + evil: true, nomen: true, regexp: true +*/ + +// The properties directive declares an exclusive list of property names. +// Any properties named in the program that are not in the list will +// produce a warning. + +// For example: + +/*properties + '\b', '\t', '\n', '\f', '\r', '!', '!=', '!==', '"', '%', '\'', + '(arguments)', '(begin)', '(breakage)', '(context)', '(error)', + '(identifier)', '(line)', '(loopage)', '(name)', '(params)', '(scope)', + '(token)', '(vars)', '(verb)', '*', '+', '-', '/', '<', '<=', '==', '===', + '>', '>=', ADSAFE, Array, Date, Function, Object, '\\', a, a_label, + a_not_allowed, a_not_defined, a_scope, abbr, acronym, address, adsafe, + adsafe_a, adsafe_autocomplete, adsafe_bad_id, adsafe_div, adsafe_fragment, + adsafe_go, adsafe_html, adsafe_id, adsafe_id_go, adsafe_lib, + adsafe_lib_second, adsafe_missing_id, adsafe_name_a, adsafe_placement, + adsafe_prefix_a, adsafe_script, adsafe_source, adsafe_subscript_a, + adsafe_tag, all, already_defined, and, anon, applet, apply, approved, area, + arity, article, aside, assign, assign_exception, + assignment_function_expression, at, attribute_case_a, audio, autocomplete, + avoid_a, b, background, 'background-attachment', 'background-color', + 'background-image', 'background-position', 'background-repeat', + bad_assignment, bad_color_a, bad_constructor, bad_entity, bad_html, bad_id_a, + bad_in_a, bad_invocation, bad_name_a, bad_new, bad_number, bad_operand, + bad_style, bad_type, bad_url_a, bad_wrap, base, bdo, big, bitwise, block, + blockquote, body, border, 'border-bottom', 'border-bottom-color', + 'border-bottom-left-radius', 'border-bottom-right-radius', + 'border-bottom-style', 'border-bottom-width', 'border-collapse', + 'border-color', 'border-left', 'border-left-color', 'border-left-style', + 'border-left-width', 'border-radius', 'border-right', 'border-right-color', + 'border-right-style', 'border-right-width', 'border-spacing', 'border-style', + 'border-top', 'border-top-color', 'border-top-left-radius', + 'border-top-right-radius', 'border-top-style', 'border-top-width', + 'border-width', bottom, 'box-shadow', br, braille, browser, button, c, call, + canvas, cap, caption, 'caption-side', center, charAt, charCodeAt, character, + cite, clear, clip, closure, cm, code, col, colgroup, color, combine_var, + command, conditional_assignment, confusing_a, confusing_regexp, + constructor_name_a, content, continue, control_a, 'counter-increment', + 'counter-reset', create, css, cursor, d, dangerous_comment, dangling_a, data, + datalist, dd, debug, del, deleted, details, devel, dfn, dialog, dir, + direction, display, disrupt, div, dl, dt, duplicate_a, edge, edition, else, + em, embed, embossed, empty, 'empty-cells', empty_block, empty_case, + empty_class, entityify, eqeq, error_report, errors, es5, eval, evidence, + evil, ex, exception, exec, expected_a, expected_a_at_b_c, expected_a_b, + expected_a_b_from_c_d, expected_at_a, expected_attribute_a, + expected_attribute_value_a, expected_class_a, expected_fraction_a, + expected_id_a, expected_identifier_a, expected_identifier_a_reserved, + expected_lang_a, expected_linear_a, expected_media_a, expected_name_a, + expected_nonstandard_style_attribute, expected_number_a, expected_operator_a, + expected_percent_a, expected_positive_a, expected_pseudo_a, + expected_selector_a, expected_small_a, expected_space_a_b, expected_string_a, + expected_style_attribute, expected_style_pattern, expected_tagname_a, + expected_type_a, f, fieldset, figure, filter, first, flag, float, floor, + font, 'font-family', 'font-size', 'font-size-adjust', 'font-stretch', + 'font-style', 'font-variant', 'font-weight', footer, forEach, for_if, forin, + form, fragment, frame, frameset, from, fromCharCode, fud, funct, function, + function_block, function_eval, function_loop, function_statement, + function_strict, functions, global, globals, h1, h2, h3, h4, h5, h6, + handheld, hasOwnProperty, head, header, height, hgroup, hr, + 'hta:application', html, html_confusion_a, html_handlers, i, id, identifier, + identifier_function, iframe, img, immed, implied_evil, in, indent, indexOf, + infix_in, init, input, ins, insecure_a, isAlpha, isArray, isDigit, isNaN, + join, jslint, json, kbd, keygen, keys, label, labeled, lang, lbp, + leading_decimal_a, led, left, legend, length, 'letter-spacing', li, lib, + line, 'line-height', link, 'list-style', 'list-style-image', + 'list-style-position', 'list-style-type', map, margin, 'margin-bottom', + 'margin-left', 'margin-right', 'margin-top', mark, 'marker-offset', match, + 'max-height', 'max-width', maxerr, maxlen, menu, message, meta, meter, + 'min-height', 'min-width', missing_a, missing_a_after_b, missing_option, + missing_property, missing_space_a_b, missing_url, missing_use_strict, mixed, + mm, mode, move_invocation, move_var, n, name, name_function, nav, + nested_comment, newcap, node, noframes, nomen, noscript, not, + not_a_constructor, not_a_defined, not_a_function, not_a_label, not_a_scope, + not_greater, nud, number, object, octal_a, ol, on, opacity, open, optgroup, + option, outer, outline, 'outline-color', 'outline-style', 'outline-width', + output, overflow, 'overflow-x', 'overflow-y', p, padding, 'padding-bottom', + 'padding-left', 'padding-right', 'padding-top', 'page-break-after', + 'page-break-before', param, parameter_a_get_b, parameter_arguments_a, + parameter_set_a, params, paren, parent, passfail, pc, plusplus, pop, + position, postscript, pre, predef, print, progress, projection, properties, + properties_report, property, prototype, pt, push, px, q, quote, quotes, r, + radix, range, raw, read_only, reason, redefinition_a, regexp, replace, + report, reserved, reserved_a, rhino, right, rp, rt, ruby, safe, samp, + scanned_a_b, screen, script, search, second, section, select, shift, + slash_equal, slice, sloppy, small, sort, source, span, speech, split, src, + statement_block, stopping, strange_loop, strict, string, strong, stupid, + style, styleproperty, sub, subscript, substr, sup, supplant, sync_a, t, + table, 'table-layout', tag_a_in_b, tbody, td, test, 'text-align', + 'text-decoration', 'text-indent', 'text-shadow', 'text-transform', textarea, + tfoot, th, thead, third, thru, time, title, toLowerCase, toString, + toUpperCase, token, too_long, too_many, top, tr, trailing_decimal_a, tree, + tt, tty, tv, type, u, ul, unclosed, unclosed_comment, unclosed_regexp, undef, + undefined, unescaped_a, unexpected_a, unexpected_char_a_b, + unexpected_comment, unexpected_else, unexpected_label_a, + unexpected_property_a, unexpected_space_a_b, 'unicode-bidi', + unnecessary_initialize, unnecessary_use, unparam, unreachable_a_b, + unrecognized_style_attribute_a, unrecognized_tag_a, unsafe, unused, url, + urls, use_array, use_braces, use_charAt, use_object, use_or, use_param, + used_before_a, var, var_a_not, vars, 'vertical-align', video, visibility, + was, weird_assignment, weird_condition, weird_new, weird_program, + weird_relation, weird_ternary, white, 'white-space', width, windows, + 'word-spacing', 'word-wrap', wrap, wrap_immediate, wrap_regexp, + write_is_wrong, writeable, 'z-index' +*/ + +// The global directive is used to declare global variables that can +// be accessed by the program. If a declaration is true, then the variable +// is writeable. Otherwise, it is read-only. + +// We build the application inside a function so that we produce only a single +// global variable. That function will be invoked immediately, and its return +// value is the JSLINT function itself. That function is also an object that +// can contain data and other functions. + +var JSLINT = (function () { + 'use strict'; + + function array_to_object(array, value) { + +// Make an object from an array of keys and a common value. + + var i, length = array.length, object = {}; + for (i = 0; i < length; i += 1) { + object[array[i]] = value; + } + return object; + } + + + var adsafe_id, // The widget's ADsafe id. + adsafe_may, // The widget may load approved scripts. + adsafe_top, // At the top of the widget script. + adsafe_went, // ADSAFE.go has been called. + allowed_option = { + anon : true, + bitwise : true, + browser : true, + cap : true, + 'continue': true, + css : true, + debug : true, + devel : true, + eqeq : true, + es5 : true, + evil : true, + forin : true, + fragment : true, + indent : 10, + maxerr : 1000, + maxlen : 256, + newcap : true, + node : true, + nomen : true, + on : true, + passfail : true, + plusplus : true, + properties: true, + regexp : true, + rhino : true, + undef : true, + unparam : true, + sloppy : true, + stupid : true, + sub : true, + vars : true, + white : true, + windows : true + }, + anonname, // The guessed name for anonymous functions. + approved, // ADsafe approved urls. + +// These are operators that should not be used with the ! operator. + + bang = { + '<' : true, + '<=' : true, + '==' : true, + '===': true, + '!==': true, + '!=' : true, + '>' : true, + '>=' : true, + '+' : true, + '-' : true, + '*' : true, + '/' : true, + '%' : true + }, + +// These are property names that should not be permitted in the safe subset. + + banned = array_to_object([ + 'arguments', 'callee', 'caller', 'constructor', 'eval', 'prototype', + 'stack', 'unwatch', 'valueOf', 'watch' + ], true), + begin, // The root token + +// browser contains a set of global names that are commonly provided by a +// web browser environment. + + browser = array_to_object([ + 'clearInterval', 'clearTimeout', 'document', 'event', 'FormData', + 'frames', 'history', 'Image', 'localStorage', 'location', 'name', + 'navigator', 'Option', 'parent', 'screen', 'sessionStorage', + 'setInterval', 'setTimeout', 'Storage', 'window', 'XMLHttpRequest' + ], false), + +// bundle contains the text messages. + + bundle = { + a_label: "'{a}' is a statement label.", + a_not_allowed: "'{a}' is not allowed.", + a_not_defined: "'{a}' is not defined.", + a_scope: "'{a}' used out of scope.", + adsafe_a: "ADsafe violation: '{a}'.", + adsafe_autocomplete: "ADsafe autocomplete violation.", + adsafe_bad_id: "ADSAFE violation: bad id.", + adsafe_div: "ADsafe violation: Wrap the widget in a div.", + adsafe_fragment: "ADSAFE: Use the fragment option.", + adsafe_go: "ADsafe violation: Misformed ADSAFE.go.", + adsafe_html: "Currently, ADsafe does not operate on whole HTML " + + "documents. It operates on

fragments and .js files.", + adsafe_id: "ADsafe violation: id does not match.", + adsafe_id_go: "ADsafe violation: Missing ADSAFE.id or ADSAFE.go.", + adsafe_lib: "ADsafe lib violation.", + adsafe_lib_second: "ADsafe: The second argument to lib must be a function.", + adsafe_missing_id: "ADSAFE violation: missing ID_.", + adsafe_name_a: "ADsafe name violation: '{a}'.", + adsafe_placement: "ADsafe script placement violation.", + adsafe_prefix_a: "ADsafe violation: An id must have a '{a}' prefix", + adsafe_script: "ADsafe script violation.", + adsafe_source: "ADsafe unapproved script source.", + adsafe_subscript_a: "ADsafe subscript '{a}'.", + adsafe_tag: "ADsafe violation: Disallowed tag '{a}'.", + already_defined: "'{a}' is already defined.", + and: "The '&&' subexpression should be wrapped in parens.", + assign_exception: "Do not assign to the exception parameter.", + assignment_function_expression: "Expected an assignment or " + + "function call and instead saw an expression.", + attribute_case_a: "Attribute '{a}' not all lower case.", + avoid_a: "Avoid '{a}'.", + bad_assignment: "Bad assignment.", + bad_color_a: "Bad hex color '{a}'.", + bad_constructor: "Bad constructor.", + bad_entity: "Bad entity.", + bad_html: "Bad HTML string", + bad_id_a: "Bad id: '{a}'.", + bad_in_a: "Bad for in variable '{a}'.", + bad_invocation: "Bad invocation.", + bad_name_a: "Bad name: '{a}'.", + bad_new: "Do not use 'new' for side effects.", + bad_number: "Bad number '{a}'.", + bad_operand: "Bad operand.", + bad_style: "Bad style.", + bad_type: "Bad type.", + bad_url_a: "Bad url '{a}'.", + bad_wrap: "Do not wrap function literals in parens unless they " + + "are to be immediately invoked.", + combine_var: "Combine this with the previous 'var' statement.", + conditional_assignment: "Expected a conditional expression and " + + "instead saw an assignment.", + confusing_a: "Confusing use of '{a}'.", + confusing_regexp: "Confusing regular expression.", + constructor_name_a: "A constructor name '{a}' should start with " + + "an uppercase letter.", + control_a: "Unexpected control character '{a}'.", + css: "A css file should begin with @charset 'UTF-8';", + dangling_a: "Unexpected dangling '_' in '{a}'.", + dangerous_comment: "Dangerous comment.", + deleted: "Only properties should be deleted.", + duplicate_a: "Duplicate '{a}'.", + empty_block: "Empty block.", + empty_case: "Empty case.", + empty_class: "Empty class.", + es5: "This is an ES5 feature.", + evil: "eval is evil.", + expected_a: "Expected '{a}'.", + expected_a_b: "Expected '{a}' and instead saw '{b}'.", + expected_a_b_from_c_d: "Expected '{a}' to match '{b}' from line " + + "{c} and instead saw '{d}'.", + expected_at_a: "Expected an at-rule, and instead saw @{a}.", + expected_a_at_b_c: "Expected '{a}' at column {b}, not column {c}.", + expected_attribute_a: "Expected an attribute, and instead saw [{a}].", + expected_attribute_value_a: "Expected an attribute value and " + + "instead saw '{a}'.", + expected_class_a: "Expected a class, and instead saw .{a}.", + expected_fraction_a: "Expected a number between 0 and 1 and " + + "instead saw '{a}'", + expected_id_a: "Expected an id, and instead saw #{a}.", + expected_identifier_a: "Expected an identifier and instead saw '{a}'.", + expected_identifier_a_reserved: "Expected an identifier and " + + "instead saw '{a}' (a reserved word).", + expected_linear_a: "Expected a linear unit and instead saw '{a}'.", + expected_lang_a: "Expected a lang code, and instead saw :{a}.", + expected_media_a: "Expected a CSS media type, and instead saw '{a}'.", + expected_name_a: "Expected a name and instead saw '{a}'.", + expected_nonstandard_style_attribute: "Expected a non-standard " + + "style attribute and instead saw '{a}'.", + expected_number_a: "Expected a number and instead saw '{a}'.", + expected_operator_a: "Expected an operator and instead saw '{a}'.", + expected_percent_a: "Expected a percentage and instead saw '{a}'", + expected_positive_a: "Expected a positive number and instead saw '{a}'", + expected_pseudo_a: "Expected a pseudo, and instead saw :{a}.", + expected_selector_a: "Expected a CSS selector, and instead saw {a}.", + expected_small_a: "Expected a small positive integer and instead saw '{a}'", + expected_space_a_b: "Expected exactly one space between '{a}' and '{b}'.", + expected_string_a: "Expected a string and instead saw {a}.", + expected_style_attribute: "Excepted a style attribute, and instead saw '{a}'.", + expected_style_pattern: "Expected a style pattern, and instead saw '{a}'.", + expected_tagname_a: "Expected a tagName, and instead saw {a}.", + expected_type_a: "Expected a type, and instead saw {a}.", + for_if: "The body of a for in should be wrapped in an if " + + "statement to filter unwanted properties from the prototype.", + function_block: "Function statements should not be placed in blocks. " + + "Use a function expression or move the statement to the top of " + + "the outer function.", + function_eval: "The Function constructor is eval.", + function_loop: "Don't make functions within a loop.", + function_statement: "Function statements are not invocable. " + + "Wrap the whole function invocation in parens.", + function_strict: "Use the function form of 'use strict'.", + html_confusion_a: "HTML confusion in regular expression '<{a}'.", + html_handlers: "Avoid HTML event handlers.", + identifier_function: "Expected an identifier in an assignment " + + "and instead saw a function invocation.", + implied_evil: "Implied eval is evil. Pass a function instead of a string.", + infix_in: "Unexpected 'in'. Compare with undefined, or use the " + + "hasOwnProperty method instead.", + insecure_a: "Insecure '{a}'.", + isNaN: "Use the isNaN function to compare with NaN.", + lang: "lang is deprecated.", + leading_decimal_a: "A leading decimal point can be confused with a dot: '.{a}'.", + missing_a: "Missing '{a}'.", + missing_a_after_b: "Missing '{a}' after '{b}'.", + missing_option: "Missing option value.", + missing_property: "Missing property name.", + missing_space_a_b: "Missing space between '{a}' and '{b}'.", + missing_url: "Missing url.", + missing_use_strict: "Missing 'use strict' statement.", + mixed: "Mixed spaces and tabs.", + move_invocation: "Move the invocation into the parens that " + + "contain the function.", + move_var: "Move 'var' declarations to the top of the function.", + name_function: "Missing name in function statement.", + nested_comment: "Nested comment.", + not: "Nested not.", + not_a_constructor: "Do not use {a} as a constructor.", + not_a_defined: "'{a}' has not been fully defined yet.", + not_a_function: "'{a}' is not a function.", + not_a_label: "'{a}' is not a label.", + not_a_scope: "'{a}' is out of scope.", + not_greater: "'{a}' should not be greater than '{b}'.", + octal_a: "Don't use octal: '{a}'. Use '\\u....' instead.", + parameter_arguments_a: "Do not mutate parameter '{a}' when using 'arguments'.", + parameter_a_get_b: "Unexpected parameter '{a}' in get {b} function.", + parameter_set_a: "Expected parameter (value) in set {a} function.", + radix: "Missing radix parameter.", + read_only: "Read only.", + redefinition_a: "Redefinition of '{a}'.", + reserved_a: "Reserved name '{a}'.", + scanned_a_b: "{a} ({b}% scanned).", + slash_equal: "A regular expression literal can be confused with '/='.", + statement_block: "Expected to see a statement and instead saw a block.", + stopping: "Stopping. ", + strange_loop: "Strange loop.", + strict: "Strict violation.", + subscript: "['{a}'] is better written in dot notation.", + sync_a: "Unexpected sync method: '{a}'.", + tag_a_in_b: "A '<{a}>' must be within '<{b}>'.", + too_long: "Line too long.", + too_many: "Too many errors.", + trailing_decimal_a: "A trailing decimal point can be confused " + + "with a dot: '.{a}'.", + type: "type is unnecessary.", + unclosed: "Unclosed string.", + unclosed_comment: "Unclosed comment.", + unclosed_regexp: "Unclosed regular expression.", + unescaped_a: "Unescaped '{a}'.", + unexpected_a: "Unexpected '{a}'.", + unexpected_char_a_b: "Unexpected character '{a}' in {b}.", + unexpected_comment: "Unexpected comment.", + unexpected_else: "Unexpected 'else' after 'return'.", + unexpected_label_a: "Unexpected label '{a}'.", + unexpected_property_a: "Unexpected /*property*/ '{a}'.", + unexpected_space_a_b: "Unexpected space between '{a}' and '{b}'.", + unnecessary_initialize: "It is not necessary to initialize '{a}' " + + "to 'undefined'.", + unnecessary_use: "Unnecessary 'use strict'.", + unreachable_a_b: "Unreachable '{a}' after '{b}'.", + unrecognized_style_attribute_a: "Unrecognized style attribute '{a}'.", + unrecognized_tag_a: "Unrecognized tag '<{a}>'.", + unsafe: "Unsafe character.", + url: "JavaScript URL.", + use_array: "Use the array literal notation [].", + use_braces: "Spaces are hard to count. Use {{a}}.", + use_charAt: "Use the charAt method.", + use_object: "Use the object literal notation {}.", + use_or: "Use the || operator.", + use_param: "Use a named parameter.", + used_before_a: "'{a}' was used before it was defined.", + var_a_not: "Variable {a} was not declared correctly.", + weird_assignment: "Weird assignment.", + weird_condition: "Weird condition.", + weird_new: "Weird construction. Delete 'new'.", + weird_program: "Weird program.", + weird_relation: "Weird relation.", + weird_ternary: "Weird ternary.", + wrap_immediate: "Wrap an immediate function invocation in parentheses " + + "to assist the reader in understanding that the expression " + + "is the result of a function, and not the function itself.", + wrap_regexp: "Wrap the /regexp/ literal in parens to " + + "disambiguate the slash operator.", + write_is_wrong: "document.write can be a form of eval." + }, + comments_off, + css_attribute_data, + css_any, + + css_colorData = array_to_object([ + "aliceblue", "antiquewhite", "aqua", "aquamarine", "azure", "beige", + "bisque", "black", "blanchedalmond", "blue", "blueviolet", "brown", + "burlywood", "cadetblue", "chartreuse", "chocolate", "coral", + "cornflowerblue", "cornsilk", "crimson", "cyan", "darkblue", + "darkcyan", "darkgoldenrod", "darkgray", "darkgreen", "darkkhaki", + "darkmagenta", "darkolivegreen", "darkorange", "darkorchid", + "darkred", "darksalmon", "darkseagreen", "darkslateblue", + "darkslategray", "darkturquoise", "darkviolet", "deeppink", + "deepskyblue", "dimgray", "dodgerblue", "firebrick", "floralwhite", + "forestgreen", "fuchsia", "gainsboro", "ghostwhite", "gold", + "goldenrod", "gray", "green", "greenyellow", "honeydew", "hotpink", + "indianred", "indigo", "ivory", "khaki", "lavender", + "lavenderblush", "lawngreen", "lemonchiffon", "lightblue", + "lightcoral", "lightcyan", "lightgoldenrodyellow", "lightgreen", + "lightpink", "lightsalmon", "lightseagreen", "lightskyblue", + "lightslategray", "lightsteelblue", "lightyellow", "lime", + "limegreen", "linen", "magenta", "maroon", "mediumaquamarine", + "mediumblue", "mediumorchid", "mediumpurple", "mediumseagreen", + "mediumslateblue", "mediumspringgreen", "mediumturquoise", + "mediumvioletred", "midnightblue", "mintcream", "mistyrose", + "moccasin", "navajowhite", "navy", "oldlace", "olive", "olivedrab", + "orange", "orangered", "orchid", "palegoldenrod", "palegreen", + "paleturquoise", "palevioletred", "papayawhip", "peachpuff", + "peru", "pink", "plum", "powderblue", "purple", "red", "rosybrown", + "royalblue", "saddlebrown", "salmon", "sandybrown", "seagreen", + "seashell", "sienna", "silver", "skyblue", "slateblue", "slategray", + "snow", "springgreen", "steelblue", "tan", "teal", "thistle", + "tomato", "turquoise", "violet", "wheat", "white", "whitesmoke", + "yellow", "yellowgreen", + + "activeborder", "activecaption", "appworkspace", "background", + "buttonface", "buttonhighlight", "buttonshadow", "buttontext", + "captiontext", "graytext", "highlight", "highlighttext", + "inactiveborder", "inactivecaption", "inactivecaptiontext", + "infobackground", "infotext", "menu", "menutext", "scrollbar", + "threeddarkshadow", "threedface", "threedhighlight", + "threedlightshadow", "threedshadow", "window", "windowframe", + "windowtext" + ], true), + + css_border_style, + css_break, + + css_lengthData = { + '%': true, + 'cm': true, + 'em': true, + 'ex': true, + 'in': true, + 'mm': true, + 'pc': true, + 'pt': true, + 'px': true + }, + + css_media, + css_overflow, + + descapes = { + 'b': '\b', + 't': '\t', + 'n': '\n', + 'f': '\f', + 'r': '\r', + '"': '"', + '/': '/', + '\\': '\\', + '!': '!' + }, + + devel = array_to_object([ + 'alert', 'confirm', 'console', 'Debug', 'opera', 'prompt', 'WSH' + ], false), + directive, + escapes = { + '\b': '\\b', + '\t': '\\t', + '\n': '\\n', + '\f': '\\f', + '\r': '\\r', + '\'': '\\\'', + '"' : '\\"', + '/' : '\\/', + '\\': '\\\\' + }, + + funct, // The current function, including the labels used in + // the function, as well as (breakage), + // (context), (loopage), (name), (params), (token), + // (vars), (verb) + + functionicity = [ + 'closure', 'exception', 'global', 'label', 'outer', 'undef', + 'unused', 'var' + ], + + functions, // All of the functions + global_funct, // The global body + global_scope, // The global scope + html_tag = { + a: {}, + abbr: {}, + acronym: {}, + address: {}, + applet: {}, + area: {empty: true, parent: ' map '}, + article: {}, + aside: {}, + audio: {}, + b: {}, + base: {empty: true, parent: ' head '}, + bdo: {}, + big: {}, + blockquote: {}, + body: {parent: ' html noframes '}, + br: {empty: true}, + button: {}, + canvas: {parent: ' body p div th td '}, + caption: {parent: ' table '}, + center: {}, + cite: {}, + code: {}, + col: {empty: true, parent: ' table colgroup '}, + colgroup: {parent: ' table '}, + command: {parent: ' menu '}, + datalist: {}, + dd: {parent: ' dl '}, + del: {}, + details: {}, + dialog: {}, + dfn: {}, + dir: {}, + div: {}, + dl: {}, + dt: {parent: ' dl '}, + em: {}, + embed: {}, + fieldset: {}, + figure: {}, + font: {}, + footer: {}, + form: {}, + frame: {empty: true, parent: ' frameset '}, + frameset: {parent: ' html frameset '}, + h1: {}, + h2: {}, + h3: {}, + h4: {}, + h5: {}, + h6: {}, + head: {parent: ' html '}, + header: {}, + hgroup: {}, + hr: {empty: true}, + 'hta:application': + {empty: true, parent: ' head '}, + html: {parent: '*'}, + i: {}, + iframe: {}, + img: {empty: true}, + input: {empty: true}, + ins: {}, + kbd: {}, + keygen: {}, + label: {}, + legend: {parent: ' details fieldset figure '}, + li: {parent: ' dir menu ol ul '}, + link: {empty: true, parent: ' head '}, + map: {}, + mark: {}, + menu: {}, + meta: {empty: true, parent: ' head noframes noscript '}, + meter: {}, + nav: {}, + noframes: {parent: ' html body '}, + noscript: {parent: ' body head noframes '}, + object: {}, + ol: {}, + optgroup: {parent: ' select '}, + option: {parent: ' optgroup select '}, + output: {}, + p: {}, + param: {empty: true, parent: ' applet object '}, + pre: {}, + progress: {}, + q: {}, + rp: {}, + rt: {}, + ruby: {}, + samp: {}, + script: {empty: true, parent: ' body div frame head iframe p pre span '}, + section: {}, + select: {}, + small: {}, + span: {}, + source: {}, + strong: {}, + style: {parent: ' head ', empty: true}, + sub: {}, + sup: {}, + table: {}, + tbody: {parent: ' table '}, + td: {parent: ' tr '}, + textarea: {}, + tfoot: {parent: ' table '}, + th: {parent: ' tr '}, + thead: {parent: ' table '}, + time: {}, + title: {parent: ' head '}, + tr: {parent: ' table tbody thead tfoot '}, + tt: {}, + u: {}, + ul: {}, + 'var': {}, + video: {} + }, + + ids, // HTML ids + in_block, + indent, + itself, // JSLint itself + json_mode, + lex, // the tokenizer + lines, + lookahead, + node = array_to_object([ + 'Buffer', 'clearInterval', 'clearTimeout', 'console', 'exports', + 'global', 'module', 'process', 'querystring', 'require', + 'setInterval', 'setTimeout', '__dirname', '__filename' + ], false), + node_js, + numbery = array_to_object(['indexOf', 'lastIndexOf', 'search'], true), + next_token, + option, + predefined, // Global variables defined by option + prereg, + prev_token, + property, + regexp_flag = array_to_object(['g', 'i', 'm'], true), + return_this = function return_this() { + return this; + }, + rhino = array_to_object([ + 'defineClass', 'deserialize', 'gc', 'help', 'load', 'loadClass', + 'print', 'quit', 'readFile', 'readUrl', 'runCommand', 'seal', + 'serialize', 'spawn', 'sync', 'toint32', 'version' + ], false), + + scope, // An object containing an object for each variable in scope + semicolon_coda = array_to_object([';', '"', '\'', ')'], true), + src, + stack, + +// standard contains the global names that are provided by the +// ECMAScript standard. + + standard = array_to_object([ + 'Array', 'Boolean', 'Date', 'decodeURI', 'decodeURIComponent', + 'encodeURI', 'encodeURIComponent', 'Error', 'eval', 'EvalError', + 'Function', 'isFinite', 'isNaN', 'JSON', 'Math', 'Number', + 'Object', 'parseInt', 'parseFloat', 'RangeError', 'ReferenceError', + 'RegExp', 'String', 'SyntaxError', 'TypeError', 'URIError' + ], false), + + strict_mode, + syntax = {}, + tab, + token, + urls, + var_mode, + warnings, + + windows = array_to_object([ + 'ActiveXObject', 'CScript', 'Debug', 'Enumerator', 'System', + 'VBArray', 'WScript', 'WSH' + ], false), + +// xmode is used to adapt to the exceptions in html parsing. +// It can have these states: +// '' .js script file +// 'html' +// 'outer' +// 'script' +// 'style' +// 'scriptstring' +// 'styleproperty' + + xmode, + xquote, + +// Regular expressions. Some of these are stupidly long. + +// unsafe comment or string + ax = /@cc|<\/?|script|\]\s*\]|<\s*!|</i, +// carriage return, carriage return linefeed, or linefeed + crlfx = /\r\n?|\n/, +// unsafe characters that are silently deleted by one or more browsers + cx = /[\u0000-\u001f\u007f-\u009f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/, +// query characters for ids + dx = /[\[\]\/\\"'*<>.&:(){}+=#]/, +// html token + hx = /^\s*(['"=>\/&#]|<(?:\/|\!(?:--)?)?|[a-zA-Z][a-zA-Z0-9_\-:]*|[0-9]+|--)/, +// identifier + ix = /^([a-zA-Z_$][a-zA-Z0-9_$]*)$/, +// javascript url + jx = /^(?:javascript|jscript|ecmascript|vbscript|mocha|livescript)\s*:/i, +// star slash + lx = /\*\/|\/\*/, +// characters in strings that need escapement + nx = /[\u0000-\u001f'\\\u007f-\u009f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g, +// outer html token + ox = /[>&]|<[\/!]?|--/, +// attributes characters + qx = /[^a-zA-Z0-9+\-_\/. ]/, +// style + sx = /^\s*([{}:#%.=,>+\[\]@()"';]|[*$\^~]=|[a-zA-Z_][a-zA-Z0-9_\-]*|[0-9]+|<\/|\/\*)/, + ssx = /^\s*([@#!"'};:\-%.=,+\[\]()*_]|[a-zA-Z][a-zA-Z0-9._\-]*|\/\*?|\d+(?:\.\d+)?|<\/)/, +// token + tx = /^\s*([(){}\[\]\?.,:;'"~#@`]|={1,3}|\/(\*(jslint|properties|property|members?|globals?)?|=|\/)?|\*[\/=]?|\+(?:=|\++)?|-(?:=|-+)?|[\^%]=?|&[&=]?|\|[|=]?|>{1,3}=?|<(?:[\/=!]|\!(\[|--)?|<=?)?|\!={0,2}|[a-zA-Z_$][a-zA-Z0-9_$]*|[0-9]+(?:[xX][0-9a-fA-F]+|\.[0-9]*)?(?:[eE][+\-]?[0-9]+)?)/, +// url badness + ux = /&|\+|\u00AD|\.\.|\/\*|%[^;]|base64|url|expression|data|mailto|script/i, + + rx = { + outer: hx, + html: hx, + style: sx, + styleproperty: ssx + }; + + + function F() {} // Used by Object.create + +// Provide critical ES5 functions to ES3. + + if (typeof Array.prototype.filter !== 'function') { + Array.prototype.filter = function (f) { + var i, length = this.length, result = [], value; + for (i = 0; i < length; i += 1) { + try { + value = this[i]; + if (f(value)) { + result.push(value); + } + } catch (ignore) { + } + } + return result; + }; + } + + if (typeof Array.prototype.forEach !== 'function') { + Array.prototype.forEach = function (f) { + var i, length = this.length; + for (i = 0; i < length; i += 1) { + try { + f(this[i]); + } catch (ignore) { + } + } + }; + } + + if (typeof Array.isArray !== 'function') { + Array.isArray = function (o) { + return Object.prototype.toString.apply(o) === '[object Array]'; + }; + } + + if (!Object.prototype.hasOwnProperty.call(Object, 'create')) { + Object.create = function (o) { + F.prototype = o; + return new F(); + }; + } + + if (typeof Object.keys !== 'function') { + Object.keys = function (o) { + var array = [], key; + for (key in o) { + if (Object.prototype.hasOwnProperty.call(o, key)) { + array.push(key); + } + } + return array; + }; + } + + if (typeof String.prototype.entityify !== 'function') { + String.prototype.entityify = function () { + return this + .replace(/&/g, '&') + .replace(//g, '>'); + }; + } + + if (typeof String.prototype.isAlpha !== 'function') { + String.prototype.isAlpha = function () { + return (this >= 'a' && this <= 'z\uffff') || + (this >= 'A' && this <= 'Z\uffff'); + }; + } + + if (typeof String.prototype.isDigit !== 'function') { + String.prototype.isDigit = function () { + return (this >= '0' && this <= '9'); + }; + } + + if (typeof String.prototype.supplant !== 'function') { + String.prototype.supplant = function (o) { + return this.replace(/\{([^{}]*)\}/g, function (a, b) { + var replacement = o[b]; + return typeof replacement === 'string' || + typeof replacement === 'number' ? replacement : a; + }); + }; + } + + + function sanitize(a) { + +// Escapify a troublesome character. + + return escapes[a] || + '\\u' + ('0000' + a.charCodeAt().toString(16)).slice(-4); + } + + + function add_to_predefined(group) { + Object.keys(group).forEach(function (name) { + predefined[name] = group[name]; + }); + } + + + function assume() { + if (!option.safe) { + if (option.rhino) { + add_to_predefined(rhino); + option.rhino = false; + } + if (option.devel) { + add_to_predefined(devel); + option.devel = false; + } + if (option.browser) { + add_to_predefined(browser); + option.browser = false; + } + if (option.windows) { + add_to_predefined(windows); + option.windows = false; + } + if (option.node) { + add_to_predefined(node); + option.node = false; + node_js = true; + } + } + } + + +// Produce an error warning. + + function artifact(tok) { + if (!tok) { + tok = next_token; + } + return tok.number || tok.string; + } + + function quit(message, line, character) { + throw { + name: 'JSLintError', + line: line, + character: character, + message: bundle.scanned_a_b.supplant({ + a: message, + b: Math.floor((line / lines.length) * 100) + }) + }; + } + + function warn(message, offender, a, b, c, d) { + var character, line, warning; + offender = offender || next_token; // ~~ + line = offender.line || 0; + character = offender.from || 0; + warning = { + id: '(error)', + raw: bundle[message] || message, + evidence: lines[line - 1] || '', + line: line, + character: character, + a: a || (offender.id === '(number)' + ? String(offender.number) + : offender.string), + b: b, + c: c, + d: d + }; + warning.reason = warning.raw.supplant(warning); + JSLINT.errors.push(warning); + if (option.passfail) { + quit(bundle.stopping, line, character); + } + warnings += 1; + if (warnings >= option.maxerr) { + quit(bundle.too_many, line, character); + } + return warning; + } + + function warn_at(message, line, character, a, b, c, d) { + return warn(message, { + line: line, + from: character + }, a, b, c, d); + } + + function stop(message, offender, a, b, c, d) { + var warning = warn(message, offender, a, b, c, d); + quit(bundle.stopping, warning.line, warning.character); + } + + function stop_at(message, line, character, a, b, c, d) { + return stop(message, { + line: line, + from: character + }, a, b, c, d); + } + + function expected_at(at) { + if (!option.white && next_token.from !== at) { + warn('expected_a_at_b_c', next_token, '', at, + next_token.from); + } + } + + function aint(it, name, expected) { + if (it[name] !== expected) { + warn('expected_a_b', it, expected, it[name]); + return true; + } + return false; + } + + +// lexical analysis and token construction + + lex = (function lex() { + var character, c, from, length, line, pos, source_row; + +// Private lex methods + + function next_line() { + var at; + if (line >= lines.length) { + return false; + } + character = 1; + source_row = lines[line]; + line += 1; + at = source_row.search(/ \t/); + if (at >= 0) { + warn_at('mixed', line, at + 1); + } + source_row = source_row.replace(/\t/g, tab); + at = source_row.search(cx); + if (at >= 0) { + warn_at('unsafe', line, at); + } + if (option.maxlen && option.maxlen < source_row.length) { + warn_at('too_long', line, source_row.length); + } + return true; + } + +// Produce a token object. The token inherits from a syntax symbol. + + function it(type, value) { + var id, the_token; + if (type === '(string)' || type === '(range)') { + if (jx.test(value)) { + warn_at('url', line, from); + } + } + the_token = Object.create(syntax[( + type === '(punctuator)' || (type === '(identifier)' && + Object.prototype.hasOwnProperty.call(syntax, value)) + ? value + : type + )] || syntax['(error)']); + if (type === '(identifier)') { + the_token.identifier = true; + if (value === '__iterator__' || value === '__proto__') { + stop_at('reserved_a', line, from, value); + } else if (!option.nomen && + (value.charAt(0) === '_' || + value.charAt(value.length - 1) === '_')) { + warn_at('dangling_a', line, from, value); + } + } + if (type === '(number)') { + the_token.number = +value; + } else if (value !== undefined) { + the_token.string = String(value); + } + the_token.line = line; + the_token.from = from; + the_token.thru = character; + id = the_token.id; + prereg = id && ( + ('(,=:[!&|?{};'.indexOf(id.charAt(id.length - 1)) >= 0) || + id === 'return' || id === 'case' + ); + return the_token; + } + + function match(x) { + var exec = x.exec(source_row), first; + if (exec) { + length = exec[0].length; + first = exec[1]; + c = first.charAt(0); + source_row = source_row.slice(length); + from = character + length - first.length; + character += length; + return first; + } + } + + function string(x) { + var c, pos = 0, r = '', result; + + function hex(n) { + var i = parseInt(source_row.substr(pos + 1, n), 16); + pos += n; + if (i >= 32 && i <= 126 && + i !== 34 && i !== 92 && i !== 39) { + warn_at('unexpected_a', line, character, '\\'); + } + character += n; + c = String.fromCharCode(i); + } + + if (json_mode && x !== '"') { + warn_at('expected_a', line, character, '"'); + } + + if (xquote === x || (xmode === 'scriptstring' && !xquote)) { + return it('(punctuator)', x); + } + + for (;;) { + while (pos >= source_row.length) { + pos = 0; + if (xmode !== 'html' || !next_line()) { + stop_at('unclosed', line, from); + } + } + c = source_row.charAt(pos); + if (c === x) { + character += 1; + source_row = source_row.slice(pos + 1); + result = it('(string)', r); + result.quote = x; + return result; + } + if (c < ' ') { + if (c === '\n' || c === '\r') { + break; + } + warn_at('control_a', line, character + pos, + source_row.slice(0, pos)); + } else if (c === xquote) { + warn_at('bad_html', line, character + pos); + } else if (c === '<') { + if (option.safe && xmode === 'html') { + warn_at('adsafe_a', line, character + pos, c); + } else if (source_row.charAt(pos + 1) === '/' && (xmode || option.safe)) { + warn_at('expected_a_b', line, character, + '<\\/', '= '0' && c <= '7' ? 'octal_a' : 'unexpected_a', + line, character, '\\' + c); + } else { + c = descapes[c]; + } + } + } + } + r += c; + character += 1; + pos += 1; + } + } + + function number(snippet) { + var digit; + if (xmode !== 'style' && xmode !== 'styleproperty' && + source_row.charAt(0).isAlpha()) { + warn_at('expected_space_a_b', + line, character, c, source_row.charAt(0)); + } + if (c === '0') { + digit = snippet.charAt(1); + if (digit.isDigit()) { + if (token.id !== '.' && xmode !== 'styleproperty') { + warn_at('unexpected_a', line, character, snippet); + } + } else if (json_mode && (digit === 'x' || digit === 'X')) { + warn_at('unexpected_a', line, character, '0x'); + } + } + if (snippet.slice(snippet.length - 1) === '.') { + warn_at('trailing_decimal_a', line, character, snippet); + } + if (xmode !== 'style') { + digit = +snippet; + if (!isFinite(digit)) { + warn_at('bad_number', line, character, snippet); + } + snippet = digit; + } + return it('(number)', snippet); + } + + function comment(snippet) { + if (comments_off || src || (xmode && xmode !== 'script' && + xmode !== 'style' && xmode !== 'styleproperty')) { + warn_at('unexpected_comment', line, character); + } else if (xmode === 'script' && /<\//i.test(source_row)) { + warn_at('unexpected_a', line, character, '<\/'); + } else if (option.safe && ax.test(snippet)) { + warn_at('dangerous_comment', line, character); + } + } + + function regexp() { + var b, + bit, + captures = 0, + depth = 0, + flag = '', + high, + letter, + length = 0, + low, + potential, + quote, + result; + for (;;) { + b = true; + c = source_row.charAt(length); + length += 1; + switch (c) { + case '': + stop_at('unclosed_regexp', line, from); + return; + case '/': + if (depth > 0) { + warn_at('unescaped_a', line, from + length, '/'); + } + c = source_row.slice(0, length - 1); + potential = Object.create(regexp_flag); + for (;;) { + letter = source_row.charAt(length); + if (potential[letter] !== true) { + break; + } + potential[letter] = false; + length += 1; + flag += letter; + } + if (source_row.charAt(length).isAlpha()) { + stop_at('unexpected_a', line, from, source_row.charAt(length)); + } + character += length; + source_row = source_row.slice(length); + quote = source_row.charAt(0); + if (quote === '/' || quote === '*') { + stop_at('confusing_regexp', line, from); + } + result = it('(regexp)', c); + result.flag = flag; + return result; + case '\\': + c = source_row.charAt(length); + if (c < ' ') { + warn_at('control_a', line, from + length, String(c)); + } else if (c === '<') { + warn_at(bundle.unexpected_a, line, from + length, '\\'); + } + length += 1; + break; + case '(': + depth += 1; + b = false; + if (source_row.charAt(length) === '?') { + length += 1; + switch (source_row.charAt(length)) { + case ':': + case '=': + case '!': + length += 1; + break; + default: + warn_at(bundle.expected_a_b, line, from + length, + ':', source_row.charAt(length)); + } + } else { + captures += 1; + } + break; + case '|': + b = false; + break; + case ')': + if (depth === 0) { + warn_at('unescaped_a', line, from + length, ')'); + } else { + depth -= 1; + } + break; + case ' ': + pos = 1; + while (source_row.charAt(length) === ' ') { + length += 1; + pos += 1; + } + if (pos > 1) { + warn_at('use_braces', line, from + length, pos); + } + break; + case '[': + c = source_row.charAt(length); + if (c === '^') { + length += 1; + if (!option.regexp) { + warn_at('insecure_a', line, from + length, c); + } else if (source_row.charAt(length) === ']') { + stop_at('unescaped_a', line, from + length, '^'); + } + } + bit = false; + if (c === ']') { + warn_at('empty_class', line, from + length - 1); + bit = true; + } +klass: do { + c = source_row.charAt(length); + length += 1; + switch (c) { + case '[': + case '^': + warn_at('unescaped_a', line, from + length, c); + bit = true; + break; + case '-': + if (bit) { + bit = false; + } else { + warn_at('unescaped_a', line, from + length, '-'); + bit = true; + } + break; + case ']': + if (!bit) { + warn_at('unescaped_a', line, from + length - 1, '-'); + } + break klass; + case '\\': + c = source_row.charAt(length); + if (c < ' ') { + warn_at(bundle.control_a, line, from + length, String(c)); + } else if (c === '<') { + warn_at(bundle.unexpected_a, line, from + length, '\\'); + } + length += 1; + bit = true; + break; + case '/': + warn_at('unescaped_a', line, from + length - 1, '/'); + bit = true; + break; + case '<': + if (xmode === 'script') { + c = source_row.charAt(length); + if (c === '!' || c === '/') { + warn_at(bundle.html_confusion_a, line, + from + length, c); + } + } + bit = true; + break; + default: + bit = true; + } + } while (c); + break; + case '.': + if (!option.regexp) { + warn_at('insecure_a', line, from + length, c); + } + break; + case ']': + case '?': + case '{': + case '}': + case '+': + case '*': + warn_at('unescaped_a', line, from + length, c); + break; + case '<': + if (xmode === 'script') { + c = source_row.charAt(length); + if (c === '!' || c === '/') { + warn_at(bundle.html_confusion_a, line, from + length, c); + } + } + break; + } + if (b) { + switch (source_row.charAt(length)) { + case '?': + case '+': + case '*': + length += 1; + if (source_row.charAt(length) === '?') { + length += 1; + } + break; + case '{': + length += 1; + c = source_row.charAt(length); + if (c < '0' || c > '9') { + warn_at(bundle.expected_number_a, line, + from + length, c); + } + length += 1; + low = +c; + for (;;) { + c = source_row.charAt(length); + if (c < '0' || c > '9') { + break; + } + length += 1; + low = +c + (low * 10); + } + high = low; + if (c === ',') { + length += 1; + high = Infinity; + c = source_row.charAt(length); + if (c >= '0' && c <= '9') { + length += 1; + high = +c; + for (;;) { + c = source_row.charAt(length); + if (c < '0' || c > '9') { + break; + } + length += 1; + high = +c + (high * 10); + } + } + } + if (source_row.charAt(length) !== '}') { + warn_at(bundle.expected_a_b, line, from + length, + '}', c); + } else { + length += 1; + } + if (source_row.charAt(length) === '?') { + length += 1; + } + if (low > high) { + warn_at(bundle.not_greater, line, from + length, + low, high); + } + break; + } + } + } + c = source_row.slice(0, length - 1); + character += length; + source_row = source_row.slice(length); + return it('(regexp)', c); + } + +// Public lex methods + + return { + init: function (source) { + if (typeof source === 'string') { + lines = source.split(crlfx); + } else { + lines = source; + } + line = 0; + next_line(); + from = 1; + }, + + range: function (begin, end) { + var c, value = ''; + from = character; + if (source_row.charAt(0) !== begin) { + stop_at('expected_a_b', line, character, begin, + source_row.charAt(0)); + } + for (;;) { + source_row = source_row.slice(1); + character += 1; + c = source_row.charAt(0); + switch (c) { + case '': + stop_at('missing_a', line, character, c); + break; + case end: + source_row = source_row.slice(1); + character += 1; + return it('(range)', value); + case xquote: + case '\\': + warn_at('unexpected_a', line, character, c); + break; + } + value += c; + } + }, + +// token -- this is called by advance to get the next token. + + token: function () { + var c, i, snippet; + + for (;;) { + while (!source_row) { + if (!next_line()) { + return it('(end)'); + } + } + while (xmode === 'outer') { + i = source_row.search(ox); + if (i === 0) { + break; + } else if (i > 0) { + character += 1; + source_row = source_row.slice(i); + break; + } else { + if (!next_line()) { + return it('(end)', ''); + } + } + } + snippet = match(rx[xmode] || tx); + if (!snippet) { + if (source_row) { + if (source_row.charAt(0) === ' ') { + if (!option.white) { + warn_at('unexpected_a', line, character, + '(space)'); + } + character += 1; + source_row = ''; + } else { + stop_at('unexpected_a', line, character, + source_row.charAt(0)); + } + } + } else { + +// identifier + + c = snippet.charAt(0); + if (c.isAlpha() || c === '_' || c === '$') { + return it('(identifier)', snippet); + } + +// number + + if (c.isDigit()) { + return number(snippet); + } + switch (snippet) { + +// string + + case '"': + case "'": + return string(snippet); + +// // comment + + case '//': + comment(source_row); + source_row = ''; + break; + +// /* comment + + case '/*': + for (;;) { + i = source_row.search(lx); + if (i >= 0) { + break; + } + comment(source_row); + if (!next_line()) { + stop_at('unclosed_comment', line, character); + } + } + comment(source_row.slice(0, i)); + character += i + 2; + if (source_row.charAt(i) === '/') { + stop_at('nested_comment', line, character); + } + source_row = source_row.slice(i + 2); + break; + + case '': + break; +// / + case '/': + if (token.id === '/=') { + stop_at( + bundle.slash_equal, + line, + from + ); + } + return prereg + ? regexp() + : it('(punctuator)', snippet); + +// punctuator + + case ''); + } + character += 3; + source_row = source_row.slice(i + 3); + break; + case '#': + if (xmode === 'html' || xmode === 'styleproperty') { + for (;;) { + c = source_row.charAt(0); + if ((c < '0' || c > '9') && + (c < 'a' || c > 'f') && + (c < 'A' || c > 'F')) { + break; + } + character += 1; + source_row = source_row.slice(1); + snippet += c; + } + if (snippet.length !== 4 && snippet.length !== 7) { + warn_at('bad_color_a', line, + from + length, snippet); + } + return it('(color)', snippet); + } + return it('(punctuator)', snippet); + + default: + if (xmode === 'outer' && c === '&') { + character += 1; + source_row = source_row.slice(1); + for (;;) { + c = source_row.charAt(0); + character += 1; + source_row = source_row.slice(1); + if (c === ';') { + break; + } + if (!((c >= '0' && c <= '9') || + (c >= 'a' && c <= 'z') || + c === '#')) { + stop_at('bad_entity', line, from + length, + character); + } + } + break; + } + return it('(punctuator)', snippet); + } + } + } + } + }; + }()); + + + function add_label(token, kind, name) { + +// Define the symbol in the current function in the current scope. + + name = name || token.string; + +// Global variables cannot be created in the safe subset. If a global variable +// already exists, do nothing. If it is predefined, define it. + + if (funct === global_funct) { + if (option.safe) { + warn('adsafe_a', token, name); + } + if (typeof global_funct[name] !== 'string') { + token.writeable = typeof predefined[name] === 'boolean' + ? predefined[name] + : true; + token.funct = funct; + global_scope[name] = token; + } + if (kind === 'becoming') { + kind = 'var'; + } + +// Ordinary variables. + + } else { + +// Warn if the variable already exists. + + if (typeof funct[name] === 'string') { + if (funct[name] === 'undef') { + if (!option.undef) { + warn('used_before_a', token, name); + } + kind = 'var'; + } else { + warn('already_defined', token, name); + } + } else { + +// Add the symbol to the current function. + + token.funct = funct; + token.writeable = true; + scope[name] = token; + } + } + funct[name] = kind; + } + + + function peek(distance) { + +// Peek ahead to a future token. The distance is how far ahead to look. The +// default is the next token. + + var found, slot = 0; + + distance = distance || 0; + while (slot <= distance) { + found = lookahead[slot]; + if (!found) { + found = lookahead[slot] = lex.token(); + } + slot += 1; + } + return found; + } + + + function advance(id, match) { + +// Produce the next token, also looking for programming errors. + + if (indent) { + +// If indentation checking was requested, then inspect all of the line breakings. +// The var statement is tricky because the names might be aligned or not. We +// look at the first line break after the var to determine the programmer's +// intention. + + if (var_mode && next_token.line !== token.line) { + if ((var_mode !== indent || !next_token.edge) && + next_token.from === indent.at - + (next_token.edge ? option.indent : 0)) { + var dent = indent; + for (;;) { + dent.at -= option.indent; + if (dent === var_mode) { + break; + } + dent = dent.was; + } + dent.open = false; + } + var_mode = null; + } + if (next_token.id === '?' && indent.mode === ':' && + token.line !== next_token.line) { + indent.at -= option.indent; + } + if (indent.open) { + +// If the token is an edge. + + if (next_token.edge) { + if (next_token.edge === 'label') { + expected_at(1); + } else if (next_token.edge === 'case' || indent.mode === 'statement') { + expected_at(indent.at - option.indent); + } else if (indent.mode !== 'array' || next_token.line !== token.line) { + expected_at(indent.at); + } + +// If the token is not an edge, but is the first token on the line. + + } else if (next_token.line !== token.line) { + if (next_token.from < indent.at + (indent.mode === + 'expression' ? 0 : option.indent)) { + expected_at(indent.at + option.indent); + } + indent.wrap = true; + } + } else if (next_token.line !== token.line) { + if (next_token.edge) { + expected_at(indent.at); + } else { + indent.wrap = true; + if (indent.mode === 'statement' || indent.mode === 'var') { + expected_at(indent.at + option.indent); + } else if (next_token.from < indent.at + (indent.mode === + 'expression' ? 0 : option.indent)) { + expected_at(indent.at + option.indent); + } + } + } + } + + switch (token.id) { + case '(number)': + if (next_token.id === '.') { + warn('trailing_decimal_a'); + } + break; + case '-': + if (next_token.id === '-' || next_token.id === '--') { + warn('confusing_a'); + } + break; + case '+': + if (next_token.id === '+' || next_token.id === '++') { + warn('confusing_a'); + } + break; + } + if (token.id === '(string)' || token.identifier) { + anonname = token.string; + } + + if (id && next_token.id !== id) { + if (match) { + warn('expected_a_b_from_c_d', next_token, id, + match.id, match.line, artifact()); + } else if (!next_token.identifier || next_token.string !== id) { + warn('expected_a_b', next_token, id, artifact()); + } + } + prev_token = token; + token = next_token; + next_token = lookahead.shift() || lex.token(); + } + + + function advance_identifier(string) { + if (next_token.identifier && next_token.string === string) { + advance(); + } else { + warn('expected_a_b', next_token, string, artifact()); + } + } + + + function do_safe() { + if (option.adsafe) { + option.safe = true; + } + if (option.safe) { + option.browser = + option['continue'] = + option.css = + option.debug = + option.devel = + option.evil = + option.forin = + option.newcap = + option.nomen = + option.on = + option.rhino = + option.sloppy = + option.sub = + option.undef = + option.windows = false; + + + delete predefined.Array; + delete predefined.Date; + delete predefined.Function; + delete predefined.Object; + delete predefined['eval']; + + add_to_predefined({ + ADSAFE: false, + lib: false + }); + } + } + + + function do_globals() { + var name, writeable; + for (;;) { + if (next_token.id !== '(string)' && !next_token.identifier) { + return; + } + name = next_token.string; + advance(); + writeable = false; + if (next_token.id === ':') { + advance(':'); + switch (next_token.id) { + case 'true': + writeable = predefined[name] !== false; + advance('true'); + break; + case 'false': + advance('false'); + break; + default: + stop('unexpected_a'); + } + } + predefined[name] = writeable; + if (next_token.id !== ',') { + return; + } + advance(','); + } + } + + + function do_jslint() { + var name, value; + while (next_token.id === '(string)' || next_token.identifier) { + name = next_token.string; + if (!allowed_option[name]) { + stop('unexpected_a'); + } + advance(); + if (next_token.id !== ':') { + stop('expected_a_b', next_token, ':', artifact()); + } + advance(':'); + if (typeof allowed_option[name] === 'number') { + value = next_token.number; + if (value > allowed_option[name] || value <= 0 || + Math.floor(value) !== value) { + stop('expected_small_a'); + } + option[name] = value; + } else { + if (next_token.id === 'true') { + option[name] = true; + } else if (next_token.id === 'false') { + option[name] = false; + } else { + stop('unexpected_a'); + } + } + advance(); + if (next_token.id === ',') { + advance(','); + } + } + assume(); + } + + + function do_properties() { + var name; + option.properties = true; + for (;;) { + if (next_token.id !== '(string)' && !next_token.identifier) { + return; + } + name = next_token.string; + advance(); + if (next_token.id === ':') { + for (;;) { + advance(); + if (next_token.id !== '(string)' && !next_token.identifier) { + break; + } + } + } + property[name] = 0; + if (next_token.id !== ',') { + return; + } + advance(','); + } + } + + + directive = function directive() { + var command = this.id, + old_comments_off = comments_off, + old_indent = indent; + comments_off = true; + indent = null; + if (next_token.line === token.line && next_token.from === token.thru) { + warn('missing_space_a_b', next_token, artifact(token), artifact()); + } + if (lookahead.length > 0) { + warn('unexpected_a', this); + } + switch (command) { + case '/*properties': + case '/*property': + case '/*members': + case '/*member': + do_properties(); + break; + case '/*jslint': + if (option.safe) { + warn('adsafe_a', this); + } + do_jslint(); + break; + case '/*globals': + case '/*global': + if (option.safe) { + warn('adsafe_a', this); + } + do_globals(); + break; + default: + stop('unexpected_a', this); + } + comments_off = old_comments_off; + advance('*/'); + indent = old_indent; + }; + + +// Indentation intention + + function edge(mode) { + next_token.edge = indent ? indent.open && (mode || 'edge') : ''; + } + + + function step_in(mode) { + var open; + if (typeof mode === 'number') { + indent = { + at: +mode, + open: true, + was: indent + }; + } else if (!indent) { + indent = { + at: 1, + mode: 'statement', + open: true + }; + } else if (mode === 'statement') { + indent = { + at: indent.at, + open: true, + was: indent + }; + } else { + open = mode === 'var' || next_token.line !== token.line; + indent = { + at: (open || mode === 'control' + ? indent.at + option.indent + : indent.at) + (indent.wrap ? option.indent : 0), + mode: mode, + open: open, + was: indent + }; + if (mode === 'var' && open) { + var_mode = indent; + } + } + } + + function step_out(id, symbol) { + if (id) { + if (indent && indent.open) { + indent.at -= option.indent; + edge(); + } + advance(id, symbol); + } + if (indent) { + indent = indent.was; + } + } + +// Functions for conformance of whitespace. + + function one_space(left, right) { + left = left || token; + right = right || next_token; + if (right.id !== '(end)' && !option.white && + (token.line !== right.line || + token.thru + 1 !== right.from)) { + warn('expected_space_a_b', right, artifact(token), artifact(right)); + } + } + + function one_space_only(left, right) { + left = left || token; + right = right || next_token; + if (right.id !== '(end)' && (left.line !== right.line || + (!option.white && left.thru + 1 !== right.from))) { + warn('expected_space_a_b', right, artifact(left), artifact(right)); + } + } + + function no_space(left, right) { + left = left || token; + right = right || next_token; + if ((!option.white || xmode === 'styleproperty' || xmode === 'style') && + left.thru !== right.from && left.line === right.line) { + warn('unexpected_space_a_b', right, artifact(left), artifact(right)); + } + } + + function no_space_only(left, right) { + left = left || token; + right = right || next_token; + if (right.id !== '(end)' && (left.line !== right.line || + (!option.white && left.thru !== right.from))) { + warn('unexpected_space_a_b', right, artifact(left), artifact(right)); + } + } + + function spaces(left, right) { + if (!option.white) { + left = left || token; + right = right || next_token; + if (left.thru === right.from && left.line === right.line) { + warn('missing_space_a_b', right, artifact(left), artifact(right)); + } + } + } + + function comma() { + if (next_token.id !== ',') { + warn_at('expected_a_b', token.line, token.thru, ',', artifact()); + } else { + if (!option.white) { + no_space_only(); + } + advance(','); + spaces(); + } + } + + + function semicolon() { + if (next_token.id !== ';') { + warn_at('expected_a_b', token.line, token.thru, ';', artifact()); + } else { + if (!option.white) { + no_space_only(); + } + advance(';'); + if (semicolon_coda[next_token.id] !== true) { + spaces(); + } + } + } + + function use_strict() { + if (next_token.string === 'use strict') { + if (strict_mode) { + warn('unnecessary_use'); + } + edge(); + advance(); + semicolon(); + strict_mode = true; + option.undef = false; + return true; + } + return false; + } + + + function are_similar(a, b) { + if (a === b) { + return true; + } + if (Array.isArray(a)) { + if (Array.isArray(b) && a.length === b.length) { + var i; + for (i = 0; i < a.length; i += 1) { + if (!are_similar(a[i], b[i])) { + return false; + } + } + return true; + } + return false; + } + if (Array.isArray(b)) { + return false; + } + if (a.id === '(number)' && b.id === '(number)') { + return a.number === b.number; + } + if (a.arity === b.arity && a.string === b.string) { + switch (a.arity) { + case 'prefix': + case 'suffix': + case undefined: + return a.id === b.id && are_similar(a.first, b.first); + case 'infix': + return are_similar(a.first, b.first) && + are_similar(a.second, b.second); + case 'ternary': + return are_similar(a.first, b.first) && + are_similar(a.second, b.second) && + are_similar(a.third, b.third); + case 'function': + case 'regexp': + return false; + default: + return true; + } + } else { + if (a.id === '.' && b.id === '[' && b.arity === 'infix') { + return a.second.string === b.second.string && b.second.id === '(string)'; + } + if (a.id === '[' && a.arity === 'infix' && b.id === '.') { + return a.second.string === b.second.string && a.second.id === '(string)'; + } + } + return false; + } + + +// This is the heart of JSLINT, the Pratt parser. In addition to parsing, it +// is looking for ad hoc lint patterns. We add .fud to Pratt's model, which is +// like .nud except that it is only used on the first token of a statement. +// Having .fud makes it much easier to define statement-oriented languages like +// JavaScript. I retained Pratt's nomenclature. + +// .nud Null denotation +// .fud First null denotation +// .led Left denotation +// lbp Left binding power +// rbp Right binding power + +// They are elements of the parsing method called Top Down Operator Precedence. + + function expression(rbp, initial) { + +// rbp is the right binding power. +// initial indicates that this is the first expression of a statement. + + var left; + if (next_token.id === '(end)') { + stop('unexpected_a', token, next_token.id); + } + advance(); + if (option.safe && scope[token.string] && + scope[token.string] === global_scope[token.string] && + (next_token.id !== '(' && next_token.id !== '.')) { + warn('adsafe_a', token); + } + if (initial) { + anonname = 'anonymous'; + funct['(verb)'] = token.string; + } + if (initial === true && token.fud) { + left = token.fud(); + } else { + if (token.nud) { + left = token.nud(); + } else { + if (next_token.id === '(number)' && token.id === '.') { + warn('leading_decimal_a', token, artifact()); + advance(); + return token; + } + stop('expected_identifier_a', token, token.id); + } + while (rbp < next_token.lbp) { + advance(); + if (token.led) { + left = token.led(left); + } else { + stop('expected_operator_a', token, token.id); + } + } + } + return left; + } + + +// Functional constructors for making the symbols that will be inherited by +// tokens. + + function symbol(s, p) { + var x = syntax[s]; + if (!x || typeof x !== 'object') { + syntax[s] = x = { + id: s, + lbp: p || 0, + string: s + }; + } + return x; + } + + function postscript(x) { + x.postscript = true; + return x; + } + + function ultimate(s) { + var x = symbol(s, 0); + x.from = 1; + x.thru = 1; + x.line = 0; + x.edge = 'edge'; + s.string = s; + return postscript(x); + } + + + function stmt(s, f) { + var x = symbol(s); + x.identifier = x.reserved = true; + x.fud = f; + return x; + } + + function labeled_stmt(s, f) { + var x = stmt(s, f); + x.labeled = true; + } + + function disrupt_stmt(s, f) { + var x = stmt(s, f); + x.disrupt = true; + } + + + function reserve_name(x) { + var c = x.id.charAt(0); + if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')) { + x.identifier = x.reserved = true; + } + return x; + } + + + function prefix(s, f) { + var x = symbol(s, 150); + reserve_name(x); + x.nud = typeof f === 'function' + ? f + : function () { + if (s === 'typeof') { + one_space(); + } else { + no_space_only(); + } + this.first = expression(150); + this.arity = 'prefix'; + if (this.id === '++' || this.id === '--') { + if (!option.plusplus) { + warn('unexpected_a', this); + } else if ((!this.first.identifier || this.first.reserved) && + this.first.id !== '.' && this.first.id !== '[') { + warn('bad_operand', this); + } + } + return this; + }; + return x; + } + + + function type(s, t, nud) { + var x = symbol(s); + x.arity = t; + if (nud) { + x.nud = nud; + } + return x; + } + + + function reserve(s, f) { + var x = symbol(s); + x.identifier = x.reserved = true; + if (typeof f === 'function') { + x.nud = f; + } + return x; + } + + + function constant(name) { + var x = reserve(name); + x.string = name; + x.nud = return_this; + return x; + } + + + function reservevar(s, v) { + return reserve(s, function () { + if (typeof v === 'function') { + v(this); + } + return this; + }); + } + + + function infix(s, p, f, w) { + var x = symbol(s, p); + reserve_name(x); + x.led = function (left) { + this.arity = 'infix'; + if (!w) { + spaces(prev_token, token); + spaces(); + } + if (!option.bitwise && this.bitwise) { + warn('unexpected_a', this); + } + if (typeof f === 'function') { + return f(left, this); + } + this.first = left; + this.second = expression(p); + return this; + }; + return x; + } + + function expected_relation(node, message) { + if (node.assign) { + warn(message || bundle.conditional_assignment, node); + } + return node; + } + + function expected_condition(node, message) { + switch (node.id) { + case '[': + case '-': + if (node.arity !== 'infix') { + warn(message || bundle.weird_condition, node); + } + break; + case 'false': + case 'function': + case 'Infinity': + case 'NaN': + case 'null': + case 'true': + case 'undefined': + case 'void': + case '(number)': + case '(regexp)': + case '(string)': + case '{': + warn(message || bundle.weird_condition, node); + break; + case '(': + if (node.first.id === '.' && numbery[node.first.second.string] === true) { + warn(message || bundle.weird_condition, node); + } + break; + } + return node; + } + + function check_relation(node) { + switch (node.arity) { + case 'prefix': + switch (node.id) { + case '{': + case '[': + warn('unexpected_a', node); + break; + case '!': + warn('confusing_a', node); + break; + } + break; + case 'function': + case 'regexp': + warn('unexpected_a', node); + break; + default: + if (node.id === 'NaN') { + warn('isNaN', node); + } + } + return node; + } + + + function relation(s, eqeq) { + return infix(s, 100, function (left, that) { + check_relation(left); + if (eqeq && !option.eqeq) { + warn('expected_a_b', that, eqeq, that.id); + } + var right = expression(100); + if (are_similar(left, right) || + ((left.id === '(string)' || left.id === '(number)') && + (right.id === '(string)' || right.id === '(number)'))) { + warn('weird_relation', that); + } + that.first = left; + that.second = check_relation(right); + return that; + }); + } + + + function assignop(s, op) { + var x = infix(s, 20, function (left, that) { + var l; + that.first = left; + if (left.identifier) { + if (scope[left.string]) { + if (scope[left.string].writeable === false) { + warn('read_only', left); + } + } else { + stop('read_only'); + } + if (funct['(params)']) { + funct['(params)'].forEach(function (value) { + if (value.string === left.string) { + value.assign = true; + } + }); + } + } else if (option.safe) { + l = left; + do { + if (typeof predefined[l.string] === 'boolean') { + warn('adsafe_a', l); + } + l = l.first; + } while (l); + } + if (left === syntax['function']) { + warn('identifier_function', token); + } + if (left.id === '.' || left.id === '[') { + if (!left.first || left.first.string === 'arguments') { + warn('bad_assignment', that); + } + } else if (left.identifier) { + if (!left.reserved && funct[left.string] === 'exception') { + warn('assign_exception', left); + } + } else { + warn('bad_assignment', that); + } + that.second = expression(19); + if (that.id === '=' && are_similar(that.first, that.second)) { + warn('weird_assignment', that); + } + return that; + }); + x.assign = true; + if (op) { + if (syntax[op].bitwise) { + x.bitwise = true; + } + } + return x; + } + + + function bitwise(s, p) { + var x = infix(s, p, 'number'); + x.bitwise = true; + return x; + } + + + function suffix(s) { + var x = symbol(s, 150); + x.led = function (left) { + no_space_only(prev_token, token); + if (!option.plusplus) { + warn('unexpected_a', this); + } else if ((!left.identifier || left.reserved) && + left.id !== '.' && left.id !== '[') { + warn('bad_operand', this); + } + this.first = left; + this.arity = 'suffix'; + return this; + }; + return x; + } + + + function optional_identifier() { + if (next_token.identifier) { + advance(); + if (option.safe && banned[token.string]) { + warn('adsafe_a', token); + } else if (token.reserved && !option.es5) { + warn('expected_identifier_a_reserved', token); + } + return token.string; + } + } + + + function identifier() { + var i = optional_identifier(); + if (!i) { + stop(token.id === 'function' && next_token.id === '(' + ? 'name_function' + : 'expected_identifier_a'); + } + return i; + } + + + function statement() { + + var label, old_scope = scope, the_statement; + +// We don't like the empty statement. + + if (next_token.id === ';') { + warn('unexpected_a'); + semicolon(); + return; + } + +// Is this a labeled statement? + + if (next_token.identifier && !next_token.reserved && peek().id === ':') { + edge('label'); + label = next_token; + advance(); + advance(':'); + scope = Object.create(old_scope); + add_label(label, 'label'); + if (next_token.labeled !== true || funct === global_funct) { + stop('unexpected_label_a', label); + } else if (jx.test(label.string + ':')) { + warn('url', label); + } + next_token.label = label; + } + +// Parse the statement. + + if (token.id !== 'else') { + edge(); + } + step_in('statement'); + the_statement = expression(0, true); + if (the_statement) { + +// Look for the final semicolon. + + if (the_statement.arity === 'statement') { + if (the_statement.id === 'switch' || + (the_statement.block && the_statement.id !== 'do')) { + spaces(); + } else { + semicolon(); + } + } else { + +// If this is an expression statement, determine if it is acceptable. +// We do not like +// new Blah(); +// statments. If it is to be used at all, new should only be used to make +// objects, not side effects. The expression statements we do like do +// assignment or invocation or delete. + + if (the_statement.id === '(') { + if (the_statement.first.id === 'new') { + warn('bad_new'); + } + } else if (!the_statement.assign && + the_statement.id !== 'delete' && + the_statement.id !== '++' && + the_statement.id !== '--') { + warn('assignment_function_expression', token); + } + semicolon(); + } + } + step_out(); + scope = old_scope; + return the_statement; + } + + + function statements() { + var array = [], disruptor, the_statement; + +// A disrupt statement may not be followed by any other statement. +// If the last statement is disrupt, then the sequence is disrupt. + + while (next_token.postscript !== true) { + if (next_token.id === ';') { + warn('unexpected_a', next_token); + semicolon(); + } else { + if (next_token.string === 'use strict') { + if ((!node_js && xmode !== 'script') || funct !== global_funct || array.length > 0) { + warn('function_strict'); + } + use_strict(); + } + if (disruptor) { + warn('unreachable_a_b', next_token, next_token.string, + disruptor.string); + disruptor = null; + } + the_statement = statement(); + if (the_statement) { + array.push(the_statement); + if (the_statement.disrupt) { + disruptor = the_statement; + array.disrupt = true; + } + } + } + } + return array; + } + + + function block(ordinary) { + +// array block is array sequence of statements wrapped in braces. +// ordinary is false for function bodies and try blocks. +// ordinary is true for if statements, while, etc. + + var array, + curly = next_token, + old_in_block = in_block, + old_scope = scope, + old_strict_mode = strict_mode; + + in_block = ordinary; + scope = Object.create(scope); + spaces(); + if (next_token.id === '{') { + advance('{'); + step_in(); + if (!ordinary && !use_strict() && !old_strict_mode && + !option.sloppy && funct['(context)'] === global_funct) { + warn('missing_use_strict'); + } + array = statements(); + strict_mode = old_strict_mode; + step_out('}', curly); + } else if (!ordinary) { + stop('expected_a_b', next_token, '{', artifact()); + } else { + warn('expected_a_b', next_token, '{', artifact()); + array = [statement()]; + array.disrupt = array[0].disrupt; + } + funct['(verb)'] = null; + scope = old_scope; + in_block = old_in_block; + if (ordinary && array.length === 0) { + warn('empty_block'); + } + return array; + } + + + function tally_property(name) { + if (option.properties && typeof property[name] !== 'number') { + warn('unexpected_property_a', token, name); + } + if (typeof property[name] === 'number') { + property[name] += 1; + } else { + property[name] = 1; + } + } + + +// ECMAScript parser + + syntax['(identifier)'] = { + id: '(identifier)', + lbp: 0, + identifier: true, + nud: function () { + var name = this.string, + variable = scope[name], + site, + writeable; + +// If the variable is not in scope, then we may have an undeclared variable. +// Check the predefined list. If it was predefined, create the global +// variable. + + if (typeof variable !== 'object') { + writeable = predefined[name]; + if (typeof writeable === 'boolean') { + global_scope[name] = variable = { + string: name, + writeable: writeable, + funct: global_funct + }; + global_funct[name] = 'var'; + +// But if the variable is not in scope, and is not predefined, and if we are not +// in the global scope, then we have an undefined variable error. + + } else { + if (!option.undef) { + warn('used_before_a', token); + } + scope[name] = variable = { + string: name, + writeable: true, + funct: funct + }; + funct[name] = 'undef'; + } + + } + site = variable.funct; + +// The name is in scope and defined in the current function. + + if (funct === site) { + +// Change 'unused' to 'var', and reject labels. + + switch (funct[name]) { + case 'becoming': + warn('unexpected_a', token); + funct[name] = 'var'; + break; + case 'unused': + funct[name] = 'var'; + break; + case 'unparam': + funct[name] = 'parameter'; + break; + case 'unction': + funct[name] = 'function'; + break; + case 'label': + warn('a_label', token, name); + break; + } + +// If the name is already defined in the current +// function, but not as outer, then there is a scope error. + + } else { + switch (funct[name]) { + case 'closure': + case 'function': + case 'var': + case 'unused': + warn('a_scope', token, name); + break; + case 'label': + warn('a_label', token, name); + break; + case 'outer': + case 'global': + break; + default: + +// If the name is defined in an outer function, make an outer entry, and if +// it was unused, make it var. + + switch (site[name]) { + case 'becoming': + case 'closure': + case 'function': + case 'parameter': + case 'unction': + case 'unused': + case 'var': + site[name] = 'closure'; + funct[name] = site === global_funct + ? 'global' + : 'outer'; + break; + case 'unparam': + site[name] = 'parameter'; + funct[name] = 'outer'; + break; + case 'undef': + funct[name] = 'undef'; + break; + case 'label': + warn('a_label', token, name); + break; + } + } + } + return this; + }, + led: function () { + stop('expected_operator_a'); + } + }; + +// Build the syntax table by declaring the syntactic elements. + + type('(array)', 'array'); + type('(color)', 'color'); + type('(function)', 'function'); + type('(number)', 'number', return_this); + type('(object)', 'object'); + type('(string)', 'string', return_this); + type('(boolean)', 'boolean', return_this); + type('(range)', 'range'); + type('(regexp)', 'regexp', return_this); + + ultimate('(begin)'); + ultimate('(end)'); + ultimate('(error)'); + postscript(symbol(''); + postscript(symbol('}')); + symbol(')'); + symbol(']'); + postscript(symbol('"')); + postscript(symbol('\'')); + symbol(';'); + symbol(':'); + symbol(','); + symbol('#'); + symbol('@'); + symbol('*/'); + postscript(reserve('case')); + reserve('catch'); + postscript(reserve('default')); + reserve('else'); + reserve('finally'); + + reservevar('arguments', function (x) { + if (strict_mode && funct === global_funct) { + warn('strict', x); + } else if (option.safe) { + warn('adsafe_a', x); + } + funct['(arguments)'] = true; + }); + reservevar('eval', function (x) { + if (option.safe) { + warn('adsafe_a', x); + } + }); + constant('false', 'boolean'); + constant('Infinity', 'number'); + constant('NaN', 'number'); + constant('null', ''); + reservevar('this', function (x) { + if (option.safe) { + warn('adsafe_a', x); + } else if (strict_mode && funct['(token)'] && + (funct['(token)'].arity === 'statement' && + funct['(name)'].charAt(0) > 'Z')) { + warn('strict', x); + } + }); + constant('true', 'boolean'); + constant('undefined', ''); + + infix('?', 30, function (left, that) { + step_in('?'); + that.first = expected_condition(expected_relation(left)); + that.second = expression(0); + spaces(); + step_out(); + var colon = next_token; + advance(':'); + step_in(':'); + spaces(); + that.third = expression(10); + that.arity = 'ternary'; + if (are_similar(that.second, that.third)) { + warn('weird_ternary', colon); + } else if (are_similar(that.first, that.second)) { + warn('use_or', that); + } + step_out(); + return that; + }); + + infix('||', 40, function (left, that) { + function paren_check(that) { + if (that.id === '&&' && !that.paren) { + warn('and', that); + } + return that; + } + + that.first = paren_check(expected_condition(expected_relation(left))); + that.second = paren_check(expected_relation(expression(40))); + if (are_similar(that.first, that.second)) { + warn('weird_condition', that); + } + return that; + }); + + infix('&&', 50, function (left, that) { + that.first = expected_condition(expected_relation(left)); + that.second = expected_relation(expression(50)); + if (are_similar(that.first, that.second)) { + warn('weird_condition', that); + } + return that; + }); + + prefix('void', function () { + this.first = expression(0); + this.arity = 'prefix'; + if (option.es5) { + warn('expected_a_b', this, 'undefined', 'void'); + } else if (this.first.number !== 0) { + warn('expected_a_b', this.first, '0', artifact(this.first)); + } + return this; + }); + + bitwise('|', 70); + bitwise('^', 80); + bitwise('&', 90); + + relation('==', '==='); + relation('==='); + relation('!=', '!=='); + relation('!=='); + relation('<'); + relation('>'); + relation('<='); + relation('>='); + + bitwise('<<', 120); + bitwise('>>', 120); + bitwise('>>>', 120); + + infix('in', 120, function (left, that) { + warn('infix_in', that); + that.left = left; + that.right = expression(130); + return that; + }); + infix('instanceof', 120); + infix('+', 130, function (left, that) { + if (left.id === '(number)') { + if (left.number === 0) { + warn('unexpected_a', left, '0'); + } + } else if (left.id === '(string)') { + if (left.string === '') { + warn('expected_a_b', left, 'String', '\'\''); + } + } + var right = expression(130); + if (right.id === '(number)') { + if (right.number === 0) { + warn('unexpected_a', right, '0'); + } + } else if (right.id === '(string)') { + if (right.string === '') { + warn('expected_a_b', right, 'String', '\'\''); + } + } + if (left.id === right.id) { + if (left.id === '(string)' || left.id === '(number)') { + if (left.id === '(string)') { + left.string += right.string; + if (jx.test(left.string)) { + warn('url', left); + } + } else { + left.number += right.number; + } + left.thru = right.thru; + return left; + } + } + that.first = left; + that.second = right; + return that; + }); + prefix('+', 'num'); + prefix('+++', function () { + warn('confusing_a', token); + this.first = expression(150); + this.arity = 'prefix'; + return this; + }); + infix('+++', 130, function (left) { + warn('confusing_a', token); + this.first = left; + this.second = expression(130); + return this; + }); + infix('-', 130, function (left, that) { + if ((left.id === '(number)' && left.number === 0) || left.id === '(string)') { + warn('unexpected_a', left); + } + var right = expression(130); + if ((right.id === '(number)' && right.number === 0) || right.id === '(string)') { + warn('unexpected_a', right); + } + if (left.id === right.id && left.id === '(number)') { + left.number -= right.number; + left.thru = right.thru; + return left; + } + that.first = left; + that.second = right; + return that; + }); + prefix('-'); + prefix('---', function () { + warn('confusing_a', token); + this.first = expression(150); + this.arity = 'prefix'; + return this; + }); + infix('---', 130, function (left) { + warn('confusing_a', token); + this.first = left; + this.second = expression(130); + return this; + }); + infix('*', 140, function (left, that) { + if ((left.id === '(number)' && (left.number === 0 || left.number === 1)) || left.id === '(string)') { + warn('unexpected_a', left); + } + var right = expression(140); + if ((right.id === '(number)' && (right.number === 0 || right.number === 1)) || right.id === '(string)') { + warn('unexpected_a', right); + } + if (left.id === right.id && left.id === '(number)') { + left.number *= right.number; + left.thru = right.thru; + return left; + } + that.first = left; + that.second = right; + return that; + }); + infix('/', 140, function (left, that) { + if ((left.id === '(number)' && left.number === 0) || left.id === '(string)') { + warn('unexpected_a', left); + } + var right = expression(140); + if ((right.id === '(number)' && (right.number === 0 || right.number === 1)) || right.id === '(string)') { + warn('unexpected_a', right); + } + if (left.id === right.id && left.id === '(number)') { + left.number /= right.number; + left.thru = right.thru; + return left; + } + that.first = left; + that.second = right; + return that; + }); + infix('%', 140, function (left, that) { + if ((left.id === '(number)' && (left.number === 0 || left.number === 1)) || left.id === '(string)') { + warn('unexpected_a', left); + } + var right = expression(140); + if ((right.id === '(number)' && right.number === 0) || right.id === '(string)') { + warn('unexpected_a', right); + } + if (left.id === right.id && left.id === '(number)') { + left.number %= right.number; + left.thru = right.thru; + return left; + } + that.first = left; + that.second = right; + return that; + }); + + suffix('++'); + prefix('++'); + + suffix('--'); + prefix('--'); + prefix('delete', function () { + one_space(); + var p = expression(0); + if (!p || (p.id !== '.' && p.id !== '[')) { + warn('deleted'); + } + this.first = p; + return this; + }); + + + prefix('~', function () { + no_space_only(); + if (!option.bitwise) { + warn('unexpected_a', this); + } + expression(150); + return this; + }); + prefix('!', function () { + no_space_only(); + this.first = expected_condition(expression(150)); + this.arity = 'prefix'; + if (bang[this.first.id] === true || this.first.assign) { + warn('confusing_a', this); + } + return this; + }); + prefix('typeof', null); + prefix('new', function () { + one_space(); + var c = expression(160), n, p, v; + this.first = c; + if (c.id !== 'function') { + if (c.identifier) { + switch (c.string) { + case 'Object': + warn('use_object', token); + break; + case 'Array': + if (next_token.id === '(') { + p = next_token; + p.first = this; + advance('('); + if (next_token.id !== ')') { + n = expression(0); + p.second = [n]; + if (n.id !== '(number)' || next_token.id === ',') { + warn('use_array', p); + } + while (next_token.id === ',') { + advance(','); + p.second.push(expression(0)); + } + } else { + warn('use_array', token); + } + advance(')', p); + return p; + } + warn('use_array', token); + break; + case 'Number': + case 'String': + case 'Boolean': + case 'Math': + case 'JSON': + warn('not_a_constructor', c); + break; + case 'Function': + if (!option.evil) { + warn('function_eval'); + } + break; + case 'Date': + case 'RegExp': + case 'this': + break; + default: + if (c.id !== 'function') { + v = c.string.charAt(0); + if (!option.newcap && (v < 'A' || v > 'Z')) { + warn('constructor_name_a', token); + } + } + } + } else { + if (c.id !== '.' && c.id !== '[' && c.id !== '(') { + warn('bad_constructor', token); + } + } + } else { + warn('weird_new', this); + } + if (next_token.id !== '(') { + warn('missing_a', next_token, '()'); + } + return this; + }); + + infix('(', 160, function (left, that) { + var p; + if (indent && indent.mode === 'expression') { + no_space(prev_token, token); + } else { + no_space_only(prev_token, token); + } + if (!left.immed && left.id === 'function') { + warn('wrap_immediate'); + } + p = []; + if (left.identifier) { + if (left.string.match(/^[A-Z]([A-Z0-9_$]*[a-z][A-Za-z0-9_$]*)?$/)) { + if (left.string !== 'Number' && left.string !== 'String' && + left.string !== 'Boolean' && left.string !== 'Date') { + if (left.string === 'Math' || left.string === 'JSON') { + warn('not_a_function', left); + } else if (left.string === 'Object') { + warn('use_object', token); + } else if (left.string === 'Array' || !option.newcap) { + warn('missing_a', left, 'new'); + } + } + } + } else if (left.id === '.') { + if (option.safe && left.first.string === 'Math' && + left.second === 'random') { + warn('adsafe_a', left); + } else if (left.second.string === 'split' && + left.first.id === '(string)') { + warn('use_array', left.second); + } + } + step_in(); + if (next_token.id !== ')') { + no_space(); + for (;;) { + edge(); + p.push(expression(10)); + if (next_token.id !== ',') { + break; + } + comma(); + } + } + no_space(); + step_out(')', that); + if (typeof left === 'object') { + if (left.string === 'parseInt' && p.length === 1) { + warn('radix', left); + } + if (!option.evil) { + if (left.string === 'eval' || left.string === 'Function' || + left.string === 'execScript') { + warn('evil', left); + } else if (p[0] && p[0].id === '(string)' && + (left.string === 'setTimeout' || + left.string === 'setInterval')) { + warn('implied_evil', left); + } + } + if (!left.identifier && left.id !== '.' && left.id !== '[' && + left.id !== '(' && left.id !== '&&' && left.id !== '||' && + left.id !== '?') { + warn('bad_invocation', left); + } + } + that.first = left; + that.second = p; + return that; + }, true); + + prefix('(', function () { + step_in('expression'); + no_space(); + edge(); + if (next_token.id === 'function') { + next_token.immed = true; + } + var value = expression(0); + value.paren = true; + no_space(); + step_out(')', this); + if (value.id === 'function') { + switch (next_token.id) { + case '(': + warn('move_invocation'); + break; + case '.': + case '[': + warn('unexpected_a'); + break; + default: + warn('bad_wrap', this); + } + } + return value; + }); + + infix('.', 170, function (left, that) { + no_space(prev_token, token); + no_space(); + var name = identifier(); + if (typeof name === 'string') { + tally_property(name); + } + that.first = left; + that.second = token; + if (left && left.string === 'arguments' && + (name === 'callee' || name === 'caller')) { + warn('avoid_a', left, 'arguments.' + name); + } else if (!option.evil && left && left.string === 'document' && + (name === 'write' || name === 'writeln')) { + warn('write_is_wrong', left); + } else if (!option.stupid && name.indexOf('Sync') > 0) { + warn('sync_a', token); + } else if (option.adsafe) { + if (!adsafe_top && left.string === 'ADSAFE') { + if (name === 'id' || name === 'lib') { + warn('adsafe_a', that); + } else if (name === 'go') { + if (xmode !== 'script') { + warn('adsafe_a', that); + } else if (adsafe_went || next_token.id !== '(' || + peek(0).id !== '(string)' || + peek(0).string !== adsafe_id || + peek(1).id !== ',') { + stop('adsafe_a', that, 'go'); + } + adsafe_went = true; + adsafe_may = false; + } + } + adsafe_top = false; + } + if (!option.evil && (name === 'eval' || name === 'execScript')) { + warn('evil'); + } else if (option.safe) { + for (;;) { + if (banned[name] === true) { + warn('adsafe_a', token, name); + } + if (typeof predefined[left.string] !== 'boolean' || //// check for writeable + next_token.id === '(') { + break; + } + if (next_token.id !== '.') { + warn('adsafe_a', that); + break; + } + advance('.'); + token.first = that; + token.second = name; + that = token; + name = identifier(); + if (typeof name === 'string') { + tally_property(name); + } + } + } + return that; + }, true); + + infix('[', 170, function (left, that) { + var e, s; + no_space_only(prev_token, token); + no_space(); + step_in(); + edge(); + e = expression(0); + switch (e.id) { + case '(number)': + if (e.id === '(number)' && left.id === 'arguments') { + warn('use_param', left); + } + break; + case '(string)': + if (option.safe && (banned[e.string] || + e.string.charAt(0) === '_' || e.string.slice(-1) === '_')) { + warn('adsafe_subscript_a', e); + } else if (!option.evil && + (e.string === 'eval' || e.string === 'execScript')) { + warn('evil', e); + } else if (!option.sub && ix.test(e.string)) { + s = syntax[e.string]; + if (!s || !s.reserved) { + warn('subscript', e); + } + } + tally_property(e.string); + break; + default: + if (option.safe) { + if ((e.id !== '+' || e.arity !== 'prefix') && + e.id !== '-' && e.id !== '*') { + warn('adsafe_subscript_a', e); + } + } + } + step_out(']', that); + no_space(prev_token, token); + that.first = left; + that.second = e; + return that; + }, true); + + prefix('[', function () { + this.arity = 'prefix'; + this.first = []; + step_in('array'); + while (next_token.id !== '(end)') { + while (next_token.id === ',') { + warn('unexpected_a', next_token); + advance(','); + } + if (next_token.id === ']') { + break; + } + indent.wrap = false; + edge(); + this.first.push(expression(10)); + if (next_token.id === ',') { + comma(); + if (next_token.id === ']' && !option.es5) { + warn('unexpected_a', token); + break; + } + } else { + break; + } + } + step_out(']', this); + return this; + }, 170); + + + function property_name() { + var id = optional_identifier(true); + if (!id) { + if (next_token.id === '(string)') { + id = next_token.string; + if (option.safe) { + if (banned[id]) { + warn('adsafe_a'); + } else if (id.charAt(0) === '_' || + id.charAt(id.length - 1) === '_') { + warn('dangling_a'); + } + } + advance(); + } else if (next_token.id === '(number)') { + id = next_token.number.toString(); + advance(); + } + } + return id; + } + + + function function_params() { + var id, paren = next_token, params = []; + advance('('); + step_in(); + no_space(); + if (next_token.id === ')') { + no_space(); + step_out(')', paren); + return params; + } + for (;;) { + edge(); + id = identifier(); + params.push(token); + add_label(token, option.unparam ? 'parameter' : 'unparam'); + if (next_token.id === ',') { + comma(); + } else { + no_space(); + step_out(')', paren); + return params; + } + } + } + + + + function do_function(func, name) { + var old_funct = funct, + old_option = option, + old_scope = scope; + funct = { + '(name)' : name || '\'' + (anonname || '').replace(nx, sanitize) + '\'', + '(line)' : next_token.line, + '(context)' : old_funct, + '(breakage)' : 0, + '(loopage)' : 0, + '(scope)' : scope, + '(token)' : func + }; + option = Object.create(old_option); + scope = Object.create(old_scope); + functions.push(funct); + func.name = name; + if (name) { + add_label(func, 'function', name); + } + func.writeable = false; + func.first = funct['(params)'] = function_params(); + one_space(); + func.block = block(false); + if (funct['(arguments)']) { + func.first.forEach(function (value) { + if (value.assign) { + warn('parameter_arguments_a', value, value.string); + } + }); + } + funct = old_funct; + option = old_option; + scope = old_scope; + } + + + assignop('='); + assignop('+=', '+'); + assignop('-=', '-'); + assignop('*=', '*'); + assignop('/=', '/').nud = function () { + stop('slash_equal'); + }; + assignop('%=', '%'); + assignop('&=', '&'); + assignop('|=', '|'); + assignop('^=', '^'); + assignop('<<=', '<<'); + assignop('>>=', '>>'); + assignop('>>>=', '>>>'); + + + prefix('{', function () { + var get, i, j, name, p, set, seen = {}; + this.arity = 'prefix'; + this.first = []; + step_in(); + while (next_token.id !== '}') { + indent.wrap = false; + +// JSLint recognizes the ES5 extension for get/set in object literals, +// but requires that they be used in pairs. + + edge(); + if (next_token.string === 'get' && peek().id !== ':') { + if (!option.es5) { + warn('es5'); + } + get = next_token; + advance('get'); + one_space_only(); + name = next_token; + i = property_name(); + if (!i) { + stop('missing_property'); + } + get.string = ''; + do_function(get); + if (funct['(loopage)']) { + warn('function_loop', get); + } + p = get.first; + if (p && p.length) { + warn('parameter_a_get_b', p[0], p[0].string, i); + } + comma(); + set = next_token; + spaces(); + edge(); + advance('set'); + set.string = ''; + one_space_only(); + j = property_name(); + if (i !== j) { + stop('expected_a_b', token, i, j || next_token.string); + } + do_function(set); + if (set.block.length === 0) { + warn('missing_a', token, 'throw'); + } + p = set.first; + if (!p || p.length !== 1) { + stop('parameter_set_a', set, 'value'); + } else if (p[0].string !== 'value') { + stop('expected_a_b', p[0], 'value', p[0].string); + } + name.first = [get, set]; + } else { + name = next_token; + i = property_name(); + if (typeof i !== 'string') { + stop('missing_property'); + } + advance(':'); + spaces(); + name.first = expression(10); + } + this.first.push(name); + if (seen[i] === true) { + warn('duplicate_a', next_token, i); + } + seen[i] = true; + tally_property(i); + if (next_token.id !== ',') { + break; + } + for (;;) { + comma(); + if (next_token.id !== ',') { + break; + } + warn('unexpected_a', next_token); + } + if (next_token.id === '}' && !option.es5) { + warn('unexpected_a', token); + } + } + step_out('}', this); + return this; + }); + + stmt('{', function () { + warn('statement_block'); + this.arity = 'statement'; + this.block = statements(); + this.disrupt = this.block.disrupt; + advance('}', this); + return this; + }); + + stmt('/*global', directive); + stmt('/*globals', directive); + stmt('/*jslint', directive); + stmt('/*member', directive); + stmt('/*members', directive); + stmt('/*property', directive); + stmt('/*properties', directive); + + stmt('var', function () { + +// JavaScript does not have block scope. It only has function scope. So, +// declaring a variable in a block can have unexpected consequences. + +// var.first will contain an array, the array containing name tokens +// and assignment tokens. + + var assign, id, name; + + if (funct['(vars)'] && !option.vars) { + warn('combine_var'); + } else if (funct !== global_funct) { + funct['(vars)'] = true; + } + this.arity = 'statement'; + this.first = []; + step_in('var'); + for (;;) { + name = next_token; + id = identifier(); + add_label(name, 'becoming'); + + if (next_token.id === '=') { + assign = next_token; + assign.first = name; + spaces(); + advance('='); + spaces(); + if (next_token.id === 'undefined') { + warn('unnecessary_initialize', token, id); + } + if (peek(0).id === '=' && next_token.identifier) { + stop('var_a_not'); + } + assign.second = expression(0); + assign.arity = 'infix'; + this.first.push(assign); + } else { + this.first.push(name); + } + if (funct[id] === 'becoming') { + funct[id] = 'unused'; + } + if (next_token.id !== ',') { + break; + } + comma(); + indent.wrap = false; + if (var_mode && next_token.line === token.line && + this.first.length === 1) { + var_mode = null; + indent.open = false; + indent.at -= option.indent; + } + spaces(); + edge(); + } + var_mode = null; + step_out(); + return this; + }); + + stmt('function', function () { + one_space(); + if (in_block) { + warn('function_block', token); + } + var name = next_token, id = identifier(); + add_label(name, 'unction'); + no_space(); + this.arity = 'statement'; + do_function(this, id); + if (next_token.id === '(' && next_token.line === token.line) { + stop('function_statement'); + } + return this; + }); + + prefix('function', function () { + if (!option.anon) { + one_space(); + } + var id = optional_identifier(); + if (id) { + no_space(); + } else { + id = ''; + } + do_function(this, id); + if (funct['(loopage)']) { + warn('function_loop'); + } + switch (next_token.id) { + case ';': + case '(': + case ')': + case ',': + case ']': + case '}': + case ':': + break; + case '.': + if (peek().string !== 'bind' || peek(1).id !== '(') { + warn('unexpected_a'); + } + break; + default: + stop('unexpected_a'); + } + this.arity = 'function'; + return this; + }); + + stmt('if', function () { + var paren = next_token; + one_space(); + advance('('); + step_in('control'); + no_space(); + edge(); + this.arity = 'statement'; + this.first = expected_condition(expected_relation(expression(0))); + no_space(); + step_out(')', paren); + one_space(); + this.block = block(true); + if (next_token.id === 'else') { + one_space(); + advance('else'); + one_space(); + this['else'] = next_token.id === 'if' || next_token.id === 'switch' + ? statement(true) + : block(true); + if (this['else'].disrupt && this.block.disrupt) { + this.disrupt = true; + } + } + return this; + }); + + stmt('try', function () { + +// try.first The catch variable +// try.second The catch clause +// try.third The finally clause +// try.block The try block + + var exception_variable, old_scope, paren; + if (option.adsafe) { + warn('adsafe_a', this); + } + one_space(); + this.arity = 'statement'; + this.block = block(false); + if (next_token.id === 'catch') { + one_space(); + advance('catch'); + one_space(); + paren = next_token; + advance('('); + step_in('control'); + no_space(); + edge(); + old_scope = scope; + scope = Object.create(old_scope); + exception_variable = next_token.string; + this.first = exception_variable; + if (!next_token.identifier) { + warn('expected_identifier_a', next_token); + } else { + add_label(next_token, 'exception'); + } + advance(); + no_space(); + step_out(')', paren); + one_space(); + this.second = block(false); + scope = old_scope; + } + if (next_token.id === 'finally') { + one_space(); + advance('finally'); + one_space(); + this.third = block(false); + } else if (!this.second) { + stop('expected_a_b', next_token, 'catch', artifact()); + } + return this; + }); + + labeled_stmt('while', function () { + one_space(); + var paren = next_token; + funct['(breakage)'] += 1; + funct['(loopage)'] += 1; + advance('('); + step_in('control'); + no_space(); + edge(); + this.arity = 'statement'; + this.first = expected_relation(expression(0)); + if (this.first.id !== 'true') { + expected_condition(this.first, bundle.unexpected_a); + } + no_space(); + step_out(')', paren); + one_space(); + this.block = block(true); + if (this.block.disrupt) { + warn('strange_loop', prev_token); + } + funct['(breakage)'] -= 1; + funct['(loopage)'] -= 1; + return this; + }); + + reserve('with'); + + labeled_stmt('switch', function () { + +// switch.first the switch expression +// switch.second the array of cases. A case is 'case' or 'default' token: +// case.first the array of case expressions +// case.second the array of statements +// If all of the arrays of statements are disrupt, then the switch is disrupt. + + var cases = [], + old_in_block = in_block, + particular, + the_case = next_token, + unbroken = true; + + function find_duplicate_case(value) { + if (are_similar(particular, value)) { + warn('duplicate_a', value); + } + } + + funct['(breakage)'] += 1; + one_space(); + advance('('); + no_space(); + step_in(); + this.arity = 'statement'; + this.first = expected_condition(expected_relation(expression(0))); + no_space(); + step_out(')', the_case); + one_space(); + advance('{'); + step_in(); + in_block = true; + this.second = []; + while (next_token.id === 'case') { + the_case = next_token; + cases.forEach(find_duplicate_case); + the_case.first = []; + the_case.arity = 'case'; + spaces(); + edge('case'); + advance('case'); + for (;;) { + one_space(); + particular = expression(0); + cases.forEach(find_duplicate_case); + cases.push(particular); + the_case.first.push(particular); + if (particular.id === 'NaN') { + warn('unexpected_a', particular); + } + no_space_only(); + advance(':'); + if (next_token.id !== 'case') { + break; + } + spaces(); + edge('case'); + advance('case'); + } + spaces(); + the_case.second = statements(); + if (the_case.second && the_case.second.length > 0) { + particular = the_case.second[the_case.second.length - 1]; + if (particular.disrupt) { + if (particular.id === 'break') { + unbroken = false; + } + } else { + warn('missing_a_after_b', next_token, 'break', 'case'); + } + } else { + warn('empty_case'); + } + this.second.push(the_case); + } + if (this.second.length === 0) { + warn('missing_a', next_token, 'case'); + } + if (next_token.id === 'default') { + spaces(); + the_case = next_token; + the_case.arity = 'case'; + edge('case'); + advance('default'); + no_space_only(); + advance(':'); + spaces(); + the_case.second = statements(); + if (the_case.second && the_case.second.length > 0) { + particular = the_case.second[the_case.second.length - 1]; + if (unbroken && particular.disrupt && particular.id !== 'break') { + this.disrupt = true; + } + } + this.second.push(the_case); + } + funct['(breakage)'] -= 1; + spaces(); + step_out('}', this); + in_block = old_in_block; + return this; + }); + + stmt('debugger', function () { + if (!option.debug) { + warn('unexpected_a', this); + } + this.arity = 'statement'; + return this; + }); + + labeled_stmt('do', function () { + funct['(breakage)'] += 1; + funct['(loopage)'] += 1; + one_space(); + this.arity = 'statement'; + this.block = block(true); + if (this.block.disrupt) { + warn('strange_loop', prev_token); + } + one_space(); + advance('while'); + var paren = next_token; + one_space(); + advance('('); + step_in(); + no_space(); + edge(); + this.first = expected_condition(expected_relation(expression(0)), bundle.unexpected_a); + no_space(); + step_out(')', paren); + funct['(breakage)'] -= 1; + funct['(loopage)'] -= 1; + return this; + }); + + labeled_stmt('for', function () { + + var blok, filter, ok = false, paren = next_token, value; + this.arity = 'statement'; + funct['(breakage)'] += 1; + funct['(loopage)'] += 1; + advance('('); + if (next_token.id === ';') { + no_space(); + advance(';'); + no_space(); + advance(';'); + no_space(); + advance(')'); + blok = block(true); + } else { + step_in('control'); + spaces(this, paren); + no_space(); + if (next_token.id === 'var') { + stop('move_var'); + } + edge(); + if (peek(0).id === 'in') { + this.forin = true; + value = next_token; + switch (funct[value.string]) { + case 'unused': + funct[value.string] = 'var'; + break; + case 'closure': + case 'var': + break; + default: + warn('bad_in_a', value); + } + advance(); + advance('in'); + this.first = value; + this.second = expression(20); + step_out(')', paren); + blok = block(true); + if (!option.forin) { + if (blok.length === 1 && typeof blok[0] === 'object' && + blok[0].string === 'if' && !blok[0]['else']) { + filter = blok[0].first; + while (filter.id === '&&') { + filter = filter.first; + } + switch (filter.id) { + case '===': + case '!==': + ok = filter.first.id === '[' + ? filter.first.first.string === this.second.string && + filter.first.second.string === this.first.string + : filter.first.id === 'typeof' && + filter.first.first.id === '[' && + filter.first.first.first.string === this.second.string && + filter.first.first.second.string === this.first.string; + break; + case '(': + ok = filter.first.id === '.' && (( + filter.first.first.string === this.second.string && + filter.first.second.string === 'hasOwnProperty' && + filter.second[0].string === this.first.string + ) || ( + filter.first.first.string === 'ADSAFE' && + filter.first.second.string === 'has' && + filter.second[0].string === this.second.string && + filter.second[1].string === this.first.string + ) || ( + filter.first.first.id === '.' && + filter.first.first.first.id === '.' && + filter.first.first.first.first.string === 'Object' && + filter.first.first.first.second.string === 'prototype' && + filter.first.first.second.string === 'hasOwnProperty' && + filter.first.second.string === 'call' && + filter.second[0].string === this.second.string && + filter.second[1].string === this.first.string + )); + break; + } + } + if (!ok) { + warn('for_if', this); + } + } + } else { + edge(); + this.first = []; + for (;;) { + this.first.push(expression(0, 'for')); + if (next_token.id !== ',') { + break; + } + comma(); + } + semicolon(); + edge(); + this.second = expected_relation(expression(0)); + if (this.second.id !== 'true') { + expected_condition(this.second, bundle.unexpected_a); + } + semicolon(token); + if (next_token.id === ';') { + stop('expected_a_b', next_token, ')', ';'); + } + this.third = []; + edge(); + for (;;) { + this.third.push(expression(0, 'for')); + if (next_token.id !== ',') { + break; + } + comma(); + } + no_space(); + step_out(')', paren); + one_space(); + blok = block(true); + } + } + if (blok.disrupt) { + warn('strange_loop', prev_token); + } + this.block = blok; + funct['(breakage)'] -= 1; + funct['(loopage)'] -= 1; + return this; + }); + + disrupt_stmt('break', function () { + var label = next_token.string; + this.arity = 'statement'; + if (funct['(breakage)'] === 0) { + warn('unexpected_a', this); + } + if (next_token.identifier && token.line === next_token.line) { + one_space_only(); + if (funct[label] !== 'label') { + warn('not_a_label', next_token); + } else if (scope[label].funct !== funct) { + warn('not_a_scope', next_token); + } + this.first = next_token; + advance(); + } + return this; + }); + + disrupt_stmt('continue', function () { + if (!option['continue']) { + warn('unexpected_a', this); + } + var label = next_token.string; + this.arity = 'statement'; + if (funct['(breakage)'] === 0) { + warn('unexpected_a', this); + } + if (next_token.identifier && token.line === next_token.line) { + one_space_only(); + if (funct[label] !== 'label') { + warn('not_a_label', next_token); + } else if (scope[label].funct !== funct) { + warn('not_a_scope', next_token); + } + this.first = next_token; + advance(); + } + return this; + }); + + disrupt_stmt('return', function () { + if (funct === global_funct && xmode !== 'scriptstring') { + warn('unexpected_a', this); + } + this.arity = 'statement'; + if (next_token.id !== ';' && next_token.line === token.line) { + one_space_only(); + if (next_token.id === '/' || next_token.id === '(regexp)') { + warn('wrap_regexp'); + } + this.first = expression(20); + } + if (peek(0).id === '}' && peek(1).id === 'else') { + warn('unexpected_else', this); + } + return this; + }); + + disrupt_stmt('throw', function () { + this.arity = 'statement'; + one_space_only(); + this.first = expression(20); + return this; + }); + + +// Superfluous reserved words + + reserve('class'); + reserve('const'); + reserve('enum'); + reserve('export'); + reserve('extends'); + reserve('import'); + reserve('super'); + +// Harmony reserved words + + reserve('implements'); + reserve('interface'); + reserve('let'); + reserve('package'); + reserve('private'); + reserve('protected'); + reserve('public'); + reserve('static'); + reserve('yield'); + + +// Parse JSON + + function json_value() { + + function json_object() { + var brace = next_token, object = {}; + advance('{'); + if (next_token.id !== '}') { + while (next_token.id !== '(end)') { + while (next_token.id === ',') { + warn('unexpected_a', next_token); + advance(','); + } + if (next_token.id !== '(string)') { + warn('expected_string_a'); + } + if (object[next_token.string] === true) { + warn('duplicate_a'); + } else if (next_token.string === '__proto__') { + warn('dangling_a'); + } else { + object[next_token.string] = true; + } + advance(); + advance(':'); + json_value(); + if (next_token.id !== ',') { + break; + } + advance(','); + if (next_token.id === '}') { + warn('unexpected_a', token); + break; + } + } + } + advance('}', brace); + } + + function json_array() { + var bracket = next_token; + advance('['); + if (next_token.id !== ']') { + while (next_token.id !== '(end)') { + while (next_token.id === ',') { + warn('unexpected_a', next_token); + advance(','); + } + json_value(); + if (next_token.id !== ',') { + break; + } + advance(','); + if (next_token.id === ']') { + warn('unexpected_a', token); + break; + } + } + } + advance(']', bracket); + } + + switch (next_token.id) { + case '{': + json_object(); + break; + case '[': + json_array(); + break; + case 'true': + case 'false': + case 'null': + case '(number)': + case '(string)': + advance(); + break; + case '-': + advance('-'); + no_space_only(); + advance('(number)'); + break; + default: + stop('unexpected_a'); + } + } + + +// CSS parsing. + + function css_name() { + if (next_token.identifier) { + advance(); + return true; + } + } + + + function css_number() { + if (next_token.id === '-') { + advance('-'); + no_space_only(); + } + if (next_token.id === '(number)') { + advance('(number)'); + return true; + } + } + + + function css_string() { + if (next_token.id === '(string)') { + advance(); + return true; + } + } + + function css_color() { + var i, number, paren, value; + if (next_token.identifier) { + value = next_token.string; + if (value === 'rgb' || value === 'rgba') { + advance(); + paren = next_token; + advance('('); + for (i = 0; i < 3; i += 1) { + if (i) { + comma(); + } + number = next_token.number; + if (next_token.id !== '(number)' || number < 0) { + warn('expected_positive_a', next_token); + advance(); + } else { + advance(); + if (next_token.id === '%') { + advance('%'); + if (number > 100) { + warn('expected_percent_a', token, number); + } + } else { + if (number > 255) { + warn('expected_small_a', token, number); + } + } + } + } + if (value === 'rgba') { + comma(); + number = next_token.number; + if (next_token.id !== '(number)' || number < 0 || number > 1) { + warn('expected_fraction_a', next_token); + } + advance(); + if (next_token.id === '%') { + warn('unexpected_a'); + advance('%'); + } + } + advance(')', paren); + return true; + } + if (css_colorData[next_token.string] === true) { + advance(); + return true; + } + } else if (next_token.id === '(color)') { + advance(); + return true; + } + return false; + } + + + function css_length() { + if (next_token.id === '-') { + advance('-'); + no_space_only(); + } + if (next_token.id === '(number)') { + advance(); + if (next_token.id !== '(string)' && + css_lengthData[next_token.string] === true) { + no_space_only(); + advance(); + } else if (+token.number !== 0) { + warn('expected_linear_a'); + } + return true; + } + return false; + } + + + function css_line_height() { + if (next_token.id === '-') { + advance('-'); + no_space_only(); + } + if (next_token.id === '(number)') { + advance(); + if (next_token.id !== '(string)' && + css_lengthData[next_token.string] === true) { + no_space_only(); + advance(); + } + return true; + } + return false; + } + + + function css_width() { + if (next_token.identifier) { + switch (next_token.string) { + case 'thin': + case 'medium': + case 'thick': + advance(); + return true; + } + } else { + return css_length(); + } + } + + + function css_margin() { + if (next_token.identifier) { + if (next_token.string === 'auto') { + advance(); + return true; + } + } else { + return css_length(); + } + } + + function css_attr() { + if (next_token.identifier && next_token.string === 'attr') { + advance(); + advance('('); + if (!next_token.identifier) { + warn('expected_name_a'); + } + advance(); + advance(')'); + return true; + } + return false; + } + + + function css_comma_list() { + while (next_token.id !== ';') { + if (!css_name() && !css_string()) { + warn('expected_name_a'); + } + if (next_token.id !== ',') { + return true; + } + comma(); + } + } + + + function css_counter() { + if (next_token.identifier && next_token.string === 'counter') { + advance(); + advance('('); + advance(); + if (next_token.id === ',') { + comma(); + if (next_token.id !== '(string)') { + warn('expected_string_a'); + } + advance(); + } + advance(')'); + return true; + } + if (next_token.identifier && next_token.string === 'counters') { + advance(); + advance('('); + if (!next_token.identifier) { + warn('expected_name_a'); + } + advance(); + if (next_token.id === ',') { + comma(); + if (next_token.id !== '(string)') { + warn('expected_string_a'); + } + advance(); + } + if (next_token.id === ',') { + comma(); + if (next_token.id !== '(string)') { + warn('expected_string_a'); + } + advance(); + } + advance(')'); + return true; + } + return false; + } + + + function css_radius() { + return css_length() && (next_token.id !== '(number)' || css_length()); + } + + + function css_shadow() { + for (;;) { + if (next_token.string === 'inset') { + advance(); + } + for (;;) { + if (!css_length()) { + break; + } + } + css_color(); + if (next_token.id !== ',') { + break; + } + advance(','); + } + return true; + } + + + function css_shape() { + var i; + if (next_token.identifier && next_token.string === 'rect') { + advance(); + advance('('); + for (i = 0; i < 4; i += 1) { + if (!css_length()) { + warn('expected_number_a'); + break; + } + } + advance(')'); + return true; + } + return false; + } + + + function css_url() { + var c, url; + if (next_token.identifier && next_token.string === 'url') { + next_token = lex.range('(', ')'); + url = next_token.string; + c = url.charAt(0); + if (c === '"' || c === '\'') { + if (url.slice(-1) !== c) { + warn('bad_url_a'); + } else { + url = url.slice(1, -1); + if (url.indexOf(c) >= 0) { + warn('bad_url_a'); + } + } + } + if (!url) { + warn('missing_url'); + } + if (ux.test(url)) { + stop('bad_url_a'); + } + urls.push(url); + advance(); + return true; + } + return false; + } + + + css_any = [css_url, function () { + for (;;) { + if (next_token.identifier) { + switch (next_token.string.toLowerCase()) { + case 'url': + css_url(); + break; + case 'expression': + warn('unexpected_a'); + advance(); + break; + default: + advance(); + } + } else { + if (next_token.id === ';' || next_token.id === '!' || + next_token.id === '(end)' || next_token.id === '}') { + return true; + } + advance(); + } + } + }]; + + + function font_face() { + advance_identifier('font-family'); + advance(':'); + if (!css_name() && !css_string()) { + stop('expected_name_a'); + } + semicolon(); + advance_identifier('src'); + advance(':'); + while (true) { + if (next_token.string === 'local') { + advance_identifier('local'); + advance('('); + if (ux.test(next_token.string)) { + stop('bad_url_a'); + } + + if (!css_name() && !css_string()) { + stop('expected_name_a'); + } + advance(')'); + } else if (!css_url()) { + stop('expected_a_b', next_token, 'url', artifact()); + } + if (next_token.id !== ',') { + break; + } + comma(); + } + semicolon(); + } + + + css_border_style = [ + 'none', 'dashed', 'dotted', 'double', 'groove', + 'hidden', 'inset', 'outset', 'ridge', 'solid' + ]; + + css_break = [ + 'auto', 'always', 'avoid', 'left', 'right' + ]; + + css_media = { + 'all': true, + 'braille': true, + 'embossed': true, + 'handheld': true, + 'print': true, + 'projection': true, + 'screen': true, + 'speech': true, + 'tty': true, + 'tv': true + }; + + css_overflow = [ + 'auto', 'hidden', 'scroll', 'visible' + ]; + + css_attribute_data = { + background: [ + true, 'background-attachment', 'background-color', + 'background-image', 'background-position', 'background-repeat' + ], + 'background-attachment': ['scroll', 'fixed'], + 'background-color': ['transparent', css_color], + 'background-image': ['none', css_url], + 'background-position': [ + 2, [css_length, 'top', 'bottom', 'left', 'right', 'center'] + ], + 'background-repeat': [ + 'repeat', 'repeat-x', 'repeat-y', 'no-repeat' + ], + 'border': [true, 'border-color', 'border-style', 'border-width'], + 'border-bottom': [ + true, 'border-bottom-color', 'border-bottom-style', + 'border-bottom-width' + ], + 'border-bottom-color': css_color, + 'border-bottom-left-radius': css_radius, + 'border-bottom-right-radius': css_radius, + 'border-bottom-style': css_border_style, + 'border-bottom-width': css_width, + 'border-collapse': ['collapse', 'separate'], + 'border-color': ['transparent', 4, css_color], + 'border-left': [ + true, 'border-left-color', 'border-left-style', 'border-left-width' + ], + 'border-left-color': css_color, + 'border-left-style': css_border_style, + 'border-left-width': css_width, + 'border-radius': function () { + function count(separator) { + var n = 1; + if (separator) { + advance(separator); + } + if (!css_length()) { + return false; + } + while (next_token.id === '(number)') { + if (!css_length()) { + return false; + } + n += 1; + } + if (n > 4) { + warn('bad_style'); + } + return true; + } + + return count() && (next_token.id !== '/' || count('/')); + }, + 'border-right': [ + true, 'border-right-color', 'border-right-style', + 'border-right-width' + ], + 'border-right-color': css_color, + 'border-right-style': css_border_style, + 'border-right-width': css_width, + 'border-spacing': [2, css_length], + 'border-style': [4, css_border_style], + 'border-top': [ + true, 'border-top-color', 'border-top-style', 'border-top-width' + ], + 'border-top-color': css_color, + 'border-top-left-radius': css_radius, + 'border-top-right-radius': css_radius, + 'border-top-style': css_border_style, + 'border-top-width': css_width, + 'border-width': [4, css_width], + bottom: [css_length, 'auto'], + 'box-shadow': ['none', css_shadow], + 'caption-side' : ['bottom', 'left', 'right', 'top'], + clear: ['both', 'left', 'none', 'right'], + clip: [css_shape, 'auto'], + color: css_color, + content: [ + 'open-quote', 'close-quote', 'no-open-quote', 'no-close-quote', + css_string, css_url, css_counter, css_attr + ], + 'counter-increment': [ + css_name, 'none' + ], + 'counter-reset': [ + css_name, 'none' + ], + cursor: [ + css_url, 'auto', 'crosshair', 'default', 'e-resize', 'help', 'move', + 'n-resize', 'ne-resize', 'nw-resize', 'pointer', 's-resize', + 'se-resize', 'sw-resize', 'w-resize', 'text', 'wait' + ], + direction: ['ltr', 'rtl'], + display: [ + 'block', 'compact', 'inline', 'inline-block', 'inline-table', + 'list-item', 'marker', 'none', 'run-in', 'table', 'table-caption', + 'table-cell', 'table-column', 'table-column-group', + 'table-footer-group', 'table-header-group', 'table-row', + 'table-row-group' + ], + 'empty-cells': ['show', 'hide'], + 'float': ['left', 'none', 'right'], + font: [ + 'caption', 'icon', 'menu', 'message-box', 'small-caption', + 'status-bar', true, 'font-size', 'font-style', 'font-weight', + 'font-family' + ], + 'font-family': css_comma_list, + 'font-size': [ + 'xx-small', 'x-small', 'small', 'medium', 'large', 'x-large', + 'xx-large', 'larger', 'smaller', css_length + ], + 'font-size-adjust': ['none', css_number], + 'font-stretch': [ + 'normal', 'wider', 'narrower', 'ultra-condensed', + 'extra-condensed', 'condensed', 'semi-condensed', + 'semi-expanded', 'expanded', 'extra-expanded' + ], + 'font-style': [ + 'normal', 'italic', 'oblique' + ], + 'font-variant': [ + 'normal', 'small-caps' + ], + 'font-weight': [ + 'normal', 'bold', 'bolder', 'lighter', css_number + ], + height: [css_length, 'auto'], + left: [css_length, 'auto'], + 'letter-spacing': ['normal', css_length], + 'line-height': ['normal', css_line_height], + 'list-style': [ + true, 'list-style-image', 'list-style-position', 'list-style-type' + ], + 'list-style-image': ['none', css_url], + 'list-style-position': ['inside', 'outside'], + 'list-style-type': [ + 'circle', 'disc', 'square', 'decimal', 'decimal-leading-zero', + 'lower-roman', 'upper-roman', 'lower-greek', 'lower-alpha', + 'lower-latin', 'upper-alpha', 'upper-latin', 'hebrew', 'katakana', + 'hiragana-iroha', 'katakana-oroha', 'none' + ], + margin: [4, css_margin], + 'margin-bottom': css_margin, + 'margin-left': css_margin, + 'margin-right': css_margin, + 'margin-top': css_margin, + 'marker-offset': [css_length, 'auto'], + 'max-height': [css_length, 'none'], + 'max-width': [css_length, 'none'], + 'min-height': css_length, + 'min-width': css_length, + opacity: css_number, + outline: [true, 'outline-color', 'outline-style', 'outline-width'], + 'outline-color': ['invert', css_color], + 'outline-style': [ + 'dashed', 'dotted', 'double', 'groove', 'inset', 'none', + 'outset', 'ridge', 'solid' + ], + 'outline-width': css_width, + overflow: css_overflow, + 'overflow-x': css_overflow, + 'overflow-y': css_overflow, + padding: [4, css_length], + 'padding-bottom': css_length, + 'padding-left': css_length, + 'padding-right': css_length, + 'padding-top': css_length, + 'page-break-after': css_break, + 'page-break-before': css_break, + position: ['absolute', 'fixed', 'relative', 'static'], + quotes: [8, css_string], + right: [css_length, 'auto'], + 'table-layout': ['auto', 'fixed'], + 'text-align': ['center', 'justify', 'left', 'right'], + 'text-decoration': [ + 'none', 'underline', 'overline', 'line-through', 'blink' + ], + 'text-indent': css_length, + 'text-shadow': ['none', 4, [css_color, css_length]], + 'text-transform': ['capitalize', 'uppercase', 'lowercase', 'none'], + top: [css_length, 'auto'], + 'unicode-bidi': ['normal', 'embed', 'bidi-override'], + 'vertical-align': [ + 'baseline', 'bottom', 'sub', 'super', 'top', 'text-top', 'middle', + 'text-bottom', css_length + ], + visibility: ['visible', 'hidden', 'collapse'], + 'white-space': [ + 'normal', 'nowrap', 'pre', 'pre-line', 'pre-wrap', 'inherit' + ], + width: [css_length, 'auto'], + 'word-spacing': ['normal', css_length], + 'word-wrap': ['break-word', 'normal'], + 'z-index': ['auto', css_number] + }; + + function style_attribute() { + var v; + while (next_token.id === '*' || next_token.id === '#' || + next_token.string === '_') { + if (!option.css) { + warn('unexpected_a'); + } + advance(); + } + if (next_token.id === '-') { + if (!option.css) { + warn('unexpected_a'); + } + advance('-'); + if (!next_token.identifier) { + warn('expected_nonstandard_style_attribute'); + } + advance(); + return css_any; + } + if (!next_token.identifier) { + warn('expected_style_attribute'); + } else { + if (Object.prototype.hasOwnProperty.call(css_attribute_data, + next_token.string)) { + v = css_attribute_data[next_token.string]; + } else { + v = css_any; + if (!option.css) { + warn('unrecognized_style_attribute_a'); + } + } + } + advance(); + return v; + } + + + function style_value(v) { + var i = 0, + n, + once, + match, + round, + start = 0, + vi; + switch (typeof v) { + case 'function': + return v(); + case 'string': + if (next_token.identifier && next_token.string === v) { + advance(); + return true; + } + return false; + } + for (;;) { + if (i >= v.length) { + return false; + } + vi = v[i]; + i += 1; + if (typeof vi === 'boolean') { + break; + } else if (typeof vi === 'number') { + n = vi; + vi = v[i]; + i += 1; + } else { + n = 1; + } + match = false; + while (n > 0) { + if (style_value(vi)) { + match = true; + n -= 1; + } else { + break; + } + } + if (match) { + return true; + } + } + start = i; + once = []; + for (;;) { + round = false; + for (i = start; i < v.length; i += 1) { + if (!once[i]) { + if (style_value(css_attribute_data[v[i]])) { + match = true; + round = true; + once[i] = true; + break; + } + } + } + if (!round) { + return match; + } + } + } + + function style_child() { + if (next_token.id === '(number)') { + advance(); + if (next_token.string === 'n' && next_token.identifier) { + no_space_only(); + advance(); + if (next_token.id === '+') { + no_space_only(); + advance('+'); + no_space_only(); + advance('(number)'); + } + } + return; + } + if (next_token.identifier && + (next_token.string === 'odd' || next_token.string === 'even')) { + advance(); + return; + } + warn('unexpected_a'); + } + + function substyle() { + var v; + for (;;) { + if (next_token.id === '}' || next_token.id === '(end)' || + (xquote && next_token.id === xquote)) { + return; + } + v = style_attribute(); + advance(':'); + if (next_token.identifier && next_token.string === 'inherit') { + advance(); + } else { + if (!style_value(v)) { + warn('unexpected_a'); + advance(); + } + } + if (next_token.id === '!') { + advance('!'); + no_space_only(); + if (next_token.identifier && next_token.string === 'important') { + advance(); + } else { + warn('expected_a_b', + next_token, 'important', artifact()); + } + } + if (next_token.id === '}' || next_token.id === xquote) { + warn('expected_a_b', next_token, ';', artifact()); + } else { + semicolon(); + } + } + } + + function style_selector() { + if (next_token.identifier) { + if (!Object.prototype.hasOwnProperty.call(html_tag, option.cap + ? next_token.string.toLowerCase() + : next_token.string)) { + warn('expected_tagname_a'); + } + advance(); + } else { + switch (next_token.id) { + case '>': + case '+': + advance(); + style_selector(); + break; + case ':': + advance(':'); + switch (next_token.string) { + case 'active': + case 'after': + case 'before': + case 'checked': + case 'disabled': + case 'empty': + case 'enabled': + case 'first-child': + case 'first-letter': + case 'first-line': + case 'first-of-type': + case 'focus': + case 'hover': + case 'last-child': + case 'last-of-type': + case 'link': + case 'only-of-type': + case 'root': + case 'target': + case 'visited': + advance_identifier(next_token.string); + break; + case 'lang': + advance_identifier('lang'); + advance('('); + if (!next_token.identifier) { + warn('expected_lang_a'); + } + advance(')'); + break; + case 'nth-child': + case 'nth-last-child': + case 'nth-last-of-type': + case 'nth-of-type': + advance_identifier(next_token.string); + advance('('); + style_child(); + advance(')'); + break; + case 'not': + advance_identifier('not'); + advance('('); + if (next_token.id === ':' && peek(0).string === 'not') { + warn('not'); + } + style_selector(); + advance(')'); + break; + default: + warn('expected_pseudo_a'); + } + break; + case '#': + advance('#'); + if (!next_token.identifier) { + warn('expected_id_a'); + } + advance(); + break; + case '*': + advance('*'); + break; + case '.': + advance('.'); + if (!next_token.identifier) { + warn('expected_class_a'); + } + advance(); + break; + case '[': + advance('['); + if (!next_token.identifier) { + warn('expected_attribute_a'); + } + advance(); + if (next_token.id === '=' || next_token.string === '~=' || + next_token.string === '$=' || + next_token.string === '|=' || + next_token.id === '*=' || + next_token.id === '^=') { + advance(); + if (next_token.id !== '(string)') { + warn('expected_string_a'); + } + advance(); + } + advance(']'); + break; + default: + stop('expected_selector_a'); + } + } + } + + function style_pattern() { + if (next_token.id === '{') { + warn('expected_style_pattern'); + } + for (;;) { + style_selector(); + if (next_token.id === '= 0) { + warn('unexpected_char_a_b', token, v.charAt(x), a); + } + ids[u] = true; + } else if (a === 'class' || a === 'type' || a === 'name') { + x = v.search(qx); + if (x >= 0) { + warn('unexpected_char_a_b', token, v.charAt(x), a); + } + ids[u] = true; + } else if (a === 'href' || a === 'background' || + a === 'content' || a === 'data' || + a.indexOf('src') >= 0 || a.indexOf('url') >= 0) { + if (option.safe && ux.test(v)) { + stop('bad_url_a', next_token, v); + } + urls.push(v); + } else if (a === 'for') { + if (option.adsafe) { + if (adsafe_id) { + if (v.slice(0, adsafe_id.length) !== adsafe_id) { + warn('adsafe_prefix_a', next_token, adsafe_id); + } else if (!/^[A-Z]+_[A-Z]+$/.test(v)) { + warn('adsafe_bad_id'); + } + } else { + warn('adsafe_bad_id'); + } + } + } else if (a === 'name') { + if (option.adsafe && v.indexOf('_') >= 0) { + warn('adsafe_name_a', next_token, v); + } + } + } + + function do_tag(name, attribute) { + var i, tag = html_tag[name], script, x; + src = false; + if (!tag) { + stop( + bundle.unrecognized_tag_a, + next_token, + name === name.toLowerCase() + ? name + : name + ' (capitalization error)' + ); + } + if (stack.length > 0) { + if (name === 'html') { + stop('unexpected_a', token, name); + } + x = tag.parent; + if (x) { + if (x.indexOf(' ' + stack[stack.length - 1].name + ' ') < 0) { + stop('tag_a_in_b', token, name, x); + } + } else if (!option.adsafe && !option.fragment) { + i = stack.length; + do { + if (i <= 0) { + stop('tag_a_in_b', token, name, 'body'); + } + i -= 1; + } while (stack[i].name !== 'body'); + } + } + switch (name) { + case 'div': + if (option.adsafe && stack.length === 1 && !adsafe_id) { + warn('adsafe_missing_id'); + } + break; + case 'script': + xmode = 'script'; + advance('>'); + if (attribute.lang) { + warn('lang', token); + } + if (option.adsafe && stack.length !== 1) { + warn('adsafe_placement', token); + } + if (attribute.src) { + if (option.adsafe && (!adsafe_may || !approved[attribute.src])) { + warn('adsafe_source', token); + } + } else { + step_in(next_token.from); + edge(); + use_strict(); + adsafe_top = true; + script = statements(); + +// JSLint is also the static analyzer for ADsafe. See www.ADsafe.org. + + if (option.adsafe) { + if (adsafe_went) { + stop('adsafe_script', token); + } + if (script.length !== 1 || + aint(script[0], 'id', '(') || + aint(script[0].first, 'id', '.') || + aint(script[0].first.first, 'string', 'ADSAFE') || + aint(script[0].second[0], 'string', adsafe_id)) { + stop('adsafe_id_go'); + } + switch (script[0].first.second.string) { + case 'id': + if (adsafe_may || adsafe_went || + script[0].second.length !== 1) { + stop('adsafe_id', next_token); + } + adsafe_may = true; + break; + case 'go': + if (adsafe_went) { + stop('adsafe_go'); + } + if (script[0].second.length !== 2 || + aint(script[0].second[1], 'id', 'function') || + !script[0].second[1].first || + aint(script[0].second[1].first[0], 'string', 'dom') || + script[0].second[1].first.length > 2 || + (script[0].second[1].first.length === 2 && + aint(script[0].second[1].first[1], 'string', 'lib'))) { + stop('adsafe_go', next_token); + } + adsafe_went = true; + break; + default: + stop('adsafe_id_go'); + } + } + indent = null; + } + xmode = 'html'; + advance(''); + styles(); + xmode = 'html'; + advance(''; + } + + function html() { + var attribute, attributes, is_empty, name, old_white = option.white, + quote, tag_name, tag, wmode; + xmode = 'html'; + xquote = ''; + stack = null; + for (;;) { + switch (next_token.string) { + case '<': + xmode = 'html'; + advance('<'); + attributes = {}; + tag_name = next_token; + name = tag_name.string; + advance_identifier(name); + if (option.cap) { + name = name.toLowerCase(); + } + tag_name.name = name; + if (!stack) { + stack = []; + do_begin(name); + } + tag = html_tag[name]; + if (typeof tag !== 'object') { + stop('unrecognized_tag_a', tag_name, name); + } + is_empty = tag.empty; + tag_name.type = name; + for (;;) { + if (next_token.id === '/') { + advance('/'); + if (next_token.id !== '>') { + warn('expected_a_b', next_token, '>', artifact()); + } + break; + } + if (next_token.id && next_token.id.charAt(0) === '>') { + break; + } + if (!next_token.identifier) { + if (next_token.id === '(end)' || next_token.id === '(error)') { + warn('expected_a_b', next_token, '>', artifact()); + } + warn('bad_name_a'); + } + option.white = false; + spaces(); + attribute = next_token.string; + option.white = old_white; + advance(); + if (!option.cap && attribute !== attribute.toLowerCase()) { + warn('attribute_case_a', token); + } + attribute = attribute.toLowerCase(); + xquote = ''; + if (Object.prototype.hasOwnProperty.call(attributes, attribute)) { + warn('duplicate_a', token, attribute); + } + if (attribute.slice(0, 2) === 'on') { + if (!option.on) { + warn('html_handlers'); + } + xmode = 'scriptstring'; + advance('='); + quote = next_token.id; + if (quote !== '"' && quote !== '\'') { + stop('expected_a_b', next_token, '"', artifact()); + } + xquote = quote; + wmode = option.white; + option.white = true; + advance(quote); + use_strict(); + statements(); + option.white = wmode; + if (next_token.id !== quote) { + stop('expected_a_b', next_token, quote, artifact()); + } + xmode = 'html'; + xquote = ''; + advance(quote); + tag = false; + } else if (attribute === 'style') { + xmode = 'scriptstring'; + advance('='); + quote = next_token.id; + if (quote !== '"' && quote !== '\'') { + stop('expected_a_b', next_token, '"', artifact()); + } + xmode = 'styleproperty'; + xquote = quote; + advance(quote); + substyle(); + xmode = 'html'; + xquote = ''; + advance(quote); + tag = false; + } else { + if (next_token.id === '=') { + advance('='); + tag = next_token.string; + if (!next_token.identifier && + next_token.id !== '"' && + next_token.id !== '\'' && + next_token.id !== '(string)' && + next_token.id !== '(number)' && + next_token.id !== '(color)') { + warn('expected_attribute_value_a', token, attribute); + } + advance(); + } else { + tag = true; + } + } + attributes[attribute] = tag; + do_attribute(attribute, tag); + } + do_tag(name, attributes); + if (!is_empty) { + stack.push(tag_name); + } + xmode = 'outer'; + advance('>'); + break; + case '') { + stop('expected_a_b', next_token, '>', artifact()); + } + xmode = 'outer'; + advance('>'); + break; + case '' || next_token.id === '(end)') { + break; + } + if (next_token.string.indexOf('--') >= 0) { + stop('unexpected_a', next_token, '--'); + } + if (next_token.string.indexOf('<') >= 0) { + stop('unexpected_a', next_token, '<'); + } + if (next_token.string.indexOf('>') >= 0) { + stop('unexpected_a', next_token, '>'); + } + } + xmode = 'outer'; + advance('>'); + break; + case '(end)': + if (stack.length !== 0) { + warn('missing_a', next_token, ''); + } + return; + default: + if (next_token.id === '(end)') { + stop('missing_a', next_token, + ''); + } else { + advance(); + } + } + if (stack && stack.length === 0 && (option.adsafe || + !option.fragment || next_token.id === '(end)')) { + break; + } + } + if (next_token.id !== '(end)') { + stop('unexpected_a'); + } + } + + +// The actual JSLINT function itself. + + itself = function JSLint(the_source, the_option) { + + var i, predef, tree; + JSLINT.errors = []; + JSLINT.tree = ''; + JSLINT.properties = ''; + begin = prev_token = token = next_token = + Object.create(syntax['(begin)']); + predefined = {}; + add_to_predefined(standard); + property = {}; + if (the_option) { + option = Object.create(the_option); + predef = option.predef; + if (predef) { + if (Array.isArray(predef)) { + for (i = 0; i < predef.length; i += 1) { + predefined[predef[i]] = true; + } + } else if (typeof predef === 'object') { + add_to_predefined(predef); + } + } + do_safe(); + } else { + option = {}; + } + option.indent = +option.indent || 4; + option.maxerr = +option.maxerr || 50; + adsafe_id = ''; + adsafe_may = adsafe_top = adsafe_went = false; + approved = {}; + if (option.approved) { + for (i = 0; i < option.approved.length; i += 1) { + approved[option.approved[i]] = option.approved[i]; + } + } else { + approved.test = 'test'; + } + tab = ''; + for (i = 0; i < option.indent; i += 1) { + tab += ' '; + } + global_scope = scope = {}; + global_funct = funct = { + '(scope)': scope, + '(breakage)': 0, + '(loopage)': 0 + }; + functions = [funct]; + + comments_off = false; + ids = {}; + in_block = false; + indent = null; + json_mode = false; + lookahead = []; + node_js = false; + prereg = true; + src = false; + stack = null; + strict_mode = false; + urls = []; + var_mode = null; + warnings = 0; + xmode = ''; + lex.init(the_source); + + assume(); + + try { + advance(); + if (next_token.id === '(number)') { + stop('unexpected_a'); + } else if (next_token.string.charAt(0) === '<') { + html(); + if (option.adsafe && !adsafe_went) { + warn('adsafe_go', this); + } + } else { + switch (next_token.id) { + case '{': + case '[': + json_mode = true; + json_value(); + break; + case '@': + case '*': + case '#': + case '.': + case ':': + xmode = 'style'; + advance(); + if (token.id !== '@' || !next_token.identifier || + next_token.string !== 'charset' || token.line !== 1 || + token.from !== 1) { + stop('css'); + } + advance(); + if (next_token.id !== '(string)' && + next_token.string !== 'UTF-8') { + stop('css'); + } + advance(); + semicolon(); + styles(); + break; + + default: + if (option.adsafe && option.fragment) { + stop('expected_a_b', + next_token, '
', artifact()); + } + +// If the first token is a semicolon, ignore it. This is sometimes used when +// files are intended to be appended to files that may be sloppy. A sloppy +// file may be depending on semicolon insertion on its last line. + + step_in(1); + if (next_token.id === ';' && !node_js) { + semicolon(); + } + adsafe_top = true; + tree = statements(); + begin.first = tree; + itself.tree = begin; + if (option.adsafe && (tree.length !== 1 || + aint(tree[0], 'id', '(') || + aint(tree[0].first, 'id', '.') || + aint(tree[0].first.first, 'string', 'ADSAFE') || + aint(tree[0].first.second, 'string', 'lib') || + tree[0].second.length !== 2 || + tree[0].second[0].id !== '(string)' || + aint(tree[0].second[1], 'id', 'function'))) { + stop('adsafe_lib'); + } + if (tree.disrupt) { + warn('weird_program', prev_token); + } + } + } + indent = null; + advance('(end)'); + itself.property = property; + } catch (e) { + if (e) { // ~~ + JSLINT.errors.push({ + reason : e.message, + line : e.line || next_token.line, + character : e.character || next_token.from + }, null); + } + } + return JSLINT.errors.length === 0; + }; + + +// Data summary. + + itself.data = function () { + var data = {functions: []}, + function_data, + globals, + i, + j, + kind, + name, + the_function, + undef = [], + unused = []; + if (itself.errors.length) { + data.errors = itself.errors; + } + + if (json_mode) { + data.json = true; + } + + if (urls.length > 0) { + data.urls = urls; + } + + globals = Object.keys(global_scope).filter(function (value) { + return value.charAt(0) !== '(' && typeof standard[value] !== 'boolean'; + }); + if (globals.length > 0) { + data.globals = globals; + } + + for (i = 1; i < functions.length; i += 1) { + the_function = functions[i]; + function_data = {}; + for (j = 0; j < functionicity.length; j += 1) { + function_data[functionicity[j]] = []; + } + for (name in the_function) { + if (Object.prototype.hasOwnProperty.call(the_function, name)) { + if (name.charAt(0) !== '(') { + kind = the_function[name]; + if (kind === 'unction' || kind === 'unparam') { + kind = 'unused'; + } + if (Array.isArray(function_data[kind])) { + function_data[kind].push(name); + if (kind === 'unused') { + unused.push({ + name: name, + line: the_function['(line)'], + 'function': the_function['(name)'] + }); + } else if (kind === 'undef') { + undef.push({ + name: name, + line: the_function['(line)'], + 'function': the_function['(name)'] + }); + } + } + } + } + } + for (j = 0; j < functionicity.length; j += 1) { + if (function_data[functionicity[j]].length === 0) { + delete function_data[functionicity[j]]; + } + } + function_data.name = the_function['(name)']; + function_data.params = the_function['(params)']; + function_data.line = the_function['(line)']; + data.functions.push(function_data); + } + + if (unused.length > 0) { + data.unused = unused; + } + if (undef.length > 0) { + data['undefined'] = undef; + } + return data; + }; + + itself.error_report = function (data) { + var evidence, i, output = [], snippets, warning; + if (data.errors) { + for (i = 0; i < data.errors.length; i += 1) { + warning = data.errors[i]; + if (warning) { + evidence = warning.evidence || ''; + output.push(''); + if (isFinite(warning.line)) { + output.push('
line ' + + String(warning.line) + + ' character ' + String(warning.character) + + '
'); + } + output.push(warning.reason.entityify() + '
'); + if (evidence) { + output.push('
' + evidence.entityify() + '
'); + } + } + } + } + if (data.unused || data['undefined']) { + output.push('
'); + if (data['undefined']) { + output.push('
undefined
'); + snippets = []; + for (i = 0; i < data['undefined'].length; i += 1) { + snippets[i] = '' + data['undefined'][i].name + + ' 
' + + data['undefined'][i]['function'] + ' ' + + String(data['undefined'][i].line) + '
'; + } + output.push(snippets.join(', ')); + output.push('
'); + } + if (data.unused) { + output.push('
unused
'); + snippets = []; + for (i = 0; i < data.unused.length; i += 1) { + snippets[i] = '' + data.unused[i].name + ' 
' + + data.unused[i]['function'] + ' ' + + String(data.unused[i].line) + '
'; + } + output.push(snippets.join(', ')); + output.push('
'); + } + output.push('
'); + } + if (data.json) { + output.push('

JSON: bad.

'); + } + return output.join(''); + }; + + + itself.report = function (data) { + var dl, err, i, j, names, output = [], the_function; + + function detail(h, value) { + var comma_needed, singularity; + if (Array.isArray(value)) { + output.push('
' + h + '
'); + value.sort().forEach(function (item) { + if (item !== singularity) { + singularity = item; + output.push((comma_needed ? ', ' : '') + singularity); + comma_needed = true; + } + }); + output.push('
'); + } else if (value) { + output.push('
' + h + '
', value, '
'); + } + } + + output.push('
'); + if (data.urls) { + detail("url", data.urls); + dl = true; + } + if (data.globals) { + detail('global', data.globals); + dl = true; + } else if (xmode === 'style') { + output.push('

CSS.

'); + } else if (data.json && !err) { + output.push('

JSON: good.

'); + } else { + output.push('
No new global variables introduced.
'); + } + if (dl) { + output.push('
'); + } else { + output[0] = ''; + } + + for (i = 0; i < data.functions.length; i += 1) { + the_function = data.functions[i]; + names = []; + if (the_function.params) { + for (j = 0; j < the_function.params.length; j += 1) { + names[j] = the_function.params[j].string; + } + } + output.push('
line ' + + String(the_function.line) + '
' + + the_function.name.entityify() + + '(' + names.join(', ') + ')'); + detail('undefined', the_function['undefined']); + detail('unused', the_function.unused); + detail('closure', the_function.closure); + detail('variable', the_function['var']); + detail('exception', the_function.exception); + detail('outer', the_function.outer); + detail('global', the_function.global); + detail('label', the_function.label); + output.push('
'); + } + return output.join(''); + }; + + itself.properties_report = function (property) { + if (!property) { + return ''; + } + var i, + key, + keys = Object.keys(property).sort(), + length, + output = ['/*properties'], + mem = ' ', + name, + not_first = false; + for (i = 0; i < keys.length; i += 1) { + key = keys[i]; + if (property[key] > 0) { + if (not_first) { + mem += ', '; + } + name = ix.test(key) + ? key + : '\'' + key.replace(nx, sanitize) + '\''; + length += name.length + 2; + if (mem.length + name.length > 80) { + output.push(mem); + mem = ' '; + } + mem += name; + not_first = true; + } + } + output.push(mem, '*/\n'); + return output.join('\n'); + }; + + itself.jslint = itself; + + itself.edition = '2012-05-09'; + + return itself; +}()); diff --git a/js/js.translator/testFiles/kotlinLib/cases/namespace.js b/js/js.translator/testFiles/kotlinLib/cases/namespace.js index e07927c51a0..7e0fbf35c59 100644 --- a/js/js.translator/testFiles/kotlinLib/cases/namespace.js +++ b/js/js.translator/testFiles/kotlinLib/cases/namespace.js @@ -14,11 +14,7 @@ * limitations under the License. */ - - -var foo = Kotlin.createNamespace({initialize:function(){ -} -, box:function(){ +var foo = Kotlin.definePackage({box:function(){ return !false; } }); diff --git a/js/js.translator/testFiles/kotlinLib/cases/namespaceWithClasses.js b/js/js.translator/testFiles/kotlinLib/cases/namespaceWithClasses.js index 6c342414a7a..e18599b54ec 100644 --- a/js/js.translator/testFiles/kotlinLib/cases/namespaceWithClasses.js +++ b/js/js.translator/testFiles/kotlinLib/cases/namespaceWithClasses.js @@ -46,9 +46,7 @@ return {A:A, B:B, C:C}; } (); - var foo = Kotlin.createNamespace(classes, {initialize:function(){ - } - , box:function(){ + var foo = Kotlin.definePackage(classes, {box:function(){ return (new foo.C).get_order() === 'ABC' && (new foo.B).get_order() === 'AB' && (new foo.A).get_order() === 'A'; } }); diff --git a/js/js.translator/testFiles/kotlin_lib.js b/js/js.translator/testFiles/kotlin_lib.js index 53e59a3cad9..2722a7e4201 100644 --- a/js/js.translator/testFiles/kotlin_lib.js +++ b/js/js.translator/testFiles/kotlin_lib.js @@ -14,6 +14,11 @@ * limitations under the License. */ +// todo org.jetbrains.k2js.test.semantics.WebDemoExamples2Test#testBuilder +var kotlin = {set:function (receiver, key, value) { + return receiver.put(key, value); +}}; + (function () { "use strict"; @@ -23,10 +28,10 @@ return obj1.equals(obj2); } } - return (obj1 === obj2); + return obj1 === obj2; }; - Kotlin.defs = {}; + Kotlin.modules = {}; Kotlin.Exceptions = {}; Kotlin.Exception = Kotlin.$createClass(); Kotlin.RuntimeException = Kotlin.$createClass(Kotlin.Exception); @@ -39,121 +44,126 @@ Kotlin.Exceptions.UnsupportedOperationException = Kotlin.$createClass(Kotlin.Exception); Kotlin.Exceptions.IOException = Kotlin.$createClass(Kotlin.Exception); - Kotlin.throwNPE = function() { + Kotlin.throwNPE = function () { throw Kotlin.$new(Kotlin.Exceptions.NullPointerException)(); }; - Kotlin.AbstractList = Kotlin.$createClass({ - set:function (index, value) { - throw Kotlin.$new(Kotlin.Exceptions.UnsupportedOperationException)(); - }, - iterator:function () { - return Kotlin.$new(Kotlin.ArrayIterator)(this); - }, - isEmpty:function () { - return (this.size() === 0); - }, - add:function (element) { - throw Kotlin.$new(Kotlin.Exceptions.UnsupportedOperationException)(); - }, - addAll:function (collection) { - var it = collection.iterator(); - while (it.get_hasNext()) { - this.add(it.next()); - } - }, - remove:function(value) { - for (var i = 0; i < this.$size; ++i) { - if (this.array[i] == value) { - this.removeByIndex(i); - return; - } - } - }, - removeByIndex:function (index) { - throw Kotlin.$new(Kotlin.Exceptions.UnsupportedOperationException)(); - }, - clear:function () { - throw Kotlin.$new(Kotlin.Exceptions.UnsupportedOperationException)(); - }, - contains:function (obj) { - for (var i = 0; i < this.$size; ++i) { - if (Kotlin.equals(this.array[i], obj)) { - return true; - } - } - return false; - } - }); + function throwAbstractFunctionInvocationError() { + throw new TypeError("Function is abstract"); + } - Kotlin.ArrayList = Kotlin.$createClass({ - initialize:function () { - this.array = []; - this.$size = 0; + Kotlin.Iterator = Kotlin.$createClass({ + initialize: function () { }, - get:function (index) { - if ((index < 0) || (index >= this.$size)) { - throw Kotlin.Exceptions.IndexOutOfBounds; - } - return (this.array)[index]; + next: throwAbstractFunctionInvocationError, + get_hasNext: throwAbstractFunctionInvocationError + }); + + var ArrayIterator = Kotlin.$createClass(Kotlin.Iterator, { + initialize: function (array) { + this.array = array; + this.size = array.length; + this.index = 0; }, - set:function (index, value) { - if ((index < 0) || (index >= this.$size)) { - throw Kotlin.Exceptions.IndexOutOfBounds; - } - (this.array)[index] = value; + next: function () { + return this.array[this.index++]; }, - size:function () { - return this.$size; + get_hasNext: function () { + return this.index < this.size; + } + }); + + var ListIterator = Kotlin.$createClass(ArrayIterator, { + initialize: function (list) { + this.list = list; + this.size = list.size(); + this.index = 0; }, - iterator:function () { - return Kotlin.$new(Kotlin.ArrayIterator)(this); + next: function () { + return this.list.get(this.index++); }, - isEmpty:function () { - return (this.$size === 0); + get_hasNext: function () { + return this.index < this.size; + } + }); + + Kotlin.AbstractList = Kotlin.$createClass({ + iterator: function () { + return Kotlin.$new(ListIterator)(this); }, - add:function (element) { - this.array[this.$size++] = element; + isEmpty: function () { + return this.size() == 0; }, - addAll:function (collection) { + addAll: function (collection) { var it = collection.iterator(); while (it.get_hasNext()) { this.add(it.next()); } }, - remove:function(value) { - for (var i = 0; i < this.$size; ++i) { - if (this.array[i] == value) { - this.removeByIndex(i); - return; - } + remove: function (o) { + var index = this.indexOf(o); + if (index != -1) { + this.removeAt(index); } }, - removeByIndex:function (index) { - for (var i = index; i < this.$size - 1; ++i) { - this.array[i] = this.array[i + 1]; - } - this.$size--; - }, - clear:function () { - this.array = []; - this.$size = 0; - }, - contains:function (obj) { - for (var i = 0; i < this.$size; ++i) { - if (Kotlin.equals(this.array[i], obj)) { - return true; - } - } - return false; + contains: function (o) { + return this.indexOf(o) != -1; } }); + Kotlin.ArrayList = Kotlin.$createClass(Kotlin.AbstractList, { + initialize: function () { + this.array = []; + this.$size = 0; + }, + get: function (index) { + if (index < 0 || index >= this.$size) { + throw Kotlin.Exceptions.IndexOutOfBounds; + } + return this.array[index]; + }, + set: function (index, value) { + if (index < 0 || index >= this.$size) { + throw Kotlin.Exceptions.IndexOutOfBounds; + } + this.array[index] = value; + }, + toArray: function () { + return this.array.slice(0, this.$size); + }, + size: function () { + return this.$size; + }, + iterator: function () { + return Kotlin.arrayIterator(this.array); + }, + add: function (element) { + this.array[this.$size++] = element; + }, + addAt: function (index, element) { + this.array.splice(index, 0, element); + }, + removeAt: function (index) { + this.array.splice(index, 1); + this.$size--; + }, + clear: function () { + this.array.length = 0; + this.$size = 0; + }, + indexOf: function (o) { + for (var i = 0, n = this.$size; i < n; ++i) { + if (Kotlin.equals(this.array[i], o)) { + return i; + } + } + return -1; + } + }); Kotlin.parseInt = function (str) { return parseInt(str, 10); - } - ; + }; Kotlin.safeParseInt = function(str) { var r = parseInt(str, 10); @@ -207,57 +217,32 @@ Kotlin.System.out().print(s); }; - Kotlin.AbstractFunctionInvocationError = Kotlin.$createClass(); - - Kotlin.Iterator = Kotlin.$createClass({ - initialize:function () { - }, - next:function () { - throw Kotlin.$new(Kotlin.AbstractFunctionInvocationError)(); - }, - get_hasNext:function () { - throw Kotlin.$new(Kotlin.AbstractFunctionInvocationError)(); - } - }); - - Kotlin.Runnable = Kotlin.$createClass({ - run:function () { - throw Kotlin.$new(Kotlin.AbstractFunctionInvocationError)(); - } - }); - - Kotlin.ArrayIterator = Kotlin.$createClass(Kotlin.Iterator, { - initialize: function (array) { - this.array = array; - this.index = 0; - }, - next: function () { - return this.array.get(this.index++); - }, - get_hasNext: function () { - return this.array.size() > this.index; - } - }); - Kotlin.RangeIterator = Kotlin.$createClass(Kotlin.Iterator, { - initialize:function (start, count, reversed) { + initialize: function (start, count, reversed) { this.$start = start; this.$count = count; this.$reversed = reversed; this.$i = this.get_start(); - }, get_start:function () { + }, + get_start: function () { return this.$start; - }, get_count:function () { + }, + get_count: function () { return this.$count; - }, set_count:function (tmp$0) { + }, + set_count: function (tmp$0) { this.$count = tmp$0; - }, get_reversed:function () { + }, + get_reversed: function () { return this.$reversed; - }, get_i:function () { + }, + get_i: function () { return this.$i; - }, set_i:function (tmp$0) { + }, + set_i: function (tmp$0) { this.$i = tmp$0; - }, next:function () { + }, + next: function () { this.set_count(this.get_count() - 1); if (this.get_reversed()) { this.set_i(this.get_i() - 1); @@ -273,44 +258,51 @@ } }); - Kotlin.NumberRange = Kotlin.$createClass({initialize:function (start, size, reversed) { - this.$start = start; - this.$size = size; - this.$reversed = reversed; - }, get_start:function () { - return this.$start; - }, get_size:function () { - return this.$size; - }, get_reversed:function () { - return this.$reversed; - }, get_end:function () { - return this.get_reversed() ? this.get_start() - this.get_size() + 1 : this.get_start() + this.get_size() - 1; - }, contains:function (number) { - if (this.get_reversed()) { - return number <= this.get_start() && number > this.get_start() - this.get_size(); + Kotlin.NumberRange = Kotlin.$createClass({ + initialize: function (start, size, reversed) { + this.$start = start; + this.$size = size; + this.$reversed = reversed; + }, + get_start: function () { + return this.$start; + }, + get_size: function () { + return this.$size; + }, + get_reversed: function () { + return this.$reversed; + }, + get_end: function () { + return this.get_reversed() ? this.get_start() - this.get_size() + 1 : this.get_start() + this.get_size() - 1; + }, + contains: function (number) { + if (this.get_reversed()) { + return number <= this.get_start() && number > this.get_start() - this.get_size(); + } + else { + return number >= this.get_start() && number < this.get_start() + this.get_size(); + } + }, + iterator: function () { + return Kotlin.$new(Kotlin.RangeIterator)(this.get_start(), this.get_size(), this.get_reversed()); } - else { - return number >= this.get_start() && number < this.get_start() + this.get_size(); - } - }, iterator:function () { - return Kotlin.$new(Kotlin.RangeIterator)(this.get_start(), this.get_size(), this.get_reversed()); - } }); - Kotlin.Comparator = Kotlin.$createClass( - { - initialize: function () { - }, - compare: function (el1, el2) { - throw Kotlin.$new(Kotlin.AbstractFunctionInvocationError)(); - } + Kotlin.Comparator = Kotlin.$createClass({ + initialize: function () { + }, + compare: throwAbstractFunctionInvocationError + }); + + var ComparatorImpl = Kotlin.$createClass(Kotlin.Comparator, { + initialize: function (comparator) { + this.compare = comparator; } - ); + }); Kotlin.comparator = function (f) { - var result = Kotlin.$new(Kotlin.Comparator)(); - result.compare = f; - return result; + return Kotlin.$new(ComparatorImpl)(f); }; Kotlin.collectionsMax = function (col, comp) { @@ -369,25 +361,8 @@ return Kotlin.$new(Kotlin.NumberRange)(0, arr.length); }; - var intrinsicArrayIterator = Kotlin.$createClass( - Kotlin.Iterator, - { - initialize: function (arr) { - this.arr = arr; - this.len = arr.length; - this.i = 0; - }, - next: function () { - return this.arr[this.i++]; - }, - get_hasNext: function () { - return this.i < this.len; - } - } - ); - - Kotlin.arrayIterator = function (arr) { - return Kotlin.$new(intrinsicArrayIterator)(arr); + Kotlin.arrayIterator = function (array) { + return Kotlin.$new(ArrayIterator)(array); }; Kotlin.toString = function (obj) { @@ -796,14 +771,11 @@ Kotlin.HashTable = Hashtable; })(); - Kotlin.HashMap = Kotlin.$createClass( - { - initialize:function () { - Kotlin.HashTable.call(this); - } - } - ); - + Kotlin.HashMap = Kotlin.$createClass({ + initialize: function () { + Kotlin.HashTable.call(this); + } + }); (function () { function HashSet(hashingFunction, equalityFunction) { diff --git a/js/js.translator/testFiles/kotlin_lib_ecma3.js b/js/js.translator/testFiles/kotlin_lib_ecma3.js index f20ce1955b2..2ec6e085be0 100644 --- a/js/js.translator/testFiles/kotlin_lib_ecma3.js +++ b/js/js.translator/testFiles/kotlin_lib_ecma3.js @@ -1,10 +1,10 @@ -/* Prototype JavaScript framework, version 1.6.1 - * (c) 2005-2009 Sam Stephenson - * - * Prototype is freely distributable under the terms of an MIT-style license. - * For details, see the Prototype web site: http://www.prototypejs.org/ - * - *--------------------------------------------------------------------------*/ +/* Prototype JavaScript framework, version 1.6.1 +* (c) 2005-2009 Sam Stephenson +* +* Prototype is freely distributable under the terms of an MIT-style license. +* For details, see the Prototype web site: http://www.prototypejs.org/ +* +*--------------------------------------------------------------------------*/ var Kotlin = {}; (function () { @@ -12,13 +12,14 @@ var Kotlin = {}; var emptyFunction = function () { }; - function $A(iterable) { - if (!iterable) return []; - if ('toArray' in Object(iterable)) return iterable.toArray(); - var length = iterable.length || 0, results = new Array(length); - while (length--) results[length] = iterable[length]; - return results; - } + Kotlin.argumentsToArrayLike = function (args) { + var n = args.length; + var result = new Array(n); + while (n--) { + result[n] = args[n]; + } + return result; + }; (function () { function extend(destination, source) { @@ -62,11 +63,6 @@ var Kotlin = {}; return array; } - function merge(array, args) { - array = slice.call(array, 0); - return update(array, args); - } - function argumentNames() { var names = this.toString().match(/^[\s\(]*function[^(]*\(([^)]*)\)/)[1] .replace(/\/\/.*?[\r\n]|\/\*(?:.|[\r\n])*?\*\//g, '') @@ -74,15 +70,6 @@ var Kotlin = {}; return names.length == 1 && !names[0] ? [] : names; } - function bind(context) { - if (arguments.length < 2 && Object.isUndefined(arguments[0])) return this; - var __method = this, args = slice.call(arguments, 1); - return function () { - var a = merge(args, arguments); - return __method.apply(context, a); - }; - } - function bindAsEventListener(context) { var __method = this, args = slice.call(arguments, 1); return function (event) { @@ -101,7 +88,6 @@ var Kotlin = {}; return { argumentNames: argumentNames, - bind: bind, bindAsEventListener: bindAsEventListener, wrap: wrap }; @@ -129,21 +115,18 @@ var Kotlin = {}; var property = properties[i]; object[property] = source[property]; } - return this; } return function () { - var result = {}; - for (var i = 0, length = arguments.length; i < length; i++) { + var result = arguments[0]; + for (var i = 1, n = arguments.length; i < n; i++) { add(result, arguments[i]); } return result; } })(); - Kotlin.createNamespace = function () { - return Kotlin.createTrait.apply(null, arguments); - }; + Kotlin.definePackage = Kotlin.createTrait; Kotlin.createClass = (function () { var METHODS = {addMethods: addMethods}; @@ -152,7 +135,7 @@ var Kotlin = {}; } function create() { - var parent = null, properties = $A(arguments); + var parent = null, properties = Kotlin.argumentsToArrayLike(arguments); if (typeof (properties[0]) == "function") { parent = properties.shift(); } @@ -243,4 +226,12 @@ var Kotlin = {}; var singletonClass = Kotlin.createClass.apply(null, arguments); return new singletonClass(); }; -})(); + + Kotlin.defineModule = function (id, module) { + if ((id in Kotlin.modules) && (id !== "JS_TESTS")) { + throw Kotlin.$new(Kotlin.Exceptions.IllegalArgumentException)(); + } + + Kotlin.modules[id] = module; + }; +})(); \ No newline at end of file diff --git a/js/js.translator/testFiles/kotlin_lib_ecma5.js b/js/js.translator/testFiles/kotlin_lib_ecma5.js index 7eb31c14ebc..aee4fbe3c72 100644 --- a/js/js.translator/testFiles/kotlin_lib_ecma5.js +++ b/js/js.translator/testFiles/kotlin_lib_ecma5.js @@ -20,89 +20,75 @@ var Kotlin = {}; return false; }; - // todo compatibility for opera https://raw.github.com/gist/1000718/es5compat-gs.js + // as separated function to reduce scope + function createConstructor(proto, initializer) { + return function () { + var o = Object.create(proto); + if (initializer != null) { + initializer.apply(o, arguments); + } + + Object.seal(o); + return o; + }; + } + + function computeProto(bases, properties) { + var proto = null; + for (var i = 0, n = bases.length; i < n; i++) { + var base = bases[i]; + var baseProto = base.proto; + if (baseProto == null || base.properties == null) { + continue; + } + + if (!proto) { + proto = Object.create(baseProto, properties || undefined); + continue; + } + Object.defineProperties(proto, base.properties); + // todo test A -> B, C(->D) *properties from D is not yet added to proto* + } + + return proto; + } + // proto must be created for class even if it is not needed (requires for is operator) - Kotlin.createClass = (function () { - function create(bases, initializer, properties) { - var proto; - var baseInitializer = null; - var isTrait = initializer == null; - if (!bases) { - proto = !properties && isTrait ? null : Object.create(null, properties || undefined); + Kotlin.createClass = function (bases, initializer, properties) { + var proto; + var baseInitializer = null; + var isTrait = initializer == null; + if (!bases) { + proto = !properties && isTrait ? null : Object.create(null, properties || undefined); + } + else if (!Array.isArray(bases)) { + baseInitializer = bases.initializer; + proto = !properties && isTrait ? bases.proto : Object.create(bases.proto, properties || undefined); + } + else { + proto = computeProto(bases, properties); + // first is superclass, other are traits + baseInitializer = bases[0].initializer; + // all bases are traits without properties + if (proto == null && !isTrait) { + proto = Object.create(null, properties || undefined); } - else if (!Array.isArray(bases)) { - baseInitializer = bases.initializer; - proto = !properties && isTrait ? bases.proto : Object.create(bases.proto, properties || undefined); - } - else { - proto = computeProto(bases, properties); - // first is superclass, other are traits - baseInitializer = bases[0].initializer; - // all bases are traits without properties - if (proto == null && !isTrait) { - proto = Object.create(null, properties || undefined); - } - } - - var constructor = createConstructor(proto, initializer); - Object.defineProperty(constructor, "proto", {value: proto}); - Object.defineProperty(constructor, "properties", {value: properties || null}); - // null for trait - if (!isTrait) { - Object.defineProperty(constructor, "initializer", {value: initializer}); - - Object.defineProperty(initializer, "baseInitializer", {value: baseInitializer}); - Object.seal(initializer); - } - - Object.seal(constructor); - return constructor; } - // as separate function to reduce scope - function createConstructor(proto, initializer) { - return function () { - var o = Object.create(proto); - if (initializer != null) { - initializer.apply(o, arguments); - } - if (initializer == null || !initializer.hasOwnProperty("skipSeal")) { - Object.seal(o); - } - return o; - }; + var constructor = createConstructor(proto, initializer); + Object.defineProperty(constructor, "proto", {value: proto}); + Object.defineProperty(constructor, "properties", {value: properties || null}); + // null for trait + if (!isTrait) { + Object.defineProperty(constructor, "initializer", {value: initializer}); + + Object.defineProperty(initializer, "baseInitializer", {value: baseInitializer}); + Object.freeze(initializer); } - function computeProto(bases, properties) { - var proto = null; - for (var i = 0, n = bases.length; i < n; i++) { - var base = bases[i]; - var baseProto = base.proto; - if (baseProto == null || base.properties == null) { - continue; - } - - if (!proto) { - proto = Object.create(baseProto, properties || undefined); - continue; - } - - // chrome bug related to getOwnPropertyDescriptor (sometimes returns undefined), so, we keep properties - //var names = Object.getOwnPropertyNames(baseProto); - //for (var j = 0, k = names.length; j < k; j++) { - // var descriptor = Object.getOwnPropertyDescriptor(baseProto, names[i]); - // Object.defineProperty(proto, names[i], descriptor); - //} - Object.defineProperties(proto, base.properties); - - // todo test A -> B, C(->D) *properties from D is not yet added to proto* - } - - return proto; - } - - return create; - })(); + Object.freeze(constructor); + return constructor; + }; Kotlin.createObject = function (initializer, properties) { var o = Object.create(null, properties || undefined); @@ -110,15 +96,18 @@ var Kotlin = {}; return o; }; - Kotlin.createNamespace = function (initializer, properties, classesAndNestedNamespaces) { - var o = Object.create(null, properties || undefined); - Object.defineProperty(o, "initialize", {value: initializer}); - var keys = Object.keys(classesAndNestedNamespaces); - for (var i = 0, n = keys.length; i < n; i++) { - var name = keys[i]; - Object.defineProperty(o, name, {value: classesAndNestedNamespaces[name]}); + + Kotlin.definePackage = function (functionsAndClasses, nestedNamespaces) { + var p = Object.create(null, functionsAndClasses || undefined); + if (nestedNamespaces) { + var keys = Object.keys(nestedNamespaces); + for (var i = 0, n = keys.length; i < n; i++) { + var name = keys[i]; + Object.defineProperty(p, name, {value:nestedNamespaces[name]}); + } } - return o; + + return p; }; Kotlin.$new = function (f) { @@ -126,7 +115,7 @@ var Kotlin = {}; }; Kotlin.$createClass = function (parent, properties) { - if (typeof (parent) != "function") { + if (parent !== null && typeof (parent) != "function") { properties = parent; parent = null; } @@ -161,10 +150,16 @@ var Kotlin = {}; } } - if (initializer) { - Object.defineProperty(initializer, "skipSeal", {value: true}); - } - return Kotlin.createClass(parent || null, initializer, descriptors); }; + + Kotlin.defineModule = function (id, module) { + var isTestMode = id === "JS_TESTS"; + if ((id in Kotlin.modules) && (!isTestMode)) { + throw Kotlin.$new(Kotlin.Exceptions.IllegalArgumentException)(); + } + + Object.freeze(module); + Object.defineProperty(Kotlin.modules, id, {value: module, writable: isTestMode}); + }; })(); diff --git a/js/js.translator/testFiles/multiFile/cases/classOfTheSameNameInAnotherPackage/A.kt b/js/js.translator/testFiles/multiFile/cases/classOfTheSameNameInAnotherPackage/A.kt new file mode 100644 index 00000000000..e046cb6ffe3 --- /dev/null +++ b/js/js.translator/testFiles/multiFile/cases/classOfTheSameNameInAnotherPackage/A.kt @@ -0,0 +1,7 @@ +package foo + +open class A() { + fun f() = 3 +} + +fun box() = (A().f() + bar.A().f()) == 9 \ No newline at end of file diff --git a/js/js.translator/testFiles/multiFile/cases/classOfTheSameNameInAnotherPackage/B.kt b/js/js.translator/testFiles/multiFile/cases/classOfTheSameNameInAnotherPackage/B.kt new file mode 100644 index 00000000000..b9d474d092e --- /dev/null +++ b/js/js.translator/testFiles/multiFile/cases/classOfTheSameNameInAnotherPackage/B.kt @@ -0,0 +1,5 @@ +package bar + +open class A() { + fun f() = 6 +} \ No newline at end of file diff --git a/js/js.translator/testFiles/object/cases/objectInObject.kt b/js/js.translator/testFiles/object/cases/objectInObject.kt new file mode 100644 index 00000000000..1d467b1b2a2 --- /dev/null +++ b/js/js.translator/testFiles/object/cases/objectInObject.kt @@ -0,0 +1,17 @@ +package foo + +object A { + val query = object {val status = "complete"} +} + +object B { + private val ov = "d" + val query = object {val status = "complete" + ov} +} + +class C { + val query = object {val status = "complete"} +} + +fun box() = A.query.status == "complete" && B.query.status == "completed" && C().query.status == "completed" + diff --git a/js/js.translator/testFiles/propertyAccess/cases/enumerable.kt b/js/js.translator/testFiles/propertyAccess/cases/enumerable.kt new file mode 100644 index 00000000000..899f049cd9e --- /dev/null +++ b/js/js.translator/testFiles/propertyAccess/cases/enumerable.kt @@ -0,0 +1,28 @@ +package foo + +import js.enumerable +import js.native + +native +fun _enumerate(o:T):T = noImpl + +native +fun _findFirst(o:Any):T = noImpl + +enumerable +class Test() { + val a:Int = 100 + val b:String = "s" +} + +class P() { + enumerable + val a:Int = 100 + val b:String = "s" +} + +fun box():Boolean { + val test = _enumerate(Test()) + val p = _enumerate(P()) + return (100 == test.a && "s" == test.b) && p.a == 100 && _findFirst(object {val test = 100}) == 100; +} diff --git a/js/js.translator/testFiles/propertyAccess/cases/overloadedOverriddenFunctionPropertyName.kt b/js/js.translator/testFiles/propertyAccess/cases/overloadedOverriddenFunctionPropertyName.kt new file mode 100644 index 00000000000..f43556f8b5b --- /dev/null +++ b/js/js.translator/testFiles/propertyAccess/cases/overloadedOverriddenFunctionPropertyName.kt @@ -0,0 +1,14 @@ +package foo + +trait I { + fun test():String +} + +class P : I { + override fun test():String {return "a" + test("b")} + private fun test(p:String):String {return p} +} + +fun box():Boolean { + return P().test() == "ab" +} \ No newline at end of file diff --git a/js/js.translator/testFiles/propertyAccess/enumerate.js b/js/js.translator/testFiles/propertyAccess/enumerate.js new file mode 100644 index 00000000000..d8c8d0491d7 --- /dev/null +++ b/js/js.translator/testFiles/propertyAccess/enumerate.js @@ -0,0 +1,13 @@ +function _enumerate(o) { + var r = {}; + for (var p in o) { + r[p] = o[p]; + } + return r; +} + +function _findFirst(o) { + for (var p in o) { + return o[p]; + } +}