如何为r.js提供伪文件系统?

时间:2012-11-26 17:12:46

标签: java javascript requirejs sandbox rhino

好的,r.js可以在Rhino上运行。哪个好。

完成它需要做的事情。

在rhino上,它基本上使用java.io.Filejava.io.FileOutputStreamjava.io.FileInputStream来实现它需要做的文件系统修改。

(背景:我正致力于为基于Maven的Java / Javascript开发人员提供更好的开发体验。作为Maven,有传统的力量和自以为是的力量。你可以在jszip.org看到进展。)

所以我想做的是将磁盘上的结构显示为魔术作为虚拟文件系统。

所以在磁盘上我们会有这样的结构:

/
/module1/src/main/js/controllers/controller.js
/module2/src/main/js/models/model.js
/module3/src/main/js/views/view.js
/webapp/src/build/js/profile.js
/webapp/src/main/js/main.js
/webapp/src/main/webapp/index.html

/webapp/src/build/js/profile.js看起来应该是这样的:

({
    appDir: "src",
    baseUrl:".",
    dir: "target",
    optimize: "closure",
    modules:[
        {
            name:"main"
        }
    ]
})

这样

  • 当r.js要求new File("src/main.js")我会实际给它new File("/webapp/src/main/js/main.js")

  • 当它要求new File("profile.js")我会给它new File("/webapp/src/build/js/profile.js")

  • 当它要求new File("controllers/controller.js")我会给它new File("/module1/src/main/js/controllers/controller.js")

  • 当它要求new File("target")时,我会给它new File("/webapp/target/webapp-1.0-SNAPSHOT")

编写所需的三个模拟类没有问题,即用来代替java.io.Filejava.io.FileInputStreamjava.io.FileOutputStream

的模拟类

诸如this之类的一些问题的答案指向像ClassShutter这样的东西,我可以看到我可以这样使用:

        context.setClassShutter(new ClassShutter() {
            public boolean visibleToScripts(String fullClassName) {
                if (File.class.getName().equals(fullClassName)) return false;
                if (FileOutputStream.class.getName().equals(fullClassName)) return false;
                if (FileInputStream.class.getName().equals(fullClassName)) return false;
                return true;
            }
        });

隐藏原始实现。

问题是让Rhino解决沙盒等效问题......我一直在

TypeError: [JavaPackage java.io.File] is not a function, it is object.

即使我先前执行了java.io.File = org.jszip.rhino.SandboxFile我的沙盒实施,而不是现在缺少的java.io.File

我甚至可以考虑在编译之前在加载的r.js文件上使用搜索和替换...但我觉得必须有更好的方法。

有没有人有任何提示?

1 个答案:

答案 0 :(得分:2)

好的,经过多次实验,这似乎就是这样做的方法:

Scriptable scope = context.newObject(global);
scope.setPrototype(global);
scope.setParentScope(null);

NativeJavaTopPackage $packages = (NativeJavaTopPackage) global.get("Packages");
NativeJavaPackage $java = (NativeJavaPackage) $packages.get("java");
NativeJavaPackage $java_io = (NativeJavaPackage) $java.get("io");

ProxyNativeJavaPackage proxy$java = new ProxyNativeJavaPackage($java);
ProxyNativeJavaPackage proxy$java_io = new ProxyNativeJavaPackage($java_io);
proxy$java_io.put("File", scope, get(scope, "Packages." + PseudoFile.class.getName()));
proxy$java_io.put("FileInputStream", scope,
        get(scope, "Packages." + PseudoFileInputStream.class.getName()));
proxy$java_io.put("FileOutputStream", scope,
        get(scope, "Packages." + PseudoFileOutputStream.class.getName()));
proxy$java.put("io", scope, proxy$java_io);
scope.put("java", scope, proxy$java);

有一个辅助方法:

private static Object get(Scriptable scope, String name) {
    Scriptable cur = scope;
    for (String part : StringUtils.split(name, ".")) {
        Object next = cur.get(part, scope);
        if (next instanceof Scriptable) {
            cur = (Scriptable) next;
        } else {
            return null;
        }
    }
    return cur;
}

ProxyNativeJavaPackage就像

public class ProxyNativeJavaPackage extends ScriptableObject implements Serializable {
    static final long serialVersionUID = 1L;

    protected final NativeJavaPackage delegate;
    private final Map<String, Object> mutations = new HashMap<String, Object>();

    public ProxyNativeJavaPackage(NativeJavaPackage delegate) {
        delegate.getClass();
        this.delegate = delegate;
    }

    @Override
    public String getClassName() {
        return delegate.getClassName();
    }

    @Override
    public boolean has(String id, Scriptable start) {
        return mutations.containsKey(id) ? mutations.get(id) != null : delegate.has(id, start);
    }

    @Override
    public boolean has(int index, Scriptable start) {
        return delegate.has(index, start);
    }

    @Override
    public void put(String id, Scriptable start, Object value) {
        mutations.put(id, value);
    }

    @Override
    public void put(int index, Scriptable start, Object value) {
        delegate.put(index, start, value);
    }

    @Override
    public Object get(String id, Scriptable start) {
        if (mutations.containsKey(id)) {
            return mutations.get(id);
        }
        return delegate.get(id, start);
    }

    @Override
    public Object get(int index, Scriptable start) {
        return delegate.get(index, start);
    }

    @Override
    public Object getDefaultValue(Class<?> ignored) {
        return toString();
    }

    @Override
    public String toString() {
        return delegate.toString();
    }

    @Override
    public boolean equals(Object obj) {
        if (obj instanceof ProxyNativeJavaPackage) {
            ProxyNativeJavaPackage that = (ProxyNativeJavaPackage) obj;
            return delegate.equals(that.delegate) && mutations.equals(that.mutations);
        }
        return false;
    }

    @Override
    public int hashCode() {
        return delegate.hashCode();
    }
}

仍然保留原始类Packages.java.io.File等,但对于r.js要求,这已足够,其他人应该可以将此技巧扩展到一般情况。