// Copyright (c) 2011 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. package org.chromium.sdk.internal.v8native.value; import org.chromium.sdk.JsValue; import org.chromium.sdk.JsValue.Type; import org.chromium.sdk.internal.v8native.V8Helper; import org.chromium.sdk.internal.v8native.protocol.input.data.FunctionValueHandle; import org.chromium.sdk.internal.v8native.protocol.input.data.ObjectValueHandle; import org.chromium.sdk.internal.v8native.protocol.input.data.RefWithDisplayData; import org.chromium.sdk.internal.v8native.protocol.input.data.ValueHandle; import org.chromium.sdk.internal.v8native.value.LoadableString.Factory; /** * A representation of a datum (value) in the remote JavaScript VM. The class must be immutable. * When additional (or more recent) data arrives, new instance should be put into * {@link ValueLoader} map. */ public abstract class ValueMirror { /** * Merges to {@link ValueMirror}s into one. Since {@link ValueMirror} is immutable * this is the right way to gather data. * @return possibly new {@link ValueMirror}, or any of the old ones, making a preference for * 'base' parameter. */ static ValueMirror merge(ValueMirror base, ValueMirror alternative) { if (base.hasProperties()) { if (alternative.hasProperties()) { // Fall through. } else { return base; } } else { if (alternative.hasProperties()) { return alternative; } else { // Fall through. } } int lenDiff = base.getStringLength() - alternative.getStringLength(); if (lenDiff < 0) { return alternative; } else { return base; } } /** * Constructs a {@link ValueMirror} given a V8 debugger object specification if it's possible. */ public static ValueMirror create(final RefWithDisplayData refWithDisplayData, Factory loadableStringFactory) { Long ref = refWithDisplayData.ref(); final Type type = V8Helper.calculateType(refWithDisplayData.type(), refWithDisplayData.className(), false); return new ValueMirror(ref) { @Override public Type getType() { return type; } @Override public String getClassName() { return refWithDisplayData.className(); } @Override public LoadableString getStringValue() { // try another format Object valueObj = refWithDisplayData.value(); String valueStr; if (valueObj == null) { valueStr = refWithDisplayData.type(); // e.g. "undefined" } else { valueStr = valueObj.toString(); } return new LoadableString.Immutable(valueStr); } @Override public boolean hasProperties() { return false; } @Override public SubpropertiesMirror getProperties() { return null; } }; } /** * Constructs a ValueMirror given a V8 debugger object specification. * @param valueHandle containing the object specification from the V8 debugger */ public static ValueMirror create(final ValueHandle valueHandle, final Factory factory) { Long ref = valueHandle.handle(); final Type type = V8Helper.calculateType(valueHandle.type(), valueHandle.className(), true); return new ValueMirror(ref) { @Override public Type getType() { return type; } @Override public LoadableString getStringValue() { return V8Helper.createLoadableString(valueHandle, factory); } @Override public SubpropertiesMirror getProperties() { ObjectValueHandle objectValueHandle = valueHandle.asObject(); if (objectValueHandle == null) { return SubpropertiesMirror.EMPTY; } int refId = (int) valueHandle.handle(); SubpropertiesMirror subpropertiesMirror; if (type == Type.TYPE_FUNCTION) { FunctionValueHandle functionValueHandle = objectValueHandle.asFunction(); subpropertiesMirror = new SubpropertiesMirror.FunctionValueBased(functionValueHandle); } else { subpropertiesMirror = new SubpropertiesMirror.ObjectValueBased(objectValueHandle); } return subpropertiesMirror; } @Override public boolean hasProperties() { return true; } @Override public String getClassName() { return valueHandle.className(); } }; } public static ValueMirror create(Long ref, final Type type, final String className, final LoadableString loadableString, final SubpropertiesMirror subpropertiesMirror) { return new ValueMirror(ref) { @Override public JsValue.Type getType() { return type; } @Override public String getClassName() { return className; } @Override public LoadableString getStringValue() { return loadableString; } @Override public SubpropertiesMirror getProperties() { return subpropertiesMirror; } @Override public boolean hasProperties() { return getProperties() != null; } }; } private final Long ref; protected ValueMirror(Long ref) { assert ref != null; this.ref = ref; } public abstract JsValue.Type getType(); public abstract SubpropertiesMirror getProperties(); public Long getRef() { return ref; } public abstract LoadableString getStringValue(); public abstract String getClassName(); public abstract boolean hasProperties(); int getStringLength() { LoadableString loadableString = getStringValue(); if (loadableString == null) { return 0; } return loadableString.getCurrentString().length(); } }