View Javadoc
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  
25  package sun.jvm.hotspot.utilities.soql;
26  
27  import java.util.*;
28  import sun.jvm.hotspot.oops.*;
29  import sun.jvm.hotspot.memory.*;
30  import sun.jvm.hotspot.runtime.*;
31  import 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   */
38  public 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 }