WEB-14888 Firefox debugger: variable is not added in the list "Local" if it was decla...
authorVladimir Krivosheev <vladimir.krivosheev@jetbrains.com>
Wed, 28 Jan 2015 13:15:40 +0000 (14:15 +0100)
committerVladimir Krivosheev <vladimir.krivosheev@jetbrains.com>
Wed, 28 Jan 2015 13:17:15 +0000 (14:17 +0100)
platform/script-debugger/backend/src/org/jetbrains/concurrency/ObsolescentAsyncFunction.java [new file with mode: 0644]
platform/script-debugger/backend/src/org/jetbrains/debugger/values/ObjectValue.java
platform/script-debugger/backend/src/org/jetbrains/debugger/values/ObjectValueBase.java
platform/script-debugger/debugger-ui/src/org/jetbrains/debugger/ScopeVariablesGroup.java
platform/script-debugger/debugger-ui/src/org/jetbrains/debugger/ValueNodeAsyncFunction.java
platform/script-debugger/debugger-ui/src/org/jetbrains/debugger/VariableView.java
platform/script-debugger/debugger-ui/src/org/jetbrains/debugger/Variables.java
platform/script-debugger/protocol/protocol-reader-runtime/src/org/jetbrains/jsonProtocol/MapFactory.java [new file with mode: 0644]
platform/script-debugger/protocol/protocol-reader/src/org/jetbrains/protocolReader/MapReader.java
platform/script-debugger/protocol/protocol-reader/src/org/jetbrains/protocolReader/ObjectValueReader.java
platform/script-debugger/protocol/protocol-reader/src/org/jetbrains/protocolReader/TypeWriter.java

diff --git a/platform/script-debugger/backend/src/org/jetbrains/concurrency/ObsolescentAsyncFunction.java b/platform/script-debugger/backend/src/org/jetbrains/concurrency/ObsolescentAsyncFunction.java
new file mode 100644 (file)
index 0000000..66f512c
--- /dev/null
@@ -0,0 +1,4 @@
+package org.jetbrains.concurrency;
+
+public interface ObsolescentAsyncFunction<PARAM, RESULT> extends AsyncFunction<PARAM, RESULT>, Obsolescent {
+}
\ No newline at end of file
index e7261ce293e4d1225004ea1317ed4e8ff35bfcea..3d9bd035ed15703d69b7a5d1edbc6dcd9fad4a57 100755 (executable)
@@ -3,7 +3,9 @@ package org.jetbrains.debugger.values;
 import com.intellij.util.ThreeState;
 import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
+import org.jetbrains.concurrency.Obsolescent;
 import org.jetbrains.concurrency.Promise;
+import org.jetbrains.debugger.EvaluateContext;
 import org.jetbrains.debugger.Variable;
 import org.jetbrains.debugger.VariablesHost;
 
@@ -19,6 +21,9 @@ public interface ObjectValue extends Value {
   @NotNull
   Promise<List<Variable>> getProperties();
 
+  @NotNull
+  Promise<List<Variable>> getProperties(@NotNull List<String> names, @NotNull EvaluateContext evaluateContext, @NotNull Obsolescent obsolescent);
+
   @NotNull
   VariablesHost getVariablesHost();
 
index 00de61f0d319658e190a1431bb56ba0fa9a542f3..4755655e704e9bed8b352470bc19b903371eadf0 100644 (file)
@@ -1,12 +1,19 @@
 package org.jetbrains.debugger.values;
 
+import com.intellij.util.SmartList;
 import com.intellij.util.ThreeState;
 import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
+import org.jetbrains.concurrency.Obsolescent;
+import org.jetbrains.concurrency.ObsolescentAsyncFunction;
 import org.jetbrains.concurrency.Promise;
+import org.jetbrains.debugger.EvaluateContext;
+import org.jetbrains.debugger.ValueModifier;
 import org.jetbrains.debugger.Variable;
 import org.jetbrains.debugger.VariablesHost;
 
+import java.util.Collections;
+import java.util.Comparator;
 import java.util.List;
 
 public abstract class ObjectValueBase<VALUE_LOADER extends ValueManager> extends ValueBase implements ObjectValue {
@@ -22,6 +29,67 @@ public abstract class ObjectValueBase<VALUE_LOADER extends ValueManager> extends
     return childrenManager.get();
   }
 
+  abstract class MyObsolescentAsyncFunction<PARAM, RESULT> implements ObsolescentAsyncFunction<PARAM, RESULT> {
+    private final Obsolescent obsolescent;
+
+    MyObsolescentAsyncFunction(@NotNull Obsolescent obsolescent) {
+      this.obsolescent = obsolescent;
+    }
+
+    @Override
+    public boolean isObsolete() {
+      return obsolescent.isObsolete() || childrenManager.valueManager.isObsolete();
+    }
+  }
+
+  @NotNull
+  @Override
+  public Promise<List<Variable>> getProperties(@NotNull final List<String> names, @NotNull final EvaluateContext evaluateContext, @NotNull final Obsolescent obsolescent) {
+    return getProperties()
+      .then(new MyObsolescentAsyncFunction<List<Variable>, List<Variable>>(obsolescent) {
+        @NotNull
+        @Override
+        public Promise<List<Variable>> fun(List<Variable> variables) {
+          final List<Variable> properties = new SmartList<Variable>();
+          int getterCount = 0;
+          for (Variable property : variables) {
+            if (!property.isReadable() || !names.contains(property.getName())) {
+              continue;
+            }
+
+            if (!properties.isEmpty()) {
+              Collections.sort(properties, new Comparator<Variable>() {
+                @Override
+                public int compare(@NotNull Variable o1, @NotNull Variable o2) {
+                  return names.indexOf(o1.getName()) - names.indexOf(o2.getName());
+                }
+              });
+            }
+
+            properties.add(property);
+            if (property.getValue() == null) {
+              getterCount++;
+            }
+          }
+
+          if (getterCount == 0) {
+            return Promise.resolve(properties);
+          }
+          else {
+            List<Promise<?>> promises = new SmartList<Promise<?>>();
+            for (Variable variable : properties) {
+              if (variable.getValue() == null) {
+                ValueModifier valueModifier = variable.getValueModifier();
+                assert valueModifier != null;
+                promises.add(valueModifier.evaluateGet(variable, evaluateContext));
+              }
+            }
+            return Promise.all(promises, properties);
+          }
+        }
+      });
+  }
+
   @Nullable
   @Override
   public String getValueString() {
index 33c9a3f1f9cdf6968139182a06dd0c37262859e0..5753cf1e6090c47cc9f791957dde1b53407d4eb2 100644 (file)
@@ -96,21 +96,21 @@ public class ScopeVariablesGroup extends XValueGroup {
       promise.done(new ValueNodeConsumer<Void>(node) {
         @Override
         public void consume(Void ignored) {
-          callFrame.getReceiverVariable().done(new Consumer<Variable>() {
-            @Override
-            public void consume(Variable variable) {
-              if (!node.isObsolete()) {
+          callFrame.getReceiverVariable()
+            .done(new Consumer<Variable>() {
+              @Override
+              public void consume(Variable variable) {
                 node.addChildren(variable == null ? XValueChildrenList.EMPTY : XValueChildrenList.singleton(new VariableView(variable, context)), true);
               }
-            }
-          }).rejected(new Consumer<Throwable>() {
-            @Override
-            public void consume(@Nullable Throwable error) {
-              if (!node.isObsolete()) {
-                node.addChildren(XValueChildrenList.EMPTY, true);
+            })
+            .rejected(new Consumer<Throwable>() {
+              @Override
+              public void consume(@Nullable Throwable error) {
+                if (!node.isObsolete()) {
+                  node.addChildren(XValueChildrenList.EMPTY, true);
+                }
               }
-            }
-          });
+            });
         }
       });
     }
index 3e6941a328f07e36ab60bf5cf460ad2581f2c75c..395a39046b288e7d15521ccfbfc3835aee365d74 100644 (file)
@@ -2,11 +2,12 @@ package org.jetbrains.debugger;
 
 import org.jetbrains.annotations.NotNull;
 import org.jetbrains.concurrency.AsyncFunction;
+import org.jetbrains.concurrency.Obsolescent;
 
 public abstract class ValueNodeAsyncFunction<PARAM, RESULT> implements AsyncFunction<PARAM, RESULT>, org.jetbrains.concurrency.Obsolescent {
-  private final com.intellij.xdebugger.Obsolescent node;
+  private final Obsolescent node;
 
-  protected ValueNodeAsyncFunction(@NotNull com.intellij.xdebugger.Obsolescent node) {
+  protected ValueNodeAsyncFunction(@NotNull Obsolescent node) {
     this.node = node;
   }
 
index 3374bc3316f19f7089648b7ede0c0118eafa3e20..e8a5f0ecba73cb3d198522ff748a5111e0cf5a0c 100644 (file)
@@ -8,6 +8,7 @@ import com.intellij.pom.Navigatable;
 import com.intellij.psi.PsiElement;
 import com.intellij.psi.PsiReference;
 import com.intellij.util.Consumer;
+import com.intellij.util.PairConsumer;
 import com.intellij.util.SmartList;
 import com.intellij.util.ThreeState;
 import com.intellij.xdebugger.XSourcePositionWrapper;
@@ -385,43 +386,31 @@ public final class VariableView extends XNamedValue implements VariableContext {
 
   @NotNull
   private Promise<Void> computeNamedProperties(@NotNull final ObjectValue value, @NotNull final XCompositeNode node, final boolean isLastChildren) {
-    // start properties loading to achieve, possibly, parallel execution (properties loading & member filter computation)
-    final Promise<List<Variable>> properties = value.getProperties();
-    return getMemberFilter()
-      .then(new ValueNodeAsyncFunction<MemberFilter, Void>(node) {
-        @NotNull
-        @Override
-        public Promise<Void> fun(MemberFilter memberFilter) {
-          VariableView.this.memberFilter = memberFilter;
-          return properties.then(new ValueNodeAsyncFunction<List<Variable>, Void>(node) {
-            @NotNull
-            @Override
-            public Promise<Void> fun(List<Variable> variables) {
-              if (value.getType() == ValueType.ARRAY && !(value instanceof ArrayValue)) {
-                computeArrayRanges(variables, node);
-                return Promise.DONE;
-              }
-
-              FunctionValue functionValue = value instanceof FunctionValue ? (FunctionValue)value : null;
-              if (functionValue != null && functionValue.hasScopes() == ThreeState.NO) {
-                functionValue = null;
-              }
-
-              remainingChildren = Variables.processNamedObjectProperties(variables, node, VariableView.this, VariableView.this.memberFilter, XCompositeNode.MAX_CHILDREN_TO_SHOW,
-                                                                         isLastChildren && functionValue == null);
-              if (remainingChildren != null) {
-                remainingChildrenOffset = XCompositeNode.MAX_CHILDREN_TO_SHOW;
-              }
-
-              if (functionValue != null) {
-                // we pass context as variable context instead of this variable value - we cannot watch function scopes variables, so, this variable name doesn't matter
-                node.addChildren(XValueChildrenList.bottomGroup(new FunctionScopesValueGroup(functionValue, context)), isLastChildren);
-              }
-              return Promise.DONE;
-            }
-          });
+    return Variables.processVariables(this, value.getProperties(), node, new PairConsumer<MemberFilter, List<Variable>>() {
+      @Override
+      public void consume(MemberFilter memberFilter, List<Variable> variables) {
+        if (value.getType() == ValueType.ARRAY && !(value instanceof ArrayValue)) {
+          computeArrayRanges(variables, node);
+          return;
         }
-      });
+
+        FunctionValue functionValue = value instanceof FunctionValue ? (FunctionValue)value : null;
+        if (functionValue != null && functionValue.hasScopes() == ThreeState.NO) {
+          functionValue = null;
+        }
+
+        remainingChildren = Variables.processNamedObjectProperties(variables, node, VariableView.this, VariableView.this.memberFilter, XCompositeNode.MAX_CHILDREN_TO_SHOW,
+                                                                   isLastChildren && functionValue == null);
+        if (remainingChildren != null) {
+          remainingChildrenOffset = XCompositeNode.MAX_CHILDREN_TO_SHOW;
+        }
+
+        if (functionValue != null) {
+          // we pass context as variable context instead of this variable value - we cannot watch function scopes variables, so, this variable name doesn't matter
+          node.addChildren(XValueChildrenList.bottomGroup(new FunctionScopesValueGroup(functionValue, context)), isLastChildren);
+        }
+      }
+    });
   }
 
   private void computeArrayRanges(@NotNull List<Variable> properties, @NotNull XCompositeNode node) {
index fee69956c3eb46e301d68a2594384e1bdba4e68f..9d3fbc5724aceb7b7b3a784e888891b544d93c87 100644 (file)
@@ -1,11 +1,14 @@
 package org.jetbrains.debugger;
 
 import com.intellij.openapi.util.text.StringUtil;
+import com.intellij.util.PairConsumer;
 import com.intellij.util.SmartList;
 import com.intellij.xdebugger.frame.XCompositeNode;
 import com.intellij.xdebugger.frame.XValueChildrenList;
 import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
+import org.jetbrains.concurrency.Obsolescent;
+import org.jetbrains.concurrency.ObsolescentFunction;
 import org.jetbrains.concurrency.Promise;
 import org.jetbrains.debugger.values.Value;
 import org.jetbrains.debugger.values.ValueType;
@@ -24,66 +27,82 @@ public final class Variables {
   };
 
   @NotNull
-  public static Promise<Void> processScopeVariables(@NotNull final Scope scope,
-                                                    @NotNull final XCompositeNode node,
-                                                    @NotNull final VariableContext context,
-                                                    final boolean isLast) {
+  public static Promise<Void> processVariables(@NotNull final VariableContext context,
+                                               @NotNull final Promise<List<Variable>> variables,
+                                               @NotNull final Obsolescent obsolescent,
+                                               @NotNull final PairConsumer<MemberFilter, List<Variable>> consumer) {
+    // start properties loading to achieve, possibly, parallel execution (properties loading & member filter computation)
     return context.getMemberFilter()
-      .then(new ValueNodeAsyncFunction<MemberFilter, Void>(node) {
+      .then(new ValueNodeAsyncFunction<MemberFilter, Void>(obsolescent) {
         @NotNull
         @Override
         public Promise<Void> fun(final MemberFilter memberFilter) {
-          return scope.getVariablesHost().get()
-            .then(new ValueNodeAsyncFunction<List<Variable>, Void>(node) {
-              @NotNull
-              @Override
-              public Promise<Void> fun(List<Variable> variables) {
-                Collection<Variable> additionalVariables = memberFilter.getAdditionalVariables();
-                List<Variable> properties = new ArrayList<Variable>(variables.size() + additionalVariables.size());
-                List<Variable> functions = new SmartList<Variable>();
-                for (Variable variable : variables) {
-                  if (memberFilter.isMemberVisible(variable, false)) {
-                    Value value = variable.getValue();
-                    if (value != null &&
-                        value.getType() == ValueType.FUNCTION &&
-                        value.getValueString() != null &&
-                        !UNNAMED_FUNCTION_PATTERN.matcher(value.getValueString()).lookingAt()) {
-                      functions.add(variable);
-                    }
-                    else {
-                      properties.add(variable);
-                    }
-                  }
-                }
+          return variables.then(new ObsolescentFunction<List<Variable>, Void>() {
+            @Override
+            public boolean isObsolete() {
+              return obsolescent.isObsolete();
+            }
+
+            @Override
+            public Void fun(List<Variable> variables) {
+              consumer.consume(memberFilter, variables);
+              return null;
+            }
+          });
+        }
+      });
+  }
 
-                Comparator<Variable> comparator = memberFilter.hasNameMappings() ? new Comparator<Variable>() {
-                  @Override
-                  public int compare(@NotNull Variable o1, @NotNull Variable o2) {
-                    return naturalCompare(memberFilter.getName(o1), memberFilter.getName(o2));
-                  }
-                } : NATURAL_NAME_COMPARATOR;
+  @NotNull
+  public static Promise<Void> processScopeVariables(@NotNull final Scope scope,
+                                                    @NotNull final XCompositeNode node,
+                                                    @NotNull final VariableContext context,
+                                                    final boolean isLast) {
+    return processVariables(context, scope.getVariablesHost().get(), node, new PairConsumer<MemberFilter, List<Variable>>() {
+      @Override
+      public void consume(final MemberFilter memberFilter, List<Variable> variables) {
+        Collection<Variable> additionalVariables = memberFilter.getAdditionalVariables();
+        List<Variable> properties = new ArrayList<Variable>(variables.size() + additionalVariables.size());
+        List<Variable> functions = new SmartList<Variable>();
+        for (Variable variable : variables) {
+          if (memberFilter.isMemberVisible(variable, false)) {
+            Value value = variable.getValue();
+            if (value != null &&
+                value.getType() == ValueType.FUNCTION &&
+                value.getValueString() != null &&
+                !UNNAMED_FUNCTION_PATTERN.matcher(value.getValueString()).lookingAt()) {
+              functions.add(variable);
+            }
+            else {
+              properties.add(variable);
+            }
+          }
+        }
 
-                Collections.sort(properties, comparator);
-                Collections.sort(functions, comparator);
+        Comparator<Variable> comparator = memberFilter.hasNameMappings() ? new Comparator<Variable>() {
+          @Override
+          public int compare(@NotNull Variable o1, @NotNull Variable o2) {
+            return naturalCompare(memberFilter.getName(o1), memberFilter.getName(o2));
+          }
+        } : NATURAL_NAME_COMPARATOR;
 
-                addAditionalVariables(variables, additionalVariables, properties, memberFilter);
+        Collections.sort(properties, comparator);
+        Collections.sort(functions, comparator);
 
-                if (!properties.isEmpty()) {
-                  node.addChildren(createVariablesList(properties, context, memberFilter), functions.isEmpty() && isLast);
-                }
+        addAditionalVariables(variables, additionalVariables, properties, memberFilter);
 
-                if (!functions.isEmpty()) {
-                  node.addChildren(XValueChildrenList.bottomGroup(new VariablesGroup("Functions", functions, context)), isLast);
-                }
-                else if (isLast && properties.isEmpty()) {
-                  node.addChildren(XValueChildrenList.EMPTY, true);
-                }
+        if (!properties.isEmpty()) {
+          node.addChildren(createVariablesList(properties, context, memberFilter), functions.isEmpty() && isLast);
+        }
 
-                return Promise.DONE;
-              }
-            });
+        if (!functions.isEmpty()) {
+          node.addChildren(XValueChildrenList.bottomGroup(new VariablesGroup("Functions", functions, context)), isLast);
         }
-      });
+        else if (isLast && properties.isEmpty()) {
+          node.addChildren(XValueChildrenList.EMPTY, true);
+        }
+      }
+    });
   }
 
   @Nullable
diff --git a/platform/script-debugger/protocol/protocol-reader-runtime/src/org/jetbrains/jsonProtocol/MapFactory.java b/platform/script-debugger/protocol/protocol-reader-runtime/src/org/jetbrains/jsonProtocol/MapFactory.java
new file mode 100644 (file)
index 0000000..dbaadc3
--- /dev/null
@@ -0,0 +1,19 @@
+package org.jetbrains.jsonProtocol;
+
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.io.JsonReaderEx;
+
+import java.util.Map;
+
+public final class MapFactory<T> extends ObjectFactory<Map<String, T>> {
+  private final ObjectFactory<T> valueFactory;
+
+  public MapFactory(@NotNull ObjectFactory<T> valueFactory) {
+    this.valueFactory = valueFactory;
+  }
+
+  @Override
+  public Map<String, T> read(JsonReaderEx reader) {
+    return JsonReaders.readMap(reader, valueFactory);
+  }
+}
index 39d3ab32dc30233dd05b55a63df2445aa7d9e358..74f12773dea72651620bb4582a00e250ae273ea7 100644 (file)
@@ -40,4 +40,13 @@ public class MapReader extends ValueReader {
     }
     out.append(')');
   }
+
+  @Override
+  void writeArrayReadCode(@NotNull ClassScope scope, boolean subtyping, @NotNull TextOutput out) {
+    beginReadCall("ObjectArray", subtyping, out);
+    out.comma().append("new org.jetbrains.jsonProtocol.MapFactory(");
+    assert componentParser != null;
+    ((ObjectValueReader)componentParser).writeFactoryNewExpression(scope, out);
+    out.append("))");
+  }
 }
index b14086233a7bc291413ac5aa6c690f92e4890189..52327500bc539a68e09a099759c49f7f859b5fcf 100644 (file)
@@ -59,6 +59,11 @@ class ObjectValueReader<T> extends ValueReader {
   }
 
   void writeFactoryArgument(@NotNull ClassScope scope, @NotNull TextOutput out) {
-    out.comma().append("new ").append(scope.requireFactoryGenerationAndGetName(refToType.type)).append(Util.TYPE_FACTORY_NAME_POSTFIX).append("()");
+    out.comma();
+    writeFactoryNewExpression(scope, out);
+  }
+
+  void writeFactoryNewExpression(@NotNull ClassScope scope, @NotNull TextOutput out) {
+    out.append("new ").append(scope.requireFactoryGenerationAndGetName(refToType.type)).append(Util.TYPE_FACTORY_NAME_POSTFIX).append("()");
   }
 }
index e47017c496a2abdde975e2dced52d4b6f68dfc7f..798a9f6e11b8e921af531330a628b7d98829bece 100644 (file)
@@ -153,7 +153,7 @@ class TypeWriter<T> {
     else {
       out.append("if (name == null)").openBlock();
       {
-        out.append("if (reader.beginObject().hasNext())").openBlock();
+        out.append("if (reader.hasNext() && reader.beginObject().hasNext())").openBlock();
         {
           out.append("name = reader.nextName()").semi();
         }