Add TypedData instance kinds.

R=johnmccutchan@google.com

Review URL: https://codereview.chromium.org//1157003003.
This commit is contained in:
Ryan Macnak 2015-06-08 17:30:11 -07:00
parent dfd7844d2c
commit 304382c1b4
11 changed files with 449 additions and 5 deletions

View file

@ -0,0 +1,69 @@
// Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
library base64;
import 'dart:typed_data';
const _decodeTable =
const [null, null, null, null, null, null, null, null,
null, null, null, null, null, null, null, null,
null, null, null, null, null, null, null, null,
null, null, null, null, null, null, null, null,
null, null, null, null, null, null, null, null,
null, null, null, 62, null, null, null, 63,
52, 53, 54, 55, 56, 57, 58, 59,
60, 61, null, null, null, 0, null, null,
null, 0, 1, 2, 3, 4, 5, 6,
7, 8, 9, 10, 11, 12, 13, 14,
15, 16, 17, 18, 19, 20, 21, 22,
23, 24, 25, null, null, null, null, null,
null, 26, 27, 28, 29, 30, 31, 32,
33, 34, 35, 36, 37, 38, 39, 40,
41, 42, 43, 44, 45, 46, 47, 48,
49, 50, 51];
Uint8List decodeBase64(String s) {
if (s.length % 4 != 0) throw "Malformed Base64: $s";
var odd_bits = 0;
if (s[s.length - 1] == '=') {
if (s[s.length - 2] == '=') {
odd_bits = 2;
} else {
odd_bits = 1;
}
}
var decodedByteLength = s.length ~/ 4 * 3 - odd_bits;
var result = new Uint8List(decodedByteLength);
var limit = s.length;
if (odd_bits != 0) {
limit = limit - 4;
}
var i = 0, j = 0;
while (i < limit) {
var triple = _decodeTable[s.codeUnitAt(i++)];
triple = (triple << 6) | _decodeTable[s.codeUnitAt(i++)];
triple = (triple << 6) | _decodeTable[s.codeUnitAt(i++)];
triple = (triple << 6) | _decodeTable[s.codeUnitAt(i++)];
result[j++] = triple >> 16;
result[j++] = (triple >> 8) & 255;
result[j++] = triple & 255;
}
if (odd_bits != 0) {
var triple = _decodeTable[s.codeUnitAt(i++)];
triple = (triple << 6) | _decodeTable[s.codeUnitAt(i++)];
triple = (triple << 6) | _decodeTable[s.codeUnitAt(i++)];
triple = (triple << 6) | _decodeTable[s.codeUnitAt(i++)];
result[j++] = triple >> 16;
if (odd_bits == 1) {
result[j++] = (triple >> 8) & 255;
}
}
assert(j == decodedByteLength);
return result;
}

View file

@ -12,6 +12,7 @@ import 'package:logging/logging.dart';
import 'package:observatory/cpu_profile.dart';
import 'package:observatory/object_graph.dart';
import 'package:observatory/tracer.dart';
import 'package:observatory/base64.dart';
import 'package:observe/observe.dart';
part 'src/service/object.dart';

View file

@ -95,6 +95,20 @@
</curly-block>
</template>
<template if="{{ ref.isTypedData }}">
<a on-click="{{ goto }}" _href="{{ url }}"><em>{{ ref.clazz.name }}</em> ({{ ref.length }})</a>
<curly-block callback="{{ expander() }}">
<div class="memberList">
<template repeat="{{ index in ref.typedElements.asMap().keys }}">
<div class="memberItem">
<div class="memberName">[{{ index }}]</div>
<div class="memberValue">{{ ref.typedElements[index].toString() }}</div>
</div>
</template>
</div>
</curly-block>
</template>
<template if="{{ ref.isMirrorReference }}">
<a on-click="{{ goto }}" _href="{{ url }}"><em>{{ ref.clazz.name }}</em></a>
<curly-block callback="{{ expander() }}">

View file

@ -199,6 +199,20 @@
</curly-block><br><br>
</template>
<template if="{{ instance.typedElements.isNotEmpty }}">
elements ({{ instance.typedElements.length }})
<curly-block expand="{{ instance.typedElements.length <= 100 }}">
<div class="memberList">
<template repeat="{{ index in instance.typedElements.asMap().keys }}">
<div class="memberItem">
<div class="memberName">[{{ index }}]</div>
<div class="memberValue">{{ instance.typedElements[index].toString() }}</div>
</div>
</template>
</div>
</curly-block><br><br>
</template>
</div>
</template>

View file

@ -117,6 +117,7 @@ abstract class ServiceObject extends Observable {
bool get isInt => false;
bool get isList => false;
bool get isMap => false;
bool get isTypedData => false;
bool get isMirrorReference => false;
bool get isWeakProperty => false;
bool get isClosure => false;
@ -1854,13 +1855,14 @@ class Instance extends ServiceObject {
@observable ServiceFunction function; // If a closure.
@observable Context context; // If a closure.
@observable String name; // If a Type.
@observable int length; // If a List or Map.
@observable int length; // If a List, Map or TypedData.
@observable var typeClass;
@observable var fields;
@observable var nativeFields;
@observable var elements; // If a List.
@observable var associations; // If a Map.
@observable var typedElements; // If a TypedData.
@observable var referent; // If a MirrorReference.
@observable Instance key; // If a WeakProperty.
@observable Instance value; // If a WeakProperty.
@ -1876,6 +1878,22 @@ class Instance extends ServiceObject {
bool get isInt => kind == 'Int';
bool get isList => kind == 'List';
bool get isMap => kind == 'Map';
bool get isTypedData {
return kind == 'Uint8ClampedList'
|| kind == 'Uint8List'
|| kind == 'Uint16List'
|| kind == 'Uint32List'
|| kind == 'Uint64List'
|| kind == 'Int8List'
|| kind == 'Int16List'
|| kind == 'Int32List'
|| kind == 'Int64List'
|| kind == 'Float32List'
|| kind == 'Float64List'
|| kind == 'Int32x4List'
|| kind == 'Float32x4List'
|| kind == 'Float64x2List';
}
bool get isMirrorReference => kind == 'MirrorReference';
bool get isWeakProperty => kind == 'WeakProperty';
bool get isClosure => kind == 'Closure';
@ -1909,6 +1927,39 @@ class Instance extends ServiceObject {
fields = map['fields'];
elements = map['elements'];
associations = map['associations'];
if (map['bytes'] != null) {
var bytes = decodeBase64(map['bytes']);
switch (map['kind']) {
case "Uint8ClampedList":
typedElements = bytes.buffer.asUint8ClampedList(); break;
case "Uint8List":
typedElements = bytes.buffer.asUint8List(); break;
case "Uint16List":
typedElements = bytes.buffer.asUint16List(); break;
case "Uint32List":
typedElements = bytes.buffer.asUint32List(); break;
case "Uint64List":
typedElements = bytes.buffer.asUint64List(); break;
case "Int8List":
typedElements = bytes.buffer.asInt8List(); break;
case "Int16List":
typedElements = bytes.buffer.asInt16List(); break;
case "Int32List":
typedElements = bytes.buffer.asInt32List(); break;
case "Int64List":
typedElements = bytes.buffer.asInt64List(); break;
case "Float32List":
typedElements = bytes.buffer.asFloat32List(); break;
case "Float64List":
typedElements = bytes.buffer.asFloat64List(); break;
case "Int32x4List":
typedElements = bytes.buffer.asInt32x4List(); break;
case "Float32x4List":
typedElements = bytes.buffer.asFloat32x4List(); break;
case "Float64x2List":
typedElements = bytes.buffer.asFloat64x2List(); break;
}
}
typeClass = map['typeClass'];
referent = map['mirrorReferent'];
key = map['propertyKey'];
@ -2101,7 +2152,7 @@ class Field extends ServiceObject {
@observable String vmName;
@observable bool guardNullable;
@observable String guardClass;
@observable var /* Class | String */ guardClass;
@observable String guardLength;
@observable SourceLocation location;

View file

@ -0,0 +1,139 @@
// Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
// VMOptions=--compile-all --error_on_bad_type --error_on_bad_override
library typed_data_test;
import 'dart:typed_data';
import 'package:observatory/service_io.dart';
import 'package:unittest/unittest.dart';
import 'test_helper.dart';
var int8List;
var int16List;
var int32List;
var int64List;
var uint8List;
var uint16List;
var uint32List;
var uint64List;
var uint8ClampedList;
var float32List;
var float64List;
var int32x4;
var float32x4;
var float64x2;
var int32x4List;
var float32x4List;
var float64x2List;
void script() {
int8List = new Int8List(2);
int8List[0] = -1;
int8List[1] = -2;
int16List = new Int16List(2);
int16List[0] = -3;
int16List[1] = -4;
int32List = new Int32List(2);
int32List[0] = -5;
int32List[1] = -6;
int64List = new Int64List(2);
int64List[0] = -7;
int64List[1] = -8;
uint8List = new Uint8List(2);
uint8List[0] = 1;
uint8List[1] = 2;
uint16List = new Uint16List(2);
uint16List[0] = 3;
uint16List[1] = 4;
uint32List = new Uint32List(2);
uint32List[0] = 5;
uint32List[1] = 6;
uint64List = new Uint64List(2);
uint64List[0] = 7;
uint64List[1] = 8;
uint8ClampedList = new Uint8ClampedList(2);
uint8ClampedList[0] = 9;
uint8ClampedList[1] = 10;
float32List = new Float32List(2);
float32List[0] = 4.25;
float32List[1] = 8.50;
float64List = new Float64List(2);
float64List[0] = 16.25;
float64List[1] = 32.50;
int32x4 = new Int32x4(1, 2, 3, 4);
float32x4 = new Float32x4(1.0, 2.0, 4.0, 8.0);
float64x2 = new Float64x2(16.0, 32.0);
int32x4List = new Int32x4List(2);
float32x4List = new Float32x4List(2);
float64x2List = new Float64x2List(2);
}
var tests = [
(Isolate isolate) async {
script();
var lib = await isolate.rootLibrary.load();
// Pre-load all the fields so we don't use await below and get better
// stacktraces.
for (var v in lib.variables) {
await v.load();
await v.staticValue.load();
}
expectTypedData(name, expectedValue) {
var variable = lib.variables.singleWhere((v) => v.name == name);
var actualValue = variable.staticValue.typedElements;
if (expectedValue is Int32x4List) {
expect(actualValue.length, equals(expectedValue.length));
for (var i = 0; i < actualValue.length; i++) {
expect(actualValue[i].x, equals(expectedValue[i].x));
expect(actualValue[i].y, equals(expectedValue[i].y));
expect(actualValue[i].z, equals(expectedValue[i].z));
expect(actualValue[i].w, equals(expectedValue[i].w));
}
} else if (expectedValue is Float32x4List) {
expect(actualValue.length, equals(expectedValue.length));
for (var i = 0; i < actualValue.length; i++) {
expect(actualValue[i].x, equals(expectedValue[i].x));
expect(actualValue[i].y, equals(expectedValue[i].y));
expect(actualValue[i].z, equals(expectedValue[i].z));
expect(actualValue[i].w, equals(expectedValue[i].w));
}
} else if (expectedValue is Float64x2List) {
expect(actualValue.length, equals(expectedValue.length));
for (var i = 0; i < actualValue.length; i++) {
expect(actualValue[i].x, equals(expectedValue[i].x));
expect(actualValue[i].y, equals(expectedValue[i].y));
}
} else {
expect(actualValue, equals(expectedValue));
}
}
expectTypedData("int8List", int8List);
expectTypedData("int16List", int16List);
expectTypedData("int32List", int32List);
expectTypedData("int64List", int64List);
expectTypedData("uint8List", uint8List);
expectTypedData("uint16List", uint16List);
expectTypedData("uint32List", uint32List);
expectTypedData("uint64List", uint64List);
expectTypedData("uint8ClampedList", uint8ClampedList);
expectTypedData("float32List", float32List);
expectTypedData("float64List", float64List);
expectTypedData("int32x4List", int32x4List);
expectTypedData("float32x4List", float32x4List);
expectTypedData("float64x2List", float64x2List);
},
];
main(args) => runIsolateTests(args, tests, testeeBefore: script);

View file

@ -277,6 +277,42 @@ void JSONStream::PrintValue(double d) {
}
static const char base64_digits[65] =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
static const char base64_pad = '=';
void JSONStream::PrintValueBase64(const uint8_t* bytes, intptr_t length) {
PrintCommaIfNeeded();
buffer_.AddChar('"');
intptr_t odd_bits = length % 3;
intptr_t even_bits = length - odd_bits;
for (intptr_t i = 0; i < even_bits; i += 3) {
intptr_t triplet = (bytes[i] << 16) | (bytes[i + 1] << 8) | bytes[i + 2];
buffer_.AddChar(base64_digits[triplet >> 18]);
buffer_.AddChar(base64_digits[(triplet >> 12) & 63]);
buffer_.AddChar(base64_digits[(triplet >> 6) & 63]);
buffer_.AddChar(base64_digits[triplet & 63]);
}
if (odd_bits == 1) {
intptr_t triplet = bytes[even_bits] << 16;
buffer_.AddChar(base64_digits[triplet >> 18]);
buffer_.AddChar(base64_digits[(triplet >> 12) & 63]);
buffer_.AddChar(base64_pad);
buffer_.AddChar(base64_pad);
} else if (odd_bits == 2) {
intptr_t triplet = (bytes[even_bits] << 16) | (bytes[even_bits + 1] << 8);
buffer_.AddChar(base64_digits[triplet >> 18]);
buffer_.AddChar(base64_digits[(triplet >> 12) & 63]);
buffer_.AddChar(base64_digits[(triplet >> 6) & 63]);
buffer_.AddChar(base64_pad);
}
buffer_.AddChar('"');
}
void JSONStream::PrintValue(const char* s) {
PrintCommaIfNeeded();
buffer_.AddChar('"');
@ -393,6 +429,14 @@ void JSONStream::PrintProperty(const char* name, const char* s) {
}
void JSONStream::PrintPropertyBase64(const char* name,
const uint8_t* b,
intptr_t len) {
PrintPropertyName(name);
PrintValueBase64(b, len);
}
bool JSONStream::PrintPropertyStr(const char* name,
const String& s,
intptr_t limit) {

View file

@ -116,6 +116,7 @@ class JSONStream : ValueObject {
void PrintValue(intptr_t i);
void PrintValue64(int64_t i);
void PrintValue(double d);
void PrintValueBase64(const uint8_t* bytes, intptr_t length);
void PrintValue(const char* s);
void PrintValue(const char* s, intptr_t len);
void PrintValueNoEscape(const char* s);
@ -133,6 +134,9 @@ class JSONStream : ValueObject {
void PrintProperty(const char* name, intptr_t i);
void PrintProperty64(const char* name, int64_t i);
void PrintProperty(const char* name, double d);
void PrintPropertyBase64(const char* name,
const uint8_t* bytes,
intptr_t length);
void PrintProperty(const char* name, const char* s);
bool PrintPropertyStr(const char* name, const String& s, intptr_t limit);
void PrintPropertyNoEscape(const char* name, const char* s);
@ -209,6 +213,11 @@ class JSONObject : public ValueObject {
void AddProperty(const char* name, double d) const {
stream_->PrintProperty(name, d);
}
void AddPropertyBase64(const char* name,
const uint8_t* bytes,
intptr_t length) const {
stream_->PrintPropertyBase64(name, bytes, length);
}
void AddProperty(const char* name, const char* s) const {
stream_->PrintProperty(name, s);
}

View file

@ -3301,9 +3301,15 @@ RawString* Class::GenerateUserVisibleName() const {
case kTypedDataUint64ArrayCid:
case kExternalTypedDataUint64ArrayCid:
return Symbols::Uint64List().raw();
case kTypedDataInt32x4ArrayCid:
case kExternalTypedDataInt32x4ArrayCid:
return Symbols::Int32x4List().raw();
case kTypedDataFloat32x4ArrayCid:
case kExternalTypedDataFloat32x4ArrayCid:
return Symbols::Float32x4List().raw();
case kTypedDataFloat64x2ArrayCid:
case kExternalTypedDataFloat64x2ArrayCid:
return Symbols::Float64x2List().raw();
case kTypedDataFloat32ArrayCid:
case kExternalTypedDataFloat32ArrayCid:
return Symbols::Float32List().raw();
@ -20108,7 +20114,23 @@ const char* TypedData::ToCString() const {
void TypedData::PrintJSONImpl(JSONStream* stream, bool ref) const {
Instance::PrintJSONImpl(stream, ref);
JSONObject jsobj(stream);
PrintSharedInstanceJSON(&jsobj, ref);
const Class& cls = Class::Handle(clazz());
const String& kind = String::Handle(cls.UserVisibleName());
jsobj.AddProperty("kind", kind.ToCString());
jsobj.AddServiceId(*this);
jsobj.AddProperty("length", Length());
if (ref) {
return;
}
{
NoSafepointScope no_safepoint;
jsobj.AddPropertyBase64("bytes",
reinterpret_cast<const uint8_t*>(DataAddr(0)),
LengthInBytes());
}
}
@ -20143,7 +20165,23 @@ const char* ExternalTypedData::ToCString() const {
void ExternalTypedData::PrintJSONImpl(JSONStream* stream,
bool ref) const {
Instance::PrintJSONImpl(stream, ref);
JSONObject jsobj(stream);
PrintSharedInstanceJSON(&jsobj, ref);
const Class& cls = Class::Handle(clazz());
const String& kind = String::Handle(cls.UserVisibleName());
jsobj.AddProperty("kind", kind.ToCString());
jsobj.AddServiceId(*this);
jsobj.AddProperty("length", Length());
if (ref) {
return;
}
{
NoSafepointScope no_safepoint;
jsobj.AddPropertyBase64("bytes",
reinterpret_cast<const uint8_t*>(DataAddr(0)),
LengthInBytes());
}
}

View file

@ -6803,7 +6803,6 @@ class TypedData : public Instance {
return ElementSizeInBytes(cid);
}
TypedDataElementType ElementType() const {
intptr_t cid = raw()->GetClassId();
return ElementType(cid);

View file

@ -1229,6 +1229,21 @@ class @Instance extends @Object {
//
// Provided for instance kinds:
// List
// Map
// Uint8ClampedList
// Uint8List
// Uint16List
// Uint32List
// Uint64List
// Int8List
// Int16List
// Int32List
// Int64List
// Float32List
// Float64List
// Int32x4List
// Float32x4List
// Float64x2List
int length [optional];
// The name of a Type instance.
@ -1278,6 +1293,21 @@ class Instance extends Object {
//
// Provided for instance kinds:
// List
// Map
// Uint8ClampedList
// Uint8List
// Uint16List
// Uint32List
// Uint64List
// Int8List
// Int16List
// Int32List
// Int64List
// Float32List
// Float64List
// Int32x4List
// Float32x4List
// Float64x2List
int length [optional];
// The name of a Type instance.
@ -1313,6 +1343,25 @@ class Instance extends Object {
// Map
MapAssociation[] associations [optional];
// The bytes of a TypedData instance.
//
// Provided for instance kinds:
// Uint8ClampedList
// Uint8List
// Uint16List
// Uint32List
// Uint64List
// Int8List
// Int16List
// Int32List
// Int64List
// Float32List
// Float64List
// Int32x4List
// Float32x4List
// Float64x2List
int[] bytes [optional];
// The function associated with a Closure instance.
//
// Provided for instance kinds:
@ -1411,6 +1460,23 @@ enum {
// Maps will be PlainInstance.
Map,
// An instance of the built-in VM TypedData implementations. User-defined
// TypedDatas will be PlainInstance.
Uint8ClampedList,
Uint8List,
Uint16List,
Uint32List,
Uint64List,
Int8List,
Int16List,
Int32List,
Int64List,
Float32List,
Float64List,
Int32x4List,
Float32x4List,
Float64x2List,
// An instance of the built-in VM Closure implementation. User-defined
// Closures will be PlainInstance.
Closure,