Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
package org.densy.scriptify.core.script.function.definition;

import org.densy.scriptify.api.script.function.definition.ScriptFunctionArgumentDefinition;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.ToString;
import org.densy.scriptify.api.script.function.definition.ScriptFunctionArgumentDefinition;

@Getter
@AllArgsConstructor
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,11 @@
import org.densy.scriptify.api.script.security.ScriptSecurityManager;
import org.densy.scriptify.core.script.constant.StandardConstantManager;
import org.densy.scriptify.core.script.function.StandardFunctionManager;
import org.densy.scriptify.core.script.module.export.ScriptConstantExport;
import org.densy.scriptify.core.script.module.export.ScriptFunctionDefinitionExport;
import org.densy.scriptify.core.script.security.StandardSecurityManager;
import org.densy.scriptify.js.rhino.script.module.RhinoModuleManager;
import org.densy.scriptify.js.rhino.script.module.RhinoModuleSourceTransformer;
import org.mozilla.javascript.Context;
import org.mozilla.javascript.ScriptableObject;

Expand All @@ -22,6 +26,7 @@
public class JsScript implements Script<Object> {

private final ScriptSecurityManager securityManager = new StandardSecurityManager();
private final RhinoModuleManager moduleManager = new RhinoModuleManager(this);
private ScriptFunctionManager functionManager = new StandardFunctionManager();
private ScriptConstantManager constantManager = new StandardConstantManager();
private final List<String> extraScript = new ArrayList<>();
Expand All @@ -33,7 +38,7 @@ public ScriptSecurityManager getSecurityManager() {

@Override
public ScriptModuleManager getModuleManager() {
throw new UnsupportedOperationException("Rhino does not support a module system.");
return moduleManager;
}

@Override
Expand Down Expand Up @@ -65,7 +70,8 @@ public void addExtraScript(String script) {
public CompiledScript<Object> compile(String script) throws ScriptException {
try {
Context context = Context.enter();
context.setWrapFactory(new JsWrapFactory());
context.setLanguageVersion(Context.VERSION_ES6);
context.setWrapFactory(new JsWrapFactory(moduleManager.getScriptAccess()));

ScriptableObject scope = context.initStandardObjects();

Expand All @@ -76,12 +82,13 @@ public CompiledScript<Object> compile(String script) throws ScriptException {
}

for (ScriptFunctionDefinition definition : functionManager.getFunctions().values()) {
scope.put(definition.getFunction().getName(), scope, new JsFunction(this, definition));
moduleManager.getGlobalModule().export(new ScriptFunctionDefinitionExport(definition));
}

for (ScriptConstant constant : constantManager.getConstants().values()) {
ScriptableObject.putConstProperty(scope, constant.getName(), constant.getValue());
moduleManager.getGlobalModule().export(new ScriptConstantExport(constant));
}
moduleManager.applyTo(context, scope);

// Building full script including extra script code
StringBuilder fullScript = new StringBuilder();
Expand All @@ -90,7 +97,12 @@ public CompiledScript<Object> compile(String script) throws ScriptException {
}
fullScript.append(script);

var compiled = context.compileString(fullScript.toString(), "script", 1, null);
var compiled = context.compileString(
RhinoModuleSourceTransformer.transformScript(fullScript.toString()),
"script",
1,
null
);
return new JsCompiledScript(context, scope, compiled);
} catch (Exception e) {
throw new ScriptException(e);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,22 +1,45 @@
package org.densy.scriptify.js.rhino.script;

import org.densy.scriptify.api.script.ScriptObject;
import org.densy.scriptify.api.script.module.export.access.ScriptAccess;
import org.densy.scriptify.js.rhino.script.access.RestrictedNativeJavaClass;
import org.densy.scriptify.js.rhino.script.access.RestrictedNativeJavaObject;
import org.densy.scriptify.js.rhino.script.access.RhinoScriptAccessSupport;
import org.mozilla.javascript.Context;
import org.mozilla.javascript.Scriptable;
import org.mozilla.javascript.WrapFactory;

public class JsWrapFactory extends WrapFactory {

public JsWrapFactory() {
private final ScriptAccess scriptAccess;

public JsWrapFactory(ScriptAccess scriptAccess) {
this.scriptAccess = scriptAccess;
this.setJavaPrimitiveWrap(false);
}

@Override
public Object wrap(Context context, Scriptable scope, Object object, Class<?> staticType) {
// Convert the ScriptObject class to the value it contains
if (object instanceof ScriptObject scriptObject) {
return Context.javaToJS(scriptObject.getValue(), scope);
return super.wrap(context, scope, scriptObject.getValue(), staticType);
}
return super.wrap(context, scope, object, staticType);
}

@Override
public Scriptable wrapAsJavaObject(Context context, Scriptable scope, Object javaObject, Class<?> staticType) {
if (RhinoScriptAccessSupport.isExplicit(scriptAccess)) {
return new RestrictedNativeJavaObject(scope, javaObject, staticType);
}
return super.wrapAsJavaObject(context, scope, javaObject, staticType);
}

@Override
public Scriptable wrapJavaClass(Context context, Scriptable scope, Class<?> javaClass) {
if (RhinoScriptAccessSupport.isExplicit(scriptAccess)) {
return new RestrictedNativeJavaClass(scope, javaClass);
}
return super.wrapJavaClass(context, scope, javaClass);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
package org.densy.scriptify.js.rhino.script.access;

import org.mozilla.javascript.BaseFunction;
import org.mozilla.javascript.Context;
import org.mozilla.javascript.Scriptable;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

final class RestrictedJavaMethodFunction extends BaseFunction {

private final Object target;
private final Class<?> type;
private final String name;
private final boolean staticOnly;

RestrictedJavaMethodFunction(Scriptable scope, Object target, Class<?> type, String name, boolean staticOnly) {
this.target = target;
this.type = type;
this.name = name;
this.staticOnly = staticOnly;
this.setParentScope(scope);
this.setPrototype(getFunctionPrototype(scope));
}

@Override
public Object call(Context context, Scriptable scope, Scriptable thisObj, Object[] args) {
for (Method method : type.getMethods()) {
if (!isCandidate(method)) {
continue;
}

Object[] converted = RhinoScriptAccessSupport.convertArguments(
context,
args,
method.getParameterTypes(),
method.isVarArgs()
);
if (converted == null) {
continue;
}

try {
Object result = method.invoke(RhinoScriptAccessSupport.isStatic(method) ? null : target, converted);
return context.getWrapFactory().wrap(context, scope, result, method.getReturnType());
} catch (IllegalArgumentException ignored) {
// Try the next overload.
} catch (IllegalAccessException | InvocationTargetException e) {
throw Context.throwAsScriptRuntimeEx(e);
}
}

throw Context.reportRuntimeError("No exported method '" + name + "' matches provided arguments");
}

private boolean isCandidate(Method method) {
return method.getName().equals(name)
&& RhinoScriptAccessSupport.isExported(method)
&& (!staticOnly || RhinoScriptAccessSupport.isStatic(method))
&& (staticOnly || !RhinoScriptAccessSupport.isStatic(method));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
package org.densy.scriptify.js.rhino.script.access;

import org.mozilla.javascript.Context;
import org.mozilla.javascript.NativeJavaClass;
import org.mozilla.javascript.Scriptable;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;

public final class RestrictedNativeJavaClass extends NativeJavaClass {

public RestrictedNativeJavaClass(Scriptable scope, Class<?> javaClass) {
super(scope, javaClass);
}

@Override
public boolean has(String name, Scriptable start) {
Class<?> type = getClassObject();
return RhinoScriptAccessSupport.findExportedField(type, name, true) != null
|| RhinoScriptAccessSupport.hasExportedMethod(type, name, true);
}

@Override
public Object get(String name, Scriptable start) {
Class<?> type = getClassObject();
Field field = RhinoScriptAccessSupport.findExportedField(type, name, true);
if (field != null) {
try {
return Context.getCurrentContext().getWrapFactory().wrap(
Context.getCurrentContext(),
getParentScope(),
field.get(null),
field.getType()
);
} catch (IllegalAccessException e) {
throw Context.throwAsScriptRuntimeEx(e);
}
}

if (RhinoScriptAccessSupport.hasExportedMethod(type, name, true)) {
return new RestrictedJavaMethodFunction(getParentScope(), null, type, name, true);
}

return Scriptable.NOT_FOUND;
}

@Override
public void put(String name, Scriptable start, Object value) {
Field field = RhinoScriptAccessSupport.findExportedField(getClassObject(), name, true);
if (field == null) {
throw Context.reportRuntimeError("Java static member is not exported: " + name);
}
try {
field.set(null, Context.jsToJava(value, field.getType()));
} catch (IllegalAccessException e) {
throw Context.throwAsScriptRuntimeEx(e);
}
}

@Override
public Object[] getIds() {
return RhinoScriptAccessSupport.getExportedClassMemberNames(getClassObject()).toArray();
}

@Override
public Scriptable construct(Context context, Scriptable scope, Object[] args) {
for (Constructor<?> constructor : RhinoScriptAccessSupport.getExportedConstructors(getClassObject())) {
Object[] converted = RhinoScriptAccessSupport.convertArguments(
context,
args,
constructor.getParameterTypes(),
constructor.isVarArgs()
);
if (converted == null) {
continue;
}

try {
Object instance = constructor.newInstance(converted);
return context.getWrapFactory().wrapNewObject(context, scope, instance);
} catch (IllegalArgumentException ignored) {
// Try the next overload.
} catch (InstantiationException | IllegalAccessException | InvocationTargetException e) {
throw Context.throwAsScriptRuntimeEx(e);
}
}

throw Context.reportRuntimeError("No exported constructor matches provided arguments for " + getClassObject().getName());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
package org.densy.scriptify.js.rhino.script.access;

import org.mozilla.javascript.Context;
import org.mozilla.javascript.NativeJavaObject;
import org.mozilla.javascript.Scriptable;

import java.lang.reflect.Field;

public final class RestrictedNativeJavaObject extends NativeJavaObject {

public RestrictedNativeJavaObject(Scriptable scope, Object javaObject, Class<?> staticType) {
super(scope, javaObject, staticType);
}

@Override
public boolean has(String name, Scriptable start) {
Class<?> type = getWrappedType();
return RhinoScriptAccessSupport.findExportedField(type, name, false) != null
|| RhinoScriptAccessSupport.hasExportedMethod(type, name, false);
}

@Override
public Object get(String name, Scriptable start) {
Class<?> type = getWrappedType();
Field field = RhinoScriptAccessSupport.findExportedField(type, name, false);
if (field != null) {
try {
Object value = field.get(unwrap());
return Context.getCurrentContext().getWrapFactory().wrap(
Context.getCurrentContext(),
getParentScope(),
value,
field.getType()
);
} catch (IllegalAccessException e) {
throw Context.throwAsScriptRuntimeEx(e);
}
}

if (RhinoScriptAccessSupport.hasExportedMethod(type, name, false)) {
return new RestrictedJavaMethodFunction(getParentScope(), unwrap(), type, name, false);
}

return Scriptable.NOT_FOUND;
}

@Override
public void put(String name, Scriptable start, Object value) {
Field field = RhinoScriptAccessSupport.findExportedField(getWrappedType(), name, false);
if (field == null) {
throw Context.reportRuntimeError("Java member is not exported: " + name);
}
try {
field.set(unwrap(), Context.jsToJava(value, field.getType()));
} catch (IllegalAccessException e) {
throw Context.throwAsScriptRuntimeEx(e);
}
}

@Override
public Object[] getIds() {
return RhinoScriptAccessSupport.getExportedInstanceMemberNames(getWrappedType()).toArray();
}

private Class<?> getWrappedType() {
Object wrapped = unwrap();
if (staticType != null && staticType != Object.class) {
return staticType;
}
return wrapped.getClass();
}
}
Loading
Loading