Compare commits
5 Commits
0.9.14-bet
...
0.9.19-bet
| Author | SHA1 | Date | |
|---|---|---|---|
|
b5b63c4342
|
|||
|
18f70a0d58
|
|||
|
d38b600366
|
|||
|
0ac7af2ea3
|
|||
|
5185c93663
|
@@ -1,15 +1,24 @@
|
||||
package me.topchetoeu.jscript.runtime.values;
|
||||
|
||||
import java.util.WeakHashMap;
|
||||
|
||||
import me.topchetoeu.jscript.runtime.Context;
|
||||
import me.topchetoeu.jscript.runtime.Extensions;
|
||||
import me.topchetoeu.jscript.runtime.Key;
|
||||
|
||||
public class NativeWrapper extends ObjectValue {
|
||||
private static final Key<WeakHashMap<Object, NativeWrapper>> WRAPPERS = new Key<>();
|
||||
private static final Object NATIVE_PROTO = new Object();
|
||||
public final Object wrapped;
|
||||
|
||||
@Override
|
||||
public ObjectValue getPrototype(Context ctx) {
|
||||
if (ctx.environment != null && prototype == NATIVE_PROTO) return ctx.environment.wrappers.getProto(wrapped.getClass());
|
||||
else return super.getPrototype(ctx);
|
||||
if (ctx.environment != null && prototype == NATIVE_PROTO) {
|
||||
var clazz = wrapped.getClass();
|
||||
var res = ctx.environment.wrappers.getProto(clazz);
|
||||
if (res != null) return res;
|
||||
}
|
||||
return super.getPrototype(ctx);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -25,8 +34,20 @@ public class NativeWrapper extends ObjectValue {
|
||||
return wrapped.hashCode();
|
||||
}
|
||||
|
||||
public NativeWrapper(Object wrapped) {
|
||||
private NativeWrapper(Object wrapped) {
|
||||
this.wrapped = wrapped;
|
||||
prototype = NATIVE_PROTO;
|
||||
}
|
||||
|
||||
public static NativeWrapper of(Extensions exts, Object wrapped) {
|
||||
var wrappers = exts == null ? null : exts.get(WRAPPERS);
|
||||
|
||||
if (wrappers == null) return new NativeWrapper(wrapped);
|
||||
if (wrappers.containsKey(wrapped)) return wrappers.get(wrapped);
|
||||
|
||||
var res = new NativeWrapper(wrapped);
|
||||
wrappers.put(wrapped, res);
|
||||
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -153,18 +153,18 @@ public class ObjectValue {
|
||||
}
|
||||
|
||||
public ObjectValue getPrototype(Context ctx) {
|
||||
if (prototype instanceof ObjectValue || prototype == null) return (ObjectValue)prototype;
|
||||
|
||||
try {
|
||||
if (prototype == OBJ_PROTO) return ctx.get(Environment.OBJECT_PROTO);
|
||||
if (prototype == ARR_PROTO) return ctx.get(Environment.ARRAY_PROTO);
|
||||
if (prototype == FUNC_PROTO) return ctx.get(Environment.FUNCTION_PROTO);
|
||||
if (prototype == ERR_PROTO) return ctx.get(Environment.ERROR_PROTO);
|
||||
if (prototype == RANGE_ERR_PROTO) return ctx.get(Environment.RANGE_ERR_PROTO);
|
||||
if (prototype == SYNTAX_ERR_PROTO) return ctx.get(Environment.SYNTAX_ERR_PROTO);
|
||||
if (prototype == TYPE_ERR_PROTO) return ctx.get(Environment.TYPE_ERR_PROTO);
|
||||
return ctx.get(Environment.OBJECT_PROTO);
|
||||
}
|
||||
catch (NullPointerException e) { return null; }
|
||||
|
||||
return (ObjectValue)prototype;
|
||||
}
|
||||
public final boolean setPrototype(PlaceholderProto val) {
|
||||
if (!extensible()) return false;
|
||||
|
||||
@@ -58,9 +58,8 @@ public class Values {
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <T> T wrapper(Object val, Class<T> clazz) {
|
||||
if (!isWrapper(val)) val = new NativeWrapper(val);
|
||||
var res = (NativeWrapper)val;
|
||||
if (res != null && clazz.isInstance(res.wrapped)) return (T)res.wrapped;
|
||||
if (isWrapper(val)) val = ((NativeWrapper)val).wrapped;
|
||||
if (val != null && clazz.isInstance(val)) return (T)val;
|
||||
else return null;
|
||||
}
|
||||
|
||||
@@ -471,7 +470,7 @@ public class Values {
|
||||
else return ctx.environment.wrappers.getConstr((Class<?>)val);
|
||||
}
|
||||
|
||||
return new NativeWrapper(val);
|
||||
return NativeWrapper.of(ctx, val);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@@ -536,7 +535,7 @@ public class Values {
|
||||
if (obj == null) return null;
|
||||
if (clazz.isInstance(obj)) return (T)obj;
|
||||
if (clazz.isAssignableFrom(NativeWrapper.class)) {
|
||||
return (T)new NativeWrapper(obj);
|
||||
return (T)NativeWrapper.of(ctx, obj);
|
||||
}
|
||||
|
||||
throw new ConvertException(type(obj), clazz.getSimpleName());
|
||||
|
||||
@@ -25,6 +25,7 @@ public class NativeWrapperProvider implements WrapperProvider {
|
||||
private final HashMap<Class<?>, FunctionValue> constructors = new HashMap<>();
|
||||
private final HashMap<Class<?>, ObjectValue> prototypes = new HashMap<>();
|
||||
private final HashMap<Class<?>, ObjectValue> namespaces = new HashMap<>();
|
||||
private final HashSet<Class<?>> ignore = new HashSet<>();
|
||||
private final Environment env;
|
||||
|
||||
private static Object call(Context ctx, String name, Method method, Object thisArg, Object... args) {
|
||||
@@ -106,11 +107,12 @@ public class NativeWrapperProvider implements WrapperProvider {
|
||||
else return name;
|
||||
}
|
||||
|
||||
private static void apply(ObjectValue obj, Environment env, ExposeTarget target, Class<?> clazz) {
|
||||
private static boolean apply(ObjectValue obj, Environment env, ExposeTarget target, Class<?> clazz) {
|
||||
var getters = new HashMap<Object, FunctionValue>();
|
||||
var setters = new HashMap<Object, FunctionValue>();
|
||||
var props = new HashSet<Object>();
|
||||
var nonProps = new HashSet<Object>();
|
||||
var any = false;
|
||||
|
||||
for (var method : clazz.getDeclaredMethods()) {
|
||||
for (var annotation : method.getAnnotationsByType(Expose.class)) {
|
||||
@@ -120,6 +122,7 @@ public class NativeWrapperProvider implements WrapperProvider {
|
||||
var name = getName(method, annotation.value());
|
||||
var key = getKey(name);
|
||||
var repeat = false;
|
||||
any = true;
|
||||
|
||||
switch (annotation.type()) {
|
||||
case INIT:
|
||||
@@ -168,6 +171,7 @@ public class NativeWrapperProvider implements WrapperProvider {
|
||||
var name = getName(method, annotation.value());
|
||||
var key = getKey(name);
|
||||
var repeat = false;
|
||||
any = true;
|
||||
|
||||
if (props.contains(key) || nonProps.contains(key)) repeat = true;
|
||||
else {
|
||||
@@ -191,6 +195,7 @@ public class NativeWrapperProvider implements WrapperProvider {
|
||||
var name = getName(field, annotation.value());
|
||||
var key = getKey(name);
|
||||
var repeat = false;
|
||||
any = true;
|
||||
|
||||
if (props.contains(key) || nonProps.contains(key)) repeat = true;
|
||||
else {
|
||||
@@ -227,6 +232,8 @@ public class NativeWrapperProvider implements WrapperProvider {
|
||||
}
|
||||
|
||||
for (var key : props) obj.defineProperty(null, key, getters.get(key), setters.get(key), true, false);
|
||||
|
||||
return any;
|
||||
}
|
||||
|
||||
private static Method getConstructor(Environment env, Class<?> clazz) {
|
||||
@@ -248,7 +255,7 @@ public class NativeWrapperProvider implements WrapperProvider {
|
||||
public static ObjectValue makeProto(Environment env, Class<?> clazz) {
|
||||
var res = new ObjectValue();
|
||||
res.defineProperty(null, Symbol.get("Symbol.typeName"), getName(clazz));
|
||||
apply(res, env, ExposeTarget.PROTOTYPE, clazz);
|
||||
if (!apply(res, env, ExposeTarget.PROTOTYPE, clazz)) return null;
|
||||
return res;
|
||||
}
|
||||
/**
|
||||
@@ -277,12 +284,27 @@ public class NativeWrapperProvider implements WrapperProvider {
|
||||
public static ObjectValue makeNamespace(Environment ctx, Class<?> clazz) {
|
||||
var res = new ObjectValue();
|
||||
res.defineProperty(null, Symbol.get("Symbol.typeName"), getName(clazz));
|
||||
apply(res, ctx, ExposeTarget.NAMESPACE, clazz);
|
||||
if (!apply(res, ctx, ExposeTarget.NAMESPACE, clazz)) return null;
|
||||
return res;
|
||||
}
|
||||
|
||||
private void updateProtoChain(Class<?> clazz, ObjectValue proto, FunctionValue constr) {
|
||||
var parent = clazz;
|
||||
|
||||
while (true) {
|
||||
parent = parent.getSuperclass();
|
||||
if (parent == null) return;
|
||||
|
||||
var parentProto = getProto(parent);
|
||||
var parentConstr = getConstr(parent);
|
||||
|
||||
if (parentProto != null) Values.setPrototype(Context.NULL, proto, parentProto);
|
||||
if (parentConstr != null) Values.setPrototype(Context.NULL, constr, parentConstr);
|
||||
}
|
||||
}
|
||||
|
||||
private void initType(Class<?> clazz, FunctionValue constr, ObjectValue proto) {
|
||||
if (constr != null && proto != null) return;
|
||||
if (constr != null && proto != null || ignore.contains(clazz)) return;
|
||||
// i vomit
|
||||
if (
|
||||
clazz == Object.class ||
|
||||
@@ -301,44 +323,72 @@ public class NativeWrapperProvider implements WrapperProvider {
|
||||
if (constr == null) constr = makeConstructor(env, clazz);
|
||||
if (proto == null) proto = makeProto(env, clazz);
|
||||
|
||||
if (constr == null || proto == null) return;
|
||||
|
||||
proto.defineProperty(null, "constructor", constr, true, false, false);
|
||||
constr.defineProperty(null, "prototype", proto, true, false, false);
|
||||
|
||||
prototypes.put(clazz, proto);
|
||||
constructors.put(clazz, constr);
|
||||
|
||||
var parent = clazz.getSuperclass();
|
||||
if (parent == null) return;
|
||||
|
||||
var parentProto = getProto(parent);
|
||||
var parentConstr = getConstr(parent);
|
||||
|
||||
if (parentProto != null) Values.setPrototype(Context.NULL, proto, parentProto);
|
||||
if (parentConstr != null) Values.setPrototype(Context.NULL, constr, parentConstr);
|
||||
updateProtoChain(clazz, proto, constr);
|
||||
}
|
||||
|
||||
public ObjectValue getProto(Class<?> clazz) {
|
||||
initType(clazz, constructors.get(clazz), prototypes.get(clazz));
|
||||
return prototypes.get(clazz);
|
||||
while (clazz != null) {
|
||||
var res = prototypes.get(clazz);
|
||||
if (res != null) return res;
|
||||
clazz = clazz.getSuperclass();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
public ObjectValue getNamespace(Class<?> clazz) {
|
||||
if (!namespaces.containsKey(clazz)) namespaces.put(clazz, makeNamespace(env, clazz));
|
||||
return namespaces.get(clazz);
|
||||
while (clazz != null) {
|
||||
var res = namespaces.get(clazz);
|
||||
if (res != null) return res;
|
||||
clazz = clazz.getSuperclass();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
public FunctionValue getConstr(Class<?> clazz) {
|
||||
initType(clazz, constructors.get(clazz), prototypes.get(clazz));
|
||||
return constructors.get(clazz);
|
||||
while (clazz != null) {
|
||||
var res = constructors.get(clazz);
|
||||
if (res != null) return res;
|
||||
clazz = clazz.getSuperclass();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override public WrapperProvider fork(Environment env) {
|
||||
return new NativeWrapperProvider(env);
|
||||
}
|
||||
|
||||
public void setProto(Class<?> clazz, ObjectValue value) {
|
||||
prototypes.put(clazz, value);
|
||||
public void set(Class<?> clazz, Class<?> wrapper) {
|
||||
if (wrapper == null) set(clazz, null, null);
|
||||
else set(clazz, makeProto(env, wrapper), makeConstructor(env, wrapper));
|
||||
}
|
||||
public void set(Class<?> clazz, ObjectValue proto, FunctionValue constructor) {
|
||||
if (proto != null && constructor != null) {
|
||||
prototypes.put(clazz, proto);
|
||||
constructors.put(clazz, constructor);
|
||||
ignore.remove(clazz);
|
||||
|
||||
for (var el : prototypes.keySet()) {
|
||||
if (clazz.isAssignableFrom(el)) {
|
||||
updateProtoChain(el, getProto(el), getConstr(el));
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
prototypes.remove(clazz);
|
||||
constructors.remove(clazz);
|
||||
ignore.remove(clazz);
|
||||
|
||||
initType(clazz, constructor, proto);
|
||||
}
|
||||
public void setConstr(Class<?> clazz, FunctionValue value) {
|
||||
constructors.put(clazz, value);
|
||||
}
|
||||
|
||||
private void initError() {
|
||||
@@ -354,8 +404,7 @@ public class NativeWrapperProvider implements WrapperProvider {
|
||||
proto.defineProperty(null, "constructor", constr, true, false, false);
|
||||
constr.defineProperty(null, "prototype", proto, true, false, false);
|
||||
|
||||
setProto(Throwable.class, proto);
|
||||
setConstr(Throwable.class, constr);
|
||||
set(Throwable.class, proto, constr);
|
||||
}
|
||||
|
||||
public NativeWrapperProvider(Environment env) {
|
||||
|
||||
Reference in New Issue
Block a user