JS: generate aliases for intrinsics.
This makes generated code more friendly to UglifyJS. For example, uglified circlet loses 15 kb.
This commit is contained in:
@@ -38,7 +38,6 @@ import org.jetbrains.kotlin.js.resolve.JsPlatform;
|
|||||||
import org.jetbrains.kotlin.js.translate.intrinsic.functions.factories.ArrayFIF;
|
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.FqNameUnsafe;
|
import org.jetbrains.kotlin.name.FqNameUnsafe;
|
||||||
import org.jetbrains.kotlin.name.Name;
|
import org.jetbrains.kotlin.name.Name;
|
||||||
import org.jetbrains.kotlin.resolve.DescriptorUtils;
|
import org.jetbrains.kotlin.resolve.DescriptorUtils;
|
||||||
@@ -87,16 +86,16 @@ public final class Namer {
|
|||||||
private static final String RECEIVER_PARAMETER_NAME = "$receiver";
|
private static final String RECEIVER_PARAMETER_NAME = "$receiver";
|
||||||
public static final String ANOTHER_THIS_PARAMETER_NAME = "$this";
|
public static final String ANOTHER_THIS_PARAMETER_NAME = "$this";
|
||||||
|
|
||||||
private static final String THROW_NPE_FUN_NAME = "throwNPE";
|
public static final String THROW_NPE_FUN_NAME = "throwNPE";
|
||||||
private static final String THROW_CLASS_CAST_EXCEPTION_FUN_NAME = "throwCCE";
|
public static final String THROW_CLASS_CAST_EXCEPTION_FUN_NAME = "throwCCE";
|
||||||
private static final String THROW_ILLEGAL_STATE_EXCEPTION_FUN_NAME = "throwISE";
|
public static final String THROW_ILLEGAL_STATE_EXCEPTION_FUN_NAME = "throwISE";
|
||||||
private static final String THROW_UNINITIALIZED_PROPERTY_ACCESS_EXCEPTION = "throwUPAE";
|
public static final String THROW_UNINITIALIZED_PROPERTY_ACCESS_EXCEPTION = "throwUPAE";
|
||||||
private static final String PROTOTYPE_NAME = "prototype";
|
private static final String PROTOTYPE_NAME = "prototype";
|
||||||
private static final String CAPTURED_VAR_FIELD = "v";
|
private static final String CAPTURED_VAR_FIELD = "v";
|
||||||
|
|
||||||
public static final JsNameRef IS_ARRAY_FUN_REF = new JsNameRef("isArray", "Array");
|
public static final JsNameRef IS_ARRAY_FUN_REF = new JsNameRef("isArray", "Array");
|
||||||
public static final String DEFINE_INLINE_FUNCTION = "defineInlineFunction";
|
public static final String DEFINE_INLINE_FUNCTION = "defineInlineFunction";
|
||||||
public static final String WRAP_FUNCTION = "wrapFunction";
|
private static final String WRAP_FUNCTION = "wrapFunction";
|
||||||
public static final String DEFAULT_PARAMETER_IMPLEMENTOR_SUFFIX = "$default";
|
public static final String DEFAULT_PARAMETER_IMPLEMENTOR_SUFFIX = "$default";
|
||||||
|
|
||||||
private static final JsNameRef JS_OBJECT = new JsNameRef("Object");
|
private static final JsNameRef JS_OBJECT = new JsNameRef("Object");
|
||||||
@@ -221,26 +220,6 @@ public final class Namer {
|
|||||||
return suggested.getNames().get(0);
|
return suggested.getNames().get(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
|
||||||
public static JsExpression throwNPEFunctionRef() {
|
|
||||||
return new JsNameRef(THROW_NPE_FUN_NAME, kotlinObject());
|
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
public static JsExpression throwClassCastExceptionFunRef() {
|
|
||||||
return new JsNameRef(THROW_CLASS_CAST_EXCEPTION_FUN_NAME, kotlinObject());
|
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
public static JsExpression throwIllegalStateExceptionFunRef() {
|
|
||||||
return new JsNameRef(THROW_ILLEGAL_STATE_EXCEPTION_FUN_NAME, kotlinObject());
|
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
public static JsExpression throwUninitializedPropertyAccessExceptionFunRef() {
|
|
||||||
return new JsNameRef(THROW_UNINITIALIZED_PROPERTY_ACCESS_EXCEPTION, kotlinObject());
|
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
public static JsNameRef kotlin(@NotNull JsName name) {
|
public static JsNameRef kotlin(@NotNull JsName name) {
|
||||||
return pureFqn(name, kotlinObject());
|
return pureFqn(name, kotlinObject());
|
||||||
@@ -326,11 +305,6 @@ public final class Namer {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
|
||||||
static String generatePackageName(@NotNull FqName packageFqName) {
|
|
||||||
return packageFqName.isRoot() ? getRootPackageName() : packageFqName.shortName().asString();
|
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
public static JsExpression getUndefinedExpression() {
|
public static JsExpression getUndefinedExpression() {
|
||||||
return new JsPrefixOperation(JsUnaryOperator.VOID, new JsIntLiteral(0));
|
return new JsPrefixOperation(JsUnaryOperator.VOID, new JsIntLiteral(0));
|
||||||
|
|||||||
@@ -18,9 +18,7 @@ package org.jetbrains.kotlin.js.translate.context;
|
|||||||
|
|
||||||
import com.google.common.collect.Lists;
|
import com.google.common.collect.Lists;
|
||||||
import com.google.common.collect.Maps;
|
import com.google.common.collect.Maps;
|
||||||
import com.intellij.openapi.util.Factory;
|
|
||||||
import com.intellij.openapi.util.text.StringUtil;
|
import com.intellij.openapi.util.text.StringUtil;
|
||||||
import com.intellij.util.containers.ContainerUtil;
|
|
||||||
import com.intellij.util.containers.hash.LinkedHashMap;
|
import com.intellij.util.containers.hash.LinkedHashMap;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
@@ -85,8 +83,6 @@ public final class StaticContext {
|
|||||||
@NotNull
|
@NotNull
|
||||||
private final Generator<JsName> innerNames = new InnerNameGenerator();
|
private final Generator<JsName> innerNames = new InnerNameGenerator();
|
||||||
@NotNull
|
@NotNull
|
||||||
private final Map<FqName, JsName> packageNames = Maps.newHashMap();
|
|
||||||
@NotNull
|
|
||||||
private final Generator<JsScope> scopes = new ScopeGenerator();
|
private final Generator<JsScope> scopes = new ScopeGenerator();
|
||||||
@NotNull
|
@NotNull
|
||||||
private final Generator<JsName> objectInstanceNames = new ObjectInstanceNameGenerator();
|
private final Generator<JsName> objectInstanceNames = new ObjectInstanceNameGenerator();
|
||||||
@@ -123,9 +119,6 @@ public final class StaticContext {
|
|||||||
@NotNull
|
@NotNull
|
||||||
private final Map<JsImportedModuleKey, JsImportedModule> importedModules = new LinkedHashMap<>();
|
private final Map<JsImportedModuleKey, JsImportedModule> importedModules = new LinkedHashMap<>();
|
||||||
|
|
||||||
@NotNull
|
|
||||||
private final JsScope rootPackageScope;
|
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
private final DeclarationExporter exporter = new DeclarationExporter(this);
|
private final DeclarationExporter exporter = new DeclarationExporter(this);
|
||||||
|
|
||||||
@@ -142,6 +135,8 @@ public final class StaticContext {
|
|||||||
|
|
||||||
private final Map<SpecialFunction, JsName> specialFunctions = new EnumMap<>(SpecialFunction.class);
|
private final Map<SpecialFunction, JsName> specialFunctions = new EnumMap<>(SpecialFunction.class);
|
||||||
|
|
||||||
|
private final Map<String, JsName> intrinsicNames = new HashMap<>();
|
||||||
|
|
||||||
public StaticContext(
|
public StaticContext(
|
||||||
@NotNull BindingTrace bindingTrace,
|
@NotNull BindingTrace bindingTrace,
|
||||||
@NotNull JsConfig config,
|
@NotNull JsConfig config,
|
||||||
@@ -153,11 +148,10 @@ public final class StaticContext {
|
|||||||
|
|
||||||
this.bindingTrace = bindingTrace;
|
this.bindingTrace = bindingTrace;
|
||||||
this.namer = Namer.newInstance(program.getRootScope());
|
this.namer = Namer.newInstance(program.getRootScope());
|
||||||
this.intrinsics = new Intrinsics(this);
|
this.intrinsics = new Intrinsics();
|
||||||
this.rootScope = fragment.getScope();
|
this.rootScope = fragment.getScope();
|
||||||
this.config = config;
|
this.config = config;
|
||||||
this.currentModule = moduleDescriptor;
|
this.currentModule = moduleDescriptor;
|
||||||
rootPackageScope = new JsObjectScope(rootScope, "<root package>");
|
|
||||||
|
|
||||||
JsName kotlinName = rootScope.declareName(Namer.KOTLIN_NAME);
|
JsName kotlinName = rootScope.declareName(Namer.KOTLIN_NAME);
|
||||||
createImportedModule(new JsImportedModuleKey(Namer.KOTLIN_LOWER_NAME, null), Namer.KOTLIN_LOWER_NAME, kotlinName, null);
|
createImportedModule(new JsImportedModuleKey(Namer.KOTLIN_LOWER_NAME, null), Namer.KOTLIN_LOWER_NAME, kotlinName, null);
|
||||||
@@ -329,12 +323,6 @@ public final class StaticContext {
|
|||||||
return expression;
|
return expression;
|
||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
|
||||||
public JsNameRef getQualifiedReference(@NotNull FqName packageFqName) {
|
|
||||||
JsName packageName = getNameForPackage(packageFqName);
|
|
||||||
return pureFqn(packageName, packageFqName.isRoot() ? null : getQualifierForParentPackage(packageFqName.parent()));
|
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
public JsName getNameForDescriptor(@NotNull DeclarationDescriptor descriptor) {
|
public JsName getNameForDescriptor(@NotNull DeclarationDescriptor descriptor) {
|
||||||
if (descriptor instanceof ClassDescriptor && KotlinBuiltIns.isAny((ClassDescriptor) descriptor)) {
|
if (descriptor instanceof ClassDescriptor && KotlinBuiltIns.isAny((ClassDescriptor) descriptor)) {
|
||||||
@@ -438,47 +426,13 @@ public final class StaticContext {
|
|||||||
return names;
|
return names;
|
||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
|
||||||
private JsName getNameForPackage(@NotNull FqName packageFqName) {
|
|
||||||
return ContainerUtil.getOrCreate(packageNames, packageFqName, (Factory<JsName>) () -> {
|
|
||||||
String name = Namer.generatePackageName(packageFqName);
|
|
||||||
return rootPackageScope.declareName(name);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
private JsNameRef getQualifierForParentPackage(@NotNull FqName packageFqName) {
|
|
||||||
JsNameRef result = null;
|
|
||||||
JsNameRef qualifier = null;
|
|
||||||
|
|
||||||
FqName fqName = packageFqName;
|
|
||||||
|
|
||||||
while (true) {
|
|
||||||
JsNameRef ref = pureFqn(getNameForPackage(fqName), null);
|
|
||||||
|
|
||||||
if (qualifier == null) {
|
|
||||||
result = ref;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
qualifier.setQualifier(ref);
|
|
||||||
}
|
|
||||||
|
|
||||||
qualifier = ref;
|
|
||||||
|
|
||||||
if (fqName.isRoot()) break;
|
|
||||||
fqName = fqName.parent();
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
public JsConfig getConfig() {
|
public JsConfig getConfig() {
|
||||||
return config;
|
return config;
|
||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
public JsName importDeclaration(@NotNull String suggestedName, @NotNull String tag, @NotNull JsExpression declaration) {
|
private JsName importDeclaration(@NotNull String suggestedName, @NotNull String tag, @NotNull JsExpression declaration) {
|
||||||
JsName result = importDeclarationImpl(suggestedName, tag, declaration);
|
JsName result = importDeclarationImpl(suggestedName, tag, declaration);
|
||||||
fragment.getNameBindings().add(new JsNameBinding(tag, result));
|
fragment.getNameBindings().add(new JsNameBinding(tag, result));
|
||||||
return result;
|
return result;
|
||||||
@@ -871,4 +825,13 @@ public final class StaticContext {
|
|||||||
return name;
|
return name;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
public JsExpression getReferenceToIntrinsic(@NotNull String name) {
|
||||||
|
JsName resultName = intrinsicNames.computeIfAbsent(name, k ->
|
||||||
|
importDeclaration(NameSuggestion.sanitizeName(name), "intrinsic:" + name, TranslationUtils.getIntrinsicFqn(name)));
|
||||||
|
|
||||||
|
return pureFqn(resultName, null);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+22
@@ -28,6 +28,7 @@ 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.SpecialFunction;
|
import org.jetbrains.kotlin.js.backend.ast.metadata.SpecialFunction;
|
||||||
import org.jetbrains.kotlin.js.config.JsConfig;
|
import org.jetbrains.kotlin.js.config.JsConfig;
|
||||||
|
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.translate.declaration.ClassModelGenerator;
|
import org.jetbrains.kotlin.js.translate.declaration.ClassModelGenerator;
|
||||||
import org.jetbrains.kotlin.js.translate.intrinsic.Intrinsics;
|
import org.jetbrains.kotlin.js.translate.intrinsic.Intrinsics;
|
||||||
@@ -317,6 +318,27 @@ public class TranslationContext {
|
|||||||
return name;
|
return name;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
public JsExpression getReferenceToIntrinsic(@NotNull String intrinsicName) {
|
||||||
|
JsExpression result;
|
||||||
|
if (inlineFunctionContext == null || !isPublicInlineFunction()) {
|
||||||
|
result = staticContext.getReferenceToIntrinsic(intrinsicName);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
String tag = "intrinsic:" + intrinsicName;
|
||||||
|
result = pureFqn(inlineFunctionContext.getImports().computeIfAbsent(tag, t -> {
|
||||||
|
JsExpression imported = TranslationUtils.getIntrinsicFqn(intrinsicName);
|
||||||
|
|
||||||
|
JsName name = JsScope.declareTemporaryName(NameSuggestion.sanitizeName(intrinsicName));
|
||||||
|
MetadataProperties.setImported(name, true);
|
||||||
|
inlineFunctionContext.getImportBlock().getStatements().add(JsAstUtils.newVar(name, imported));
|
||||||
|
return name;
|
||||||
|
}), null);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
public JsName getNameForObjectInstance(@NotNull ClassDescriptor descriptor) {
|
public JsName getNameForObjectInstance(@NotNull ClassDescriptor descriptor) {
|
||||||
return staticContext.getNameForObjectInstance(descriptor);
|
return staticContext.getNameForObjectInstance(descriptor);
|
||||||
|
|||||||
+7
-6
@@ -208,13 +208,14 @@ class ClassTranslator private constructor(
|
|||||||
private fun createMetadataRef() = JsNameRef(Namer.METADATA, context().getInnerReference(descriptor))
|
private fun createMetadataRef() = JsNameRef(Namer.METADATA, context().getInnerReference(descriptor))
|
||||||
|
|
||||||
private fun addMetadataType() {
|
private fun addMetadataType() {
|
||||||
val kotlinType = JsNameRef(Namer.CLASS_KIND_ENUM, Namer.KOTLIN_NAME)
|
val kindBuilder = StringBuilder(Namer.CLASS_KIND_ENUM + ".")
|
||||||
val typeRef = when {
|
kindBuilder.append(when {
|
||||||
DescriptorUtils.isInterface(descriptor) -> JsNameRef(Namer.CLASS_KIND_INTERFACE, kotlinType)
|
DescriptorUtils.isInterface(descriptor) -> Namer.CLASS_KIND_INTERFACE
|
||||||
DescriptorUtils.isObject(descriptor) -> JsNameRef(Namer.CLASS_KIND_OBJECT, kotlinType)
|
DescriptorUtils.isObject(descriptor) -> Namer.CLASS_KIND_OBJECT
|
||||||
else -> JsNameRef(Namer.CLASS_KIND_CLASS, kotlinType)
|
else -> Namer.CLASS_KIND_CLASS
|
||||||
}
|
})
|
||||||
|
|
||||||
|
val typeRef = context().getReferenceToIntrinsic(kindBuilder.toString())
|
||||||
metadataLiteral.propertyInitializers += JsPropertyInitializer(JsNameRef(Namer.METADATA_CLASS_KIND), typeRef)
|
metadataLiteral.propertyInitializers += JsPropertyInitializer(JsNameRef(Namer.METADATA_CLASS_KIND), typeRef)
|
||||||
|
|
||||||
val simpleName = descriptor.name
|
val simpleName = descriptor.name
|
||||||
|
|||||||
+2
-1
@@ -65,7 +65,8 @@ class EnumTranslator(
|
|||||||
val message = JsBinaryOperation(JsBinaryOperator.ADD,
|
val message = JsBinaryOperation(JsBinaryOperator.ADD,
|
||||||
JsStringLiteral("No enum constant ${descriptor.fqNameSafe}."),
|
JsStringLiteral("No enum constant ${descriptor.fqNameSafe}."),
|
||||||
nameParam.makeRef())
|
nameParam.makeRef())
|
||||||
val throwStatement = JsExpressionStatement(JsInvocation(Namer.throwIllegalStateExceptionFunRef(), message).source(psi))
|
val throwFunction = context().getReferenceToIntrinsic(Namer.THROW_ILLEGAL_STATE_EXCEPTION_FUN_NAME)
|
||||||
|
val throwStatement = JsExpressionStatement(JsInvocation(throwFunction, message).source(psi))
|
||||||
|
|
||||||
if (clauses.isNotEmpty()) {
|
if (clauses.isNotEmpty()) {
|
||||||
val defaultCase = JsDefault().apply {
|
val defaultCase = JsDefault().apply {
|
||||||
|
|||||||
+3
-3
@@ -29,7 +29,6 @@ import org.jetbrains.kotlin.js.translate.expression.translateFunction
|
|||||||
import org.jetbrains.kotlin.js.translate.general.AbstractTranslator
|
import org.jetbrains.kotlin.js.translate.general.AbstractTranslator
|
||||||
import org.jetbrains.kotlin.js.translate.general.Translation
|
import org.jetbrains.kotlin.js.translate.general.Translation
|
||||||
import org.jetbrains.kotlin.js.translate.utils.BindingUtils
|
import org.jetbrains.kotlin.js.translate.utils.BindingUtils
|
||||||
import org.jetbrains.kotlin.js.translate.utils.JsAstUtils.pureFqn
|
|
||||||
import org.jetbrains.kotlin.js.translate.utils.JsDescriptorUtils
|
import org.jetbrains.kotlin.js.translate.utils.JsDescriptorUtils
|
||||||
import org.jetbrains.kotlin.js.translate.utils.TranslationUtils
|
import org.jetbrains.kotlin.js.translate.utils.TranslationUtils
|
||||||
import org.jetbrains.kotlin.js.translate.utils.TranslationUtils.*
|
import org.jetbrains.kotlin.js.translate.utils.TranslationUtils.*
|
||||||
@@ -95,8 +94,9 @@ class DefaultPropertyTranslator(
|
|||||||
assert(descriptor is PropertyDescriptor) { "Property descriptor expected: $descriptor" }
|
assert(descriptor is PropertyDescriptor) { "Property descriptor expected: $descriptor" }
|
||||||
val result = backingFieldReference(context(), descriptor as PropertyDescriptor)
|
val result = backingFieldReference(context(), descriptor as PropertyDescriptor)
|
||||||
if (getterDescriptor is PropertyAccessorDescriptor && getterDescriptor.correspondingProperty.isLateInit) {
|
if (getterDescriptor is PropertyAccessorDescriptor && getterDescriptor.correspondingProperty.isLateInit) {
|
||||||
|
val throwFunction = context().getReferenceToIntrinsic(Namer.THROW_UNINITIALIZED_PROPERTY_ACCESS_EXCEPTION)
|
||||||
function.body.statements += JsIf(JsBinaryOperation(JsBinaryOperator.EQ, result, JsNullLiteral()),
|
function.body.statements += JsIf(JsBinaryOperation(JsBinaryOperator.EQ, result, JsNullLiteral()),
|
||||||
JsReturn(JsInvocation(Namer.throwUninitializedPropertyAccessExceptionFunRef(),
|
JsReturn(JsInvocation(throwFunction,
|
||||||
JsStringLiteral(getterDescriptor.correspondingProperty.name.asString()))))
|
JsStringLiteral(getterDescriptor.correspondingProperty.name.asString()))))
|
||||||
}
|
}
|
||||||
function.body.statements += JsReturn(result).apply { source = descriptor.source.getPsi() }
|
function.body.statements += JsReturn(result).apply { source = descriptor.source.getPsi() }
|
||||||
@@ -195,7 +195,7 @@ fun TranslationContext.contextWithPropertyMetadataCreationIntrinsified(
|
|||||||
(delegatedCall.valueArgumentsByIndex!![1] as ExpressionValueArgument).valueArgument!!.getArgumentExpression()
|
(delegatedCall.valueArgumentsByIndex!![1] as ExpressionValueArgument).valueArgument!!.getArgumentExpression()
|
||||||
return innerContextWithAliasesForExpressions(mapOf(
|
return innerContextWithAliasesForExpressions(mapOf(
|
||||||
hostExpression to host,
|
hostExpression to host,
|
||||||
fakeArgumentExpression to JsNew(pureFqn("PropertyMetadata", Namer.kotlinObject()), listOf(propertyNameLiteral))
|
fakeArgumentExpression to JsNew(getReferenceToIntrinsic("PropertyMetadata"), listOf(propertyNameLiteral))
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -263,7 +263,7 @@ public final class ExpressionVisitor extends TranslatorVisitor<JsNode> {
|
|||||||
return new JsInvocation(context.namer().kotlin(GET_KCLASS_FROM_EXPRESSION), receiver);
|
return new JsInvocation(context.namer().kotlin(GET_KCLASS_FROM_EXPRESSION), receiver);
|
||||||
}
|
}
|
||||||
|
|
||||||
return new JsInvocation(context.namer().kotlin(GET_KCLASS), UtilsKt.getReferenceToJsClass(lhs.getType(), context));
|
return new JsInvocation(context.getReferenceToIntrinsic(GET_KCLASS), UtilsKt.getReferenceToJsClass(lhs.getType(), context));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -94,7 +94,7 @@ public final class PatternTranslator extends AbstractTranslator {
|
|||||||
onFail = new JsNullLiteral();
|
onFail = new JsNullLiteral();
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
JsExpression throwCCEFunRef = Namer.throwClassCastExceptionFunRef();
|
JsExpression throwCCEFunRef = context().getReferenceToIntrinsic(Namer.THROW_CLASS_CAST_EXCEPTION_FUN_NAME);
|
||||||
onFail = new JsInvocation(throwCCEFunRef);
|
onFail = new JsInvocation(throwCCEFunRef);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -19,7 +19,6 @@ package org.jetbrains.kotlin.js.translate.intrinsic;
|
|||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.jetbrains.kotlin.descriptors.ClassDescriptor;
|
import org.jetbrains.kotlin.descriptors.ClassDescriptor;
|
||||||
import org.jetbrains.kotlin.descriptors.FunctionDescriptor;
|
import org.jetbrains.kotlin.descriptors.FunctionDescriptor;
|
||||||
import org.jetbrains.kotlin.js.translate.context.StaticContext;
|
|
||||||
import org.jetbrains.kotlin.js.translate.context.TranslationContext;
|
import org.jetbrains.kotlin.js.translate.context.TranslationContext;
|
||||||
import org.jetbrains.kotlin.js.translate.intrinsic.functions.FunctionIntrinsics;
|
import org.jetbrains.kotlin.js.translate.intrinsic.functions.FunctionIntrinsics;
|
||||||
import org.jetbrains.kotlin.js.translate.intrinsic.functions.basic.FunctionIntrinsic;
|
import org.jetbrains.kotlin.js.translate.intrinsic.functions.basic.FunctionIntrinsic;
|
||||||
@@ -37,8 +36,8 @@ public final class Intrinsics {
|
|||||||
private final BinaryOperationIntrinsics binaryOperationIntrinsics = new BinaryOperationIntrinsics();
|
private final BinaryOperationIntrinsics binaryOperationIntrinsics = new BinaryOperationIntrinsics();
|
||||||
private final ObjectIntrinsics objectIntrinsics;
|
private final ObjectIntrinsics objectIntrinsics;
|
||||||
|
|
||||||
public Intrinsics(@NotNull StaticContext staticContext) {
|
public Intrinsics() {
|
||||||
objectIntrinsics = new ObjectIntrinsics(staticContext);
|
objectIntrinsics = new ObjectIntrinsics();
|
||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
|
|||||||
+50
@@ -0,0 +1,50 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2017 JetBrains s.r.o.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright 2010-2017 JetBrains s.r.o.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.jetbrains.kotlin.js.translate.intrinsic.functions.factories
|
||||||
|
|
||||||
|
import org.jetbrains.kotlin.js.backend.ast.JsExpression
|
||||||
|
import org.jetbrains.kotlin.js.backend.ast.JsInvocation
|
||||||
|
import org.jetbrains.kotlin.js.translate.context.TranslationContext
|
||||||
|
import org.jetbrains.kotlin.js.translate.intrinsic.functions.basic.FunctionIntrinsicWithReceiverComputed
|
||||||
|
import org.jetbrains.kotlin.js.translate.utils.TranslationUtils
|
||||||
|
|
||||||
|
open class KotlinAliasedFunctionIntrinsic(private val functionName: String) : FunctionIntrinsicWithReceiverComputed() {
|
||||||
|
override fun apply(
|
||||||
|
receiver: JsExpression?,
|
||||||
|
arguments: List<JsExpression>,
|
||||||
|
context: TranslationContext
|
||||||
|
): JsExpression {
|
||||||
|
val function = context.getReferenceToIntrinsic(functionName)
|
||||||
|
return JsInvocation(function, if (receiver == null) arguments else TranslationUtils.generateInvocationArguments(receiver, arguments))
|
||||||
|
}
|
||||||
|
}
|
||||||
+4
-4
@@ -47,7 +47,7 @@ import static org.jetbrains.kotlin.js.patterns.PatternBuilder.pattern;
|
|||||||
public final class TopLevelFIF extends CompositeFIF {
|
public final class TopLevelFIF extends CompositeFIF {
|
||||||
public static final DescriptorPredicate EQUALS_IN_ANY = pattern("kotlin", "Any", "equals");
|
public static final DescriptorPredicate EQUALS_IN_ANY = pattern("kotlin", "Any", "equals");
|
||||||
@NotNull
|
@NotNull
|
||||||
private static final KotlinFunctionIntrinsic KOTLIN_ANY_EQUALS = new KotlinFunctionIntrinsic("equals") {
|
private static final KotlinAliasedFunctionIntrinsic KOTLIN_ANY_EQUALS = new KotlinAliasedFunctionIntrinsic("equals") {
|
||||||
@NotNull
|
@NotNull
|
||||||
@Override
|
@Override
|
||||||
public JsExpression apply(
|
public JsExpression apply(
|
||||||
@@ -65,7 +65,7 @@ public final class TopLevelFIF extends CompositeFIF {
|
|||||||
};
|
};
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
public static final KotlinFunctionIntrinsic KOTLIN_EQUALS = new KotlinFunctionIntrinsic("equals");
|
public static final KotlinAliasedFunctionIntrinsic KOTLIN_EQUALS = new KotlinAliasedFunctionIntrinsic("equals");
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
private static final KotlinFunctionIntrinsic KOTLIN_SUBSEQUENCE = new KotlinFunctionIntrinsic("subSequence");
|
private static final KotlinFunctionIntrinsic KOTLIN_SUBSEQUENCE = new KotlinFunctionIntrinsic("subSequence");
|
||||||
@@ -73,7 +73,7 @@ public final class TopLevelFIF extends CompositeFIF {
|
|||||||
@NotNull
|
@NotNull
|
||||||
private static final DescriptorPredicate HASH_CODE_IN_ANY = pattern("kotlin", "Any", "hashCode");
|
private static final DescriptorPredicate HASH_CODE_IN_ANY = pattern("kotlin", "Any", "hashCode");
|
||||||
@NotNull
|
@NotNull
|
||||||
private static final KotlinFunctionIntrinsic KOTLIN_HASH_CODE = new KotlinFunctionIntrinsic("hashCode");
|
private static final KotlinAliasedFunctionIntrinsic KOTLIN_HASH_CODE = new KotlinAliasedFunctionIntrinsic("hashCode");
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
private static final FunctionIntrinsic RETURN_RECEIVER_INTRINSIC = new FunctionIntrinsicWithReceiverComputed() {
|
private static final FunctionIntrinsic RETURN_RECEIVER_INTRINSIC = new FunctionIntrinsicWithReceiverComputed() {
|
||||||
@@ -164,7 +164,7 @@ public final class TopLevelFIF extends CompositeFIF {
|
|||||||
|
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
public static final KotlinFunctionIntrinsic TO_STRING = new KotlinFunctionIntrinsic("toString");
|
public static final KotlinAliasedFunctionIntrinsic TO_STRING = new KotlinAliasedFunctionIntrinsic("toString");
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
private static final FunctionIntrinsic CHAR_TO_STRING = new FunctionIntrinsicWithReceiverComputed() {
|
private static final FunctionIntrinsic CHAR_TO_STRING = new FunctionIntrinsicWithReceiverComputed() {
|
||||||
|
|||||||
+4
-14
@@ -20,25 +20,16 @@ import org.jetbrains.kotlin.builtins.CompanionObjectMapping
|
|||||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
||||||
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
||||||
import org.jetbrains.kotlin.js.backend.ast.JsExpression
|
import org.jetbrains.kotlin.js.backend.ast.JsExpression
|
||||||
import org.jetbrains.kotlin.js.backend.ast.JsName
|
|
||||||
import org.jetbrains.kotlin.js.translate.context.Namer
|
|
||||||
import org.jetbrains.kotlin.js.translate.context.StaticContext
|
|
||||||
import org.jetbrains.kotlin.js.translate.context.TranslationContext
|
import org.jetbrains.kotlin.js.translate.context.TranslationContext
|
||||||
import org.jetbrains.kotlin.js.translate.utils.JsAstUtils
|
|
||||||
import org.jetbrains.kotlin.name.FqName
|
import org.jetbrains.kotlin.name.FqName
|
||||||
import org.jetbrains.kotlin.name.Name
|
import org.jetbrains.kotlin.name.Name
|
||||||
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameUnsafe
|
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameUnsafe
|
||||||
|
|
||||||
class DefaultClassObjectIntrinsic(val staticContext: StaticContext, val tag: String, val fqName: FqName): ObjectIntrinsic {
|
class DefaultClassObjectIntrinsic( val fqName: FqName): ObjectIntrinsic {
|
||||||
private val innerName: JsName by lazy {
|
override fun apply(context: TranslationContext) = context.getReferenceToIntrinsic(fqName.asString())
|
||||||
val declaration = JsAstUtils.replaceRootReference(staticContext.getQualifiedReference(fqName), Namer.kotlinObject())
|
|
||||||
staticContext.importDeclaration(fqName.shortName().asString(), tag, declaration)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun apply(context: TranslationContext) = JsAstUtils.pureFqn(innerName, null)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class ObjectIntrinsics(private val staticContext: StaticContext) {
|
class ObjectIntrinsics {
|
||||||
private val cache = mutableMapOf<ClassDescriptor, ObjectIntrinsic>()
|
private val cache = mutableMapOf<ClassDescriptor, ObjectIntrinsic>()
|
||||||
|
|
||||||
fun getIntrinsic(classDescriptor: ClassDescriptor) = cache.getOrPut(classDescriptor) { createIntrinsic(classDescriptor) }
|
fun getIntrinsic(classDescriptor: ClassDescriptor) = cache.getOrPut(classDescriptor) { createIntrinsic(classDescriptor) }
|
||||||
@@ -53,8 +44,7 @@ class ObjectIntrinsics(private val staticContext: StaticContext) {
|
|||||||
val containingDeclaration = classDescriptor.containingDeclaration
|
val containingDeclaration = classDescriptor.containingDeclaration
|
||||||
val name = Name.identifier(containingDeclaration.name.asString() + "CompanionObject")
|
val name = Name.identifier(containingDeclaration.name.asString() + "CompanionObject")
|
||||||
|
|
||||||
return DefaultClassObjectIntrinsic(
|
return DefaultClassObjectIntrinsic(FqName("kotlin.js.internal").child(name))
|
||||||
staticContext, "intrinsic:kotlin.js.internal.${name.asString()}", FqName("kotlin.js.internal").child(name))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+6
-7
@@ -135,7 +135,7 @@ object CallableReferenceTranslator {
|
|||||||
function.body.statements += JsReturn(TranslationUtils.coerce(context, invocation, context.currentModule.builtIns.anyType))
|
function.body.statements += JsReturn(TranslationUtils.coerce(context, invocation, context.currentModule.builtIns.anyType))
|
||||||
|
|
||||||
val rawCallableRef = bindIfNecessary(function, receiver)
|
val rawCallableRef = bindIfNecessary(function, receiver)
|
||||||
return wrapFunctionCallableRef(expression.callableReference.getReferencedName(), rawCallableRef)
|
return context.wrapFunctionCallableRef(expression.callableReference.getReferencedName(), rawCallableRef)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun translateForProperty(
|
private fun translateForProperty(
|
||||||
@@ -168,7 +168,7 @@ object CallableReferenceTranslator {
|
|||||||
null
|
null
|
||||||
}
|
}
|
||||||
|
|
||||||
return wrapPropertyCallableRef(receiver, descriptor, expression.callableReference.getReferencedName(), getter, setter)
|
return context.wrapPropertyCallableRef(receiver, descriptor, expression.callableReference.getReferencedName(), getter, setter)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun isSetterVisible(descriptor: PropertyDescriptor, context: TranslationContext): Boolean {
|
private fun isSetterVisible(descriptor: PropertyDescriptor, context: TranslationContext): Boolean {
|
||||||
@@ -225,7 +225,7 @@ object CallableReferenceTranslator {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun wrapPropertyCallableRef(
|
private fun TranslationContext.wrapPropertyCallableRef(
|
||||||
receiver: JsExpression?,
|
receiver: JsExpression?,
|
||||||
descriptor: PropertyDescriptor,
|
descriptor: PropertyDescriptor,
|
||||||
name: String,
|
name: String,
|
||||||
@@ -238,7 +238,7 @@ object CallableReferenceTranslator {
|
|||||||
}
|
}
|
||||||
val nameLiteral = JsStringLiteral(name)
|
val nameLiteral = JsStringLiteral(name)
|
||||||
val argCountLiteral = JsIntLiteral(argCount)
|
val argCountLiteral = JsIntLiteral(argCount)
|
||||||
val invokeFun = JsNameRef(Namer.PROPERTY_CALLABLE_REF, Namer.kotlinObject())
|
val invokeFun = getReferenceToIntrinsic(Namer.PROPERTY_CALLABLE_REF)
|
||||||
val invocation = JsInvocation(invokeFun, nameLiteral, argCountLiteral, getter)
|
val invocation = JsInvocation(invokeFun, nameLiteral, argCountLiteral, getter)
|
||||||
if (setter != null) {
|
if (setter != null) {
|
||||||
invocation.arguments += setter
|
invocation.arguments += setter
|
||||||
@@ -246,13 +246,12 @@ object CallableReferenceTranslator {
|
|||||||
return invocation
|
return invocation
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun wrapFunctionCallableRef(
|
private fun TranslationContext.wrapFunctionCallableRef(
|
||||||
name: String,
|
name: String,
|
||||||
function: JsExpression
|
function: JsExpression
|
||||||
): JsExpression {
|
): JsExpression {
|
||||||
val nameLiteral = JsStringLiteral(name)
|
val nameLiteral = JsStringLiteral(name)
|
||||||
val invokeName = Namer.FUNCTION_CALLABLE_REF
|
val invokeFun = getReferenceToIntrinsic(Namer.FUNCTION_CALLABLE_REF)
|
||||||
val invokeFun = JsNameRef(invokeName, Namer.kotlinObject())
|
|
||||||
invokeFun.sideEffects = SideEffectKind.PURE
|
invokeFun.sideEffects = SideEffectKind.PURE
|
||||||
val invocation = JsInvocation(invokeFun, nameLiteral, function)
|
val invocation = JsInvocation(invokeFun, nameLiteral, function)
|
||||||
invocation.isCallableReference = true
|
invocation.isCallableReference = true
|
||||||
|
|||||||
+2
-2
@@ -83,8 +83,8 @@ public class VariableAccessTranslator extends AbstractTranslator implements Acce
|
|||||||
else if (original instanceof LocalVariableDescriptor) {
|
else if (original instanceof LocalVariableDescriptor) {
|
||||||
LocalVariableDescriptor originalLocal = (LocalVariableDescriptor) original;
|
LocalVariableDescriptor originalLocal = (LocalVariableDescriptor) original;
|
||||||
if (originalLocal.isLateInit()) {
|
if (originalLocal.isLateInit()) {
|
||||||
JsInvocation throwInvocation = new JsInvocation(Namer.throwUninitializedPropertyAccessExceptionFunRef(),
|
JsExpression throwFunction = context().getReferenceToIntrinsic(Namer.THROW_UNINITIALIZED_PROPERTY_ACCESS_EXCEPTION);
|
||||||
new JsStringLiteral(originalLocal.getName().asString()));
|
JsInvocation throwInvocation = new JsInvocation(throwFunction, new JsStringLiteral(originalLocal.getName().asString()));
|
||||||
return new JsConditional(new JsBinaryOperation(JsBinaryOperator.EQ, e, new JsNullLiteral()), throwInvocation, e);
|
return new JsConditional(new JsBinaryOperation(JsBinaryOperator.EQ, e, new JsNullLiteral()), throwInvocation, e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+11
-12
@@ -16,6 +16,7 @@
|
|||||||
|
|
||||||
package org.jetbrains.kotlin.js.translate.utils;
|
package org.jetbrains.kotlin.js.translate.utils;
|
||||||
|
|
||||||
|
import com.intellij.openapi.util.text.StringUtil;
|
||||||
import com.intellij.psi.PsiElement;
|
import com.intellij.psi.PsiElement;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
@@ -226,17 +227,6 @@ public final class TranslationUtils {
|
|||||||
return Translation.translateAsExpression(baseExpression, context);
|
return Translation.translateAsExpression(baseExpression, context);
|
||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
|
||||||
public static JsExpression translateLeftExpression(
|
|
||||||
@NotNull TranslationContext context,
|
|
||||||
@NotNull KtBinaryExpression expression,
|
|
||||||
@NotNull JsBlock block
|
|
||||||
) {
|
|
||||||
KtExpression left = expression.getLeft();
|
|
||||||
assert left != null : "Binary expression should have a left expression: " + expression.getText();
|
|
||||||
return Translation.translateAsExpression(left, context, block);
|
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
public static JsExpression translateRightExpression(
|
public static JsExpression translateRightExpression(
|
||||||
@NotNull TranslationContext context,
|
@NotNull TranslationContext context,
|
||||||
@@ -305,7 +295,7 @@ public final class TranslationUtils {
|
|||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
public static JsConditional sure(@NotNull JsExpression expression, @NotNull TranslationContext context) {
|
public static JsConditional sure(@NotNull JsExpression expression, @NotNull TranslationContext context) {
|
||||||
JsInvocation throwNPE = new JsInvocation(Namer.throwNPEFunctionRef());
|
JsInvocation throwNPE = new JsInvocation(context.getReferenceToIntrinsic(Namer.THROW_NPE_FUN_NAME));
|
||||||
JsConditional ensureNotNull = notNullConditional(expression, throwNPE, context);
|
JsConditional ensureNotNull = notNullConditional(expression, throwNPE, context);
|
||||||
|
|
||||||
JsExpression thenExpression = ensureNotNull.getThenExpression();
|
JsExpression thenExpression = ensureNotNull.getThenExpression();
|
||||||
@@ -548,4 +538,13 @@ public final class TranslationUtils {
|
|||||||
public static String getTagForSpecialFunction(@NotNull SpecialFunction specialFunction) {
|
public static String getTagForSpecialFunction(@NotNull SpecialFunction specialFunction) {
|
||||||
return "special:" + specialFunction.name();
|
return "special:" + specialFunction.name();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
public static JsExpression getIntrinsicFqn(@NotNull String name) {
|
||||||
|
JsExpression fqn = pureFqn(Namer.KOTLIN_NAME, null);
|
||||||
|
for (String part : StringUtil.split(name, ".")) {
|
||||||
|
fqn = pureFqn(part, fqn);
|
||||||
|
}
|
||||||
|
return fqn;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Vendored
+1
-1
@@ -1,5 +1,5 @@
|
|||||||
// EXPECTED_REACHABLE_NODES: 995
|
// EXPECTED_REACHABLE_NODES: 995
|
||||||
// HAS_NO_CAPTURED_VARS: function=A_init except=Kotlin;A
|
// HAS_NO_CAPTURED_VARS: function=A_init except=Kotlin;A;equals
|
||||||
|
|
||||||
class A() {
|
class A() {
|
||||||
var y: String? = null
|
var y: String? = null
|
||||||
|
|||||||
+1
-1
@@ -21,4 +21,4 @@ fun bar(x: Int) {
|
|||||||
println("%")
|
println("%")
|
||||||
}
|
}
|
||||||
|
|
||||||
// LINES: 1 1 1 1 1 1 1 17 2 2 3 3 4 7 7 9 9 10 10 11 14 14 16 16 1 1 22 20 20 20 20 2 2 3 3 4 3 7 7 9 9 10 10 11 10 14 14 16 16 * 20 21 21
|
// LINES: 1 1 1 1 1 1 1 1 1 17 2 2 3 3 4 7 7 9 9 10 10 11 14 14 16 16 1 1 1 1 22 20 20 20 20 2 2 3 3 4 3 7 7 9 9 10 10 11 10 14 14 16 16 * 20 21 21
|
||||||
Reference in New Issue
Block a user