diff --git a/platform/script-debugger/backend/src/org/jetbrains/debugger/ObjectProperty.java b/platform/script-debugger/backend/src/org/jetbrains/debugger/ObjectProperty.java index 4cf529638edd..a3f9a9683fc8 100644 --- a/platform/script-debugger/backend/src/org/jetbrains/debugger/ObjectProperty.java +++ b/platform/script-debugger/backend/src/org/jetbrains/debugger/ObjectProperty.java @@ -4,6 +4,7 @@ import com.intellij.openapi.util.AsyncResult; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.debugger.values.FunctionValue; +import org.jetbrains.debugger.values.ObjectValue; import org.jetbrains.debugger.values.Value; /** @@ -42,5 +43,5 @@ public interface ObjectProperty extends Variable { * Asynchronously evaluates property getter and returns property value */ @NotNull - AsyncResult evaluateGet(@NotNull EvaluateContext evaluateContext); + AsyncResult evaluateGet(@NotNull ObjectValue host, @NotNull EvaluateContext evaluateContext); } diff --git a/platform/script-debugger/backend/src/org/jetbrains/debugger/ValueModifierUtil.java b/platform/script-debugger/backend/src/org/jetbrains/debugger/ValueModifierUtil.java index 654008063f1a..ae3269ef0140 100644 --- a/platform/script-debugger/backend/src/org/jetbrains/debugger/ValueModifierUtil.java +++ b/platform/script-debugger/backend/src/org/jetbrains/debugger/ValueModifierUtil.java @@ -1,11 +1,34 @@ package org.jetbrains.debugger; import com.intellij.openapi.util.ActionCallback; +import com.intellij.openapi.util.AsyncResult; import com.intellij.util.Consumer; import org.jetbrains.annotations.NotNull; +import org.jetbrains.debugger.values.ObjectValue; import org.jetbrains.debugger.values.Value; +import java.util.Collections; +import java.util.List; +import java.util.regex.Pattern; + public final class ValueModifierUtil { + private static final Pattern KEY_NOTATION_PROPERTY_NAME_PATTERN = Pattern.compile("[\\p{L}_$]+[\\d\\p{L}_$]*"); + private static final String[] REPLACEMENT_CHARS; + + static { + REPLACEMENT_CHARS = new String[128]; + for (int i = 0; i <= 31; i++) { + REPLACEMENT_CHARS[i] = String.format("\\u%04x", (int)i); + } + REPLACEMENT_CHARS['"'] = "\\\""; + REPLACEMENT_CHARS['\\'] = "\\\\"; + REPLACEMENT_CHARS['\t'] = "\\t"; + REPLACEMENT_CHARS['\b'] = "\\b"; + REPLACEMENT_CHARS['\n'] = "\\n"; + REPLACEMENT_CHARS['\r'] = "\\r"; + REPLACEMENT_CHARS['\f'] = "\\f"; + } + public static ActionCallback setValue(@NotNull final Variable variable, String newValue, @NotNull final EvaluateContext evaluateContext, @NotNull final ValueModifier modifier) { final ActionCallback callback = new ActionCallback(); evaluateContext.evaluate(newValue).doWhenDone(new Consumer() { @@ -16,4 +39,88 @@ public final class ValueModifierUtil { }).notifyWhenRejected(callback); return callback; } + + @NotNull + public static AsyncResult evaluateGet(@NotNull final ObjectProperty property, + @NotNull EvaluateContextAdditionalParameter host, + @NotNull EvaluateContext evaluateContext, + @NotNull String selfName) { + ObjectValue getterFunction = property.getGetter(); + if (getterFunction == null) { + throw new RuntimeException("Getter is not a function"); + } + + StringBuilder builder = new StringBuilder(selfName); + appendName(builder, property.getName(), false); + return evaluateContext.evaluate(builder.toString(), Collections.singletonMap(selfName, host)).doWhenDone(new Consumer() { + @Override + public void consume(Value value) { + property.setValue(value); + } + }); + } + + public static String propertyNamesToString(List list, boolean quotedAware) { + StringBuilder builder = new StringBuilder(); + for (int i = list.size() - 1; i >= 0; i--) { + String name = list.get(i); + boolean quoted = quotedAware && (name.charAt(0) == '"' || name.charAt(0) == '\''); + appendName(builder, name, quoted); + } + return builder.toString(); + } + + public static void appendName(@NotNull StringBuilder builder, @NotNull String name, boolean quoted) { + boolean useKeyNotation = !quoted && KEY_NOTATION_PROPERTY_NAME_PATTERN.matcher(name).matches(); + if (builder.length() != 0) { + builder.append(useKeyNotation ? '.' : '['); + } + if (useKeyNotation) { + builder.append(name); + } + else { + if (quoted) { + builder.append(name); + } + else { + escape(name, builder); + } + builder.append(']'); + } + } + + public static void escape(CharSequence value, StringBuilder sb) { + int length = value.length(); + sb.ensureCapacity(sb.capacity() + length + 2); + sb.append('"'); + int last = 0; + for (int i = 0; i < length; i++) { + char c = value.charAt(i); + String replacement; + if (c < 128) { + replacement = REPLACEMENT_CHARS[c]; + if (replacement == null) { + continue; + } + } + else if (c == '\u2028') { + replacement = "\\u2028"; + } + else if (c == '\u2029') { + replacement = "\\u2029"; + } + else { + continue; + } + if (last < i) { + sb.append(value, last, i); + } + sb.append(replacement); + last = i + 1; + } + if (last < length) { + sb.append(value, last, length); + } + sb.append('"'); + } } \ No newline at end of file diff --git a/platform/script-debugger/backend/src/org/jetbrains/debugger/values/ObjectPropertyBase.java b/platform/script-debugger/backend/src/org/jetbrains/debugger/values/ObjectPropertyBase.java index dd38cc1ba260..a7b7824f38f4 100644 --- a/platform/script-debugger/backend/src/org/jetbrains/debugger/values/ObjectPropertyBase.java +++ b/platform/script-debugger/backend/src/org/jetbrains/debugger/values/ObjectPropertyBase.java @@ -1,20 +1,20 @@ package org.jetbrains.debugger.values; -import com.intellij.openapi.util.AsyncResult; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import org.jetbrains.debugger.EvaluateContext; import org.jetbrains.debugger.ObjectProperty; import org.jetbrains.debugger.ValueModifier; import org.jetbrains.debugger.VariableImpl; public abstract class ObjectPropertyBase extends VariableImpl implements ObjectProperty { private final FunctionValue getter; + private final FunctionValue setter; - protected ObjectPropertyBase(@NotNull String name, @Nullable Value value, @Nullable FunctionValue getter, @Nullable ValueModifier valueModifier) { + protected ObjectPropertyBase(@NotNull String name, @Nullable Value value, @Nullable FunctionValue getter, @Nullable FunctionValue setter, @Nullable ValueModifier valueModifier) { super(name, value, valueModifier); this.getter = getter; + this.setter = setter; } @Override @@ -31,7 +31,7 @@ public abstract class ObjectPropertyBase extends VariableImpl implements ObjectP @Nullable @Override public FunctionValue getSetter() { - return null; + return setter; } @Override @@ -43,10 +43,4 @@ public abstract class ObjectPropertyBase extends VariableImpl implements ObjectP public boolean isEnumerable() { return true; } - - @NotNull - @Override - public AsyncResult evaluateGet(@NotNull EvaluateContext evaluateContext) { - throw new UnsupportedOperationException(); - } } \ No newline at end of file diff --git a/platform/script-debugger/debugger-ui/src/org/jetbrains/debugger/DebuggerViewSupport.java b/platform/script-debugger/debugger-ui/src/org/jetbrains/debugger/DebuggerViewSupport.java index 3331155243b4..95e4a208594b 100644 --- a/platform/script-debugger/debugger-ui/src/org/jetbrains/debugger/DebuggerViewSupport.java +++ b/platform/script-debugger/debugger-ui/src/org/jetbrains/debugger/DebuggerViewSupport.java @@ -72,24 +72,7 @@ public interface DebuggerViewSupport extends MemberFilter { @NotNull @Override public String propertyNamesToString(@NotNull List list, boolean quotedAware) { - // todo - StringBuilder builder = new StringBuilder(); - for (int i = list.size() - 1; i >= 0; i--) { - String name = list.get(i); - boolean quoted = quotedAware && (name.charAt(0) == '"' || name.charAt(0) == '\''); - boolean useKeyNotation = !quoted; - if (builder.length() != 0) { - builder.append(useKeyNotation ? '.' : '['); - } - if (useKeyNotation) { - builder.append(name); - } - else { - builder.append(name); - builder.append(']'); - } - } - return builder.toString(); + return ValueModifierUtil.propertyNamesToString(list, quotedAware); } @Override diff --git a/platform/script-debugger/debugger-ui/src/org/jetbrains/debugger/VariableView.java b/platform/script-debugger/debugger-ui/src/org/jetbrains/debugger/VariableView.java index d198296d0cea..82c6b747df3c 100644 --- a/platform/script-debugger/debugger-ui/src/org/jetbrains/debugger/VariableView.java +++ b/platform/script-debugger/debugger-ui/src/org/jetbrains/debugger/VariableView.java @@ -137,7 +137,9 @@ public final class VariableView extends XNamedValue implements VariableContext { public void computePresentation(@NotNull XValueNode node, @NotNull XValuePlace place) { value = variable.getValue(); if (value == null) { - ObsolescentAsyncResults.consume(((ObjectProperty)variable).evaluateGet(context.getEvaluateContext()), node, new PairConsumer() { + ObjectValue host = (ObjectValue)((VariableView)context).getValue(); + assert host != null; + ObsolescentAsyncResults.consume(((ObjectProperty)variable).evaluateGet(host, getEvaluateContext()), node, new PairConsumer() { @Override public void consume(Value value, XValueNode node) { VariableView.this.value = value;