diff --git a/build-common/test/org/jetbrains/kotlin/serialization/js/ast/DebugJsAstProtoBuf.java b/build-common/test/org/jetbrains/kotlin/serialization/js/ast/DebugJsAstProtoBuf.java
index a57332ccd12..8c3904f52af 100644
--- a/build-common/test/org/jetbrains/kotlin/serialization/js/ast/DebugJsAstProtoBuf.java
+++ b/build-common/test/org/jetbrains/kotlin/serialization/js/ast/DebugJsAstProtoBuf.java
@@ -190,6 +190,88 @@ public final class DebugJsAstProtoBuf {
// @@protoc_insertion_point(enum_scope:org.jetbrains.kotlin.serialization.js.ast.InlineStrategy)
}
+ /**
+ * Protobuf enum {@code org.jetbrains.kotlin.serialization.js.ast.SpecialFunction}
+ */
+ public enum SpecialFunction
+ implements org.jetbrains.kotlin.protobuf.ProtocolMessageEnum {
+ /**
+ * DEFINE_INLINE_FUNCTION = 1;
+ */
+ DEFINE_INLINE_FUNCTION(0, 1),
+ /**
+ * WRAP_FUNCTION = 2;
+ */
+ WRAP_FUNCTION(1, 2),
+ ;
+
+ /**
+ * DEFINE_INLINE_FUNCTION = 1;
+ */
+ public static final int DEFINE_INLINE_FUNCTION_VALUE = 1;
+ /**
+ * WRAP_FUNCTION = 2;
+ */
+ public static final int WRAP_FUNCTION_VALUE = 2;
+
+
+ public final int getNumber() { return value; }
+
+ public static SpecialFunction valueOf(int value) {
+ switch (value) {
+ case 1: return DEFINE_INLINE_FUNCTION;
+ case 2: return WRAP_FUNCTION;
+ default: return null;
+ }
+ }
+
+ public static org.jetbrains.kotlin.protobuf.Internal.EnumLiteMap
+ internalGetValueMap() {
+ return internalValueMap;
+ }
+ private static org.jetbrains.kotlin.protobuf.Internal.EnumLiteMap
+ internalValueMap =
+ new org.jetbrains.kotlin.protobuf.Internal.EnumLiteMap() {
+ public SpecialFunction findValueByNumber(int number) {
+ return SpecialFunction.valueOf(number);
+ }
+ };
+
+ public final org.jetbrains.kotlin.protobuf.Descriptors.EnumValueDescriptor
+ getValueDescriptor() {
+ return getDescriptor().getValues().get(index);
+ }
+ public final org.jetbrains.kotlin.protobuf.Descriptors.EnumDescriptor
+ getDescriptorForType() {
+ return getDescriptor();
+ }
+ public static final org.jetbrains.kotlin.protobuf.Descriptors.EnumDescriptor
+ getDescriptor() {
+ return org.jetbrains.kotlin.serialization.js.ast.DebugJsAstProtoBuf.getDescriptor().getEnumTypes().get(2);
+ }
+
+ private static final SpecialFunction[] VALUES = values();
+
+ public static SpecialFunction valueOf(
+ org.jetbrains.kotlin.protobuf.Descriptors.EnumValueDescriptor desc) {
+ if (desc.getType() != getDescriptor()) {
+ throw new java.lang.IllegalArgumentException(
+ "EnumValueDescriptor is not for this type.");
+ }
+ return VALUES[desc.getIndex()];
+ }
+
+ private final int index;
+ private final int value;
+
+ private SpecialFunction(int index, int value) {
+ this.index = index;
+ this.value = value;
+ }
+
+ // @@protoc_insertion_point(enum_scope:org.jetbrains.kotlin.serialization.js.ast.SpecialFunction)
+ }
+
public interface LocationOrBuilder extends
// @@protoc_insertion_point(interface_extends:org.jetbrains.kotlin.serialization.js.ast.Location)
org.jetbrains.kotlin.protobuf.MessageOrBuilder {
@@ -47073,13 +47155,22 @@ public final class DebugJsAstProtoBuf {
int getLocalNameId();
/**
- * optional bool imported = 4;
+ * optional bool imported = 4 [default = false];
*/
boolean hasImported();
/**
- * optional bool imported = 4;
+ * optional bool imported = 4 [default = false];
*/
boolean getImported();
+
+ /**
+ * optional .org.jetbrains.kotlin.serialization.js.ast.SpecialFunction special_function = 5;
+ */
+ boolean hasSpecialFunction();
+ /**
+ * optional .org.jetbrains.kotlin.serialization.js.ast.SpecialFunction special_function = 5;
+ */
+ org.jetbrains.kotlin.serialization.js.ast.DebugJsAstProtoBuf.SpecialFunction getSpecialFunction();
}
/**
* Protobuf type {@code org.jetbrains.kotlin.serialization.js.ast.Name}
@@ -47153,6 +47244,17 @@ public final class DebugJsAstProtoBuf {
imported_ = input.readBool();
break;
}
+ case 40: {
+ int rawValue = input.readEnum();
+ org.jetbrains.kotlin.serialization.js.ast.DebugJsAstProtoBuf.SpecialFunction value = org.jetbrains.kotlin.serialization.js.ast.DebugJsAstProtoBuf.SpecialFunction.valueOf(rawValue);
+ if (value == null) {
+ unknownFields.mergeVarintField(5, rawValue);
+ } else {
+ bitField0_ |= 0x00000010;
+ specialFunction_ = value;
+ }
+ break;
+ }
}
}
} catch (org.jetbrains.kotlin.protobuf.InvalidProtocolBufferException e) {
@@ -47241,23 +47343,39 @@ public final class DebugJsAstProtoBuf {
public static final int IMPORTED_FIELD_NUMBER = 4;
private boolean imported_;
/**
- * optional bool imported = 4;
+ * optional bool imported = 4 [default = false];
*/
public boolean hasImported() {
return ((bitField0_ & 0x00000008) == 0x00000008);
}
/**
- * optional bool imported = 4;
+ * optional bool imported = 4 [default = false];
*/
public boolean getImported() {
return imported_;
}
+ public static final int SPECIAL_FUNCTION_FIELD_NUMBER = 5;
+ private org.jetbrains.kotlin.serialization.js.ast.DebugJsAstProtoBuf.SpecialFunction specialFunction_;
+ /**
+ * optional .org.jetbrains.kotlin.serialization.js.ast.SpecialFunction special_function = 5;
+ */
+ public boolean hasSpecialFunction() {
+ return ((bitField0_ & 0x00000010) == 0x00000010);
+ }
+ /**
+ * optional .org.jetbrains.kotlin.serialization.js.ast.SpecialFunction special_function = 5;
+ */
+ public org.jetbrains.kotlin.serialization.js.ast.DebugJsAstProtoBuf.SpecialFunction getSpecialFunction() {
+ return specialFunction_;
+ }
+
private void initFields() {
temporary_ = false;
identifier_ = 0;
localNameId_ = 0;
imported_ = false;
+ specialFunction_ = org.jetbrains.kotlin.serialization.js.ast.DebugJsAstProtoBuf.SpecialFunction.DEFINE_INLINE_FUNCTION;
}
private byte memoizedIsInitialized = -1;
public final boolean isInitialized() {
@@ -47288,6 +47406,9 @@ public final class DebugJsAstProtoBuf {
if (((bitField0_ & 0x00000008) == 0x00000008)) {
output.writeBool(4, imported_);
}
+ if (((bitField0_ & 0x00000010) == 0x00000010)) {
+ output.writeEnum(5, specialFunction_.getNumber());
+ }
getUnknownFields().writeTo(output);
}
@@ -47313,6 +47434,10 @@ public final class DebugJsAstProtoBuf {
size += org.jetbrains.kotlin.protobuf.CodedOutputStream
.computeBoolSize(4, imported_);
}
+ if (((bitField0_ & 0x00000010) == 0x00000010)) {
+ size += org.jetbrains.kotlin.protobuf.CodedOutputStream
+ .computeEnumSize(5, specialFunction_.getNumber());
+ }
size += getUnknownFields().getSerializedSize();
memoizedSerializedSize = size;
return size;
@@ -47438,6 +47563,8 @@ public final class DebugJsAstProtoBuf {
bitField0_ = (bitField0_ & ~0x00000004);
imported_ = false;
bitField0_ = (bitField0_ & ~0x00000008);
+ specialFunction_ = org.jetbrains.kotlin.serialization.js.ast.DebugJsAstProtoBuf.SpecialFunction.DEFINE_INLINE_FUNCTION;
+ bitField0_ = (bitField0_ & ~0x00000010);
return this;
}
@@ -47482,6 +47609,10 @@ public final class DebugJsAstProtoBuf {
to_bitField0_ |= 0x00000008;
}
result.imported_ = imported_;
+ if (((from_bitField0_ & 0x00000010) == 0x00000010)) {
+ to_bitField0_ |= 0x00000010;
+ }
+ result.specialFunction_ = specialFunction_;
result.bitField0_ = to_bitField0_;
onBuilt();
return result;
@@ -47510,6 +47641,9 @@ public final class DebugJsAstProtoBuf {
if (other.hasImported()) {
setImported(other.getImported());
}
+ if (other.hasSpecialFunction()) {
+ setSpecialFunction(other.getSpecialFunction());
+ }
this.mergeUnknownFields(other.getUnknownFields());
return this;
}
@@ -47639,19 +47773,19 @@ public final class DebugJsAstProtoBuf {
private boolean imported_ ;
/**
- * optional bool imported = 4;
+ * optional bool imported = 4 [default = false];
*/
public boolean hasImported() {
return ((bitField0_ & 0x00000008) == 0x00000008);
}
/**
- * optional bool imported = 4;
+ * optional bool imported = 4 [default = false];
*/
public boolean getImported() {
return imported_;
}
/**
- * optional bool imported = 4;
+ * optional bool imported = 4 [default = false];
*/
public Builder setImported(boolean value) {
bitField0_ |= 0x00000008;
@@ -47660,7 +47794,7 @@ public final class DebugJsAstProtoBuf {
return this;
}
/**
- * optional bool imported = 4;
+ * optional bool imported = 4 [default = false];
*/
public Builder clearImported() {
bitField0_ = (bitField0_ & ~0x00000008);
@@ -47669,6 +47803,41 @@ public final class DebugJsAstProtoBuf {
return this;
}
+ private org.jetbrains.kotlin.serialization.js.ast.DebugJsAstProtoBuf.SpecialFunction specialFunction_ = org.jetbrains.kotlin.serialization.js.ast.DebugJsAstProtoBuf.SpecialFunction.DEFINE_INLINE_FUNCTION;
+ /**
+ * optional .org.jetbrains.kotlin.serialization.js.ast.SpecialFunction special_function = 5;
+ */
+ public boolean hasSpecialFunction() {
+ return ((bitField0_ & 0x00000010) == 0x00000010);
+ }
+ /**
+ * optional .org.jetbrains.kotlin.serialization.js.ast.SpecialFunction special_function = 5;
+ */
+ public org.jetbrains.kotlin.serialization.js.ast.DebugJsAstProtoBuf.SpecialFunction getSpecialFunction() {
+ return specialFunction_;
+ }
+ /**
+ * optional .org.jetbrains.kotlin.serialization.js.ast.SpecialFunction special_function = 5;
+ */
+ public Builder setSpecialFunction(org.jetbrains.kotlin.serialization.js.ast.DebugJsAstProtoBuf.SpecialFunction value) {
+ if (value == null) {
+ throw new NullPointerException();
+ }
+ bitField0_ |= 0x00000010;
+ specialFunction_ = value;
+ onChanged();
+ return this;
+ }
+ /**
+ * optional .org.jetbrains.kotlin.serialization.js.ast.SpecialFunction special_function = 5;
+ */
+ public Builder clearSpecialFunction() {
+ bitField0_ = (bitField0_ & ~0x00000010);
+ specialFunction_ = org.jetbrains.kotlin.serialization.js.ast.DebugJsAstProtoBuf.SpecialFunction.DEFINE_INLINE_FUNCTION;
+ onChanged();
+ return this;
+ }
+
// @@protoc_insertion_point(builder_scope:org.jetbrains.kotlin.serialization.js.ast.Name)
}
@@ -49160,19 +49329,23 @@ public final class DebugJsAstProtoBuf {
"ule\022\024\n\014signature_id\030\001 \002(\005\022\025\n\rexpression_" +
"id\030\002 \002(\005\"\034\n\013StringTable\022\r\n\005entry\030\001 \003(\t\"K" +
"\n\tNameTable\022>\n\005entry\030\001 \003(\0132/.org.jetbrai",
- "ns.kotlin.serialization.js.ast.Name\"V\n\004N" +
- "ame\022\021\n\ttemporary\030\001 \002(\010\022\022\n\nidentifier\030\002 \001" +
- "(\005\022\025\n\rlocal_name_id\030\003 \001(\005\022\020\n\010imported\030\004 " +
- "\001(\010\"\346\001\n\005Chunk\022L\n\014string_table\030\001 \002(\01326.or" +
- "g.jetbrains.kotlin.serialization.js.ast." +
- "StringTable\022H\n\nname_table\030\002 \002(\01324.org.je" +
- "tbrains.kotlin.serialization.js.ast.Name" +
- "Table\022E\n\010fragment\030\003 \002(\01323.org.jetbrains." +
- "kotlin.serialization.js.ast.Fragment*@\n\013" +
- "SideEffects\022\021\n\rAFFECTS_STATE\020\001\022\024\n\020DEPEND",
- "S_ON_STATE\020\002\022\010\n\004PURE\020\003*?\n\016InlineStrategy" +
- "\022\017\n\013AS_FUNCTION\020\000\022\014\n\010IN_PLACE\020\001\022\016\n\nNOT_I" +
- "NLINE\020\002B\024B\022DebugJsAstProtoBuf"
+ "ns.kotlin.serialization.js.ast.Name\"\263\001\n\004" +
+ "Name\022\021\n\ttemporary\030\001 \002(\010\022\022\n\nidentifier\030\002 " +
+ "\001(\005\022\025\n\rlocal_name_id\030\003 \001(\005\022\027\n\010imported\030\004" +
+ " \001(\010:\005false\022T\n\020special_function\030\005 \001(\0162:." +
+ "org.jetbrains.kotlin.serialization.js.as" +
+ "t.SpecialFunction\"\346\001\n\005Chunk\022L\n\014string_ta" +
+ "ble\030\001 \002(\01326.org.jetbrains.kotlin.seriali" +
+ "zation.js.ast.StringTable\022H\n\nname_table\030" +
+ "\002 \002(\01324.org.jetbrains.kotlin.serializati" +
+ "on.js.ast.NameTable\022E\n\010fragment\030\003 \002(\01323.",
+ "org.jetbrains.kotlin.serialization.js.as" +
+ "t.Fragment*@\n\013SideEffects\022\021\n\rAFFECTS_STA" +
+ "TE\020\001\022\024\n\020DEPENDS_ON_STATE\020\002\022\010\n\004PURE\020\003*?\n\016" +
+ "InlineStrategy\022\017\n\013AS_FUNCTION\020\000\022\014\n\010IN_PL" +
+ "ACE\020\001\022\016\n\nNOT_INLINE\020\002*@\n\017SpecialFunction" +
+ "\022\032\n\026DEFINE_INLINE_FUNCTION\020\001\022\021\n\rWRAP_FUN" +
+ "CTION\020\002B\024B\022DebugJsAstProtoBuf"
};
org.jetbrains.kotlin.protobuf.Descriptors.FileDescriptor.InternalDescriptorAssigner assigner =
new org.jetbrains.kotlin.protobuf.Descriptors.FileDescriptor. InternalDescriptorAssigner() {
@@ -49509,7 +49682,7 @@ public final class DebugJsAstProtoBuf {
internal_static_org_jetbrains_kotlin_serialization_js_ast_Name_fieldAccessorTable = new
org.jetbrains.kotlin.protobuf.GeneratedMessage.FieldAccessorTable(
internal_static_org_jetbrains_kotlin_serialization_js_ast_Name_descriptor,
- new java.lang.String[] { "Temporary", "Identifier", "LocalNameId", "Imported", });
+ new java.lang.String[] { "Temporary", "Identifier", "LocalNameId", "Imported", "SpecialFunction", });
internal_static_org_jetbrains_kotlin_serialization_js_ast_Chunk_descriptor =
getDescriptor().getMessageTypes().get(54);
internal_static_org_jetbrains_kotlin_serialization_js_ast_Chunk_fieldAccessorTable = new
diff --git a/js/js.ast/src/org/jetbrains/kotlin/js/backend/ast/metadata/metadataProperties.kt b/js/js.ast/src/org/jetbrains/kotlin/js/backend/ast/metadata/metadataProperties.kt
index 905ff62a09d..8ffed53d6a0 100644
--- a/js/js.ast/src/org/jetbrains/kotlin/js/backend/ast/metadata/metadataProperties.kt
+++ b/js/js.ast/src/org/jetbrains/kotlin/js/backend/ast/metadata/metadataProperties.kt
@@ -31,6 +31,8 @@ var JsName.descriptor: DeclarationDescriptor? by MetadataProperty(default = null
var JsName.localAlias: JsName? by MetadataProperty(default = null)
+var JsName.specialFunction: SpecialFunction? by MetadataProperty(default = null)
+
var JsExpression.localAlias: JsName? by MetadataProperty(default = null)
// TODO: move this to module 'js.inliner' and change dependency on 'frontend' to dependency on 'descriptors'
@@ -133,4 +135,9 @@ enum class SideEffectKind {
AFFECTS_STATE,
DEPENDS_ON_STATE,
PURE
+}
+
+enum class SpecialFunction(val suggestedName: String) {
+ DEFINE_INLINE_FUNCTION("defineInlineFunction"),
+ WRAP_FUNCTION("wrapFunction")
}
\ No newline at end of file
diff --git a/js/js.dce/src/org/jetbrains/kotlin/js/dce/Analyzer.kt b/js/js.dce/src/org/jetbrains/kotlin/js/dce/Analyzer.kt
index b3b49667711..aaf531ab685 100644
--- a/js/js.dce/src/org/jetbrains/kotlin/js/dce/Analyzer.kt
+++ b/js/js.dce/src/org/jetbrains/kotlin/js/dce/Analyzer.kt
@@ -359,6 +359,7 @@ class Analyzer(private val context: Context) : JsVisitor() {
private fun enterFunction(function: JsFunction, arguments: List) {
functionsToEnter += function
context.addNodesForLocalVars(function.collectLocalVariables())
+ context.markSpecialFunctions(function.body)
for ((param, arg) in function.parameters.zip(arguments)) {
if (arg is JsFunction && arg.name == null && isProperFunctionalParameter(arg.body, param)) {
@@ -377,6 +378,7 @@ class Analyzer(private val context: Context) : JsVisitor() {
private fun enterFunctionWithGivenNodes(function: JsFunction, arguments: List) {
functionsToEnter += function
context.addNodesForLocalVars(function.collectLocalVariables())
+ context.markSpecialFunctions(function.body)
for ((param, arg) in function.parameters.zip(arguments)) {
val paramNode = context.nodes[param.name]!!
diff --git a/js/js.dce/src/org/jetbrains/kotlin/js/dce/Context.kt b/js/js.dce/src/org/jetbrains/kotlin/js/dce/Context.kt
index 53015ee5bc0..d21b038b623 100644
--- a/js/js.dce/src/org/jetbrains/kotlin/js/dce/Context.kt
+++ b/js/js.dce/src/org/jetbrains/kotlin/js/dce/Context.kt
@@ -17,6 +17,9 @@
package org.jetbrains.kotlin.js.dce
import org.jetbrains.kotlin.js.backend.ast.*
+import org.jetbrains.kotlin.js.backend.ast.metadata.SpecialFunction
+import org.jetbrains.kotlin.js.backend.ast.metadata.specialFunction
+import org.jetbrains.kotlin.js.translate.utils.JsAstUtils
import org.jetbrains.kotlin.js.translate.utils.jsAstUtils.array
import org.jetbrains.kotlin.js.translate.utils.jsAstUtils.index
@@ -32,6 +35,45 @@ class Context {
nodes += names.filter { it !in nodes }.associate { it to Node(it) }
}
+ fun markSpecialFunctions(root: JsNode) {
+ val candidates = mutableMapOf()
+ val unsuitableNames = mutableSetOf()
+ val assignedNames = mutableSetOf()
+ root.accept(object : RecursiveJsVisitor() {
+ override fun visit(x: JsVars.JsVar) {
+ val name = x.name
+ if (!assignedNames.add(name)) {
+ unsuitableNames += name
+ }
+
+ val initializer = x.initExpression
+ if (initializer != null) {
+ val specialName = when {
+ isDefineInlineFunction(initializer) -> SpecialFunction.DEFINE_INLINE_FUNCTION
+ isWrapFunction(initializer) -> SpecialFunction.WRAP_FUNCTION
+ else -> null
+ }
+ specialName?.let { candidates[name] = specialName }
+ }
+ super.visit(x)
+ }
+
+ override fun visitBinaryExpression(x: JsBinaryOperation) {
+ JsAstUtils.decomposeAssignmentToVariable(x)?.let { (left, _) -> unsuitableNames += left }
+ }
+
+ override fun visitFunction(x: JsFunction) {
+ x.name?.let { unsuitableNames += it }
+ }
+ })
+
+ for ((name, function) in candidates) {
+ if (name !in unsuitableNames) {
+ name.specialFunction = function
+ }
+ }
+ }
+
fun extractNode(expression: JsExpression): Node? {
val node = extractNodeImpl(expression)?.original
return if (node != null && moduleExportsNode in generateSequence(node) { it.qualifier?.parent }) {
diff --git a/js/js.dce/src/org/jetbrains/kotlin/js/dce/DeadCodeElimination.kt b/js/js.dce/src/org/jetbrains/kotlin/js/dce/DeadCodeElimination.kt
index 5815a3e6914..83879552a9b 100644
--- a/js/js.dce/src/org/jetbrains/kotlin/js/dce/DeadCodeElimination.kt
+++ b/js/js.dce/src/org/jetbrains/kotlin/js/dce/DeadCodeElimination.kt
@@ -18,10 +18,7 @@ package org.jetbrains.kotlin.js.dce
import com.google.gwt.dev.js.rhino.CodePosition
import com.google.gwt.dev.js.rhino.ErrorReporter
-import org.jetbrains.kotlin.js.backend.ast.JsBlock
-import org.jetbrains.kotlin.js.backend.ast.JsGlobalBlock
-import org.jetbrains.kotlin.js.backend.ast.JsNode
-import org.jetbrains.kotlin.js.backend.ast.JsProgram
+import org.jetbrains.kotlin.js.backend.ast.*
import org.jetbrains.kotlin.js.dce.Context.Node
import org.jetbrains.kotlin.js.inline.util.collectDefinedNames
import org.jetbrains.kotlin.js.inline.util.fixForwardNameReferences
diff --git a/js/js.dce/src/org/jetbrains/kotlin/js/dce/util.kt b/js/js.dce/src/org/jetbrains/kotlin/js/dce/util.kt
index 1d5efa21184..2beadabfaf4 100644
--- a/js/js.dce/src/org/jetbrains/kotlin/js/dce/util.kt
+++ b/js/js.dce/src/org/jetbrains/kotlin/js/dce/util.kt
@@ -17,6 +17,8 @@
package org.jetbrains.kotlin.js.dce
import org.jetbrains.kotlin.js.backend.ast.*
+import org.jetbrains.kotlin.js.backend.ast.metadata.SpecialFunction
+import org.jetbrains.kotlin.js.backend.ast.metadata.specialFunction
import org.jetbrains.kotlin.js.dce.Context.Node
fun Context.isObjectDefineProperty(function: JsExpression) = isObjectFunction(function, "defineProperty")
@@ -25,9 +27,11 @@ fun Context.isObjectGetOwnPropertyDescriptor(function: JsExpression) = isObjectF
fun Context.isDefineModule(function: JsExpression): Boolean = isKotlinFunction(function, "defineModule")
-fun Context.isDefineInlineFunction(function: JsExpression): Boolean = isKotlinFunction(function, "defineInlineFunction")
+fun Context.isDefineInlineFunction(function: JsExpression): Boolean =
+ isKotlinFunction(function, "defineInlineFunction") || isSpecialFunction(function, SpecialFunction.DEFINE_INLINE_FUNCTION)
-fun Context.isWrapFunction(function: JsExpression): Boolean = isKotlinFunction(function, "wrapFunction")
+fun Context.isWrapFunction(function: JsExpression): Boolean =
+ isKotlinFunction(function, "wrapFunction") || isSpecialFunction(function, SpecialFunction.WRAP_FUNCTION)
fun Context.isObjectFunction(function: JsExpression, functionName: String): Boolean {
if (function !is JsNameRef) return false
@@ -45,6 +49,9 @@ fun Context.isKotlinFunction(function: JsExpression, name: String): Boolean {
return receiver in nodes && receiver.ident.toLowerCase() == "kotlin"
}
+fun isSpecialFunction(expr: JsExpression, specialFunction: SpecialFunction): Boolean =
+ expr is JsNameRef && expr.qualifier == null && expr.name?.specialFunction == specialFunction
+
fun Context.isAmdDefine(function: JsExpression): Boolean = isTopLevelFunction(function, "define")
fun Context.isTopLevelFunction(function: JsExpression, name: String): Boolean {
diff --git a/js/js.inliner/src/org/jetbrains/kotlin/js/inline/FunctionReader.kt b/js/js.inliner/src/org/jetbrains/kotlin/js/inline/FunctionReader.kt
index f80e35e366e..a39284ced69 100644
--- a/js/js.inliner/src/org/jetbrains/kotlin/js/inline/FunctionReader.kt
+++ b/js/js.inliner/src/org/jetbrains/kotlin/js/inline/FunctionReader.kt
@@ -22,10 +22,7 @@ import com.intellij.util.containers.SLRUCache
import org.jetbrains.kotlin.builtins.isFunctionTypeOrSubtype
import org.jetbrains.kotlin.descriptors.CallableDescriptor
import org.jetbrains.kotlin.js.backend.ast.*
-import org.jetbrains.kotlin.js.backend.ast.metadata.SideEffectKind
-import org.jetbrains.kotlin.js.backend.ast.metadata.imported
-import org.jetbrains.kotlin.js.backend.ast.metadata.inlineStrategy
-import org.jetbrains.kotlin.js.backend.ast.metadata.sideEffects
+import org.jetbrains.kotlin.js.backend.ast.metadata.*
import org.jetbrains.kotlin.js.config.JsConfig
import org.jetbrains.kotlin.js.inline.util.*
import org.jetbrains.kotlin.js.parser.OffsetToSourceMapping
@@ -51,6 +48,7 @@ private val JS_IDENTIFIER_PART = "$JS_IDENTIFIER_START\\p{Pc}\\p{Mc}\\p{Mn}\\d"
private val JS_IDENTIFIER="[$JS_IDENTIFIER_START][$JS_IDENTIFIER_PART]*"
private val DEFINE_MODULE_PATTERN = ("($JS_IDENTIFIER)\\.defineModule\\(\\s*(['\"])([^'\"]+)\\2\\s*,\\s*(\\w+)\\s*\\)").toRegex().toPattern()
private val DEFINE_MODULE_FIND_PATTERN = ".defineModule("
+private val WRAP_FUNCTION_PATTERN = Regex("var\\s+($JS_IDENTIFIER)\\s*=\\s*($JS_IDENTIFIER)\\.wrapFunction\\s*;").toPattern()
class FunctionReader(
private val reporter: JsConfig.Reporter,
@@ -73,10 +71,13 @@ class FunctionReader(
val fileContent: String,
val moduleVariable: String,
val kotlinVariable: String,
+ val wrapFunctionVariable: String?,
offsetToSourceMappingProvider: () -> OffsetToSourceMapping,
val sourceMap: SourceMap?
) {
val offsetToSourceMapping by lazy(offsetToSourceMappingProvider)
+
+ val wrapFunctionRegex = wrapFunctionVariable?.let { Regex("\\s*$it\\s*\\(\\s*").toPattern() }
}
private val moduleNameToInfo by lazy {
@@ -98,6 +99,10 @@ class FunctionReader(
val moduleVariable = preciseMatcher.group(4)
val kotlinVariable = preciseMatcher.group(1)
+ val wrapFunctionVariable = WRAP_FUNCTION_PATTERN.matcher(content).let { matcher ->
+ if (matcher.find() && matcher.group(2) == kotlinVariable) matcher.group(1) else null
+ }
+
val sourceMap = sourceMapContent?.let {
val sourceMapResult = SourceMapParser.parse(StringReader(it))
when (sourceMapResult) {
@@ -114,6 +119,7 @@ class FunctionReader(
fileContent = content,
moduleVariable = moduleVariable,
kotlinVariable = kotlinVariable,
+ wrapFunctionVariable = wrapFunctionVariable,
offsetToSourceMappingProvider = { OffsetToSourceMapping(content) },
sourceMap = sourceMap
)
@@ -195,10 +201,11 @@ class FunctionReader(
offset++
}
- val wrapFunctionMatcher = wrapFunctionRegex.matcher(ShallowSubSequence(source, offset, source.length))
- val isWrapped = wrapFunctionMatcher.lookingAt()
+ val sourcePart = ShallowSubSequence(source, offset, source.length)
+ val wrapFunctionMatcher = info.wrapFunctionRegex?.matcher(sourcePart)
+ val isWrapped = wrapFunctionMatcher?.lookingAt() == true
if (isWrapped) {
- offset += wrapFunctionMatcher.end()
+ offset += wrapFunctionMatcher!!.end()
}
val position = info.offsetToSourceMapping[offset]
@@ -220,12 +227,19 @@ class FunctionReader(
wrapperStatements?.forEach { remapper.remap(it) }
}
+ val allDefinedNames = collectDefinedNamesInAllScopes(function)
val replacements = hashMapOf(info.moduleVariable to moduleReference,
info.kotlinVariable to Namer.kotlinObject())
- replaceExternalNames(function, replacements)
- wrapperStatements?.forEach { replaceExternalNames(it, replacements) }
+ replaceExternalNames(function, replacements, allDefinedNames)
+ wrapperStatements?.forEach { replaceExternalNames(it, replacements, allDefinedNames) }
function.markInlineArguments(descriptor)
+ info.wrapFunctionVariable.let { wrapFunction ->
+ for (externalName in (collectReferencedNames(function) - allDefinedNames).filter { it.ident == wrapFunction }) {
+ externalName.specialFunction = SpecialFunction.WRAP_FUNCTION
+ }
+ }
+
val namesWithoutSizeEffects = wrapperStatements.orEmpty().asSequence()
.flatMap { collectDefinedNames(it).asSequence() }
.toSet()
@@ -283,12 +297,10 @@ private fun JsFunction.markInlineArguments(descriptor: CallableDescriptor) {
visitor.accept(this)
}
-private fun replaceExternalNames(node: JsNode, replacements: Map) {
- val skipNames = collectDefinedNamesInAllScopes(node)
-
+private fun replaceExternalNames(node: JsNode, replacements: Map, definedNames: Set) {
val visitor = object: JsVisitorWithContextImpl() {
override fun endVisit(x: JsNameRef, ctx: JsContext) {
- if (x.qualifier != null || x.name in skipNames) return
+ if (x.qualifier != null || x.name in definedNames) return
replacements[x.ident]?.let {
ctx.replaceMe(it)
@@ -299,8 +311,6 @@ private fun replaceExternalNames(node: JsNode, replacements: Map =
collectReferencedNames(scope).filter { it.staticRef is JsFunction }
-private fun collectReferencedNames(scope: JsNode): Set {
+fun collectReferencedNames(scope: JsNode): Set {
val references = mutableSetOf()
object : RecursiveJsVisitor() {
diff --git a/js/js.serializer/src/js-ast.proto b/js/js.serializer/src/js-ast.proto
index 6ff59c10c97..986becd6d64 100644
--- a/js/js.serializer/src/js-ast.proto
+++ b/js/js.serializer/src/js-ast.proto
@@ -427,6 +427,12 @@ message Name {
optional int32 identifier = 2;
optional int32 local_name_id = 3;
optional bool imported = 4 [default = false];
+ optional SpecialFunction special_function = 5;
+}
+
+enum SpecialFunction {
+ DEFINE_INLINE_FUNCTION = 1;
+ WRAP_FUNCTION = 2;
}
diff --git a/js/js.serializer/src/org/jetbrains/kotlin/serialization/js/ast/JsAstDeserializer.kt b/js/js.serializer/src/org/jetbrains/kotlin/serialization/js/ast/JsAstDeserializer.kt
index 046143a0412..16526238bb3 100644
--- a/js/js.serializer/src/org/jetbrains/kotlin/serialization/js/ast/JsAstDeserializer.kt
+++ b/js/js.serializer/src/org/jetbrains/kotlin/serialization/js/ast/JsAstDeserializer.kt
@@ -18,6 +18,7 @@ package org.jetbrains.kotlin.serialization.js.ast
import org.jetbrains.kotlin.js.backend.ast.*
import org.jetbrains.kotlin.js.backend.ast.metadata.*
+import org.jetbrains.kotlin.js.backend.ast.metadata.SpecialFunction
import org.jetbrains.kotlin.protobuf.CodedInputStream
import org.jetbrains.kotlin.serialization.js.ast.JsAstProtoBuf.*
import org.jetbrains.kotlin.serialization.js.ast.JsAstProtoBuf.Expression.ExpressionCase
@@ -443,6 +444,9 @@ class JsAstDeserializer(program: JsProgram, private val sourceRoots: Iterable KotlinInlineStrategy.NOT_INLINE
}
+ private fun map(specialFunction: JsAstProtoBuf.SpecialFunction) = when(specialFunction) {
+ JsAstProtoBuf.SpecialFunction.DEFINE_INLINE_FUNCTION -> SpecialFunction.DEFINE_INLINE_FUNCTION
+ JsAstProtoBuf.SpecialFunction.WRAP_FUNCTION -> SpecialFunction.WRAP_FUNCTION
+ }
+
private fun withLocation(fileId: Int?, location: Location?, action: () -> T): T {
val deserializedFile = fileId?.let { deserializeString(it) }
val file = deserializedFile ?: fileStack.peek()
diff --git a/js/js.serializer/src/org/jetbrains/kotlin/serialization/js/ast/JsAstProtoBuf.java b/js/js.serializer/src/org/jetbrains/kotlin/serialization/js/ast/JsAstProtoBuf.java
index 22bd4023af8..b1f8c70568c 100644
--- a/js/js.serializer/src/org/jetbrains/kotlin/serialization/js/ast/JsAstProtoBuf.java
+++ b/js/js.serializer/src/org/jetbrains/kotlin/serialization/js/ast/JsAstProtoBuf.java
@@ -138,6 +138,62 @@ public final class JsAstProtoBuf {
// @@protoc_insertion_point(enum_scope:org.jetbrains.kotlin.serialization.js.ast.InlineStrategy)
}
+ /**
+ * Protobuf enum {@code org.jetbrains.kotlin.serialization.js.ast.SpecialFunction}
+ */
+ public enum SpecialFunction
+ implements org.jetbrains.kotlin.protobuf.Internal.EnumLite {
+ /**
+ * DEFINE_INLINE_FUNCTION = 1;
+ */
+ DEFINE_INLINE_FUNCTION(0, 1),
+ /**
+ * WRAP_FUNCTION = 2;
+ */
+ WRAP_FUNCTION(1, 2),
+ ;
+
+ /**
+ * DEFINE_INLINE_FUNCTION = 1;
+ */
+ public static final int DEFINE_INLINE_FUNCTION_VALUE = 1;
+ /**
+ * WRAP_FUNCTION = 2;
+ */
+ public static final int WRAP_FUNCTION_VALUE = 2;
+
+
+ public final int getNumber() { return value; }
+
+ public static SpecialFunction valueOf(int value) {
+ switch (value) {
+ case 1: return DEFINE_INLINE_FUNCTION;
+ case 2: return WRAP_FUNCTION;
+ default: return null;
+ }
+ }
+
+ public static org.jetbrains.kotlin.protobuf.Internal.EnumLiteMap
+ internalGetValueMap() {
+ return internalValueMap;
+ }
+ private static org.jetbrains.kotlin.protobuf.Internal.EnumLiteMap
+ internalValueMap =
+ new org.jetbrains.kotlin.protobuf.Internal.EnumLiteMap() {
+ public SpecialFunction findValueByNumber(int number) {
+ return SpecialFunction.valueOf(number);
+ }
+ };
+
+ private final int value;
+
+ private SpecialFunction(int index, int value) {
+ this.value = value;
+ }
+
+ // @@protoc_insertion_point(enum_scope:org.jetbrains.kotlin.serialization.js.ast.SpecialFunction)
+ }
+
public interface LocationOrBuilder extends
// @@protoc_insertion_point(interface_extends:org.jetbrains.kotlin.serialization.js.ast.Location)
org.jetbrains.kotlin.protobuf.MessageLiteOrBuilder {
@@ -34765,13 +34821,22 @@ public final class JsAstProtoBuf {
int getLocalNameId();
/**
- * optional bool imported = 4;
+ * optional bool imported = 4 [default = false];
*/
boolean hasImported();
/**
- * optional bool imported = 4;
+ * optional bool imported = 4 [default = false];
*/
boolean getImported();
+
+ /**
+ * optional .org.jetbrains.kotlin.serialization.js.ast.SpecialFunction special_function = 5;
+ */
+ boolean hasSpecialFunction();
+ /**
+ * optional .org.jetbrains.kotlin.serialization.js.ast.SpecialFunction special_function = 5;
+ */
+ org.jetbrains.kotlin.serialization.js.ast.JsAstProtoBuf.SpecialFunction getSpecialFunction();
}
/**
* Protobuf type {@code org.jetbrains.kotlin.serialization.js.ast.Name}
@@ -34843,6 +34908,18 @@ public final class JsAstProtoBuf {
imported_ = input.readBool();
break;
}
+ case 40: {
+ int rawValue = input.readEnum();
+ org.jetbrains.kotlin.serialization.js.ast.JsAstProtoBuf.SpecialFunction value = org.jetbrains.kotlin.serialization.js.ast.JsAstProtoBuf.SpecialFunction.valueOf(rawValue);
+ if (value == null) {
+ unknownFieldsCodedOutput.writeRawVarint32(tag);
+ unknownFieldsCodedOutput.writeRawVarint32(rawValue);
+ } else {
+ bitField0_ |= 0x00000010;
+ specialFunction_ = value;
+ }
+ break;
+ }
}
}
} catch (org.jetbrains.kotlin.protobuf.InvalidProtocolBufferException e) {
@@ -34925,23 +35002,39 @@ public final class JsAstProtoBuf {
public static final int IMPORTED_FIELD_NUMBER = 4;
private boolean imported_;
/**
- * optional bool imported = 4;
+ * optional bool imported = 4 [default = false];
*/
public boolean hasImported() {
return ((bitField0_ & 0x00000008) == 0x00000008);
}
/**
- * optional bool imported = 4;
+ * optional bool imported = 4 [default = false];
*/
public boolean getImported() {
return imported_;
}
+ public static final int SPECIAL_FUNCTION_FIELD_NUMBER = 5;
+ private org.jetbrains.kotlin.serialization.js.ast.JsAstProtoBuf.SpecialFunction specialFunction_;
+ /**
+ * optional .org.jetbrains.kotlin.serialization.js.ast.SpecialFunction special_function = 5;
+ */
+ public boolean hasSpecialFunction() {
+ return ((bitField0_ & 0x00000010) == 0x00000010);
+ }
+ /**
+ * optional .org.jetbrains.kotlin.serialization.js.ast.SpecialFunction special_function = 5;
+ */
+ public org.jetbrains.kotlin.serialization.js.ast.JsAstProtoBuf.SpecialFunction getSpecialFunction() {
+ return specialFunction_;
+ }
+
private void initFields() {
temporary_ = false;
identifier_ = 0;
localNameId_ = 0;
imported_ = false;
+ specialFunction_ = org.jetbrains.kotlin.serialization.js.ast.JsAstProtoBuf.SpecialFunction.DEFINE_INLINE_FUNCTION;
}
private byte memoizedIsInitialized = -1;
public final boolean isInitialized() {
@@ -34972,6 +35065,9 @@ public final class JsAstProtoBuf {
if (((bitField0_ & 0x00000008) == 0x00000008)) {
output.writeBool(4, imported_);
}
+ if (((bitField0_ & 0x00000010) == 0x00000010)) {
+ output.writeEnum(5, specialFunction_.getNumber());
+ }
output.writeRawBytes(unknownFields);
}
@@ -34997,6 +35093,10 @@ public final class JsAstProtoBuf {
size += org.jetbrains.kotlin.protobuf.CodedOutputStream
.computeBoolSize(4, imported_);
}
+ if (((bitField0_ & 0x00000010) == 0x00000010)) {
+ size += org.jetbrains.kotlin.protobuf.CodedOutputStream
+ .computeEnumSize(5, specialFunction_.getNumber());
+ }
size += unknownFields.size();
memoizedSerializedSize = size;
return size;
@@ -35099,6 +35199,8 @@ public final class JsAstProtoBuf {
bitField0_ = (bitField0_ & ~0x00000004);
imported_ = false;
bitField0_ = (bitField0_ & ~0x00000008);
+ specialFunction_ = org.jetbrains.kotlin.serialization.js.ast.JsAstProtoBuf.SpecialFunction.DEFINE_INLINE_FUNCTION;
+ bitField0_ = (bitField0_ & ~0x00000010);
return this;
}
@@ -35138,6 +35240,10 @@ public final class JsAstProtoBuf {
to_bitField0_ |= 0x00000008;
}
result.imported_ = imported_;
+ if (((from_bitField0_ & 0x00000010) == 0x00000010)) {
+ to_bitField0_ |= 0x00000010;
+ }
+ result.specialFunction_ = specialFunction_;
result.bitField0_ = to_bitField0_;
return result;
}
@@ -35156,6 +35262,9 @@ public final class JsAstProtoBuf {
if (other.hasImported()) {
setImported(other.getImported());
}
+ if (other.hasSpecialFunction()) {
+ setSpecialFunction(other.getSpecialFunction());
+ }
setUnknownFields(
getUnknownFields().concat(other.unknownFields));
return this;
@@ -35286,19 +35395,19 @@ public final class JsAstProtoBuf {
private boolean imported_ ;
/**
- * optional bool imported = 4;
+ * optional bool imported = 4 [default = false];
*/
public boolean hasImported() {
return ((bitField0_ & 0x00000008) == 0x00000008);
}
/**
- * optional bool imported = 4;
+ * optional bool imported = 4 [default = false];
*/
public boolean getImported() {
return imported_;
}
/**
- * optional bool imported = 4;
+ * optional bool imported = 4 [default = false];
*/
public Builder setImported(boolean value) {
bitField0_ |= 0x00000008;
@@ -35307,7 +35416,7 @@ public final class JsAstProtoBuf {
return this;
}
/**
- * optional bool imported = 4;
+ * optional bool imported = 4 [default = false];
*/
public Builder clearImported() {
bitField0_ = (bitField0_ & ~0x00000008);
@@ -35316,6 +35425,41 @@ public final class JsAstProtoBuf {
return this;
}
+ private org.jetbrains.kotlin.serialization.js.ast.JsAstProtoBuf.SpecialFunction specialFunction_ = org.jetbrains.kotlin.serialization.js.ast.JsAstProtoBuf.SpecialFunction.DEFINE_INLINE_FUNCTION;
+ /**
+ * optional .org.jetbrains.kotlin.serialization.js.ast.SpecialFunction special_function = 5;
+ */
+ public boolean hasSpecialFunction() {
+ return ((bitField0_ & 0x00000010) == 0x00000010);
+ }
+ /**
+ * optional .org.jetbrains.kotlin.serialization.js.ast.SpecialFunction special_function = 5;
+ */
+ public org.jetbrains.kotlin.serialization.js.ast.JsAstProtoBuf.SpecialFunction getSpecialFunction() {
+ return specialFunction_;
+ }
+ /**
+ * optional .org.jetbrains.kotlin.serialization.js.ast.SpecialFunction special_function = 5;
+ */
+ public Builder setSpecialFunction(org.jetbrains.kotlin.serialization.js.ast.JsAstProtoBuf.SpecialFunction value) {
+ if (value == null) {
+ throw new NullPointerException();
+ }
+ bitField0_ |= 0x00000010;
+ specialFunction_ = value;
+
+ return this;
+ }
+ /**
+ * optional .org.jetbrains.kotlin.serialization.js.ast.SpecialFunction special_function = 5;
+ */
+ public Builder clearSpecialFunction() {
+ bitField0_ = (bitField0_ & ~0x00000010);
+ specialFunction_ = org.jetbrains.kotlin.serialization.js.ast.JsAstProtoBuf.SpecialFunction.DEFINE_INLINE_FUNCTION;
+
+ return this;
+ }
+
// @@protoc_insertion_point(builder_scope:org.jetbrains.kotlin.serialization.js.ast.Name)
}
diff --git a/js/js.serializer/src/org/jetbrains/kotlin/serialization/js/ast/JsAstSerializer.kt b/js/js.serializer/src/org/jetbrains/kotlin/serialization/js/ast/JsAstSerializer.kt
index 18b29dfdbd8..1f843447645 100644
--- a/js/js.serializer/src/org/jetbrains/kotlin/serialization/js/ast/JsAstSerializer.kt
+++ b/js/js.serializer/src/org/jetbrains/kotlin/serialization/js/ast/JsAstSerializer.kt
@@ -19,6 +19,7 @@ package org.jetbrains.kotlin.serialization.js.ast
import com.intellij.psi.PsiElement
import org.jetbrains.kotlin.js.backend.ast.*
import org.jetbrains.kotlin.js.backend.ast.metadata.*
+import org.jetbrains.kotlin.js.backend.ast.metadata.SpecialFunction
import org.jetbrains.kotlin.serialization.js.ast.JsAstProtoBuf.*
import org.jetbrains.kotlin.serialization.js.ast.JsAstProtoBuf.BinaryOperation.Type.*
import org.jetbrains.kotlin.serialization.js.ast.JsAstProtoBuf.UnaryOperation.Type.*
@@ -552,6 +553,11 @@ class JsAstSerializer(private val pathResolver: (File) -> String) {
KotlinInlineStrategy.NOT_INLINE -> InlineStrategy.NOT_INLINE
}
+ private fun map(specialFunction: SpecialFunction) = when (specialFunction) {
+ SpecialFunction.DEFINE_INLINE_FUNCTION -> JsAstProtoBuf.SpecialFunction.DEFINE_INLINE_FUNCTION
+ SpecialFunction.WRAP_FUNCTION -> JsAstProtoBuf.SpecialFunction.WRAP_FUNCTION
+ }
+
private fun serialize(name: JsName): Int = nameMap.getOrPut(name) {
val builder = Name.newBuilder()
builder.identifier = serialize(name.ident)
@@ -564,6 +570,10 @@ class JsAstSerializer(private val pathResolver: (File) -> String) {
builder.imported = true
}
+ name.specialFunction?.let {
+ builder.specialFunction = map(it)
+ }
+
val result = nameTableBuilder.entryCount
nameTableBuilder.addEntry(builder)
result
diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/context/Namer.java b/js/js.translator/src/org/jetbrains/kotlin/js/translate/context/Namer.java
index a54d4dc54e8..9e4a655f0ec 100644
--- a/js/js.translator/src/org/jetbrains/kotlin/js/translate/context/Namer.java
+++ b/js/js.translator/src/org/jetbrains/kotlin/js/translate/context/Namer.java
@@ -29,6 +29,7 @@ import org.jetbrains.kotlin.incremental.components.NoLookupLocation;
import org.jetbrains.kotlin.js.backend.ast.*;
import org.jetbrains.kotlin.js.backend.ast.metadata.MetadataProperties;
import org.jetbrains.kotlin.js.backend.ast.metadata.SideEffectKind;
+import org.jetbrains.kotlin.js.backend.ast.metadata.SpecialFunction;
import org.jetbrains.kotlin.js.backend.ast.metadata.TypeCheck;
import org.jetbrains.kotlin.js.config.JsConfig;
import org.jetbrains.kotlin.js.naming.NameSuggestion;
@@ -361,6 +362,18 @@ public final class Namer {
return pureFqn(WRAP_FUNCTION, kotlinObject());
}
+ @NotNull
+ public static JsExpression createSpecialFunction(@NotNull SpecialFunction specialFunction) {
+ switch (specialFunction) {
+ case DEFINE_INLINE_FUNCTION:
+ return createInlineFunction();
+ case WRAP_FUNCTION:
+ return wrapFunction();
+ default:
+ throw new IllegalArgumentException("Unknown function: " + specialFunction);
+ }
+ }
+
@NotNull
public static String suggestedModuleName(@NotNull String id) {
if (id.isEmpty()) {
diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/context/StaticContext.java b/js/js.translator/src/org/jetbrains/kotlin/js/translate/context/StaticContext.java
index d9fc0a284c2..00e12d73892 100644
--- a/js/js.translator/src/org/jetbrains/kotlin/js/translate/context/StaticContext.java
+++ b/js/js.translator/src/org/jetbrains/kotlin/js/translate/context/StaticContext.java
@@ -31,6 +31,7 @@ import org.jetbrains.kotlin.descriptors.impl.LocalVariableDescriptor;
import org.jetbrains.kotlin.js.backend.ast.*;
import org.jetbrains.kotlin.js.backend.ast.metadata.MetadataProperties;
import org.jetbrains.kotlin.js.backend.ast.metadata.SideEffectKind;
+import org.jetbrains.kotlin.js.backend.ast.metadata.SpecialFunction;
import org.jetbrains.kotlin.js.config.JsConfig;
import org.jetbrains.kotlin.js.naming.NameSuggestion;
import org.jetbrains.kotlin.js.naming.NameSuggestionKt;
@@ -42,6 +43,7 @@ import org.jetbrains.kotlin.js.translate.intrinsic.Intrinsics;
import org.jetbrains.kotlin.js.translate.utils.AnnotationsUtils;
import org.jetbrains.kotlin.js.translate.utils.JsAstUtils;
import org.jetbrains.kotlin.js.translate.utils.SignatureUtilsKt;
+import org.jetbrains.kotlin.js.translate.utils.TranslationUtils;
import org.jetbrains.kotlin.name.ClassId;
import org.jetbrains.kotlin.name.FqName;
import org.jetbrains.kotlin.resolve.BindingContext;
@@ -141,6 +143,8 @@ public final class StaticContext {
private final Map modulesImportedForInline = new HashMap<>();
+ private final Map specialFunctions = new EnumMap<>(SpecialFunction.class);
+
public StaticContext(
@NotNull BindingTrace bindingTrace,
@NotNull JsConfig config,
@@ -859,4 +863,14 @@ public final class StaticContext {
return moduleRef;
}).deepCopy();
}
+
+ @NotNull
+ public JsName getNameForSpecialFunction(@NotNull SpecialFunction specialFunction) {
+ return specialFunctions.computeIfAbsent(specialFunction, f -> {
+ JsExpression expression = Namer.createSpecialFunction(specialFunction);
+ JsName name = importDeclaration(f.getSuggestedName(), TranslationUtils.getTagForSpecialFunction(f), expression);
+ MetadataProperties.setSpecialFunction(name, f);
+ return name;
+ });
+ }
}
diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/context/TranslationContext.java b/js/js.translator/src/org/jetbrains/kotlin/js/translate/context/TranslationContext.java
index 2b67799ffe4..e7c8f2752e6 100644
--- a/js/js.translator/src/org/jetbrains/kotlin/js/translate/context/TranslationContext.java
+++ b/js/js.translator/src/org/jetbrains/kotlin/js/translate/context/TranslationContext.java
@@ -26,6 +26,7 @@ import org.jetbrains.kotlin.descriptors.impl.TypeAliasConstructorDescriptor;
import org.jetbrains.kotlin.incremental.components.NoLookupLocation;
import org.jetbrains.kotlin.js.backend.ast.*;
import org.jetbrains.kotlin.js.backend.ast.metadata.MetadataProperties;
+import org.jetbrains.kotlin.js.backend.ast.metadata.SpecialFunction;
import org.jetbrains.kotlin.js.config.JsConfig;
import org.jetbrains.kotlin.js.naming.SuggestedName;
import org.jetbrains.kotlin.js.translate.declaration.ClassModelGenerator;
@@ -819,4 +820,22 @@ public class TranslationContext {
public TranslationContext getParent() {
return parent;
}
+
+ @NotNull
+ public JsName getNameForSpecialFunction(@NotNull SpecialFunction function) {
+ if (inlineFunctionContext == null || !isPublicInlineFunction()) {
+ return staticContext.getNameForSpecialFunction(function);
+ }
+ else {
+ String tag = TranslationUtils.getTagForSpecialFunction(function);
+ return inlineFunctionContext.getImports().computeIfAbsent(tag, t -> {
+ JsExpression imported = Namer.createSpecialFunction(function);
+ JsName result = JsScope.declareTemporaryName(function.getSuggestedName());
+ MetadataProperties.setImported(result, true);
+ MetadataProperties.setSpecialFunction(result, function);
+ inlineFunctionContext.getImportBlock().getStatements().add(JsAstUtils.newVar(result, imported));
+ return result;
+ });
+ }
+ }
}
diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/declaration/AbstractDeclarationVisitor.kt b/js/js.translator/src/org/jetbrains/kotlin/js/translate/declaration/AbstractDeclarationVisitor.kt
index 1c3269fcc65..2b8ec1f5e3b 100644
--- a/js/js.translator/src/org/jetbrains/kotlin/js/translate/declaration/AbstractDeclarationVisitor.kt
+++ b/js/js.translator/src/org/jetbrains/kotlin/js/translate/declaration/AbstractDeclarationVisitor.kt
@@ -113,7 +113,7 @@ abstract class AbstractDeclarationVisitor : TranslatorVisitor() {
function.body.statements += FunctionBodyTranslator.setDefaultValueForArguments(descriptor, innerContext)
}
innerContext.translateFunction(expression, function)
- return innerContext.wrapWithInlineMetadata(function, descriptor)
+ return innerContext.wrapWithInlineMetadata(context, function, descriptor)
}
// used from kotlinx.serialization
diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/expression/FunctionTranslator.kt b/js/js.translator/src/org/jetbrains/kotlin/js/translate/expression/FunctionTranslator.kt
index 0519f5a183b..c987c71dc45 100644
--- a/js/js.translator/src/org/jetbrains/kotlin/js/translate/expression/FunctionTranslator.kt
+++ b/js/js.translator/src/org/jetbrains/kotlin/js/translate/expression/FunctionTranslator.kt
@@ -101,11 +101,14 @@ fun TranslationContext.translateFunction(declaration: KtDeclarationWithBody, fun
function.functionDescriptor = descriptor
}
-fun TranslationContext.wrapWithInlineMetadata(function: JsFunction, descriptor: FunctionDescriptor): JsExpression {
+fun TranslationContext.wrapWithInlineMetadata(
+ outerContext: TranslationContext,
+ function: JsFunction, descriptor: FunctionDescriptor
+): JsExpression {
val sourceInfo = descriptor.source.getPsi()
return if (descriptor.isInline && descriptor.isEffectivelyPublicApi) {
val metadata = InlineMetadata.compose(function, descriptor, this)
- val functionWithMetadata = metadata.functionWithMetadata(sourceInfo)
+ val functionWithMetadata = metadata.functionWithMetadata(outerContext, sourceInfo)
config.configuration[JSConfigurationKeys.INCREMENTAL_RESULTS_CONSUMER]?.apply {
val psiFile = (descriptor.source.containingFile as? PsiSourceFile)?.psiFile ?: return@apply
@@ -136,6 +139,6 @@ fun TranslationContext.wrapWithInlineMetadata(function: JsFunction, descriptor:
else {
null
}
- if (block != null) InlineMetadata.wrapFunction(FunctionWithWrapper(function, block), sourceInfo) else function
+ if (block != null) InlineMetadata.wrapFunction(outerContext, FunctionWithWrapper(function, block), sourceInfo) else function
}
}
diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/expression/InlineMetadata.kt b/js/js.translator/src/org/jetbrains/kotlin/js/translate/expression/InlineMetadata.kt
index 6398f583e8a..c89ff9d657d 100644
--- a/js/js.translator/src/org/jetbrains/kotlin/js/translate/expression/InlineMetadata.kt
+++ b/js/js.translator/src/org/jetbrains/kotlin/js/translate/expression/InlineMetadata.kt
@@ -18,6 +18,8 @@ package org.jetbrains.kotlin.js.translate.expression
import org.jetbrains.kotlin.descriptors.CallableDescriptor
import org.jetbrains.kotlin.js.backend.ast.*
+import org.jetbrains.kotlin.js.backend.ast.metadata.SpecialFunction
+import org.jetbrains.kotlin.js.backend.ast.metadata.specialFunction
import org.jetbrains.kotlin.js.inline.util.FunctionWithWrapper
import org.jetbrains.kotlin.js.translate.context.Namer
import org.jetbrains.kotlin.js.translate.context.TranslationContext
@@ -43,8 +45,11 @@ class InlineMetadata(val tag: JsStringLiteral, val function: FunctionWithWrapper
}
private fun decomposeCreateFunctionCall(call: JsInvocation): InlineMetadata? {
- val qualifier = call.qualifier
- if (qualifier !is JsNameRef || qualifier.ident != Namer.DEFINE_INLINE_FUNCTION) return null
+ val qualifier = call.qualifier as? JsNameRef ?: return null
+ if (qualifier.ident != Namer.DEFINE_INLINE_FUNCTION &&
+ qualifier.name?.specialFunction != SpecialFunction.DEFINE_INLINE_FUNCTION) {
+ return null
+ }
val arguments = call.arguments
if (arguments.size != METADATA_PROPERTIES_COUNT) return null
@@ -61,8 +66,7 @@ class InlineMetadata(val tag: JsStringLiteral, val function: FunctionWithWrapper
if (callExpression !is JsInvocation) return null
val qualifier = callExpression.qualifier as? JsNameRef ?: return null
- val qualifierQualifier = qualifier.qualifier as? JsNameRef ?: return null
- if (qualifierQualifier.qualifier != null || qualifier.ident != Namer.WRAP_FUNCTION) return null
+ if (qualifier.name?.specialFunction != SpecialFunction.WRAP_FUNCTION) return null
if (callExpression.arguments.size != 1) return null
val argument = callExpression.arguments[0] as? JsFunction ?: return null
@@ -78,7 +82,7 @@ class InlineMetadata(val tag: JsStringLiteral, val function: FunctionWithWrapper
}
@JvmStatic
- fun wrapFunction(function: FunctionWithWrapper, sourceInfo: Any?): JsExpression {
+ fun wrapFunction(context: TranslationContext, function: FunctionWithWrapper, sourceInfo: Any?): JsExpression {
val wrapperBody = function.wrapperBody ?: JsBlock(JsReturn(function.function))
val wrapper = JsFunction(function.function.scope, wrapperBody, "")
function.wrapperBody?.statements?.forEach {
@@ -90,10 +94,11 @@ class InlineMetadata(val tag: JsStringLiteral, val function: FunctionWithWrapper
}
}
- return JsInvocation(Namer.wrapFunction(), wrapper).source(sourceInfo)
+ return JsInvocation(context.getNameForSpecialFunction(SpecialFunction.WRAP_FUNCTION).makeRef(), wrapper).source(sourceInfo)
}
}
- fun functionWithMetadata(sourceInfo: Any?): JsExpression =
- JsInvocation(Namer.createInlineFunction(), tag, wrapFunction(function, sourceInfo))
+ fun functionWithMetadata(context: TranslationContext, sourceInfo: Any?): JsExpression =
+ JsInvocation(context.getNameForSpecialFunction(SpecialFunction.DEFINE_INLINE_FUNCTION).makeRef(),
+ tag, wrapFunction(context, function, sourceInfo))
}
\ No newline at end of file
diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/expression/LiteralFunctionTranslator.kt b/js/js.translator/src/org/jetbrains/kotlin/js/translate/expression/LiteralFunctionTranslator.kt
index b958f44c4ca..bf0fcceaa1e 100644
--- a/js/js.translator/src/org/jetbrains/kotlin/js/translate/expression/LiteralFunctionTranslator.kt
+++ b/js/js.translator/src/org/jetbrains/kotlin/js/translate/expression/LiteralFunctionTranslator.kt
@@ -114,7 +114,7 @@ class LiteralFunctionTranslator(context: TranslationContext) : AbstractTranslato
private fun TranslationContext.addFunctionDeclaration(name: JsName, function: JsFunction, source: Any?) {
addFunctionButNotExport(name, if (isPublicInlineFunction) {
- InlineMetadata.wrapFunction(FunctionWithWrapper(function, null), source)
+ InlineMetadata.wrapFunction(this, FunctionWithWrapper(function, null), source)
}
else {
function
@@ -127,7 +127,7 @@ fun JsFunction.withCapturedParameters(
invokingContext: TranslationContext,
source: KtDeclaration
): JsExpression {
- context.addFunctionDeclaration(functionName, this, source)
+ invokingContext.addFunctionDeclaration(functionName, this, source)
val ref = JsAstUtils.pureFqn(functionName, null)
val invocation = JsInvocation(ref).apply { sideEffects = SideEffectKind.PURE }
diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/utils/TranslationUtils.java b/js/js.translator/src/org/jetbrains/kotlin/js/translate/utils/TranslationUtils.java
index 45ccb8921e0..11d069ffaa1 100644
--- a/js/js.translator/src/org/jetbrains/kotlin/js/translate/utils/TranslationUtils.java
+++ b/js/js.translator/src/org/jetbrains/kotlin/js/translate/utils/TranslationUtils.java
@@ -25,6 +25,7 @@ import org.jetbrains.kotlin.descriptors.impl.LocalVariableAccessorDescriptor;
import org.jetbrains.kotlin.descriptors.impl.LocalVariableDescriptor;
import org.jetbrains.kotlin.incremental.components.NoLookupLocation;
import org.jetbrains.kotlin.js.backend.ast.*;
+import org.jetbrains.kotlin.js.backend.ast.metadata.SpecialFunction;
import org.jetbrains.kotlin.js.translate.context.Namer;
import org.jetbrains.kotlin.js.translate.context.TemporaryConstVariable;
import org.jetbrains.kotlin.js.translate.context.TranslationContext;
@@ -58,14 +59,15 @@ public final class TranslationUtils {
}
@NotNull
- public static JsPropertyInitializer translateFunctionAsEcma5PropertyDescriptor(@NotNull JsFunction function,
- @NotNull FunctionDescriptor descriptor,
- @NotNull TranslationContext context) {
+ public static JsPropertyInitializer translateFunctionAsEcma5PropertyDescriptor(
+ @NotNull JsFunction function, @NotNull FunctionDescriptor descriptor,
+ @NotNull TranslationContext context
+ ) {
JsExpression functionExpression = function;
if (InlineUtil.isInline(descriptor)) {
InlineMetadata metadata = InlineMetadata.compose(function, descriptor, context);
PsiElement sourceInfo = KotlinSourceElementKt.getPsi(descriptor.getSource());
- functionExpression = metadata.functionWithMetadata(sourceInfo);
+ functionExpression = metadata.functionWithMetadata(context, sourceInfo);
}
if (DescriptorUtils.isExtension(descriptor) ||
@@ -414,4 +416,9 @@ public final class TranslationUtils {
}
return e;
}
+
+ @NotNull
+ public static String getTagForSpecialFunction(@NotNull SpecialFunction specialFunction) {
+ return "special:" + specialFunction.name();
+ }
}
diff --git a/js/js.translator/testData/box/inlineMultiModule/repeatedImport.kt b/js/js.translator/testData/box/inlineMultiModule/repeatedImport.kt
index 3a690376d76..710d3c9ce22 100644
--- a/js/js.translator/testData/box/inlineMultiModule/repeatedImport.kt
+++ b/js/js.translator/testData/box/inlineMultiModule/repeatedImport.kt
@@ -1,3 +1,4 @@
+// EXPECTED_REACHABLE_NODES: 994
// MODULE: lib
// FILE: lib.kt
fun foo(x: String) = "foo($x)"