1

好的,所以r.js可以在Rhino上运行。这是伟大的。

去做它需要做的事情。

在 rhino 上,它基本上使用java.io.File,java.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.File,java.io.FileInputStreamjava.io.FileOutputStream,的类

诸如此类的一些问题答案指向诸如 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.SandboxFilemap 我的沙盒实现而不是现在丢失的java.io.File

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

有没有人有任何提示?

4

1 回答 1

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.Fileetc,但对于r.js要求这已经足够了,其他人应该可以将此技巧扩展到一般情况。

于 2012-11-27T09:07:05.333 回答