运行存储在Java字符串

时间:2017-12-16 05:30:27

标签: java c java-native-interface

我在这里有一个相当有趣的场景。假设我有一段存储在Java String中的C代码。我需要在我自己的Java程序中运行这段代码。

案例1

class Main{
      public static void main(String[] args) {
           String cCode  = "printf(\"Hello World\n\");"
            // I need to run the cCode here.
            // We are allowed to  call a method with params.
       }
}

我认为我应该做的是。

  1. 在Main
  2. 中创建原生字段
  3. 将sCode写入文件
  4. 来自Java的Exceulte shell命令来编译c代码。
  5. 从java
  6. 调用本机方法

    案例2

    我正在考虑执行上述过程,因为如果源代码是预定义的,我知道如何使用JNI执行此操作。

     class Main{
    
          static {
            System.loadLibrary("Main"); // Load native library at runtime
          }
    
          private native void sayHello();
    
          public static void main(String[] args) {
            new Main().sayHello();
          }
    
        }
    

    在预先编写的C代码的情况下。我们做的是。

    1. 使用javac Main.java
    2. 编译java类
    3. 为C lib生成标头。 javah -jni Main
    4. 通过编写C代码
    5. 完成Main.c
    6. 使用gcc -share -I/path/to/jni -I/path/to/jni_md -o Main.so
    7. 编译C代码
    8. 运行Main。 java Main
    9. 有人能告诉我,我是否采取了正确的路径(案例1 )?或者有更好的方法吗?

      **注意:这里的关键点是,我只允许编译一次java代码。 (开头)。**

      编辑:在检查了@Dúthomhas的评论和回答后,我想我应该再解释一下。我为机器学习项目做这个的原因。已经确定数值计算部分具有瓶颈并且使用C作为上述方法值得尝试它的风险。所以安全性现在已经不在了。我们只需将此作为实验。

2 个答案:

答案 0 :(得分:3)

非答案答案:不要这样做。

你要求做的是一个非常糟糕的主意,有几个原因,两个主要原因是:

  • 可能会打开(严重)安全漏洞
  • 实施成本可能超过收益

要求嵌入完全不同的语言意味着添加和链接库和大量代码以同步库,以及执行静态分析和沙箱代码的代码。换句话说,您要求在已有的语言之上实现整个语言。

可以认为C无论如何都是基本系统,甚至可以实现JVM(通常是),但这不是重点。问题不在于C库,它是 C编译器/解释器,就简单的解释编程语言而言,它是一个相当复杂的代码库。

建议:使用Java

ToolProvider类专门为您提供Java代码的动态编译。看看它。

确保使用SecurityManager类正确地沙箱化代码。 (并且,如果可能的话,在一个单独的受限制的JVM中运行它。)

建议:使用JavaScript / ECMAScript

ScriptEngine类正是为此而设计的。再次,谷歌周围的例子。而且,再次,不要忘记安全。

建议:使用现有库

但我真的想/必须使用C

唉。 可以使用C,但只是非常困难。 Google围绕“嵌入式c解释器”提供小型C语言解释器,您可能能够集成到您的源中。 (祝你好运!)

答案 1 :(得分:1)

让我澄清你的两个案例

案例1 是指在运行过程中运行C 程序。这不是大多数人认为的"从java" 调用本机方法。您无法在Main中创建本机字段。实际上,Java没有原生字段,只有原生方法。但是在案例1 中,您也不会使用本机方法。您可以编译和运行任何用C语言或任何其他语言编写的程序(前提是运行时环境中有编译器)。为此,您使用shell命令(Runtime.exec())来编译程序并运行它。 C程序的参数可以在命令行上传递'如Runtime.exec()中所述。它可以与您选择的任何IPC的Java进程通信。通常,我们只是使用管道来读取子进程的标准输出。

案例2 使用JNI(Java Native Interface)在进程中运行本机代码。您在此处描述的流程是正确的,但您可以修改它以使用在运行时构建的共享库。首先,删除本机方法,并将loadLibrary()删除到单独的类。确保只有在Main类运行gcc -o libMain.so命令(使用与案例1 中相同的Runtime.exec())后,类加载器才会加载此类。这样,静态构造函数将加载新构建的库。

Cases 中,您不需要重新编译Java。您不需要运行 javah 来构建libMain.so - 这只是一个方便的步骤,以保证C标头与Java类同步。但在您的情况下,Java类不会更改 - 因此本机方法签名也不会更改。

请注意案例1 更容易,尤其是如果您必须运行不同的已编译C'字符串',但案例2 可以提供更好的性能,如果你必须多次调用本机方法。