KT-12877: support plain reference to declaration in UMD wrapper, support plain reference to package
This commit is contained in:
committed by
Alexey Andreev
parent
495c876b3b
commit
d63a727474
@@ -5030,6 +5030,18 @@ public class BoxJsTestGenerated extends AbstractBoxJsTest {
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("externalPackagePlain.kt")
|
||||
public void testExternalPackagePlain() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("js/js.translator/testData/box/jsModule/externalPackagePlain.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("externalPackageUmdFallback.kt")
|
||||
public void testExternalPackageUmdFallback() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("js/js.translator/testData/box/jsModule/externalPackageUmdFallback.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("externalProperty.kt")
|
||||
public void testExternalProperty() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("js/js.translator/testData/box/jsModule/externalProperty.kt");
|
||||
|
||||
@@ -26,6 +26,7 @@ import org.jetbrains.kotlin.js.config.JsConfig;
|
||||
import org.jetbrains.kotlin.js.coroutine.CoroutineTransformer;
|
||||
import org.jetbrains.kotlin.js.facade.exceptions.TranslationException;
|
||||
import org.jetbrains.kotlin.js.inline.JsInliner;
|
||||
import org.jetbrains.kotlin.js.translate.context.StaticContext;
|
||||
import org.jetbrains.kotlin.js.inline.clean.RemoveUnusedImportsKt;
|
||||
import org.jetbrains.kotlin.js.translate.context.TranslationContext;
|
||||
import org.jetbrains.kotlin.js.translate.general.Translation;
|
||||
@@ -96,7 +97,10 @@ public final class K2JSTranslator {
|
||||
expandIsCalls(program, context);
|
||||
ProgressIndicatorAndCompilationCanceledStatus.checkCanceled();
|
||||
|
||||
List<String> importedModules = new ArrayList<String>(context.getImportedModules().keySet());
|
||||
List<String> importedModules = new ArrayList<String>();
|
||||
for (StaticContext.ImportedModule module : context.getImportedModules()) {
|
||||
importedModules.add(module.getExternalName());
|
||||
}
|
||||
return new TranslationResult.Success(config, files, program, diagnostics, importedModules, moduleDescriptor,
|
||||
bindingTrace.getBindingContext());
|
||||
}
|
||||
|
||||
+100
-20
@@ -121,9 +121,9 @@ public final class StaticContext {
|
||||
private final Map<DeclarationDescriptor, JsExpression> fqnCache = new HashMap<DeclarationDescriptor, JsExpression>();
|
||||
|
||||
@NotNull
|
||||
private final Map<String, JsName> importedModules = new LinkedHashMap<String, JsName>();
|
||||
private final Map<ImportedModuleKey, ImportedModule> importedModules = new LinkedHashMap<ImportedModuleKey, ImportedModule>();
|
||||
|
||||
private Map<String, JsName> readOnlyImportedModules;
|
||||
private Collection<ImportedModule> readOnlyImportedModules;
|
||||
|
||||
@NotNull
|
||||
private final JsScope rootPackageScope;
|
||||
@@ -166,6 +166,10 @@ public final class StaticContext {
|
||||
this.currentModule = moduleDescriptor;
|
||||
this.rootFunction = rootFunction;
|
||||
rootPackageScope = new JsObjectScope(rootScope, "<root package>", "root-package");
|
||||
|
||||
JsName kotlinName = rootScope.declareName(Namer.KOTLIN_NAME);
|
||||
importedModules.put(new ImportedModuleKey(Namer.KOTLIN_LOWER_NAME, null),
|
||||
new ImportedModule(Namer.KOTLIN_LOWER_NAME, kotlinName, null));
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@@ -194,9 +198,9 @@ public final class StaticContext {
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public Map<String, JsName> getImportedModules() {
|
||||
public Collection<ImportedModule> getImportedModules() {
|
||||
if (readOnlyImportedModules == null) {
|
||||
readOnlyImportedModules = Collections.unmodifiableMap(importedModules);
|
||||
readOnlyImportedModules = Collections.unmodifiableCollection(importedModules.values());
|
||||
}
|
||||
return readOnlyImportedModules;
|
||||
}
|
||||
@@ -253,14 +257,7 @@ public final class StaticContext {
|
||||
if (config.getModuleKind() != ModuleKind.PLAIN) {
|
||||
String moduleName = AnnotationsUtils.getModuleName(suggested.getDescriptor());
|
||||
if (moduleName != null) {
|
||||
return JsAstUtils.pureFqn(getModuleInternalName(moduleName), null);
|
||||
}
|
||||
|
||||
if (isNativeObject(suggested.getDescriptor())) {
|
||||
String fileModuleName = AnnotationsUtils.getFileModuleName(getBindingContext(), suggested.getDescriptor());
|
||||
if (fileModuleName != null) {
|
||||
return pureFqn(getNameForDescriptor(suggested.getDescriptor()), pureFqn(getModuleInternalName(fileModuleName), null));
|
||||
}
|
||||
return JsAstUtils.pureFqn(getImportedModule(moduleName, suggested.getDescriptor()).internalName, null);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -279,6 +276,14 @@ public final class StaticContext {
|
||||
expression = getQualifiedExpression(suggested.getScope());
|
||||
}
|
||||
|
||||
if (isNativeObject(suggested.getDescriptor())) {
|
||||
String fileModuleName = AnnotationsUtils.getFileModuleName(getBindingContext(), suggested.getDescriptor());
|
||||
if (fileModuleName != null) {
|
||||
JsName moduleJsName = getImportedModule(fileModuleName, null).internalName;
|
||||
expression = pureFqn(moduleJsName, null);
|
||||
}
|
||||
}
|
||||
|
||||
for (JsName partName : partNames) {
|
||||
expression = new JsNameRef(partName, expression);
|
||||
applySideEffects(expression, suggested.getDescriptor());
|
||||
@@ -611,18 +616,28 @@ public final class StaticContext {
|
||||
|
||||
if (UNKNOWN_EXTERNAL_MODULE_NAME.equals(moduleName)) return null;
|
||||
|
||||
return getModuleInternalName(moduleName);
|
||||
return getImportedModule(moduleName, null).getInternalName();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private JsName getModuleInternalName(@NotNull String baseName) {
|
||||
JsName moduleId = baseName.equals(Namer.KOTLIN_LOWER_NAME) ? rootScope.declareName(Namer.KOTLIN_NAME) :
|
||||
importedModules.get(baseName);
|
||||
if (moduleId == null) {
|
||||
moduleId = rootScope.declareFreshName(Namer.LOCAL_MODULE_PREFIX + Namer.suggestedModuleName(baseName));
|
||||
importedModules.put(baseName, moduleId);
|
||||
private ImportedModule getImportedModule(@NotNull String baseName, @Nullable DeclarationDescriptor descriptor) {
|
||||
ImportedModuleKey key = new ImportedModuleKey(baseName, descriptor);
|
||||
|
||||
ImportedModule module = importedModules.get(key);
|
||||
if (module == null) {
|
||||
JsName internalName = rootScope.declareFreshName(Namer.LOCAL_MODULE_PREFIX + Namer.suggestedModuleName(baseName));
|
||||
JsName plainName = descriptor != null ? rootScope.declareName(getPlainId(descriptor)) : null;
|
||||
module = new ImportedModule(baseName, internalName, plainName != null ? pureFqn(plainName, null) : null);
|
||||
importedModules.put(key, module);
|
||||
}
|
||||
return moduleId;
|
||||
return module;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private String getPlainId(@NotNull DeclarationDescriptor declaration) {
|
||||
SuggestedName suggestedName = nameSuggestion.suggest(declaration);
|
||||
assert suggestedName != null : "Declaration should not be ModuleDescriptor, therefore suggestedName should be non-null";
|
||||
return suggestedName.getNames().get(0);
|
||||
}
|
||||
|
||||
private static JsExpression applySideEffects(JsExpression expression, DeclarationDescriptor descriptor) {
|
||||
@@ -742,4 +757,69 @@ public final class StaticContext {
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static class ImportedModule {
|
||||
@NotNull
|
||||
private final String externalName;
|
||||
|
||||
@NotNull
|
||||
private final JsName internalName;
|
||||
|
||||
@Nullable
|
||||
private final JsExpression plainReference;
|
||||
|
||||
ImportedModule(@NotNull String externalName, @NotNull JsName internalName, @Nullable JsExpression plainReference) {
|
||||
this.externalName = externalName;
|
||||
this.internalName = internalName;
|
||||
this.plainReference = plainReference;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public String getExternalName() {
|
||||
return externalName;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public JsName getInternalName() {
|
||||
return internalName;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public JsExpression getPlainReference() {
|
||||
return plainReference;
|
||||
}
|
||||
}
|
||||
|
||||
private static class ImportedModuleKey {
|
||||
@NotNull
|
||||
private final String baseName;
|
||||
|
||||
@Nullable
|
||||
private final DeclarationDescriptor declaration;
|
||||
|
||||
public ImportedModuleKey(@NotNull String baseName, @Nullable DeclarationDescriptor declaration) {
|
||||
this.baseName = baseName;
|
||||
this.declaration = declaration;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
|
||||
ImportedModuleKey key = (ImportedModuleKey) o;
|
||||
|
||||
if (!baseName.equals(key.baseName)) return false;
|
||||
if (declaration != null ? !declaration.equals(key.declaration) : key.declaration != null) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = baseName.hashCode();
|
||||
result = 31 * result + (declaration != null ? declaration.hashCode() : 0);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -94,7 +94,7 @@ public class TranslationContext {
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public Map<String, JsName> getImportedModules() {
|
||||
public Collection<StaticContext.ImportedModule> getImportedModules() {
|
||||
return staticContext.getImportedModules();
|
||||
}
|
||||
|
||||
|
||||
+21
-12
@@ -18,12 +18,13 @@ package org.jetbrains.kotlin.js.translate.general
|
||||
|
||||
import com.google.dart.compiler.backend.js.ast.*
|
||||
import org.jetbrains.kotlin.js.translate.context.Namer
|
||||
import org.jetbrains.kotlin.js.translate.context.StaticContext
|
||||
import org.jetbrains.kotlin.js.translate.utils.JsAstUtils
|
||||
import org.jetbrains.kotlin.serialization.js.ModuleKind
|
||||
|
||||
object ModuleWrapperTranslation {
|
||||
@JvmStatic fun wrapIfNecessary(
|
||||
moduleId: String, function: JsExpression, importedModules: List<String>,
|
||||
moduleId: String, function: JsExpression, importedModules: List<StaticContext.ImportedModule>,
|
||||
program: JsProgram, kind: ModuleKind
|
||||
): List<JsStatement> {
|
||||
return when (kind) {
|
||||
@@ -36,7 +37,7 @@ object ModuleWrapperTranslation {
|
||||
|
||||
private fun wrapUmd(
|
||||
moduleId: String, function: JsExpression,
|
||||
importedModules: List<String>, program: JsProgram
|
||||
importedModules: List<StaticContext.ImportedModule>, program: JsProgram
|
||||
): List<JsStatement> {
|
||||
val scope = program.scope
|
||||
val defineName = scope.declareName("define")
|
||||
@@ -78,13 +79,13 @@ object ModuleWrapperTranslation {
|
||||
|
||||
private fun wrapAmd(
|
||||
moduleId: String,function: JsExpression,
|
||||
importedModules: List<String>, program: JsProgram
|
||||
importedModules: List<StaticContext.ImportedModule>, program: JsProgram
|
||||
): List<JsStatement> {
|
||||
val scope = program.scope
|
||||
val defineName = scope.declareName("define")
|
||||
val invocationArgs = listOf(
|
||||
program.getStringLiteral(moduleId),
|
||||
JsArrayLiteral(listOf(program.getStringLiteral("exports")) + importedModules.map { program.getStringLiteral(it) }),
|
||||
JsArrayLiteral(listOf(program.getStringLiteral("exports")) + importedModules.map { program.getStringLiteral(it.externalName) }),
|
||||
function
|
||||
)
|
||||
|
||||
@@ -92,19 +93,23 @@ object ModuleWrapperTranslation {
|
||||
return listOf(invocation.makeStmt())
|
||||
}
|
||||
|
||||
private fun wrapCommonJs(function: JsExpression, importedModules: List<String>, program: JsProgram): List<JsStatement> {
|
||||
private fun wrapCommonJs(
|
||||
function: JsExpression,
|
||||
importedModules: List<StaticContext.ImportedModule>,
|
||||
program: JsProgram
|
||||
): List<JsStatement> {
|
||||
val scope = program.scope
|
||||
val moduleName = scope.declareName("module")
|
||||
val requireName = scope.declareName("require")
|
||||
|
||||
val invocationArgs = importedModules.map { JsInvocation(requireName.makeRef(), program.getStringLiteral(it)) }
|
||||
val invocationArgs = importedModules.map { JsInvocation(requireName.makeRef(), program.getStringLiteral(it.externalName)) }
|
||||
val invocation = JsInvocation(function, listOf(JsNameRef("exports", moduleName.makeRef())) + invocationArgs)
|
||||
return listOf(invocation.makeStmt())
|
||||
}
|
||||
|
||||
private fun wrapPlain(
|
||||
moduleId: String, function: JsExpression,
|
||||
importedModules: List<String>, program: JsProgram
|
||||
importedModules: List<StaticContext.ImportedModule>, program: JsProgram
|
||||
): List<JsStatement> {
|
||||
val invocation = makePlainInvocation(moduleId, function, importedModules, program)
|
||||
val statements = mutableListOf<JsStatement>()
|
||||
@@ -126,13 +131,13 @@ object ModuleWrapperTranslation {
|
||||
private fun addModuleValidation(
|
||||
currentModuleId: String,
|
||||
program: JsProgram,
|
||||
moduleName: String
|
||||
module: StaticContext.ImportedModule
|
||||
): JsStatement {
|
||||
val moduleRef = makePlainModuleRef(moduleName, program)
|
||||
val moduleRef = makePlainModuleRef(module, program)
|
||||
val moduleExistsCond = JsAstUtils.typeOfIs(moduleRef, program.getStringLiteral("undefined"))
|
||||
val moduleNotFoundMessage = program.getStringLiteral(
|
||||
"Error loading module '" + currentModuleId + "'. Its dependency '" + moduleName + "' was not found. " +
|
||||
"Please, check whether '" + moduleName + "' is loaded prior to '" + currentModuleId + "'.")
|
||||
"Error loading module '" + currentModuleId + "'. Its dependency '" + module.externalName + "' was not found. " +
|
||||
"Please, check whether '" + module.externalName + "' is loaded prior to '" + currentModuleId + "'.")
|
||||
val moduleNotFoundThrow = JsThrow(JsNew(JsNameRef("Error"), listOf<JsExpression>(moduleNotFoundMessage)))
|
||||
return JsIf(moduleExistsCond, JsBlock(moduleNotFoundThrow))
|
||||
}
|
||||
@@ -140,7 +145,7 @@ object ModuleWrapperTranslation {
|
||||
private fun makePlainInvocation(
|
||||
moduleId: String,
|
||||
function: JsExpression,
|
||||
importedModules: List<String>,
|
||||
importedModules: List<StaticContext.ImportedModule>,
|
||||
program: JsProgram
|
||||
): JsInvocation {
|
||||
val invocationArgs = importedModules.map { makePlainModuleRef(it, program) }
|
||||
@@ -151,6 +156,10 @@ object ModuleWrapperTranslation {
|
||||
return JsInvocation(function, listOf(selfArg) + invocationArgs)
|
||||
}
|
||||
|
||||
private fun makePlainModuleRef(module: StaticContext.ImportedModule, program: JsProgram): JsExpression {
|
||||
return module.plainReference ?: makePlainModuleRef(module.externalName, program)
|
||||
}
|
||||
|
||||
private fun makePlainModuleRef(moduleId: String, program: JsProgram): JsExpression {
|
||||
// TODO: we could use `this.moduleName` syntax. However, this does not work for `kotlin` module in Rhino, since
|
||||
// we run kotlin.js in a parent scope. Consider better solution
|
||||
|
||||
@@ -274,13 +274,10 @@ public final class Translation {
|
||||
|
||||
// Invoke function passing modules as arguments
|
||||
// This should help minifier tool to recognize references to these modules as local variables and make them shorter.
|
||||
List<String> importedModuleList = new ArrayList<String>();
|
||||
JsName kotlinName = program.getScope().declareName(Namer.KOTLIN_NAME);
|
||||
rootFunction.getParameters().add(new JsParameter((kotlinName)));
|
||||
importedModuleList.add(Namer.KOTLIN_LOWER_NAME);
|
||||
List<StaticContext.ImportedModule> importedModuleList = new ArrayList<StaticContext.ImportedModule>();
|
||||
|
||||
for (String importedModule : staticContext.getImportedModules().keySet()) {
|
||||
rootFunction.getParameters().add(new JsParameter(staticContext.getImportedModules().get(importedModule)));
|
||||
for (StaticContext.ImportedModule importedModule : staticContext.getImportedModules()) {
|
||||
rootFunction.getParameters().add(new JsParameter(importedModule.getInternalName()));
|
||||
importedModuleList.add(importedModule);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
function lib(y) {
|
||||
function foo(y) {
|
||||
return 23 + y;
|
||||
}
|
||||
function baz(y) {
|
||||
return 42 + y;
|
||||
}
|
||||
@@ -2,11 +2,17 @@
|
||||
// NO_JS_MODULE_SYSTEM
|
||||
package foo
|
||||
|
||||
@JsModule("lib")
|
||||
@JsModule("libfoo")
|
||||
@JsNonModule
|
||||
@native fun foo(x: Int): Int = noImpl
|
||||
|
||||
@JsModule("libbar")
|
||||
@JsNonModule
|
||||
@JsName("baz")
|
||||
@native fun bar(x: Int): Int = noImpl
|
||||
|
||||
fun box(): String {
|
||||
assertEquals(65, foo(42))
|
||||
assertEquals(142, bar(100))
|
||||
return "OK"
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
var lib = function() {
|
||||
function A(x) {
|
||||
this.x = x;
|
||||
}
|
||||
A.prototype.foo = function (y) {
|
||||
return this.x + y;
|
||||
};
|
||||
|
||||
B = {
|
||||
x: 123,
|
||||
foo: function(y) {
|
||||
return this.x + y;
|
||||
}
|
||||
};
|
||||
|
||||
function foo(y) {
|
||||
return 323 + y;
|
||||
}
|
||||
|
||||
var bar = 423;
|
||||
|
||||
return {
|
||||
A: A,
|
||||
B: B,
|
||||
foo: foo,
|
||||
bar: bar,
|
||||
mbar: -1
|
||||
};
|
||||
}();
|
||||
@@ -0,0 +1,36 @@
|
||||
@file:JsModule("lib")
|
||||
@file:JsNonModule
|
||||
package foo
|
||||
|
||||
@native class A(@native val x: Int = noImpl) {
|
||||
@native fun foo(y: Int): Int = noImpl
|
||||
}
|
||||
|
||||
@native object B {
|
||||
@native val x: Int = noImpl
|
||||
|
||||
@native fun foo(y: Int): Int = noImpl
|
||||
}
|
||||
|
||||
@native fun foo(y: Int): Int = noImpl
|
||||
|
||||
@native val bar: Int = noImpl
|
||||
|
||||
@native var mbar: Int = noImpl
|
||||
|
||||
fun box(): String {
|
||||
val a = A(23)
|
||||
assertEquals(23, a.x)
|
||||
assertEquals(65, a.foo(42))
|
||||
|
||||
assertEquals(123, B.x)
|
||||
assertEquals(265, B.foo(142))
|
||||
|
||||
assertEquals(365, foo(42))
|
||||
assertEquals(423, bar)
|
||||
|
||||
mbar = 523
|
||||
assertEquals(523, mbar)
|
||||
|
||||
return "OK"
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
var lib = function() {
|
||||
function A(x) {
|
||||
this.x = x;
|
||||
}
|
||||
A.prototype.foo = function (y) {
|
||||
return this.x + y;
|
||||
};
|
||||
|
||||
B = {
|
||||
x: 123,
|
||||
foo: function(y) {
|
||||
return this.x + y;
|
||||
}
|
||||
};
|
||||
|
||||
function foo(y) {
|
||||
return 323 + y;
|
||||
}
|
||||
|
||||
var bar = 423;
|
||||
|
||||
return {
|
||||
A: A,
|
||||
B: B,
|
||||
foo: foo,
|
||||
bar: bar,
|
||||
mbar: -1
|
||||
};
|
||||
}();
|
||||
@@ -0,0 +1,38 @@
|
||||
// MODULE_KIND: UMD
|
||||
// NO_JS_MODULE_SYSTEM
|
||||
@file:JsModule("lib")
|
||||
@file:JsNonModule
|
||||
package foo
|
||||
|
||||
@native class A(@native val x: Int = noImpl) {
|
||||
@native fun foo(y: Int): Int = noImpl
|
||||
}
|
||||
|
||||
@native object B {
|
||||
@native val x: Int = noImpl
|
||||
|
||||
@native fun foo(y: Int): Int = noImpl
|
||||
}
|
||||
|
||||
@native fun foo(y: Int): Int = noImpl
|
||||
|
||||
@native val bar: Int = noImpl
|
||||
|
||||
@native var mbar: Int = noImpl
|
||||
|
||||
fun box(): String {
|
||||
val a = A(23)
|
||||
assertEquals(23, a.x)
|
||||
assertEquals(65, a.foo(42))
|
||||
|
||||
assertEquals(123, B.x)
|
||||
assertEquals(265, B.foo(142))
|
||||
|
||||
assertEquals(365, foo(42))
|
||||
assertEquals(423, bar)
|
||||
|
||||
mbar = 523
|
||||
assertEquals(523, mbar)
|
||||
|
||||
return "OK"
|
||||
}
|
||||
Reference in New Issue
Block a user