diff --git a/app/src/androidTest/java/com/stardust/scriptdroid/statics/RhinoE4XTest.java b/app/src/androidTest/java/com/stardust/scriptdroid/statics/RhinoE4XTest.java new file mode 100644 index 00000000..c9fe4f69 --- /dev/null +++ b/app/src/androidTest/java/com/stardust/scriptdroid/statics/RhinoE4XTest.java @@ -0,0 +1,21 @@ +package com.stardust.scriptdroid.statics; + +import org.junit.Test; +import org.mozilla.javascript.Context; +import org.mozilla.javascript.Scriptable; + +/** + * Created by Stardust on 2017/5/13. + */ + +public class RhinoE4XTest { + + @Test + public void testAttributeName() { + Context context = Context.enter(); + Scriptable scriptable = context.initStandardObjects(); + context.setOptimizationLevel(-1); + Object o = context.evaluateString(scriptable, "XML.ignoreProcessingInstructions = true; ().attributes()[0].name()", "", 1, null); + System.out.println(o); + } +} diff --git a/app/src/main/java/com/stardust/scriptdroid/App.java b/app/src/main/java/com/stardust/scriptdroid/App.java index 2c177113..c8f855d4 100644 --- a/app/src/main/java/com/stardust/scriptdroid/App.java +++ b/app/src/main/java/com/stardust/scriptdroid/App.java @@ -27,6 +27,9 @@ import com.stardust.theme.ThemeColor; import com.stardust.theme.ThemeColorManager; import com.stardust.util.ScreenMetrics; +import org.mozilla.javascript.Context; +import org.mozilla.javascript.Scriptable; + import java.lang.ref.WeakReference; /** diff --git a/app/src/main/java/com/stardust/scriptdroid/Pref.java b/app/src/main/java/com/stardust/scriptdroid/Pref.java index 20bd6bd7..2ef5dd91 100644 --- a/app/src/main/java/com/stardust/scriptdroid/Pref.java +++ b/app/src/main/java/com/stardust/scriptdroid/Pref.java @@ -17,6 +17,7 @@ import org.mozilla.javascript.tools.debugger.GuiCallback; public class Pref { private static final SharedPreferences DISPOSABLE_BOOLEAN = App.getApp().getSharedPreferences("DISPOSABLE_BOOLEAN", Context.MODE_PRIVATE); + private static final String KEY_SERVER_ADDRESS = "Still love you...17.5.14"; private static SharedPreferences.OnSharedPreferenceChangeListener onSharedPreferenceChangeListener = new SharedPreferences.OnSharedPreferenceChangeListener() { @Override public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) { @@ -116,4 +117,11 @@ public class Pref { return getDisposableBoolean("Still Love Eating 17.4.6", true); } + public static String getServerAddressOrDefault(String defaultAddress) { + return def().getString(KEY_SERVER_ADDRESS, defaultAddress); + } + + public static void saveServerAddress(String address) { + def().edit().putString(KEY_SERVER_ADDRESS, address).apply(); + } } diff --git a/app/src/main/java/com/stardust/scriptdroid/external/CommonUtils.java b/app/src/main/java/com/stardust/scriptdroid/external/CommonUtils.java index 71c06d4e..e128525f 100644 --- a/app/src/main/java/com/stardust/scriptdroid/external/CommonUtils.java +++ b/app/src/main/java/com/stardust/scriptdroid/external/CommonUtils.java @@ -1,7 +1,18 @@ package com.stardust.scriptdroid.external; +import android.content.Context; +import android.content.Intent; import android.os.Bundle; +import com.stardust.autojs.script.FileScriptSource; +import com.stardust.autojs.script.ScriptSource; +import com.stardust.autojs.script.SequenceScriptSource; +import com.stardust.autojs.script.StringScriptSource; +import com.stardust.scriptdroid.script.PathChecker; +import com.stardust.scriptdroid.script.Scripts; + +import java.io.File; + /** * Created by Stardust on 2017/4/1. */ @@ -16,4 +27,33 @@ public class CommonUtils { return bundle.containsKey(CommonUtils.EXTRA_KEY_PATH) || bundle.containsKey(EXTRA_KEY_PRE_EXECUTE_SCRIPT); } + public static void handleIntent(Context context, Intent intent) { + String path = getPath(intent); + String directoryPath = null; + String script = intent.getStringExtra(CommonUtils.EXTRA_KEY_PRE_EXECUTE_SCRIPT); + ScriptSource source = null; + if (path == null && script != null) { + source = new StringScriptSource(script); + } else if (path != null && new PathChecker(context).checkAndToastError(path)) { + ScriptSource fileScriptSource = new FileScriptSource(path); + if (script != null) { + source = new SequenceScriptSource(fileScriptSource.getName(), new StringScriptSource(script), fileScriptSource); + } else { + source = fileScriptSource; + } + directoryPath = new File(path).getParent(); + } + if (source != null) { + if (directoryPath == null) + Scripts.run(source); + else + Scripts.run(source, directoryPath); + } + } + + private static String getPath(Intent intent) { + if (intent.getData() != null && intent.getData().getPath() != null) + return intent.getData().getPath(); + return intent.getStringExtra(CommonUtils.EXTRA_KEY_PATH); + } } diff --git a/app/src/main/java/com/stardust/scriptdroid/external/open/RunIntentActivity.java b/app/src/main/java/com/stardust/scriptdroid/external/open/RunIntentActivity.java index 703a9222..3190786f 100644 --- a/app/src/main/java/com/stardust/scriptdroid/external/open/RunIntentActivity.java +++ b/app/src/main/java/com/stardust/scriptdroid/external/open/RunIntentActivity.java @@ -27,41 +27,11 @@ public class RunIntentActivity extends Activity { protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); try { - handleIntent(getIntent()); + CommonUtils.handleIntent(this, getIntent()); } catch (Exception e) { e.printStackTrace(); Toast.makeText(this, R.string.edit_and_run_handle_intent_error, Toast.LENGTH_LONG).show(); } finish(); } - - private void handleIntent(Intent intent) { - String path = getPath(intent); - String directoryPath = null; - String script = intent.getStringExtra(CommonUtils.EXTRA_KEY_PRE_EXECUTE_SCRIPT); - ScriptSource source = null; - if (path == null && script != null) { - source = new StringScriptSource(script); - } else if (path != null && new PathChecker(this).checkAndToastError(path)) { - ScriptSource fileScriptSource = new FileScriptSource(path); - if (script != null) { - source = new SequenceScriptSource(fileScriptSource.getName(), new StringScriptSource(script), fileScriptSource); - } else { - source = fileScriptSource; - } - directoryPath = new File(path).getParent(); - } - if (source != null) { - if (directoryPath == null) - Scripts.run(source); - else - Scripts.run(source, directoryPath); - } - } - - private String getPath(Intent intent) { - if (intent.getData() != null && intent.getData().getPath() != null) - return intent.getData().getPath(); - return intent.getStringExtra(CommonUtils.EXTRA_KEY_PATH); - } } diff --git a/app/src/main/java/com/stardust/scriptdroid/external/tasker/FireSettingReceiver.java b/app/src/main/java/com/stardust/scriptdroid/external/tasker/FireSettingReceiver.java index 5f276a43..70be8ea1 100644 --- a/app/src/main/java/com/stardust/scriptdroid/external/tasker/FireSettingReceiver.java +++ b/app/src/main/java/com/stardust/scriptdroid/external/tasker/FireSettingReceiver.java @@ -30,7 +30,7 @@ public class FireSettingReceiver extends AbstractPluginSettingReceiver { @Override protected void firePluginSetting(@NonNull Context context, @NonNull Bundle bundle) { - context.startActivity(new Intent(App.getApp(), RunIntentActivity.class) + CommonUtils.handleIntent(context, new Intent(App.getApp(), RunIntentActivity.class) .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) .putExtra(CommonUtils.EXTRA_KEY_PATH, bundle.getString(CommonUtils.EXTRA_KEY_PATH)) .putExtra(CommonUtils.EXTRA_KEY_PRE_EXECUTE_SCRIPT, bundle.getString(CommonUtils.EXTRA_KEY_PRE_EXECUTE_SCRIPT))); diff --git a/app/src/main/java/com/stardust/scriptdroid/script/PathChecker.java b/app/src/main/java/com/stardust/scriptdroid/script/PathChecker.java index 891fd32a..951508a0 100644 --- a/app/src/main/java/com/stardust/scriptdroid/script/PathChecker.java +++ b/app/src/main/java/com/stardust/scriptdroid/script/PathChecker.java @@ -1,6 +1,7 @@ package com.stardust.scriptdroid.script; import android.app.Activity; +import android.content.Context; import android.os.Build; import android.text.TextUtils; import android.widget.Toast; @@ -17,10 +18,10 @@ import static android.content.pm.PackageManager.PERMISSION_GRANTED; public class PathChecker { public static final int CHECK_RESULT_OK = 0; - private Activity mActivity; + private Context mContext; - public PathChecker(Activity activity) { - mActivity = activity; + public PathChecker(Context context) { + mContext = context; } @@ -35,14 +36,14 @@ public class PathChecker { public boolean checkAndToastError(String path) { int result = checkWithStoragePermission(path); if (result != CHECK_RESULT_OK) { - Toast.makeText(mActivity, result, Toast.LENGTH_SHORT).show(); + Toast.makeText(mContext, mContext.getString(result) + ":" + path, Toast.LENGTH_SHORT).show(); return false; } return true; } private int checkWithStoragePermission(String path) { - if (!hasStorageReadPermission(mActivity)) { + if (mContext instanceof Activity && !hasStorageReadPermission((Activity) mContext)) { return com.stardust.autojs.R.string.text_no_file_rw_permission; } return check(path); diff --git a/app/src/main/java/com/stardust/scriptdroid/sublime_plugin_client/SublimePluginClient.java b/app/src/main/java/com/stardust/scriptdroid/sublime_plugin_client/SublimePluginClient.java index e0123a86..cdcbcbba 100644 --- a/app/src/main/java/com/stardust/scriptdroid/sublime_plugin_client/SublimePluginClient.java +++ b/app/src/main/java/com/stardust/scriptdroid/sublime_plugin_client/SublimePluginClient.java @@ -9,8 +9,10 @@ import com.stardust.util.UiHandler; import org.greenrobot.eventbus.EventBus; +import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; +import java.io.InputStreamReader; import java.io.OutputStream; import java.net.Socket; import java.util.concurrent.Executor; @@ -117,15 +119,14 @@ public class SublimePluginClient { } private void startReadLoop(InputStream stream) throws IOException { - byte[] buffer = new byte[8192]; + BufferedReader reader = new BufferedReader(new InputStreamReader(stream)); while (!Thread.currentThread().isInterrupted()) { - int len = stream.read(buffer); - if (len <= 0) { + String line = reader.readLine(); + if (line == null) { return; } if (mResponseHandler != null) { - String str = new String(buffer, 0, len); - JsonElement jsonElement = new JsonParser().parse(str); + JsonElement jsonElement = new JsonParser().parse(line); JsonObject jsonObject = jsonElement.getAsJsonObject(); mResponseHandler.handle(jsonObject); } diff --git a/app/src/main/java/com/stardust/scriptdroid/sublime_plugin_client/SublimePluginClientManager.java b/app/src/main/java/com/stardust/scriptdroid/sublime_plugin_client/SublimePluginService.java similarity index 96% rename from app/src/main/java/com/stardust/scriptdroid/sublime_plugin_client/SublimePluginClientManager.java rename to app/src/main/java/com/stardust/scriptdroid/sublime_plugin_client/SublimePluginService.java index bb3df54a..3c0d69fe 100644 --- a/app/src/main/java/com/stardust/scriptdroid/sublime_plugin_client/SublimePluginClientManager.java +++ b/app/src/main/java/com/stardust/scriptdroid/sublime_plugin_client/SublimePluginService.java @@ -9,7 +9,7 @@ import java.io.IOException; * Created by Stardust on 2017/5/11. */ -public class SublimePluginClientManager { +public class SublimePluginService { private static SublimePluginClient client; diff --git a/app/src/main/java/com/stardust/scriptdroid/ui/console/JraskaConsole.java b/app/src/main/java/com/stardust/scriptdroid/ui/console/JraskaConsole.java index 8fc2f4bd..af381050 100644 --- a/app/src/main/java/com/stardust/scriptdroid/ui/console/JraskaConsole.java +++ b/app/src/main/java/com/stardust/scriptdroid/ui/console/JraskaConsole.java @@ -10,7 +10,7 @@ import android.util.SparseArray; import com.jraska.console.Console; import com.stardust.autojs.runtime.api.AbstractConsole; import com.stardust.scriptdroid.App; -import com.stardust.scriptdroid.sublime_plugin_client.SublimePluginClientManager; +import com.stardust.scriptdroid.sublime_plugin_client.SublimePluginService; import com.stardust.util.SparseArrayEntries; import com.stardust.util.TextUtils; @@ -49,14 +49,14 @@ public class JraskaConsole extends AbstractConsole { public void println(int level, CharSequence charSequence) { Console.write(getLevelSpannable(level, getTag(level))); Console.writeLine(getLevelSpannable(level, charSequence)); - SublimePluginClientManager.log(getTag(level) + charSequence.toString()); + SublimePluginService.log(getTag(level) + charSequence.toString()); } @Override public void write(int level, CharSequence data) { Console.write(getLevelSpannable(level, getTag(level))); Console.write(getLevelSpannable(level, data)); - SublimePluginClientManager.log(getTag(level) + data.toString()); + SublimePluginService.log(getTag(level) + data.toString()); } private CharSequence getTag(int level) { diff --git a/app/src/main/java/com/stardust/scriptdroid/ui/main/MainActivity.java b/app/src/main/java/com/stardust/scriptdroid/ui/main/MainActivity.java index dd88e0be..26863cc3 100644 --- a/app/src/main/java/com/stardust/scriptdroid/ui/main/MainActivity.java +++ b/app/src/main/java/com/stardust/scriptdroid/ui/main/MainActivity.java @@ -16,7 +16,6 @@ import android.support.v4.view.ViewPager; import android.support.v4.widget.DrawerLayout; import android.support.v7.app.ActionBarDrawerToggle; import android.support.v7.widget.Toolbar; -import android.util.Log; import android.view.Gravity; import android.view.View; import android.widget.ImageView; @@ -25,7 +24,6 @@ import android.widget.Toast; import com.afollestad.materialdialogs.DialogAction; import com.afollestad.materialdialogs.MaterialDialog; -import com.google.gson.JsonObject; import com.stardust.app.FragmentPagerAdapterBuilder; import com.stardust.app.NotAskAgainDialog; import com.stardust.app.OnActivityResultDelegate; @@ -38,8 +36,6 @@ import com.stardust.scriptdroid.script.ScriptFile; import com.stardust.scriptdroid.script.StorageScriptProvider; import com.stardust.scriptdroid.script.sample.Sample; import com.stardust.scriptdroid.service.AccessibilityWatchDogService; -import com.stardust.scriptdroid.sublime_plugin_client.SublimePluginClient; -import com.stardust.scriptdroid.sublime_plugin_client.SublimeResponseHandler; import com.stardust.scriptdroid.tool.AccessibilityServiceTool; import com.stardust.scriptdroid.tool.DrawableSaver; import com.stardust.scriptdroid.ui.BaseActivity; @@ -148,7 +144,7 @@ public class MainActivity extends BaseActivity { } private void setUpFragment() { - SlideMenuFragment.setFragment(this, R.id.fragment_slide_menu); + SideMenuFragment.setFragment(this, R.id.fragment_slide_menu); } @SuppressLint("SetTextI18n") diff --git a/app/src/main/java/com/stardust/scriptdroid/ui/main/SlideMenuFragment.java b/app/src/main/java/com/stardust/scriptdroid/ui/main/SideMenuFragment.java similarity index 91% rename from app/src/main/java/com/stardust/scriptdroid/ui/main/SlideMenuFragment.java rename to app/src/main/java/com/stardust/scriptdroid/ui/main/SideMenuFragment.java index e66a47a0..59d681f6 100644 --- a/app/src/main/java/com/stardust/scriptdroid/ui/main/SlideMenuFragment.java +++ b/app/src/main/java/com/stardust/scriptdroid/ui/main/SideMenuFragment.java @@ -15,13 +15,14 @@ import android.widget.Toast; import com.afollestad.materialdialogs.DialogAction; import com.afollestad.materialdialogs.MaterialDialog; import com.stardust.app.Fragment; +import com.stardust.scriptdroid.Pref; import com.stardust.scriptdroid.R; import com.stardust.scriptdroid.autojs.AutoJs; import com.stardust.scriptdroid.external.floating_window.FloatingWindowManger; import com.stardust.scriptdroid.external.floating_window.menu.HoverMenuService; import com.stardust.scriptdroid.service.AccessibilityWatchDogService; import com.stardust.scriptdroid.sublime_plugin_client.SublimePluginClient; -import com.stardust.scriptdroid.sublime_plugin_client.SublimePluginClientManager; +import com.stardust.scriptdroid.sublime_plugin_client.SublimePluginService; import com.stardust.scriptdroid.tool.AccessibilityServiceTool; import com.stardust.scriptdroid.tool.WifiTool; import com.stardust.scriptdroid.ui.console.LogActivity; @@ -42,11 +43,11 @@ import java.util.concurrent.Executor; * Created by Stardust on 2017/1/30. */ -public class SlideMenuFragment extends Fragment { +public class SideMenuFragment extends Fragment { public static void setFragment(FragmentActivity activity, int viewId) { - SlideMenuFragment fragment = new SlideMenuFragment(); + SideMenuFragment fragment = new SideMenuFragment(); activity.getSupportFragmentManager().beginTransaction().replace(viewId, fragment).commit(); } @@ -153,13 +154,14 @@ public class SlideMenuFragment extends Fragment { @ViewBinding.Check(R.id.sw_debug) private void setDebugEnabled(boolean enabled) { - if (enabled && !SublimePluginClientManager.isConnected()) { + if (enabled && !SublimePluginService.isConnected()) { new MaterialDialog.Builder(getActivity()) - .title("服务器地址") - .input("", WifiTool.getWifiAddress(getActivity()), new MaterialDialog.InputCallback() { + .title(R.string.text_server_address) + .input("", getServerAddress(), new MaterialDialog.InputCallback() { @Override public void onInput(@NonNull MaterialDialog dialog, CharSequence input) { - SublimePluginClientManager.connect(input.toString()); + Pref.saveServerAddress(input.toString()); + SublimePluginService.connect(input.toString()); } }) .neutralText(R.string.text_help) @@ -171,10 +173,14 @@ public class SlideMenuFragment extends Fragment { }) .show(); } else if (!enabled) { - SublimePluginClientManager.disconnectIfNeeded(); + SublimePluginService.disconnectIfNeeded(); } } + private CharSequence getServerAddress() { + return Pref.getServerAddressOrDefault(WifiTool.getWifiAddress(getActivity())); + } + @ViewBinding.Click(R.id.stop_all_running_scripts) private void stopAllRunningScripts() { int n = AutoJs.getInstance().getScriptEngineService().stopAll(); diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 531a16d4..12a97a26 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -181,6 +181,7 @@ 连接电脑 已连接 已断开连接 + 服务器地址 diff --git a/app/src/test/java/com/stardust/scriptdroid/ExampleUnitTest.java b/app/src/test/java/com/stardust/scriptdroid/ExampleUnitTest.java index 565d43d1..54bd1bdd 100644 --- a/app/src/test/java/com/stardust/scriptdroid/ExampleUnitTest.java +++ b/app/src/test/java/com/stardust/scriptdroid/ExampleUnitTest.java @@ -4,6 +4,21 @@ import com.jecelyin.common.http.Base64; import com.stardust.util.LimitedHashMap; import org.junit.Test; +import org.mozilla.javascript.Context; +import org.mozilla.javascript.Scriptable; +import org.mozilla.javascript.tools.shell.ShellContextFactory; +import org.mozilla.javascript.xml.XMLLib; + +import java.io.BufferedWriter; +import java.io.IOException; +import java.io.OutputStream; +import java.io.OutputStreamWriter; +import java.io.PipedInputStream; +import java.io.PipedOutputStream; +import java.io.PipedReader; +import java.io.PipedWriter; +import java.io.Writer; +import java.nio.channels.Pipe; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; @@ -35,29 +50,30 @@ public class ExampleUnitTest { @Test public void test() { - LimitedHashMap hashMap = new LimitedHashMap<>(5); - hashMap.put("a", 1); - hashMap.put("b", 1); - hashMap.put("c", 1); - hashMap.put("d", 1); - hashMap.put("e", 1); - hashMap.get("a"); - hashMap.put("f", 1); - assertFalse(hashMap.containsKey("a")); + PipedInputStream inputStream = new PipedInputStream(1024); + try { + System.setIn(inputStream); + OutputStream outputStream = new PipedOutputStream(inputStream); + outputStream.write("().attributes()[0].name()\n".getBytes()); + org.mozilla.javascript.tools.shell.Main.exec(new String[]{}); + inputStream.close(); + outputStream.close(); + } catch (IOException e) { + e.printStackTrace(); + } + org.mozilla.javascript.xmlimpl.XMLLibImpl + + } @Test public void testAutoReorder() { - LimitedHashMap hashMap = new LimitedHashMap<>(5); - hashMap.put("a", 1); - hashMap.put("b", 2); - hashMap.put("c", 3); - hashMap.put("d", 4); - hashMap.put("e", 5); - hashMap.get("a"); - hashMap.put("f", 6); - assertTrue(hashMap.containsKey("a")); - assertFalse(hashMap.containsKey("b")); + Context context = Context.enter(); + Scriptable scriptable = context.initStandardObjects(); + context.setOptimizationLevel(-1); + Object o = context.evaluateString(scriptable, " ().attributes()[0].name()", "", 1, null); + System.out.println(o); + Context.exit(); } diff --git a/app/src/test/java/com/stardust/scriptdroid/Main.java b/app/src/test/java/com/stardust/scriptdroid/Main.java new file mode 100644 index 00000000..c37a0ac3 --- /dev/null +++ b/app/src/test/java/com/stardust/scriptdroid/Main.java @@ -0,0 +1,735 @@ +package com.stardust.scriptdroid; + +import org.mozilla.javascript.Context; +import org.mozilla.javascript.ContextAction; +import org.mozilla.javascript.Function; +import org.mozilla.javascript.GeneratedClassLoader; +import org.mozilla.javascript.Kit; +import org.mozilla.javascript.NativeArray; +import org.mozilla.javascript.RhinoException; +import org.mozilla.javascript.Script; +import org.mozilla.javascript.Scriptable; +import org.mozilla.javascript.ScriptableObject; +import org.mozilla.javascript.SecurityController; +import org.mozilla.javascript.commonjs.module.ModuleScope; +import org.mozilla.javascript.commonjs.module.Require; +import org.mozilla.javascript.tools.SourceReader; +import org.mozilla.javascript.tools.ToolErrorReporter; +import org.mozilla.javascript.tools.shell.Global; +import org.mozilla.javascript.tools.shell.QuitAction; +import org.mozilla.javascript.tools.shell.SecurityProxy; +import org.mozilla.javascript.tools.shell.ShellConsole; +import org.mozilla.javascript.tools.shell.ShellContextFactory; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; +import java.io.PrintStream; +import java.io.UnsupportedEncodingException; +import java.lang.ref.ReferenceQueue; +import java.lang.ref.SoftReference; +import java.net.URI; +import java.net.URISyntaxException; +import java.nio.charset.Charset; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +/** + * The shell program. + *

+ * Can execute scripts interactively or in batch mode at the command line. + * An example of controlling the JavaScript engine. + * + * @author Norris Boyd + */ +public class Main { + public static ShellContextFactory + shellContextFactory = new ShellContextFactory(); + + public static Global global = new Global(); + static protected ToolErrorReporter errorReporter; + static protected int exitCode = 0; + static private final int EXITCODE_RUNTIME_ERROR = 3; + static private final int EXITCODE_FILE_NOT_FOUND = 4; + static boolean processStdin = true; + static List fileList = new ArrayList(); + static List modulePath; + static String mainModule; + static boolean sandboxed = false; + static boolean useRequire = false; + static Require require; + private static SecurityProxy securityImpl; + private final static ScriptCache scriptCache = new ScriptCache(32); + + static { + global.initQuitAction(new IProxy(IProxy.SYSTEM_EXIT)); + } + + /** + * Proxy class to avoid proliferation of anonymous classes. + */ + private static class IProxy implements ContextAction, QuitAction { + private static final int PROCESS_FILES = 1; + private static final int EVAL_INLINE_SCRIPT = 2; + private static final int SYSTEM_EXIT = 3; + + private int type; + String[] args; + String scriptText; + + IProxy(int type) { + this.type = type; + } + + public Object run(Context cx) { + if (useRequire) { + require = global.installRequire(cx, modulePath, sandboxed); + } + if (type == PROCESS_FILES) { + processFiles(cx, args); + } else if (type == EVAL_INLINE_SCRIPT) { + evalInlineScript(cx, scriptText); + } else { + throw Kit.codeBug(); + } + return null; + } + + public void quit(Context cx, int exitCode) { + if (type == SYSTEM_EXIT) { + System.exit(exitCode); + return; + } + throw Kit.codeBug(); + } + } + + /** + * Main entry point. + *

+ * Process arguments as would a normal Java program. Also + * create a new Context and associate it with the current thread. + * Then set up the execution environment and begin to + * execute scripts. + */ + public static void main(String args[]) { + try { + if (Boolean.getBoolean("rhino.use_java_policy_security")) { + initJavaPolicySecuritySupport(); + } + } catch (SecurityException ex) { + ex.printStackTrace(System.err); + } + + int result = exec(args); + if (result != 0) { + System.exit(result); + } + } + + /** + * Execute the given arguments, but don't System.exit at the end. + */ + public static int exec(String origArgs[]) { + errorReporter = new ToolErrorReporter(false, global.getErr()); + shellContextFactory.setErrorReporter(errorReporter); + String[] args = processOptions(origArgs); + if (exitCode > 0) { + return exitCode; + } + if (processStdin) { + fileList.add(null); + } + if (true) { + global.init(shellContextFactory); + } + IProxy iproxy = new IProxy(IProxy.PROCESS_FILES); + iproxy.args = args; + shellContextFactory.call(iproxy); + + return exitCode; + } + + static void processFiles(Context cx, String[] args) { + // define "arguments" array in the top-level object: + // need to allocate new array since newArray requires instances + // of exactly Object[], not ObjectSubclass[] + Object[] array = new Object[args.length]; + System.arraycopy(args, 0, array, 0, args.length); + Scriptable argsObj = cx.newArray(global, array); + global.defineProperty("arguments", argsObj, + ScriptableObject.DONTENUM); + + for (String file : fileList) { + try { + processSource(cx, file); + } catch (IOException ioex) { + Context.reportError(ToolErrorReporter.getMessage( + "msg.couldnt.read.source", file, ioex.getMessage())); + exitCode = EXITCODE_FILE_NOT_FOUND; + } catch (RhinoException rex) { + ToolErrorReporter.reportException( + cx.getErrorReporter(), rex); + exitCode = EXITCODE_RUNTIME_ERROR; + } catch (VirtualMachineError ex) { + // Treat StackOverflow and OutOfMemory as runtime errors + ex.printStackTrace(); + String msg = ToolErrorReporter.getMessage( + "msg.uncaughtJSException", ex.toString()); + Context.reportError(msg); + exitCode = EXITCODE_RUNTIME_ERROR; + } + } + } + + static void evalInlineScript(Context cx, String scriptText) { + try { + Script script = cx.compileString(scriptText, "", 1, null); + if (script != null) { + script.exec(cx, getShellScope()); + } + } catch (RhinoException rex) { + ToolErrorReporter.reportException( + cx.getErrorReporter(), rex); + exitCode = EXITCODE_RUNTIME_ERROR; + } catch (VirtualMachineError ex) { + // Treat StackOverflow and OutOfMemory as runtime errors + ex.printStackTrace(); + String msg = ToolErrorReporter.getMessage( + "msg.uncaughtJSException", ex.toString()); + Context.reportError(msg); + exitCode = EXITCODE_RUNTIME_ERROR; + } + } + + public static Global getGlobal() { + return global; + } + + static Scriptable getShellScope() { + return getScope(null); + } + + static Scriptable getScope(String path) { + if (useRequire) { + // If CommonJS modules are enabled use a module scope that resolves + // relative ids relative to the current URL, file or working directory. + URI uri; + if (path == null) { + // use current directory for shell and -e switch + uri = new File(System.getProperty("user.dir")).toURI(); + } else { + // find out whether this is a file path or a URL + if (SourceReader.toUrl(path) != null) { + try { + uri = new URI(path); + } catch (URISyntaxException x) { + // fall back to file uri + uri = new File(path).toURI(); + } + } else { + uri = new File(path).toURI(); + } + } + return new ModuleScope(global, uri, null); + } else { + return global; + } + } + + /** + * Parse arguments. + */ + public static String[] processOptions(String args[]) { + String usageError; + goodUsage: + for (int i = 0; ; ++i) { + if (i == args.length) { + return new String[0]; + } + String arg = args[i]; + if (!arg.startsWith("-")) { + processStdin = false; + fileList.add(arg); + mainModule = arg; + String[] result = new String[args.length - i - 1]; + System.arraycopy(args, i + 1, result, 0, args.length - i - 1); + return result; + } + if (arg.equals("-version")) { + if (++i == args.length) { + usageError = arg; + break goodUsage; + } + int version; + try { + version = Integer.parseInt(args[i]); + } catch (NumberFormatException ex) { + usageError = args[i]; + break goodUsage; + } + if (!Context.isValidLanguageVersion(version)) { + usageError = args[i]; + break goodUsage; + } + shellContextFactory.setLanguageVersion(version); + continue; + } + if (arg.equals("-opt") || arg.equals("-O")) { + if (++i == args.length) { + usageError = arg; + break goodUsage; + } + int opt; + try { + opt = Integer.parseInt(args[i]); + } catch (NumberFormatException ex) { + usageError = args[i]; + break goodUsage; + } + if (opt == -2) { + // Compatibility with Cocoon Rhino fork + opt = -1; + } else if (!Context.isValidOptimizationLevel(opt)) { + usageError = args[i]; + break goodUsage; + } + shellContextFactory.setOptimizationLevel(opt); + continue; + } + if (arg.equals("-encoding")) { + if (++i == args.length) { + usageError = arg; + break goodUsage; + } + String enc = args[i]; + shellContextFactory.setCharacterEncoding(enc); + continue; + } + if (arg.equals("-strict")) { + shellContextFactory.setStrictMode(true); + shellContextFactory.setAllowReservedKeywords(false); + errorReporter.setIsReportingWarnings(true); + continue; + } + if (arg.equals("-fatal-warnings")) { + shellContextFactory.setWarningAsError(true); + continue; + } + if (arg.equals("-e")) { + processStdin = false; + if (++i == args.length) { + usageError = arg; + break goodUsage; + } + if (false) { + global.init(shellContextFactory); + } + IProxy iproxy = new IProxy(IProxy.EVAL_INLINE_SCRIPT); + iproxy.scriptText = args[i]; + shellContextFactory.call(iproxy); + continue; + } + if (arg.equals("-require")) { + useRequire = true; + continue; + } + if (arg.equals("-sandbox")) { + sandboxed = true; + useRequire = true; + continue; + } + if (arg.equals("-modules")) { + if (++i == args.length) { + usageError = arg; + break goodUsage; + } + if (modulePath == null) { + modulePath = new ArrayList(); + } + modulePath.add(args[i]); + useRequire = true; + continue; + } + if (arg.equals("-w")) { + errorReporter.setIsReportingWarnings(true); + continue; + } + if (arg.equals("-f")) { + processStdin = false; + if (++i == args.length) { + usageError = arg; + break goodUsage; + } + if (args[i].equals("-")) { + fileList.add(null); + } else { + fileList.add(args[i]); + mainModule = args[i]; + } + continue; + } + if (arg.equals("-sealedlib")) { + global.setSealedStdLib(true); + continue; + } + if (arg.equals("-debug")) { + shellContextFactory.setGeneratingDebug(true); + continue; + } + if (arg.equals("-?") || + arg.equals("-help")) { + // print usage message + global.getOut().println( + ToolErrorReporter.getMessage("msg.shell.usage", Main.class.getName())); + exitCode = 1; + return null; + } + usageError = arg; + break goodUsage; + } + // print error and usage message + global.getOut().println( + ToolErrorReporter.getMessage("msg.shell.invalid", usageError)); + global.getOut().println( + ToolErrorReporter.getMessage("msg.shell.usage", Main.class.getName())); + exitCode = 1; + return null; + } + + private static void initJavaPolicySecuritySupport() { + Throwable exObj; + try { + Class cl = Class.forName + ("org.mozilla.javascript.tools.shell.JavaPolicySecurity"); + securityImpl = (SecurityProxy) cl.newInstance(); + SecurityController.initGlobal(securityImpl); + return; + } catch (ClassNotFoundException ex) { + exObj = ex; + } catch (IllegalAccessException ex) { + exObj = ex; + } catch (InstantiationException ex) { + exObj = ex; + } catch (LinkageError ex) { + exObj = ex; + } + throw Kit.initCause(new IllegalStateException( + "Can not load security support: " + exObj), exObj); + } + + /** + * Evaluate JavaScript source. + * + * @param cx the current context + * @param filename the name of the file to compile, or null + * for interactive mode. + * @throws IOException if the source could not be read + * @throws RhinoException thrown during evaluation of source + */ + public static void processSource(Context cx, String filename) + throws IOException { + if (filename == null || filename.equals("-")) { + Scriptable scope = getShellScope(); + Charset cs; + String charEnc = shellContextFactory.getCharacterEncoding(); + if (charEnc != null) { + cs = Charset.forName(charEnc); + } else { + cs = Charset.defaultCharset(); + } + ShellConsole console = global.getConsole(cs); + if (filename == null) { + // print implementation version + console.println(cx.getImplementationVersion()); + } + + int lineno = 1; + boolean hitEOF = false; + while (!hitEOF) { + String[] prompts = global.getPrompts(cx); + String prompt = null; + if (filename == null) + prompt = prompts[0]; + console.flush(); + String source = ""; + + // Collect lines of source to compile. + while (true) { + String newline; + try { + newline = console.readLine(prompt); + } catch (IOException ioe) { + console.println(ioe.toString()); + break; + } + if (newline == null) { + hitEOF = true; + break; + } + source = source + newline + "\n"; + lineno++; + if (cx.stringIsCompilableUnit(source)) + break; + prompt = prompts[1]; + } + try { + Script script = cx.compileString(source, "", lineno, null); + if (script != null) { + Object result = script.exec(cx, scope); + // Avoid printing out undefined or function definitions. + if (result != Context.getUndefinedValue() && + !(result instanceof Function && + source.trim().startsWith("function"))) { + try { + console.println(Context.toString(result)); + } catch (RhinoException rex) { + ToolErrorReporter.reportException( + cx.getErrorReporter(), rex); + } + } + // NativeArray h = global.history; + //h.put((int) h.getLength(), h, source); + } + } catch (RhinoException rex) { + ToolErrorReporter.reportException( + cx.getErrorReporter(), rex); + exitCode = EXITCODE_RUNTIME_ERROR; + } catch (VirtualMachineError ex) { + // Treat StackOverflow and OutOfMemory as runtime errors + ex.printStackTrace(); + String msg = ToolErrorReporter.getMessage( + "msg.uncaughtJSException", ex.toString()); + Context.reportError(msg); + exitCode = EXITCODE_RUNTIME_ERROR; + } + } + console.println(); + console.flush(); + } else if (useRequire && filename.equals(mainModule)) { + require.requireMain(cx, filename); + } else { + processFile(cx, getScope(filename), filename); + } + } + + public static void processFileNoThrow(Context cx, Scriptable scope, String filename) { + try { + processFile(cx, scope, filename); + } catch (IOException ioex) { + Context.reportError(ToolErrorReporter.getMessage( + "msg.couldnt.read.source", filename, ioex.getMessage())); + exitCode = EXITCODE_FILE_NOT_FOUND; + } catch (RhinoException rex) { + ToolErrorReporter.reportException( + cx.getErrorReporter(), rex); + exitCode = EXITCODE_RUNTIME_ERROR; + } catch (VirtualMachineError ex) { + // Treat StackOverflow and OutOfMemory as runtime errors + ex.printStackTrace(); + String msg = ToolErrorReporter.getMessage( + "msg.uncaughtJSException", ex.toString()); + Context.reportError(msg); + exitCode = EXITCODE_RUNTIME_ERROR; + } + } + + public static void processFile(Context cx, Scriptable scope, String filename) + throws IOException { + if (securityImpl == null) { + processFileSecure(cx, scope, filename, null); + } else { + //securityImpl.callProcessFileSecure(cx, scope, filename); + } + } + + static void processFileSecure(Context cx, Scriptable scope, + String path, Object securityDomain) + throws IOException { + + boolean isClass = path.endsWith(".class"); + Object source = readFileOrUrl(path, !isClass); + + byte[] digest = getDigest(source); + String key = path + "_" + cx.getOptimizationLevel(); + ScriptReference ref = scriptCache.get(key, digest); + Script script = ref != null ? ref.get() : null; + + if (script == null) { + if (isClass) { + script = loadCompiledScript(cx, path, (byte[]) source, securityDomain); + } else { + String strSrc = (String) source; + // Support the executable script #! syntax: If + // the first line begins with a '#', treat the whole + // line as a comment. + if (strSrc.length() > 0 && strSrc.charAt(0) == '#') { + for (int i = 1; i != strSrc.length(); ++i) { + int c = strSrc.charAt(i); + if (c == '\n' || c == '\r') { + strSrc = strSrc.substring(i); + break; + } + } + } + script = cx.compileString(strSrc, path, 1, securityDomain); + } + scriptCache.put(key, digest, script); + } + + if (script != null) { + script.exec(cx, scope); + } + } + + private static byte[] getDigest(Object source) { + byte[] bytes, digest = null; + + if (source != null) { + if (source instanceof String) { + try { + bytes = ((String) source).getBytes("UTF-8"); + } catch (UnsupportedEncodingException ue) { + bytes = ((String) source).getBytes(); + } + } else { + bytes = (byte[]) source; + } + try { + MessageDigest md = MessageDigest.getInstance("MD5"); + digest = md.digest(bytes); + } catch (NoSuchAlgorithmException nsa) { + // Should not happen + throw new RuntimeException(nsa); + } + } + + return digest; + } + + private static Script loadCompiledScript(Context cx, String path, + byte[] data, Object securityDomain) + throws FileNotFoundException { + if (data == null) { + throw new FileNotFoundException(path); + } + // XXX: For now extract class name of compiled Script from path + // instead of parsing class bytes + int nameStart = path.lastIndexOf('/'); + if (nameStart < 0) { + nameStart = 0; + } else { + ++nameStart; + } + int nameEnd = path.lastIndexOf('.'); + if (nameEnd < nameStart) { + // '.' does not exist in path (nameEnd < 0) + // or it comes before nameStart + nameEnd = path.length(); + } + String name = path.substring(nameStart, nameEnd); + try { + GeneratedClassLoader loader = SecurityController.createLoader(cx.getApplicationClassLoader(), securityDomain); + Class clazz = loader.defineClass(name, data); + loader.linkClass(clazz); + if (!Script.class.isAssignableFrom(clazz)) { + throw Context.reportRuntimeError("msg.must.implement.Script"); + } + return (Script) clazz.newInstance(); + } catch (IllegalAccessException iaex) { + Context.reportError(iaex.toString()); + throw new RuntimeException(iaex); + } catch (InstantiationException inex) { + Context.reportError(inex.toString()); + throw new RuntimeException(inex); + } + } + + public static InputStream getIn() { + return getGlobal().getIn(); + } + + public static void setIn(InputStream in) { + getGlobal().setIn(in); + } + + public static PrintStream getOut() { + return getGlobal().getOut(); + } + + public static void setOut(PrintStream out) { + getGlobal().setOut(out); + } + + public static PrintStream getErr() { + return getGlobal().getErr(); + } + + public static void setErr(PrintStream err) { + getGlobal().setErr(err); + } + + /** + * Read file or url specified by path. + * + * @return file or url content as byte[] or as String if + * convertToString is true. + */ + private static Object readFileOrUrl(String path, boolean convertToString) + throws IOException { + return SourceReader.readFileOrUrl(path, convertToString, + shellContextFactory.getCharacterEncoding()); + } + + static class ScriptReference extends SoftReference