|
1 // Copyright (c) 2009 The Chromium Authors. All rights reserved. |
|
2 // Use of this source code is governed by a BSD-style license that can be |
|
3 // found in the LICENSE file. |
|
4 |
|
5 package org.chromium.sdk.internal.tools.v8; |
|
6 |
|
7 import java.util.ArrayList; |
|
8 import java.util.List; |
|
9 |
|
10 import org.chromium.sdk.Script; |
|
11 import org.chromium.sdk.Version; |
|
12 import org.chromium.sdk.Script.Type; |
|
13 import org.chromium.sdk.internal.DataWithRef; |
|
14 import org.chromium.sdk.internal.JsonUtil; |
|
15 import org.chromium.sdk.internal.PropertyReference; |
|
16 import org.chromium.sdk.internal.PropertyType; |
|
17 import org.chromium.sdk.internal.V8ContextFilter; |
|
18 import org.chromium.sdk.internal.protocol.AfterCompileBody; |
|
19 import org.chromium.sdk.internal.protocol.BacktraceCommandBody; |
|
20 import org.chromium.sdk.internal.protocol.BreakEventBody; |
|
21 import org.chromium.sdk.internal.protocol.BreakpointBody; |
|
22 import org.chromium.sdk.internal.protocol.CommandResponse; |
|
23 import org.chromium.sdk.internal.protocol.CommandResponseBody; |
|
24 import org.chromium.sdk.internal.protocol.EventNotification; |
|
25 import org.chromium.sdk.internal.protocol.EventNotificationBody; |
|
26 import org.chromium.sdk.internal.protocol.FailedCommandResponse; |
|
27 import org.chromium.sdk.internal.protocol.FrameObject; |
|
28 import org.chromium.sdk.internal.protocol.IncomingMessage; |
|
29 import org.chromium.sdk.internal.protocol.ScopeBody; |
|
30 import org.chromium.sdk.internal.protocol.ScopeRef; |
|
31 import org.chromium.sdk.internal.protocol.SuccessCommandResponse; |
|
32 import org.chromium.sdk.internal.protocol.VersionBody; |
|
33 import org.chromium.sdk.internal.protocol.data.ContextData; |
|
34 import org.chromium.sdk.internal.protocol.data.ContextHandle; |
|
35 import org.chromium.sdk.internal.protocol.data.FunctionValueHandle; |
|
36 import org.chromium.sdk.internal.protocol.data.ObjectValueHandle; |
|
37 import org.chromium.sdk.internal.protocol.data.PropertyObject; |
|
38 import org.chromium.sdk.internal.protocol.data.PropertyWithRef; |
|
39 import org.chromium.sdk.internal.protocol.data.PropertyWithValue; |
|
40 import org.chromium.sdk.internal.protocol.data.RefWithDisplayData; |
|
41 import org.chromium.sdk.internal.protocol.data.ScriptHandle; |
|
42 import org.chromium.sdk.internal.protocol.data.SomeHandle; |
|
43 import org.chromium.sdk.internal.protocol.data.SomeRef; |
|
44 import org.chromium.sdk.internal.protocol.data.SomeSerialized; |
|
45 import org.chromium.sdk.internal.protocol.data.ValueHandle; |
|
46 import org.chromium.sdk.internal.protocolparser.JsonProtocolModelParseException; |
|
47 import org.chromium.sdk.internal.protocolparser.JsonProtocolParseException; |
|
48 import org.chromium.sdk.internal.protocolparser.dynamicimpl.JsonProtocolParser; |
|
49 import org.chromium.sdk.internal.tools.v8.request.ScriptsMessage; |
|
50 import org.json.simple.JSONObject; |
|
51 |
|
52 /** |
|
53 * A utility class to process V8 debugger messages. |
|
54 */ |
|
55 public class V8ProtocolUtil { |
|
56 |
|
57 /** |
|
58 * Computes a script type given a V8 Long type value |
|
59 * |
|
60 * @param typeNumber a type designator from a V8 JSON response |
|
61 * @return a type corresponding to {@code typeNumber} or {@code null} if |
|
62 * {@code typeNumber == null} |
|
63 */ |
|
64 public static Script.Type getScriptType(Long typeNumber) { |
|
65 if (typeNumber == null) { |
|
66 return null; |
|
67 } |
|
68 switch (typeNumber.intValue()) { |
|
69 case ScriptsMessage.SCRIPTS_NORMAL: |
|
70 return Type.NORMAL; |
|
71 case ScriptsMessage.SCRIPTS_NATIVE: |
|
72 return Type.NATIVE; |
|
73 case ScriptsMessage.SCRIPTS_EXTENSION: |
|
74 return Type.EXTENSION; |
|
75 default: |
|
76 throw new IllegalArgumentException("unknown script type: " + typeNumber); |
|
77 } |
|
78 } |
|
79 |
|
80 /** |
|
81 * Returns the value of "ref" field in object corresponding to the fieldName |
|
82 * in parent. |
|
83 * |
|
84 * @param parent to get the object from |
|
85 * @param fieldName of the object to get the "ref" from |
|
86 * @return ref value or null if fieldName or "ref" not found |
|
87 */ |
|
88 public static Long getObjectRef(SomeRef child) { |
|
89 if (child == null) { |
|
90 return null; |
|
91 } |
|
92 return child.ref(); |
|
93 } |
|
94 |
|
95 |
|
96 /** |
|
97 * Constructs {@code PropertyReference}s from the specified object, be it in |
|
98 * the "original" or "inlineRefs" format. |
|
99 * |
|
100 * @param handle to get property references from |
|
101 * @return an array of PropertyReferences |
|
102 */ |
|
103 public static List<? extends PropertyReference> extractObjectProperties( |
|
104 ObjectValueHandle handle) { |
|
105 List<PropertyObject> props = handle.properties(); |
|
106 int propsLen = props.size(); |
|
107 List<PropertyReference> objProps = new ArrayList<PropertyReference>(propsLen); |
|
108 for (int i = 0; i < propsLen; i++) { |
|
109 PropertyObject prop = props.get(i); |
|
110 putMirror(objProps, prop, PropertyNameGetter.SUBPROPERTY); |
|
111 } |
|
112 |
|
113 return objProps; |
|
114 } |
|
115 public static List<? extends PropertyReference> extractObjectInternalProperties( |
|
116 ObjectValueHandle handle) { |
|
117 List<PropertyReference> objProps = new ArrayList<PropertyReference>(3); |
|
118 SomeRef protoObject = handle.protoObject(); |
|
119 RefWithDisplayData protoObjectRef = protoObject.asWithDisplayData(); |
|
120 if (protoObjectRef != null) { |
|
121 putMirror(objProps, protoObjectRef, PropertyNameGetter.PROTO_OBJECT); |
|
122 } |
|
123 SomeRef constructorFunction = handle.constructorFunction(); |
|
124 RefWithDisplayData constructorFunctionRef = constructorFunction.asWithDisplayData(); |
|
125 if (constructorFunctionRef != null) { |
|
126 putMirror(objProps, constructorFunctionRef, PropertyNameGetter.CONSTRUCTOR_FUNCTION); |
|
127 } |
|
128 return objProps; |
|
129 } |
|
130 |
|
131 static <OBJ> void putMirror(List<PropertyReference> refs, OBJ propertyObject, |
|
132 V8ProtocolUtil.PropertyNameGetter<OBJ> nameGetter) { |
|
133 PropertyReference propertyRef = V8ProtocolUtil.extractProperty(propertyObject, nameGetter); |
|
134 if (propertyRef != null) { |
|
135 refs.add(propertyRef); |
|
136 } |
|
137 } |
|
138 |
|
139 /** |
|
140 * Constructs one {@code PropertyReference} from the specified object, be it in |
|
141 * the "original" or "inlineRefs" format. |
|
142 * |
|
143 * @param prop json object |
|
144 * @param valuePropertyName name of value property in this prop object, might be null |
|
145 * @return PropertyReference or null if we ignore this property |
|
146 */ |
|
147 public static <OBJ> PropertyReference extractProperty(OBJ prop, |
|
148 PropertyNameGetter<OBJ> nameGetter) { |
|
149 String name = nameGetter.getName(prop); |
|
150 if (name == null) { |
|
151 return null; |
|
152 } |
|
153 |
|
154 if (isInternalProperty(name)) { |
|
155 return null; |
|
156 } |
|
157 |
|
158 DataWithRef propValue = nameGetter.getRef(prop); |
|
159 |
|
160 Long propType = nameGetter.getPropertyType(prop); |
|
161 // propType is NORMAL by default |
|
162 int propTypeValue = propType != null |
|
163 ? propType.intValue() |
|
164 : PropertyType.NORMAL.value; |
|
165 if (propTypeValue == PropertyType.FIELD.value || |
|
166 propTypeValue == PropertyType.CONSTANT_FUNCTION.value || |
|
167 propTypeValue == PropertyType.CALLBACKS.value || |
|
168 propTypeValue == PropertyType.NORMAL.value) { |
|
169 return new PropertyReference(name, propValue); |
|
170 } |
|
171 return null; |
|
172 } |
|
173 |
|
174 static abstract class PropertyNameGetter<OBJ> { |
|
175 static class SimpleNameGetter extends PropertyNameGetter<RefWithDisplayData> { |
|
176 private final String name; |
|
177 SimpleNameGetter(String name) { |
|
178 this.name = name; |
|
179 } |
|
180 @Override |
|
181 String getName(RefWithDisplayData ref) { |
|
182 return name; |
|
183 } |
|
184 @Override |
|
185 DataWithRef getRef(RefWithDisplayData prop) { |
|
186 return DataWithRef.fromSomeRef(prop.getSuper()); |
|
187 } |
|
188 @Override |
|
189 Long getPropertyType(RefWithDisplayData prop) { |
|
190 return null; |
|
191 } |
|
192 } |
|
193 |
|
194 static final PropertyNameGetter<PropertyObject> LOCAL = new SubpropertyNameGetter() { |
|
195 @Override |
|
196 String getName(PropertyObject ref) { |
|
197 String name = super.getName(ref); |
|
198 if (V8ProtocolUtil.isInternalProperty(name)) { |
|
199 return null; |
|
200 } |
|
201 return name; |
|
202 } |
|
203 }; |
|
204 /** The name of the "this" object to report as a variable name. */ |
|
205 static final PropertyNameGetter<RefWithDisplayData> THIS = new SimpleNameGetter("this"); |
|
206 static final PropertyNameGetter<RefWithDisplayData> PROTO_OBJECT = |
|
207 new SimpleNameGetter("__proto__"); |
|
208 static final PropertyNameGetter<RefWithDisplayData> CONSTRUCTOR_FUNCTION = |
|
209 new SimpleNameGetter("constructor"); |
|
210 |
|
211 static final PropertyNameGetter<PropertyObject> SUBPROPERTY = new SubpropertyNameGetter(); |
|
212 static class SubpropertyNameGetter extends PropertyNameGetter<PropertyObject> { |
|
213 @Override |
|
214 String getName(PropertyObject ref) { |
|
215 Object nameObject = ref.name(); |
|
216 return nameObject.toString(); |
|
217 } |
|
218 @Override |
|
219 DataWithRef getRef(PropertyObject prop) { |
|
220 PropertyWithValue asPropertyWithValue = prop.asPropertyWithValue(); |
|
221 if (asPropertyWithValue != null) { |
|
222 return DataWithRef.fromSomeRef(asPropertyWithValue.getValue().getSuper()); |
|
223 } else { |
|
224 return DataWithRef.fromLong(prop.asPropertyWithRef().ref()); |
|
225 } |
|
226 } |
|
227 @Override |
|
228 Long getPropertyType(PropertyObject prop) { |
|
229 PropertyWithRef asPropertyWithRef = prop.asPropertyWithRef(); |
|
230 if (asPropertyWithRef == null) { |
|
231 return null; |
|
232 } |
|
233 return asPropertyWithRef.propertyType(); |
|
234 } |
|
235 } |
|
236 |
|
237 abstract DataWithRef getRef(OBJ prop); |
|
238 |
|
239 /** |
|
240 * @return property name or null if we should skip this property |
|
241 */ |
|
242 abstract String getName(OBJ ref); |
|
243 abstract Long getPropertyType(OBJ prop); |
|
244 } |
|
245 |
|
246 /** |
|
247 * @param propertyName the property name to check |
|
248 * @return whether the given property name corresponds to an internal V8 |
|
249 * property |
|
250 */ |
|
251 public static boolean isInternalProperty(String propertyName) { |
|
252 // Chrome can return properties like ".arguments". They should be ignored. |
|
253 return propertyName.length() == 0 || propertyName.startsWith("."); |
|
254 } |
|
255 |
|
256 /** |
|
257 * Gets a function name from the given function handle. |
|
258 * |
|
259 * @param functionObject the function handle |
|
260 * @return the actual of inferred function name. Will handle {@code null} or |
|
261 * unnamed functions |
|
262 */ |
|
263 public static String getFunctionName(JSONObject functionObject) { |
|
264 if (functionObject == null) { |
|
265 return "<unknown>"; |
|
266 } else { |
|
267 String name = getNameOrInferred(functionObject, V8Protocol.LOCAL_NAME); |
|
268 if (isNullOrEmpty(name)) { |
|
269 return "(anonymous function)"; |
|
270 } else { |
|
271 return name; |
|
272 } |
|
273 } |
|
274 } |
|
275 |
|
276 /** |
|
277 * Gets a script id from a script response. |
|
278 * |
|
279 * @param scriptObject to get the "id" value from |
|
280 * @return the script id |
|
281 */ |
|
282 public static Long getScriptIdFromResponse(ScriptHandle scriptObject) { |
|
283 return scriptObject.id(); |
|
284 } |
|
285 |
|
286 /** |
|
287 * Determines if a {@code script} is valid in the current debug context. |
|
288 * Returns {@code null} if it is not, otherwise returns {@code script}. |
|
289 * |
|
290 * @param script to check and, possibly, modify |
|
291 * @param refs from the corresponding V8 response |
|
292 * @return script with a non-null name if the script is valid, {@code null} |
|
293 * otherwise |
|
294 */ |
|
295 public static ScriptHandle validScript(ScriptHandle script, List<SomeHandle> refs, |
|
296 V8ContextFilter contextFilter) { |
|
297 Long contextRef = V8ProtocolUtil.getObjectRef(script.context()); |
|
298 for (int i = 0, size = refs.size(); i < size; i++) { |
|
299 SomeHandle ref = refs.get(i); |
|
300 if (ref.handle() != contextRef.longValue()) { |
|
301 continue; |
|
302 } |
|
303 ContextHandle contextHandle; |
|
304 try { |
|
305 contextHandle = ref.asContextHandle(); |
|
306 } catch (JsonProtocolParseException e) { |
|
307 throw new RuntimeException(e); |
|
308 } |
|
309 if (!contextFilter.isContextOurs(contextHandle)) { |
|
310 return null; |
|
311 } |
|
312 return script; |
|
313 } |
|
314 return null; // good context not found |
|
315 } |
|
316 |
|
317 public static Version parseVersionResponse(SuccessCommandResponse versionResponse) { |
|
318 VersionBody body; |
|
319 try { |
|
320 body = versionResponse.getBody().asVersionBody(); |
|
321 } catch (JsonProtocolParseException e) { |
|
322 throw new RuntimeException(e); |
|
323 } |
|
324 String versionString = body.getV8Version(); |
|
325 if (versionString == null) { |
|
326 return null; |
|
327 } |
|
328 return Version.parseString(versionString); |
|
329 } |
|
330 |
|
331 private static String getNameOrInferred(JSONObject obj, V8Protocol nameProperty) { |
|
332 String name = JsonUtil.getAsString(obj, nameProperty); |
|
333 if (isNullOrEmpty(name)) { |
|
334 name = JsonUtil.getAsString(obj, V8Protocol.INFERRED_NAME); |
|
335 } |
|
336 return name; |
|
337 } |
|
338 |
|
339 private static boolean isNullOrEmpty(String value) { |
|
340 return value == null || value.length() == 0; |
|
341 } |
|
342 |
|
343 public static JsonProtocolParser getV8Parser() { |
|
344 return parser; |
|
345 } |
|
346 |
|
347 private static final JsonProtocolParser parser; |
|
348 static { |
|
349 try { |
|
350 // TODO(peter.rybin): change to ParserHolder. |
|
351 parser = new JsonProtocolParser(new Class<?>[] { |
|
352 IncomingMessage.class, |
|
353 EventNotification.class, |
|
354 SuccessCommandResponse.class, |
|
355 FailedCommandResponse.class, |
|
356 CommandResponse.class, |
|
357 BreakEventBody.class, |
|
358 EventNotificationBody.class, |
|
359 CommandResponseBody.class, |
|
360 BacktraceCommandBody.class, |
|
361 FrameObject.class, |
|
362 BreakpointBody.class, |
|
363 ScopeBody.class, |
|
364 ScopeRef.class, |
|
365 VersionBody.class, |
|
366 AfterCompileBody.class, |
|
367 |
|
368 SomeHandle.class, |
|
369 ScriptHandle.class, |
|
370 ValueHandle.class, |
|
371 RefWithDisplayData.class, |
|
372 PropertyObject.class, |
|
373 PropertyWithRef.class, |
|
374 PropertyWithValue.class, |
|
375 ObjectValueHandle.class, |
|
376 FunctionValueHandle.class, |
|
377 SomeRef.class, |
|
378 SomeSerialized.class, |
|
379 ContextHandle.class, |
|
380 ContextData.class, |
|
381 }); |
|
382 } catch (JsonProtocolModelParseException e) { |
|
383 throw new RuntimeException(e); |
|
384 } |
|
385 } |
|
386 } |