001 /*
002 * Copyright 2010-2015 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.google.common.collect.Maps;
020 import com.google.dart.compiler.backend.js.ast.JsName;
021 import com.google.dart.compiler.backend.js.ast.JsObjectScope;
022 import org.jetbrains.annotations.NotNull;
023 import org.jetbrains.annotations.Nullable;
024 import org.jetbrains.kotlin.builtins.PrimitiveType;
025 import org.jetbrains.kotlin.descriptors.DeclarationDescriptor;
026 import org.jetbrains.kotlin.name.FqNameUnsafe;
027 import org.jetbrains.kotlin.name.Name;
028
029 import java.util.Map;
030
031 import static com.google.dart.compiler.backend.js.ast.JsScopesKt.JsObjectScope;
032 import static org.jetbrains.kotlin.resolve.DescriptorUtils.getFqName;
033
034 /**
035 * Provides a mechanism to bind some of the Kotlin/Java declarations with library implementations.
036 * Makes sense only for those declaration that cannot be annotated. (Use library annotation in this case)
037 */
038 public final class StandardClasses {
039
040 private final class Builder {
041
042 @Nullable
043 private /*var*/ FqNameUnsafe currentFQName = null;
044 @Nullable
045 private /*var*/ String currentObjectName = null;
046
047 @NotNull
048 public Builder forFQ(@NotNull String classFQName) {
049 currentFQName = new FqNameUnsafe(classFQName);
050 return this;
051 }
052
053 @NotNull
054 public Builder kotlinClass(@NotNull String kotlinName) {
055 kotlinTopLevelObject(kotlinName);
056 constructor();
057 return this;
058 }
059
060 private void kotlinTopLevelObject(@NotNull String kotlinName) {
061 assert currentFQName != null;
062 currentObjectName = kotlinName;
063 declareKotlinObject(currentFQName, kotlinName);
064 }
065
066 @NotNull
067 private Builder constructor() {
068 assert currentFQName != null;
069 assert currentObjectName != null;
070 declareInner(currentFQName, "<init>", currentObjectName);
071 return this;
072 }
073
074 @NotNull
075 public Builder methods(@NotNull String... methodNames) {
076 assert currentFQName != null;
077 declareMethods(currentFQName, methodNames);
078 return this;
079 }
080
081 @NotNull
082 public Builder properties(@NotNull String... propertyNames) {
083 assert currentFQName != null;
084 declareReadonlyProperties(currentFQName, propertyNames);
085 return this;
086 }
087 }
088
089 @NotNull
090 public static StandardClasses bindImplementations(@NotNull JsObjectScope kotlinObjectScope) {
091 StandardClasses standardClasses = new StandardClasses(kotlinObjectScope);
092 declareKotlinStandardClasses(standardClasses);
093 return standardClasses;
094 }
095
096 private static void declareKotlinStandardClasses(@NotNull StandardClasses standardClasses) {
097 for (PrimitiveType type : PrimitiveType.NUMBER_TYPES) {
098 if (type == PrimitiveType.CHAR || type == PrimitiveType.LONG) continue;
099
100 String typeName = type.getTypeName().asString();
101 standardClasses.declare().forFQ("kotlin." + typeName + "Range").kotlinClass("NumberRange")
102 .methods("iterator", "contains").properties("start", "end", "increment");
103
104 standardClasses.declare().forFQ("kotlin." + typeName + "Progression").kotlinClass("NumberProgression")
105 .methods("iterator", "contains").properties("start", "end", "increment");
106 }
107
108 standardClasses.declare().forFQ("kotlin.LongRange").kotlinClass("LongRange")
109 .methods("iterator", "contains").properties("start", "end", "increment");
110
111 standardClasses.declare().forFQ("kotlin.CharRange").kotlinClass("CharRange")
112 .methods("iterator", "contains").properties("start", "end", "increment");
113
114
115 standardClasses.declare().forFQ("kotlin.LongProgression").kotlinClass("LongProgression")
116 .methods("iterator", "contains").properties("start", "end", "increment");
117
118 standardClasses.declare().forFQ("kotlin.CharProgression").kotlinClass("CharProgression")
119 .methods("iterator", "contains").properties("start", "end", "increment");
120
121 standardClasses.declare().forFQ("kotlin.Enum").kotlinClass("Enum");
122
123 standardClasses.declare().forFQ("kotlin.Comparable").kotlinClass("Comparable");
124
125 standardClasses.declare().forFQ("koltin.Throwable").kotlinClass("Throwable");
126 }
127
128
129 @NotNull
130 private final JsObjectScope kotlinScope;
131
132
133 @NotNull
134 private final Map<FqNameUnsafe, JsName> standardObjects = Maps.newHashMap();
135
136 @NotNull
137 private final Map<FqNameUnsafe, JsObjectScope> scopeMap = Maps.newHashMap();
138
139 private StandardClasses(@NotNull JsObjectScope kotlinScope) {
140 this.kotlinScope = kotlinScope;
141 }
142
143 private void declareTopLevelObjectInScope(@NotNull JsObjectScope scope, @NotNull Map<FqNameUnsafe, JsName> map,
144 @NotNull FqNameUnsafe fullQualifiedName, @NotNull String name) {
145 JsName declaredName = scope.declareName(name);
146 map.put(fullQualifiedName, declaredName);
147 scopeMap.put(fullQualifiedName, JsObjectScope(scope, "scope for " + name));
148 }
149
150 private void declareKotlinObject(@NotNull FqNameUnsafe fullQualifiedName, @NotNull String kotlinLibName) {
151 declareTopLevelObjectInScope(kotlinScope, standardObjects, fullQualifiedName, kotlinLibName);
152 }
153
154 private void declareInner(@NotNull FqNameUnsafe fullQualifiedClassName,
155 @NotNull String shortMethodName,
156 @NotNull String javascriptName) {
157 JsObjectScope classScope = scopeMap.get(fullQualifiedClassName);
158 assert classScope != null;
159 FqNameUnsafe fullQualifiedMethodName = fullQualifiedClassName.child(Name.guess(shortMethodName));
160 standardObjects.put(fullQualifiedMethodName, classScope.declareName(javascriptName));
161 }
162
163 private void declareMethods(@NotNull FqNameUnsafe classFQName,
164 @NotNull String... methodNames) {
165 for (String methodName : methodNames) {
166 declareInner(classFQName, methodName, methodName);
167 }
168 }
169
170 private void declareReadonlyProperties(@NotNull FqNameUnsafe classFQName,
171 @NotNull String... propertyNames) {
172 for (String propertyName : propertyNames) {
173 declareInner(classFQName, propertyName, propertyName);
174 }
175 }
176
177 public boolean isStandardObject(@NotNull DeclarationDescriptor descriptor) {
178 return standardObjects.containsKey(getFqName(descriptor));
179 }
180
181 @NotNull
182 public JsName getStandardObjectName(@NotNull DeclarationDescriptor descriptor) {
183 return standardObjects.get(getFqName(descriptor));
184 }
185
186 @NotNull
187 private Builder declare() {
188 return new Builder();
189 }
190 }