package com.oracle.truffle.js.runtime.interop;
import com.oracle.truffle.api.CompilerAsserts;
import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
import com.oracle.truffle.api.TruffleLanguage;
import com.oracle.truffle.api.dsl.Cached;
import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.api.interop.InteropLibrary;
import com.oracle.truffle.api.interop.TruffleObject;
import com.oracle.truffle.api.library.CachedLibrary;
import com.oracle.truffle.api.library.ExportLibrary;
import com.oracle.truffle.api.library.ExportMessage;
import com.oracle.truffle.js.lang.JavaScriptLanguage;
import com.oracle.truffle.js.nodes.JSGuards;
import com.oracle.truffle.js.runtime.BigInt;
import com.oracle.truffle.js.runtime.Symbol;
@ExportLibrary(InteropLibrary.class)
public final class JSMetaType implements TruffleObject {
@FunctionalInterface
public interface TypeCheck {
boolean check(InteropLibrary lib, Object value);
}
public static final JSMetaType NULL = new JSMetaType("null", InteropLibrary::isNull);
public static final JSMetaType BOOLEAN = new JSMetaType("boolean", InteropLibrary::isBoolean);
public static final JSMetaType STRING = new JSMetaType("string", InteropLibrary::isString);
public static final JSMetaType NUMBER = new JSMetaType("number", InteropLibrary::isNumber);
public static final JSMetaType FUNCTION = new JSMetaType("function", (l, v) -> l.isExecutable(v) || l.isInstantiable(v));
public static final JSMetaType DATE = new JSMetaType("date", InteropLibrary::isInstant);
public static final JSMetaType ARRAY = new JSMetaType("array", InteropLibrary::hasArrayElements);
public static final JSMetaType OBJECT = new JSMetaType("object", InteropLibrary::hasMembers);
@CompilationFinal(dimensions = 1) static final JSMetaType[] KNOWN_TYPES = new JSMetaType[]{NULL, BOOLEAN, STRING, NUMBER, FUNCTION, DATE, ARRAY, OBJECT};
public static final JSMetaType JS_NULL = new JSMetaType("null", (l, v) -> JSGuards.isJSNull(v));
public static final JSMetaType JS_UNDEFINED = new JSMetaType("undefined", (l, v) -> JSGuards.isUndefined(v));
public static final JSMetaType JS_BIGINT = new JSMetaType("bigint", (l, v) -> v instanceof BigInt);
public static final JSMetaType JS_SYMBOL = new JSMetaType("symbol", (l, v) -> v instanceof Symbol);
public static final JSMetaType JS_PROXY = new JSMetaType("Proxy", (l, v) -> JSGuards.isJSProxy(v));
private final String typeName;
private final TypeCheck isInstance;
public JSMetaType(String typeName, TypeCheck isInstance) {
this.typeName = typeName;
this.isInstance = isInstance;
}
public boolean isInstance(Object instance, InteropLibrary interop) {
CompilerAsserts.partialEvaluationConstant(this);
return isInstance.check(interop, instance);
}
@SuppressWarnings("static-method")
@ExportMessage
boolean hasLanguage() {
return true;
}
@SuppressWarnings("static-method")
@ExportMessage
Class<? extends TruffleLanguage<?>> getLanguage() {
return JavaScriptLanguage.class;
}
@SuppressWarnings("static-method")
@ExportMessage
boolean isMetaObject() {
return true;
}
@ExportMessage(name = "getMetaQualifiedName")
@ExportMessage(name = "getMetaSimpleName")
public String getTypeName() {
return typeName;
}
@ExportMessage(name = "toDisplayString")
Object toDisplayString(@SuppressWarnings("unused") boolean allowSideEffects) {
return typeName;
}
@TruffleBoundary
@Override
public String toString() {
return "JSMetaType[" + typeName + "]";
}
@ExportMessage
static class IsMetaInstance {
@Specialization(guards = "type == cachedType", limit = "3")
static boolean doCached(@SuppressWarnings("unused") JSMetaType type, Object value,
@Cached("type") JSMetaType cachedType,
@CachedLibrary("value") InteropLibrary valueLib) {
return cachedType.isInstance.check(valueLib, value);
}
@TruffleBoundary
@Specialization(replaces = "doCached")
static boolean doGeneric(JSMetaType type, Object value) {
return type.isInstance.check(InteropLibrary.getFactory().getUncached(), value);
}
}
}