Compare commits

...

4 Commits

Author SHA1 Message Date
b5b63c4342 fix: make global cache of native wrappers 2024-03-28 16:08:07 +02:00
18f70a0d58 fix: i hate wrappers 2024-03-28 15:10:21 +02:00
d38b600366 fix: some more wrapper issues 2024-03-28 14:52:49 +02:00
0ac7af2ea3 fix: take into account empty classes 2024-03-28 14:21:23 +02:00
3 changed files with 65 additions and 24 deletions

View File

@@ -1,15 +1,21 @@
package me.topchetoeu.jscript.runtime.values; package me.topchetoeu.jscript.runtime.values;
import java.util.WeakHashMap;
import me.topchetoeu.jscript.runtime.Context; import me.topchetoeu.jscript.runtime.Context;
import me.topchetoeu.jscript.runtime.Extensions;
import me.topchetoeu.jscript.runtime.Key;
public class NativeWrapper extends ObjectValue { public class NativeWrapper extends ObjectValue {
private static final Key<WeakHashMap<Object, NativeWrapper>> WRAPPERS = new Key<>();
private static final Object NATIVE_PROTO = new Object(); private static final Object NATIVE_PROTO = new Object();
public final Object wrapped; public final Object wrapped;
@Override @Override
public ObjectValue getPrototype(Context ctx) { public ObjectValue getPrototype(Context ctx) {
if (ctx.environment != null && prototype == NATIVE_PROTO) { if (ctx.environment != null && prototype == NATIVE_PROTO) {
var res = ctx.environment.wrappers.getProto(wrapped.getClass()); var clazz = wrapped.getClass();
var res = ctx.environment.wrappers.getProto(clazz);
if (res != null) return res; if (res != null) return res;
} }
return super.getPrototype(ctx); return super.getPrototype(ctx);
@@ -28,8 +34,20 @@ public class NativeWrapper extends ObjectValue {
return wrapped.hashCode(); return wrapped.hashCode();
} }
public NativeWrapper(Object wrapped) { private NativeWrapper(Object wrapped) {
this.wrapped = wrapped; this.wrapped = wrapped;
prototype = NATIVE_PROTO; 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;
}
} }

View File

@@ -58,9 +58,8 @@ public class Values {
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public static <T> T wrapper(Object val, Class<T> clazz) { public static <T> T wrapper(Object val, Class<T> clazz) {
if (!isWrapper(val)) val = new NativeWrapper(val); if (isWrapper(val)) val = ((NativeWrapper)val).wrapped;
var res = (NativeWrapper)val; if (val != null && clazz.isInstance(val)) return (T)val;
if (res != null && clazz.isInstance(res.wrapped)) return (T)res.wrapped;
else return null; else return null;
} }
@@ -471,7 +470,7 @@ public class Values {
else return ctx.environment.wrappers.getConstr((Class<?>)val); else return ctx.environment.wrappers.getConstr((Class<?>)val);
} }
return new NativeWrapper(val); return NativeWrapper.of(ctx, val);
} }
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
@@ -536,7 +535,7 @@ public class Values {
if (obj == null) return null; if (obj == null) return null;
if (clazz.isInstance(obj)) return (T)obj; if (clazz.isInstance(obj)) return (T)obj;
if (clazz.isAssignableFrom(NativeWrapper.class)) { if (clazz.isAssignableFrom(NativeWrapper.class)) {
return (T)new NativeWrapper(obj); return (T)NativeWrapper.of(ctx, obj);
} }
throw new ConvertException(type(obj), clazz.getSimpleName()); throw new ConvertException(type(obj), clazz.getSimpleName());

View File

@@ -288,8 +288,23 @@ public class NativeWrapperProvider implements WrapperProvider {
return res; 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) { 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 // i vomit
if ( if (
clazz == Object.class || clazz == Object.class ||
@@ -316,32 +331,35 @@ public class NativeWrapperProvider implements WrapperProvider {
prototypes.put(clazz, proto); prototypes.put(clazz, proto);
constructors.put(clazz, constr); constructors.put(clazz, constr);
var parent = clazz; updateProtoChain(clazz, proto, constr);
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);
}
} }
public ObjectValue getProto(Class<?> clazz) { public ObjectValue getProto(Class<?> clazz) {
initType(clazz, constructors.get(clazz), prototypes.get(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) { public ObjectValue getNamespace(Class<?> clazz) {
if (!namespaces.containsKey(clazz)) namespaces.put(clazz, makeNamespace(env, 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) { public FunctionValue getConstr(Class<?> clazz) {
initType(clazz, constructors.get(clazz), prototypes.get(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) { @Override public WrapperProvider fork(Environment env) {
@@ -357,6 +375,12 @@ public class NativeWrapperProvider implements WrapperProvider {
prototypes.put(clazz, proto); prototypes.put(clazz, proto);
constructors.put(clazz, constructor); constructors.put(clazz, constructor);
ignore.remove(clazz); ignore.remove(clazz);
for (var el : prototypes.keySet()) {
if (clazz.isAssignableFrom(el)) {
updateProtoChain(el, getProto(el), getConstr(el));
}
}
} }
else { else {
prototypes.remove(clazz); prototypes.remove(clazz);