| /* |
| * Copyright (c) 2003, 2007, Oracle and/or its affiliates. All rights reserved. |
| * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
| * |
| * This code is free software; you can redistribute it and/or modify it |
| * under the terms of the GNU General Public License version 2 only, as |
| * published by the Free Software Foundation. |
| * |
| * This code is distributed in the hope that it will be useful, but WITHOUT |
| * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
| * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
| * version 2 for more details (a copy is included in the LICENSE file that |
| * accompanied this code). |
| * |
| * You should have received a copy of the GNU General Public License version |
| * 2 along with this work; if not, write to the Free Software Foundation, |
| * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
| * |
| * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA |
| * or visit www.oracle.com if you need additional information or have any |
| * questions. |
| * |
| */ |
| |
| package sun.jvm.hotspot.utilities.soql; |
| |
| import java.util.*; |
| import sun.jvm.hotspot.oops.*; |
| import sun.jvm.hotspot.memory.*; |
| import sun.jvm.hotspot.runtime.*; |
| import sun.jvm.hotspot.utilities.*; |
| |
| /** |
| * This is SOQL (Simple Object Query Language) engine. This |
| * uses JavaScript engine for the "select" and "where" expression |
| * parts. |
| */ |
| public class SOQLEngine extends JSJavaScriptEngine { |
| public static synchronized SOQLEngine getEngine() { |
| if (soleInstance == null) { |
| soleInstance = new SOQLEngine(); |
| } |
| return soleInstance; |
| } |
| |
| /** |
| Query is of the form |
| |
| select <java script code to select> |
| [ from [instanceof] <class name> [<identifier>] |
| [ where <java script boolean expression> ] |
| ] |
| */ |
| public synchronized void executeQuery(String query, ObjectVisitor visitor) |
| throws SOQLException { |
| debugPrint("query : " + query); |
| StringTokenizer st = new StringTokenizer(query); |
| if (st.hasMoreTokens()) { |
| String first = st.nextToken(); |
| if (! first.equals("select") ) { |
| throw new SOQLException("query syntax error: no 'select' clause"); |
| } |
| } else { |
| throw new SOQLException("query syntax error: no 'select' clause"); |
| } |
| |
| int selectStart = query.indexOf("select"); |
| int fromStart = query.indexOf("from"); |
| |
| String selectExpr = null; |
| String className = null; |
| boolean isInstanceOf = false; |
| String whereExpr = null; |
| String identifier = null; |
| |
| if (fromStart != -1) { |
| selectExpr = query.substring(selectStart + "select".length(), fromStart); |
| st = new StringTokenizer(query.substring(fromStart + "from".length())); |
| |
| if (st.hasMoreTokens()) { |
| String tmp = st.nextToken(); |
| if (tmp.equals("instanceof")) { |
| isInstanceOf = true; |
| if (! st.hasMoreTokens()) { |
| throw new SOQLException("no class name after 'instanceof'"); |
| } |
| className = st.nextToken(); |
| } else { |
| className = tmp; |
| } |
| } else { |
| throw new SOQLException("query syntax error: class name must follow 'from'"); |
| } |
| |
| if (st.hasMoreTokens()) { |
| identifier = st.nextToken(); |
| if (identifier.equals("where")) { |
| throw new SOQLException("query syntax error: identifier should follow class name"); |
| } |
| if (st.hasMoreTokens()) { |
| String tmp = st.nextToken(); |
| if (! tmp.equals("where")) { |
| throw new SOQLException("query syntax error: 'where' clause expected after 'from' clause"); |
| } |
| int whereEnd = query.lastIndexOf("where") + 5; // "where".length |
| whereExpr = query.substring(whereEnd); |
| } |
| } else { |
| throw new SOQLException("query syntax error: identifier should follow class name"); |
| } |
| } else { // no from clause |
| selectExpr = query.substring(selectStart + "select".length(), query.length()); |
| } |
| |
| executeQuery(new SOQLQuery(selectExpr, isInstanceOf, className, identifier, whereExpr), visitor); |
| } |
| |
| private void executeQuery(SOQLQuery q, ObjectVisitor visitor) throws SOQLException { |
| InstanceKlass kls = null; |
| if (q.className != null) { |
| kls = SystemDictionaryHelper.findInstanceKlass(q.className); |
| if (kls == null) { |
| throw new SOQLException(q.className + " is not found!"); |
| } |
| } |
| |
| |
| StringBuffer buf = new StringBuffer(); |
| buf.append("function result("); |
| if (q.identifier != null) { |
| buf.append(q.identifier); |
| } |
| buf.append(") { return "); |
| buf.append(q.selectExpr.replace('\n', ' ')); |
| buf.append("; }"); |
| |
| String selectCode = buf.toString(); |
| debugPrint(selectCode); |
| String whereCode = null; |
| if (q.whereExpr != null) { |
| buf = new StringBuffer(); |
| buf.append("function filter("); |
| buf.append(q.identifier); |
| buf.append(") { return "); |
| buf.append(q.whereExpr.replace('\n', ' ')); |
| buf.append("; }"); |
| whereCode = buf.toString(); |
| debugPrint(whereCode); |
| } else { |
| whereCode = "filter = null;"; |
| } |
| |
| beginQuery(); |
| // compile select expression and where condition |
| evalString(selectCode, "", 1); |
| evalString(whereCode, "", 1); |
| |
| // iterate thru heap, if needed |
| if (q.className != null) { |
| try { |
| iterateOops(kls, visitor, q.isInstanceOf); |
| } finally { |
| endQuery(); |
| } |
| } else { |
| // simple "select <expr>" query |
| try { |
| Object select = call("result", new Object[] {}); |
| visitor.visit(select); |
| } catch (Exception e) { |
| e.printStackTrace(); |
| } |
| } |
| } |
| |
| private void dispatchObject(Oop oop, ObjectVisitor visitor, boolean filterExists) { |
| JSJavaObject jsObj = factory.newJSJavaObject(oop); |
| Object[] args = new Object[] { jsObj }; |
| boolean b = true; |
| |
| try { |
| if (filterExists) { |
| Object res = call("filter", args); |
| if (res instanceof Boolean) { |
| b = ((Boolean)res).booleanValue(); |
| } else if (res instanceof Number) { |
| b = ((Number)res).intValue() != 0; |
| } else { |
| b = (res != null); |
| } |
| } |
| |
| if (b) { |
| Object select = call("result", args); |
| visitor.visit(select); |
| } |
| } catch (Exception e) { |
| throw new RuntimeException(e); |
| } |
| } |
| |
| private void iterateOops(final InstanceKlass ik, final ObjectVisitor visitor, |
| boolean includeSubtypes) { |
| ObjectHeap oh = VM.getVM().getObjectHeap(); |
| oh.iterateObjectsOfKlass(new HeapVisitor() { |
| boolean filterExists; |
| public void prologue(long usedSize) { |
| filterExists = getScriptEngine().get("filter") != null; |
| } |
| public boolean doObj(Oop obj) { |
| dispatchObject(obj, visitor, filterExists); |
| return false; |
| } |
| public void epilogue() {} |
| }, ik, includeSubtypes); |
| } |
| |
| // we create fresh ObjectReader and factory to avoid |
| // excessive cache across queries. |
| private void beginQuery() { |
| objReader = new ObjectReader(); |
| factory = new JSJavaFactoryImpl(); |
| } |
| |
| // at the end of query we clear object reader cache |
| // and factory cache |
| private void endQuery() { |
| objReader = null; |
| factory = null; |
| } |
| |
| protected ObjectReader getObjectReader() { |
| return objReader; |
| } |
| |
| protected JSJavaFactory getJSJavaFactory() { |
| return factory; |
| } |
| |
| protected boolean isQuitting() { |
| return false; |
| } |
| |
| protected void quit() { |
| // do nothing |
| } |
| |
| private static void debugPrint(String msg) { |
| if (debug) System.out.println(msg); |
| } |
| |
| private static final boolean debug; |
| static { |
| debug = System.getProperty("sun.jvm.hotspot.utilities.soql.SOQLEngine.debug") != null; |
| } |
| |
| protected SOQLEngine() { |
| super(debug); |
| start(); |
| } |
| |
| private ObjectReader objReader; |
| private JSJavaFactory factory; |
| private static SOQLEngine soleInstance; |
| } |