Very big code migration started. Still in progress. Tests fail.

This commit is contained in:
Pavel Talanov
2012-01-20 22:58:25 +04:00
parent 04eb707744
commit de75cfd007
32 changed files with 2510 additions and 857 deletions
+458 -662
View File
File diff suppressed because it is too large Load Diff
@@ -7,47 +7,66 @@ import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import java.util.Stack;
//TODO: code gets duplicated
public class Aliaser { public class Aliaser {
public static Aliaser newInstance() { public static Aliaser newInstance() {
return new Aliaser(); return new Aliaser();
} }
//TODO: rename
@NotNull @NotNull
private final Map<DeclarationDescriptor, JsName> aliasesForDescriptors = new HashMap<DeclarationDescriptor, JsName>(); private final Map<DeclarationDescriptor, JsName> aliasesForDescriptors = new HashMap<DeclarationDescriptor, JsName>();
@NotNull @NotNull
private final Stack<JsName> thisAliases = new Stack<JsName>(); private final Map<DeclarationDescriptor, JsName> aliasesForThis = new HashMap<DeclarationDescriptor, JsName>();
@NotNull
private final Map<DeclarationDescriptor, JsName> aliasesForReceiver = new HashMap<DeclarationDescriptor, JsName>();
private Aliaser() { private Aliaser() {
} }
@NotNull @NotNull
public JsNameRef getAliasForThis() { public JsNameRef getAliasForThis(@NotNull DeclarationDescriptor descriptor) {
assert !thisAliases.empty() : "No alias. Use hasAliasForThis function to check."; JsName aliasName = aliasesForThis.get(descriptor.getOriginal());
return thisAliases.peek().makeRef(); assert aliasName != null : "This " + descriptor.getOriginal() + " doesn't have an alias.";
return aliasName.makeRef();
} }
public void setAliasForThis(@NotNull JsName alias) { public void setAliasForThis(@NotNull DeclarationDescriptor descriptor, @NotNull JsName alias) {
thisAliases.push(alias); aliasesForThis.put(descriptor.getOriginal(), alias);
} }
//NOTE: here we are passing alias to check that they are set and removed in consistent order. public void removeAliasForThis(@NotNull DeclarationDescriptor descriptor) {
public void removeAliasForThis(@NotNull JsName aliasToRemove) { JsName removed = aliasesForThis.remove(descriptor.getOriginal());
assert !thisAliases.empty() : "Error: removing alias, when it is not set."; assert removed != null;
JsName lastAlias = thisAliases.pop();
assert lastAlias.equals(aliasToRemove) : "Error: inconsistent alias removing.";
} }
public boolean hasAliasForThis() { public boolean hasAliasForThis(@NotNull DeclarationDescriptor descriptor) {
return (!thisAliases.empty()); return (aliasesForThis.containsKey(descriptor.getOriginal()));
} }
public boolean hasAliasForDeclaration(@NotNull DeclarationDescriptor declaration) { @NotNull
return aliasesForDescriptors.containsKey(declaration.getOriginal()); public JsNameRef getAliasForReceiver(@NotNull DeclarationDescriptor descriptor) {
JsName aliasName = aliasesForReceiver.get(descriptor.getOriginal());
assert aliasName != null : "This descriptor doesn't have an alias.";
return aliasName.makeRef();
} }
public void setAliasForReceiver(@NotNull DeclarationDescriptor descriptor, @NotNull JsName alias) {
aliasesForReceiver.put(descriptor.getOriginal(), alias);
}
public void removeAliasForReceiver(@NotNull DeclarationDescriptor descriptor) {
JsName removed = aliasesForReceiver.remove(descriptor.getOriginal());
assert removed != null;
}
public boolean hasAliasForReceiver(@NotNull DeclarationDescriptor descriptor) {
return (aliasesForReceiver.containsKey(descriptor.getOriginal()));
}
@NotNull @NotNull
public JsName getAliasForDeclaration(@NotNull DeclarationDescriptor declaration) { public JsName getAliasForDeclaration(@NotNull DeclarationDescriptor declaration) {
JsName alias = aliasesForDescriptors.get(declaration.getOriginal()); JsName alias = aliasesForDescriptors.get(declaration.getOriginal());
@@ -64,4 +83,10 @@ public class Aliaser {
assert (hasAliasForDeclaration(declaration.getOriginal())) : "This declaration does not has an alias!"; assert (hasAliasForDeclaration(declaration.getOriginal())) : "This declaration does not has an alias!";
aliasesForDescriptors.remove(declaration.getOriginal()); aliasesForDescriptors.remove(declaration.getOriginal());
} }
public boolean hasAliasForDeclaration(@NotNull DeclarationDescriptor declaration) {
return aliasesForDescriptors.containsKey(declaration.getOriginal());
}
} }
@@ -32,11 +32,44 @@ public final class StandardClasses {
declareRange(standardClasses); declareRange(standardClasses);
declareString(standardClasses); declareString(standardClasses);
declareJavaArrayList(standardClasses); declareJavaArrayList(standardClasses);
declareJavaHasMap(standardClasses);
declareJavaStringBuilder(standardClasses);
declareTopLevelFunctions(standardClasses); declareTopLevelFunctions(standardClasses);
declareJavaCollection(standardClasses);
declareMap(standardClasses);
declareInteger(standardClasses); declareInteger(standardClasses);
return standardClasses; return standardClasses;
} }
private static void declareMap(@NotNull StandardClasses standardClasses) {
String hashMapFQName = "java.util.Map";
standardClasses.declareStandardTopLevelObject(hashMapFQName, "Map");
standardClasses.declareStandardInnerDeclaration(hashMapFQName, "<init>", "HashMap");
declareMethods(standardClasses, hashMapFQName, "size", "put", "get",
"isEmpty", "remove", "addAll", "clear", "keySet");
}
private static void declareJavaCollection(@NotNull StandardClasses standardClasses) {
String collection = "java.util.Collection";
standardClasses.declareStandardTopLevelObject(collection, "Collection");
declareMethods(standardClasses, collection, "iterator");
}
private static void declareJavaStringBuilder(@NotNull StandardClasses standardClasses) {
String stringBuilderFQName = "java.util.StringBuilder";
standardClasses.declareStandardTopLevelObject(stringBuilderFQName, "StringBuilder");
standardClasses.declareStandardInnerDeclaration(stringBuilderFQName, "<init>", "StringBuilder");
declareMethods(standardClasses, stringBuilderFQName, "append", "toString");
}
private static void declareJavaHasMap(@NotNull StandardClasses standardClasses) {
String hashMapFQName = "java.util.HashMap";
standardClasses.declareStandardTopLevelObject(hashMapFQName, "HashMap");
standardClasses.declareStandardInnerDeclaration(hashMapFQName, "<init>", "HashMap");
declareMethods(standardClasses, hashMapFQName, "size", "put", "get",
"isEmpty", "remove", "addAll", "clear", "keySet");
}
//TODO: refactor //TODO: refactor
private static void declareTopLevelFunctions(@NotNull StandardClasses standardClasses) { private static void declareTopLevelFunctions(@NotNull StandardClasses standardClasses) {
String parseIntFQName = "js.parseInt"; String parseIntFQName = "js.parseInt";
@@ -12,6 +12,7 @@ import org.jetbrains.jet.lang.psi.JetPropertyAccessor;
import org.jetbrains.jet.lang.resolve.BindingContext; import org.jetbrains.jet.lang.resolve.BindingContext;
import org.jetbrains.k2js.translate.intrinsic.Intrinsics; import org.jetbrains.k2js.translate.intrinsic.Intrinsics;
import org.jetbrains.k2js.translate.utils.BindingUtils; import org.jetbrains.k2js.translate.utils.BindingUtils;
import org.jetbrains.k2js.translate.utils.TranslationUtils;
import static org.jetbrains.jet.lang.resolve.DescriptorUtils.getFQName; import static org.jetbrains.jet.lang.resolve.DescriptorUtils.getFQName;
@@ -165,14 +166,15 @@ public final class TranslationContext {
} }
@NotNull @NotNull
public TemporaryVariable newAliasForThis() { public TemporaryVariable newAliasForThis(@NotNull DeclarationDescriptor descriptor) {
TemporaryVariable aliasForThis = dynamicContext.declareTemporary(new JsThisRef()); JsExpression thisQualifier = TranslationUtils.getThisQualifier(this, descriptor);
aliaser().setAliasForThis(aliasForThis.name()); TemporaryVariable aliasForThis = dynamicContext.declareTemporary(thisQualifier);
aliaser().setAliasForThis(descriptor, aliasForThis.name());
return aliasForThis; return aliasForThis;
} }
public void removeAliasForThis(@NotNull JsName aliasToRemove) { public void removeAliasForThis(@NotNull DeclarationDescriptor descriptor) {
aliaser().removeAliasForThis(aliasToRemove); aliaser().removeAliasForThis(descriptor);
} }
@NotNull @NotNull
@@ -15,7 +15,7 @@ import java.util.List;
/** /**
* @author Pavel.Talanov * @author Pavel.Talanov
*/ */
//TODO: rework translator to translate everything in the namespace not only in one file //TODO: rework translator to translateAsLocalNameReference everything in the namespace not only in one file
// TEST IT // TEST IT
public final class NamespaceTranslator extends AbstractTranslator { public final class NamespaceTranslator extends AbstractTranslator {
@@ -4,6 +4,7 @@ import com.google.dart.compiler.backend.js.ast.*;
import com.google.dart.compiler.util.AstUtil; import com.google.dart.compiler.util.AstUtil;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor;
import org.jetbrains.jet.lang.psi.*; import org.jetbrains.jet.lang.psi.*;
import org.jetbrains.jet.lang.resolve.BindingContext; import org.jetbrains.jet.lang.resolve.BindingContext;
import org.jetbrains.jet.lang.resolve.constants.CompileTimeConstant; import org.jetbrains.jet.lang.resolve.constants.CompileTimeConstant;
@@ -23,6 +24,7 @@ import org.jetbrains.k2js.translate.utils.BindingUtils;
import java.util.List; import java.util.List;
import static org.jetbrains.k2js.translate.utils.BindingUtils.getCompileTimeValue; import static org.jetbrains.k2js.translate.utils.BindingUtils.getCompileTimeValue;
import static org.jetbrains.k2js.translate.utils.BindingUtils.getDescriptorForReferenceExpression;
import static org.jetbrains.k2js.translate.utils.TranslationUtils.notNullCheck; import static org.jetbrains.k2js.translate.utils.TranslationUtils.notNullCheck;
import static org.jetbrains.k2js.translate.utils.TranslationUtils.translateInitializerForProperty; import static org.jetbrains.k2js.translate.utils.TranslationUtils.translateInitializerForProperty;
@@ -375,8 +377,10 @@ public final class ExpressionVisitor extends TranslatorVisitor<JsNode> {
@NotNull @NotNull
public JsNode visitThisExpression(@NotNull JetThisExpression expression, public JsNode visitThisExpression(@NotNull JetThisExpression expression,
@NotNull TranslationContext context) { @NotNull TranslationContext context) {
if (context.aliaser().hasAliasForThis()) { DeclarationDescriptor descriptor =
return context.aliaser().getAliasForThis(); getDescriptorForReferenceExpression(context.bindingContext(), expression.getInstanceReference());
if (context.aliaser().hasAliasForThis(descriptor)) {
return context.aliaser().getAliasForThis(descriptor);
} }
return new JsThisRef(); return new JsThisRef();
} }
@@ -3,9 +3,13 @@ package org.jetbrains.k2js.translate.expression;
import com.google.dart.compiler.backend.js.ast.*; import com.google.dart.compiler.backend.js.ast.*;
import com.google.dart.compiler.util.AstUtil; import com.google.dart.compiler.util.AstUtil;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor;
import org.jetbrains.jet.lang.descriptors.FunctionDescriptor; import org.jetbrains.jet.lang.descriptors.FunctionDescriptor;
import org.jetbrains.jet.lang.descriptors.Modality;
import org.jetbrains.jet.lang.descriptors.ValueParameterDescriptor; import org.jetbrains.jet.lang.descriptors.ValueParameterDescriptor;
import org.jetbrains.jet.lang.psi.*; import org.jetbrains.jet.lang.psi.*;
import org.jetbrains.jet.lang.types.JetStandardClasses;
import org.jetbrains.jet.lang.types.JetType;
import org.jetbrains.k2js.translate.context.TemporaryVariable; import org.jetbrains.k2js.translate.context.TemporaryVariable;
import org.jetbrains.k2js.translate.context.TranslationContext; import org.jetbrains.k2js.translate.context.TranslationContext;
import org.jetbrains.k2js.translate.general.AbstractTranslator; import org.jetbrains.k2js.translate.general.AbstractTranslator;
@@ -16,6 +20,8 @@ import java.util.ArrayList;
import java.util.List; import java.util.List;
import static org.jetbrains.k2js.translate.utils.BindingUtils.getFunctionDescriptor; import static org.jetbrains.k2js.translate.utils.BindingUtils.getFunctionDescriptor;
import static org.jetbrains.k2js.translate.utils.DescriptorUtils.getExpectedReceiverDescriptor;
import static org.jetbrains.k2js.translate.utils.DescriptorUtils.getExpectedThisDescriptor;
import static org.jetbrains.k2js.translate.utils.TranslationUtils.functionWithScope; import static org.jetbrains.k2js.translate.utils.TranslationUtils.functionWithScope;
@@ -64,11 +70,17 @@ public final class FunctionTranslator extends AbstractTranslator {
return new JsPropertyInitializer(functionName.makeRef(), function); return new JsPropertyInitializer(functionName.makeRef(), function);
} }
//TODO: refactor
@NotNull @NotNull
public JsExpression translateAsLiteral() { public JsExpression translateAsLiteral() {
TemporaryVariable aliasForThis = context().newAliasForThis(); DeclarationDescriptor expectedThisDescriptor = getExpectedThisDescriptor(descriptor);
if (expectedThisDescriptor == null) {
return generateFunctionObject();
}
TemporaryVariable aliasForThis = context().newAliasForThis(expectedThisDescriptor);
JsFunction function = generateFunctionObject(); JsFunction function = generateFunctionObject();
context().removeAliasForThis(aliasForThis.name()); context().removeAliasForThis(expectedThisDescriptor);
return AstUtil.newSequence(aliasForThis.assignmentExpression(), function); return AstUtil.newSequence(aliasForThis.assignmentExpression(), function);
} }
@@ -83,8 +95,7 @@ public final class FunctionTranslator extends AbstractTranslator {
private void restoreContext() { private void restoreContext() {
if (isExtensionFunction()) { if (isExtensionFunction()) {
JsName receiverAlias = functionBodyContext.jsScope().findExistingName(RECEIVER_PARAMETER_NAME); functionBodyContext.removeAliasForThis(getExpectedReceiverDescriptor(descriptor));
functionBodyContext.removeAliasForThis(receiverAlias);
} }
} }
@@ -100,21 +111,20 @@ public final class FunctionTranslator extends AbstractTranslator {
throw new AssertionError("Unsupported type of functionDeclaration."); throw new AssertionError("Unsupported type of functionDeclaration.");
} }
private boolean isLiteral() {
return functionDeclaration instanceof JetFunctionLiteralExpression;
}
private boolean isDeclaration() {
return (functionDeclaration instanceof JetNamedFunction) ||
(functionDeclaration instanceof JetPropertyAccessor);
}
private void translateBody() { private void translateBody() {
JetExpression jetBodyExpression = functionDeclaration.getBodyExpression(); JetExpression jetBodyExpression = functionDeclaration.getBodyExpression();
//TODO decide if there are cases where this assert is illegal if (jetBodyExpression == null) {
assert jetBodyExpression != null : "Function without body not supported"; assert descriptor.getModality().equals(Modality.ABSTRACT);
return;
}
JsNode realBody = Translation.translateExpression(jetBodyExpression, functionBodyContext); JsNode realBody = Translation.translateExpression(jetBodyExpression, functionBodyContext);
functionBody.addStatement(wrapWithReturnIfNeeded(realBody, !functionDeclaration.hasBlockBody())); functionBody.addStatement(wrapWithReturnIfNeeded(realBody, mustAddReturnToGeneratedFunctionBody()));
}
private boolean mustAddReturnToGeneratedFunctionBody() {
JetType functionReturnType = descriptor.getReturnType();
assert functionReturnType != null : "Function return typed type must be resolved.";
return (!functionDeclaration.hasBlockBody()) && (!JetStandardClasses.isUnit(functionReturnType));
} }
@NotNull @NotNull
@@ -173,14 +183,24 @@ public final class FunctionTranslator extends AbstractTranslator {
private void mayBeAddThisParameterForExtensionFunction(@NotNull List<JsParameter> jsParameters) { private void mayBeAddThisParameterForExtensionFunction(@NotNull List<JsParameter> jsParameters) {
if (isExtensionFunction()) { if (isExtensionFunction()) {
//TODO: dont do this
JsName receiver = functionBodyContext.jsScope().declareName(RECEIVER_PARAMETER_NAME); JsName receiver = functionBodyContext.jsScope().declareName(RECEIVER_PARAMETER_NAME);
context().aliaser().setAliasForThis(receiver); DeclarationDescriptor expectedReceiverDescriptor = getExpectedReceiverDescriptor(descriptor);
context().aliaser().setAliasForReceiver(expectedReceiverDescriptor, receiver);
jsParameters.add(new JsParameter(receiver)); jsParameters.add(new JsParameter(receiver));
} }
} }
private boolean isExtensionFunction() { private boolean isExtensionFunction() {
FunctionDescriptor functionDescriptor = getFunctionDescriptor(context().bindingContext(), functionDeclaration); return DescriptorUtils.isExtensionFunction(descriptor);
return DescriptorUtils.isExtensionFunction(functionDescriptor); }
private boolean isLiteral() {
return functionDeclaration instanceof JetFunctionLiteralExpression;
}
private boolean isDeclaration() {
return (functionDeclaration instanceof JetNamedFunction) ||
(functionDeclaration instanceof JetPropertyAccessor);
} }
} }
@@ -40,7 +40,7 @@ public final class ClassInitializerTranslator extends AbstractInitializerTransla
@NotNull @NotNull
protected JsFunction generateInitializerFunction() { protected JsFunction generateInitializerFunction() {
JsFunction result = functionWithScope(initializerMethodScope); JsFunction result = functionWithScope(initializerMethodScope);
//NOTE: that while we translate constructor parameters we also add property initializer statements //NOTE: that while we translateAsLocalNameReference constructor parameters we also add property initializer statements
// for properties declared as constructor parameters // for properties declared as constructor parameters
result.setParameters(translatePrimaryConstructorParameters()); result.setParameters(translatePrimaryConstructorParameters());
mayBeAddCallToSuperMethod(); mayBeAddCallToSuperMethod();
@@ -10,6 +10,7 @@ import org.jetbrains.jet.lang.descriptors.*;
import org.jetbrains.jet.lang.psi.*; import org.jetbrains.jet.lang.psi.*;
import org.jetbrains.jet.lang.resolve.calls.DefaultValueArgument; import org.jetbrains.jet.lang.resolve.calls.DefaultValueArgument;
import org.jetbrains.jet.lang.resolve.calls.ResolvedCall; import org.jetbrains.jet.lang.resolve.calls.ResolvedCall;
import org.jetbrains.jet.lang.resolve.calls.ResolvedCallImpl;
import org.jetbrains.jet.lang.resolve.calls.ResolvedValueArgument; import org.jetbrains.jet.lang.resolve.calls.ResolvedValueArgument;
import org.jetbrains.k2js.translate.context.TranslationContext; import org.jetbrains.k2js.translate.context.TranslationContext;
import org.jetbrains.k2js.translate.general.AbstractTranslator; import org.jetbrains.k2js.translate.general.AbstractTranslator;
@@ -31,6 +32,7 @@ import static org.jetbrains.k2js.translate.utils.TranslationUtils.*;
* @author Pavel Talanov * @author Pavel Talanov
*/ */
//TODO: write tests on calling backing fields as functions //TODO: write tests on calling backing fields as functions
//TODO: constructor receives too many parameters
public final class CallTranslator extends AbstractTranslator { public final class CallTranslator extends AbstractTranslator {
private static final class Builder { private static final class Builder {
@@ -46,10 +48,9 @@ public final class CallTranslator extends AbstractTranslator {
private CallTranslator buildFromUnary(@NotNull JetUnaryExpression unaryExpression) { private CallTranslator buildFromUnary(@NotNull JetUnaryExpression unaryExpression) {
JsExpression receiver = TranslationUtils.translateBaseExpression(context, unaryExpression); JsExpression receiver = TranslationUtils.translateBaseExpression(context, unaryExpression);
List<JsExpression> arguments = Collections.emptyList(); List<JsExpression> arguments = Collections.emptyList();
DeclarationDescriptor descriptor = getDescriptorForReferenceExpression ResolvedCall<?> resolvedCall =
(context.bindingContext(), unaryExpression.getOperationReference()); getResolvedCall(context.bindingContext(), unaryExpression.getOperationReference());
assert descriptor instanceof FunctionDescriptor; return new CallTranslator(receiver, arguments, resolvedCall, null, context);
return new CallTranslator(receiver, arguments, (FunctionDescriptor) descriptor, context);
} }
//TODO: method too long //TODO: method too long
@@ -70,11 +71,9 @@ public final class CallTranslator extends AbstractTranslator {
arguments = Arrays.asList(rightExpression); arguments = Arrays.asList(rightExpression);
} }
DeclarationDescriptor descriptor = getDescriptorForReferenceExpression ResolvedCall<?> resolvedCall =
(context.bindingContext(), binaryExpression.getOperationReference()); getResolvedCall(context.bindingContext(), binaryExpression.getOperationReference());
assert descriptor instanceof FunctionDescriptor; return new CallTranslator(receiver, arguments, resolvedCall, null, context);
FunctionDescriptor functionDescriptor = (FunctionDescriptor) descriptor;
return new CallTranslator(receiver, arguments, functionDescriptor, context);
} }
@NotNull @NotNull
@@ -84,19 +83,16 @@ public final class CallTranslator extends AbstractTranslator {
JetExpression selectorExpression = dotExpression.getSelectorExpression(); JetExpression selectorExpression = dotExpression.getSelectorExpression();
assert selectorExpression instanceof JetCallExpression; assert selectorExpression instanceof JetCallExpression;
JetCallExpression callExpression = (JetCallExpression) selectorExpression; JetCallExpression callExpression = (JetCallExpression) selectorExpression;
FunctionDescriptor descriptor = ResolvedCall<?> resolvedCall = getResolvedCallForCallExpression(context.bindingContext(), callExpression);
getFunctionDescriptorForCallExpression(context.bindingContext(), callExpression);
List<JsExpression> arguments = translateArgumentsForCallExpression(callExpression, context); List<JsExpression> arguments = translateArgumentsForCallExpression(callExpression, context);
return new CallTranslator(receiver, arguments, descriptor, context); return new CallTranslator(receiver, arguments, resolvedCall, null, context);
} }
@NotNull @NotNull
private CallTranslator buildFromCallExpression(@NotNull JetCallExpression callExpression) { private CallTranslator buildFromCallExpression(@NotNull JetCallExpression callExpression) {
FunctionDescriptor descriptor = ResolvedCall<?> resolvedCall = getResolvedCallForCallExpression(context.bindingContext(), callExpression);
getFunctionDescriptorForCallExpression(context.bindingContext(), callExpression);
JsExpression receiver = getImplicitReceiver(context, descriptor);
List<JsExpression> arguments = translateArgumentsForCallExpression(callExpression, context); List<JsExpression> arguments = translateArgumentsForCallExpression(callExpression, context);
return new CallTranslator(receiver, arguments, descriptor, context); return new CallTranslator(null, arguments, resolvedCall, null, context);
} }
@NotNull @NotNull
@@ -126,16 +122,28 @@ public final class CallTranslator extends AbstractTranslator {
} }
@NotNull @NotNull
public static JsExpression translate(@Nullable JsExpression receiver, @NotNull CallableDescriptor functionDescriptor, public static JsExpression translate(@Nullable JsExpression receiver,
@NotNull CallableDescriptor descriptor,
@NotNull TranslationContext context) { @NotNull TranslationContext context) {
return translate(receiver, Collections.<JsExpression>emptyList(), functionDescriptor, context); //TODO: HACK!
return translate(receiver, Collections.<JsExpression>emptyList(),
ResolvedCallImpl.create(descriptor), null, context);
}
@NotNull
public static JsExpression translate(@Nullable JsExpression receiver,
@NotNull ResolvedCall<?> resolvedCall,
@Nullable CallableDescriptor descriptorToCall,
@NotNull TranslationContext context) {
return translate(receiver, Collections.<JsExpression>emptyList(), resolvedCall, descriptorToCall, context);
} }
@NotNull @NotNull
public static JsExpression translate(@Nullable JsExpression receiver, @NotNull List<JsExpression> arguments, public static JsExpression translate(@Nullable JsExpression receiver, @NotNull List<JsExpression> arguments,
@NotNull CallableDescriptor functionDescriptor, @NotNull ResolvedCall<?> resolvedCall,
@Nullable CallableDescriptor descriptorToCall,
@NotNull TranslationContext context) { @NotNull TranslationContext context) {
return (new CallTranslator(receiver, arguments, functionDescriptor, context)).translate(); return (new CallTranslator(receiver, arguments, resolvedCall, descriptorToCall, context)).translate();
} }
@NotNull @NotNull
@@ -184,15 +192,25 @@ public final class CallTranslator extends AbstractTranslator {
@NotNull @NotNull
private final List<JsExpression> arguments; private final List<JsExpression> arguments;
@NotNull
private final ResolvedCall<?> resolvedCall;
@NotNull @NotNull
private final CallableDescriptor descriptor; private final CallableDescriptor descriptor;
private CallTranslator(@Nullable JsExpression receiver, @NotNull List<JsExpression> arguments, private CallTranslator(@Nullable JsExpression receiver, @NotNull List<JsExpression> arguments,
@NotNull CallableDescriptor descriptor, @NotNull TranslationContext context) { @NotNull ResolvedCall<? extends CallableDescriptor> resolvedCall,
@Nullable CallableDescriptor descriptorToCall,
@NotNull TranslationContext context) {
super(context); super(context);
this.receiver = receiver; this.receiver = receiver;
this.arguments = arguments; this.arguments = arguments;
this.descriptor = descriptor; this.resolvedCall = resolvedCall;
if (descriptorToCall != null) {
this.descriptor = descriptorToCall;
} else {
this.descriptor = resolvedCall.getCandidateDescriptor().getOriginal();
}
} }
@NotNull @NotNull
@@ -218,7 +236,7 @@ public final class CallTranslator extends AbstractTranslator {
} }
private boolean isExtensionFunction() { private boolean isExtensionFunction() {
return DescriptorUtils.isExtensionFunction(descriptor); return resolvedCall.getReceiverArgument().exists();
} }
@NotNull @NotNull
@@ -258,13 +276,14 @@ public final class CallTranslator extends AbstractTranslator {
} }
@NotNull @NotNull
private JsExpression getterCall(PropertyDescriptor variableDescriptor) { private JsExpression getterCall(@NotNull PropertyDescriptor variableDescriptor) {
return PropertyAccessTranslator.translateAsPropertyGetterCall(variableDescriptor, context()); return PropertyAccessTranslator.translateAsPropertyGetterCall(variableDescriptor, resolvedCall, context());
} }
@NotNull @NotNull
private JsExpression qualifiedMethodReference(@NotNull DeclarationDescriptor descriptor) { private JsExpression qualifiedMethodReference(@NotNull DeclarationDescriptor descriptor) {
JsExpression methodReference = ReferenceTranslator.translateReference(descriptor, context()); JsExpression methodReference = ReferenceTranslator.translateAsLocalNameReference(descriptor, context());
//TODO: should insert checks for IMPLICIT RECEIVER
if (isExtensionFunction()) { if (isExtensionFunction()) {
return extensionFunctionReference(methodReference); return extensionFunctionReference(methodReference);
} else if (receiver != null) { } else if (receiver != null) {
@@ -275,7 +294,7 @@ public final class CallTranslator extends AbstractTranslator {
@NotNull @NotNull
private JsExpression extensionFunctionReference(@NotNull JsExpression methodReference) { private JsExpression extensionFunctionReference(@NotNull JsExpression methodReference) {
JsExpression qualifier = TranslationUtils.getExtensionFunctionImplicitReceiver(context(), descriptor); JsExpression qualifier = TranslationUtils.getImplicitReceiver(context(), resolvedCall.getReceiverArgument());
if (qualifier != null) { if (qualifier != null) {
AstUtil.setQualifier(methodReference, qualifier); AstUtil.setQualifier(methodReference, qualifier);
} }
@@ -284,7 +303,8 @@ public final class CallTranslator extends AbstractTranslator {
@NotNull @NotNull
private JsExpression constructorCall() { private JsExpression constructorCall() {
JsNew constructorCall = new JsNew(qualifiedMethodReference(descriptor)); JsExpression constructorReference = ReferenceTranslator.translateAsFQReference(descriptor, context());
JsNew constructorCall = new JsNew(constructorReference);
constructorCall.setArguments(arguments); constructorCall.setArguments(arguments);
return constructorCall; return constructorCall;
} }
@@ -7,21 +7,20 @@ import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor; import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor;
import org.jetbrains.jet.lang.descriptors.PropertyDescriptor; import org.jetbrains.jet.lang.descriptors.PropertyDescriptor;
import org.jetbrains.jet.lang.descriptors.PropertyGetterDescriptor;
import org.jetbrains.jet.lang.descriptors.PropertySetterDescriptor;
import org.jetbrains.jet.lang.psi.JetExpression; import org.jetbrains.jet.lang.psi.JetExpression;
import org.jetbrains.jet.lang.psi.JetQualifiedExpression; import org.jetbrains.jet.lang.psi.JetQualifiedExpression;
import org.jetbrains.jet.lang.psi.JetSimpleNameExpression; import org.jetbrains.jet.lang.psi.JetSimpleNameExpression;
import org.jetbrains.jet.lang.resolve.calls.ResolvedCall;
import org.jetbrains.k2js.translate.context.TranslationContext; import org.jetbrains.k2js.translate.context.TranslationContext;
import org.jetbrains.k2js.translate.general.Translation; import org.jetbrains.k2js.translate.general.Translation;
import java.util.Arrays; import java.util.Arrays;
import static org.jetbrains.k2js.translate.utils.BindingUtils.getDescriptorForReferenceExpression; import static org.jetbrains.k2js.translate.utils.BindingUtils.getDescriptorForReferenceExpression;
import static org.jetbrains.k2js.translate.utils.BindingUtils.getResolvedCall;
import static org.jetbrains.k2js.translate.utils.PsiUtils.getSelectorAsSimpleName; import static org.jetbrains.k2js.translate.utils.PsiUtils.getSelectorAsSimpleName;
import static org.jetbrains.k2js.translate.utils.PsiUtils.isBackingFieldReference; import static org.jetbrains.k2js.translate.utils.PsiUtils.isBackingFieldReference;
import static org.jetbrains.k2js.translate.utils.TranslationUtils.backingFieldReference; import static org.jetbrains.k2js.translate.utils.TranslationUtils.backingFieldReference;
import static org.jetbrains.k2js.translate.utils.TranslationUtils.getImplicitReceiver;
/** /**
* @author Pavel Talanov * @author Pavel Talanov
@@ -42,8 +41,9 @@ public final class PropertyAccessTranslator extends AccessTranslator {
@NotNull @NotNull
public static JsExpression translateAsPropertyGetterCall(@NotNull PropertyDescriptor descriptor, public static JsExpression translateAsPropertyGetterCall(@NotNull PropertyDescriptor descriptor,
@NotNull ResolvedCall<?> resolvedCall,
@NotNull TranslationContext context) { @NotNull TranslationContext context) {
return (newInstance(descriptor, context)) return (newInstance(descriptor, resolvedCall, context))
.translateAsGet(); .translateAsGet();
} }
@@ -56,8 +56,9 @@ public final class PropertyAccessTranslator extends AccessTranslator {
@NotNull @NotNull
private static PropertyAccessTranslator newInstance(@NotNull PropertyDescriptor descriptor, private static PropertyAccessTranslator newInstance(@NotNull PropertyDescriptor descriptor,
@NotNull ResolvedCall<?> resolvedCall,
@NotNull TranslationContext context) { @NotNull TranslationContext context) {
return new PropertyAccessTranslator(descriptor, null, false, context); return new PropertyAccessTranslator(descriptor, null, false, resolvedCall, context);
} }
@NotNull @NotNull
@@ -65,16 +66,21 @@ public final class PropertyAccessTranslator extends AccessTranslator {
@NotNull TranslationContext context) { @NotNull TranslationContext context) {
JetExpression qualifier = expression.getReceiverExpression(); JetExpression qualifier = expression.getReceiverExpression();
JetSimpleNameExpression selector = getNotNullSelector(expression); JetSimpleNameExpression selector = getNotNullSelector(expression);
ResolvedCall<?> resolvedCall = getResolvedCall(context.bindingContext(), selector);
PropertyDescriptor propertyDescriptor = getPropertyDescriptor(selector, context); PropertyDescriptor propertyDescriptor = getPropertyDescriptor(selector, context);
boolean isBackingFieldAccess = isBackingFieldReference(selector); boolean isBackingFieldAccess = isBackingFieldReference(selector);
return new PropertyAccessTranslator(propertyDescriptor, qualifier, isBackingFieldAccess, context); JsExpression jsQualifier = Translation.translateAsExpression(qualifier, context);
return new PropertyAccessTranslator(propertyDescriptor, jsQualifier,
isBackingFieldAccess, resolvedCall, context);
} }
@NotNull @NotNull
public static PropertyAccessTranslator newInstance(@NotNull JetSimpleNameExpression expression, public static PropertyAccessTranslator newInstance(@NotNull JetSimpleNameExpression expression,
@NotNull TranslationContext context) { @NotNull TranslationContext context) {
PropertyDescriptor propertyDescriptor = getPropertyDescriptor(expression, context); PropertyDescriptor propertyDescriptor = getPropertyDescriptor(expression, context);
return new PropertyAccessTranslator(propertyDescriptor, null, isBackingFieldReference(expression), context); ResolvedCall<?> resolvedCall = getResolvedCall(context.bindingContext(), expression);
return new PropertyAccessTranslator(propertyDescriptor, null,
isBackingFieldReference(expression), resolvedCall, context);
} }
@NotNull @NotNull
@@ -121,19 +127,23 @@ public final class PropertyAccessTranslator extends AccessTranslator {
} }
@Nullable @Nullable
private final JetExpression qualifier; private final JsExpression qualifier;
@NotNull @NotNull
private final PropertyDescriptor propertyDescriptor; private final PropertyDescriptor propertyDescriptor;
private final boolean isBackingFieldAccess; private final boolean isBackingFieldAccess;
@NotNull
ResolvedCall<?> resolvedCall;
private PropertyAccessTranslator(@NotNull PropertyDescriptor descriptor, private PropertyAccessTranslator(@NotNull PropertyDescriptor descriptor,
@Nullable JetExpression qualifier, @Nullable JsExpression qualifier,
boolean isBackingFieldAccess, boolean isBackingFieldAccess,
@NotNull ResolvedCall<?> resolvedCall,
@NotNull TranslationContext context) { @NotNull TranslationContext context) {
super(context); super(context);
this.qualifier = qualifier; this.qualifier = qualifier;
this.propertyDescriptor = descriptor.getOriginal(); this.propertyDescriptor = descriptor.getOriginal();
this.isBackingFieldAccess = isBackingFieldAccess; this.isBackingFieldAccess = isBackingFieldAccess;
this.resolvedCall = resolvedCall;
} }
@Override @Override
@@ -153,7 +163,7 @@ public final class PropertyAccessTranslator extends AccessTranslator {
@NotNull @NotNull
private JsExpression getterCall() { private JsExpression getterCall() {
return CallTranslator.translate(translateQualifier(), getGetterDescriptor(), context()); return CallTranslator.translate(qualifier, resolvedCall, propertyDescriptor.getGetter(), context());
} }
@Override @Override
@@ -168,7 +178,8 @@ public final class PropertyAccessTranslator extends AccessTranslator {
@NotNull @NotNull
private JsExpression setterCall(@NotNull JsExpression toSetTo) { private JsExpression setterCall(@NotNull JsExpression toSetTo) {
return CallTranslator.translate(translateQualifier(), Arrays.asList(toSetTo), getSetterDescriptor(), context()); return CallTranslator.translate(qualifier, Arrays.asList(toSetTo),
resolvedCall, propertyDescriptor.getSetter(), context());
} }
@NotNull @NotNull
@@ -177,35 +188,10 @@ public final class PropertyAccessTranslator extends AccessTranslator {
return AstUtil.newAssignment(backingFieldReference, toSetTo); return AstUtil.newAssignment(backingFieldReference, toSetTo);
} }
@NotNull
private JsExpression translateQualifier() {
if (qualifier != null) {
return Translation.translateAsExpression(qualifier, context());
}
JsExpression implicitReceiver = getImplicitReceiver(context(), propertyDescriptor);
assert implicitReceiver != null : "Property can only be a member of class or a namespace.";
return implicitReceiver;
}
@NotNull @NotNull
private static JetSimpleNameExpression getNotNullSelector(@NotNull JetQualifiedExpression qualifiedExpression) { private static JetSimpleNameExpression getNotNullSelector(@NotNull JetQualifiedExpression qualifiedExpression) {
JetSimpleNameExpression selectorExpression = getSelectorAsSimpleName(qualifiedExpression); JetSimpleNameExpression selectorExpression = getSelectorAsSimpleName(qualifiedExpression);
assert selectorExpression != null : MESSAGE; assert selectorExpression != null : MESSAGE;
return selectorExpression; return selectorExpression;
} }
@NotNull
private PropertyGetterDescriptor getGetterDescriptor() {
PropertyGetterDescriptor getter = propertyDescriptor.getGetter();
assert getter != null : propertyDescriptor.getName() + " does not have a getter.";
return getter;
}
@NotNull
private PropertySetterDescriptor getSetterDescriptor() {
PropertySetterDescriptor setter = propertyDescriptor.getSetter();
assert setter != null;
return setter;
}
} }
@@ -18,6 +18,7 @@ public final class ReferenceAccessTranslator extends AccessTranslator {
return new ReferenceAccessTranslator(expression, context); return new ReferenceAccessTranslator(expression, context);
} }
//TODO: condider evaluating only once
@NotNull @NotNull
private final JetSimpleNameExpression expression; private final JetSimpleNameExpression expression;
@@ -4,19 +4,18 @@ 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.JsName;
import com.google.dart.compiler.util.AstUtil; import com.google.dart.compiler.util.AstUtil;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor; import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor;
import org.jetbrains.jet.lang.descriptors.NamespaceDescriptor;
import org.jetbrains.jet.lang.psi.JetSimpleNameExpression; import org.jetbrains.jet.lang.psi.JetSimpleNameExpression;
import org.jetbrains.k2js.translate.context.TranslationContext; import org.jetbrains.k2js.translate.context.TranslationContext;
import org.jetbrains.k2js.translate.general.AbstractTranslator;
import static org.jetbrains.k2js.translate.utils.BindingUtils.getDescriptorForReferenceExpression; import static org.jetbrains.k2js.translate.utils.BindingUtils.getDescriptorForReferenceExpression;
import static org.jetbrains.k2js.translate.utils.TranslationUtils.getImplicitReceiver;
/** /**
* @author Pavel Talanov * @author Pavel Talanov
*/ */
public final class ReferenceTranslator extends AbstractTranslator { //TODO: get rid of the class, move to util
public final class ReferenceTranslator {
@NotNull @NotNull
public static JsExpression translateSimpleName(@NotNull JetSimpleNameExpression expression, public static JsExpression translateSimpleName(@NotNull JetSimpleNameExpression expression,
@@ -26,42 +25,27 @@ public final class ReferenceTranslator extends AbstractTranslator {
} }
DeclarationDescriptor referencedDescriptor = DeclarationDescriptor referencedDescriptor =
getDescriptorForReferenceExpression(context.bindingContext(), expression); getDescriptorForReferenceExpression(context.bindingContext(), expression);
return (new ReferenceTranslator(referencedDescriptor, true, context)).translate(); return translateAsLocalNameReference(referencedDescriptor, context);
} }
@NotNull @NotNull
public static JsExpression translateReference(@NotNull DeclarationDescriptor referencedDescriptor, public static JsExpression translateAsFQReference(@NotNull DeclarationDescriptor referencedDescriptor,
@NotNull TranslationContext context) { @NotNull TranslationContext context) {
return (new ReferenceTranslator(referencedDescriptor, false, context)).translate();
assert context.hasQualifierForDescriptor(referencedDescriptor);
JsName referencedName = context.getNameForDescriptor(referencedDescriptor);
JsExpression qualifier = context.getQualifierForDescriptor(referencedDescriptor);
return AstUtil.qualified(referencedName, qualifier);
} }
@NotNull @NotNull
private final DeclarationDescriptor referencedDescriptor; public static JsExpression translateAsLocalNameReference(@NotNull DeclarationDescriptor referencedDescriptor,
@NotNull TranslationContext context) {
private final boolean shouldQualify; if (referencedDescriptor.getContainingDeclaration() instanceof NamespaceDescriptor) {
return translateAsFQReference(referencedDescriptor, context);
private ReferenceTranslator(@NotNull DeclarationDescriptor referencedDescriptor,
boolean shouldQualify,
@NotNull TranslationContext context) {
super(context);
this.referencedDescriptor = referencedDescriptor;
this.shouldQualify = shouldQualify;
}
@NotNull
public JsExpression translate() {
JsName referencedName = context().getNameForDescriptor(referencedDescriptor);
JsExpression implicitReceiver = getImplicitReceiver(context(), referencedDescriptor);
return generateReference(referencedName, implicitReceiver);
}
@NotNull
private JsExpression generateReference(@NotNull JsName referencedName, @Nullable JsExpression implicitReceiver) {
if (shouldQualify && implicitReceiver != null) {
return AstUtil.qualified(referencedName, implicitReceiver);
} else {
return referencedName.makeRef();
} }
JsName referencedName = context.getNameForDescriptor(referencedDescriptor);
return referencedName.makeRef();
} }
} }
@@ -222,23 +222,13 @@ public final class BindingUtils {
} }
@NotNull @NotNull
private static ResolvedCall<?> getResolvedCall(@NotNull BindingContext context, public static ResolvedCall<?> getResolvedCall(@NotNull BindingContext context,
@NotNull JetExpression expression) { @NotNull JetExpression expression) {
ResolvedCall<? extends CallableDescriptor> resolvedCall = context.get(BindingContext.RESOLVED_CALL, expression); ResolvedCall<? extends CallableDescriptor> resolvedCall = context.get(BindingContext.RESOLVED_CALL, expression);
assert resolvedCall != null : "Must resolve to a call."; assert resolvedCall != null : "Must resolve to a call.";
return resolvedCall; return resolvedCall;
} }
@NotNull
public static FunctionDescriptor getFunctionDescriptorForCallExpression(@NotNull BindingContext context,
@NotNull JetCallExpression expression) {
ResolvedCall<?> resolvedCall = getResolvedCallForCallExpression(context, expression);
CallableDescriptor descriptor = getDescriptorForResolvedCall(resolvedCall);
assert descriptor instanceof FunctionDescriptor :
"Callee expression must have resolved call with descriptor of type FunctionDescriptor";
return (FunctionDescriptor) descriptor;
}
@NotNull @NotNull
public static ResolvedCall<?> getResolvedCallForCallExpression(@NotNull BindingContext context, public static ResolvedCall<?> getResolvedCallForCallExpression(@NotNull BindingContext context,
@NotNull JetCallExpression expression) { @NotNull JetCallExpression expression) {
@@ -4,6 +4,7 @@ import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import org.jetbrains.jet.lang.descriptors.*; import org.jetbrains.jet.lang.descriptors.*;
import org.jetbrains.jet.lang.resolve.scopes.JetScope; import org.jetbrains.jet.lang.resolve.scopes.JetScope;
import org.jetbrains.jet.lang.resolve.scopes.receivers.ReceiverDescriptor;
import org.jetbrains.jet.lang.types.expressions.OperatorConventions; import org.jetbrains.jet.lang.types.expressions.OperatorConventions;
import java.util.ArrayList; import java.util.ArrayList;
@@ -136,4 +137,30 @@ public final class DescriptorUtils {
} }
return name; return name;
} }
//TODO: why callable descriptor
@Nullable
public static DeclarationDescriptor getExpectedThisDescriptor(@NotNull CallableDescriptor functionDescriptor) {
ReceiverDescriptor receiverParameter = functionDescriptor.getReceiverParameter();
if (!receiverParameter.exists()) {
return null;
}
DeclarationDescriptor declarationDescriptor =
receiverParameter.getType().getConstructor().getDeclarationDescriptor();
//TODO: WHY assert?
assert declarationDescriptor != null;
return declarationDescriptor.getOriginal();
}
@Nullable
public static DeclarationDescriptor getExpectedReceiverDescriptor(@NotNull CallableDescriptor functionDescriptor) {
ReceiverDescriptor receiverParameter = functionDescriptor.getReceiverParameter();
if (!receiverParameter.exists()) {
return null;
}
DeclarationDescriptor declarationDescriptor =
functionDescriptor.getReceiverParameter().getType().getConstructor().getDeclarationDescriptor();
assert declarationDescriptor != null;
return declarationDescriptor.getOriginal();
}
} }
@@ -4,11 +4,11 @@ import com.google.dart.compiler.backend.js.ast.*;
import com.google.dart.compiler.util.AstUtil; import com.google.dart.compiler.util.AstUtil;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import org.jetbrains.jet.lang.descriptors.CallableDescriptor;
import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor; import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor;
import org.jetbrains.jet.lang.descriptors.FunctionDescriptor; import org.jetbrains.jet.lang.descriptors.FunctionDescriptor;
import org.jetbrains.jet.lang.descriptors.PropertyDescriptor; import org.jetbrains.jet.lang.descriptors.PropertyDescriptor;
import org.jetbrains.jet.lang.psi.*; import org.jetbrains.jet.lang.psi.*;
import org.jetbrains.jet.lang.resolve.scopes.receivers.ReceiverDescriptor;
import org.jetbrains.k2js.translate.context.NamingScope; import org.jetbrains.k2js.translate.context.NamingScope;
import org.jetbrains.k2js.translate.context.TranslationContext; import org.jetbrains.k2js.translate.context.TranslationContext;
import org.jetbrains.k2js.translate.general.Translation; import org.jetbrains.k2js.translate.general.Translation;
@@ -17,7 +17,6 @@ import java.util.ArrayList;
import java.util.List; import java.util.List;
import static org.jetbrains.k2js.translate.utils.BindingUtils.*; import static org.jetbrains.k2js.translate.utils.BindingUtils.*;
import static org.jetbrains.k2js.translate.utils.DescriptorUtils.isExtensionFunction;
/** /**
* @author Pavel Talanov * @author Pavel Talanov
@@ -57,6 +56,7 @@ public final class TranslationUtils {
return Translation.translateAsExpression(jetExpression, context); return Translation.translateAsExpression(jetExpression, context);
} }
//TODO: refactor backing field reference generation to use the generic way
@NotNull @NotNull
public static JsNameRef backingFieldReference(@NotNull TranslationContext context, public static JsNameRef backingFieldReference(@NotNull TranslationContext context,
@NotNull JetProperty expression) { @NotNull JetProperty expression) {
@@ -69,7 +69,7 @@ public final class TranslationUtils {
@NotNull PropertyDescriptor descriptor) { @NotNull PropertyDescriptor descriptor) {
JsName backingFieldName = context.getNameForDescriptor(descriptor); JsName backingFieldName = context.getNameForDescriptor(descriptor);
if (isOwnedByClass(descriptor)) { if (isOwnedByClass(descriptor)) {
return getThisQualifiedNameReference(context, backingFieldName); return AstUtil.qualified(backingFieldName, new JsThisRef());
} }
assert isOwnedByNamespace(descriptor) assert isOwnedByNamespace(descriptor)
: "Only classes and namespaces may own backing fields."; : "Only classes and namespaces may own backing fields.";
@@ -109,19 +109,11 @@ public final class TranslationUtils {
} }
@NotNull @NotNull
private static JsNameRef getThisQualifiedNameReference(@NotNull TranslationContext context, public static JsExpression getThisQualifier(@NotNull TranslationContext context,
@NotNull JsName name) { @NotNull DeclarationDescriptor correspondingDeclaration) {
JsExpression qualifier = getThisQualifier(context);
JsNameRef reference = name.makeRef();
AstUtil.setQualifier(reference, qualifier);
return reference;
}
@NotNull
private static JsExpression getThisQualifier(@NotNull TranslationContext context) {
JsExpression qualifier; JsExpression qualifier;
if (context.aliaser().hasAliasForThis()) { if (context.aliaser().hasAliasForThis(correspondingDeclaration)) {
qualifier = context.aliaser().getAliasForThis(); qualifier = context.aliaser().getAliasForThis(correspondingDeclaration);
} else { } else {
qualifier = new JsThisRef(); qualifier = new JsThisRef();
} }
@@ -200,32 +192,26 @@ public final class TranslationUtils {
@Nullable @Nullable
public static JsExpression getImplicitReceiver(@NotNull TranslationContext context, public static JsExpression getImplicitReceiver(@NotNull TranslationContext context,
@NotNull DeclarationDescriptor referencedDescriptor) { @NotNull ReceiverDescriptor receiverDescriptor) {
if (referencedDescriptor instanceof FunctionDescriptor) { DeclarationDescriptor correspondingDescriptor =
if (isExtensionFunction((FunctionDescriptor) referencedDescriptor)) { receiverDescriptor.getType().getConstructor().getDeclarationDescriptor();
return TranslationUtils.getThisQualifier(context); assert correspondingDescriptor != null;
} return context.aliaser().getAliasForReceiver(correspondingDescriptor);
}
return getImplicitReceiverBasedOnOwner(context, referencedDescriptor);
} }
@Nullable @Nullable
public static JsExpression getExtensionFunctionImplicitReceiver(@NotNull TranslationContext context, public static JsExpression getImplicitThis(@NotNull TranslationContext context,
@NotNull CallableDescriptor descriptor) { @NotNull ReceiverDescriptor receiverDescriptor) {
return getImplicitReceiverBasedOnOwner(context, descriptor); if (!receiverDescriptor.exists()) {
return new JsThisRef();
}
DeclarationDescriptor correspondingDescriptor =
receiverDescriptor.getType().getConstructor().getDeclarationDescriptor();
assert correspondingDescriptor != null;
if (!context.aliaser().hasAliasForThis(correspondingDescriptor)) {
return new JsThisRef();
}
return context.aliaser().getAliasForThis(correspondingDescriptor);
} }
@Nullable
private static JsExpression getImplicitReceiverBasedOnOwner(@NotNull TranslationContext context,
@NotNull DeclarationDescriptor referencedDescriptor) {
if (isOwnedByClass(referencedDescriptor)) {
return TranslationUtils.getThisQualifier(context);
}
if (isOwnedByNamespace(referencedDescriptor)) {
if (context.hasQualifierForDescriptor(referencedDescriptor)) {
return context.getQualifierForDescriptor(referencedDescriptor);
}
}
return null;
}
} }
@@ -123,4 +123,10 @@ public class KotlinLibTest extends TranslationTest {
new RhinoFunctionResultChecker("test", true)); new RhinoFunctionResultChecker("test", true));
} }
@Test
public void hashMap() throws Exception {
runRhinoTest(Arrays.asList(kotlinLibraryPath(), cases("hashMap.js")),
new RhinoFunctionResultChecker("test", true));
}
} }
@@ -27,7 +27,12 @@ public final class RhinoSystemOutputChecker implements RhinoResultChecker {
throws Exception { throws Exception {
runMain(context, scope); runMain(context, scope);
String result = getSystemOutput(context, scope); String result = getSystemOutput(context, scope);
assertTrue("Returned:\n" + result + "\n\nExpected:\n" + expectedResult, result.equals(expectedResult)); String trimmedExpected = trimSpace(expectedResult);
String trimmedActual = trimSpace(result);
System.out.println(trimmedActual);
System.out.println(trimmedExpected);
assertTrue("Returned:\n" + trimmedActual + "END_OF_RETURNED\nExpected:\n" + trimmedExpected
+ "END_OF_EXPECTED\n", trimmedExpected.equals(trimmedActual));
} }
private String getSystemOutput(@NotNull Context context, @NotNull Scriptable scope) { private String getSystemOutput(@NotNull Context context, @NotNull Scriptable scope) {
@@ -40,4 +45,13 @@ public final class RhinoSystemOutputChecker implements RhinoResultChecker {
String callToMain = GenerationUtils.generateCallToMain("Anonymous", arguments); String callToMain = GenerationUtils.generateCallToMain("Anonymous", arguments);
context.evaluateString(scope, callToMain, "function call", 0, null); context.evaluateString(scope, callToMain, "function call", 0, null);
} }
public String trimSpace(String s) {
String[] choppedUpString = s.trim().split("\\s");
StringBuilder sb = new StringBuilder();
for (String word : choppedUpString) {
sb.append(word);
}
return sb.toString();
}
} }
@@ -16,8 +16,17 @@ public final class WebDemoExamples2Test extends TranslationTest {
@Test @Test
public void bottles() throws Exception { public void bottles() throws Exception {
testWithMain("bottles", "2", "2");
testWithMain("bottles", ""); testWithMain("bottles", "");
} }
@Test
public void life() throws Exception {
testWithMain("life", "", "2");
}
@Test
public void builder() throws Exception {
testWithMain("builder", "");
}
} }
@@ -0,0 +1,25 @@
var map = new Kotlin.HashMap()
map.put(3, 4)
map.put(6, 3)
function test() {
if (map.containsKey(4) || map.containsKey(5)) return false;
if (map.containsKey({}) || map.containsKey(11)) return false;
if (!map.containsKey(3) || !map.containsKey(6)) return false;
var obj = map.get(3);
if (obj !== 4) return false;
obj = map.get(6);
if (obj !== 3) return false;
if (map.size() !== 2) return false;
map.put(2, 3);
if (map.size() !== 3) return false;
if (!map.containsKey(2) || !map.containsKey(3) || !map.containsKey(6)) return false;
if (!map.containsValue(4) || !map.containsValue(3)) return false;
map.put(2, 1000);
if (map.size() !== 3) return false;
if (map.get(2)!== 1000) return false;
return true;
}
+521
View File
@@ -492,3 +492,524 @@ Kotlin.NumberRange = Kotlin.Class.create({initialize:function (start, size, reve
return new Kotlin.RangeIterator(this.get_start(), this.get_size(), this.get_reversed()); return new Kotlin.RangeIterator(this.get_start(), this.get_size(), this.get_reversed());
} }
}); });
/**
* Copyright 2010 Tim Down.
*
* 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.
*/
/**
* jshashtable
*
* jshashtable is a JavaScript implementation of a hash table. It creates a single constructor function called Hashtable
* in the global scope.
*
* Author: Tim Down <tim@timdown.co.uk>
* Version: 2.1
* Build date: 21 March 2010
* Website: http://www.timdown.co.uk/jshashtable
*/
(function() {
var FUNCTION = "function";
var arrayRemoveAt = (typeof Array.prototype.splice == FUNCTION) ?
function(arr, idx) {
arr.splice(idx, 1);
} :
function(arr, idx) {
var itemsAfterDeleted, i, len;
if (idx === arr.length - 1) {
arr.length = idx;
} else {
itemsAfterDeleted = arr.slice(idx + 1);
arr.length = idx;
for (i = 0, len = itemsAfterDeleted.length; i < len; ++i) {
arr[idx + i] = itemsAfterDeleted[i];
}
}
};
function hashObject(obj) {
var hashCode;
if (typeof obj == "string") {
return obj;
} else if (typeof obj.hashCode == FUNCTION) {
// Check the hashCode method really has returned a string
hashCode = obj.hashCode();
return (typeof hashCode == "string") ? hashCode : hashObject(hashCode);
} else if (typeof obj.toString == FUNCTION) {
return obj.toString();
} else {
try {
return String(obj);
} catch (ex) {
// For host objects (such as ActiveObjects in IE) that have no toString() method and throw an error when
// passed to String()
return Object.prototype.toString.call(obj);
}
}
}
function equals_fixedValueHasEquals(fixedValue, variableValue) {
return fixedValue.equals(variableValue);
}
function equals_fixedValueNoEquals(fixedValue, variableValue) {
return (typeof variableValue.equals == FUNCTION) ?
variableValue.equals(fixedValue) : (fixedValue === variableValue);
}
function createKeyValCheck(kvStr) {
return function(kv) {
if (kv === null) {
throw new Error("null is not a valid " + kvStr);
} else if (typeof kv == "undefined") {
throw new Error(kvStr + " must not be undefined");
}
};
}
var checkKey = createKeyValCheck("key"), checkValue = createKeyValCheck("value");
/*----------------------------------------------------------------------------------------------------------------*/
function Bucket(hash, firstKey, firstValue, equalityFunction) {
this[0] = hash;
this.entries = [];
this.addEntry(firstKey, firstValue);
if (equalityFunction !== null) {
this.getEqualityFunction = function() {
return equalityFunction;
};
}
}
var EXISTENCE = 0, ENTRY = 1, ENTRY_INDEX_AND_VALUE = 2;
function createBucketSearcher(mode) {
return function(key) {
var i = this.entries.length, entry, equals = this.getEqualityFunction(key);
while (i--) {
entry = this.entries[i];
if ( equals(key, entry[0]) ) {
switch (mode) {
case EXISTENCE:
return true;
case ENTRY:
return entry;
case ENTRY_INDEX_AND_VALUE:
return [ i, entry[1] ];
}
}
}
return false;
};
}
function createBucketLister(entryProperty) {
return function(aggregatedArr) {
var startIndex = aggregatedArr.length;
for (var i = 0, len = this.entries.length; i < len; ++i) {
aggregatedArr[startIndex + i] = this.entries[i][entryProperty];
}
};
}
Bucket.prototype = {
getEqualityFunction: function(searchValue) {
return (typeof searchValue.equals == FUNCTION) ? equals_fixedValueHasEquals : equals_fixedValueNoEquals;
},
getEntryForKey: createBucketSearcher(ENTRY),
getEntryAndIndexForKey: createBucketSearcher(ENTRY_INDEX_AND_VALUE),
removeEntryForKey: function(key) {
var result = this.getEntryAndIndexForKey(key);
if (result) {
arrayRemoveAt(this.entries, result[0]);
return result[1];
}
return null;
},
addEntry: function(key, value) {
this.entries[this.entries.length] = [key, value];
},
keys: createBucketLister(0),
values: createBucketLister(1),
getEntries: function(entries) {
var startIndex = entries.length;
for (var i = 0, len = this.entries.length; i < len; ++i) {
// Clone the entry stored in the bucket before adding to array
entries[startIndex + i] = this.entries[i].slice(0);
}
},
containsKey: createBucketSearcher(EXISTENCE),
containsValue: function(value) {
var i = this.entries.length;
while (i--) {
if ( value === this.entries[i][1] ) {
return true;
}
}
return false;
}
};
/*----------------------------------------------------------------------------------------------------------------*/
// Supporting functions for searching hashtable buckets
function searchBuckets(buckets, hash) {
var i = buckets.length, bucket;
while (i--) {
bucket = buckets[i];
if (hash === bucket[0]) {
return i;
}
}
return null;
}
function getBucketForHash(bucketsByHash, hash) {
var bucket = bucketsByHash[hash];
// Check that this is a genuine bucket and not something inherited from the bucketsByHash's prototype
return ( bucket && (bucket instanceof Bucket) ) ? bucket : null;
}
/*----------------------------------------------------------------------------------------------------------------*/
var Hashtable = function(hashingFunctionParam, equalityFunctionParam) {
var that = this;
var buckets = [];
var bucketsByHash = {};
var hashingFunction = (typeof hashingFunctionParam == FUNCTION) ? hashingFunctionParam : hashObject;
var equalityFunction = (typeof equalityFunctionParam == FUNCTION) ? equalityFunctionParam : null;
this.put = function(key, value) {
checkKey(key);
checkValue(value);
var hash = hashingFunction(key), bucket, bucketEntry, oldValue = null;
// Check if a bucket exists for the bucket key
bucket = getBucketForHash(bucketsByHash, hash);
if (bucket) {
// Check this bucket to see if it already contains this key
bucketEntry = bucket.getEntryForKey(key);
if (bucketEntry) {
// This bucket entry is the current mapping of key to value, so replace old value and we're done.
oldValue = bucketEntry[1];
bucketEntry[1] = value;
} else {
// The bucket does not contain an entry for this key, so add one
bucket.addEntry(key, value);
}
} else {
// No bucket exists for the key, so create one and put our key/value mapping in
bucket = new Bucket(hash, key, value, equalityFunction);
buckets[buckets.length] = bucket;
bucketsByHash[hash] = bucket;
}
return oldValue;
};
this.get = function(key) {
checkKey(key);
var hash = hashingFunction(key);
// Check if a bucket exists for the bucket key
var bucket = getBucketForHash(bucketsByHash, hash);
if (bucket) {
// Check this bucket to see if it contains this key
var bucketEntry = bucket.getEntryForKey(key);
if (bucketEntry) {
// This bucket entry is the current mapping of key to value, so return the value.
return bucketEntry[1];
}
}
return null;
};
this.containsKey = function(key) {
checkKey(key);
var bucketKey = hashingFunction(key);
// Check if a bucket exists for the bucket key
var bucket = getBucketForHash(bucketsByHash, bucketKey);
return bucket ? bucket.containsKey(key) : false;
};
this.containsValue = function(value) {
checkValue(value);
var i = buckets.length;
while (i--) {
if (buckets[i].containsValue(value)) {
return true;
}
}
return false;
};
this.clear = function() {
buckets.length = 0;
bucketsByHash = {};
};
this.isEmpty = function() {
return !buckets.length;
};
var createBucketAggregator = function(bucketFuncName) {
return function() {
var aggregated = [], i = buckets.length;
while (i--) {
buckets[i][bucketFuncName](aggregated);
}
return aggregated;
};
};
this.keys = createBucketAggregator("keys");
this.values = createBucketAggregator("values");
this.entries = createBucketAggregator("getEntries");
this.remove = function(key) {
checkKey(key);
var hash = hashingFunction(key), bucketIndex, oldValue = null;
// Check if a bucket exists for the bucket key
var bucket = getBucketForHash(bucketsByHash, hash);
if (bucket) {
// Remove entry from this bucket for this key
oldValue = bucket.removeEntryForKey(key);
if (oldValue !== null) {
// Entry was removed, so check if bucket is empty
if (!bucket.entries.length) {
// Bucket is empty, so remove it from the bucket collections
bucketIndex = searchBuckets(buckets, hash);
arrayRemoveAt(buckets, bucketIndex);
delete bucketsByHash[hash];
}
}
}
return oldValue;
};
this.size = function() {
var total = 0, i = buckets.length;
while (i--) {
total += buckets[i].entries.length;
}
return total;
};
this.each = function(callback) {
var entries = that.entries(), i = entries.length, entry;
while (i--) {
entry = entries[i];
callback(entry[0], entry[1]);
}
};
this.putAll = function(hashtable, conflictCallback) {
var entries = hashtable.entries();
var entry, key, value, thisValue, i = entries.length;
var hasConflictCallback = (typeof conflictCallback == FUNCTION);
while (i--) {
entry = entries[i];
key = entry[0];
value = entry[1];
// Check for a conflict. The default behaviour is to overwrite the value for an existing key
if ( hasConflictCallback && (thisValue = that.get(key)) ) {
value = conflictCallback(key, thisValue, value);
}
that.put(key, value);
}
};
this.clone = function() {
var clone = new Hashtable(hashingFunctionParam, equalityFunctionParam);
clone.putAll(that);
return clone;
};
this.keySet = function() {
var res = new Kotlin.HashSet();
var keys = this.keys();
var i = keys.length;
while (i--) {
res.add(keys[i]);
}
return res;
}
};
Kotlin.HashTable = Hashtable;
})();
Kotlin.HashMap = Kotlin.Class.create(
{
initialize : function() {
Kotlin.HashTable.call(this);
}
}
);
Kotlin.StringBuilder = Kotlin.Class.create(
{
initialize : function() {
this.string = "";
},
append : function(obj) {
this.string = this.string + obj.toString();
}
}
);
/**
* Copyright 2010 Tim Down.
*
* 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.
*/
/**
* HashSet
*
* This is a JavaScript implementation of HashSet, similar in concept to those found in Java or C#'s standard libraries.
* It is distributed as part of jshashtable and depends on jshashtable.js. It creates a single constructor function
* called HashSet in the global scope.
*
* Author: Tim Down <tim@timdown.co.uk>
* Version: 2.1
* Build date: 27 March 2010
* Website: http://www.timdown.co.uk/jshashtable/
*/
(function() {
function HashSet(hashingFunction, equalityFunction) {
var hashTable = new Hashtable(hashingFunction, equalityFunction);
this.add = function(o) {
hashTable.put(o, true);
};
this.addAll = function(arr) {
var i = arr.length;
while (i--) {
hashTable.put(arr[i], true);
}
};
this.values = function() {
return hashTable.keys();
};
this.remove = function(o) {
return hashTable.remove(o) ? o : null;
};
this.contains = function(o) {
return hashTable.containsKey(o);
};
this.clear = function() {
hashTable.clear();
};
this.size = function() {
return hashTable.size();
};
this.isEmpty = function() {
return hashTable.isEmpty();
};
this.clone = function() {
var h = new HashSet(hashingFunction, equalityFunction);
h.addAll(hashTable.keys());
return h;
};
this.intersection = function(hashSet) {
var intersection = new HashSet(hashingFunction, equalityFunction);
var values = hashSet.values(), i = values.length, val;
while (i--) {
val = values[i];
if (hashTable.containsKey(val)) {
intersection.add(val);
}
}
return intersection;
};
this.union = function(hashSet) {
var union = this.clone();
var values = hashSet.values(), i = values.length, val;
while (i--) {
val = values[i];
if (!hashTable.containsKey(val)) {
union.add(val);
}
}
return union;
};
this.isSubsetOf = function(hashSet) {
var values = hashTable.keys(), i = values.length;
while (i--) {
if (!hashSet.contains(values[i])) {
return false;
}
}
return true;
};
}
Kotlin.HashSet = Kotlin.Class.create(
{
initialize : function() {
HashSet.call(this);
}
}
)
}());
@@ -0,0 +1,16 @@
/**
* Copyright 2010 Tim Down.
*
* 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.
*/
function HashSet(c,a){var b=new Hashtable(c,a);this.add=function(d){b.put(d,true)};this.addAll=function(d){var e=d.length;while(e--){b.put(d[e],true)}};this.values=function(){return b.keys()};this.remove=function(d){return b.remove(d)?d:null};this.contains=function(d){return b.containsKey(d)};this.clear=function(){b.clear()};this.size=function(){return b.size()};this.isEmpty=function(){return b.isEmpty()};this.clone=function(){var d=new HashSet(c,a);d.addAll(b.keys());return d};this.intersection=function(d){var h=new HashSet(c,a);var e=d.values(),f=e.length,g;while(f--){g=e[f];if(b.containsKey(g)){h.add(g)}}return h};this.union=function(d){var g=this.clone();var e=d.values(),f=e.length,h;while(f--){h=e[f];if(!b.containsKey(h)){g.add(h)}}return g};this.isSubsetOf=function(d){var e=b.keys(),f=e.length;while(f--){if(!d.contains(e[f])){return false}}return true}};
@@ -0,0 +1,107 @@
/**
* Copyright 2010 Tim Down.
*
* 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.
*/
/**
* HashSet
*
* This is a JavaScript implementation of HashSet, similar in concept to those found in Java or C#'s standard libraries.
* It is distributed as part of jshashtable and depends on jshashtable.js. It creates a single constructor function
* called HashSet in the global scope.
*
* Author: Tim Down <tim@timdown.co.uk>
* Version: 2.1
* Build date: 27 March 2010
* Website: http://www.timdown.co.uk/jshashtable/
*/
function HashSet(hashingFunction, equalityFunction) {
var hashTable = new Hashtable(hashingFunction, equalityFunction);
this.add = function(o) {
hashTable.put(o, true);
};
this.addAll = function(arr) {
var i = arr.length;
while (i--) {
hashTable.put(arr[i], true);
}
};
this.values = function() {
return hashTable.keys();
};
this.remove = function(o) {
return hashTable.remove(o) ? o : null;
};
this.contains = function(o) {
return hashTable.containsKey(o);
};
this.clear = function() {
hashTable.clear();
};
this.size = function() {
return hashTable.size();
};
this.isEmpty = function() {
return hashTable.isEmpty();
};
this.clone = function() {
var h = new HashSet(hashingFunction, equalityFunction);
h.addAll(hashTable.keys());
return h;
};
this.intersection = function(hashSet) {
var intersection = new HashSet(hashingFunction, equalityFunction);
var values = hashSet.values(), i = values.length, val;
while (i--) {
val = values[i];
if (hashTable.containsKey(val)) {
intersection.add(val);
}
}
return intersection;
};
this.union = function(hashSet) {
var union = this.clone();
var values = hashSet.values(), i = values.length, val;
while (i--) {
val = values[i];
if (!hashTable.containsKey(val)) {
union.add(val);
}
}
return union;
};
this.isSubsetOf = function(hashSet) {
var values = hashTable.keys(), i = values.length;
while (i--) {
if (!hashSet.contains(values[i])) {
return false;
}
}
return true;
};
}
@@ -0,0 +1,16 @@
/**
* Copyright 2010 Tim Down.
*
* 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.
*/
var Hashtable=(function(){var p="function";var n=(typeof Array.prototype.splice==p)?function(s,r){s.splice(r,1)}:function(u,t){var s,v,r;if(t===u.length-1){u.length=t}else{s=u.slice(t+1);u.length=t;for(v=0,r=s.length;v<r;++v){u[t+v]=s[v]}}};function a(t){var r;if(typeof t=="string"){return t}else{if(typeof t.hashCode==p){r=t.hashCode();return(typeof r=="string")?r:a(r)}else{if(typeof t.toString==p){return t.toString()}else{try{return String(t)}catch(s){return Object.prototype.toString.call(t)}}}}}function g(r,s){return r.equals(s)}function e(r,s){return(typeof s.equals==p)?s.equals(r):(r===s)}function c(r){return function(s){if(s===null){throw new Error("null is not a valid "+r)}else{if(typeof s=="undefined"){throw new Error(r+" must not be undefined")}}}}var q=c("key"),l=c("value");function d(u,s,t,r){this[0]=u;this.entries=[];this.addEntry(s,t);if(r!==null){this.getEqualityFunction=function(){return r}}}var h=0,j=1,f=2;function o(r){return function(t){var s=this.entries.length,v,u=this.getEqualityFunction(t);while(s--){v=this.entries[s];if(u(t,v[0])){switch(r){case h:return true;case j:return v;case f:return[s,v[1]]}}}return false}}function k(r){return function(u){var v=u.length;for(var t=0,s=this.entries.length;t<s;++t){u[v+t]=this.entries[t][r]}}}d.prototype={getEqualityFunction:function(r){return(typeof r.equals==p)?g:e},getEntryForKey:o(j),getEntryAndIndexForKey:o(f),removeEntryForKey:function(s){var r=this.getEntryAndIndexForKey(s);if(r){n(this.entries,r[0]);return r[1]}return null},addEntry:function(r,s){this.entries[this.entries.length]=[r,s]},keys:k(0),values:k(1),getEntries:function(s){var u=s.length;for(var t=0,r=this.entries.length;t<r;++t){s[u+t]=this.entries[t].slice(0)}},containsKey:o(h),containsValue:function(s){var r=this.entries.length;while(r--){if(s===this.entries[r][1]){return true}}return false}};function m(s,t){var r=s.length,u;while(r--){u=s[r];if(t===u[0]){return r}}return null}function i(r,s){var t=r[s];return(t&&(t instanceof d))?t:null}function b(t,r){var w=this;var v=[];var u={};var x=(typeof t==p)?t:a;var s=(typeof r==p)?r:null;this.put=function(B,C){q(B);l(C);var D=x(B),E,A,z=null;E=i(u,D);if(E){A=E.getEntryForKey(B);if(A){z=A[1];A[1]=C}else{E.addEntry(B,C)}}else{E=new d(D,B,C,s);v[v.length]=E;u[D]=E}return z};this.get=function(A){q(A);var B=x(A);var C=i(u,B);if(C){var z=C.getEntryForKey(A);if(z){return z[1]}}return null};this.containsKey=function(A){q(A);var z=x(A);var B=i(u,z);return B?B.containsKey(A):false};this.containsValue=function(A){l(A);var z=v.length;while(z--){if(v[z].containsValue(A)){return true}}return false};this.clear=function(){v.length=0;u={}};this.isEmpty=function(){return !v.length};var y=function(z){return function(){var A=[],B=v.length;while(B--){v[B][z](A)}return A}};this.keys=y("keys");this.values=y("values");this.entries=y("getEntries");this.remove=function(B){q(B);var C=x(B),z,A=null;var D=i(u,C);if(D){A=D.removeEntryForKey(B);if(A!==null){if(!D.entries.length){z=m(v,C);n(v,z);delete u[C]}}}return A};this.size=function(){var A=0,z=v.length;while(z--){A+=v[z].entries.length}return A};this.each=function(C){var z=w.entries(),A=z.length,B;while(A--){B=z[A];C(B[0],B[1])}};this.putAll=function(H,C){var B=H.entries();var E,F,D,z,A=B.length;var G=(typeof C==p);while(A--){E=B[A];F=E[0];D=E[1];if(G&&(z=w.get(F))){D=C(F,z,D)}w.put(F,D)}};this.clone=function(){var z=new b(t,r);z.putAll(w);return z}}return b})();
@@ -0,0 +1,370 @@
/**
* Copyright 2010 Tim Down.
*
* 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.
*/
/**
* jshashtable
*
* jshashtable is a JavaScript implementation of a hash table. It creates a single constructor function called Hashtable
* in the global scope.
*
* Author: Tim Down <tim@timdown.co.uk>
* Version: 2.1
* Build date: 21 March 2010
* Website: http://www.timdown.co.uk/jshashtable
*/
var Hashtable = (function() {
var FUNCTION = "function";
var arrayRemoveAt = (typeof Array.prototype.splice == FUNCTION) ?
function(arr, idx) {
arr.splice(idx, 1);
} :
function(arr, idx) {
var itemsAfterDeleted, i, len;
if (idx === arr.length - 1) {
arr.length = idx;
} else {
itemsAfterDeleted = arr.slice(idx + 1);
arr.length = idx;
for (i = 0, len = itemsAfterDeleted.length; i < len; ++i) {
arr[idx + i] = itemsAfterDeleted[i];
}
}
};
function hashObject(obj) {
var hashCode;
if (typeof obj == "string") {
return obj;
} else if (typeof obj.hashCode == FUNCTION) {
// Check the hashCode method really has returned a string
hashCode = obj.hashCode();
return (typeof hashCode == "string") ? hashCode : hashObject(hashCode);
} else if (typeof obj.toString == FUNCTION) {
return obj.toString();
} else {
try {
return String(obj);
} catch (ex) {
// For host objects (such as ActiveObjects in IE) that have no toString() method and throw an error when
// passed to String()
return Object.prototype.toString.call(obj);
}
}
}
function equals_fixedValueHasEquals(fixedValue, variableValue) {
return fixedValue.equals(variableValue);
}
function equals_fixedValueNoEquals(fixedValue, variableValue) {
return (typeof variableValue.equals == FUNCTION) ?
variableValue.equals(fixedValue) : (fixedValue === variableValue);
}
function createKeyValCheck(kvStr) {
return function(kv) {
if (kv === null) {
throw new Error("null is not a valid " + kvStr);
} else if (typeof kv == "undefined") {
throw new Error(kvStr + " must not be undefined");
}
};
}
var checkKey = createKeyValCheck("key"), checkValue = createKeyValCheck("value");
/*----------------------------------------------------------------------------------------------------------------*/
function Bucket(hash, firstKey, firstValue, equalityFunction) {
this[0] = hash;
this.entries = [];
this.addEntry(firstKey, firstValue);
if (equalityFunction !== null) {
this.getEqualityFunction = function() {
return equalityFunction;
};
}
}
var EXISTENCE = 0, ENTRY = 1, ENTRY_INDEX_AND_VALUE = 2;
function createBucketSearcher(mode) {
return function(key) {
var i = this.entries.length, entry, equals = this.getEqualityFunction(key);
while (i--) {
entry = this.entries[i];
if ( equals(key, entry[0]) ) {
switch (mode) {
case EXISTENCE:
return true;
case ENTRY:
return entry;
case ENTRY_INDEX_AND_VALUE:
return [ i, entry[1] ];
}
}
}
return false;
};
}
function createBucketLister(entryProperty) {
return function(aggregatedArr) {
var startIndex = aggregatedArr.length;
for (var i = 0, len = this.entries.length; i < len; ++i) {
aggregatedArr[startIndex + i] = this.entries[i][entryProperty];
}
};
}
Bucket.prototype = {
getEqualityFunction: function(searchValue) {
return (typeof searchValue.equals == FUNCTION) ? equals_fixedValueHasEquals : equals_fixedValueNoEquals;
},
getEntryForKey: createBucketSearcher(ENTRY),
getEntryAndIndexForKey: createBucketSearcher(ENTRY_INDEX_AND_VALUE),
removeEntryForKey: function(key) {
var result = this.getEntryAndIndexForKey(key);
if (result) {
arrayRemoveAt(this.entries, result[0]);
return result[1];
}
return null;
},
addEntry: function(key, value) {
this.entries[this.entries.length] = [key, value];
},
keys: createBucketLister(0),
values: createBucketLister(1),
getEntries: function(entries) {
var startIndex = entries.length;
for (var i = 0, len = this.entries.length; i < len; ++i) {
// Clone the entry stored in the bucket before adding to array
entries[startIndex + i] = this.entries[i].slice(0);
}
},
containsKey: createBucketSearcher(EXISTENCE),
containsValue: function(value) {
var i = this.entries.length;
while (i--) {
if ( value === this.entries[i][1] ) {
return true;
}
}
return false;
}
};
/*----------------------------------------------------------------------------------------------------------------*/
// Supporting functions for searching hashtable buckets
function searchBuckets(buckets, hash) {
var i = buckets.length, bucket;
while (i--) {
bucket = buckets[i];
if (hash === bucket[0]) {
return i;
}
}
return null;
}
function getBucketForHash(bucketsByHash, hash) {
var bucket = bucketsByHash[hash];
// Check that this is a genuine bucket and not something inherited from the bucketsByHash's prototype
return ( bucket && (bucket instanceof Bucket) ) ? bucket : null;
}
/*----------------------------------------------------------------------------------------------------------------*/
function Hashtable(hashingFunctionParam, equalityFunctionParam) {
var that = this;
var buckets = [];
var bucketsByHash = {};
var hashingFunction = (typeof hashingFunctionParam == FUNCTION) ? hashingFunctionParam : hashObject;
var equalityFunction = (typeof equalityFunctionParam == FUNCTION) ? equalityFunctionParam : null;
this.put = function(key, value) {
checkKey(key);
checkValue(value);
var hash = hashingFunction(key), bucket, bucketEntry, oldValue = null;
// Check if a bucket exists for the bucket key
bucket = getBucketForHash(bucketsByHash, hash);
if (bucket) {
// Check this bucket to see if it already contains this key
bucketEntry = bucket.getEntryForKey(key);
if (bucketEntry) {
// This bucket entry is the current mapping of key to value, so replace old value and we're done.
oldValue = bucketEntry[1];
bucketEntry[1] = value;
} else {
// The bucket does not contain an entry for this key, so add one
bucket.addEntry(key, value);
}
} else {
// No bucket exists for the key, so create one and put our key/value mapping in
bucket = new Bucket(hash, key, value, equalityFunction);
buckets[buckets.length] = bucket;
bucketsByHash[hash] = bucket;
}
return oldValue;
};
this.get = function(key) {
checkKey(key);
var hash = hashingFunction(key);
// Check if a bucket exists for the bucket key
var bucket = getBucketForHash(bucketsByHash, hash);
if (bucket) {
// Check this bucket to see if it contains this key
var bucketEntry = bucket.getEntryForKey(key);
if (bucketEntry) {
// This bucket entry is the current mapping of key to value, so return the value.
return bucketEntry[1];
}
}
return null;
};
this.containsKey = function(key) {
checkKey(key);
var bucketKey = hashingFunction(key);
// Check if a bucket exists for the bucket key
var bucket = getBucketForHash(bucketsByHash, bucketKey);
return bucket ? bucket.containsKey(key) : false;
};
this.containsValue = function(value) {
checkValue(value);
var i = buckets.length;
while (i--) {
if (buckets[i].containsValue(value)) {
return true;
}
}
return false;
};
this.clear = function() {
buckets.length = 0;
bucketsByHash = {};
};
this.isEmpty = function() {
return !buckets.length;
};
var createBucketAggregator = function(bucketFuncName) {
return function() {
var aggregated = [], i = buckets.length;
while (i--) {
buckets[i][bucketFuncName](aggregated);
}
return aggregated;
};
};
this.keys = createBucketAggregator("keys");
this.values = createBucketAggregator("values");
this.entries = createBucketAggregator("getEntries");
this.remove = function(key) {
checkKey(key);
var hash = hashingFunction(key), bucketIndex, oldValue = null;
// Check if a bucket exists for the bucket key
var bucket = getBucketForHash(bucketsByHash, hash);
if (bucket) {
// Remove entry from this bucket for this key
oldValue = bucket.removeEntryForKey(key);
if (oldValue !== null) {
// Entry was removed, so check if bucket is empty
if (!bucket.entries.length) {
// Bucket is empty, so remove it from the bucket collections
bucketIndex = searchBuckets(buckets, hash);
arrayRemoveAt(buckets, bucketIndex);
delete bucketsByHash[hash];
}
}
}
return oldValue;
};
this.size = function() {
var total = 0, i = buckets.length;
while (i--) {
total += buckets[i].entries.length;
}
return total;
};
this.each = function(callback) {
var entries = that.entries(), i = entries.length, entry;
while (i--) {
entry = entries[i];
callback(entry[0], entry[1]);
}
};
this.putAll = function(hashtable, conflictCallback) {
var entries = hashtable.entries();
var entry, key, value, thisValue, i = entries.length;
var hasConflictCallback = (typeof conflictCallback == FUNCTION);
while (i--) {
entry = entries[i];
key = entry[0];
value = entry[1];
// Check for a conflict. The default behaviour is to overwrite the value for an existing key
if ( hasConflictCallback && (thisValue = that.get(key)) ) {
value = conflictCallback(key, thisValue, value);
}
that.put(key, value);
}
};
this.clone = function() {
var clone = new Hashtable(hashingFunctionParam, equalityFunctionParam);
clone.putAll(that);
return clone;
};
}
return Hashtable;
})();
@@ -29,7 +29,7 @@
import js.* import js.*
fun main(args : Array<String>) { fun main(args : Array<String>) {
if (args.isEmpty) { if (args.isEmpty()) {
printBottles(99) printBottles(99)
} }
else { else {
@@ -72,4 +72,4 @@ fun bottlesOfBeer(count : Int) : String =
// From the std package // From the std package
// This is an extension property, i.e. a property that is defined for the // This is an extension property, i.e. a property that is defined for the
// type Array<T>, but does not sit inside the class Array // type Array<T>, but does not sit inside the class Array
val <T> Array<T>.isEmpty : Boolean get() = size == 0 fun <T> Array<T>.isEmpty() = size == 0
@@ -0,0 +1,145 @@
/**
* This is an example of a Type-Safe Groovy-style Builder
*
* Builders are good for declaratively describing data in your code.
* In this example we show how to describe an HTML page in Kotlin.
*
* See this page for details:
* http://confluence.jetbrains.net/display/Kotlin/Type-safe+Groovy-style+builders
*/
import js.*
import java.util.*
fun main(args : Array<String>) {
val result =
html {
head {
title {+"XML encoding with Kotlin"}
}
body {
h1 {+"XML encoding with Kotlin"}
p {+"this format can be used as an alternative markup to XML"}
// an element with attributes and text content
a(href = "http://jetbrains.com/kotlin") {+"Kotlin"}
// mixed content
p {
+"This is some"
b {+"mixed"}
+"text. For more see the"
a(href = "http://jetbrains.com/kotlin") {+"Kotlin"}
+"project"
}
p {+"some text"}
// content generated from command-line arguments
p {
+"Command line arguments were:"
ul {
for (arg in args)
li {+arg}
}
}
}
}
println(result)
}
trait Element {
fun render(builder : StringBuilder, indent : String)
fun toString() : String? {
val builder = StringBuilder()
render(builder, "")
return builder.toString()
}
}
class TextElement(val text : String) : Element {
override fun render(builder : StringBuilder, indent : String) {
builder.append("$indent$text\n")
}
}
abstract class Tag(val name : String) : Element {
val children = ArrayList<Element>()
val attributes = HashMap<String, String>()
protected fun initTag<T : Element>(tag : T, init : T.() -> Unit) : T {
tag.init()
children.add(tag)
return tag
}
override fun render(builder : StringBuilder, indent : String) {
builder.append("$indent<$name${renderAttributes()}>\n")
for (c in children) {
c.render(builder, indent + " ")
}
builder.append("$indent</$name>\n")
}
private fun renderAttributes() : String? {
val builder = StringBuilder()
for (a in attributes.keySet()) {
builder.append(" $a=\"${attributes[a]}\"")
}
return builder.toString()
}
}
abstract class TagWithText(name : String) : Tag(name) {
fun String.plus() {
children.add(TextElement(this))
}
}
class HTML() : TagWithText("html") {
fun head(init : Head.() -> Unit) = initTag(Head(), init)
fun body(init : Body.() -> Unit) = initTag(Body(), init)
}
class Head() : TagWithText("head") {
fun title(init : Title.() -> Unit) = initTag(Title(), init)
}
class Title() : TagWithText("title")
abstract class BodyTag(name : String) : TagWithText(name) {
fun b(init : B.() -> Unit) = initTag(B(), init)
fun p(init : P.() -> Unit) = initTag(P(), init)
fun h1(init : H1.() -> Unit) = initTag(H1(), init)
fun ul(init : UL.() -> Unit) = initTag(UL(), init)
fun a(href : String, init : A.() -> Unit) {
val a = initTag(A(), init)
a.href = href
}
}
class Body() : BodyTag("body")
class UL() : BodyTag("ul") {
fun li(init : LI.() -> Unit) = initTag(LI(), init)
}
class B() : BodyTag("b")
class LI() : BodyTag("li")
class P() : BodyTag("p")
class H1() : BodyTag("h1")
class A() : BodyTag("a") {
public var href : String
get() = attributes["href"]
set(value) {
attributes["href"] = value
}
}
fun html(init : HTML.() -> Unit) : HTML {
val html = HTML()
html.init()
return html
}
// An excerpt from the Standard Library
fun <K, V> Map<K, V>.set(key : K, value : V) = this.put(key, value)
@@ -0,0 +1,178 @@
/**
* This is a straightforward implementation of The Game of Life
* See http://en.wikipedia.org/wiki/Conway's_Game_of_Life
*/
package life
import java.util.*
/*
* A field where cells live. Effectively immutable
*/
class Field(
val width : Int,
val height : Int,
// This function tells the constructor which cells are alive
// if init(i, j) is true, the cell (i, j) is alive
init : (Int, Int) -> Boolean
) {
private val live : Array<Array<Boolean>> = Array(height) {i -> Array(width) {j -> init(i, j)}}
private fun liveCount(i : Int, j : Int)
= if (i in 0..height-1 &&
j in 0..width-1 &&
live[i][j]) 1 else 0
// How many neighbors of (i, j) are alive?
fun liveNeighbors(i : Int, j : Int) =
liveCount(i - 1, j - 1) +
liveCount(i - 1, j) +
liveCount(i - 1, j + 1) +
liveCount(i, j - 1) +
liveCount(i, j + 1) +
liveCount(i + 1, j - 1) +
liveCount(i + 1, j) +
liveCount(i + 1, j + 1)
// You can say field[i, j], and this function gets called
fun get(i : Int, j : Int) = live[i][j]
}
/**
* This function takes the present state of the field
* and return a new field representing the next moment of time
*/
fun next(field : Field) : Field {
return Field(field.width, field.height) {i, j ->
val n = field.liveNeighbors(i, j)
if (field[i, j])
// (i, j) is alive
n in 2..3 // It remains alive iff it has 2 or 3 neighbors
else
// (i, j) is dead
n == 3 // A new cell is born if there are 3 neighbors alive
}
}
/** A few colony examples here */
fun main(args : Array<String>) {
// Simplistic demo
printField("***", 3)
// "Star burst"
printField("""
__*__
_***_
__*__
""", 10)
// Stable colony
printField("""
__*__
_*_*_
__*__
""", 3)
// Stable from the step 2
printField("""
__**__
__**__
__**__
""", 3)
// Oscillating colony
printField("""
__**__
__**__
__**__
__**__
""", 6)
// A fancier oscillating colony
printField("""
---------------
---***---***---
---------------
-*----*-*----*-
-*----*-*----*-
-*----*-*----*-
---***---***---
---------------
---***---***---
-*----*-*----*-
-*----*-*----*-
-*----*-*----*-
---------------
---***---***---
---------------
""", 10)
}
// UTILITIES
fun printField(s : String, steps : Int) {
var field = makeField(s)
for (step in 1..steps) {
println("Step: $step")
for (i in 0..field.height-1) {
for (j in 0..field.width-1) {
print(if (field[i, j]) "*" else " ")
}
println("")
}
field = next(field)
}
}
fun makeField(s : String) : Field {
val lines = s.split("\n").sure()
val w = max<String?>(lines.toList(), comparator<String?> {o1, o2 ->
val l1 : Int = o1?.size ?: 0
val l2 = o2?.size ?: 0
l1 - l2
}).sure()
val data = Array(lines.size) {Array(w.size) {false}}
// workaround
for (i in data.indices) {
data[i] = Array(w.size) {false}
for (j in data[i].indices)
data[i][j] = false
}
for (line in lines.indices) {
for (x in lines[line].indices) {
val c = lines[line].sure()[x]
data[line][x] = c == '*'
}
}
return Field(w.size, lines.size) {i, j -> data[i][j]}
}
// An excerpt from the Standard Library
val String?.indices : IntRange get() = IntRange(0, this.sure().size)
fun <K, V> Map<K, V>.set(k : K, v : V) { put(k, v) }
fun comparator<T> (f : (T, T) -> Int) : Comparator<T> = object : Comparator<T> {
override fun compare(o1 : T, o2 : T) : Int = f(o1, o2)
}
fun println(message : Any?) {
System.out?.println(message)
}
fun print(message : Any?) {
System.out?.print(message)
}
val <T> Array<T>.isEmpty : Boolean get() = size == 0
fun String.split(s : String) = (this as java.lang.String).split(s)
val String.size : Int
get() = length
fun <T, C: Collection<T>> Array<T>.to(result: C) : C {
for (elem in this)
result.add(elem)
return result
}
fun <T> Array<T>.toList() : List<T> = this.to(ArrayList<T>())
@@ -298,4 +298,4 @@ Take one down, pass it around, 1 bottle of beer on the wall.
Take one down, pass it around, no more bottles of beer on the wall. Take one down, pass it around, no more bottles of beer on the wall.
No more bottles of beer on the wall, no more bottles of beer. No more bottles of beer on the wall, no more bottles of beer.
Go to the store and buy some more, 99 bottles of beer on the wall. Go to the store and buy some more, 99 bottles of beer on the wall.
@@ -0,0 +1,12 @@
The "2 bottles of beer" song
2 bottles of beer on the wall, 2 bottles of beer.
Take one down, pass it around, 1 bottle of beer on the wall.
1 bottle of beer on the wall, 1 bottle of beer.
Take one down, pass it around, no more bottles of beer on the wall.
No more bottles of beer on the wall, no more bottles of beer.
Go to the store and buy some more, 2 bottles of beer on the wall.
@@ -0,0 +1,37 @@
<html>
<head>
<title>
XML encoding with Kotlin
</title>
</head>
<body>
<h1>
XML encoding with Kotlin
</h1>
<p>
this format can be used as an alternative markup to XML
</p>
<a href="http://jetbrains.com/kotlin">
Kotlin
</a>
<p>
This is some
<b>
mixed
</b>
text. For more see the
<a href="http://jetbrains.com/kotlin">
Kotlin
</a>
project
</p>
<p>
some text
</p>
<p>
Command line arguments were:
<ul>
</ul>
</p>
</body>
</html>
@@ -0,0 +1,323 @@
Step: 1
***
Step: 2
*
Step: 3
Step: 1
*
***
*
Step: 2
***
* *
***
Step: 3
*
* *
* *
* *
*
Step: 4
*
***
** **
***
*
Step: 5
***
* *
* *
* *
***
Step: 6
***
* * *
*** **
* * *
***
Step: 7
***
* *
* *
* *
***
Step: 8
**
* * *
*** **
* * *
**
Step: 9
***
* * *
* * *
* * *
***
Step: 10
***
* *
** * *
* *
***
Step: 1
*
* *
*
Step: 2
*
* *
*
Step: 3
*
* *
*
Step: 1
**
**
**
Step: 2
**
* *
**
Step: 3
**
* *
**
Step: 1
**
**
**
**
Step: 2
**
*
*
**
Step: 3
**
**
**
**
Step: 4
**
*
*
**
Step: 5
**
**
**
**
Step: 6
**
*
*
**
Step: 1
*** ***
* * * *
* * * *
* * * *
*** ***
*** ***
* * * *
* * * *
* * * *
*** ***
Step: 2
* *
* *
** **
*** ** ** ***
* * * * * *
** **
** **
* * * * * *
*** ** ** ***
** **
* *
* *
Step: 3
** **
** **
* * * * * *
*** ** ** ***
* * * * * *
*** ***
*** ***
* * * * * *
*** ** ** ***
* * * * * *
** **
** **
Step: 4
*** ***
* * * *
* * * *
* * * *
*** ***
*** ***
* * * *
* * * *
* * * *
*** ***
Step: 5
* *
* *
** **
*** ** ** ***
* * * * * *
** **
** **
* * * * * *
*** ** ** ***
** **
* *
* *
Step: 6
** **
** **
* * * * * *
*** ** ** ***
* * * * * *
*** ***
*** ***
* * * * * *
*** ** ** ***
* * * * * *
** **
** **
Step: 7
*** ***
* * * *
* * * *
* * * *
*** ***
*** ***
* * * *
* * * *
* * * *
*** ***
Step: 8
* *
* *
** **
*** ** ** ***
* * * * * *
** **
** **
* * * * * *
*** ** ** ***
** **
* *
* *
Step: 9
** **
** **
* * * * * *
*** ** ** ***
* * * * * *
*** ***
*** ***
* * * * * *
*** ** ** ***
* * * * * *
** **
** **
Step: 10
*** ***
* * * *
* * * *
* * * *
*** ***
*** ***
* * * *
* * * *
* * * *
*** ***