/*
* 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.context;
import com.intellij.openapi.util.text.
StringUtil;
import org.jetbrains.annotations.
NotNull;
import org.jetbrains.annotations.
Nullable;
import org.jetbrains.kotlin.builtins.
PrimitiveType;
import org.jetbrains.kotlin.descriptors.
CallableDescriptor;
import org.jetbrains.kotlin.descriptors.
ClassDescriptor;
import org.jetbrains.kotlin.descriptors.
SimpleFunctionDescriptor;
import org.jetbrains.kotlin.descriptors.
TypeParameterDescriptor;
import org.jetbrains.kotlin.idea.
KotlinLanguage;
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;
import org.jetbrains.kotlin.js.naming.
SuggestedName;
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.
JsDescriptorUtils;
import org.jetbrains.kotlin.name.
FqNameUnsafe;
import org.jetbrains.kotlin.name.
Name;
import org.jetbrains.kotlin.resolve.
DescriptorUtils;
import java.util.
Arrays;
import java.util.
Collection;
import java.util.
Collections;
import java.util.
List;
import static org.jetbrains.kotlin.js.translate.utils.
JsAstUtils.pureFqn;
import static org.jetbrains.kotlin.js.translate.utils.
JsDescriptorUtils.getModuleName;
/**
* Encapsulates different types of constants and naming conventions.
*/
public final class
Namer {
public static final
String KOTLIN_NAME =
KotlinLanguage.
NAME;
public static final
String KOTLIN_LOWER_NAME =
KOTLIN_NAME.
toLowerCase();
public static final
String EQUALS_METHOD_NAME =
getStableMangledNameForDescriptor(
JsPlatform.
INSTANCE.
getBuiltIns().
getAny(), "equals");
public static final
String COMPARE_TO_METHOD_NAME =
getStableMangledNameForDescriptor(
JsPlatform.
INSTANCE.
getBuiltIns().
getComparable(), "compareTo");
public static final
String LONG_FROM_NUMBER = "fromNumber";
public static final
String LONG_TO_NUMBER = "toNumber";
public static final
String LONG_FROM_INT = "fromInt";
public static final
String LONG_ZERO = "ZERO";
public static final
String LONG_ONE = "ONE";
public static final
String LONG_NEG_ONE = "NEG_ONE";
public static final
String LONG_MAX_VALUE = "MAX_VALUE";
public static final
String LONG_MIN_VALUE = "MIN_VALUE";
public static final
String PRIMITIVE_COMPARE_TO = "primitiveCompareTo";
public static final
String IS_CHAR = "isChar";
public static final
String IS_NUMBER = "isNumber";
private static final
String IS_CHAR_SEQUENCE = "isCharSequence";
public static final
String GET_KCLASS = "getKClass";
public static final
String GET_KCLASS_FROM_EXPRESSION = "getKClassFromExpression";
public static final
String CALLEE_NAME = "$fun";
public static final
String CALL_FUNCTION = "call";
private static final
String APPLY_FUNCTION = "apply";
public static final
String OUTER_FIELD_NAME = "$outer";
private static final
String DELEGATE = "$delegate";
private static final
String ROOT_PACKAGE = "_";
private static final
String RECEIVER_PARAMETER_NAME = "$receiver";
public static final
String ANOTHER_THIS_PARAMETER_NAME = "$this";
public static final
String THROW_CLASS_CAST_EXCEPTION_FUN_NAME = "throwCCE";
public static final
String THROW_ILLEGAL_STATE_EXCEPTION_FUN_NAME = "throwISE";
public static final
String THROW_UNINITIALIZED_PROPERTY_ACCESS_EXCEPTION = "throwUPAE";
public static final
String NULL_CHECK_INTRINSIC_NAME = "ensureNotNull";
private static final
String PROTOTYPE_NAME = "prototype";
private static final
String CAPTURED_VAR_FIELD = "v";
public static final
JsNameRef IS_ARRAY_FUN_REF = new
JsNameRef("isArray", "Array");
public static final
String DEFINE_INLINE_FUNCTION = "defineInlineFunction";
public static final
String DEFAULT_PARAMETER_IMPLEMENTOR_SUFFIX = "$default";
private static final
JsNameRef JS_OBJECT = new
JsNameRef("Object");
private static final
JsNameRef JS_OBJECT_CREATE_FUNCTION = new
JsNameRef("create",
JS_OBJECT);
public static final
String LOCAL_MODULE_PREFIX = "$module$";
public static final
String METADATA = "$metadata$";
public static final
String METADATA_SUPERTYPES = "interfaces";
public static final
String METADATA_SIMPLE_NAME = "simpleName";
public static final
String METADATA_CLASS_KIND = "kind";
public static final
String CLASS_KIND_ENUM = "Kind";
public static final
String CLASS_KIND_CLASS = "CLASS";
public static final
String CLASS_KIND_INTERFACE = "INTERFACE";
public static final
String CLASS_KIND_OBJECT = "OBJECT";
public static final
String OBJECT_INSTANCE_VAR_SUFFIX = "_instance";
public static final
String OBJECT_INSTANCE_FUNCTION_SUFFIX = "_getInstance";
public static final
String ENUM_NAME_FIELD = "name$";
public static final
String ENUM_ORDINAL_FIELD = "ordinal$";
public static final
String IMPORTS_FOR_INLINE_PROPERTY = "$$importsForInline$$";
@
NotNull
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);
String moduleName =
getModuleName(
functionDescriptor);
FqNameUnsafe fqNameParent =
DescriptorUtils.
getFqName(
functionDescriptor).
parent();
String qualifier = null;
if (!
fqNameParent.
isRoot()) {
qualifier =
fqNameParent.
asString();
}
SuggestedName suggestedName = new
NameSuggestion().
suggest(
functionDescriptor);
assert
suggestedName != null : "Suggested name can be null only for module descriptors: " +
functionDescriptor;
String mangledName =
suggestedName.
getNames().
get(0);
return
StringUtil.
join(
Arrays.
asList(
moduleName,
qualifier,
mangledName), ".");
}
@
NotNull
public static
String getReceiverParameterName() {
return
RECEIVER_PARAMETER_NAME;
}
@
NotNull
public static
String getRootPackageName() {
return
ROOT_PACKAGE;
}
@
NotNull
public static
String getPrototypeName() {
return
PROTOTYPE_NAME;
}
@
NotNull
public static
String getDelegatePrefix() {
return
DELEGATE;
}
@
NotNull
public static
JsNameRef getFunctionCallRef(@
NotNull JsExpression functionExpression) {
JsNameRef result =
pureFqn(
CALL_FUNCTION,
functionExpression);
MetadataProperties.
setJsCall(
result, true);
return
result;
}
@
NotNull
public static
JsNameRef getFunctionApplyRef(@
NotNull JsExpression functionExpression) {
return
pureFqn(
APPLY_FUNCTION,
functionExpression);
}
@
NotNull
public static
JsInvocation createObjectWithPrototypeFrom(@
NotNull JsExpression referenceToClass) {
return new
JsInvocation(
JS_OBJECT_CREATE_FUNCTION.
deepCopy(),
JsAstUtils.
prototypeOf(
referenceToClass));
}
@
NotNull
public static
JsNameRef getCapturedVarAccessor(@
NotNull JsExpression ref) {
JsNameRef result = new
JsNameRef(
CAPTURED_VAR_FIELD,
ref);
MetadataProperties.
setSideEffects(
result,
SideEffectKind.
DEPENDS_ON_STATE);
return
result;
}
@
NotNull
public static
String isInstanceSuggestedName(@
NotNull TypeParameterDescriptor descriptor) {
return "is" +
NameSuggestion.
sanitizeName(
descriptor.
getName().
getIdentifier());
}
@
NotNull
public static
Namer newInstance(@
NotNull JsScope rootScope) {
return new
Namer(
rootScope);
}
@
NotNull
private final
JsObjectScope kotlinScope;
@
NotNull
public static final
String FUNCTION_CALLABLE_REF = "getCallableRef";
@
NotNull
public static final
String PROPERTY_CALLABLE_REF = "getPropertyCallableRef";
@
NotNull
private final
JsExpression callGetProperty;
@
NotNull
private final
JsExpression callSetProperty;
private
Namer(@
NotNull JsScope rootScope) {
kotlinScope = new
JsObjectScope(
rootScope, "Kotlin standard object");
callGetProperty =
kotlin("callGetter");
callSetProperty =
kotlin("callSetter");
}
// TODO: get rid of this function
@
NotNull
private static
String getStableMangledNameForDescriptor(@
NotNull ClassDescriptor descriptor, @
NotNull String functionName) {
Collection<
SimpleFunctionDescriptor>
functions =
descriptor.
getDefaultType().
getMemberScope().
getContributedFunctions(
Name.
identifier(
functionName),
NoLookupLocation.
FROM_BACKEND);
assert
functions.
size() == 1 : "Can't select a single function: " +
functionName + " in " +
descriptor;
SuggestedName suggested = new
NameSuggestion().
suggest(
functions.
iterator().
next());
assert
suggested != null : "Suggested name for class members is always non-null: " +
functions.
iterator().
next();
return
suggested.
getNames().
get(0);
}
@
NotNull
public static
JsNameRef kotlin(@
NotNull JsName name) {
return
pureFqn(
name,
kotlinObject());
}
@
NotNull
public
JsNameRef kotlin(@
NotNull String name) {
return
kotlin(
kotlinScope.
declareName(
name));
}
@
NotNull
public static
JsNameRef kotlinObject() {
return
pureFqn(
KOTLIN_NAME, null);
}
@
NotNull
public
JsExpression isTypeOf(@
NotNull JsExpression type) {
return
invokeFunctionAndSetTypeCheckMetadata("isTypeOf",
type,
TypeCheck.
TYPEOF);
}
@
NotNull
public
JsExpression isInstanceOf(@
NotNull JsExpression type) {
return
invokeFunctionAndSetTypeCheckMetadata("isInstanceOf",
type,
TypeCheck.
INSTANCEOF);
}
@
NotNull
public
JsExpression orNull(@
NotNull JsExpression callable) {
return
invokeFunctionAndSetTypeCheckMetadata("orNull",
callable,
TypeCheck.
OR_NULL);
}
@
NotNull
public
JsExpression andPredicate(@
NotNull JsExpression a, @
NotNull JsExpression b) {
return
invokeFunctionAndSetTypeCheckMetadata("andPredicate",
Arrays.
asList(
a,
b),
TypeCheck.
AND_PREDICATE);
}
@
NotNull
public
JsExpression isComparable() {
return
kotlin("isComparable");
}
@
NotNull
public
JsExpression isCharSequence() {
return
kotlin(
IS_CHAR_SEQUENCE);
}
@
NotNull
public
JsExpression isArray() {
return
kotlin("isArray");
}
@
NotNull
public
JsExpression isPrimitiveArray(@
NotNull PrimitiveType type) {
return
kotlin("is" +
type.
getArrayTypeName().
asString());
}
@
NotNull
private
JsExpression invokeFunctionAndSetTypeCheckMetadata(
@
NotNull String functionName,
@
Nullable JsExpression argument,
@
NotNull TypeCheck metadata
) {
List<
JsExpression>
arguments =
argument != null ?
Collections.
singletonList(
argument) :
Collections.
emptyList();
return
invokeFunctionAndSetTypeCheckMetadata(
functionName,
arguments,
metadata);
}
@
NotNull
private
JsExpression invokeFunctionAndSetTypeCheckMetadata(
@
NotNull String functionName,
@
NotNull List<
JsExpression>
arguments,
@
NotNull TypeCheck metadata
) {
JsInvocation invocation = new
JsInvocation(
kotlin(
functionName));
invocation.
getArguments().
addAll(
arguments);
MetadataProperties.
setTypeCheck(
invocation,
metadata);
MetadataProperties.
setSideEffects(
invocation,
SideEffectKind.
PURE);
return
invocation;
}
@
NotNull
public static
JsExpression isInstanceOf(@
NotNull JsExpression instance, @
NotNull JsExpression type) {
JsInvocation result = new
JsInvocation(new
JsNameRef("isType",
KOTLIN_NAME),
instance,
type);
MetadataProperties.
setSideEffects(
result,
SideEffectKind.
PURE);
return
result;
}
@
NotNull
public static
JsExpression getUndefinedExpression() {
return new
JsPrefixOperation(
JsUnaryOperator.
VOID, new
JsIntLiteral(0));
}
@
NotNull
public
JsExpression getCallGetProperty() {
return
callGetProperty.
deepCopy();
}
@
NotNull
public
JsExpression getCallSetProperty() {
return
callSetProperty.
deepCopy();
}
public static
JsNameRef kotlinLong() {
return
pureFqn("Long",
kotlinObject());
}
@
NotNull
public static
JsExpression createSpecialFunction(@
NotNull SpecialFunction specialFunction) {
return
pureFqn(
specialFunction.
getSuggestedName(),
kotlinObject());
}
@
NotNull
public static
String suggestedModuleName(@
NotNull String id) {
if (
id.
isEmpty()) {
return "_";
}
StringBuilder sb = new
StringBuilder(
id.
length());
char
c =
id.
charAt(0);
if (
Character.
isJavaIdentifierStart(
c)) {
sb.
append(
c);
}
else {
sb.
append('_');
if (
Character.
isJavaIdentifierPart(
c)) {
sb.
append(
c);
}
}
for (int
i = 1;
i <
id.
length(); ++
i) {
c =
id.
charAt(
i);
sb.
append(
Character.
isJavaIdentifierPart(
c) ?
c : '_');
}
return
sb.
toString();
}
public static
JsNameRef imul() {
return
pureFqn("imul",
kotlinObject());
}
public static boolean
requiresEscaping(@
NotNull String name) {
// TODO: remove if there is existing implementation of this method
// TODO: handle JavaScript keywords
if (
name.
isEmpty() || !
Character.
isJavaIdentifierStart(
name.
charAt(0))) return true;
for (int
i = 1;
i <
name.
length(); ++
i) {
if (!
Character.
isJavaIdentifierPart(
name.
charAt(
i))) return true;
}
return false;
}
}