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.Map;
import java.util.Stack;
//TODO: code gets duplicated
public class Aliaser {
public static Aliaser newInstance() {
return new Aliaser();
}
//TODO: rename
@NotNull
private final Map<DeclarationDescriptor, JsName> aliasesForDescriptors = new HashMap<DeclarationDescriptor, JsName>();
@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() {
}
@NotNull
public JsNameRef getAliasForThis() {
assert !thisAliases.empty() : "No alias. Use hasAliasForThis function to check.";
return thisAliases.peek().makeRef();
public JsNameRef getAliasForThis(@NotNull DeclarationDescriptor descriptor) {
JsName aliasName = aliasesForThis.get(descriptor.getOriginal());
assert aliasName != null : "This " + descriptor.getOriginal() + " doesn't have an alias.";
return aliasName.makeRef();
}
public void setAliasForThis(@NotNull JsName alias) {
thisAliases.push(alias);
public void setAliasForThis(@NotNull DeclarationDescriptor descriptor, @NotNull JsName 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 JsName aliasToRemove) {
assert !thisAliases.empty() : "Error: removing alias, when it is not set.";
JsName lastAlias = thisAliases.pop();
assert lastAlias.equals(aliasToRemove) : "Error: inconsistent alias removing.";
public void removeAliasForThis(@NotNull DeclarationDescriptor descriptor) {
JsName removed = aliasesForThis.remove(descriptor.getOriginal());
assert removed != null;
}
public boolean hasAliasForThis() {
return (!thisAliases.empty());
public boolean hasAliasForThis(@NotNull DeclarationDescriptor descriptor) {
return (aliasesForThis.containsKey(descriptor.getOriginal()));
}
public boolean hasAliasForDeclaration(@NotNull DeclarationDescriptor declaration) {
return aliasesForDescriptors.containsKey(declaration.getOriginal());
@NotNull
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
public JsName getAliasForDeclaration(@NotNull DeclarationDescriptor declaration) {
JsName alias = aliasesForDescriptors.get(declaration.getOriginal());
@@ -64,4 +83,10 @@ public class Aliaser {
assert (hasAliasForDeclaration(declaration.getOriginal())) : "This declaration does not has an alias!";
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);
declareString(standardClasses);
declareJavaArrayList(standardClasses);
declareJavaHasMap(standardClasses);
declareJavaStringBuilder(standardClasses);
declareTopLevelFunctions(standardClasses);
declareJavaCollection(standardClasses);
declareMap(standardClasses);
declareInteger(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
private static void declareTopLevelFunctions(@NotNull StandardClasses standardClasses) {
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.k2js.translate.intrinsic.Intrinsics;
import org.jetbrains.k2js.translate.utils.BindingUtils;
import org.jetbrains.k2js.translate.utils.TranslationUtils;
import static org.jetbrains.jet.lang.resolve.DescriptorUtils.getFQName;
@@ -165,14 +166,15 @@ public final class TranslationContext {
}
@NotNull
public TemporaryVariable newAliasForThis() {
TemporaryVariable aliasForThis = dynamicContext.declareTemporary(new JsThisRef());
aliaser().setAliasForThis(aliasForThis.name());
public TemporaryVariable newAliasForThis(@NotNull DeclarationDescriptor descriptor) {
JsExpression thisQualifier = TranslationUtils.getThisQualifier(this, descriptor);
TemporaryVariable aliasForThis = dynamicContext.declareTemporary(thisQualifier);
aliaser().setAliasForThis(descriptor, aliasForThis.name());
return aliasForThis;
}
public void removeAliasForThis(@NotNull JsName aliasToRemove) {
aliaser().removeAliasForThis(aliasToRemove);
public void removeAliasForThis(@NotNull DeclarationDescriptor descriptor) {
aliaser().removeAliasForThis(descriptor);
}
@NotNull
@@ -15,7 +15,7 @@ import java.util.List;
/**
* @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
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 org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor;
import org.jetbrains.jet.lang.psi.*;
import org.jetbrains.jet.lang.resolve.BindingContext;
import org.jetbrains.jet.lang.resolve.constants.CompileTimeConstant;
@@ -23,6 +24,7 @@ import org.jetbrains.k2js.translate.utils.BindingUtils;
import java.util.List;
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.translateInitializerForProperty;
@@ -375,8 +377,10 @@ public final class ExpressionVisitor extends TranslatorVisitor<JsNode> {
@NotNull
public JsNode visitThisExpression(@NotNull JetThisExpression expression,
@NotNull TranslationContext context) {
if (context.aliaser().hasAliasForThis()) {
return context.aliaser().getAliasForThis();
DeclarationDescriptor descriptor =
getDescriptorForReferenceExpression(context.bindingContext(), expression.getInstanceReference());
if (context.aliaser().hasAliasForThis(descriptor)) {
return context.aliaser().getAliasForThis(descriptor);
}
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.util.AstUtil;
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.Modality;
import org.jetbrains.jet.lang.descriptors.ValueParameterDescriptor;
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.TranslationContext;
import org.jetbrains.k2js.translate.general.AbstractTranslator;
@@ -16,6 +20,8 @@ import java.util.ArrayList;
import java.util.List;
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;
@@ -64,11 +70,17 @@ public final class FunctionTranslator extends AbstractTranslator {
return new JsPropertyInitializer(functionName.makeRef(), function);
}
//TODO: refactor
@NotNull
public JsExpression translateAsLiteral() {
TemporaryVariable aliasForThis = context().newAliasForThis();
DeclarationDescriptor expectedThisDescriptor = getExpectedThisDescriptor(descriptor);
if (expectedThisDescriptor == null) {
return generateFunctionObject();
}
TemporaryVariable aliasForThis = context().newAliasForThis(expectedThisDescriptor);
JsFunction function = generateFunctionObject();
context().removeAliasForThis(aliasForThis.name());
context().removeAliasForThis(expectedThisDescriptor);
return AstUtil.newSequence(aliasForThis.assignmentExpression(), function);
}
@@ -83,8 +95,7 @@ public final class FunctionTranslator extends AbstractTranslator {
private void restoreContext() {
if (isExtensionFunction()) {
JsName receiverAlias = functionBodyContext.jsScope().findExistingName(RECEIVER_PARAMETER_NAME);
functionBodyContext.removeAliasForThis(receiverAlias);
functionBodyContext.removeAliasForThis(getExpectedReceiverDescriptor(descriptor));
}
}
@@ -100,21 +111,20 @@ public final class FunctionTranslator extends AbstractTranslator {
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() {
JetExpression jetBodyExpression = functionDeclaration.getBodyExpression();
//TODO decide if there are cases where this assert is illegal
assert jetBodyExpression != null : "Function without body not supported";
if (jetBodyExpression == null) {
assert descriptor.getModality().equals(Modality.ABSTRACT);
return;
}
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
@@ -173,14 +183,24 @@ public final class FunctionTranslator extends AbstractTranslator {
private void mayBeAddThisParameterForExtensionFunction(@NotNull List<JsParameter> jsParameters) {
if (isExtensionFunction()) {
//TODO: dont do this
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));
}
}
private boolean isExtensionFunction() {
FunctionDescriptor functionDescriptor = getFunctionDescriptor(context().bindingContext(), functionDeclaration);
return DescriptorUtils.isExtensionFunction(functionDescriptor);
return DescriptorUtils.isExtensionFunction(descriptor);
}
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
protected JsFunction generateInitializerFunction() {
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
result.setParameters(translatePrimaryConstructorParameters());
mayBeAddCallToSuperMethod();
@@ -10,6 +10,7 @@ import org.jetbrains.jet.lang.descriptors.*;
import org.jetbrains.jet.lang.psi.*;
import org.jetbrains.jet.lang.resolve.calls.DefaultValueArgument;
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.k2js.translate.context.TranslationContext;
import org.jetbrains.k2js.translate.general.AbstractTranslator;
@@ -31,6 +32,7 @@ import static org.jetbrains.k2js.translate.utils.TranslationUtils.*;
* @author Pavel Talanov
*/
//TODO: write tests on calling backing fields as functions
//TODO: constructor receives too many parameters
public final class CallTranslator extends AbstractTranslator {
private static final class Builder {
@@ -46,10 +48,9 @@ public final class CallTranslator extends AbstractTranslator {
private CallTranslator buildFromUnary(@NotNull JetUnaryExpression unaryExpression) {
JsExpression receiver = TranslationUtils.translateBaseExpression(context, unaryExpression);
List<JsExpression> arguments = Collections.emptyList();
DeclarationDescriptor descriptor = getDescriptorForReferenceExpression
(context.bindingContext(), unaryExpression.getOperationReference());
assert descriptor instanceof FunctionDescriptor;
return new CallTranslator(receiver, arguments, (FunctionDescriptor) descriptor, context);
ResolvedCall<?> resolvedCall =
getResolvedCall(context.bindingContext(), unaryExpression.getOperationReference());
return new CallTranslator(receiver, arguments, resolvedCall, null, context);
}
//TODO: method too long
@@ -70,11 +71,9 @@ public final class CallTranslator extends AbstractTranslator {
arguments = Arrays.asList(rightExpression);
}
DeclarationDescriptor descriptor = getDescriptorForReferenceExpression
(context.bindingContext(), binaryExpression.getOperationReference());
assert descriptor instanceof FunctionDescriptor;
FunctionDescriptor functionDescriptor = (FunctionDescriptor) descriptor;
return new CallTranslator(receiver, arguments, functionDescriptor, context);
ResolvedCall<?> resolvedCall =
getResolvedCall(context.bindingContext(), binaryExpression.getOperationReference());
return new CallTranslator(receiver, arguments, resolvedCall, null, context);
}
@NotNull
@@ -84,19 +83,16 @@ public final class CallTranslator extends AbstractTranslator {
JetExpression selectorExpression = dotExpression.getSelectorExpression();
assert selectorExpression instanceof JetCallExpression;
JetCallExpression callExpression = (JetCallExpression) selectorExpression;
FunctionDescriptor descriptor =
getFunctionDescriptorForCallExpression(context.bindingContext(), callExpression);
ResolvedCall<?> resolvedCall = getResolvedCallForCallExpression(context.bindingContext(), callExpression);
List<JsExpression> arguments = translateArgumentsForCallExpression(callExpression, context);
return new CallTranslator(receiver, arguments, descriptor, context);
return new CallTranslator(receiver, arguments, resolvedCall, null, context);
}
@NotNull
private CallTranslator buildFromCallExpression(@NotNull JetCallExpression callExpression) {
FunctionDescriptor descriptor =
getFunctionDescriptorForCallExpression(context.bindingContext(), callExpression);
JsExpression receiver = getImplicitReceiver(context, descriptor);
ResolvedCall<?> resolvedCall = getResolvedCallForCallExpression(context.bindingContext(), callExpression);
List<JsExpression> arguments = translateArgumentsForCallExpression(callExpression, context);
return new CallTranslator(receiver, arguments, descriptor, context);
return new CallTranslator(null, arguments, resolvedCall, null, context);
}
@NotNull
@@ -126,16 +122,28 @@ public final class CallTranslator extends AbstractTranslator {
}
@NotNull
public static JsExpression translate(@Nullable JsExpression receiver, @NotNull CallableDescriptor functionDescriptor,
public static JsExpression translate(@Nullable JsExpression receiver,
@NotNull CallableDescriptor descriptor,
@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
public static JsExpression translate(@Nullable JsExpression receiver, @NotNull List<JsExpression> arguments,
@NotNull CallableDescriptor functionDescriptor,
@NotNull ResolvedCall<?> resolvedCall,
@Nullable CallableDescriptor descriptorToCall,
@NotNull TranslationContext context) {
return (new CallTranslator(receiver, arguments, functionDescriptor, context)).translate();
return (new CallTranslator(receiver, arguments, resolvedCall, descriptorToCall, context)).translate();
}
@NotNull
@@ -184,15 +192,25 @@ public final class CallTranslator extends AbstractTranslator {
@NotNull
private final List<JsExpression> arguments;
@NotNull
private final ResolvedCall<?> resolvedCall;
@NotNull
private final CallableDescriptor descriptor;
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);
this.receiver = receiver;
this.arguments = arguments;
this.descriptor = descriptor;
this.resolvedCall = resolvedCall;
if (descriptorToCall != null) {
this.descriptor = descriptorToCall;
} else {
this.descriptor = resolvedCall.getCandidateDescriptor().getOriginal();
}
}
@NotNull
@@ -218,7 +236,7 @@ public final class CallTranslator extends AbstractTranslator {
}
private boolean isExtensionFunction() {
return DescriptorUtils.isExtensionFunction(descriptor);
return resolvedCall.getReceiverArgument().exists();
}
@NotNull
@@ -258,13 +276,14 @@ public final class CallTranslator extends AbstractTranslator {
}
@NotNull
private JsExpression getterCall(PropertyDescriptor variableDescriptor) {
return PropertyAccessTranslator.translateAsPropertyGetterCall(variableDescriptor, context());
private JsExpression getterCall(@NotNull PropertyDescriptor variableDescriptor) {
return PropertyAccessTranslator.translateAsPropertyGetterCall(variableDescriptor, resolvedCall, context());
}
@NotNull
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()) {
return extensionFunctionReference(methodReference);
} else if (receiver != null) {
@@ -275,7 +294,7 @@ public final class CallTranslator extends AbstractTranslator {
@NotNull
private JsExpression extensionFunctionReference(@NotNull JsExpression methodReference) {
JsExpression qualifier = TranslationUtils.getExtensionFunctionImplicitReceiver(context(), descriptor);
JsExpression qualifier = TranslationUtils.getImplicitReceiver(context(), resolvedCall.getReceiverArgument());
if (qualifier != null) {
AstUtil.setQualifier(methodReference, qualifier);
}
@@ -284,7 +303,8 @@ public final class CallTranslator extends AbstractTranslator {
@NotNull
private JsExpression constructorCall() {
JsNew constructorCall = new JsNew(qualifiedMethodReference(descriptor));
JsExpression constructorReference = ReferenceTranslator.translateAsFQReference(descriptor, context());
JsNew constructorCall = new JsNew(constructorReference);
constructorCall.setArguments(arguments);
return constructorCall;
}
@@ -7,21 +7,20 @@ import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor;
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.JetQualifiedExpression;
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.general.Translation;
import java.util.Arrays;
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.isBackingFieldReference;
import static org.jetbrains.k2js.translate.utils.TranslationUtils.backingFieldReference;
import static org.jetbrains.k2js.translate.utils.TranslationUtils.getImplicitReceiver;
/**
* @author Pavel Talanov
@@ -42,8 +41,9 @@ public final class PropertyAccessTranslator extends AccessTranslator {
@NotNull
public static JsExpression translateAsPropertyGetterCall(@NotNull PropertyDescriptor descriptor,
@NotNull ResolvedCall<?> resolvedCall,
@NotNull TranslationContext context) {
return (newInstance(descriptor, context))
return (newInstance(descriptor, resolvedCall, context))
.translateAsGet();
}
@@ -56,8 +56,9 @@ public final class PropertyAccessTranslator extends AccessTranslator {
@NotNull
private static PropertyAccessTranslator newInstance(@NotNull PropertyDescriptor descriptor,
@NotNull ResolvedCall<?> resolvedCall,
@NotNull TranslationContext context) {
return new PropertyAccessTranslator(descriptor, null, false, context);
return new PropertyAccessTranslator(descriptor, null, false, resolvedCall, context);
}
@NotNull
@@ -65,16 +66,21 @@ public final class PropertyAccessTranslator extends AccessTranslator {
@NotNull TranslationContext context) {
JetExpression qualifier = expression.getReceiverExpression();
JetSimpleNameExpression selector = getNotNullSelector(expression);
ResolvedCall<?> resolvedCall = getResolvedCall(context.bindingContext(), selector);
PropertyDescriptor propertyDescriptor = getPropertyDescriptor(selector, context);
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
public static PropertyAccessTranslator newInstance(@NotNull JetSimpleNameExpression expression,
@NotNull TranslationContext 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
@@ -121,19 +127,23 @@ public final class PropertyAccessTranslator extends AccessTranslator {
}
@Nullable
private final JetExpression qualifier;
private final JsExpression qualifier;
@NotNull
private final PropertyDescriptor propertyDescriptor;
private final boolean isBackingFieldAccess;
@NotNull
ResolvedCall<?> resolvedCall;
private PropertyAccessTranslator(@NotNull PropertyDescriptor descriptor,
@Nullable JetExpression qualifier,
@Nullable JsExpression qualifier,
boolean isBackingFieldAccess,
@NotNull ResolvedCall<?> resolvedCall,
@NotNull TranslationContext context) {
super(context);
this.qualifier = qualifier;
this.propertyDescriptor = descriptor.getOriginal();
this.isBackingFieldAccess = isBackingFieldAccess;
this.resolvedCall = resolvedCall;
}
@Override
@@ -153,7 +163,7 @@ public final class PropertyAccessTranslator extends AccessTranslator {
@NotNull
private JsExpression getterCall() {
return CallTranslator.translate(translateQualifier(), getGetterDescriptor(), context());
return CallTranslator.translate(qualifier, resolvedCall, propertyDescriptor.getGetter(), context());
}
@Override
@@ -168,7 +178,8 @@ public final class PropertyAccessTranslator extends AccessTranslator {
@NotNull
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
@@ -177,35 +188,10 @@ public final class PropertyAccessTranslator extends AccessTranslator {
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
private static JetSimpleNameExpression getNotNullSelector(@NotNull JetQualifiedExpression qualifiedExpression) {
JetSimpleNameExpression selectorExpression = getSelectorAsSimpleName(qualifiedExpression);
assert selectorExpression != null : MESSAGE;
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);
}
//TODO: condider evaluating only once
@NotNull
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.util.AstUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor;
import org.jetbrains.jet.lang.descriptors.NamespaceDescriptor;
import org.jetbrains.jet.lang.psi.JetSimpleNameExpression;
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.TranslationUtils.getImplicitReceiver;
/**
* @author Pavel Talanov
*/
public final class ReferenceTranslator extends AbstractTranslator {
//TODO: get rid of the class, move to util
public final class ReferenceTranslator {
@NotNull
public static JsExpression translateSimpleName(@NotNull JetSimpleNameExpression expression,
@@ -26,42 +25,27 @@ public final class ReferenceTranslator extends AbstractTranslator {
}
DeclarationDescriptor referencedDescriptor =
getDescriptorForReferenceExpression(context.bindingContext(), expression);
return (new ReferenceTranslator(referencedDescriptor, true, context)).translate();
return translateAsLocalNameReference(referencedDescriptor, context);
}
@NotNull
public static JsExpression translateReference(@NotNull DeclarationDescriptor referencedDescriptor,
@NotNull TranslationContext context) {
return (new ReferenceTranslator(referencedDescriptor, false, context)).translate();
public static JsExpression translateAsFQReference(@NotNull DeclarationDescriptor referencedDescriptor,
@NotNull TranslationContext context) {
assert context.hasQualifierForDescriptor(referencedDescriptor);
JsName referencedName = context.getNameForDescriptor(referencedDescriptor);
JsExpression qualifier = context.getQualifierForDescriptor(referencedDescriptor);
return AstUtil.qualified(referencedName, qualifier);
}
@NotNull
private final DeclarationDescriptor referencedDescriptor;
public static JsExpression translateAsLocalNameReference(@NotNull DeclarationDescriptor referencedDescriptor,
@NotNull TranslationContext context) {
private final boolean shouldQualify;
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();
if (referencedDescriptor.getContainingDeclaration() instanceof NamespaceDescriptor) {
return translateAsFQReference(referencedDescriptor, context);
}
JsName referencedName = context.getNameForDescriptor(referencedDescriptor);
return referencedName.makeRef();
}
}
@@ -222,23 +222,13 @@ public final class BindingUtils {
}
@NotNull
private static ResolvedCall<?> getResolvedCall(@NotNull BindingContext context,
@NotNull JetExpression expression) {
public static ResolvedCall<?> getResolvedCall(@NotNull BindingContext context,
@NotNull JetExpression expression) {
ResolvedCall<? extends CallableDescriptor> resolvedCall = context.get(BindingContext.RESOLVED_CALL, expression);
assert resolvedCall != null : "Must resolve to a call.";
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
public static ResolvedCall<?> getResolvedCallForCallExpression(@NotNull BindingContext context,
@NotNull JetCallExpression expression) {
@@ -4,6 +4,7 @@ import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.jet.lang.descriptors.*;
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 java.util.ArrayList;
@@ -136,4 +137,30 @@ public final class DescriptorUtils {
}
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 org.jetbrains.annotations.NotNull;
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.FunctionDescriptor;
import org.jetbrains.jet.lang.descriptors.PropertyDescriptor;
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.TranslationContext;
import org.jetbrains.k2js.translate.general.Translation;
@@ -17,7 +17,6 @@ import java.util.ArrayList;
import java.util.List;
import static org.jetbrains.k2js.translate.utils.BindingUtils.*;
import static org.jetbrains.k2js.translate.utils.DescriptorUtils.isExtensionFunction;
/**
* @author Pavel Talanov
@@ -57,6 +56,7 @@ public final class TranslationUtils {
return Translation.translateAsExpression(jetExpression, context);
}
//TODO: refactor backing field reference generation to use the generic way
@NotNull
public static JsNameRef backingFieldReference(@NotNull TranslationContext context,
@NotNull JetProperty expression) {
@@ -69,7 +69,7 @@ public final class TranslationUtils {
@NotNull PropertyDescriptor descriptor) {
JsName backingFieldName = context.getNameForDescriptor(descriptor);
if (isOwnedByClass(descriptor)) {
return getThisQualifiedNameReference(context, backingFieldName);
return AstUtil.qualified(backingFieldName, new JsThisRef());
}
assert isOwnedByNamespace(descriptor)
: "Only classes and namespaces may own backing fields.";
@@ -109,19 +109,11 @@ public final class TranslationUtils {
}
@NotNull
private static JsNameRef getThisQualifiedNameReference(@NotNull TranslationContext context,
@NotNull JsName name) {
JsExpression qualifier = getThisQualifier(context);
JsNameRef reference = name.makeRef();
AstUtil.setQualifier(reference, qualifier);
return reference;
}
@NotNull
private static JsExpression getThisQualifier(@NotNull TranslationContext context) {
public static JsExpression getThisQualifier(@NotNull TranslationContext context,
@NotNull DeclarationDescriptor correspondingDeclaration) {
JsExpression qualifier;
if (context.aliaser().hasAliasForThis()) {
qualifier = context.aliaser().getAliasForThis();
if (context.aliaser().hasAliasForThis(correspondingDeclaration)) {
qualifier = context.aliaser().getAliasForThis(correspondingDeclaration);
} else {
qualifier = new JsThisRef();
}
@@ -200,32 +192,26 @@ public final class TranslationUtils {
@Nullable
public static JsExpression getImplicitReceiver(@NotNull TranslationContext context,
@NotNull DeclarationDescriptor referencedDescriptor) {
if (referencedDescriptor instanceof FunctionDescriptor) {
if (isExtensionFunction((FunctionDescriptor) referencedDescriptor)) {
return TranslationUtils.getThisQualifier(context);
}
}
return getImplicitReceiverBasedOnOwner(context, referencedDescriptor);
@NotNull ReceiverDescriptor receiverDescriptor) {
DeclarationDescriptor correspondingDescriptor =
receiverDescriptor.getType().getConstructor().getDeclarationDescriptor();
assert correspondingDescriptor != null;
return context.aliaser().getAliasForReceiver(correspondingDescriptor);
}
@Nullable
public static JsExpression getExtensionFunctionImplicitReceiver(@NotNull TranslationContext context,
@NotNull CallableDescriptor descriptor) {
return getImplicitReceiverBasedOnOwner(context, descriptor);
public static JsExpression getImplicitThis(@NotNull TranslationContext context,
@NotNull ReceiverDescriptor receiverDescriptor) {
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));
}
@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 {
runMain(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) {
@@ -40,4 +45,13 @@ public final class RhinoSystemOutputChecker implements RhinoResultChecker {
String callToMain = GenerationUtils.generateCallToMain("Anonymous", arguments);
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
public void bottles() throws Exception {
testWithMain("bottles", "2", "2");
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());
}
});
/**
* 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.*
fun main(args : Array<String>) {
if (args.isEmpty) {
if (args.isEmpty()) {
printBottles(99)
}
else {
@@ -72,4 +72,4 @@ fun bottlesOfBeer(count : Int) : String =
// From the std package
// 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
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.
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
*** ***
* * * *
* * * *
* * * *
*** ***
*** ***
* * * *
* * * *
* * * *
*** ***