Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Move instanceof logic to InstanceofUtil + Fix SA instanceof reference #1296

Merged
merged 2 commits into from
Nov 11, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,7 @@
import com.laytonsmith.core.constructs.CFunction;
import com.laytonsmith.core.constructs.CKeyword;
import com.laytonsmith.core.constructs.CLabel;
import com.laytonsmith.core.constructs.CNull;
import com.laytonsmith.core.constructs.CString;
import com.laytonsmith.core.constructs.CVoid;
import com.laytonsmith.core.constructs.IVariable;
import com.laytonsmith.core.constructs.InstanceofUtil;
import com.laytonsmith.core.constructs.Target;
Expand Down Expand Up @@ -430,18 +428,8 @@ public CClassType typecheck(ParseTree ast, Environment env, Set<ConfigCompileExc
public static void requireType(CClassType type, CClassType expected,
Target t, Environment env, Set<ConfigCompileException> exceptions) {

// Handle types that cause exceptions in the InstanceofUtil isInstanceof check.
if(type.equals(expected) || type == CClassType.AUTO || type == CNull.TYPE) {
return;
}
if(type == CVoid.TYPE || expected == CVoid.TYPE || expected == CNull.TYPE) {
exceptions.add(new ConfigCompileException("Expected type " + expected.getSimpleName()
+ ", but received type " + type.getSimpleName() + " instead.", t));
return;
}

// Handle 'normal' types.
if(!InstanceofUtil.isInstanceof(type, expected, env)) {
// Generate an exception if the given type is not instanceof the expected type.
if(!InstanceofUtil.isInstanceof(type, expected, null, env)) {
exceptions.add(new ConfigCompileException("Expected type " + expected.getSimpleName()
+ ", but received type " + type.getSimpleName() + " instead.", t));
}
Expand All @@ -461,9 +449,7 @@ public static void requireAnyType(CClassType type, CClassType[] expected,

// Return if the type is instanceof any expected type.
for(CClassType exp : expected) {
if(type.equals(exp) || type == CClassType.AUTO || type == CNull.TYPE
|| (type != CVoid.TYPE && exp != CVoid.TYPE && exp != CNull.TYPE)
|| InstanceofUtil.isInstanceof(type, exp, env)) {
if(InstanceofUtil.isInstanceof(type, exp, null, env)) {
return;
}
}
Expand Down
53 changes: 33 additions & 20 deletions src/main/java/com/laytonsmith/core/constructs/InstanceofUtil.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
import com.laytonsmith.PureUtilities.ClassLoading.ClassDiscovery;
import com.laytonsmith.annotations.typeof;
import com.laytonsmith.core.FullyQualifiedClassName;
import com.laytonsmith.core.Static;
import com.laytonsmith.core.constructs.generics.LeftHandGenericUse;
import com.laytonsmith.core.environments.Environment;
import com.laytonsmith.core.natives.interfaces.Mixed;
Expand Down Expand Up @@ -150,20 +149,41 @@ public static boolean isInstanceof(Mixed value, CClassType instanceofThis, LeftH

/**
* Returns whether or not a given MethodScript type is an instance of the specified MethodScript type.
*
* @param type The type to check for
* @param instanceofThis The CClassType to check. This may not be null.
* @param generics The LHS generics definition\
* The following rules apply in the given order:
* <ul>
* <li>If instanceofThis == Java null, {@code true} is returned.</li>
* <li>If instanceofThis.equals(type) where the generic declaration of instanceofThis is absent or the generic
* parameters of type are instanceof the given instanceofThisGenerics, {@code true} is returned.</li>
* <li>Java null is only instanceof Java null.</li>
* <li>auto and null are instanceof any type.</li>
* <li>Any type is instanceof auto.</li>
* <li>Nothing is instanceof void and null.</li>
* <li>void is instanceof nothing.</li>
* <li>{@code A<B>} is instanceof {@code A}.</li>
* </ul>
* @param type - The type to check for.
* Java {@code null} can be used to indicate no type (e.g. from control flow breaking statements).
* @param instanceofThis - The {@link CClassType} to check against.
* Java {@code null} can be used to indicate that anything is allowed to match this
* (i.e. making this method return {@code true}).
* @param instanceofThisGenerics
* @param env
* @return
* @return {@code true} if type is instance of instanceofThis.
*/
public static boolean isInstanceof(CClassType type, CClassType instanceofThis, LeftHandGenericUse generics, Environment env) {
Static.AssertNonNull(instanceofThis, "instanceofThis may not be null");
instanceofThis = CClassType.getNakedClassType(instanceofThis.getFQCN());
// Return true for AUTO, as everything can be used as AUTO.
if(instanceofThis == CClassType.AUTO) {
public static boolean isInstanceof(
CClassType type, CClassType instanceofThis, LeftHandGenericUse instanceofThisGenerics, Environment env) {
instanceofThis = (instanceofThis != null ? CClassType.getNakedClassType(instanceofThis.getFQCN()) : null);

// Handle special cases.
if(instanceofThis == null || (instanceofThis.equals(type) && (instanceofThis.getGenericDeclaration() == null
|| type.getGenericParameters().isInstanceof(instanceofThisGenerics))) || CClassType.AUTO.equals(type)
|| CNull.TYPE.equals(type) || CClassType.AUTO.equals(instanceofThis)) {
return true;
}
if(type == null || CVoid.TYPE.equals(type)
|| CVoid.TYPE.equals(instanceofThis) || CNull.TYPE.equals(instanceofThis)) {
return false;
}

// Get cached result or compute and cache result.
Set<CClassType> castableClasses = ISINSTANCEOF_CACHE.get(type);
Expand All @@ -173,15 +193,8 @@ public static boolean isInstanceof(CClassType type, CClassType instanceofThis, L
}

// Return the result.
if(!castableClasses.contains(instanceofThis)) {
return false;
}

if(instanceofThis.getGenericDeclaration() != null ) {
return type.getGenericParameters().isInstanceof(generics, env);
} else {
return true;
}
return castableClasses.contains(instanceofThis) && (instanceofThis.getGenericDeclaration() == null
|| type.getGenericParameters().isInstanceof(instanceofThisGenerics));
}

private static FullyQualifiedClassName typeof(Class<? extends Mixed> c) {
Expand Down