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 017package org.jetbrains.k2js.translate.context; 018 019import com.google.common.collect.Maps; 020import com.google.dart.compiler.backend.js.ast.JsName; 021import com.google.dart.compiler.backend.js.ast.JsScope; 022import org.jetbrains.annotations.NotNull; 023import org.jetbrains.annotations.Nullable; 024import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor; 025import org.jetbrains.jet.lang.resolve.name.FqNameUnsafe; 026import org.jetbrains.jet.lang.resolve.name.Name; 027 028import java.util.Map; 029 030import 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 */ 036public 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}