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    }