fix: don't include non-exposing wrappers in proto chain

feat: allow adding custom wrappers
This commit is contained in:
TopchetoEU 2024-03-28 00:57:09 +02:00
parent 510422cab7
commit 5185c93663
Signed by: topchetoeu
GPG Key ID: 6531B8583E5F6ED4
3 changed files with 48 additions and 20 deletions

View File

@ -8,8 +8,11 @@ public class NativeWrapper extends ObjectValue {
@Override @Override
public ObjectValue getPrototype(Context ctx) { public ObjectValue getPrototype(Context ctx) {
if (ctx.environment != null && prototype == NATIVE_PROTO) return ctx.environment.wrappers.getProto(wrapped.getClass()); if (ctx.environment != null && prototype == NATIVE_PROTO) {
else return super.getPrototype(ctx); var res = ctx.environment.wrappers.getProto(wrapped.getClass());
if (res != null) return res;
}
return super.getPrototype(ctx);
} }
@Override @Override

View File

@ -153,18 +153,18 @@ public class ObjectValue {
} }
public ObjectValue getPrototype(Context ctx) { public ObjectValue getPrototype(Context ctx) {
if (prototype instanceof ObjectValue || prototype == null) return (ObjectValue)prototype;
try { try {
if (prototype == OBJ_PROTO) return ctx.get(Environment.OBJECT_PROTO);
if (prototype == ARR_PROTO) return ctx.get(Environment.ARRAY_PROTO); if (prototype == ARR_PROTO) return ctx.get(Environment.ARRAY_PROTO);
if (prototype == FUNC_PROTO) return ctx.get(Environment.FUNCTION_PROTO); if (prototype == FUNC_PROTO) return ctx.get(Environment.FUNCTION_PROTO);
if (prototype == ERR_PROTO) return ctx.get(Environment.ERROR_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 == RANGE_ERR_PROTO) return ctx.get(Environment.RANGE_ERR_PROTO);
if (prototype == SYNTAX_ERR_PROTO) return ctx.get(Environment.SYNTAX_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); if (prototype == TYPE_ERR_PROTO) return ctx.get(Environment.TYPE_ERR_PROTO);
return ctx.get(Environment.OBJECT_PROTO);
} }
catch (NullPointerException e) { return null; } catch (NullPointerException e) { return null; }
return (ObjectValue)prototype;
} }
public final boolean setPrototype(PlaceholderProto val) { public final boolean setPrototype(PlaceholderProto val) {
if (!extensible()) return false; if (!extensible()) return false;

View File

@ -25,6 +25,7 @@ public class NativeWrapperProvider implements WrapperProvider {
private final HashMap<Class<?>, FunctionValue> constructors = new HashMap<>(); private final HashMap<Class<?>, FunctionValue> constructors = new HashMap<>();
private final HashMap<Class<?>, ObjectValue> prototypes = new HashMap<>(); private final HashMap<Class<?>, ObjectValue> prototypes = new HashMap<>();
private final HashMap<Class<?>, ObjectValue> namespaces = new HashMap<>(); private final HashMap<Class<?>, ObjectValue> namespaces = new HashMap<>();
private final HashSet<Class<?>> ignore = new HashSet<>();
private final Environment env; private final Environment env;
private static Object call(Context ctx, String name, Method method, Object thisArg, Object... args) { 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; 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 getters = new HashMap<Object, FunctionValue>();
var setters = new HashMap<Object, FunctionValue>(); var setters = new HashMap<Object, FunctionValue>();
var props = new HashSet<Object>(); var props = new HashSet<Object>();
var nonProps = new HashSet<Object>(); var nonProps = new HashSet<Object>();
var any = false;
for (var method : clazz.getDeclaredMethods()) { for (var method : clazz.getDeclaredMethods()) {
for (var annotation : method.getAnnotationsByType(Expose.class)) { for (var annotation : method.getAnnotationsByType(Expose.class)) {
@ -120,6 +122,7 @@ public class NativeWrapperProvider implements WrapperProvider {
var name = getName(method, annotation.value()); var name = getName(method, annotation.value());
var key = getKey(name); var key = getKey(name);
var repeat = false; var repeat = false;
any = true;
switch (annotation.type()) { switch (annotation.type()) {
case INIT: case INIT:
@ -168,6 +171,7 @@ public class NativeWrapperProvider implements WrapperProvider {
var name = getName(method, annotation.value()); var name = getName(method, annotation.value());
var key = getKey(name); var key = getKey(name);
var repeat = false; var repeat = false;
any = true;
if (props.contains(key) || nonProps.contains(key)) repeat = true; if (props.contains(key) || nonProps.contains(key)) repeat = true;
else { else {
@ -191,6 +195,7 @@ public class NativeWrapperProvider implements WrapperProvider {
var name = getName(field, annotation.value()); var name = getName(field, annotation.value());
var key = getKey(name); var key = getKey(name);
var repeat = false; var repeat = false;
any = true;
if (props.contains(key) || nonProps.contains(key)) repeat = true; if (props.contains(key) || nonProps.contains(key)) repeat = true;
else { 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); 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) { 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) { public static ObjectValue makeProto(Environment env, Class<?> clazz) {
var res = new ObjectValue(); var res = new ObjectValue();
res.defineProperty(null, Symbol.get("Symbol.typeName"), getName(clazz)); 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; return res;
} }
/** /**
@ -277,7 +284,7 @@ public class NativeWrapperProvider implements WrapperProvider {
public static ObjectValue makeNamespace(Environment ctx, Class<?> clazz) { public static ObjectValue makeNamespace(Environment ctx, Class<?> clazz) {
var res = new ObjectValue(); var res = new ObjectValue();
res.defineProperty(null, Symbol.get("Symbol.typeName"), getName(clazz)); 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; return res;
} }
@ -301,20 +308,27 @@ public class NativeWrapperProvider implements WrapperProvider {
if (constr == null) constr = makeConstructor(env, clazz); if (constr == null) constr = makeConstructor(env, clazz);
if (proto == null) proto = makeProto(env, clazz); if (proto == null) proto = makeProto(env, clazz);
if (constr == null || proto == null) return;
proto.defineProperty(null, "constructor", constr, true, false, false); proto.defineProperty(null, "constructor", constr, true, false, false);
constr.defineProperty(null, "prototype", proto, true, false, false); constr.defineProperty(null, "prototype", proto, true, false, false);
prototypes.put(clazz, proto); prototypes.put(clazz, proto);
constructors.put(clazz, constr); constructors.put(clazz, constr);
var parent = clazz.getSuperclass(); var parent = clazz;
if (parent == null) return;
var parentProto = getProto(parent); while (true) {
var parentConstr = getConstr(parent); parent = parent.getSuperclass();
if (parent == null) return;
if (parentProto != null) Values.setPrototype(Context.NULL, proto, parentProto); var parentProto = getProto(parent);
if (parentConstr != null) Values.setPrototype(Context.NULL, constr, parentConstr); var parentConstr = getConstr(parent);
if (parentProto != null) Values.setPrototype(Context.NULL, proto, parentProto);
if (parentConstr != null) Values.setPrototype(Context.NULL, constr, parentConstr);
}
} }
public ObjectValue getProto(Class<?> clazz) { public ObjectValue getProto(Class<?> clazz) {
@ -334,11 +348,23 @@ public class NativeWrapperProvider implements WrapperProvider {
return new NativeWrapperProvider(env); return new NativeWrapperProvider(env);
} }
public void setProto(Class<?> clazz, ObjectValue value) { public void set(Class<?> clazz, Class<?> wrapper) {
prototypes.put(clazz, value); if (wrapper == null) set(clazz, null, null);
else set(clazz, makeProto(env, wrapper), makeConstructor(env, wrapper));
} }
public void setConstr(Class<?> clazz, FunctionValue value) { public void set(Class<?> clazz, ObjectValue proto, FunctionValue constructor) {
constructors.put(clazz, value); if (proto != null && constructor != null) {
prototypes.put(clazz, proto);
constructors.put(clazz, constructor);
ignore.remove(clazz);
}
else {
prototypes.remove(clazz);
constructors.remove(clazz);
ignore.remove(clazz);
initType(clazz, constructor, proto);
}
} }
private void initError() { private void initError() {
@ -354,8 +380,7 @@ public class NativeWrapperProvider implements WrapperProvider {
proto.defineProperty(null, "constructor", constr, true, false, false); proto.defineProperty(null, "constructor", constr, true, false, false);
constr.defineProperty(null, "prototype", proto, true, false, false); constr.defineProperty(null, "prototype", proto, true, false, false);
setProto(Throwable.class, proto); set(Throwable.class, proto, constr);
setConstr(Throwable.class, constr);
} }
public NativeWrapperProvider(Environment env) { public NativeWrapperProvider(Environment env) {