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.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.k2js.translate.context.Namer; 024 import org.jetbrains.k2js.translate.context.TranslationContext; 025 026 import java.util.Arrays; 027 import java.util.Collections; 028 import java.util.List; 029 030 public final class JsAstUtils { 031 private static final JsNameRef DEFINE_PROPERTY = new JsNameRef("defineProperty"); 032 public static final JsNameRef CREATE_OBJECT = new JsNameRef("create"); 033 private static final JsNameRef EMPTY_REF = new JsNameRef(""); 034 035 private static final JsNameRef VALUE = new JsNameRef("value"); 036 private static final JsPropertyInitializer WRITABLE = new JsPropertyInitializer(new JsNameRef("writable"), JsLiteral.TRUE); 037 private static final JsPropertyInitializer ENUMERABLE = new JsPropertyInitializer(new JsNameRef("enumerable"), JsLiteral.TRUE); 038 039 public static final String LENDS_JS_DOC_TAG = "lends"; 040 041 static { 042 JsNameRef globalObjectReference = new JsNameRef("Object"); 043 DEFINE_PROPERTY.setQualifier(globalObjectReference); 044 CREATE_OBJECT.setQualifier(globalObjectReference); 045 } 046 047 private JsAstUtils() { 048 } 049 050 @NotNull 051 public static JsStatement convertToStatement(@NotNull JsNode jsNode) { 052 assert (jsNode instanceof JsExpression) || (jsNode instanceof JsStatement) 053 : "Unexpected node of type: " + jsNode.getClass().toString(); 054 if (jsNode instanceof JsExpression) { 055 return ((JsExpression) jsNode).makeStmt(); 056 } 057 return (JsStatement) jsNode; 058 } 059 060 @NotNull 061 public static JsBlock convertToBlock(@NotNull JsNode jsNode) { 062 if (jsNode instanceof JsBlock) { 063 return (JsBlock) jsNode; 064 } 065 return new JsBlock(convertToStatement(jsNode)); 066 } 067 068 @NotNull 069 public static JsExpression convertToExpression(@NotNull JsNode jsNode) { 070 assert jsNode instanceof JsExpression : "Unexpected node of type: " + jsNode.getClass().toString(); 071 return (JsExpression) jsNode; 072 } 073 074 @NotNull 075 public static JsPrefixOperation negated(@NotNull JsExpression expression) { 076 return new JsPrefixOperation(JsUnaryOperator.NOT, expression); 077 } 078 079 @NotNull 080 public static JsBinaryOperation and(@NotNull JsExpression op1, @NotNull JsExpression op2) { 081 return new JsBinaryOperation(JsBinaryOperator.AND, op1, op2); 082 } 083 084 @NotNull 085 public static JsBinaryOperation or(@NotNull JsExpression op1, @NotNull JsExpression op2) { 086 return new JsBinaryOperation(JsBinaryOperator.OR, op1, op2); 087 } 088 089 public static void setQualifier(@NotNull JsExpression selector, @Nullable JsExpression receiver) { 090 assert (selector instanceof JsInvocation || selector instanceof JsNameRef); 091 if (selector instanceof JsInvocation) { 092 setQualifier(((JsInvocation) selector).getQualifier(), receiver); 093 return; 094 } 095 setQualifierForNameRef((JsNameRef) selector, receiver); 096 } 097 098 private static void setQualifierForNameRef(@NotNull JsNameRef selector, @Nullable JsExpression receiver) { 099 JsExpression qualifier = selector.getQualifier(); 100 if (qualifier == null) { 101 selector.setQualifier(receiver); 102 } 103 else { 104 setQualifier(qualifier, receiver); 105 } 106 } 107 108 @NotNull 109 public static JsBinaryOperation equality(@NotNull JsExpression arg1, @NotNull JsExpression arg2) { 110 return new JsBinaryOperation(JsBinaryOperator.REF_EQ, arg1, arg2); 111 } 112 113 @NotNull 114 public static JsBinaryOperation inequality(@NotNull JsExpression arg1, @NotNull JsExpression arg2) { 115 return new JsBinaryOperation(JsBinaryOperator.REF_NEQ, arg1, arg2); 116 } 117 118 @NotNull 119 public static JsBinaryOperation lessThanEq(@NotNull JsExpression arg1, @NotNull JsExpression arg2) { 120 return new JsBinaryOperation(JsBinaryOperator.LTE, arg1, arg2); 121 } 122 123 @NotNull 124 public static JsExpression assignment(@NotNull JsExpression left, @NotNull JsExpression right) { 125 return new JsBinaryOperation(JsBinaryOperator.ASG, left, right); 126 } 127 128 @NotNull 129 public static JsBinaryOperation sum(@NotNull JsExpression left, @NotNull JsExpression right) { 130 return new JsBinaryOperation(JsBinaryOperator.ADD, left, right); 131 } 132 133 @NotNull 134 public static JsBinaryOperation addAssign(@NotNull JsExpression left, @NotNull JsExpression right) { 135 return new JsBinaryOperation(JsBinaryOperator.ASG_ADD, left, right); 136 } 137 138 @NotNull 139 public static JsBinaryOperation subtract(@NotNull JsExpression left, @NotNull JsExpression right) { 140 return new JsBinaryOperation(JsBinaryOperator.SUB, left, right); 141 } 142 143 @NotNull 144 public static JsPrefixOperation not(@NotNull JsExpression expression) { 145 return new JsPrefixOperation(JsUnaryOperator.NOT, expression); 146 } 147 148 @NotNull 149 public static JsBinaryOperation typeof(@NotNull JsExpression expression, @NotNull JsStringLiteral string) { 150 return equality(new JsPrefixOperation(JsUnaryOperator.TYPEOF, expression), string); 151 } 152 153 @NotNull 154 public static JsVars newVar(@NotNull JsName name, @Nullable JsExpression expr) { 155 return new JsVars(new JsVars.JsVar(name, expr)); 156 } 157 158 public static void setArguments(@NotNull HasArguments invocation, @NotNull List<JsExpression> newArgs) { 159 List<JsExpression> arguments = invocation.getArguments(); 160 assert arguments.isEmpty() : "Arguments already set."; 161 arguments.addAll(newArgs); 162 } 163 164 public static void setArguments(@NotNull HasArguments invocation, JsExpression... arguments) { 165 setArguments(invocation, Arrays.asList(arguments)); 166 } 167 168 public static void setParameters(@NotNull JsFunction function, @NotNull List<JsParameter> newParams) { 169 List<JsParameter> parameters = function.getParameters(); 170 assert parameters.isEmpty() : "Arguments already set."; 171 parameters.addAll(newParams); 172 } 173 174 @NotNull 175 public static JsExpression newSequence(@NotNull List<JsExpression> expressions) { 176 assert !expressions.isEmpty(); 177 if (expressions.size() == 1) { 178 return expressions.get(0); 179 } 180 JsExpression result = expressions.get(expressions.size() - 1); 181 for (int i = expressions.size() - 2; i >= 0; i--) { 182 result = new JsBinaryOperation(JsBinaryOperator.COMMA, expressions.get(i), result); 183 } 184 return result; 185 } 186 187 @NotNull 188 public static JsFunction createFunctionWithEmptyBody(@NotNull JsScope parent) { 189 return new JsFunction(parent, new JsBlock()); 190 } 191 192 @NotNull 193 public static List<JsExpression> toStringLiteralList(@NotNull List<String> strings, @NotNull JsProgram program) { 194 if (strings.isEmpty()) { 195 return Collections.emptyList(); 196 } 197 198 List<JsExpression> result = new SmartList<JsExpression>(); 199 for (String str : strings) { 200 result.add(program.getStringLiteral(str)); 201 } 202 return result; 203 } 204 205 @NotNull 206 public static JsInvocation defineProperty( 207 @NotNull String name, 208 @NotNull JsObjectLiteral value, 209 @NotNull TranslationContext context 210 ) { 211 return new JsInvocation(DEFINE_PROPERTY, JsLiteral.THIS, context.program().getStringLiteral(name), value); 212 } 213 214 @NotNull 215 public static JsStatement defineSimpleProperty(@NotNull String name, @NotNull JsExpression value) { 216 return assignment(new JsNameRef(name, JsLiteral.THIS), value).makeStmt(); 217 } 218 219 @NotNull 220 public static JsObjectLiteral createDataDescriptor(@NotNull JsExpression value, boolean writable, boolean enumerable) { 221 JsObjectLiteral dataDescriptor = new JsObjectLiteral(); 222 dataDescriptor.getPropertyInitializers().add(new JsPropertyInitializer(VALUE, value)); 223 if (writable) { 224 dataDescriptor.getPropertyInitializers().add(WRITABLE); 225 } 226 if (enumerable) { 227 dataDescriptor.getPropertyInitializers().add(ENUMERABLE); 228 } 229 return dataDescriptor; 230 } 231 232 @NotNull 233 public static JsFunction createPackage(@NotNull List<JsStatement> to, @NotNull JsScope scope) { 234 JsFunction packageBlockFunction = createFunctionWithEmptyBody(scope); 235 to.add(new JsInvocation(EMPTY_REF, new JsInvocation(packageBlockFunction)).makeStmt()); 236 return packageBlockFunction; 237 } 238 239 @NotNull 240 public static JsObjectLiteral wrapValue(@NotNull JsExpression label, @NotNull JsExpression value) { 241 return new JsObjectLiteral(Collections.singletonList(new JsPropertyInitializer(label, value))); 242 } 243 244 public static void replaceRootReference(@NotNull JsNameRef fullQualifier, @NotNull JsExpression newQualifier) { 245 JsNameRef qualifier = fullQualifier; 246 while (true) { 247 JsExpression parent = qualifier.getQualifier(); 248 assert parent instanceof JsNameRef : "unexpected qualifier: " + parent + ", original: " + fullQualifier; 249 if (((JsNameRef) parent).getQualifier() == null) { 250 assert Namer.getRootPackageName().equals(((JsNameRef) parent).getIdent()); 251 qualifier.setQualifier(newQualifier); 252 return; 253 } 254 qualifier = (JsNameRef) parent; 255 } 256 } 257 }