Skip to content

Commit

Permalink
[GR-5276] Added support for executable types.
Browse files Browse the repository at this point in the history
  • Loading branch information
tzezula committed Oct 27, 2017
1 parent 48d8333 commit 779cd83
Show file tree
Hide file tree
Showing 3 changed files with 275 additions and 3 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,17 @@ public final class TypeDescriptor {
*/
public static final TypeDescriptor NATIVE_POINTER = new TypeDescriptor(new PrimitiveImpl(PrimitiveKind.NATIVE_POINTER));

/**
* Represents an executable type returning any type and accepting any number of parameters of
* any type. To create an executable type with concrete types use
* {@link TypeDescriptor#executable(org.graalvm.polyglot.tck.TypeDescriptor, org.graalvm.polyglot.tck.TypeDescriptor...)}
* .
*
* @see Value#canExecute().
* @since 0.30
*/
public static final TypeDescriptor EXECUTABLE = new TypeDescriptor(new ExecutableImpl(null, Collections.emptyList()));

private static final TypeDescriptor[] PREDEFINED_TYPES = new TypeDescriptor[]{
NULL, BOOLEAN, NUMBER, STRING, HOST_OBJECT, NATIVE_POINTER, OBJECT, ARRAY
};
Expand Down Expand Up @@ -239,6 +250,28 @@ public static TypeDescriptor array(TypeDescriptor componentType) {
return componentType == null ? ARRAY : new TypeDescriptor(new ArrayImpl(componentType.impl));
}

/**
* Creates a new executable type with a given return type and parameter types.
*
* @param returnType the required return type, use {@code null} literal as any type
* @param parameterTypes the required parameter types
* @return an executable type
* @since 0.30
*/
public static TypeDescriptor executable(TypeDescriptor returnType, TypeDescriptor... parameterTypes) {
Objects.requireNonNull(parameterTypes, "Parameter types cannot be null");
if (returnType == null && parameterTypes.length == 0) {
return EXECUTABLE;
}
final TypeDescriptorImpl retImpl = returnType == null ? null : returnType.impl;
final List<TypeDescriptorImpl> paramTypeImpls = new ArrayList<>(parameterTypes.length);
for (TypeDescriptor td : parameterTypes) {
Objects.requireNonNull(td, "Parameter types cannot contain null");
paramTypeImpls.add(td.impl);
}
return new TypeDescriptor(new ExecutableImpl(retImpl, paramTypeImpls));
}

/**
* Creates a type for given {@link Value}.
*
Expand Down Expand Up @@ -390,6 +423,109 @@ public String toString() {
}
}

private static final class ExecutableImpl extends TypeDescriptorImpl {
private final TypeDescriptorImpl retType;
private final List<? extends TypeDescriptorImpl> paramTypes;

ExecutableImpl(final TypeDescriptorImpl retType, final List<? extends TypeDescriptorImpl> paramTypes) {
assert paramTypes != null;
this.retType = retType;
this.paramTypes = paramTypes;
}

@Override
TypeDescriptorImpl narrow(final TypeDescriptorImpl origType, final TypeDescriptorImpl byType) {
final TypeDescriptorImpl other = other(origType, byType);
final Class<? extends TypeDescriptorImpl> otherClz = other.getClass();
if (otherClz == PrimitiveImpl.class) {
return null;
}
if (otherClz == ExecutableImpl.class) {
ExecutableImpl otherExecutable = (ExecutableImpl) other;
final TypeDescriptorImpl narrowedRetType;
if (retType == null) {
narrowedRetType = otherExecutable.retType;
} else {
narrowedRetType = otherExecutable.retType == null ? null : retType.narrow(retType, otherExecutable.retType);
if (narrowedRetType == null) {
return null;
}
}
final List<? extends TypeDescriptorImpl> narrowedParamTypes;
if (otherExecutable.paramTypes.isEmpty()) {
narrowedParamTypes = paramTypes;
} else {
if (paramTypes.size() < otherExecutable.paramTypes.size()) {
return null;
}
final List<TypeDescriptorImpl> npts = new ArrayList<>(paramTypes.size());
for (int i = 0; i < paramTypes.size(); i++) {
final TypeDescriptorImpl pt = paramTypes.get(i);
final TypeDescriptorImpl npt;
if (i < otherExecutable.paramTypes.size()) {
final TypeDescriptorImpl opt = otherExecutable.paramTypes.get(i);
npt = opt.narrow(opt, pt);
if (npt == null) {
return null;
}
} else {
npt = pt;
}
npts.add(npt);
}
narrowedParamTypes = npts;
}
return new ExecutableImpl(narrowedRetType, narrowedParamTypes);
} else {
return other.narrow(origType, byType);
}
}

@Override
Set<? extends TypeDescriptorImpl> explode() {
return Collections.singleton(this);
}

@Override
public int hashCode() {
int res = 17;
res = res + (retType == null ? 0 : retType.hashCode());
for (TypeDescriptorImpl paramType : paramTypes) {
res = res * 31 + paramType.hashCode();
}
return res;
}

@Override
public boolean equals(final Object obj) {
if (obj == this) {
return true;
}
if (obj == null || obj.getClass() != ExecutableImpl.class) {
return false;
}
final ExecutableImpl other = (ExecutableImpl) obj;
return Objects.equals(retType, other.retType) && paramTypes.equals(other.paramTypes);
}

@Override
public String toString() {
final StringBuilder sb = new StringBuilder("Executable(");
boolean first = true;
for (TypeDescriptorImpl paramType : paramTypes) {
if (first) {
first = false;
} else {
sb.append(", ");
}
sb.append(paramType);
}
sb.append("):");
sb.append(retType == null ? "<any>" : retType);
return sb.toString();
}
}

private static final class ArrayImpl extends TypeDescriptorImpl {
private final TypeDescriptorImpl contentType;

Expand All @@ -401,7 +537,7 @@ private static final class ArrayImpl extends TypeDescriptorImpl {
TypeDescriptorImpl narrow(final TypeDescriptorImpl origType, final TypeDescriptorImpl byType) {
final TypeDescriptorImpl other = other(origType, byType);
final Class<? extends TypeDescriptorImpl> otherClz = other.getClass();
if (otherClz == PrimitiveImpl.class) {
if (otherClz == PrimitiveImpl.class || otherClz == ExecutableImpl.class) {
return null;
} else if (otherClz == ArrayImpl.class) {
final ArrayImpl origArray = (ArrayImpl) origType;
Expand Down Expand Up @@ -472,7 +608,7 @@ private UnionImpl(Set<TypeDescriptorImpl> types) {
TypeDescriptorImpl narrow(final TypeDescriptorImpl origType, TypeDescriptorImpl byType) {
final TypeDescriptorImpl other = other(origType, byType);
final Class<? extends TypeDescriptorImpl> otherClz = other.getClass();
if (otherClz == PrimitiveImpl.class || otherClz == ArrayImpl.class) {
if (otherClz == PrimitiveImpl.class || otherClz == ArrayImpl.class || otherClz == ExecutableImpl.class) {
for (TypeDescriptorImpl type : types) {
final TypeDescriptorImpl narrowed = other == origType ? other.narrow(other, type) : type.narrow(type, other);
if (narrowed != null) {
Expand All @@ -485,11 +621,17 @@ TypeDescriptorImpl narrow(final TypeDescriptorImpl origType, TypeDescriptorImpl
final UnionImpl byUnion = (UnionImpl) byType;
final Set<TypeDescriptorImpl> copy = new HashSet<>(origUnion.types.size());
ArrayImpl arrayToNarrow = null;
Collection<ExecutableImpl> execsToNarrow = null;
for (TypeDescriptorImpl type : origUnion.types) {
if (byUnion.types.contains(type)) {
copy.add(type);
} else if (type.getClass() == ArrayImpl.class) {
arrayToNarrow = (ArrayImpl) type;
} else if (type.getClass() == ExecutableImpl.class) {
if (execsToNarrow == null) {
execsToNarrow = new ArrayList<>();
}
execsToNarrow.add((ExecutableImpl) type);
}
}
if (arrayToNarrow != null) {
Expand All @@ -507,6 +649,23 @@ TypeDescriptorImpl narrow(final TypeDescriptorImpl origType, TypeDescriptorImpl
}
}
}
if (execsToNarrow != null) {
final List<ExecutableImpl> byExecs = new ArrayList<>();
for (TypeDescriptorImpl type : byUnion.types) {
if (type.getClass() == ExecutableImpl.class) {
byExecs.add((ExecutableImpl) type);
}
}
for (ExecutableImpl execToNarrow : execsToNarrow) {
for (ExecutableImpl byExec : byExecs) {
final TypeDescriptorImpl narrowedExec = execToNarrow.narrow(execToNarrow, byExec);
if (narrowedExec != null) {
copy.add(narrowedExec);
break;
}
}
}
}
final int copySize = copy.size();
if (copySize == 0) {
return null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ public class SLTCKLanguageProvider implements LanguageProvider {
private static final String ID = "sl";
private static final String PATTERN_VALUE_FNC = "function %s() {return %s;}";
private static final String PATTERN_BIN_OP_FNC = "function %s(a,b) {return a %s b;}";
private static final String PATTERN_POST_OP_FNC = "function %s(a) {a %s;}";
private static final String[] PATTERN_STATEMENTS = {
"function %s() {r = 0;\n%s\nreturn r;\n}",
"function %s(p1) {r = 0;\n%s\nreturn r;\n}",
Expand All @@ -83,7 +84,7 @@ public Collection<? extends Snippet> createValueConstructors(Context context) {
res.add(createValueConstructor(context, "1", "number", "createNumber", TypeDescriptor.NUMBER));
res.add(createValueConstructor(context, "9223372036854775808", "bigNumber", "createBigNumber", TypeDescriptor.NUMBER));
res.add(createValueConstructor(context, "\"string\"", "string", "createString", TypeDescriptor.STRING));
final Snippet.Builder opb = Snippet.newBuilder(
Snippet.Builder opb = Snippet.newBuilder(
"object",
eval(
context,
Expand All @@ -95,6 +96,20 @@ public Collection<? extends Snippet> createValueConstructors(Context context) {
"createObject"),
TypeDescriptor.OBJECT);
res.add(opb.build());
opb = Snippet.newBuilder(
"function",
eval(
context,
"function fn() {\n" +
"}" +
"function createFunction() {\n" +
"return fn;\n" +
"}",
"createFunction"),
TypeDescriptor.union(
TypeDescriptor.OBJECT,
TypeDescriptor.EXECUTABLE));
res.add(opb.build());
return Collections.unmodifiableCollection(res);
}

Expand Down Expand Up @@ -153,6 +168,9 @@ public Collection<? extends Snippet> createExpressions(Context context) {
Assert.assertTrue(TypeDescriptor.BOOLEAN.isAssignable(TypeDescriptor.forValue(snippetRun.getResult())));
}
}).build());
res.add(createPostfixOperator(context, "()", "callNoArg", TypeDescriptor.NULL, TypeDescriptor.executable(null)).build());
res.add(createPostfixOperator(context, "(1)", "callOneArg", TypeDescriptor.NULL, TypeDescriptor.executable(null, TypeDescriptor.NUMBER)).build());
res.add(createPostfixOperator(context, "(1, \"\")", "callTwoArgs", TypeDescriptor.NULL, TypeDescriptor.executable(null, TypeDescriptor.NUMBER, TypeDescriptor.STRING)).build());
return Collections.unmodifiableCollection(res);
}

Expand Down Expand Up @@ -210,6 +228,16 @@ private static Snippet.Builder createBinaryOperator(
return Snippet.newBuilder(operator, fnc, type).parameterTypes(ltype, rtype);
}

private static Snippet.Builder createPostfixOperator(
final Context context,
final String operator,
final String functionName,
final TypeDescriptor type,
final TypeDescriptor ltype) {
final Value fnc = eval(context, String.format(PATTERN_POST_OP_FNC, functionName, operator), functionName);
return Snippet.newBuilder(operator, fnc, type).parameterTypes(ltype);
}

private static Snippet createStatement(
final Context context,
final String id,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@
*/
package com.oracle.truffle.tck.tests;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.graalvm.polyglot.tck.TypeDescriptor;
import org.junit.Assert;
import org.junit.Test;
Expand Down Expand Up @@ -143,4 +146,86 @@ public void testUnion() {
final TypeDescriptor objOrArrNum = TypeDescriptor.union(TypeDescriptor.OBJECT, arrNum);
Assert.assertTrue(numOrBoolOrArrNumBool.isAssignable(objOrArrNum));
}

@Test
public void testExecutable() {
final TypeDescriptor exeAnyAny = TypeDescriptor.EXECUTABLE;
final TypeDescriptor exeAnyNoArgs = TypeDescriptor.executable(null);
final TypeDescriptor exeAnyStr = TypeDescriptor.executable(null, TypeDescriptor.STRING);
final TypeDescriptor exeAnyStrNum = TypeDescriptor.executable(null, TypeDescriptor.STRING, TypeDescriptor.NUMBER);
final TypeDescriptor exeStrNoArgs = TypeDescriptor.executable(TypeDescriptor.STRING);
final TypeDescriptor exeStrStr = TypeDescriptor.executable(TypeDescriptor.STRING, TypeDescriptor.STRING);
final TypeDescriptor exeAnyUnionUnion = TypeDescriptor.executable(null, TypeDescriptor.union(TypeDescriptor.NUMBER, TypeDescriptor.STRING),
TypeDescriptor.union(TypeDescriptor.NUMBER, TypeDescriptor.OBJECT));
final List<TypeDescriptor> eds = new ArrayList<>();
Collections.addAll(eds, exeAnyAny, exeAnyNoArgs, exeAnyStr, exeAnyStrNum, exeStrNoArgs, exeStrStr, exeAnyUnionUnion);
final List<TypeDescriptor> otherTypes = new ArrayList<>();
Collections.addAll(otherTypes, PREDEFINED);
otherTypes.add(TypeDescriptor.array(TypeDescriptor.BOOLEAN));
otherTypes.add(TypeDescriptor.union(TypeDescriptor.BOOLEAN, TypeDescriptor.NUMBER));
for (TypeDescriptor td : otherTypes) {
for (TypeDescriptor ed : eds) {
Assert.assertFalse(ed.isAssignable(td));
Assert.assertFalse(td.isAssignable(ed));
}
}
Assert.assertTrue(exeAnyAny.isAssignable(exeAnyNoArgs));
Assert.assertFalse(exeAnyAny.isAssignable(exeAnyStr));
Assert.assertFalse(exeAnyAny.isAssignable(exeAnyStrNum));
Assert.assertTrue(exeAnyAny.isAssignable(exeStrNoArgs));
Assert.assertFalse(exeAnyAny.isAssignable(exeStrStr));
Assert.assertFalse(exeAnyAny.isAssignable(exeAnyUnionUnion));
Assert.assertTrue(exeAnyNoArgs.isAssignable(exeAnyAny));
Assert.assertFalse(exeAnyNoArgs.isAssignable(exeAnyStr));
Assert.assertFalse(exeAnyNoArgs.isAssignable(exeAnyStrNum));
Assert.assertTrue(exeAnyNoArgs.isAssignable(exeStrNoArgs));
Assert.assertFalse(exeAnyNoArgs.isAssignable(exeStrStr));
Assert.assertFalse(exeAnyNoArgs.isAssignable(exeAnyUnionUnion));
Assert.assertTrue(exeAnyStr.isAssignable(exeAnyAny));
Assert.assertTrue(exeAnyStr.isAssignable(exeAnyNoArgs));
Assert.assertFalse(exeAnyStr.isAssignable(exeAnyStrNum));
Assert.assertTrue(exeAnyStr.isAssignable(exeStrNoArgs));
Assert.assertTrue(exeAnyStr.isAssignable(exeStrStr));
Assert.assertFalse(exeAnyStr.isAssignable(exeAnyUnionUnion));
Assert.assertTrue(exeAnyStrNum.isAssignable(exeAnyAny));
Assert.assertTrue(exeAnyStrNum.isAssignable(exeAnyNoArgs));
Assert.assertTrue(exeAnyStrNum.isAssignable(exeAnyStr));
Assert.assertTrue(exeAnyStrNum.isAssignable(exeStrNoArgs));
Assert.assertTrue(exeAnyStrNum.isAssignable(exeStrStr));
Assert.assertTrue(exeAnyStrNum.isAssignable(exeAnyUnionUnion));
Assert.assertFalse(exeStrNoArgs.isAssignable(exeAnyAny));
Assert.assertFalse(exeStrNoArgs.isAssignable(exeAnyNoArgs));
Assert.assertFalse(exeStrNoArgs.isAssignable(exeAnyStr));
Assert.assertFalse(exeStrNoArgs.isAssignable(exeAnyStrNum));
Assert.assertFalse(exeStrNoArgs.isAssignable(exeStrStr));
Assert.assertFalse(exeStrNoArgs.isAssignable(exeAnyUnionUnion));
Assert.assertFalse(exeStrStr.isAssignable(exeAnyAny));
Assert.assertFalse(exeStrStr.isAssignable(exeAnyNoArgs));
Assert.assertFalse(exeStrStr.isAssignable(exeAnyStr));
Assert.assertFalse(exeStrStr.isAssignable(exeAnyStrNum));
Assert.assertTrue(exeStrStr.isAssignable(exeStrNoArgs));
Assert.assertFalse(exeStrStr.isAssignable(exeAnyUnionUnion));
Assert.assertTrue(exeAnyUnionUnion.isAssignable(exeAnyAny));
Assert.assertTrue(exeAnyUnionUnion.isAssignable(exeAnyNoArgs));
Assert.assertTrue(exeAnyUnionUnion.isAssignable(exeAnyStr));
Assert.assertTrue(exeAnyUnionUnion.isAssignable(exeAnyStrNum));
Assert.assertTrue(exeAnyUnionUnion.isAssignable(exeStrNoArgs));
Assert.assertTrue(exeAnyUnionUnion.isAssignable(exeStrStr));
// Arrays
final TypeDescriptor ae1 = TypeDescriptor.array(TypeDescriptor.EXECUTABLE);
final TypeDescriptor ae2 = TypeDescriptor.array(TypeDescriptor.executable(null, TypeDescriptor.BOOLEAN));
final TypeDescriptor ab = TypeDescriptor.array(TypeDescriptor.BOOLEAN);
Assert.assertFalse(ae1.isAssignable(ae2));
Assert.assertFalse(ae1.isAssignable(ab));
Assert.assertTrue(ae2.isAssignable(ae1));
Assert.assertFalse(ae2.isAssignable(ab));
// Unions
final TypeDescriptor ue1 = TypeDescriptor.union(TypeDescriptor.EXECUTABLE, TypeDescriptor.OBJECT);
final TypeDescriptor ue2 = TypeDescriptor.union(TypeDescriptor.executable(null, TypeDescriptor.BOOLEAN), TypeDescriptor.STRING);
final TypeDescriptor up = TypeDescriptor.union(TypeDescriptor.BOOLEAN, TypeDescriptor.NUMBER);
Assert.assertFalse(ue1.isAssignable(ue2));
Assert.assertFalse(ue1.isAssignable(up));
Assert.assertTrue(ue2.isAssignable(ue1));
Assert.assertFalse(ue2.isAssignable(up));
}
}

0 comments on commit 779cd83

Please sign in to comment.