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)"