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.utils; 018 019 import com.google.dart.compiler.backend.js.ast.*; 020 import com.intellij.util.SmartList; 021 import org.jetbrains.annotations.NotNull; 022 import org.jetbrains.annotations.Nullable; 023 import org.jetbrains.kotlin.js.translate.context.Namer; 024 import org.jetbrains.kotlin.js.translate.context.TranslationContext; 025 import org.jetbrains.kotlin.types.expressions.OperatorConventions; 026 import org.jetbrains.kotlin.util.OperatorNameConventions; 027 028 import java.util.Arrays; 029 import java.util.Collections; 030 import java.util.List; 031 032 public final class JsAstUtils { 033 private static final JsNameRef DEFINE_PROPERTY = new JsNameRef("defineProperty"); 034 public static final JsNameRef CREATE_OBJECT = new JsNameRef("create"); 035 036 private static final JsNameRef VALUE = new JsNameRef("value"); 037 private static final JsPropertyInitializer WRITABLE = new JsPropertyInitializer(new JsNameRef("writable"), JsLiteral.TRUE); 038 private static final JsPropertyInitializer ENUMERABLE = new JsPropertyInitializer(new JsNameRef("enumerable"), JsLiteral.TRUE); 039 040 public static final String LENDS_JS_DOC_TAG = "lends"; 041 042 static { 043 JsNameRef globalObjectReference = new JsNameRef("Object"); 044 DEFINE_PROPERTY.setQualifier(globalObjectReference); 045 CREATE_OBJECT.setQualifier(globalObjectReference); 046 } 047 048 private JsAstUtils() { 049 } 050 051 @NotNull 052 public static JsStatement convertToStatement(@NotNull JsNode jsNode) { 053 assert (jsNode instanceof JsExpression) || (jsNode instanceof JsStatement) 054 : "Unexpected node of type: " + jsNode.getClass().toString(); 055 if (jsNode instanceof JsExpression) { 056 return ((JsExpression) jsNode).makeStmt(); 057 } 058 return (JsStatement) jsNode; 059 } 060 061 @NotNull 062 public static JsBlock convertToBlock(@NotNull JsNode jsNode) { 063 if (jsNode instanceof JsBlock) { 064 return (JsBlock) jsNode; 065 } 066 JsBlock block = new JsBlock(); 067 block.getStatements().add(convertToStatement(jsNode)); 068 return block; 069 } 070 071 @NotNull 072 private static JsStatement deBlockIfPossible(@NotNull JsStatement statement) { 073 if (statement instanceof JsBlock && ((JsBlock)statement).getStatements().size() == 1) { 074 return ((JsBlock)statement).getStatements().get(0); 075 } 076 else { 077 return statement; 078 } 079 } 080 081 @NotNull 082 public static JsIf newJsIf( 083 @NotNull JsExpression ifExpression, 084 @NotNull JsStatement thenStatement, 085 @Nullable JsStatement elseStatement 086 ) { 087 elseStatement = elseStatement != null ? deBlockIfPossible(elseStatement) : null; 088 return new JsIf(ifExpression, deBlockIfPossible(thenStatement), elseStatement); 089 } 090 091 @NotNull 092 public static JsIf newJsIf(@NotNull JsExpression ifExpression, @NotNull JsStatement thenStatement) { 093 return newJsIf(ifExpression, thenStatement, null); 094 } 095 096 @Nullable 097 public static JsExpression extractExpressionFromStatement(@Nullable JsStatement statement) { 098 return statement instanceof JsExpressionStatement ? ((JsExpressionStatement) statement).getExpression() : null; 099 } 100 101 @NotNull 102 public static JsStatement mergeStatementInBlockIfNeeded(@NotNull JsStatement statement, @NotNull JsBlock block) { 103 if (block.isEmpty()) { 104 return statement; 105 } else { 106 if (isEmptyStatement(statement)) { 107 return deBlockIfPossible(block); 108 } 109 block.getStatements().add(statement); 110 return block; 111 } 112 } 113 114 public static boolean isEmptyStatement(@NotNull JsStatement statement) { 115 return statement instanceof JsEmpty; 116 } 117 118 public static boolean isEmptyExpression(@NotNull JsExpression expression) { 119 return expression instanceof JsEmptyExpression; 120 } 121 122 @NotNull 123 public static JsInvocation invokeKotlinFunction(@NotNull String name, @NotNull JsExpression... argument) { 124 return new JsInvocation(new JsNameRef(name, Namer.KOTLIN_OBJECT_REF), argument); 125 } 126 127 @NotNull 128 public static JsInvocation invokeMethod(@NotNull JsExpression thisObject, @NotNull String name, @NotNull JsExpression... arguments) { 129 return new JsInvocation(new JsNameRef(name, thisObject), arguments); 130 } 131 132 @NotNull 133 public static JsExpression toInt32(@NotNull JsExpression expression) { 134 return new JsBinaryOperation(JsBinaryOperator.BIT_OR, expression, JsNumberLiteral.ZERO); 135 } 136 137 @NotNull 138 public static JsExpression charToInt(@NotNull JsExpression expression) { 139 return invokeMethod(expression, "charCodeAt", JsNumberLiteral.ZERO); 140 } 141 142 @NotNull 143 public static JsExpression toShort(@NotNull JsExpression expression) { 144 return invokeKotlinFunction(OperatorConventions.SHORT.getIdentifier(), expression); 145 } 146 147 @NotNull 148 public static JsExpression toByte(@NotNull JsExpression expression) { 149 return invokeKotlinFunction(OperatorConventions.BYTE.getIdentifier(), expression); 150 } 151 152 @NotNull 153 public static JsExpression toLong(@NotNull JsExpression expression) { 154 return invokeKotlinFunction(OperatorConventions.LONG.getIdentifier(), expression); 155 } 156 157 @NotNull 158 public static JsExpression toChar(@NotNull JsExpression expression) { 159 return invokeKotlinFunction(OperatorConventions.CHAR.getIdentifier(), expression); 160 } 161 162 @NotNull 163 public static JsExpression compareTo(@NotNull JsExpression left, @NotNull JsExpression right) { 164 return invokeKotlinFunction(OperatorNameConventions.COMPARE_TO.getIdentifier(), left, right); 165 } 166 167 @NotNull 168 public static JsExpression primitiveCompareTo(@NotNull JsExpression left, @NotNull JsExpression right) { 169 return invokeKotlinFunction(Namer.PRIMITIVE_COMPARE_TO, left, right); 170 } 171 172 @NotNull 173 private static JsExpression rangeTo(@NotNull String rangeClassName, @NotNull JsExpression rangeStart, @NotNull JsExpression rangeEnd) { 174 JsNameRef expr = new JsNameRef(rangeClassName, Namer.KOTLIN_NAME); 175 JsNew numberRangeConstructorInvocation = new JsNew(expr); 176 setArguments(numberRangeConstructorInvocation, rangeStart, rangeEnd); 177 return numberRangeConstructorInvocation; 178 } 179 180 @NotNull 181 public static JsExpression numberRangeTo(@NotNull JsExpression rangeStart, @NotNull JsExpression rangeEnd) { 182 return rangeTo(Namer.NUMBER_RANGE, rangeStart, rangeEnd); 183 } 184 185 @NotNull 186 public static JsExpression charRangeTo(@NotNull JsExpression rangeStart, @NotNull JsExpression rangeEnd) { 187 return rangeTo(Namer.CHAR_RANGE, rangeStart, rangeEnd); 188 } 189 190 public static JsExpression newLong(long value, @NotNull TranslationContext context) { 191 if (value < Integer.MIN_VALUE || value > Integer.MAX_VALUE) { 192 int low = (int) value; 193 int high = (int) (value >> 32); 194 List<JsExpression> args = new SmartList<JsExpression>(); 195 args.add(context.program().getNumberLiteral(low)); 196 args.add(context.program().getNumberLiteral(high)); 197 return new JsNew(Namer.KOTLIN_LONG_NAME_REF, args); 198 } 199 else { 200 if (value == 0) { 201 return new JsNameRef(Namer.LONG_ZERO, Namer.KOTLIN_LONG_NAME_REF); 202 } 203 else if (value == 1) { 204 return new JsNameRef(Namer.LONG_ONE, Namer.KOTLIN_LONG_NAME_REF); 205 } 206 else if (value == -1) { 207 return new JsNameRef(Namer.LONG_NEG_ONE, Namer.KOTLIN_LONG_NAME_REF); 208 } 209 return longFromInt(context.program().getNumberLiteral((int) value)); 210 } 211 } 212 213 @NotNull 214 public static JsExpression longFromInt(@NotNull JsExpression expression) { 215 return invokeMethod(Namer.KOTLIN_LONG_NAME_REF, Namer.LONG_FROM_INT, expression); 216 } 217 218 @NotNull 219 public static JsExpression longFromNumber(@NotNull JsExpression expression) { 220 return invokeMethod(Namer.KOTLIN_LONG_NAME_REF, Namer.LONG_FROM_NUMBER, expression); 221 } 222 223 @NotNull 224 public static JsExpression equalsForObject(@NotNull JsExpression left, @NotNull JsExpression right) { 225 return invokeMethod(left, Namer.EQUALS_METHOD_NAME, right); 226 } 227 228 @NotNull 229 public static JsExpression compareForObject(@NotNull JsExpression left, @NotNull JsExpression right) { 230 return invokeMethod(left, Namer.COMPARE_TO_METHOD_NAME, right); 231 } 232 233 @NotNull 234 public static JsPrefixOperation negated(@NotNull JsExpression expression) { 235 return new JsPrefixOperation(JsUnaryOperator.NOT, expression); 236 } 237 238 @NotNull 239 public static JsBinaryOperation and(@NotNull JsExpression op1, @NotNull JsExpression op2) { 240 return new JsBinaryOperation(JsBinaryOperator.AND, op1, op2); 241 } 242 243 @NotNull 244 public static JsBinaryOperation or(@NotNull JsExpression op1, @NotNull JsExpression op2) { 245 return new JsBinaryOperation(JsBinaryOperator.OR, op1, op2); 246 } 247 248 public static void setQualifier(@NotNull JsExpression selector, @Nullable JsExpression receiver) { 249 assert (selector instanceof JsInvocation || selector instanceof JsNameRef); 250 if (selector instanceof JsInvocation) { 251 setQualifier(((JsInvocation) selector).getQualifier(), receiver); 252 return; 253 } 254 setQualifierForNameRef((JsNameRef) selector, receiver); 255 } 256 257 private static void setQualifierForNameRef(@NotNull JsNameRef selector, @Nullable JsExpression receiver) { 258 JsExpression qualifier = selector.getQualifier(); 259 if (qualifier == null) { 260 selector.setQualifier(receiver); 261 } 262 else { 263 setQualifier(qualifier, receiver); 264 } 265 } 266 267 @NotNull 268 public static JsBinaryOperation equality(@NotNull JsExpression arg1, @NotNull JsExpression arg2) { 269 return new JsBinaryOperation(JsBinaryOperator.REF_EQ, arg1, arg2); 270 } 271 272 @NotNull 273 public static JsBinaryOperation inequality(@NotNull JsExpression arg1, @NotNull JsExpression arg2) { 274 return new JsBinaryOperation(JsBinaryOperator.REF_NEQ, arg1, arg2); 275 } 276 277 @NotNull 278 public static JsBinaryOperation lessThanEq(@NotNull JsExpression arg1, @NotNull JsExpression arg2) { 279 return new JsBinaryOperation(JsBinaryOperator.LTE, arg1, arg2); 280 } 281 282 @NotNull 283 public static JsBinaryOperation lessThan(@NotNull JsExpression arg1, @NotNull JsExpression arg2) { 284 return new JsBinaryOperation(JsBinaryOperator.LT, arg1, arg2); 285 } 286 287 @NotNull 288 public static JsBinaryOperation greaterThan(@NotNull JsExpression arg1, @NotNull JsExpression arg2) { 289 return new JsBinaryOperation(JsBinaryOperator.GT, arg1, arg2); 290 } 291 292 @NotNull 293 public static JsBinaryOperation greaterThanEq(@NotNull JsExpression arg1, @NotNull JsExpression arg2) { 294 return new JsBinaryOperation(JsBinaryOperator.GTE, arg1, arg2); 295 } 296 297 @NotNull 298 public static JsExpression assignment(@NotNull JsExpression left, @NotNull JsExpression right) { 299 return new JsBinaryOperation(JsBinaryOperator.ASG, left, right); 300 } 301 302 @NotNull 303 public static JsBinaryOperation sum(@NotNull JsExpression left, @NotNull JsExpression right) { 304 return new JsBinaryOperation(JsBinaryOperator.ADD, left, right); 305 } 306 307 @NotNull 308 public static JsBinaryOperation addAssign(@NotNull JsExpression left, @NotNull JsExpression right) { 309 return new JsBinaryOperation(JsBinaryOperator.ASG_ADD, left, right); 310 } 311 312 @NotNull 313 public static JsBinaryOperation subtract(@NotNull JsExpression left, @NotNull JsExpression right) { 314 return new JsBinaryOperation(JsBinaryOperator.SUB, left, right); 315 } 316 317 @NotNull 318 public static JsBinaryOperation mul(@NotNull JsExpression left, @NotNull JsExpression right) { 319 return new JsBinaryOperation(JsBinaryOperator.MUL, left, right); 320 } 321 322 @NotNull 323 public static JsBinaryOperation div(@NotNull JsExpression left, @NotNull JsExpression right) { 324 return new JsBinaryOperation(JsBinaryOperator.DIV, left, right); 325 } 326 327 @NotNull 328 public static JsBinaryOperation mod(@NotNull JsExpression left, @NotNull JsExpression right) { 329 return new JsBinaryOperation(JsBinaryOperator.MOD, left, right); 330 } 331 332 @NotNull 333 public static JsPrefixOperation not(@NotNull JsExpression expression) { 334 return new JsPrefixOperation(JsUnaryOperator.NOT, expression); 335 } 336 337 @NotNull 338 public static JsBinaryOperation typeOfIs(@NotNull JsExpression expression, @NotNull JsStringLiteral string) { 339 return equality(new JsPrefixOperation(JsUnaryOperator.TYPEOF, expression), string); 340 } 341 342 @NotNull 343 public static JsVars newVar(@NotNull JsName name, @Nullable JsExpression expr) { 344 return new JsVars(new JsVars.JsVar(name, expr)); 345 } 346 347 public static void setArguments(@NotNull HasArguments invocation, @NotNull List<JsExpression> newArgs) { 348 List<JsExpression> arguments = invocation.getArguments(); 349 assert arguments.isEmpty() : "Arguments already set."; 350 arguments.addAll(newArgs); 351 } 352 353 public static void setArguments(@NotNull HasArguments invocation, JsExpression... arguments) { 354 setArguments(invocation, Arrays.asList(arguments)); 355 } 356 357 public static void setParameters(@NotNull JsFunction function, @NotNull List<JsParameter> newParams) { 358 List<JsParameter> parameters = function.getParameters(); 359 assert parameters.isEmpty() : "Arguments already set."; 360 parameters.addAll(newParams); 361 } 362 363 @NotNull 364 public static JsExpression newSequence(@NotNull List<JsExpression> expressions) { 365 assert !expressions.isEmpty(); 366 if (expressions.size() == 1) { 367 return expressions.get(0); 368 } 369 JsExpression result = expressions.get(expressions.size() - 1); 370 for (int i = expressions.size() - 2; i >= 0; i--) { 371 result = new JsBinaryOperation(JsBinaryOperator.COMMA, expressions.get(i), result); 372 } 373 return result; 374 } 375 376 @NotNull 377 public static JsFunction createFunctionWithEmptyBody(@NotNull JsScope parent) { 378 return new JsFunction(parent, new JsBlock(), "<anonymous>"); 379 } 380 381 @NotNull 382 public static List<JsExpression> toStringLiteralList(@NotNull List<String> strings, @NotNull JsProgram program) { 383 if (strings.isEmpty()) { 384 return Collections.emptyList(); 385 } 386 387 List<JsExpression> result = new SmartList<JsExpression>(); 388 for (String str : strings) { 389 result.add(program.getStringLiteral(str)); 390 } 391 return result; 392 } 393 394 @NotNull 395 public static JsInvocation defineProperty( 396 @NotNull String name, 397 @NotNull JsObjectLiteral value, 398 @NotNull TranslationContext context 399 ) { 400 return new JsInvocation(DEFINE_PROPERTY, JsLiteral.THIS, context.program().getStringLiteral(name), value); 401 } 402 403 @NotNull 404 public static JsStatement defineSimpleProperty(@NotNull String name, @NotNull JsExpression value) { 405 return assignment(new JsNameRef(name, JsLiteral.THIS), value).makeStmt(); 406 } 407 408 @NotNull 409 public static JsObjectLiteral createDataDescriptor(@NotNull JsExpression value, boolean writable, boolean enumerable) { 410 JsObjectLiteral dataDescriptor = new JsObjectLiteral(); 411 dataDescriptor.getPropertyInitializers().add(new JsPropertyInitializer(VALUE, value)); 412 if (writable) { 413 dataDescriptor.getPropertyInitializers().add(WRITABLE); 414 } 415 if (enumerable) { 416 dataDescriptor.getPropertyInitializers().add(ENUMERABLE); 417 } 418 return dataDescriptor; 419 } 420 421 @NotNull 422 public static JsFunction createPackage(@NotNull List<JsStatement> to, @NotNull JsObjectScope scope) { 423 JsFunction packageBlockFunction = createFunctionWithEmptyBody(scope); 424 425 JsName kotlinObjectAsParameter = packageBlockFunction.getScope().declareNameUnsafe(Namer.KOTLIN_NAME); 426 packageBlockFunction.getParameters().add(new JsParameter(kotlinObjectAsParameter)); 427 428 to.add(new JsInvocation(packageBlockFunction, Namer.KOTLIN_OBJECT_REF).makeStmt()); 429 430 return packageBlockFunction; 431 } 432 433 @NotNull 434 public static JsObjectLiteral wrapValue(@NotNull JsExpression label, @NotNull JsExpression value) { 435 return new JsObjectLiteral(Collections.singletonList(new JsPropertyInitializer(label, value))); 436 } 437 438 public static JsExpression replaceRootReference(@NotNull JsNameRef fullQualifier, @NotNull JsExpression newQualifier) { 439 if (fullQualifier.getQualifier() == null) { 440 assert Namer.getRootPackageName().equals(fullQualifier.getIdent()) : "Expected root package, but: " + fullQualifier.getIdent(); 441 return newQualifier; 442 } 443 444 fullQualifier = fullQualifier.deepCopy(); 445 JsNameRef qualifier = fullQualifier; 446 while (true) { 447 JsExpression parent = qualifier.getQualifier(); 448 assert parent instanceof JsNameRef : "unexpected qualifier: " + parent + ", original: " + fullQualifier; 449 if (((JsNameRef) parent).getQualifier() == null) { 450 assert Namer.getRootPackageName().equals(((JsNameRef) parent).getIdent()); 451 qualifier.setQualifier(newQualifier); 452 return fullQualifier; 453 } 454 qualifier = (JsNameRef) parent; 455 } 456 } 457 458 @NotNull 459 public static List<JsStatement> flattenStatement(@NotNull JsStatement statement) { 460 if (statement instanceof JsBlock) { 461 return ((JsBlock) statement).getStatements(); 462 } 463 464 return new SmartList<JsStatement>(statement); 465 } 466 }