diff --git a/build-common/test/org/jetbrains/kotlin/serialization/js/DebugJsProtoBuf.java b/build-common/test/org/jetbrains/kotlin/serialization/js/DebugJsProtoBuf.java index 939925fe0b1..77397409943 100644 --- a/build-common/test/org/jetbrains/kotlin/serialization/js/DebugJsProtoBuf.java +++ b/build-common/test/org/jetbrains/kotlin/serialization/js/DebugJsProtoBuf.java @@ -746,6 +746,10 @@ public final class DebugJsProtoBuf { * COMMON_JS = 3; */ COMMON_JS(2, 3), + /** + * UMD = 4; + */ + UMD(3, 4), ; /** @@ -760,6 +764,10 @@ public final class DebugJsProtoBuf { * COMMON_JS = 3; */ public static final int COMMON_JS_VALUE = 3; + /** + * UMD = 4; + */ + public static final int UMD_VALUE = 4; public final int getNumber() { return value; } @@ -769,6 +777,7 @@ public final class DebugJsProtoBuf { case 1: return PLAIN; case 2: return AMD; case 3: return COMMON_JS; + case 4: return UMD; default: return null; } } @@ -2301,42 +2310,43 @@ public final class DebugJsProtoBuf { "rg.jetbrains.kotlin.serialization.js\0320co" + "re/deserialization/src/descriptors.debug" + ".proto\"!\n\007Classes\022\026\n\nclass_name\030\001 \003(\005B\002\020" + - "\001\"\214\002\n\007Library\022G\n\005entry\030\001 \003(\01328.org.jetbr" + + "\001\"\225\002\n\007Library\022G\n\005entry\030\001 \003(\01328.org.jetbr" + "ains.kotlin.serialization.js.Library.Fil" + "eEntry\022H\n\004kind\030\002 \001(\01623.org.jetbrains.kot" + "lin.serialization.js.Library.Kind:\005PLAIN" + "\022\027\n\017importedModules\030\003 \003(\t\032*\n\tFileEntry\022\014" + - "\n\004path\030\001 \002(\t\022\017\n\007content\030\002 \002(\014\")\n\004Kind\022\t\n", - "\005PLAIN\020\001\022\007\n\003AMD\020\002\022\r\n\tCOMMON_JS\020\003:t\n\020clas" + - "s_annotation\022).org.jetbrains.kotlin.seri" + - "alization.Class\030\202\001 \003(\0132..org.jetbrains.k" + - "otlin.serialization.Annotation:\200\001\n\026const" + - "ructor_annotation\022/.org.jetbrains.kotlin" + - ".serialization.Constructor\030\202\001 \003(\0132..org." + - "jetbrains.kotlin.serialization.Annotatio" + - "n:z\n\023function_annotation\022,.org.jetbrains" + - ".kotlin.serialization.Function\030\202\001 \003(\0132.." + - "org.jetbrains.kotlin.serialization.Annot", - "ation:z\n\023property_annotation\022,.org.jetbr" + - "ains.kotlin.serialization.Property\030\202\001 \003(" + - "\0132..org.jetbrains.kotlin.serialization.A" + - "nnotation:\210\001\n\022compile_time_value\022,.org.j" + - "etbrains.kotlin.serialization.Property\030\203" + - "\001 \001(\0132=.org.jetbrains.kotlin.serializati" + - "on.Annotation.Argument.Value:}\n\025enum_ent" + - "ry_annotation\022-.org.jetbrains.kotlin.ser" + - "ialization.EnumEntry\030\202\001 \003(\0132..org.jetbra" + - "ins.kotlin.serialization.Annotation:\201\001\n\024", - "parameter_annotation\0222.org.jetbrains.kot" + - "lin.serialization.ValueParameter\030\202\001 \003(\0132" + - "..org.jetbrains.kotlin.serialization.Ann" + - "otation:r\n\017type_annotation\022(.org.jetbrai" + - "ns.kotlin.serialization.Type\030\202\001 \003(\0132..or" + - "g.jetbrains.kotlin.serialization.Annotat" + - "ion:\205\001\n\031type_parameter_annotation\0221.org." + - "jetbrains.kotlin.serialization.TypeParam" + - "eter\030\202\001 \003(\0132..org.jetbrains.kotlin.seria" + - "lization.AnnotationB\021B\017DebugJsProtoBuf" + "\n\004path\030\001 \002(\t\022\017\n\007content\030\002 \002(\014\"2\n\004Kind\022\t\n", + "\005PLAIN\020\001\022\007\n\003AMD\020\002\022\r\n\tCOMMON_JS\020\003\022\007\n\003UMD\020" + + "\004:t\n\020class_annotation\022).org.jetbrains.ko" + + "tlin.serialization.Class\030\202\001 \003(\0132..org.je" + + "tbrains.kotlin.serialization.Annotation:" + + "\200\001\n\026constructor_annotation\022/.org.jetbrai" + + "ns.kotlin.serialization.Constructor\030\202\001 \003" + + "(\0132..org.jetbrains.kotlin.serialization." + + "Annotation:z\n\023function_annotation\022,.org." + + "jetbrains.kotlin.serialization.Function\030" + + "\202\001 \003(\0132..org.jetbrains.kotlin.serializat", + "ion.Annotation:z\n\023property_annotation\022,." + + "org.jetbrains.kotlin.serialization.Prope" + + "rty\030\202\001 \003(\0132..org.jetbrains.kotlin.serial" + + "ization.Annotation:\210\001\n\022compile_time_valu" + + "e\022,.org.jetbrains.kotlin.serialization.P" + + "roperty\030\203\001 \001(\0132=.org.jetbrains.kotlin.se" + + "rialization.Annotation.Argument.Value:}\n" + + "\025enum_entry_annotation\022-.org.jetbrains.k" + + "otlin.serialization.EnumEntry\030\202\001 \003(\0132..o" + + "rg.jetbrains.kotlin.serialization.Annota", + "tion:\201\001\n\024parameter_annotation\0222.org.jetb" + + "rains.kotlin.serialization.ValueParamete" + + "r\030\202\001 \003(\0132..org.jetbrains.kotlin.serializ" + + "ation.Annotation:r\n\017type_annotation\022(.or" + + "g.jetbrains.kotlin.serialization.Type\030\202\001" + + " \003(\0132..org.jetbrains.kotlin.serializatio" + + "n.Annotation:\205\001\n\031type_parameter_annotati" + + "on\0221.org.jetbrains.kotlin.serialization." + + "TypeParameter\030\202\001 \003(\0132..org.jetbrains.kot" + + "lin.serialization.AnnotationB\021B\017DebugJsP", + "rotoBuf" }; org.jetbrains.kotlin.protobuf.Descriptors.FileDescriptor.InternalDescriptorAssigner assigner = new org.jetbrains.kotlin.protobuf.Descriptors.FileDescriptor.InternalDescriptorAssigner() { diff --git a/build.xml b/build.xml index 5bf2a3e231a..156d0e2aa92 100644 --- a/build.xml +++ b/build.xml @@ -319,6 +319,7 @@ + @@ -358,7 +359,8 @@ prettyprint="true" languagein="ECMASCRIPT5_STRICT" warning="${warningLevel}" - output="${js.stdlib.output.dir}/kotlin.js"> + output="${js.stdlib.output.dir}/kotlin.js" + outputWrapperFile="${stdlib.js.dir}/closure-wrapper.txt"> diff --git a/compiler/cli/cli-common/src/org/jetbrains/kotlin/cli/common/arguments/K2JSCompilerArguments.java b/compiler/cli/cli-common/src/org/jetbrains/kotlin/cli/common/arguments/K2JSCompilerArguments.java index ca0070736ce..97abbb235e0 100644 --- a/compiler/cli/cli-common/src/org/jetbrains/kotlin/cli/common/arguments/K2JSCompilerArguments.java +++ b/compiler/cli/cli-common/src/org/jetbrains/kotlin/cli/common/arguments/K2JSCompilerArguments.java @@ -49,7 +49,7 @@ public class K2JSCompilerArguments extends CommonCompilerArguments { public String target; @Argument(value = "module-kind", description = "Kind of a module generated by compiler. Supported values are: plain (by default), " + - "amd, commonjs.") + "amd, commonjs, umd.") public String moduleKind; @Nullable diff --git a/compiler/cli/src/org/jetbrains/kotlin/cli/js/K2JSCompiler.java b/compiler/cli/src/org/jetbrains/kotlin/cli/js/K2JSCompiler.java index ce8d40462d4..262b6ef8f08 100644 --- a/compiler/cli/src/org/jetbrains/kotlin/cli/js/K2JSCompiler.java +++ b/compiler/cli/src/org/jetbrains/kotlin/cli/js/K2JSCompiler.java @@ -287,6 +287,9 @@ public class K2JSCompiler extends CLICompiler { else if (moduleKindName.equals("commonjs")) { moduleKind = ModuleKind.COMMON_JS; } + else if (moduleKindName.equals("umd")) { + moduleKind = ModuleKind.UMD; + } else { messageCollector.report(CompilerMessageSeverity.ERROR, "Unknown module kind: " + moduleKindName + ". " + "valid values are: plain, amd, commonjs", diff --git a/js/js.serializer/src/js.proto b/js/js.serializer/src/js.proto index d4bbc76b028..4c332e0a50b 100644 --- a/js/js.serializer/src/js.proto +++ b/js/js.serializer/src/js.proto @@ -66,9 +66,10 @@ message Library { } enum Kind { - PLAIN = 1; - AMD = 2; - COMMON_JS = 3; + PLAIN = 1; + AMD = 2; + COMMON_JS = 3; + UMD = 4; } repeated FileEntry entry = 1; diff --git a/js/js.serializer/src/org/jetbrains/kotlin/serialization/js/JsProtoBuf.java b/js/js.serializer/src/org/jetbrains/kotlin/serialization/js/JsProtoBuf.java index fae5362f7d1..97ee2c8d66b 100644 --- a/js/js.serializer/src/org/jetbrains/kotlin/serialization/js/JsProtoBuf.java +++ b/js/js.serializer/src/org/jetbrains/kotlin/serialization/js/JsProtoBuf.java @@ -650,6 +650,10 @@ public final class JsProtoBuf { * COMMON_JS = 3; */ COMMON_JS(2, 3), + /** + * UMD = 4; + */ + UMD(3, 4), ; /** @@ -664,6 +668,10 @@ public final class JsProtoBuf { * COMMON_JS = 3; */ public static final int COMMON_JS_VALUE = 3; + /** + * UMD = 4; + */ + public static final int UMD_VALUE = 4; public final int getNumber() { return value; } @@ -673,6 +681,7 @@ public final class JsProtoBuf { case 1: return PLAIN; case 2: return AMD; case 3: return COMMON_JS; + case 4: return UMD; default: return null; } } diff --git a/js/js.serializer/src/org/jetbrains/kotlin/serialization/js/KotlinJavascriptSerializationUtil.kt b/js/js.serializer/src/org/jetbrains/kotlin/serialization/js/KotlinJavascriptSerializationUtil.kt index 0c65266edd6..1fd95ce8598 100644 --- a/js/js.serializer/src/org/jetbrains/kotlin/serialization/js/KotlinJavascriptSerializationUtil.kt +++ b/js/js.serializer/src/org/jetbrains/kotlin/serialization/js/KotlinJavascriptSerializationUtil.kt @@ -94,6 +94,7 @@ object KotlinJavascriptSerializationUtil { ModuleKind.PLAIN -> JsProtoBuf.Library.Kind.PLAIN ModuleKind.AMD -> JsProtoBuf.Library.Kind.AMD ModuleKind.COMMON_JS -> JsProtoBuf.Library.Kind.COMMON_JS + ModuleKind.UMD -> JsProtoBuf.Library.Kind.UMD } importedModules.forEach { contentBuilder.addImportedModules(it) } @@ -249,6 +250,7 @@ private fun ByteArray.toContentMap(): JsModuleProto { null, JsProtoBuf.Library.Kind.PLAIN -> ModuleKind.PLAIN JsProtoBuf.Library.Kind.AMD -> ModuleKind.AMD JsProtoBuf.Library.Kind.COMMON_JS -> ModuleKind.COMMON_JS + JsProtoBuf.Library.Kind.UMD -> ModuleKind.UMD }, imported = content.importedModulesList ) diff --git a/js/js.serializer/src/org/jetbrains/kotlin/serialization/js/ModuleKind.java b/js/js.serializer/src/org/jetbrains/kotlin/serialization/js/ModuleKind.java index e6b222c2ab0..fcdb3a0eb94 100644 --- a/js/js.serializer/src/org/jetbrains/kotlin/serialization/js/ModuleKind.java +++ b/js/js.serializer/src/org/jetbrains/kotlin/serialization/js/ModuleKind.java @@ -21,5 +21,6 @@ package org.jetbrains.kotlin.serialization.js; public enum ModuleKind { PLAIN, AMD, - COMMON_JS + COMMON_JS, + UMD } diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/general/Translation.java b/js/js.translator/src/org/jetbrains/kotlin/js/translate/general/Translation.java index eecdc8d9977..61421168975 100644 --- a/js/js.translator/src/org/jetbrains/kotlin/js/translate/general/Translation.java +++ b/js/js.translator/src/org/jetbrains/kotlin/js/translate/general/Translation.java @@ -42,7 +42,6 @@ import org.jetbrains.kotlin.js.translate.test.JSTestGenerator; import org.jetbrains.kotlin.js.translate.test.JSTester; import org.jetbrains.kotlin.js.translate.test.QUnitTester; import org.jetbrains.kotlin.js.translate.utils.JsAstUtils; -import org.jetbrains.kotlin.js.translate.utils.JsDescriptorUtils; import org.jetbrains.kotlin.js.translate.utils.mutator.AssignToExpressionMutator; import org.jetbrains.kotlin.psi.KtDeclarationWithBody; import org.jetbrains.kotlin.psi.KtExpression; @@ -50,6 +49,7 @@ import org.jetbrains.kotlin.psi.KtFile; import org.jetbrains.kotlin.psi.KtNamedFunction; import org.jetbrains.kotlin.resolve.BindingTrace; import org.jetbrains.kotlin.resolve.bindingContextUtil.BindingContextUtilsKt; +import org.jetbrains.kotlin.serialization.js.ModuleKind; import org.jetbrains.kotlin.utils.ExceptionUtilsKt; import java.util.ArrayList; @@ -188,6 +188,16 @@ public final class Translation { } } + private static class ImportedModule { + final String id; + final JsName name; + + public ImportedModule(String id, JsName name) { + this.id = id; + this.name = name; + } + } + @NotNull private static TranslationContext doGenerateAst( @NotNull BindingTrace bindingTrace, @@ -211,27 +221,18 @@ public final class Translation { mayBeGenerateTests(files, config, rootBlock, context); - JsBlock block = program.getGlobalBlock(); - // Invoke function passing modules as arguments // This should help minifier tool to recognize references to this modules as local variables and make them shorter. - List invocationArgs = new ArrayList(staticContext.getImportedModules().size() + 1); + List importedModuleList = new ArrayList(); JsName kotlinName = program.getScope().declareName(Namer.KOTLIN_NAME); rootFunction.getParameters().add(new JsParameter((kotlinName))); - invocationArgs.add(kotlinName.makeRef()); + importedModuleList.add(new ImportedModule(Namer.KOTLIN_LOWER_NAME, kotlinName)); for (String importedModule : staticContext.getImportedModules().keySet()) { rootFunction.getParameters().add(new JsParameter(staticContext.getImportedModules().get(importedModule))); - JsName globalId = program.getScope().declareName(Namer.suggestedModuleName(importedModule)); - invocationArgs.add(JsAstUtils.pureFqn(globalId, null)); + importedModuleList.add(new ImportedModule(importedModule, globalId)); } - JsInvocation invocation = new JsInvocation(rootFunction, invocationArgs); - - String thisModuleName = JsDescriptorUtils.getModuleNameFromDescriptorName(moduleDescriptor); - JsName thisModuleId = program.getScope().declareName(Namer.suggestedModuleName(thisModuleName)); - block.getStatements().add(JsAstUtils.newVar(thisModuleId, - new JsBinaryOperation(JsBinaryOperator.OR, thisModuleId.makeRef(), invocation))); if (mainCallParameters.shouldBeGenerated()) { JsStatement statement = generateCallToMain(context, files, mainCallParameters.arguments()); @@ -240,9 +241,144 @@ public final class Translation { } } + JsBlock block = program.getGlobalBlock(); + block.getStatements().addAll(wrapIfNecessary(config.getModuleId(), rootFunction, importedModuleList, program, + config.getModuleKind())); + return context; } + @NotNull + private static List wrapIfNecessary( + @NotNull String moduleId, + @NotNull JsExpression function, + @NotNull List importedModules, + @NotNull JsProgram program, + @NotNull ModuleKind kind + ) { + switch (kind) { + case AMD: + return wrapAmd(moduleId, function, importedModules, program); + case COMMON_JS: + return wrapCommonJs(function, importedModules, program); + case UMD: + return wrapUmd(moduleId, function, importedModules, program); + case PLAIN: + default: + return wrapPlain(moduleId, function, importedModules, program); + } + } + + @NotNull + private static List wrapUmd( + @Nullable String moduleId, + @NotNull JsExpression function, + @NotNull List importedModules, + @NotNull JsProgram program + ) { + JsScope scope = program.getScope(); + JsName rootName = scope.declareName("root"); + JsName factoryName = scope.declareName("factory"); + JsName defineName = scope.declareName("define"); + JsName exportsName = scope.declareName("exports"); + + JsExpression amdTest = JsAstUtils.and(JsAstUtils.typeOfIs(defineName.makeRef(), program.getStringLiteral("function")), + new JsNameRef("amd", defineName.makeRef())); + JsExpression commonJsTest = JsAstUtils.typeOfIs(exportsName.makeRef(), program.getStringLiteral("object")); + + JsBlock amdBody = new JsBlock(wrapAmd(moduleId, factoryName.makeRef(), importedModules, program)); + JsBlock commonJsBody = new JsBlock(wrapCommonJs(factoryName.makeRef(), importedModules, program)); + JsInvocation plainInvocation = makePlainInvocation(factoryName.makeRef(), importedModules); + JsExpression plainExpr = moduleId != null ? + JsAstUtils.assignment(new JsNameRef(moduleId, rootName.makeRef()), plainInvocation) : + plainInvocation; + + JsStatement selector = JsAstUtils.newJsIf(amdTest, amdBody, JsAstUtils.newJsIf(commonJsTest, commonJsBody, plainExpr.makeStmt())); + JsFunction adapter = new JsFunction(program.getScope(), new JsBlock(selector), "UMD adapter"); + adapter.getParameters().add(new JsParameter(rootName)); + adapter.getParameters().add(new JsParameter(factoryName)); + + return Collections.singletonList(new JsInvocation(adapter, JsLiteral.THIS, function).makeStmt()); + } + + @NotNull + private static List wrapAmd( + @Nullable String moduleId, + @NotNull JsExpression function, + @NotNull List importedModules, + @NotNull JsProgram program + ) { + JsScope scope = program.getScope(); + JsName defineName = scope.declareName("define"); + List invocationArgs = new ArrayList(); + + if (moduleId != null) { + invocationArgs.add(program.getStringLiteral(moduleId)); + } + + List moduleNameList = new ArrayList(importedModules.size()); + for (ImportedModule importedModule : importedModules) { + moduleNameList.add(program.getStringLiteral(importedModule.id)); + } + invocationArgs.add(new JsArrayLiteral(moduleNameList)); + + invocationArgs.add(function); + + JsInvocation invocation = new JsInvocation(defineName.makeRef(), invocationArgs); + return Collections.singletonList(invocation.makeStmt()); + } + + @NotNull + private static List wrapCommonJs( + @NotNull JsExpression function, + @NotNull List importedModules, + @NotNull JsProgram program + ) { + JsScope scope = program.getScope(); + JsName moduleName = scope.declareName("module"); + JsName requireName = scope.declareName("require"); + + List invocationArgs = new ArrayList(); + for (ImportedModule importedModule : importedModules) { + invocationArgs.add(new JsInvocation(requireName.makeRef(), program.getStringLiteral(importedModule.id))); + } + + JsInvocation invocation = new JsInvocation(function, invocationArgs); + JsExpression assignment = JsAstUtils.assignment(new JsNameRef("exports", moduleName.makeRef()), invocation); + return Collections.singletonList(assignment.makeStmt()); + } + + @NotNull + private static List wrapPlain( + @Nullable String moduleId, + @NotNull JsExpression function, + @NotNull List importedModules, + @NotNull JsProgram program + ) { + JsInvocation invocation = makePlainInvocation(function, importedModules); + + JsStatement statement; + if (moduleId == null) { + statement = invocation.makeStmt(); + } + else { + statement = JsAstUtils.newVar(program.getScope().declareName(moduleId), function); + } + + return Collections.singletonList(statement); + } + + @NotNull + private static JsInvocation makePlainInvocation(@NotNull JsExpression function, @NotNull List importedModules) { + List invocationArgs = new ArrayList(importedModules.size()); + + for (ImportedModule importedModule : importedModules) { + invocationArgs.add(importedModule.name.makeRef()); + } + + return new JsInvocation(function, invocationArgs); + } + private static void defineModule(@NotNull TranslationContext context, @NotNull List statements, @NotNull String moduleId) { JsName rootPackageName = context.scope().findName(Namer.getRootPackageName()); if (rootPackageName != null) { diff --git a/js/js.translator/testData/closure-wrapper.txt b/js/js.translator/testData/closure-wrapper.txt new file mode 100644 index 00000000000..fe5a03e07b4 --- /dev/null +++ b/js/js.translator/testData/closure-wrapper.txt @@ -0,0 +1,13 @@ +(function (root, factory) { + if (typeof define === 'function' && define.amd) { + define('Kotlin', [], factory); + } else if (typeof exports === 'object') { + module.exports = factory(); + } else { + root.Kotlin = factory(); + } +}(this, function () { + var Kotlin = {}; +%output% + return Kotlin; +})); \ No newline at end of file diff --git a/js/js.translator/testData/kotlin_lib.js b/js/js.translator/testData/kotlin_lib.js index 6a59e57ec87..1b2dc121f86 100644 --- a/js/js.translator/testData/kotlin_lib.js +++ b/js/js.translator/testData/kotlin_lib.js @@ -14,7 +14,6 @@ * limitations under the License. */ -var Kotlin = Kotlin || {}; (function (Kotlin) { "use strict"; diff --git a/js/js.translator/testData/kotlin_lib_ecma5.js b/js/js.translator/testData/kotlin_lib_ecma5.js index 4bc4f1688bf..6ef1edda8c2 100644 --- a/js/js.translator/testData/kotlin_lib_ecma5.js +++ b/js/js.translator/testData/kotlin_lib_ecma5.js @@ -14,8 +14,6 @@ * limitations under the License. */ -var Kotlin = Kotlin || {}; - (function (Kotlin) { 'use strict'; diff --git a/js/js.translator/testData/long.js b/js/js.translator/testData/long.js index 625d9ffe6fa..391e5e797a9 100644 --- a/js/js.translator/testData/long.js +++ b/js/js.translator/testData/long.js @@ -26,8 +26,6 @@ // distributed under the License is distributed on an "AS-IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -var Kotlin = Kotlin || {}; - (function (Kotlin) { "use strict"; diff --git a/update_dependencies.xml b/update_dependencies.xml index fb71a68bb84..bb033964f88 100644 --- a/update_dependencies.xml +++ b/update_dependencies.xml @@ -252,7 +252,7 @@ -