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.ranges." + typeName + "Range").kotlinClass("NumberRange");
102                standardClasses.declare().forFQ("kotlin.ranges." + typeName + "Progression").kotlinClass("NumberProgression");
103            }
104    
105            standardClasses.declare().forFQ("kotlin.ranges.LongRange").kotlinClass("LongRange");
106            standardClasses.declare().forFQ("kotlin.ranges.CharRange").kotlinClass("CharRange");
107    
108            standardClasses.declare().forFQ("kotlin.ranges.LongProgression").kotlinClass("LongProgression");
109            standardClasses.declare().forFQ("kotlin.ranges.CharProgression").kotlinClass("CharProgression");
110    
111            standardClasses.declare().forFQ("kotlin.Enum").kotlinClass("Enum");
112    
113            standardClasses.declare().forFQ("kotlin.Comparable").kotlinClass("Comparable");
114    
115            standardClasses.declare().forFQ("koltin.Throwable").kotlinClass("Throwable");
116        }
117    
118    
119        @NotNull
120        private final JsObjectScope kotlinScope;
121    
122    
123        @NotNull
124        private final Map<FqNameUnsafe, JsName> standardObjects = Maps.newHashMap();
125    
126        @NotNull
127        private final Map<FqNameUnsafe, JsObjectScope> scopeMap = Maps.newHashMap();
128    
129        private StandardClasses(@NotNull JsObjectScope kotlinScope) {
130            this.kotlinScope = kotlinScope;
131        }
132    
133        private void declareTopLevelObjectInScope(@NotNull JsObjectScope scope, @NotNull Map<FqNameUnsafe, JsName> map,
134                                                  @NotNull FqNameUnsafe fullQualifiedName, @NotNull String name) {
135            JsName declaredName = scope.declareName(name);
136            map.put(fullQualifiedName, declaredName);
137            scopeMap.put(fullQualifiedName, JsObjectScope(scope, "scope for " + name));
138        }
139    
140        private void declareKotlinObject(@NotNull FqNameUnsafe fullQualifiedName, @NotNull String kotlinLibName) {
141            declareTopLevelObjectInScope(kotlinScope, standardObjects, fullQualifiedName, kotlinLibName);
142        }
143    
144        private void declareInner(@NotNull FqNameUnsafe fullQualifiedClassName,
145                                  @NotNull String shortMethodName,
146                                  @NotNull String javascriptName) {
147            JsObjectScope classScope = scopeMap.get(fullQualifiedClassName);
148            assert classScope != null;
149            FqNameUnsafe fullQualifiedMethodName = fullQualifiedClassName.child(Name.guess(shortMethodName));
150            standardObjects.put(fullQualifiedMethodName, classScope.declareName(javascriptName));
151        }
152    
153        private void declareMethods(@NotNull FqNameUnsafe classFQName,
154                                    @NotNull String... methodNames) {
155            for (String methodName : methodNames) {
156                declareInner(classFQName, methodName, methodName);
157            }
158        }
159    
160        private void declareReadonlyProperties(@NotNull FqNameUnsafe classFQName,
161                                               @NotNull String... propertyNames) {
162            for (String propertyName : propertyNames) {
163                declareInner(classFQName, propertyName, propertyName);
164            }
165        }
166    
167        public boolean isStandardObject(@NotNull DeclarationDescriptor descriptor) {
168            return standardObjects.containsKey(getFqName(descriptor));
169        }
170    
171        @NotNull
172        public JsName getStandardObjectName(@NotNull DeclarationDescriptor descriptor) {
173            return standardObjects.get(getFqName(descriptor));
174        }
175    
176        @NotNull
177        private Builder declare() {
178            return new Builder();
179        }
180    }