Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
Add more tests for define_object
  • Loading branch information
LadyCailin committed May 21, 2020
1 parent 095dc25 commit c70037a
Show file tree
Hide file tree
Showing 3 changed files with 163 additions and 8 deletions.
49 changes: 42 additions & 7 deletions src/main/java/com/laytonsmith/core/functions/ObjectManagement.java
Expand Up @@ -160,10 +160,10 @@ private Mixed evaluateArray(ParseTree data, Target t) {
return n;
}

private CArray evaluateArrayNoNull(ParseTree data, Target t) {
private CArray evaluateArrayNoNull(ParseTree data, String component, Target t) {
Mixed d = evaluateArray(data, t);
if(d instanceof CNull) {
throw new CREClassDefinitionError("Unexpected null value, expected an array", t);
throw new CREClassDefinitionError("Unexpected null value for " + component + ", expected an array", t);
}
return (CArray) d;
}
Expand All @@ -187,8 +187,21 @@ private CString evaluateStringNoNull(ParseTree data, Target t) {

private Mixed evaluateMixed(ParseTree data, Target t) {
if(data.isDynamic()) {
throw new CREClassDefinitionError("Expected a non-dynamic value, but " + data.getData()
+ " was found.", t);
// TODO: Since CClassType doesn't know about other classes yet, we can't allow hardcoding yet, as it's
// a chicken and egg problem. Eventually, when we get a two pass compiler, this can be re-allowed, but
// until then, we have to accept dynamic input. "Dynamic input" in this case is just the
// __to_class_reference__ function however.
// throw new CREClassDefinitionError("Expected a non-dynamic value, but " + data.getData()
// + " was found.", t);
if(!(data.getData() instanceof CFunction) || !data.getData().val().equals("__to_class_reference__")) {
throw new CREClassDefinitionError("Expected __to_class_reference__, but found " + data.getData()
+ " instead", t);
}
return new __to_class_reference__().exec(t, null,
data.getChildren().stream()
.map((parseTree -> parseTree.getData()))
.collect(Collectors.toList())
.toArray(new Mixed[1]));
}
return data.getData();
}
Expand All @@ -200,7 +213,7 @@ public Mixed execs(Target t, Environment env, Script parent, ParseTree... nodes)
AccessModifier.class, t);

// 1 - Object Modifiers
Set<ObjectModifier> objectModifiers = evaluateArrayNoNull(nodes[1], t).asList().stream()
Set<ObjectModifier> objectModifiers = evaluateArrayNoNull(nodes[1], "object modifiers", t).asList().stream()
.map((item) -> ArgumentValidation.getEnum(item, ObjectModifier.class, t))
.collect(Collectors.toSet());

Expand All @@ -214,7 +227,7 @@ public Mixed execs(Target t, Environment env, Script parent, ParseTree... nodes)
// 4 - Superclasses
Set<UnqualifiedClassName> superclasses = new HashSet<>();
{
CArray su = evaluateArrayNoNull(nodes[4], t);
CArray su = evaluateArrayNoNull(nodes[4], "superclasses", t);
if(!type.canUseExtends() && !su.isEmpty()) {
throw new CREClassDefinitionError("An object definition of type " + type.name().toLowerCase()
+ " may not extend"
Expand All @@ -237,7 +250,7 @@ public Mixed execs(Target t, Environment env, Script parent, ParseTree... nodes)
// 5 - Interfaces
Set<UnqualifiedClassName> interfaces = new HashSet<>();
{
CArray su = evaluateArrayNoNull(nodes[5], t);
CArray su = evaluateArrayNoNull(nodes[5], "interfaces", t);
for(Mixed m : su) {
if(m instanceof CClassType) {
interfaces.add(new UnqualifiedClassName(((CClassType) m).getFQCN()));
Expand Down Expand Up @@ -278,6 +291,7 @@ public Mixed execs(Target t, Environment env, Script parent, ParseTree... nodes)

// 10 - Class Comment
SmartComment classComment = null;
// TODO

Class<? extends Mixed> nativeClass = null;
if(objectModifiers.contains(ObjectModifier.NATIVE)) {
Expand Down Expand Up @@ -571,4 +585,25 @@ public Set<OptimizationOption> optimizationOptions() {
}

}

@api
@hide("This is only used internally to solve the chicken and egg problem")
public static class __to_class_reference__ extends DummyFunction {

@Override
public Class<? extends CREThrowable>[] thrown() {
return new Class[]{CREClassDefinitionError.class};
}


@Override
public Mixed exec(Target t, Environment environment, Mixed... args) throws ConfigRuntimeException {
try {
return CClassType.get(FullyQualifiedClassName.forFullyQualifiedClass(args[0].val()));
} catch (ClassNotFoundException ex) {
throw new CREClassDefinitionError(ex.getMessage(), t, ex);
}
}

}
}
Expand Up @@ -140,6 +140,9 @@ public AccessModifier getAccessModifier() {
* @return
*/
public Set<ObjectModifier> getObjectModifiers() {
if(objectModifiers.isEmpty()) {
return EnumSet.noneOf(ObjectModifier.class);
}
return EnumSet.copyOf(objectModifiers);
}

Expand Down
Expand Up @@ -3,15 +3,20 @@
import com.laytonsmith.core.FullyQualifiedClassName;
import com.laytonsmith.core.MethodScriptCompiler;
import com.laytonsmith.core.compiler.CompilerEnvironment;
import com.laytonsmith.core.constructs.CClassType;
import com.laytonsmith.core.constructs.CPrimitive;
import com.laytonsmith.core.constructs.CString;
import com.laytonsmith.core.constructs.NativeTypeList;
import com.laytonsmith.core.constructs.Target;
import com.laytonsmith.core.environments.Environment;
import com.laytonsmith.core.environments.GlobalEnv;
import com.laytonsmith.core.natives.interfaces.Mixed;
import com.laytonsmith.testing.StaticTest;
import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.EnumSet;
import java.util.HashSet;
import java.util.Set;
import org.junit.Test;
import static org.junit.Assert.*;
Expand All @@ -27,7 +32,8 @@ public class ObjectDefinitionTableTest {
static Set<Class<? extends Environment.EnvironmentImpl>> envs = Environment.getDefaultEnvClasses();

private void doCompile(String script) throws Exception {
MethodScriptCompiler.compile(MethodScriptCompiler.lex(script, null, new File("Test.ms"), true), env, null);
MethodScriptCompiler.compile(MethodScriptCompiler.lex(script, null, new File("Test.ms"), true), env,
Environment.getDefaultEnvClasses());
}

private ObjectDefinition getObjectDefinition(String fqcn) throws Exception {
Expand Down Expand Up @@ -96,4 +102,115 @@ public void testEmptyClassIsDefined() throws Exception {
getObjectDefinition("Test");
}

@Test
public void testNoSyntaxSugarBasic() throws Exception {
String script = "define_object("
+ "'PUBLIC'," // accessModifier
+ "array()," // objectModifier
+ "'CLASS'," // objectType
+ "'test'," // name
+ "array()," // superclasses
+ "array()," // interfaces
+ "null," // enum list
+ "array()," // element list
+ "array()," // annotations
+ "null," // containing class
+ "'class comment'," // class comment
+ "array()" // generic parameters
+ ")"; //
doCompile(script);
ObjectDefinition clazz = getObjectDefinition("test");
assertEquals(AccessModifier.PUBLIC, clazz.getAccessModifier());
assertTrue(clazz.getObjectModifiers().isEmpty());
assertEquals(ObjectType.CLASS, clazz.getObjectType());
assertEquals("test", clazz.getFQCN().getFQCN());
assertEquals(new HashSet<>(Arrays.asList(Mixed.TYPE)), clazz.getSuperclasses());
assertEquals(new HashSet<CClassType>(), clazz.getInterfaces());
// TODO: Check enum list, once it's implemented
assertTrue(clazz.getElements().isEmpty());
assertNull(clazz.getContainingClass());
// TODO: This is currently skipped
// assertEquals("class comment", clazz.getElementComment().getBody());
// TODO: Generic parameters aren't properly implemented yet
assertEquals(new ArrayList<Object>(), clazz.getGenericParameters());
}

@Test
public void testNoSyntaxSugarWithContainingClass() throws Exception {
String script = "define_object("
+ "'PUBLIC'," // accessModifier
+ "array()," // objectModifier
+ "'CLASS'," // objectType
+ "'outer'," // name
+ "array()," // superclasses
+ "array()," // interfaces
+ "null," // enum list
+ "array()," // element list
+ "array()," // annotations
+ "null," // containing class
+ "'class comment'," // class comment
+ "array()" // generic parameters
+ ");\n"
+ "define_object("
+ "'PUBLIC'," // accessModifier
+ "array()," // objectModifier
+ "'CLASS'," // objectType
+ "'inner'," // name
+ "array()," // superclasses
+ "array()," // interfaces
+ "null," // enum list
+ "array()," // element list
+ "array()," // annotations
+ "__to_class_reference__('outer')," // containing class
+ "'class comment'," // class comment
+ "array()" // generic parameters
+ ")"; //
doCompile(script);
ObjectDefinition clazz = getObjectDefinition("inner");
assertEquals(CClassType.get(FullyQualifiedClassName.forFullyQualifiedClass("outer")),
clazz.getContainingClass());
}

@Test
public void testNoSyntaxSugarPrivateClass() throws Exception {
String script = "define_object("
+ "'PRIVATE'," // accessModifier
+ "array()," // objectModifier
+ "'CLASS'," // objectType
+ "'test'," // name
+ "array()," // superclasses
+ "array()," // interfaces
+ "null," // enum list
+ "array()," // element list
+ "array()," // annotations
+ "null," // containing class
+ "'class comment'," // class comment
+ "array()" // generic parameters
+ ")"; //
doCompile(script);
ObjectDefinition clazz = getObjectDefinition("test");
assertEquals(AccessModifier.PRIVATE, clazz.getAccessModifier());
}

@Test
public void testNoSyntaxSugarObjectModifiers() throws Exception {
String script = "define_object("
+ "'PUBLIC'," // accessModifier
+ "array('STATIC', 'FINAL')," // objectModifier
+ "'CLASS'," // objectType
+ "'test'," // name
+ "array()," // superclasses
+ "array()," // interfaces
+ "null," // enum list
+ "array()," // element list
+ "array()," // annotations
+ "null," // containing class
+ "'class comment'," // class comment
+ "array()" // generic parameters
+ ")"; //
doCompile(script);
ObjectDefinition clazz = getObjectDefinition("test");
assertEquals(EnumSet.of(ObjectModifier.STATIC, ObjectModifier.FINAL), clazz.getObjectModifiers());
}

}

0 comments on commit c70037a

Please sign in to comment.