001 /*
002 * Copyright 2010-2016 JetBrains s.r.o.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 * http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016
017 package org.jetbrains.kotlin.js.translate.context;
018
019 import com.intellij.openapi.util.text.StringUtil;
020 import org.jetbrains.annotations.NotNull;
021 import org.jetbrains.annotations.Nullable;
022 import org.jetbrains.kotlin.descriptors.CallableDescriptor;
023 import org.jetbrains.kotlin.descriptors.ClassDescriptor;
024 import org.jetbrains.kotlin.descriptors.SimpleFunctionDescriptor;
025 import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor;
026 import org.jetbrains.kotlin.idea.KotlinLanguage;
027 import org.jetbrains.kotlin.incremental.components.NoLookupLocation;
028 import org.jetbrains.kotlin.js.backend.ast.*;
029 import org.jetbrains.kotlin.js.backend.ast.metadata.MetadataProperties;
030 import org.jetbrains.kotlin.js.backend.ast.metadata.SideEffectKind;
031 import org.jetbrains.kotlin.js.backend.ast.metadata.TypeCheck;
032 import org.jetbrains.kotlin.js.naming.NameSuggestion;
033 import org.jetbrains.kotlin.js.naming.SuggestedName;
034 import org.jetbrains.kotlin.js.resolve.JsPlatform;
035 import org.jetbrains.kotlin.js.translate.utils.JsAstUtils;
036 import org.jetbrains.kotlin.js.translate.utils.JsDescriptorUtils;
037 import org.jetbrains.kotlin.name.FqName;
038 import org.jetbrains.kotlin.name.FqNameUnsafe;
039 import org.jetbrains.kotlin.name.Name;
040 import org.jetbrains.kotlin.resolve.DescriptorUtils;
041
042 import java.util.Arrays;
043 import java.util.Collection;
044 import java.util.Collections;
045 import java.util.List;
046
047 import static org.jetbrains.kotlin.js.translate.utils.JsAstUtils.pureFqn;
048 import static org.jetbrains.kotlin.js.translate.utils.JsDescriptorUtils.getModuleName;
049
050 /**
051 * Encapsulates different types of constants and naming conventions.
052 */
053 public final class Namer {
054 public static final String KOTLIN_NAME = KotlinLanguage.NAME;
055 public static final String KOTLIN_LOWER_NAME = KOTLIN_NAME.toLowerCase();
056
057 public static final String EQUALS_METHOD_NAME = getStableMangledNameForDescriptor(JsPlatform.INSTANCE.getBuiltIns().getAny(), "equals");
058 public static final String COMPARE_TO_METHOD_NAME = getStableMangledNameForDescriptor(JsPlatform.INSTANCE.getBuiltIns().getComparable(), "compareTo");
059 public static final String LONG_FROM_NUMBER = "fromNumber";
060 public static final String LONG_TO_NUMBER = "toNumber";
061 public static final String LONG_FROM_INT = "fromInt";
062 public static final String LONG_ZERO = "ZERO";
063 public static final String LONG_ONE = "ONE";
064 public static final String LONG_NEG_ONE = "NEG_ONE";
065 public static final String PRIMITIVE_COMPARE_TO = "primitiveCompareTo";
066 public static final String IS_CHAR = "isChar";
067 public static final String IS_NUMBER = "isNumber";
068 private static final String IS_CHAR_SEQUENCE = "isCharSequence";
069 public static final String GET_KCLASS = "getKClass";
070 public static final String GET_KCLASS_FROM_EXPRESSION = "getKClassFromExpression";
071
072 public static final String CALLEE_NAME = "$fun";
073
074 public static final String CALL_FUNCTION = "call";
075 private static final String APPLY_FUNCTION = "apply";
076
077 public static final String OUTER_FIELD_NAME = "$outer";
078
079 private static final String DELEGATE = "$delegate";
080
081 private static final String ROOT_PACKAGE = "_";
082
083 private static final String RECEIVER_PARAMETER_NAME = "$receiver";
084 public static final String ANOTHER_THIS_PARAMETER_NAME = "$this";
085
086 private static final String THROW_NPE_FUN_NAME = "throwNPE";
087 private static final String THROW_CLASS_CAST_EXCEPTION_FUN_NAME = "throwCCE";
088 private static final String THROW_ILLEGAL_STATE_EXCEPTION_FUN_NAME = "throwISE";
089 private static final String PROTOTYPE_NAME = "prototype";
090 private static final String CAPTURED_VAR_FIELD = "v";
091
092 public static final JsNameRef IS_ARRAY_FUN_REF = new JsNameRef("isArray", "Array");
093 public static final String DEFINE_INLINE_FUNCTION = "defineInlineFunction";
094 public static final String DEFAULT_PARAMETER_IMPLEMENTOR_SUFFIX = "$default";
095
096 private static final JsNameRef JS_OBJECT = new JsNameRef("Object");
097 private static final JsNameRef JS_OBJECT_CREATE_FUNCTION = new JsNameRef("create", JS_OBJECT);
098
099 public static final String LOCAL_MODULE_PREFIX = "$module$";
100 public static final String METADATA = "$metadata$";
101 public static final String METADATA_SUPERTYPES = "interfaces";
102 public static final String METADATA_SIMPLE_NAME = "simpleName";
103 public static final String METADATA_CLASS_KIND = "kind";
104 public static final String CLASS_KIND_ENUM = "Kind";
105 public static final String CLASS_KIND_CLASS = "CLASS";
106 public static final String CLASS_KIND_INTERFACE = "INTERFACE";
107 public static final String CLASS_KIND_OBJECT = "OBJECT";
108
109 public static final String OBJECT_INSTANCE_VAR_SUFFIX = "_instance";
110 public static final String OBJECT_INSTANCE_FUNCTION_SUFFIX = "_getInstance";
111
112 public static final String ENUM_NAME_FIELD = "name$";
113 public static final String ENUM_ORDINAL_FIELD = "ordinal$";
114
115 @NotNull
116 public static String getFunctionTag(@NotNull CallableDescriptor functionDescriptor) {
117 functionDescriptor = (CallableDescriptor) JsDescriptorUtils.findRealInlineDeclaration(functionDescriptor);
118 String moduleName = getModuleName(functionDescriptor);
119 FqNameUnsafe fqNameParent = DescriptorUtils.getFqName(functionDescriptor).parent();
120 String qualifier = null;
121
122 if (!fqNameParent.isRoot()) {
123 qualifier = fqNameParent.asString();
124 }
125
126 SuggestedName suggestedName = new NameSuggestion().suggest(functionDescriptor);
127 assert suggestedName != null : "Suggested name can be null only for module descriptors: " + functionDescriptor;
128 String mangledName = suggestedName.getNames().get(0);
129 return StringUtil.join(Arrays.asList(moduleName, qualifier, mangledName), ".");
130 }
131
132 @NotNull
133 public static String getReceiverParameterName() {
134 return RECEIVER_PARAMETER_NAME;
135 }
136
137 @NotNull
138 public static String getRootPackageName() {
139 return ROOT_PACKAGE;
140 }
141
142 @NotNull
143 public static String getPrototypeName() {
144 return PROTOTYPE_NAME;
145 }
146
147 @NotNull
148 public static String getDelegatePrefix() {
149 return DELEGATE;
150 }
151
152 @NotNull
153 public static String getDelegateName(@NotNull String propertyName) {
154 return propertyName + DELEGATE;
155 }
156
157 @NotNull
158 public static JsNameRef getDelegateNameRef(String propertyName) {
159 return new JsNameRef(getDelegateName(propertyName), JsLiteral.THIS);
160 }
161
162 @NotNull
163 public static JsNameRef getFunctionCallRef(@NotNull JsExpression functionExpression) {
164 return pureFqn(CALL_FUNCTION, functionExpression);
165 }
166
167 @NotNull
168 public static JsNameRef getFunctionApplyRef(@NotNull JsExpression functionExpression) {
169 return pureFqn(APPLY_FUNCTION, functionExpression);
170 }
171
172 @NotNull
173 public static JsInvocation createObjectWithPrototypeFrom(@NotNull JsExpression referenceToClass) {
174 return new JsInvocation(JS_OBJECT_CREATE_FUNCTION.deepCopy(), JsAstUtils.prototypeOf(referenceToClass));
175 }
176
177 @NotNull
178 public static JsNameRef getCapturedVarAccessor(@NotNull JsExpression ref) {
179 return pureFqn(CAPTURED_VAR_FIELD, ref);
180 }
181
182 @NotNull
183 public static String isInstanceSuggestedName(@NotNull TypeParameterDescriptor descriptor) {
184 return "is" + descriptor.getName().getIdentifier();
185 }
186
187 @NotNull
188 public static Namer newInstance(@NotNull JsScope rootScope) {
189 return new Namer(rootScope);
190 }
191
192 @NotNull
193 private final JsObjectScope kotlinScope;
194 @NotNull
195 public static final String FUNCTION_CALLABLE_REF = "getCallableRef";
196 @NotNull
197 public static final String PROPERTY_CALLABLE_REF = "getPropertyCallableRef";
198 @NotNull
199 private final JsExpression callGetProperty;
200 @NotNull
201 private final JsExpression callSetProperty;
202
203 @NotNull
204 private final JsName isTypeName;
205
206 private Namer(@NotNull JsScope rootScope) {
207 kotlinScope = new JsObjectScope(rootScope, "Kotlin standard object");
208
209 callGetProperty = kotlin("callGetter");
210 callSetProperty = kotlin("callSetter");
211
212 isTypeName = kotlinScope.declareName("isType");
213 }
214
215 // TODO: get rid of this function
216 @NotNull
217 public static String getStableMangledNameForDescriptor(@NotNull ClassDescriptor descriptor, @NotNull String functionName) {
218 Collection<SimpleFunctionDescriptor> functions = descriptor.getDefaultType().getMemberScope().getContributedFunctions(
219 Name.identifier(functionName), NoLookupLocation.FROM_BACKEND);
220 assert functions.size() == 1 : "Can't select a single function: " + functionName + " in " + descriptor;
221 SuggestedName suggested = new NameSuggestion().suggest(functions.iterator().next());
222 assert suggested != null : "Suggested name for class members is always non-null: " + functions.iterator().next();
223 return suggested.getNames().get(0);
224 }
225
226 @NotNull
227 public static JsExpression throwNPEFunctionRef() {
228 return new JsNameRef(THROW_NPE_FUN_NAME, kotlinObject());
229 }
230
231 @NotNull
232 public static JsExpression throwClassCastExceptionFunRef() {
233 return new JsNameRef(THROW_CLASS_CAST_EXCEPTION_FUN_NAME, kotlinObject());
234 }
235
236 @NotNull
237 public static JsExpression throwIllegalStateExceptionFunRef() {
238 return new JsNameRef(THROW_ILLEGAL_STATE_EXCEPTION_FUN_NAME, kotlinObject());
239 }
240
241 @NotNull
242 public static JsNameRef kotlin(@NotNull JsName name) {
243 return pureFqn(name, kotlinObject());
244 }
245
246 @NotNull
247 public JsNameRef kotlin(@NotNull String name) {
248 return kotlin(kotlinScope.declareName(name));
249 }
250
251 @NotNull
252 public static JsNameRef kotlinObject() {
253 return pureFqn(KOTLIN_NAME, null);
254 }
255
256 @NotNull
257 public JsExpression isTypeOf(@NotNull JsExpression type) {
258 return invokeFunctionAndSetTypeCheckMetadata("isTypeOf", type, TypeCheck.TYPEOF);
259 }
260
261 @NotNull
262 public JsExpression isInstanceOf(@NotNull JsExpression type) {
263 return invokeFunctionAndSetTypeCheckMetadata("isInstanceOf", type, TypeCheck.INSTANCEOF);
264 }
265
266 @NotNull
267 public JsExpression orNull(@NotNull JsExpression callable) {
268 return invokeFunctionAndSetTypeCheckMetadata("orNull", callable, TypeCheck.OR_NULL);
269 }
270
271 @NotNull
272 public JsExpression andPredicate(@NotNull JsExpression a, @NotNull JsExpression b) {
273 return invokeFunctionAndSetTypeCheckMetadata("andPredicate", Arrays.asList(a, b), TypeCheck.AND_PREDICATE);
274 }
275
276 @NotNull
277 public JsExpression isComparable() {
278 return kotlin("isComparable");
279 }
280
281 @NotNull
282 public JsExpression isCharSequence() {
283 return kotlin(IS_CHAR_SEQUENCE);
284 }
285
286 @NotNull
287 private JsExpression invokeFunctionAndSetTypeCheckMetadata(
288 @NotNull String functionName,
289 @Nullable JsExpression argument,
290 @NotNull TypeCheck metadata
291 ) {
292 List<JsExpression> arguments = argument != null ? Collections.singletonList(argument) : Collections.<JsExpression>emptyList();
293 return invokeFunctionAndSetTypeCheckMetadata(functionName, arguments, metadata);
294 }
295
296 @NotNull
297 private JsExpression invokeFunctionAndSetTypeCheckMetadata(
298 @NotNull String functionName,
299 @NotNull List<JsExpression> arguments,
300 @NotNull TypeCheck metadata
301 ) {
302 JsInvocation invocation = new JsInvocation(kotlin(functionName));
303 invocation.getArguments().addAll(arguments);
304 MetadataProperties.setTypeCheck(invocation, metadata);
305 MetadataProperties.setSideEffects(invocation, SideEffectKind.PURE);
306 return invocation;
307 }
308
309 @NotNull
310 public JsExpression isInstanceOf(@NotNull JsExpression instance, @NotNull JsExpression type) {
311 JsInvocation result = new JsInvocation(kotlin(isTypeName), instance, type);
312 MetadataProperties.setSideEffects(result, SideEffectKind.PURE);
313 return result;
314 }
315
316 @NotNull
317 static String generatePackageName(@NotNull FqName packageFqName) {
318 return packageFqName.isRoot() ? getRootPackageName() : packageFqName.shortName().asString();
319 }
320
321 @NotNull
322 public static JsExpression getUndefinedExpression() {
323 return new JsPrefixOperation(JsUnaryOperator.VOID, JsNumberLiteral.ZERO);
324 }
325
326 @NotNull
327 public JsExpression getCallGetProperty() {
328 return callGetProperty.deepCopy();
329 }
330
331 @NotNull
332 public JsExpression getCallSetProperty() {
333 return callSetProperty.deepCopy();
334 }
335
336 public static JsNameRef kotlinLong() {
337 return pureFqn("Long", kotlinObject());
338 }
339
340 @NotNull
341 public static JsNameRef createInlineFunction() {
342 return pureFqn(DEFINE_INLINE_FUNCTION, kotlinObject());
343 }
344
345 @NotNull
346 public static String suggestedModuleName(@NotNull String id) {
347 if (id.isEmpty()) {
348 return "_";
349 }
350
351 StringBuilder sb = new StringBuilder(id.length());
352 char c = id.charAt(0);
353 if (Character.isJavaIdentifierStart(c)) {
354 sb.append(c);
355 }
356 else {
357 sb.append('_');
358 if (Character.isJavaIdentifierPart(c)) {
359 sb.append(c);
360 }
361 }
362
363 for (int i = 1; i < id.length(); ++i) {
364 c = id.charAt(i);
365 sb.append(Character.isJavaIdentifierPart(c) ? c : '_');
366 }
367
368 return sb.toString();
369 }
370
371 public static JsNameRef imul() {
372 return pureFqn("imul", kotlinObject());
373 }
374
375 public static boolean requiresEscaping(@NotNull String name) {
376 // TODO: remove if there is existing implementation of this method
377 // TODO: handle JavaScript keywords
378 if (name.isEmpty() || !Character.isJavaIdentifierStart(name.charAt(0))) return true;
379 for (int i = 1; i < name.length(); ++i) {
380 if (!Character.isJavaIdentifierPart(name.charAt(i))) return true;
381 }
382 return false;
383 }
384 }