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