Implement inlining of Array constructor in JS BE

See KT-15456
This commit is contained in:
Alexey Andreev
2017-04-28 16:24:31 +03:00
parent 6523f237a6
commit 1900b20e6e
16 changed files with 156 additions and 71 deletions
@@ -1,6 +1,3 @@
// TODO: muted automatically, investigate should it be ran for JS or not
// IGNORE_BACKEND: JS
fun testArray() { fun testArray() {
Array<String>(5) { i -> Array<String>(5) { i ->
if (i == 3) return if (i == 3) return
@@ -17,8 +14,44 @@ fun testIntArray() {
throw AssertionError() throw AssertionError()
} }
fun testLongArray() {
LongArray(5) { i ->
if (i == 3) return
i.toLong()
}
throw AssertionError()
}
fun testBooleanArray() {
BooleanArray(5) { i ->
if (i == 3) return
i % 2 == 0
}
throw AssertionError()
}
fun testCharArray() {
CharArray(5) { i ->
if (i == 3) return
i.toChar()
}
throw AssertionError()
}
fun testFloatArray() {
FloatArray(5) { i ->
if (i == 3) return
i.toFloat()
}
throw AssertionError()
}
fun box(): String { fun box(): String {
testArray() testArray()
testIntArray() testIntArray()
testLongArray()
testBooleanArray()
testCharArray()
testFloatArray()
return "OK" return "OK"
} }
@@ -28,7 +28,6 @@ import org.jetbrains.kotlin.js.inline.util.IdentitySet
import org.jetbrains.kotlin.js.inline.util.isCallInvocation import org.jetbrains.kotlin.js.inline.util.isCallInvocation
import org.jetbrains.kotlin.js.parser.parseFunction import org.jetbrains.kotlin.js.parser.parseFunction
import org.jetbrains.kotlin.js.translate.context.Namer import org.jetbrains.kotlin.js.translate.context.Namer
import org.jetbrains.kotlin.js.translate.reference.CallExpressionTranslator
import org.jetbrains.kotlin.js.translate.utils.JsDescriptorUtils.getModuleName import org.jetbrains.kotlin.js.translate.utils.JsDescriptorUtils.getModuleName
import org.jetbrains.kotlin.resolve.descriptorUtil.isExtension import org.jetbrains.kotlin.resolve.descriptorUtil.isExtension
import org.jetbrains.kotlin.resolve.inline.InlineStrategy import org.jetbrains.kotlin.resolve.inline.InlineStrategy
@@ -143,7 +142,7 @@ class FunctionReader(private val config: JsConfig, private val currentModuleName
private fun readFunctionFromSource(descriptor: CallableDescriptor, info: ModuleInfo): JsFunction? { private fun readFunctionFromSource(descriptor: CallableDescriptor, info: ModuleInfo): JsFunction? {
val source = info.fileContent val source = info.fileContent
val tag = Namer.getFunctionTag(descriptor) val tag = Namer.getFunctionTag(descriptor, config)
val index = source.indexOf(tag) val index = source.indexOf(tag)
if (index < 0) return null if (index < 0) return null
@@ -174,8 +173,6 @@ private fun JsFunction.markInlineArguments(descriptor: CallableDescriptor) {
val offset = if (descriptor.isExtension) 1 else 0 val offset = if (descriptor.isExtension) 1 else 0
for ((i, param) in params.withIndex()) { for ((i, param) in params.withIndex()) {
if (!CallExpressionTranslator.shouldBeInlined(descriptor)) continue
val type = param.type val type = param.type
if (!type.isFunctionTypeOrSubtype) continue if (!type.isFunctionTypeOrSubtype) continue
@@ -47,6 +47,7 @@ import static org.jetbrains.kotlin.js.translate.utils.JsAstUtils.flattenStatemen
public class JsInliner extends JsVisitorWithContextImpl { public class JsInliner extends JsVisitorWithContextImpl {
private final JsConfig config;
private final Map<JsName, JsFunction> functions; private final Map<JsName, JsFunction> functions;
private final Map<String, JsFunction> accessors; private final Map<String, JsFunction> accessors;
private final Stack<JsInliningContext> inliningContexts = new Stack<>(); private final Stack<JsInliningContext> inliningContexts = new Stack<>();
@@ -76,7 +77,7 @@ public class JsInliner extends JsVisitorWithContextImpl {
accessorInvocationTransformer.accept(fragment.getInitializerBlock()); accessorInvocationTransformer.accept(fragment.getInitializerBlock());
} }
FunctionReader functionReader = new FunctionReader(config, currentModuleName, fragments); FunctionReader functionReader = new FunctionReader(config, currentModuleName, fragments);
JsInliner inliner = new JsInliner(functions, accessors, functionReader, trace); JsInliner inliner = new JsInliner(config, functions, accessors, functionReader, trace);
for (JsProgramFragment fragment : fragmentsToProcess) { for (JsProgramFragment fragment : fragmentsToProcess) {
inliner.inliningContexts.push(inliner.new JsInliningContext()); inliner.inliningContexts.push(inliner.new JsInliningContext());
inliner.accept(fragment.getDeclarationBlock()); inliner.accept(fragment.getDeclarationBlock());
@@ -92,11 +93,13 @@ public class JsInliner extends JsVisitorWithContextImpl {
} }
private JsInliner( private JsInliner(
@NotNull JsConfig config,
@NotNull Map<JsName, JsFunction> functions, @NotNull Map<JsName, JsFunction> functions,
@NotNull Map<String, JsFunction> accessors, @NotNull Map<String, JsFunction> accessors,
@NotNull FunctionReader functionReader, @NotNull FunctionReader functionReader,
@NotNull DiagnosticSink trace @NotNull DiagnosticSink trace
) { ) {
this.config = config;
this.functions = functions; this.functions = functions;
this.accessors = accessors; this.accessors = accessors;
this.functionReader = functionReader; this.functionReader = functionReader;
@@ -285,7 +288,7 @@ public class JsInliner extends JsVisitorWithContextImpl {
private final FunctionContext functionContext; private final FunctionContext functionContext;
JsInliningContext() { JsInliningContext() {
functionContext = new FunctionContext(functionReader) { functionContext = new FunctionContext(functionReader, config) {
@Nullable @Nullable
@Override @Override
protected JsFunction lookUpStaticFunction(@Nullable JsName functionName) { protected JsFunction lookUpStaticFunction(@Nullable JsName functionName) {
@@ -19,11 +19,12 @@ package org.jetbrains.kotlin.js.inline.context
import org.jetbrains.kotlin.js.backend.ast.* import org.jetbrains.kotlin.js.backend.ast.*
import org.jetbrains.kotlin.js.backend.ast.metadata.isCallableReference import org.jetbrains.kotlin.js.backend.ast.metadata.isCallableReference
import org.jetbrains.kotlin.js.backend.ast.metadata.descriptor import org.jetbrains.kotlin.js.backend.ast.metadata.descriptor
import org.jetbrains.kotlin.js.config.JsConfig
import org.jetbrains.kotlin.js.inline.FunctionReader import org.jetbrains.kotlin.js.inline.FunctionReader
import org.jetbrains.kotlin.js.inline.util.* import org.jetbrains.kotlin.js.inline.util.*
import org.jetbrains.kotlin.js.translate.context.Namer import org.jetbrains.kotlin.js.translate.context.Namer
abstract class FunctionContext(private val functionReader: FunctionReader) { abstract class FunctionContext(private val functionReader: FunctionReader, private val config: JsConfig) {
protected abstract fun lookUpStaticFunction(functionName: JsName?): JsFunction? protected abstract fun lookUpStaticFunction(functionName: JsName?): JsFunction?
protected abstract fun lookUpStaticFunctionByTag(functionTag: String): JsFunction? protected abstract fun lookUpStaticFunctionByTag(functionTag: String): JsFunction?
@@ -70,7 +71,7 @@ abstract class FunctionContext(private val functionReader: FunctionReader) {
val descriptor = call.descriptor val descriptor = call.descriptor
if (descriptor != null) { if (descriptor != null) {
if (descriptor in functionReader) return functionReader[descriptor] if (descriptor in functionReader) return functionReader[descriptor]
lookUpStaticFunctionByTag(Namer.getFunctionTag(descriptor))?.let { return it } lookUpStaticFunctionByTag(Namer.getFunctionTag(descriptor, config))?.let { return it }
} }
/** remove ending `()` */ /** remove ending `()` */
+13 -3
View File
@@ -16,16 +16,17 @@
// a package is omitted to get declarations directly under the module // a package is omitted to get declarations directly under the module
external private fun <T> Array(size: Int): Array<T> @PublishedApi
external internal fun <T> Array(size: Int): Array<T>
@JsName("newArray") @JsName("newArray")
fun <T> newArray(size: Int, initValue: T) = fillArrayVal(Array<T>(size), initValue) fun <T> newArray(size: Int, initValue: T) = fillArrayVal(Array<T>(size), initValue)
@JsName("newArrayF") @JsName("newArrayF")
fun <T> arrayWithFun(size: Int, init: (Int) -> T) = fillArrayFun(Array<T>(size), init) inline fun <T> arrayWithFun(size: Int, init: (Int) -> T) = fillArrayFun(Array<T>(size), init)
@JsName("fillArray") @JsName("fillArray")
fun <T> fillArrayFun(array: Array<T>, init: (Int) -> T): Array<T> { inline fun <T> fillArrayFun(array: Array<T>, init: (Int) -> T): Array<T> {
for (i in 0..array.size - 1) { for (i in 0..array.size - 1) {
array[i] = init(i) array[i] = init(i)
} }
@@ -43,6 +44,9 @@ fun booleanArray(size: Int, init: dynamic): Array<Boolean> {
} }
} }
@JsName("booleanArrayF")
inline fun booleanArrayWithFun(size: Int, init: (Int) -> Boolean): Array<Boolean> = fillArrayFun(booleanArray(size, false), init)
@JsName("charArray") @JsName("charArray")
@Suppress("UNUSED_PARAMETER") @Suppress("UNUSED_PARAMETER")
fun charArray(size: Int, init: dynamic): Array<Char> { fun charArray(size: Int, init: dynamic): Array<Char> {
@@ -54,6 +58,9 @@ fun charArray(size: Int, init: dynamic): Array<Char> {
} }
} }
@JsName("charArrayF")
inline fun charArrayWithFun(size: Int, init: (Int) -> Char): Array<Char> = fillArrayFun(charArray(size, null), init)
@JsName("longArray") @JsName("longArray")
fun longArray(size: Int, init: dynamic): Array<Long> { fun longArray(size: Int, init: dynamic): Array<Long> {
val result: dynamic = Array<Long>(size) val result: dynamic = Array<Long>(size)
@@ -65,6 +72,9 @@ fun longArray(size: Int, init: dynamic): Array<Long> {
} }
} }
@JsName("longArrayF")
inline fun longArrayWithFun(size: Int, init: (Int) -> Long): Array<Long> = fillArrayFun(longArray(size, false), init)
private fun <T> fillArrayVal(array: Array<T>, initValue: T): Array<T> { private fun <T> fillArrayVal(array: Array<T>, initValue: T): Array<T> {
for (i in 0..array.size - 1) { for (i in 0..array.size - 1) {
array[i] = initValue array[i] = initValue
@@ -788,13 +788,7 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest {
@TestMetadata("nonLocalReturnArrayConstructor.kt") @TestMetadata("nonLocalReturnArrayConstructor.kt")
public void testNonLocalReturnArrayConstructor() throws Exception { public void testNonLocalReturnArrayConstructor() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/arrays/nonLocalReturnArrayConstructor.kt"); String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/arrays/nonLocalReturnArrayConstructor.kt");
try { doTest(fileName);
doTest(fileName);
}
catch (Throwable ignore) {
return;
}
throw new AssertionError("Looks like this test can be unmuted. Remove IGNORE_BACKEND directive for that.");
} }
@TestMetadata("nonNullArray.kt") @TestMetadata("nonNullArray.kt")
@@ -417,13 +417,7 @@ public class JsTypedArraysBoxTestGenerated extends AbstractJsTypedArraysBoxTest
@TestMetadata("nonLocalReturnArrayConstructor.kt") @TestMetadata("nonLocalReturnArrayConstructor.kt")
public void testNonLocalReturnArrayConstructor() throws Exception { public void testNonLocalReturnArrayConstructor() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/arrays/nonLocalReturnArrayConstructor.kt"); String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/arrays/nonLocalReturnArrayConstructor.kt");
try { doTest(fileName);
doTest(fileName);
}
catch (Throwable ignore) {
return;
}
throw new AssertionError("Looks like this test can be unmuted. Remove IGNORE_BACKEND directive for that.");
} }
@TestMetadata("nonNullArray.kt") @TestMetadata("nonNullArray.kt")
@@ -30,9 +30,11 @@ import org.jetbrains.kotlin.js.backend.ast.*;
import org.jetbrains.kotlin.js.backend.ast.metadata.MetadataProperties; 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.SideEffectKind;
import org.jetbrains.kotlin.js.backend.ast.metadata.TypeCheck; import org.jetbrains.kotlin.js.backend.ast.metadata.TypeCheck;
import org.jetbrains.kotlin.js.config.JsConfig;
import org.jetbrains.kotlin.js.naming.NameSuggestion; import org.jetbrains.kotlin.js.naming.NameSuggestion;
import org.jetbrains.kotlin.js.naming.SuggestedName; import org.jetbrains.kotlin.js.naming.SuggestedName;
import org.jetbrains.kotlin.js.resolve.JsPlatform; import org.jetbrains.kotlin.js.resolve.JsPlatform;
import org.jetbrains.kotlin.js.translate.intrinsic.functions.factories.ArrayFIF;
import org.jetbrains.kotlin.js.translate.utils.JsAstUtils; import org.jetbrains.kotlin.js.translate.utils.JsAstUtils;
import org.jetbrains.kotlin.js.translate.utils.JsDescriptorUtils; import org.jetbrains.kotlin.js.translate.utils.JsDescriptorUtils;
import org.jetbrains.kotlin.name.FqName; import org.jetbrains.kotlin.name.FqName;
@@ -114,7 +116,10 @@ public final class Namer {
public static final String ENUM_ORDINAL_FIELD = "ordinal$"; public static final String ENUM_ORDINAL_FIELD = "ordinal$";
@NotNull @NotNull
public static String getFunctionTag(@NotNull CallableDescriptor functionDescriptor) { public static String getFunctionTag(@NotNull CallableDescriptor functionDescriptor, @NotNull JsConfig config) {
String intrinsicTag = ArrayFIF.INSTANCE.getTag(functionDescriptor, config);
if (intrinsicTag != null) return intrinsicTag;
functionDescriptor = (CallableDescriptor) JsDescriptorUtils.findRealInlineDeclaration(functionDescriptor); functionDescriptor = (CallableDescriptor) JsDescriptorUtils.findRealInlineDeclaration(functionDescriptor);
String moduleName = getModuleName(functionDescriptor); String moduleName = getModuleName(functionDescriptor);
FqNameUnsafe fqNameParent = DescriptorUtils.getFqName(functionDescriptor).parent(); FqNameUnsafe fqNameParent = DescriptorUtils.getFqName(functionDescriptor).parent();
@@ -782,7 +782,7 @@ public final class StaticContext {
} }
public void addInlineCall(@NotNull CallableDescriptor descriptor) { public void addInlineCall(@NotNull CallableDescriptor descriptor) {
String tag = Namer.getFunctionTag(descriptor); String tag = Namer.getFunctionTag(descriptor, config);
fragment.getInlineModuleMap().put(tag, getModuleExpressionFor(descriptor)); fragment.getInlineModuleMap().put(tag, getModuleExpressionFor(descriptor));
} }
} }
@@ -110,7 +110,7 @@ abstract class AbstractDeclarationVisitor : TranslatorVisitor<Unit>() {
function.body.statements += FunctionBodyTranslator.setDefaultValueForArguments(descriptor, innerContext) function.body.statements += FunctionBodyTranslator.setDefaultValueForArguments(descriptor, innerContext)
} }
innerContext.translateFunction(expression, function) innerContext.translateFunction(expression, function)
return innerContext.wrapWithInlineMetadata(function, descriptor) return innerContext.wrapWithInlineMetadata(function, descriptor, context.config)
} }
protected abstract fun addFunction( protected abstract fun addFunction(
@@ -26,6 +26,7 @@ import org.jetbrains.kotlin.js.backend.ast.JsScope
import org.jetbrains.kotlin.js.backend.ast.metadata.descriptor import org.jetbrains.kotlin.js.backend.ast.metadata.descriptor
import org.jetbrains.kotlin.js.backend.ast.metadata.functionDescriptor import org.jetbrains.kotlin.js.backend.ast.metadata.functionDescriptor
import org.jetbrains.kotlin.js.backend.ast.metadata.hasDefaultValue import org.jetbrains.kotlin.js.backend.ast.metadata.hasDefaultValue
import org.jetbrains.kotlin.js.config.JsConfig
import org.jetbrains.kotlin.js.translate.context.Namer import org.jetbrains.kotlin.js.translate.context.Namer
import org.jetbrains.kotlin.js.translate.context.TranslationContext import org.jetbrains.kotlin.js.translate.context.TranslationContext
import org.jetbrains.kotlin.js.translate.reference.CallExpressionTranslator.shouldBeInlined import org.jetbrains.kotlin.js.translate.reference.CallExpressionTranslator.shouldBeInlined
@@ -100,9 +101,9 @@ fun TranslationContext.translateFunction(declaration: KtDeclarationWithBody, fun
function.functionDescriptor = descriptor function.functionDescriptor = descriptor
} }
fun TranslationContext.wrapWithInlineMetadata(function: JsFunction, descriptor: FunctionDescriptor): JsExpression { fun TranslationContext.wrapWithInlineMetadata(function: JsFunction, descriptor: FunctionDescriptor, config: JsConfig): JsExpression {
return if (shouldBeInlined(descriptor, this) && descriptor.isEffectivelyPublicApi) { return if (shouldBeInlined(descriptor, this) && descriptor.isEffectivelyPublicApi) {
val metadata = InlineMetadata.compose(function, descriptor) val metadata = InlineMetadata.compose(function, descriptor, config)
metadata.functionWithMetadata metadata.functionWithMetadata
} }
else { else {
@@ -18,6 +18,7 @@ package org.jetbrains.kotlin.js.translate.expression
import org.jetbrains.kotlin.descriptors.CallableDescriptor import org.jetbrains.kotlin.descriptors.CallableDescriptor
import org.jetbrains.kotlin.js.backend.ast.* import org.jetbrains.kotlin.js.backend.ast.*
import org.jetbrains.kotlin.js.config.JsConfig
import org.jetbrains.kotlin.js.translate.context.Namer import org.jetbrains.kotlin.js.translate.context.Namer
private val METADATA_PROPERTIES_COUNT = 2 private val METADATA_PROPERTIES_COUNT = 2
@@ -25,9 +26,9 @@ private val METADATA_PROPERTIES_COUNT = 2
class InlineMetadata(val tag: JsStringLiteral, val function: JsFunction) { class InlineMetadata(val tag: JsStringLiteral, val function: JsFunction) {
companion object { companion object {
@JvmStatic @JvmStatic
fun compose(function: JsFunction, descriptor: CallableDescriptor): InlineMetadata { fun compose(function: JsFunction, descriptor: CallableDescriptor, config: JsConfig): InlineMetadata {
val program = function.scope.program val program = function.scope.program
val tag = program.getStringLiteral(Namer.getFunctionTag(descriptor)) val tag = program.getStringLiteral(Namer.getFunctionTag(descriptor, config))
return InlineMetadata(tag, function) return InlineMetadata(tag, function)
} }
@@ -211,7 +211,7 @@ public final class PatternTranslator extends AbstractTranslator {
} }
if (isArray(type)) { if (isArray(type)) {
if (ArrayFIF.typedArraysEnabled(context())) { if (ArrayFIF.typedArraysEnabled(context().getConfig())) {
return namer().isArray(); return namer().isArray();
} }
else { else {
@@ -255,7 +255,7 @@ public final class PatternTranslator extends AbstractTranslator {
return namer().isTypeOf(program().getStringLiteral("number")); return namer().isTypeOf(program().getStringLiteral("number"));
} }
if (ArrayFIF.typedArraysEnabled(context())) { if (ArrayFIF.typedArraysEnabled(context().getConfig())) {
if (KotlinBuiltIns.isPrimitiveArray(type)) { if (KotlinBuiltIns.isPrimitiveArray(type)) {
PrimitiveType arrayType = KotlinBuiltIns.getPrimitiveArrayElementType(type); PrimitiveType arrayType = KotlinBuiltIns.getPrimitiveArrayElementType(type);
assert arrayType != null; assert arrayType != null;
@@ -20,33 +20,40 @@ import com.intellij.openapi.util.text.StringUtil.decapitalize
import org.jetbrains.kotlin.builtins.KotlinBuiltIns import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.builtins.PrimitiveType import org.jetbrains.kotlin.builtins.PrimitiveType
import org.jetbrains.kotlin.builtins.PrimitiveType.* import org.jetbrains.kotlin.builtins.PrimitiveType.*
import org.jetbrains.kotlin.builtins.getValueParameterTypesFromFunctionType
import org.jetbrains.kotlin.builtins.isBuiltinFunctionalType
import org.jetbrains.kotlin.descriptors.CallableDescriptor
import org.jetbrains.kotlin.descriptors.ConstructorDescriptor
import org.jetbrains.kotlin.js.backend.ast.* import org.jetbrains.kotlin.js.backend.ast.*
import org.jetbrains.kotlin.js.backend.ast.metadata.SideEffectKind import org.jetbrains.kotlin.js.backend.ast.metadata.descriptor
import org.jetbrains.kotlin.js.backend.ast.metadata.sideEffects import org.jetbrains.kotlin.js.backend.ast.metadata.inlineStrategy
import org.jetbrains.kotlin.js.config.JSConfigurationKeys import org.jetbrains.kotlin.js.config.JSConfigurationKeys
import org.jetbrains.kotlin.js.config.JsConfig
import org.jetbrains.kotlin.js.patterns.NamePredicate import org.jetbrains.kotlin.js.patterns.NamePredicate
import org.jetbrains.kotlin.js.patterns.PatternBuilder.pattern import org.jetbrains.kotlin.js.patterns.PatternBuilder.pattern
import org.jetbrains.kotlin.js.translate.callTranslator.CallInfo
import org.jetbrains.kotlin.js.translate.context.Namer import org.jetbrains.kotlin.js.translate.context.Namer
import org.jetbrains.kotlin.js.translate.context.TranslationContext import org.jetbrains.kotlin.js.translate.context.TranslationContext
import org.jetbrains.kotlin.js.translate.intrinsic.functions.basic.BuiltInPropertyIntrinsic import org.jetbrains.kotlin.js.translate.intrinsic.functions.basic.BuiltInPropertyIntrinsic
import org.jetbrains.kotlin.js.translate.intrinsic.functions.basic.FunctionIntrinsicWithReceiverComputed import org.jetbrains.kotlin.js.translate.intrinsic.functions.basic.FunctionIntrinsic
import org.jetbrains.kotlin.js.translate.utils.JsAstUtils import org.jetbrains.kotlin.js.translate.utils.JsAstUtils
import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.resolve.inline.InlineStrategy
import java.util.* import java.util.*
object ArrayFIF : CompositeFIF() { object ArrayFIF : CompositeFIF() {
@JvmField @JvmField
val GET_INTRINSIC = intrinsify { receiver, arguments, _ -> val GET_INTRINSIC = intrinsify { callInfo, arguments, _ ->
assert(arguments.size == 1) { "Array get expression must have one argument." } assert(arguments.size == 1) { "Array get expression must have one argument." }
val (indexExpression) = arguments val (indexExpression) = arguments
JsArrayAccess(receiver!!, indexExpression) JsArrayAccess(callInfo.dispatchReceiver, indexExpression)
} }
@JvmField @JvmField
val SET_INTRINSIC = intrinsify { receiver, arguments, _ -> val SET_INTRINSIC = intrinsify { callInfo, arguments, _ ->
assert(arguments.size == 2) { "Array set expression must have two arguments." } assert(arguments.size == 2) { "Array set expression must have two arguments." }
val (indexExpression, value) = arguments val (indexExpression, value) = arguments
val arrayAccess = JsArrayAccess(receiver!!, indexExpression) val arrayAccess = JsArrayAccess(callInfo.dispatchReceiver, indexExpression)
JsAstUtils.assignment(arrayAccess, value) JsAstUtils.assignment(arrayAccess, value)
} }
@@ -54,10 +61,10 @@ object ArrayFIF : CompositeFIF() {
val LENGTH_PROPERTY_INTRINSIC = BuiltInPropertyIntrinsic("length") val LENGTH_PROPERTY_INTRINSIC = BuiltInPropertyIntrinsic("length")
@JvmStatic @JvmStatic
fun typedArraysEnabled(ctx: TranslationContext) = ctx.config.configuration.getBoolean(JSConfigurationKeys.TYPED_ARRAYS_ENABLED) fun typedArraysEnabled(config: JsConfig) = config.configuration.getBoolean(JSConfigurationKeys.TYPED_ARRAYS_ENABLED)
fun castOrCreatePrimitiveArray(ctx: TranslationContext, type: PrimitiveType?, arg: JsArrayLiteral): JsExpression { fun castOrCreatePrimitiveArray(ctx: TranslationContext, type: PrimitiveType?, arg: JsArrayLiteral): JsExpression {
if (type == null || !typedArraysEnabled(ctx)) return arg if (type == null || !typedArraysEnabled(ctx.config)) return arg
if (type in TYPED_ARRAY_MAP) { if (type in TYPED_ARRAY_MAP) {
return createTypedArray(type, arg) return createTypedArray(type, arg)
@@ -81,6 +88,30 @@ object ArrayFIF : CompositeFIF() {
private val PrimitiveType.lowerCaseName private val PrimitiveType.lowerCaseName
get() = typeName.asString().toLowerCase() get() = typeName.asString().toLowerCase()
fun getTag(descriptor: CallableDescriptor, config: JsConfig): String? {
if (descriptor !is ConstructorDescriptor) return null
val constructedClass = descriptor.constructedClass
if (!KotlinBuiltIns.isArrayOrPrimitiveArray(constructedClass)) return null
if (descriptor.valueParameters.size != 2) return null
val (sizeParam, functionParam) = descriptor.valueParameters
if (!KotlinBuiltIns.isInt(sizeParam.type) || !functionParam.type.isBuiltinFunctionalType) return null
if (functionParam.type.getValueParameterTypesFromFunctionType().size != 1) return null
val primitiveType = KotlinBuiltIns.getPrimitiveArrayElementType(constructedClass.defaultType)
return if (typedArraysEnabled(config) && primitiveType != null) {
if (primitiveType in TYPED_ARRAY_MAP) {
"kotlin.fillArray"
}
else {
"kotlin.${primitiveType.lowerCaseName}ArrayF"
}
}
else {
"kotlin.newArrayF"
}
}
init { init {
val arrayName = KotlinBuiltIns.FQ_NAMES.array.shortName() val arrayName = KotlinBuiltIns.FQ_NAMES.array.shortName()
@@ -97,7 +128,7 @@ object ArrayFIF : CompositeFIF() {
assert(arguments.size == 1) { "Array <init>(Int) expression must have one argument." } assert(arguments.size == 1) { "Array <init>(Int) expression must have one argument." }
val (size) = arguments val (size) = arguments
if (typedArraysEnabled(context)) { if (typedArraysEnabled(context.config)) {
if (type in TYPED_ARRAY_MAP) { if (type in TYPED_ARRAY_MAP) {
createTypedArray(type, size) createTypedArray(type, size)
} }
@@ -116,24 +147,11 @@ object ArrayFIF : CompositeFIF() {
} }
}) })
add(pattern(NamePredicate(type.arrayTypeName), "<init>(Int,Function1)"), intrinsify { _, arguments, context -> add(pattern(NamePredicate(type.arrayTypeName), "<init>(Int,Function1)"), createConstructorIntrinsic(type))
assert(arguments.size == 2) { "Array <init>(Int,Function1) expression must have two arguments." }
val (size, fn) = arguments
if (typedArraysEnabled(context)) {
if (type in TYPED_ARRAY_MAP) {
JsAstUtils.invokeKotlinFunction("fillArray", createTypedArray(type, size), fn)
}
else {
JsAstUtils.invokeKotlinFunction("${type.lowerCaseName}Array", size, fn)
}
}
else {
JsAstUtils.invokeKotlinFunction("newArrayF", size, fn)
}
})
add(pattern(NamePredicate(type.arrayTypeName), "iterator"), intrinsify { receiver, _, context -> add(pattern(NamePredicate(type.arrayTypeName), "iterator"), intrinsify { callInfo, _, context ->
if (typedArraysEnabled(context)) { val receiver = callInfo.dispatchReceiver
if (typedArraysEnabled(context.config)) {
JsAstUtils.invokeKotlinFunction("${type.lowerCaseName}ArrayIterator", receiver!!) JsAstUtils.invokeKotlinFunction("${type.lowerCaseName}ArrayIterator", receiver!!)
} }
else { else {
@@ -143,7 +161,7 @@ object ArrayFIF : CompositeFIF() {
}) })
} }
add(pattern(NamePredicate(arrayName), "<init>(Int,Function1)"), KotlinFunctionIntrinsic("newArrayF")) add(pattern(NamePredicate(arrayName), "<init>(Int,Function1)"), createConstructorIntrinsic(null))
add(pattern(NamePredicate(arrayName), "iterator"), KotlinFunctionIntrinsic("arrayIterator")) add(pattern(NamePredicate(arrayName), "iterator"), KotlinFunctionIntrinsic("arrayIterator"))
add(pattern(Namer.KOTLIN_LOWER_NAME, "arrayOfNulls"), KotlinFunctionIntrinsic("newArray", JsLiteral.NULL)) add(pattern(Namer.KOTLIN_LOWER_NAME, "arrayOfNulls"), KotlinFunctionIntrinsic("newArray", JsLiteral.NULL))
@@ -153,10 +171,33 @@ object ArrayFIF : CompositeFIF() {
add(arrayFactoryMethods, intrinsify { _, arguments, _ -> arguments[0] }) add(arrayFactoryMethods, intrinsify { _, arguments, _ -> arguments[0] })
} }
private fun intrinsify(f: (receiver: JsExpression?, arguments: List<JsExpression>, context: TranslationContext) -> JsExpression) private fun createConstructorIntrinsic(type: PrimitiveType?): FunctionIntrinsic {
= object : FunctionIntrinsicWithReceiverComputed() { return intrinsify { callInfo, arguments, context ->
override fun apply(receiver: JsExpression?, arguments: List<JsExpression>, context: TranslationContext): JsExpression { assert(arguments.size == 2) { "Array <init>(Int,Function1) expression must have two arguments." }
return f(receiver, arguments, context) val (size, fn) = arguments
val invocation = if (typedArraysEnabled(context.config) && type != null) {
if (type in TYPED_ARRAY_MAP) {
JsAstUtils.invokeKotlinFunction("fillArray", createTypedArray(type, size), fn)
}
else {
JsAstUtils.invokeKotlinFunction("${type.lowerCaseName}ArrayF", size, fn)
}
} }
else {
JsAstUtils.invokeKotlinFunction("newArrayF", size, fn)
}
invocation.inlineStrategy = InlineStrategy.IN_PLACE
val descriptor = callInfo.resolvedCall.resultingDescriptor.original
invocation.descriptor = descriptor
context.addInlineCall(descriptor)
invocation
} }
}
private fun intrinsify(f: (callInfo: CallInfo, arguments: List<JsExpression>, context: TranslationContext) -> JsExpression)
= object : FunctionIntrinsic() {
override fun apply(callInfo: CallInfo, arguments: List<JsExpression>, context: TranslationContext): JsExpression {
return f(callInfo, arguments, context)
}
}
} }
@@ -21,8 +21,10 @@ import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import org.jetbrains.kotlin.builtins.KotlinBuiltIns; import org.jetbrains.kotlin.builtins.KotlinBuiltIns;
import org.jetbrains.kotlin.descriptors.*; import org.jetbrains.kotlin.descriptors.*;
import org.jetbrains.kotlin.js.translate.context.Namer;
import org.jetbrains.kotlin.resolve.DescriptorUtils; import org.jetbrains.kotlin.resolve.DescriptorUtils;
import org.jetbrains.kotlin.resolve.calls.tasks.DynamicCallsKt; import org.jetbrains.kotlin.resolve.calls.tasks.DynamicCallsKt;
import org.jetbrains.kotlin.resolve.descriptorUtil.DescriptorUtilsKt;
import org.jetbrains.kotlin.resolve.scopes.receivers.ImplicitReceiver; import org.jetbrains.kotlin.resolve.scopes.receivers.ImplicitReceiver;
import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValue; import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValue;
import org.jetbrains.kotlin.types.KotlinType; import org.jetbrains.kotlin.types.KotlinType;
@@ -147,6 +149,9 @@ public final class JsDescriptorUtils {
@NotNull @NotNull
public static String getModuleName(@NotNull DeclarationDescriptor descriptor) { public static String getModuleName(@NotNull DeclarationDescriptor descriptor) {
ModuleDescriptor moduleDescriptor = DescriptorUtils.getContainingModule(findRealInlineDeclaration(descriptor)); ModuleDescriptor moduleDescriptor = DescriptorUtils.getContainingModule(findRealInlineDeclaration(descriptor));
if (DescriptorUtils.getContainingModule(descriptor) == moduleDescriptor.getBuiltIns().getBuiltInsModule()) {
return Namer.KOTLIN_LOWER_NAME;
}
String moduleName = moduleDescriptor.getName().asString(); String moduleName = moduleDescriptor.getName().asString();
return moduleName.substring(1, moduleName.length() - 1); return moduleName.substring(1, moduleName.length() - 1);
} }
@@ -179,7 +184,7 @@ public final class JsDescriptorUtils {
public static boolean isImmediateSubtypeOfError(@NotNull ClassDescriptor descriptor) { public static boolean isImmediateSubtypeOfError(@NotNull ClassDescriptor descriptor) {
if (!isExceptionClass(descriptor)) return false; if (!isExceptionClass(descriptor)) return false;
ClassDescriptor superClass = org.jetbrains.kotlin.resolve.descriptorUtil.DescriptorUtilsKt.getSuperClassOrAny(descriptor); ClassDescriptor superClass = DescriptorUtilsKt.getSuperClassOrAny(descriptor);
return TypeUtilsKt.isThrowable(superClass.getDefaultType()) || AnnotationsUtils.isNativeObject(superClass); return TypeUtilsKt.isThrowable(superClass.getDefaultType()) || AnnotationsUtils.isNativeObject(superClass);
} }
@@ -62,7 +62,7 @@ public final class TranslationUtils {
@NotNull TranslationContext context) { @NotNull TranslationContext context) {
JsExpression functionExpression = function; JsExpression functionExpression = function;
if (InlineUtil.isInline(descriptor)) { if (InlineUtil.isInline(descriptor)) {
InlineMetadata metadata = InlineMetadata.compose(function, descriptor); InlineMetadata metadata = InlineMetadata.compose(function, descriptor, context.getConfig());
functionExpression = metadata.getFunctionWithMetadata(); functionExpression = metadata.getFunctionWithMetadata();
} }