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.AstPackage.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    
126    
127        @NotNull
128        private final JsObjectScope kotlinScope;
129    
130    
131        @NotNull
132        private final Map<FqNameUnsafe, JsName> standardObjects = Maps.newHashMap();
133    
134        @NotNull
135        private final Map<FqNameUnsafe, JsObjectScope> scopeMap = Maps.newHashMap();
136    
137        private StandardClasses(@NotNull JsObjectScope kotlinScope) {
138            this.kotlinScope = kotlinScope;
139        }
140    
141        private void declareTopLevelObjectInScope(@NotNull JsObjectScope scope, @NotNull Map<FqNameUnsafe, JsName> map,
142                                                  @NotNull FqNameUnsafe fullQualifiedName, @NotNull String name) {
143            JsName declaredName = scope.declareName(name);
144            map.put(fullQualifiedName, declaredName);
145            scopeMap.put(fullQualifiedName, JsObjectScope(scope, "scope for " + name));
146        }
147    
148        private void declareKotlinObject(@NotNull FqNameUnsafe fullQualifiedName, @NotNull String kotlinLibName) {
149            declareTopLevelObjectInScope(kotlinScope, standardObjects, fullQualifiedName, kotlinLibName);
150        }
151    
152        private void declareInner(@NotNull FqNameUnsafe fullQualifiedClassName,
153                                  @NotNull String shortMethodName,
154                                  @NotNull String javascriptName) {
155            JsObjectScope classScope = scopeMap.get(fullQualifiedClassName);
156            assert classScope != null;
157            FqNameUnsafe fullQualifiedMethodName = fullQualifiedClassName.child(Name.guess(shortMethodName));
158            standardObjects.put(fullQualifiedMethodName, classScope.declareName(javascriptName));
159        }
160    
161        private void declareMethods(@NotNull FqNameUnsafe classFQName,
162                                    @NotNull String... methodNames) {
163            for (String methodName : methodNames) {
164                declareInner(classFQName, methodName, methodName);
165            }
166        }
167    
168        private void declareReadonlyProperties(@NotNull FqNameUnsafe classFQName,
169                                               @NotNull String... propertyNames) {
170            for (String propertyName : propertyNames) {
171                declareInner(classFQName, propertyName, propertyName);
172            }
173        }
174    
175        public boolean isStandardObject(@NotNull DeclarationDescriptor descriptor) {
176            return standardObjects.containsKey(getFqName(descriptor));
177        }
178    
179        @NotNull
180        public JsName getStandardObjectName(@NotNull DeclarationDescriptor descriptor) {
181            return standardObjects.get(getFqName(descriptor));
182        }
183    
184        @NotNull
185        private Builder declare() {
186            return new Builder();
187        }
188    }