Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(176)

Side by Side Diff: runtime/lib/typed_data.cc

Issue 24239003: Fix range checks for typed data in native code and the optimizer. (Closed) Base URL: http://dart.googlecode.com/svn/branches/bleeding_edge/dart/
Patch Set: Created 7 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « no previous file | runtime/vm/flow_graph_optimizer.cc » ('j') | runtime/vm/flow_graph_optimizer.cc » ('J')
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file 1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
2 // for details. All rights reserved. Use of this source code is governed by a 2 // for details. All rights reserved. Use of this source code is governed by a
3 // BSD-style license that can be found in the LICENSE file. 3 // BSD-style license that can be found in the LICENSE file.
4 4
5 #include "vm/bootstrap_natives.h" 5 #include "vm/bootstrap_natives.h"
6 6
7 #include "include/dart_api.h" 7 #include "include/dart_api.h"
8 8
9 #include "vm/bigint_operations.h" 9 #include "vm/bigint_operations.h"
10 #include "vm/exceptions.h" 10 #include "vm/exceptions.h"
11 #include "vm/native_entry.h" 11 #include "vm/native_entry.h"
12 #include "vm/object.h" 12 #include "vm/object.h"
13 13
14 namespace dart { 14 namespace dart {
15 15
16 // TypedData. 16 // TypedData.
17 17
18 // Checks to see if offset_in_bytes is in the range.
19 static bool RangeCheck(intptr_t offset_in_bytes, intptr_t length_in_bytes) {
20 return ((offset_in_bytes >= 0) &&
21 (length_in_bytes > 0) &&
22 (offset_in_bytes < length_in_bytes));
23 }
24
25
26 // Checks to see if offsetInBytes + num_bytes is in the range. 18 // Checks to see if offsetInBytes + num_bytes is in the range.
27 static void SetRangeCheck(intptr_t offset_in_bytes, 19 static void RangeCheck(intptr_t offset_in_bytes,
28 intptr_t num_bytes, 20 intptr_t num_bytes,
29 intptr_t length_in_bytes, 21 intptr_t length_in_bytes,
30 intptr_t element_size_in_bytes) { 22 intptr_t element_size_in_bytes) {
siva 2013/09/20 22:03:45 indentation For a little more readability I am wo
Florian Schneider 2013/09/23 11:02:26 Done.
31 if (!Utils::RangeCheck(offset_in_bytes, num_bytes, length_in_bytes)) { 23 if (!Utils::RangeCheck(offset_in_bytes, num_bytes, length_in_bytes)) {
32 const String& error = String::Handle(String::NewFormatted( 24 const String& error = String::Handle(String::NewFormatted(
33 "index (%" Pd ") must be in the range [0..%" Pd ")", 25 "index (%" Pd ") must be in the range [0..%" Pd ")",
34 (offset_in_bytes / element_size_in_bytes), 26 (offset_in_bytes / element_size_in_bytes) + num_bytes,
siva 2013/09/20 22:03:45 I think this should be ((offset_in_bytes + num_byt
Florian Schneider 2013/09/23 11:02:26 Done.
35 (length_in_bytes / element_size_in_bytes))); 27 (length_in_bytes / element_size_in_bytes)));
36 const Array& args = Array::Handle(Array::New(1)); 28 const Array& args = Array::Handle(Array::New(1));
37 args.SetAt(0, error); 29 args.SetAt(0, error);
38 Exceptions::ThrowByType(Exceptions::kRange, args); 30 Exceptions::ThrowByType(Exceptions::kRange, args);
39 } 31 }
40 } 32 }
41 33
42 34
43 // Checks to see if a length will not result in an OOM error. 35 // Checks to see if a length will not result in an OOM error.
44 static void LengthCheck(intptr_t len, intptr_t max) { 36 static void LengthCheck(intptr_t len, intptr_t max) {
(...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after
83 const Smi& length) { 75 const Smi& length) {
84 const DstType& dst_array = DstType::Cast(dst); 76 const DstType& dst_array = DstType::Cast(dst);
85 const SrcType& src_array = SrcType::Cast(src); 77 const SrcType& src_array = SrcType::Cast(src);
86 intptr_t element_size_in_bytes = dst_array.ElementSizeInBytes(); 78 intptr_t element_size_in_bytes = dst_array.ElementSizeInBytes();
87 intptr_t dst_offset_in_bytes = dst_start.Value() * element_size_in_bytes; 79 intptr_t dst_offset_in_bytes = dst_start.Value() * element_size_in_bytes;
88 intptr_t src_offset_in_bytes = src_start.Value() * element_size_in_bytes; 80 intptr_t src_offset_in_bytes = src_start.Value() * element_size_in_bytes;
89 intptr_t length_in_bytes = length.Value() * element_size_in_bytes; 81 intptr_t length_in_bytes = length.Value() * element_size_in_bytes;
90 if (dst_array.ElementType() != src_array.ElementType()) { 82 if (dst_array.ElementType() != src_array.ElementType()) {
91 return Bool::False().raw(); 83 return Bool::False().raw();
92 } 84 }
93 SetRangeCheck(src_offset_in_bytes, 85 RangeCheck(src_offset_in_bytes,
94 length_in_bytes, 86 length_in_bytes,
95 src_array.LengthInBytes(), 87 src_array.LengthInBytes(),
96 element_size_in_bytes); 88 element_size_in_bytes);
siva 2013/09/20 22:03:45 indentation
Florian Schneider 2013/09/23 11:02:26 Done.
97 SetRangeCheck(dst_offset_in_bytes, 89 RangeCheck(dst_offset_in_bytes,
98 length_in_bytes, 90 length_in_bytes,
99 dst_array.LengthInBytes(), 91 dst_array.LengthInBytes(),
100 element_size_in_bytes); 92 element_size_in_bytes);
siva 2013/09/20 22:03:45 indentation
Florian Schneider 2013/09/23 11:02:26 Done.
101 TypedData::Copy<DstType, SrcType>(dst_array, dst_offset_in_bytes, 93 TypedData::Copy<DstType, SrcType>(dst_array, dst_offset_in_bytes,
102 src_array, src_offset_in_bytes, 94 src_array, src_offset_in_bytes,
103 length_in_bytes); 95 length_in_bytes);
104 return Bool::True().raw(); 96 return Bool::True().raw();
105 } 97 }
106 98
107 99
108 DEFINE_NATIVE_ENTRY(TypedData_setRange, 5) { 100 DEFINE_NATIVE_ENTRY(TypedData_setRange, 5) {
109 GET_NON_NULL_NATIVE_ARGUMENT(Instance, dst, arguments->NativeArgAt(0)); 101 GET_NON_NULL_NATIVE_ARGUMENT(Instance, dst, arguments->NativeArgAt(0));
110 GET_NON_NULL_NATIVE_ARGUMENT(Smi, dst_start, arguments->NativeArgAt(1)); 102 GET_NON_NULL_NATIVE_ARGUMENT(Smi, dst_start, arguments->NativeArgAt(1));
(...skipping 65 matching lines...) Expand 10 before | Expand all | Expand 10 after
176 } \ 168 } \
177 169
178 170
179 #define TYPED_DATA_NEW_NATIVE(name) \ 171 #define TYPED_DATA_NEW_NATIVE(name) \
180 TYPED_DATA_NEW(name) \ 172 TYPED_DATA_NEW(name) \
181 EXT_TYPED_DATA_NEW(name) \ 173 EXT_TYPED_DATA_NEW(name) \
182 174
183 175
184 CLASS_LIST_TYPED_DATA(TYPED_DATA_NEW_NATIVE) 176 CLASS_LIST_TYPED_DATA(TYPED_DATA_NEW_NATIVE)
185 177
186 #define TYPED_DATA_GETTER(getter, object) \ 178 #define TYPED_DATA_GETTER(getter, object, num_bytes) \
187 DEFINE_NATIVE_ENTRY(TypedData_##getter, 2) { \ 179 DEFINE_NATIVE_ENTRY(TypedData_##getter, 2) { \
188 GET_NON_NULL_NATIVE_ARGUMENT(Instance, instance, arguments->NativeArgAt(0)); \ 180 GET_NON_NULL_NATIVE_ARGUMENT(Instance, instance, arguments->NativeArgAt(0)); \
189 GET_NON_NULL_NATIVE_ARGUMENT(Smi, offsetInBytes, arguments->NativeArgAt(1)); \ 181 GET_NON_NULL_NATIVE_ARGUMENT(Smi, offsetInBytes, arguments->NativeArgAt(1)); \
190 if (instance.IsTypedData()) { \ 182 if (instance.IsTypedData()) { \
191 const TypedData& array = TypedData::Cast(instance); \ 183 const TypedData& array = TypedData::Cast(instance); \
192 ASSERT(RangeCheck(offsetInBytes.Value(), array.LengthInBytes())); \ 184 RangeCheck(offsetInBytes.Value(), num_bytes, array.LengthInBytes(), 1); \
193 return object::New(array.getter(offsetInBytes.Value())); \ 185 return object::New(array.getter(offsetInBytes.Value())); \
194 } \ 186 } \
195 if (instance.IsExternalTypedData()) { \ 187 if (instance.IsExternalTypedData()) { \
196 const ExternalTypedData& array = ExternalTypedData::Cast(instance); \ 188 const ExternalTypedData& array = ExternalTypedData::Cast(instance); \
197 ASSERT(RangeCheck(offsetInBytes.Value(), array.LengthInBytes())); \ 189 RangeCheck(offsetInBytes.Value(), num_bytes, array.LengthInBytes(), 1); \
198 return object::New(array.getter(offsetInBytes.Value())); \ 190 return object::New(array.getter(offsetInBytes.Value())); \
199 } \ 191 } \
200 const String& error = String::Handle(String::NewFormatted( \ 192 const String& error = String::Handle(String::NewFormatted( \
201 "Expected a TypedData object but found %s", instance.ToCString())); \ 193 "Expected a TypedData object but found %s", instance.ToCString())); \
202 const Array& args = Array::Handle(Array::New(1)); \ 194 const Array& args = Array::Handle(Array::New(1)); \
203 args.SetAt(0, error); \ 195 args.SetAt(0, error); \
204 Exceptions::ThrowByType(Exceptions::kArgument, args); \ 196 Exceptions::ThrowByType(Exceptions::kArgument, args); \
205 return object::null(); \ 197 return object::null(); \
206 } \ 198 } \
207 199
208 200
209 #define TYPED_DATA_SETTER(setter, object, get_object_value) \ 201 #define TYPED_DATA_SETTER(setter, object, get_object_value, num_bytes) \
210 DEFINE_NATIVE_ENTRY(TypedData_##setter, 3) { \ 202 DEFINE_NATIVE_ENTRY(TypedData_##setter, 3) { \
211 GET_NON_NULL_NATIVE_ARGUMENT(Instance, instance, arguments->NativeArgAt(0)); \ 203 GET_NON_NULL_NATIVE_ARGUMENT(Instance, instance, arguments->NativeArgAt(0)); \
212 GET_NON_NULL_NATIVE_ARGUMENT(Smi, offsetInBytes, arguments->NativeArgAt(1)); \ 204 GET_NON_NULL_NATIVE_ARGUMENT(Smi, offsetInBytes, arguments->NativeArgAt(1)); \
213 GET_NON_NULL_NATIVE_ARGUMENT(object, value, arguments->NativeArgAt(2)); \ 205 GET_NON_NULL_NATIVE_ARGUMENT(object, value, arguments->NativeArgAt(2)); \
siva 2013/09/20 22:03:45 maybe more readable to use elem_size_in_bytes inst
Florian Schneider 2013/09/23 11:02:26 Changed to access_size.
214 if (instance.IsTypedData()) { \ 206 if (instance.IsTypedData()) { \
215 const TypedData& array = TypedData::Cast(instance); \ 207 const TypedData& array = TypedData::Cast(instance); \
216 ASSERT(RangeCheck(offsetInBytes.Value(), array.LengthInBytes())); \ 208 RangeCheck(offsetInBytes.Value(), num_bytes, array.LengthInBytes(), 1); \
siva 2013/09/20 22:03:45 shouldn't this be num_bytes instead of '1' here?
Florian Schneider 2013/09/23 11:02:26 Done.
217 array.setter(offsetInBytes.Value(), value.get_object_value()); \ 209 array.setter(offsetInBytes.Value(), value.get_object_value()); \
218 } else if (instance.IsExternalTypedData()) { \ 210 } else if (instance.IsExternalTypedData()) { \
219 const ExternalTypedData& array = ExternalTypedData::Cast(instance); \ 211 const ExternalTypedData& array = ExternalTypedData::Cast(instance); \
220 ASSERT(RangeCheck(offsetInBytes.Value(), array.LengthInBytes())); \ 212 RangeCheck(offsetInBytes.Value(), num_bytes, array.LengthInBytes(), 1); \
221 array.setter(offsetInBytes.Value(), value.get_object_value()); \ 213 array.setter(offsetInBytes.Value(), value.get_object_value()); \
222 } else { \ 214 } else { \
223 const String& error = String::Handle(String::NewFormatted( \ 215 const String& error = String::Handle(String::NewFormatted( \
224 "Expected a TypedData object but found %s", instance.ToCString())); \ 216 "Expected a TypedData object but found %s", instance.ToCString())); \
225 const Array& args = Array::Handle(Array::New(1)); \ 217 const Array& args = Array::Handle(Array::New(1)); \
226 args.SetAt(0, error); \ 218 args.SetAt(0, error); \
227 Exceptions::ThrowByType(Exceptions::kArgument, args); \ 219 Exceptions::ThrowByType(Exceptions::kArgument, args); \
228 } \ 220 } \
229 return Object::null(); \ 221 return Object::null(); \
230 } 222 }
231 223
232 224
233 #define TYPED_DATA_UINT64_GETTER(getter, object) \ 225 #define TYPED_DATA_UINT64_GETTER(getter, object) \
234 DEFINE_NATIVE_ENTRY(TypedData_##getter, 2) { \ 226 DEFINE_NATIVE_ENTRY(TypedData_##getter, 2) { \
235 GET_NON_NULL_NATIVE_ARGUMENT(Instance, instance, arguments->NativeArgAt(0)); \ 227 GET_NON_NULL_NATIVE_ARGUMENT(Instance, instance, arguments->NativeArgAt(0)); \
236 GET_NON_NULL_NATIVE_ARGUMENT(Smi, offsetInBytes, arguments->NativeArgAt(1)); \ 228 GET_NON_NULL_NATIVE_ARGUMENT(Smi, offsetInBytes, arguments->NativeArgAt(1)); \
237 uint64_t value = 0; \ 229 uint64_t value = 0; \
238 if (instance.IsTypedData()) { \ 230 if (instance.IsTypedData()) { \
239 const TypedData& array = TypedData::Cast(instance); \ 231 const TypedData& array = TypedData::Cast(instance); \
240 ASSERT(RangeCheck(offsetInBytes.Value(), array.LengthInBytes())); \ 232 RangeCheck(offsetInBytes.Value(), 8, array.LengthInBytes(), 1); \
241 value = array.getter(offsetInBytes.Value()); \ 233 value = array.getter(offsetInBytes.Value()); \
242 } else if (instance.IsExternalTypedData()) { \ 234 } else if (instance.IsExternalTypedData()) { \
243 const ExternalTypedData& array = ExternalTypedData::Cast(instance); \ 235 const ExternalTypedData& array = ExternalTypedData::Cast(instance); \
244 ASSERT(RangeCheck(offsetInBytes.Value(), array.LengthInBytes())); \ 236 RangeCheck(offsetInBytes.Value(), 8, array.LengthInBytes(), 1); \
245 value = array.getter(offsetInBytes.Value()); \ 237 value = array.getter(offsetInBytes.Value()); \
246 } else { \ 238 } else { \
247 const String& error = String::Handle(String::NewFormatted( \ 239 const String& error = String::Handle(String::NewFormatted( \
248 "Expected a TypedData object but found %s", instance.ToCString())); \ 240 "Expected a TypedData object but found %s", instance.ToCString())); \
249 const Array& args = Array::Handle(Array::New(1)); \ 241 const Array& args = Array::Handle(Array::New(1)); \
250 args.SetAt(0, error); \ 242 args.SetAt(0, error); \
251 Exceptions::ThrowByType(Exceptions::kArgument, args); \ 243 Exceptions::ThrowByType(Exceptions::kArgument, args); \
252 } \ 244 } \
253 return Integer::NewFromUint64(value); \ 245 return Integer::NewFromUint64(value); \
254 } \ 246 } \
(...skipping 10 matching lines...) Expand all
265 if (value.IsBigint()) { \ 257 if (value.IsBigint()) { \
266 const Bigint& bigint = Bigint::Cast(value); \ 258 const Bigint& bigint = Bigint::Cast(value); \
267 ASSERT(BigintOperations::FitsIntoUint64(bigint)); \ 259 ASSERT(BigintOperations::FitsIntoUint64(bigint)); \
268 object_value = BigintOperations::AbsToUint64(bigint); \ 260 object_value = BigintOperations::AbsToUint64(bigint); \
269 } else { \ 261 } else { \
270 ASSERT(value.IsMint() || value.IsSmi()); \ 262 ASSERT(value.IsMint() || value.IsSmi()); \
271 object_value = value.AsInt64Value(); \ 263 object_value = value.AsInt64Value(); \
272 } \ 264 } \
273 if (instance.IsTypedData()) { \ 265 if (instance.IsTypedData()) { \
274 const TypedData& array = TypedData::Cast(instance); \ 266 const TypedData& array = TypedData::Cast(instance); \
275 ASSERT(RangeCheck(offsetInBytes.Value(), array.LengthInBytes())); \ 267 RangeCheck(offsetInBytes.Value(), 8, array.LengthInBytes(), 1); \
276 array.setter(offsetInBytes.Value(), object_value); \ 268 array.setter(offsetInBytes.Value(), object_value); \
277 } else if (instance.IsExternalTypedData()) { \ 269 } else if (instance.IsExternalTypedData()) { \
278 const ExternalTypedData& array = ExternalTypedData::Cast(instance); \ 270 const ExternalTypedData& array = ExternalTypedData::Cast(instance); \
279 ASSERT(RangeCheck(offsetInBytes.Value(), array.LengthInBytes())); \ 271 RangeCheck(offsetInBytes.Value(), 8, array.LengthInBytes(), 1); \
280 array.setter(offsetInBytes.Value(), object_value); \ 272 array.setter(offsetInBytes.Value(), object_value); \
281 } else { \ 273 } else { \
282 const String& error = String::Handle(String::NewFormatted( \ 274 const String& error = String::Handle(String::NewFormatted( \
283 "Expected a TypedData object but found %s", instance.ToCString())); \ 275 "Expected a TypedData object but found %s", instance.ToCString())); \
284 const Array& args = Array::Handle(Array::New(1)); \ 276 const Array& args = Array::Handle(Array::New(1)); \
285 args.SetAt(0, error); \ 277 args.SetAt(0, error); \
286 Exceptions::ThrowByType(Exceptions::kArgument, args); \ 278 Exceptions::ThrowByType(Exceptions::kArgument, args); \
287 } \ 279 } \
288 return Object::null(); \ 280 return Object::null(); \
289 } 281 }
290 282
291 283
292 #define TYPED_DATA_NATIVES(getter, setter, object, get_object_value) \ 284 #define TYPED_DATA_NATIVES(getter, setter, object, get_object_value, num_bytes)\
293 TYPED_DATA_GETTER(getter, object) \ 285 TYPED_DATA_GETTER(getter, object, num_bytes) \
294 TYPED_DATA_SETTER(setter, object, get_object_value) \ 286 TYPED_DATA_SETTER(setter, object, get_object_value, num_bytes) \
295 287
296 288
297 #define TYPED_DATA_UINT64_NATIVES(getter, setter, object) \ 289 #define TYPED_DATA_UINT64_NATIVES(getter, setter, object) \
298 TYPED_DATA_UINT64_GETTER(getter, object) \ 290 TYPED_DATA_UINT64_GETTER(getter, object) \
299 TYPED_DATA_UINT64_SETTER(setter, object) \ 291 TYPED_DATA_UINT64_SETTER(setter, object) \
300 292
301 293
302 TYPED_DATA_NATIVES(GetInt8, SetInt8, Smi, Value) 294 TYPED_DATA_NATIVES(GetInt8, SetInt8, Smi, Value, 1)
303 TYPED_DATA_NATIVES(GetUint8, SetUint8, Smi, Value) 295 TYPED_DATA_NATIVES(GetUint8, SetUint8, Smi, Value, 1)
304 TYPED_DATA_NATIVES(GetInt16, SetInt16, Smi, Value) 296 TYPED_DATA_NATIVES(GetInt16, SetInt16, Smi, Value, 2)
305 TYPED_DATA_NATIVES(GetUint16, SetUint16, Smi, Value) 297 TYPED_DATA_NATIVES(GetUint16, SetUint16, Smi, Value, 2)
306 TYPED_DATA_NATIVES(GetInt32, SetInt32, Integer, AsInt64Value) 298 TYPED_DATA_NATIVES(GetInt32, SetInt32, Integer, AsInt64Value, 4)
307 TYPED_DATA_NATIVES(GetUint32, SetUint32, Integer, AsInt64Value) 299 TYPED_DATA_NATIVES(GetUint32, SetUint32, Integer, AsInt64Value, 4)
308 TYPED_DATA_NATIVES(GetInt64, SetInt64, Integer, AsInt64Value) 300 TYPED_DATA_NATIVES(GetInt64, SetInt64, Integer, AsInt64Value, 8)
309 TYPED_DATA_UINT64_NATIVES(GetUint64, SetUint64, Integer) 301 TYPED_DATA_UINT64_NATIVES(GetUint64, SetUint64, Integer)
310 TYPED_DATA_NATIVES(GetFloat32, SetFloat32, Double, value) 302 TYPED_DATA_NATIVES(GetFloat32, SetFloat32, Double, value, 4)
311 TYPED_DATA_NATIVES(GetFloat64, SetFloat64, Double, value) 303 TYPED_DATA_NATIVES(GetFloat64, SetFloat64, Double, value, 8)
312 TYPED_DATA_NATIVES(GetFloat32x4, SetFloat32x4, Float32x4, value) 304 TYPED_DATA_NATIVES(GetFloat32x4, SetFloat32x4, Float32x4, value, 16)
313 305
314 306
315 DEFINE_NATIVE_ENTRY(ByteData_ToEndianInt16, 2) { 307 DEFINE_NATIVE_ENTRY(ByteData_ToEndianInt16, 2) {
316 GET_NON_NULL_NATIVE_ARGUMENT(Smi, host_value, arguments->NativeArgAt(0)); 308 GET_NON_NULL_NATIVE_ARGUMENT(Smi, host_value, arguments->NativeArgAt(0));
317 GET_NON_NULL_NATIVE_ARGUMENT(Bool, little_endian, arguments->NativeArgAt(1)); 309 GET_NON_NULL_NATIVE_ARGUMENT(Bool, little_endian, arguments->NativeArgAt(1));
318 int16_t value = host_value.Value(); 310 int16_t value = host_value.Value();
319 if (little_endian.value()) { 311 if (little_endian.value()) {
320 value = Utils::HostToLittleEndian16(value); 312 value = Utils::HostToLittleEndian16(value);
321 } else { 313 } else {
322 value = Utils::HostToBigEndian16(value); 314 value = Utils::HostToBigEndian16(value);
(...skipping 98 matching lines...) Expand 10 before | Expand all | Expand 10 after
421 value = bit_cast<double>( 413 value = bit_cast<double>(
422 Utils::HostToLittleEndian64(bit_cast<uint64_t>(value))); 414 Utils::HostToLittleEndian64(bit_cast<uint64_t>(value)));
423 } else { 415 } else {
424 value = bit_cast<double>( 416 value = bit_cast<double>(
425 Utils::HostToBigEndian64(bit_cast<uint64_t>(value))); 417 Utils::HostToBigEndian64(bit_cast<uint64_t>(value)));
426 } 418 }
427 return Double::New(value); 419 return Double::New(value);
428 } 420 }
429 421
430 } // namespace dart 422 } // namespace dart
OLDNEW
« no previous file with comments | « no previous file | runtime/vm/flow_graph_optimizer.cc » ('j') | runtime/vm/flow_graph_optimizer.cc » ('J')

Powered by Google App Engine
This is Rietveld 408576698