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 }