1/* 2 * Copyright (c) 2003, 2007, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. 8 * 9 * This code is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * version 2 for more details (a copy is included in the LICENSE file that 13 * accompanied this code). 14 * 15 * You should have received a copy of the GNU General Public License version 16 * 2 along with this work; if not, write to the Free Software Foundation, 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * questions. 22 * 23 */ 24 25package sun.jvm.hotspot.utilities.soql; 26 27import java.util.*; 28import sun.jvm.hotspot.oops.*; 29import sun.jvm.hotspot.memory.*; 30import sun.jvm.hotspot.runtime.*; 31import sun.jvm.hotspot.utilities.*; 32 33/** 34 * This is SOQL (Simple Object Query Language) engine. This 35 * uses JavaScript engine for the "select" and "where" expression 36 * parts. 37 */ 38public class SOQLEngine extends JSJavaScriptEngine { 39 public static synchronized SOQLEngine getEngine() { 40 if (soleInstance == null) { 41 soleInstance = new SOQLEngine(); 42 } 43 return soleInstance; 44 } 45 46 /** 47 Query is of the form 48 49 select <java script code to select> 50 [ from [instanceof] <class name> [<identifier>] 51 [ where <java script boolean expression> ] 52 ] 53 */ 54 public synchronized void executeQuery(String query, ObjectVisitor visitor) 55 throws SOQLException { 56 debugPrint("query : " + query); 57 StringTokenizer st = new StringTokenizer(query); 58 if (st.hasMoreTokens()) { 59 String first = st.nextToken(); 60 if (! first.equals("select") ) { 61 throw new SOQLException("query syntax error: no 'select' clause"); 62 } 63 } else { 64 throw new SOQLException("query syntax error: no 'select' clause"); 65 } 66 67 int selectStart = query.indexOf("select"); 68 int fromStart = query.indexOf("from"); 69 70 String selectExpr = null; 71 String className = null; 72 boolean isInstanceOf = false; 73 String whereExpr = null; 74 String identifier = null; 75 76 if (fromStart != -1) { 77 selectExpr = query.substring(selectStart + "select".length(), fromStart); 78 st = new StringTokenizer(query.substring(fromStart + "from".length())); 79 80 if (st.hasMoreTokens()) { 81 String tmp = st.nextToken(); 82 if (tmp.equals("instanceof")) { 83 isInstanceOf = true; 84 if (! st.hasMoreTokens()) { 85 throw new SOQLException("no class name after 'instanceof'"); 86 } 87 className = st.nextToken(); 88 } else { 89 className = tmp; 90 } 91 } else { 92 throw new SOQLException("query syntax error: class name must follow 'from'"); 93 } 94 95 if (st.hasMoreTokens()) { 96 identifier = st.nextToken(); 97 if (identifier.equals("where")) { 98 throw new SOQLException("query syntax error: identifier should follow class name"); 99 } 100 if (st.hasMoreTokens()) { 101 String tmp = st.nextToken(); 102 if (! tmp.equals("where")) { 103 throw new SOQLException("query syntax error: 'where' clause expected after 'from' clause"); 104 } 105 int whereEnd = query.lastIndexOf("where") + 5; // "where".length 106 whereExpr = query.substring(whereEnd); 107 } 108 } else { 109 throw new SOQLException("query syntax error: identifier should follow class name"); 110 } 111 } else { // no from clause 112 selectExpr = query.substring(selectStart + "select".length(), query.length()); 113 } 114 115 executeQuery(new SOQLQuery(selectExpr, isInstanceOf, className, identifier, whereExpr), visitor); 116 } 117 118 private void executeQuery(SOQLQuery q, ObjectVisitor visitor) throws SOQLException { 119 InstanceKlass kls = null; 120 if (q.className != null) { 121 kls = SystemDictionaryHelper.findInstanceKlass(q.className); 122 if (kls == null) { 123 throw new SOQLException(q.className + " is not found!"); 124 } 125 } 126 127 128 StringBuffer buf = new StringBuffer(); 129 buf.append("function result("); 130 if (q.identifier != null) { 131 buf.append(q.identifier); 132 } 133 buf.append(") { return "); 134 buf.append(q.selectExpr.replace('\n', ' ')); 135 buf.append("; }"); 136 137 String selectCode = buf.toString(); 138 debugPrint(selectCode); 139 String whereCode = null; 140 if (q.whereExpr != null) { 141 buf = new StringBuffer(); 142 buf.append("function filter("); 143 buf.append(q.identifier); 144 buf.append(") { return "); 145 buf.append(q.whereExpr.replace('\n', ' ')); 146 buf.append("; }"); 147 whereCode = buf.toString(); 148 debugPrint(whereCode); 149 } else { 150 whereCode = "filter = null;"; 151 } 152 153 beginQuery(); 154 // compile select expression and where condition 155 evalString(selectCode, "", 1); 156 evalString(whereCode, "", 1); 157 158 // iterate thru heap, if needed 159 if (q.className != null) { 160 try { 161 iterateOops(kls, visitor, q.isInstanceOf); 162 } finally { 163 endQuery(); 164 } 165 } else { 166 // simple "select <expr>" query 167 try { 168 Object select = call("result", new Object[] {}); 169 visitor.visit(select); 170 } catch (Exception e) { 171 e.printStackTrace(); 172 } 173 } 174 } 175 176 private void dispatchObject(Oop oop, ObjectVisitor visitor, boolean filterExists) { 177 JSJavaObject jsObj = factory.newJSJavaObject(oop); 178 Object[] args = new Object[] { jsObj }; 179 boolean b = true; 180 181 try { 182 if (filterExists) { 183 Object res = call("filter", args); 184 if (res instanceof Boolean) { 185 b = ((Boolean)res).booleanValue(); 186 } else if (res instanceof Number) { 187 b = ((Number)res).intValue() != 0; 188 } else { 189 b = (res != null); 190 } 191 } 192 193 if (b) { 194 Object select = call("result", args); 195 visitor.visit(select); 196 } 197 } catch (Exception e) { 198 throw new RuntimeException(e); 199 } 200 } 201 202 private void iterateOops(final InstanceKlass ik, final ObjectVisitor visitor, 203 boolean includeSubtypes) { 204 ObjectHeap oh = VM.getVM().getObjectHeap(); 205 oh.iterateObjectsOfKlass(new HeapVisitor() { 206 boolean filterExists; 207 public void prologue(long usedSize) { 208 filterExists = getScriptEngine().get("filter") != null; 209 } 210 public boolean doObj(Oop obj) { 211 dispatchObject(obj, visitor, filterExists); 212 return false; 213 } 214 public void epilogue() {} 215 }, ik, includeSubtypes); 216 } 217 218 // we create fresh ObjectReader and factory to avoid 219 // excessive cache across queries. 220 private void beginQuery() { 221 objReader = new ObjectReader(); 222 factory = new JSJavaFactoryImpl(); 223 } 224 225 // at the end of query we clear object reader cache 226 // and factory cache 227 private void endQuery() { 228 objReader = null; 229 factory = null; 230 } 231 232 protected ObjectReader getObjectReader() { 233 return objReader; 234 } 235 236 protected JSJavaFactory getJSJavaFactory() { 237 return factory; 238 } 239 240 protected boolean isQuitting() { 241 return false; 242 } 243 244 protected void quit() { 245 // do nothing 246 } 247 248 private static void debugPrint(String msg) { 249 if (debug) System.out.println(msg); 250 } 251 252 private static final boolean debug; 253 static { 254 debug = System.getProperty("sun.jvm.hotspot.utilities.soql.SOQLEngine.debug") != null; 255 } 256 257 protected SOQLEngine() { 258 super(debug); 259 start(); 260 } 261 262 private ObjectReader objReader; 263 private JSJavaFactory factory; 264 private static SOQLEngine soleInstance; 265} 266