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.dangerous;
018
019 import com.google.common.collect.Lists;
020 import org.jetbrains.annotations.NotNull;
021 import org.jetbrains.annotations.Nullable;
022 import org.jetbrains.jet.lang.psi.JetBlockExpression;
023 import org.jetbrains.jet.lang.psi.JetElement;
024 import org.jetbrains.jet.lang.psi.JetExpression;
025 import org.jetbrains.k2js.translate.context.TranslationContext;
026
027 import java.util.List;
028
029 /**
030 * This module uses a methaphor for naming.
031 * <p/>
032 * Dangerous are the nodes that can be expressions in Kotlin but can't be expressions in JavaScript.
033 * These are: when, if, inlined functions.
034 * The issue with them is that we have to translate them to a list of statements. And also all the expressions which must be computed before
035 * the dangerous expressions.
036 * RootNode is a node which contains such an expression. For example, it may be a statement expression belongs to.
037 */
038 public class DangerousData {
039 @NotNull
040 private final List<JetExpression> nodesToBeGeneratedBefore = Lists.newArrayList();
041
042
043 @NotNull
044 public static DangerousData collect(@NotNull JetExpression expression, @NotNull TranslationContext context) {
045 if (cantContainDangerousElements(expression)) {
046 return emptyData();
047 }
048 return doCollectData(expression, context);
049 }
050
051 private static boolean cantContainDangerousElements(@NotNull JetElement element) {
052 return element instanceof JetBlockExpression;
053 }
054
055 @NotNull
056 private static DangerousData doCollectData(@NotNull JetExpression expression,
057 @NotNull TranslationContext context) {
058 DangerousData data = new DangerousData();
059 FindDangerousVisitor visitor = new FindDangerousVisitor(context);
060 expression.accept(visitor, data);
061 if (!data.exists()) {
062 return emptyData();
063 }
064 data.setRootNode(expression);
065 FindPreviousVisitor findPreviousVisitor = new FindPreviousVisitor(data);
066 expression.accept(findPreviousVisitor, data);
067 return data;
068 }
069
070 private static final DangerousData EMPTY = new DangerousData() {
071 @Override
072 public boolean exists() {
073 return false;
074 }
075
076 @Override
077 public boolean shouldBeTranslated() {
078 return false;
079 }
080 };
081
082 @NotNull
083 public static DangerousData emptyData() {
084 return EMPTY;
085 }
086
087 @Nullable
088 private JetExpression dangerousNode = null;
089
090 @Nullable
091 private JetExpression rootNode = null;
092
093 public void setDangerousNode(@NotNull JetExpression dangerousNode) {
094 assert this.dangerousNode == null : "Should be assigned only once";
095 this.dangerousNode = dangerousNode;
096 }
097
098 @NotNull
099 public List<JetExpression> getNodesToBeGeneratedBefore() {
100 return nodesToBeGeneratedBefore;
101 }
102
103 public boolean exists() {
104 return dangerousNode != null;
105 }
106
107 public boolean shouldBeTranslated() {
108 return exists() && !nodesToBeGeneratedBefore.isEmpty();
109 }
110
111 @NotNull
112 public JetExpression getDangerousNode() {
113 assert dangerousNode != null;
114 return dangerousNode;
115 }
116
117 @NotNull
118 public JetExpression getRootNode() {
119 assert rootNode != null;
120 return rootNode;
121 }
122
123 @SuppressWarnings("NullableProblems")
124 public void setRootNode(@NotNull JetExpression rootNode) {
125 this.rootNode = rootNode;
126 }
127 }