JS: moved PatternBuilder to JS frontend

This commit is contained in:
Alexey Tsvetkov
2014-12-18 16:39:07 +03:00
parent f9648c6f21
commit 200e3afbe1
21 changed files with 112 additions and 95 deletions
@@ -0,0 +1,58 @@
/*
* Copyright 2010-2015 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.kotlin.js.descriptorUtils
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.descriptors.ClassifierDescriptor
import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.resolve.DescriptorUtils
import org.jetbrains.kotlin.types.JetType
import org.jetbrains.kotlin.types.TypeProjection
import com.intellij.openapi.util.text.StringUtil
public val JetType.nameIfStandardType: Name?
get() {
val descriptor = getConstructor().getDeclarationDescriptor()
if (descriptor?.getContainingDeclaration() == KotlinBuiltIns.getInstance().getBuiltInsPackageFragment()) {
return descriptor?.getName()
}
return null
}
public fun JetType.getJetTypeFqName(printTypeArguments: Boolean): String {
val declaration = requireNotNull(getConstructor().getDeclarationDescriptor())
if (declaration is TypeParameterDescriptor) {
return StringUtil.join(declaration.getUpperBounds(), { (type) -> type.getJetTypeFqName(printTypeArguments) }, "&")
}
val typeArguments = getArguments()
val typeArgumentsAsString: String
if (printTypeArguments && !typeArguments.isEmpty()) {
val joinedTypeArguments = StringUtil.join(typeArguments, { (projection) -> projection.getType().getJetTypeFqName(false) }, ", ")
typeArgumentsAsString = "<" + joinedTypeArguments + ">"
} else {
typeArgumentsAsString = ""
}
return DescriptorUtils.getFqName(declaration).asString() + typeArgumentsAsString
}
@@ -0,0 +1,26 @@
/*
* Copyright 2010-2015 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.kotlin.js.patterns;
import com.google.common.base.Predicate;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.kotlin.descriptors.FunctionDescriptor;
public interface DescriptorPredicate extends Predicate<FunctionDescriptor> {
@Override
boolean apply(@Nullable FunctionDescriptor descriptor);
}
@@ -0,0 +1,96 @@
/*
* Copyright 2010-2015 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.kotlin.js.patterns;
import com.google.common.base.Predicate;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import com.intellij.util.Function;
import com.intellij.util.containers.ContainerUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.kotlin.builtins.PrimitiveType;
import org.jetbrains.kotlin.name.Name;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Set;
public final class NamePredicate implements Predicate<Name> {
@NotNull
public static final NamePredicate PRIMITIVE_NUMBERS = new NamePredicate(
ContainerUtil.map(PrimitiveType.NUMBER_TYPES,
new Function<PrimitiveType, String>() {
@Override
public String fun(PrimitiveType type) {
return type.getTypeName().asString();
}
}));
@NotNull
public static final NamePredicate PRIMITIVE_NUMBERS_MAPPED_TO_PRIMITIVE_JS = new NamePredicate(
ContainerUtil.mapNotNull(PrimitiveType.NUMBER_TYPES,
new Function<PrimitiveType, String>() {
@Override
public String fun(PrimitiveType type) {
return type != PrimitiveType.LONG ? type.getTypeName().asString() : null;
}
}));
@NotNull
public static final NamePredicate STRING = new NamePredicate("String");
@NotNull
public static final NamePredicate NUMBER = new NamePredicate("Number");
@NotNull
public static final NamePredicate BOOLEAN = new NamePredicate("Boolean");
@NotNull
public static final NamePredicate CHAR = new NamePredicate(PrimitiveType.CHAR.getTypeName());
@NotNull
public static final NamePredicate LONG = new NamePredicate(PrimitiveType.LONG.getTypeName());
@NotNull
private final Set<Name> validNames = Sets.newHashSet();
public NamePredicate(@NotNull String... validNames) {
this(Arrays.asList(validNames));
}
public NamePredicate(@NotNull List<String> validNames) {
for (String validName : validNames) {
this.validNames.add(Name.guess(validName));
}
}
public NamePredicate(@NotNull Collection<Name> validNames) {
this.validNames.addAll(validNames);
}
public NamePredicate(@NotNull Name... validNames) {
this.validNames.addAll(Lists.newArrayList(validNames));
}
@Override
public boolean apply(@Nullable Name name) {
return name != null && validNames.contains(name);
}
}
@@ -0,0 +1,271 @@
/*
* Copyright 2010-2015 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.kotlin.js.patterns;
import com.google.common.collect.Lists;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.kotlin.descriptors.*;
import org.jetbrains.kotlin.name.Name;
import org.jetbrains.kotlin.idea.JetLanguage;
import org.jetbrains.kotlin.resolve.DescriptorUtils;
import org.jetbrains.kotlin.resolve.OverrideResolver;
import java.util.Arrays;
import java.util.List;
import static org.jetbrains.kotlin.js.descriptorUtils.DescriptorUtilsPackage.getNameIfStandardType;
import static org.jetbrains.kotlin.js.descriptorUtils.DescriptorUtilsPackage.getJetTypeFqName;
public final class PatternBuilder {
@NotNull
private static final NamePredicate KOTLIN_NAME_PREDICATE = new NamePredicate("kotlin");
@NotNull
private static final Name KOTLIN_NAME = Name.identifier(JetLanguage.NAME.toLowerCase());
private PatternBuilder() {
}
@NotNull
public static DescriptorPredicate pattern(@NotNull NamePredicate checker, @NotNull String stringWithPattern) {
List<NamePredicate> checkers = Lists.newArrayList(checker);
checkers.addAll(parseFqNamesFromString(stringWithPattern));
return pattern(checkers, parseArgumentsFromString(stringWithPattern));
}
@NotNull
public static DescriptorPredicate pattern(@NotNull String stringWithPattern, @NotNull NamePredicate checker) {
List<NamePredicate> checkers = Lists.newArrayList(parseFqNamesFromString(stringWithPattern));
checkers.add(checker);
return pattern(checkers);
}
@NotNull
public static DescriptorPredicate pattern(@NotNull String stringWithPattern) {
return pattern(parseFqNamesFromString(stringWithPattern), parseArgumentsFromString(stringWithPattern));
}
@NotNull
private static List<NamePredicate> parseFqNamesFromString(@NotNull String stringWithPattern) {
stringWithPattern = getNamePatternFromString(stringWithPattern);
String[] subPatterns = stringWithPattern.split("\\.");
List<NamePredicate> checkers = Lists.newArrayList();
for (String subPattern : subPatterns) {
String[] validNames = subPattern.split("\\|");
checkers.add(new NamePredicate(validNames));
}
return checkers;
}
@Nullable
private static List<NamePredicate> parseArgumentsFromString(@NotNull String stringWithPattern) {
stringWithPattern = getArgumentsPatternFromString(stringWithPattern);
if (stringWithPattern == null) return null;
List<NamePredicate> checkers = Lists.newArrayList();
if (stringWithPattern.isEmpty()) {
return checkers;
}
String[] subPatterns = stringWithPattern.split("\\,");
for (String subPattern : subPatterns) {
String[] validNames = subPattern.split("\\|");
checkers.add(new NamePredicate(validNames));
}
return checkers;
}
@NotNull
private static String getNamePatternFromString(@NotNull String stringWithPattern) {
int left = stringWithPattern.indexOf("(");
if (left < 0) {
return stringWithPattern;
}
else {
return stringWithPattern.substring(0, left);
}
}
@Nullable
private static String getArgumentsPatternFromString(@NotNull String stringWithPattern) {
int left = stringWithPattern.indexOf("(");
if (left < 0) {
return null;
}
else {
int right = stringWithPattern.indexOf(")");
assert right == stringWithPattern.length() - 1 : "expected ')' at the end: " + stringWithPattern;
return stringWithPattern.substring(left + 1, right);
}
}
@NotNull
private static DescriptorPredicate pattern(@NotNull List<NamePredicate> checkers) {
return pattern(checkers, null);
}
@NotNull
private static DescriptorPredicate pattern(@NotNull List<NamePredicate> checkers, @Nullable List<NamePredicate> arguments) {
assert !checkers.isEmpty();
final List<NamePredicate> checkersWithPrefixChecker = Lists.newArrayList();
if (!checkers.get(0).apply(KOTLIN_NAME)) {
checkersWithPrefixChecker.add(KOTLIN_NAME_PREDICATE);
}
checkersWithPrefixChecker.addAll(checkers);
assert checkersWithPrefixChecker.size() > 1;
final List<NamePredicate> argumentCheckers = arguments != null ? Lists.newArrayList(arguments) : null;
return new DescriptorPredicate() {
@Override
public boolean apply(@Nullable FunctionDescriptor descriptor) {
assert descriptor != null : "argument for DescriptorPredicate.apply should not be null, checkers=" + checkersWithPrefixChecker;
//TODO: no need to wrap if we check beforehand
try {
return doApply(descriptor);
}
catch (IllegalArgumentException e) {
return false;
}
}
private boolean doApply(@NotNull FunctionDescriptor descriptor) {
List<Name> nameParts = DescriptorUtils.getFqName(descriptor).pathSegments();
if (nameParts.size() != checkersWithPrefixChecker.size()) return false;
return allNamePartsValid(nameParts) && checkAllArgumentsValidIfNeeded(descriptor);
}
private boolean checkAllArgumentsValidIfNeeded(@NotNull FunctionDescriptor descriptor) {
if (argumentCheckers != null) {
List<ValueParameterDescriptor> valueParameterDescriptors = descriptor.getValueParameters();
if (valueParameterDescriptors.size() != argumentCheckers.size()) {
return false;
}
for (int i = 0; i < valueParameterDescriptors.size(); i++) {
ValueParameterDescriptor valueParameterDescriptor = valueParameterDescriptors.get(i);
Name name = getNameIfStandardType(valueParameterDescriptor.getType());
NamePredicate namePredicate = argumentCheckers.get(i);
if (!namePredicate.apply(name)) return false;
}
}
return true;
}
private boolean allNamePartsValid(@NotNull List<Name> nameParts) {
for (int i = 0; i < nameParts.size(); ++i) {
Name namePart = nameParts.get(i);
NamePredicate correspondingPredicate = checkersWithPrefixChecker.get(i);
if (!correspondingPredicate.apply(namePart)) {
return false;
}
}
return true;
}
};
}
@NotNull
public static DescriptorPredicate pattern(@NotNull NamePredicate... checkers) {
return pattern(Arrays.asList(checkers));
}
@NotNull
public static DescriptorPredicateImpl pattern(@NotNull String... names) {
return new DescriptorPredicateImpl(names);
}
public static class DescriptorPredicateImpl implements DescriptorPredicate {
private final String[] names;
private String receiverFqName;
private boolean checkOverridden;
public DescriptorPredicateImpl(String... names) {
this.names = names;
}
public DescriptorPredicateImpl isExtensionOf(String receiverFqName) {
this.receiverFqName = receiverFqName;
return this;
}
public DescriptorPredicateImpl checkOverridden() {
this.checkOverridden = true;
return this;
}
private boolean matches(@NotNull CallableDescriptor callable) {
DeclarationDescriptor descriptor = callable;
int nameIndex = names.length - 1;
while (true) {
if (nameIndex == -1) {
return false;
}
if (!descriptor.getName().asString().equals(names[nameIndex])) {
return false;
}
nameIndex--;
descriptor = descriptor.getContainingDeclaration();
if (descriptor instanceof PackageFragmentDescriptor) {
return nameIndex == 0 && names[0].equals(((PackageFragmentDescriptor) descriptor).getFqName().asString());
}
}
}
@Override
public boolean apply(@Nullable FunctionDescriptor functionDescriptor) {
assert functionDescriptor != null :
"argument for DescriptorPredicate.apply should not be null, receiverFqName=" + receiverFqName + " names=" + Arrays.asList(names);
ReceiverParameterDescriptor actualReceiver = functionDescriptor.getExtensionReceiverParameter();
if (actualReceiver != null) {
if (receiverFqName == null) return false;
String actualReceiverFqName = getJetTypeFqName(actualReceiver.getType(), false);
if (!actualReceiverFqName.equals(receiverFqName)) return false;
}
if (!(functionDescriptor.getContainingDeclaration() instanceof ClassDescriptor)) {
return matches(functionDescriptor);
}
for (CallableMemberDescriptor real : OverrideResolver.getOverriddenDeclarations(functionDescriptor)) {
if (matches(real)) {
return true;
}
}
if (checkOverridden) {
for (CallableDescriptor overridden : DescriptorUtils.getAllOverriddenDescriptors(functionDescriptor)) {
if (matches(overridden)) {
return true;
}
}
}
return false;
}
}
}