JS backend: add support multi declaration

This commit is contained in:
Erokhin Stanislav
2013-08-16 22:13:16 +04:00
parent 8cc1617790
commit 45e827aeb9
15 changed files with 414 additions and 27 deletions
@@ -0,0 +1,56 @@
/*
* Copyright 2010-2013 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.k2js.test.semantics;
import org.jetbrains.k2js.test.SingleFileTranslationTest;
public class MultiDeclarationTest extends SingleFileTranslationTest {
public MultiDeclarationTest() {
super("multideclaration/");
}
// TODO: Add support Map.Entry
public void TestMultiValForMap() throws Exception {
checkFooBoxIsOk();
}
public void testMultiValOrVar() throws Exception {
checkFooBoxIsOk();
}
public void testMultiValInFor() throws Exception {
checkFooBoxIsOk();
}
public void testMultiValInIntFor() throws Exception {
checkFooBoxIsOk();
}
public void testMultiValInIntRangeFor() throws Exception {
checkFooBoxIsOk();
}
public void testMultiValForArray() throws Exception {
checkFooBoxIsOk();
}
// TODO: Add support ranges for user types
public void TestMultiValForRange() throws Exception {
checkFooBoxIsOk();
}
}
@@ -115,6 +115,14 @@ public final class ExpressionVisitor extends TranslatorVisitor<JsNode> {
return source(jsBlock, jetBlock);
}
@Override
public JsNode visitMultiDeclaration(@NotNull JetMultiDeclaration multiDeclaration, @NotNull TranslationContext context) {
JetExpression jetInitializer = multiDeclaration.getInitializer();
assert jetInitializer != null : "Initializer for multi declaration must be not null";
JsExpression initializer = Translation.translateAsExpression(jetInitializer, context);
return MultiDeclarationTranslator.translate(multiDeclaration, context.scope().declareTemporary(), initializer, context);
}
@Override
@NotNull
public JsNode visitReturnExpression(@NotNull JetReturnExpression jetReturnExpression,
@@ -0,0 +1,92 @@
/*
* Copyright 2010-2013 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.k2js.translate.expression;
import com.google.dart.compiler.backend.js.ast.JsExpression;
import com.google.dart.compiler.backend.js.ast.JsName;
import com.google.dart.compiler.backend.js.ast.JsNameRef;
import com.google.dart.compiler.backend.js.ast.JsVars;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.jet.lang.descriptors.FunctionDescriptor;
import org.jetbrains.jet.lang.descriptors.VariableDescriptor;
import org.jetbrains.jet.lang.psi.JetMultiDeclaration;
import org.jetbrains.jet.lang.psi.JetMultiDeclarationEntry;
import org.jetbrains.jet.lang.resolve.BindingContext;
import org.jetbrains.jet.lang.resolve.BindingContextUtils;
import org.jetbrains.jet.lang.resolve.calls.model.ResolvedCall;
import org.jetbrains.k2js.translate.context.TranslationContext;
import org.jetbrains.k2js.translate.general.AbstractTranslator;
import org.jetbrains.k2js.translate.reference.CallBuilder;
import java.util.ArrayList;
import java.util.List;
public class MultiDeclarationTranslator extends AbstractTranslator {
// if multiObjectName was init, initializer must be null
@NotNull
public static JsVars translate(
@NotNull JetMultiDeclaration multiDeclaration,
@NotNull JsName multiObjectName,
@Nullable JsExpression initializer,
@NotNull TranslationContext context
) {
return new MultiDeclarationTranslator(multiDeclaration, multiObjectName, initializer, context).translate();
}
@NotNull
private final JetMultiDeclaration multiDeclaration;
@NotNull
private final JsName multiObjectName;
@Nullable
private final JsExpression initializer;
private MultiDeclarationTranslator(
@NotNull JetMultiDeclaration multiDeclaration,
@NotNull JsName multiObjectName,
@Nullable JsExpression initializer,
@NotNull TranslationContext context
) {
super(context);
this.multiDeclaration = multiDeclaration;
this.multiObjectName = multiObjectName;
this.initializer = initializer;
}
private JsVars translate() {
List<JsVars.JsVar> jsVars = new ArrayList<JsVars.JsVar>();
if (initializer != null) {
jsVars.add(new JsVars.JsVar(multiObjectName, initializer));
}
JsNameRef multiObjNameRef = multiObjectName.makeRef();
for (JetMultiDeclarationEntry entry : multiDeclaration.getEntries()) {
ResolvedCall<FunctionDescriptor> entryInitCall = context().bindingContext().get(BindingContext.COMPONENT_RESOLVED_CALL, entry);
assert entryInitCall != null : "Entry init call must be not null";
JsExpression entryInitializer = CallBuilder.build(context())
.receiver(multiObjNameRef)
.resolvedCall(entryInitCall)
.translate();
VariableDescriptor descriptor = BindingContextUtils.getNotNull( context().bindingContext(), BindingContext.VARIABLE, entry);
JsName name = context().getNameForDescriptor(descriptor);
jsVars.add(new JsVars.JsVar(name, entryInitializer));
}
return new JsVars(jsVars, true);
}
}
@@ -86,9 +86,7 @@ public final class ArrayForTranslator extends ForTranslator {
@NotNull
private JsStatement getBody() {
JsArrayAccess arrayAccess = new JsArrayAccess(loopRange.reference(), index.reference());
JsStatement currentVar = newVar(parameterName, arrayAccess);
JsStatement realBody = translateOriginalBodyExpression();
return new JsBlock(currentVar, realBody);
return translateBody(arrayAccess);
}
@NotNull
@@ -21,8 +21,12 @@ import com.google.dart.compiler.backend.js.ast.JsExpression;
import com.google.dart.compiler.backend.js.ast.JsName;
import com.google.dart.compiler.backend.js.ast.JsStatement;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.jet.lang.psi.JetForExpression;
import org.jetbrains.jet.lang.psi.JetMultiDeclaration;
import org.jetbrains.jet.lang.psi.JetParameter;
import org.jetbrains.k2js.translate.context.TranslationContext;
import org.jetbrains.k2js.translate.expression.MultiDeclarationTranslator;
import org.jetbrains.k2js.translate.general.AbstractTranslator;
import org.jetbrains.k2js.translate.general.Translation;
@@ -51,34 +55,54 @@ public abstract class ForTranslator extends AbstractTranslator {
protected final JetForExpression expression;
@NotNull
protected final JsName parameterName;
@Nullable
protected final JetMultiDeclaration multiParameter;
protected ForTranslator(@NotNull JetForExpression forExpression, @NotNull TranslationContext context) {
super(context);
this.expression = forExpression;
this.multiParameter = forExpression.getMultiParameter();
this.parameterName = declareParameter();
}
@NotNull
private JsName declareParameter() {
return context().getNameForElement(getLoopParameter(expression));
}
@NotNull
protected JsStatement translateOriginalBodyExpression() {
return Translation.translateAsStatement(getLoopBody(expression), context());
}
@NotNull
protected JsStatement translateBody(JsExpression itemValue) {
JsStatement currentVar = newVar(parameterName, itemValue);
JsStatement realBody = translateOriginalBodyExpression();
if (realBody instanceof JsBlock) {
JsBlock block = (JsBlock) realBody;
block.getStatements().add(0, currentVar);
return block;
JetParameter loopParameter = getLoopParameter(expression);
if (loopParameter != null) {
return context().getNameForElement(loopParameter);
}
else {
return new JsBlock(currentVar, realBody);
assert parameterIsMultiDeclaration() : "If loopParameter is null, multi parameter must be not null";
return context().scope().declareTemporary();
}
private boolean parameterIsMultiDeclaration() {
return multiParameter != null;
}
@NotNull
private JsStatement makeCurrentVarInit(@Nullable JsExpression itemValue) {
if (multiParameter == null) {
return newVar(parameterName, itemValue);
} else {
return MultiDeclarationTranslator.translate(multiParameter, parameterName, itemValue, context());
}
}
@NotNull
protected JsStatement translateBody(@Nullable JsExpression itemValue) {
JsStatement realBody = Translation.translateAsStatement(getLoopBody(expression), context());
if (itemValue == null && !parameterIsMultiDeclaration()) {
return realBody;
} else {
JsStatement currentVarInit = makeCurrentVarInit(itemValue);
if (realBody instanceof JsBlock) {
JsBlock block = (JsBlock) realBody;
block.getStatements().add(0, currentVarInit);
return block;
}
else {
return new JsBlock(currentVarInit, realBody);
}
}
}
}
@@ -79,7 +79,7 @@ public final class RangeForTranslator extends ForTranslator {
@NotNull
private JsFor generateForExpression() {
JsFor result = new JsFor(initExpression(), getCondition(), getIncrExpression());
result.setBody(translateOriginalBodyExpression());
result.setBody(translateBody(null));
return result;
}
@@ -82,7 +82,7 @@ public final class RangeLiteralForTranslator extends ForTranslator {
blockStatements.add(generateForExpression(initExpression(),
getCondition(),
getIncrExpression(),
translateOriginalBodyExpression()));
translateBody(null)));
return new JsBlock(blockStatements);
}
@@ -108,11 +108,9 @@ public final class PsiUtils {
return body;
}
@NotNull
@Nullable
public static JetParameter getLoopParameter(@NotNull JetForExpression expression) {
JetParameter loopParameter = expression.getLoopParameter();
assert loopParameter != null;
return loopParameter;
return expression.getLoopParameter();
}
@NotNull
@@ -0,0 +1,21 @@
package foo
class A {
fun component1() : Int = 1
}
fun A.component2() : String = "n"
fun box(): String {
val list = Array<A>(1, {A()})
var i = 0;
var s = ""
for ((a,b) in list) {
i = a;
s = b;
}
if (i != 1) return "i != 1, it: " + i
if (s != "n") return "s != 'n', it: " + s
return "OK"
}
@@ -0,0 +1,40 @@
package foo
import java.util.HashMap
public inline fun <K,V> Map<K,V>.iterator(): Iterator<Map.Entry<K,V>> {
val entrySet = this.entrySet()
return entrySet.iterator()
}
/** Returns the key of the entry */
fun <K,V> Map.Entry<K,V>.component1() : K {
return getKey()
}
/** Returns the value of the entry */
fun <K,V> Map.Entry<K,V>.component2() : V {
return getValue()
}
fun box(): String {
val map = HashMap<Int, String>()
map.put(1, "s1")
map.put(2, "s2")
var s1 = ""
var s2 = ""
for ((k,v) in map) {
when (k) {
2 -> s2 = v
1 -> s1 = v
else -> {}
}
}
if (s1 != "s1") return "s1 != 's1', it: ${s1}"
if (s2 != "s2") return "s2 != 's2', it: ${s2}"
return "OK"
}
@@ -0,0 +1,41 @@
package foo
class C(val i: Int): Comparable<C>, A() {
public override fun compareTo(other: C): Int {
return if (other is C) other.i - i else 0
}
}
fun ComparableRange<C>.iterator() : Iterator<C> {
var curI: Int = start.i - 1
return object :Iterator<C> {
public override fun next(): C {
curI++
return C(curI)
}
public override fun hasNext(): Boolean {
return curI < end.i
}
}
}
open class A {
fun component1() : Int = 1
}
fun A.component2() : String = "n"
fun box(): String {
var i = 0;
var s = ""
for ((a,b) in C(0)..C(2)) {
i = a;
s = b;
}
if (i != 1) return "i != 1, it: " + i
if (s != "n") return "s != 'n', it: " + s
return "OK"
}
@@ -0,0 +1,25 @@
package foo
import java.util.ArrayList
class A {
fun component1() : Int = 1
}
fun A.component2() : String = "n"
fun box(): String {
val list = ArrayList<A>()
list.add(A())
var i = 0;
var s = ""
for ((a,b) in list) {
i = a;
s = b;
}
if (i != 1) return "i != 1, it: " + i
if (s != "n") return "s != 'n', it: " + s
return "OK"
}
@@ -0,0 +1,21 @@
package foo
fun Int.component1() : Int {
return this + 10;
}
fun Int.component2() : String = "b"
fun box(): String {
var i = 0;
var s = "";
for ((a, b) in 1..4) {
i = a
s = b
}
if (i != 14) return "i != 14, it: $i"
if (s != "b") return "s != 'b', it: $s"
return "OK"
}
@@ -0,0 +1,22 @@
package foo
fun Int.component1() : Int {
return this + 10;
}
fun Int.component2() : String = "b"
fun box(): String {
var i = 0;
var s = "";
val range = 1..4
for ((a, b) in range) {
i = a
s = b
}
if (i != 14) return "i != 14, it: $i"
if (s != "b") return "s != 'b', it: $s"
return "OK"
}
@@ -0,0 +1,41 @@
package foo
class A {
fun component1(): Int = 1
}
fun A.component2(): String = "n"
class B {
fun component1(): Int = 1
fun component2(): Int = 2
fun component3(): Int = 3
}
class C {
fun component1(): Int = 42
}
fun box(): String {
val (a, b) = A()
if (a != 1) return "a != 1, it: $a"
if (b != "n") return "b != 'n', it: $b"
var (x, y) = A()
if (x != 1) return "x != 1, it: $x"
if (y != "n") return "y != 'n', it: $y"
x = 3
if (x != 3) return "x != 3, it: $x"
y = "m"
if (y != "m") return "y != 'm', it: $y"
var (b1, b2, b3) = B()
if (b1 != 1) return "b1 != 1, it: $b1"
if (b2 != 2) return "b2 != 2, it: $b2"
if (b3 != 3) return "b3 != 3, it: $b3"
val (c) = C()
if (c != 42) return "c != 42, it: $c"
return "OK"
}