org.chromium.sdk/src/org/chromium/sdk/internal/tools/v8/V8ProtocolUtil.java
changeset 2 e4420d2515f1
child 276 f2f4a1259de8
equal deleted inserted replaced
1:ef76fc2ac88c 2:e4420d2515f1
       
     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 }