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

Side by Side Diff: lib/byte_array.cc

Issue 10782016: Enforce length/size limits for variable size heap object in order to (Closed) Base URL: http://dart.googlecode.com/svn/branches/bleeding_edge/dart/runtime/
Patch Set: Created 8 years, 5 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 | « lib/array.cc ('k') | lib/string.cc » ('j') | lib/string.cc » ('J')
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file 1 // Copyright (c) 2012, 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 "vm/bigint_operations.h" 7 #include "vm/bigint_operations.h"
8 #include "vm/exceptions.h" 8 #include "vm/exceptions.h"
9 #include "vm/native_entry.h" 9 #include "vm/native_entry.h"
10 #include "vm/object.h" 10 #include "vm/object.h"
11 11
12 namespace dart { 12 namespace dart {
13 13
14 // ByteArray 14 // ByteArray
15 15
16 static void RangeCheck(const ByteArray& array, 16 static void RangeCheck(const ByteArray& array,
17 intptr_t index, 17 intptr_t index,
18 intptr_t num_bytes) { 18 intptr_t num_bytes) {
19 if (!Utils::RangeCheck(index, num_bytes, array.ByteLength())) { 19 if (!Utils::RangeCheck(index, num_bytes, array.ByteLength())) {
20 GrowableArray<const Object*> arguments; 20 GrowableArray<const Object*> arguments;
21 const Smi &index_object = Smi::Handle(Smi::New(index)); 21 const Smi &index_object = Smi::Handle(Smi::New(index));
22 arguments.Add(&index_object); 22 arguments.Add(&index_object);
cshapiro 2012/07/17 22:54:30 Out of the kindness of your heart, please make thi
turnidge 2012/07/18 18:17:04 Done.
23 Exceptions::ThrowByType(Exceptions::kIndexOutOfRange, arguments); 23 Exceptions::ThrowByType(Exceptions::kIndexOutOfRange, arguments);
24 } 24 }
25 } 25 }
26 26
27 27
28 #define GETTER_ARGUMENTS(ArrayT, ValueT) \ 28 #define GETTER_ARGUMENTS(ArrayT, ValueT) \
29 GET_NATIVE_ARGUMENT(ArrayT, array, arguments->At(0)); \ 29 GET_NATIVE_ARGUMENT(ArrayT, array, arguments->At(0)); \
30 GET_NATIVE_ARGUMENT(Smi, index, arguments->At(1)); 30 GET_NATIVE_ARGUMENT(Smi, index, arguments->At(1));
31 31
32 32
(...skipping 220 matching lines...) Expand 10 before | Expand all | Expand 10 after
253 RangeCheck(src, src_start_value, length_value); 253 RangeCheck(src, src_start_value, length_value);
254 RangeCheck(dst, dst_start_value, length_value); 254 RangeCheck(dst, dst_start_value, length_value);
255 ByteArray::Copy(dst, dst_start_value, src, src_start_value, length_value); 255 ByteArray::Copy(dst, dst_start_value, src, src_start_value, length_value);
256 } 256 }
257 257
258 258
259 // Int8Array 259 // Int8Array
260 260
261 DEFINE_NATIVE_ENTRY(Int8Array_new, 1) { 261 DEFINE_NATIVE_ENTRY(Int8Array_new, 1) {
262 GET_NATIVE_ARGUMENT(Smi, length, arguments->At(0)); 262 GET_NATIVE_ARGUMENT(Smi, length, arguments->At(0));
263 if (length.Value() < 0) { 263 intptr_t len = length.Value();
264 if (len < 0 || len > Int8Array::kMaxElements) {
cshapiro 2012/07/17 22:54:30 Maybe some derivative or close cousin to the Range
turnidge 2012/07/18 18:17:04 Done.
265 const String& error = String::Handle(String::NewFormatted(
266 "length (%ld) must be in the range [0..%ld]",
267 len, Int8Array::kMaxElements));
264 GrowableArray<const Object*> args; 268 GrowableArray<const Object*> args;
265 args.Add(&length); 269 args.Add(&error);
266 Exceptions::ThrowByType(Exceptions::kIllegalArgument, args); 270 Exceptions::ThrowByType(Exceptions::kIllegalArgument, args);
267 } 271 }
268 const Int8Array& new_array = 272 const Int8Array& new_array = Int8Array::Handle(Int8Array::New(len));
269 Int8Array::Handle(Int8Array::New(length.Value()));
270 arguments->SetReturn(new_array); 273 arguments->SetReturn(new_array);
271 } 274 }
272 275
273 276
274 DEFINE_NATIVE_ENTRY(Int8Array_getIndexed, 2) { 277 DEFINE_NATIVE_ENTRY(Int8Array_getIndexed, 2) {
275 GETTER(Int8Array, Smi, int8_t); 278 GETTER(Int8Array, Smi, int8_t);
276 } 279 }
277 280
278 281
279 DEFINE_NATIVE_ENTRY(Int8Array_setIndexed, 3) { 282 DEFINE_NATIVE_ENTRY(Int8Array_setIndexed, 3) {
280 SETTER(Int8Array, Smi, Value, int8_t); 283 SETTER(Int8Array, Smi, Value, int8_t);
281 } 284 }
282 285
283 286
284 // Uint8Array 287 // Uint8Array
285 288
286 DEFINE_NATIVE_ENTRY(Uint8Array_new, 1) { 289 DEFINE_NATIVE_ENTRY(Uint8Array_new, 1) {
287 GET_NATIVE_ARGUMENT(Smi, length, arguments->At(0)); 290 GET_NATIVE_ARGUMENT(Smi, length, arguments->At(0));
288 if (length.Value() < 0) { 291 intptr_t len = length.Value();
292 if (len < 0 || len > Uint8Array::kMaxElements) {
293 const String& error = String::Handle(String::NewFormatted(
294 "length (%ld) must be in the range [0..%ld]",
295 len, Uint8Array::kMaxElements));
289 GrowableArray<const Object*> args; 296 GrowableArray<const Object*> args;
290 args.Add(&length); 297 args.Add(&error);
291 Exceptions::ThrowByType(Exceptions::kIllegalArgument, args); 298 Exceptions::ThrowByType(Exceptions::kIllegalArgument, args);
292 } 299 }
293 const Uint8Array& new_array = 300 const Uint8Array& new_array = Uint8Array::Handle(Uint8Array::New(len));
294 Uint8Array::Handle(Uint8Array::New(length.Value()));
295 arguments->SetReturn(new_array); 301 arguments->SetReturn(new_array);
296 } 302 }
297 303
298 304
299 DEFINE_NATIVE_ENTRY(Uint8Array_getIndexed, 2) { 305 DEFINE_NATIVE_ENTRY(Uint8Array_getIndexed, 2) {
300 GETTER(Uint8Array, Smi, uint8_t); 306 GETTER(Uint8Array, Smi, uint8_t);
301 } 307 }
302 308
303 309
304 DEFINE_NATIVE_ENTRY(Uint8Array_setIndexed, 3) { 310 DEFINE_NATIVE_ENTRY(Uint8Array_setIndexed, 3) {
305 SETTER(Uint8Array, Smi, Value, uint8_t); 311 SETTER(Uint8Array, Smi, Value, uint8_t);
306 } 312 }
307 313
308 314
309 // Int16Array 315 // Int16Array
310 316
311 DEFINE_NATIVE_ENTRY(Int16Array_new, 1) { 317 DEFINE_NATIVE_ENTRY(Int16Array_new, 1) {
312 GET_NATIVE_ARGUMENT(Smi, length, arguments->At(0)); 318 GET_NATIVE_ARGUMENT(Smi, length, arguments->At(0));
313 if (length.Value() < 0) { 319 intptr_t len = length.Value();
320 if (len < 0 || len > Int16Array::kMaxElements) {
321 const String& error = String::Handle(String::NewFormatted(
322 "length (%ld) must be in the range [0..%ld]",
323 len, Int16Array::kMaxElements));
314 GrowableArray<const Object*> args; 324 GrowableArray<const Object*> args;
315 args.Add(&length); 325 args.Add(&error);
316 Exceptions::ThrowByType(Exceptions::kIllegalArgument, args); 326 Exceptions::ThrowByType(Exceptions::kIllegalArgument, args);
317 } 327 }
318 const Int16Array& new_array = 328 const Int16Array& new_array = Int16Array::Handle(Int16Array::New(len));
319 Int16Array::Handle(Int16Array::New(length.Value()));
320 arguments->SetReturn(new_array); 329 arguments->SetReturn(new_array);
321 } 330 }
322 331
323 332
324 DEFINE_NATIVE_ENTRY(Int16Array_getIndexed, 2) { 333 DEFINE_NATIVE_ENTRY(Int16Array_getIndexed, 2) {
325 GETTER(Int16Array, Smi, int16_t); 334 GETTER(Int16Array, Smi, int16_t);
326 } 335 }
327 336
328 337
329 DEFINE_NATIVE_ENTRY(Int16Array_setIndexed, 3) { 338 DEFINE_NATIVE_ENTRY(Int16Array_setIndexed, 3) {
330 SETTER(Int16Array, Smi, Value, int16_t); 339 SETTER(Int16Array, Smi, Value, int16_t);
331 } 340 }
332 341
333 342
334 // Uint16Array 343 // Uint16Array
335 344
336 DEFINE_NATIVE_ENTRY(Uint16Array_new, 1) { 345 DEFINE_NATIVE_ENTRY(Uint16Array_new, 1) {
337 GET_NATIVE_ARGUMENT(Smi, length, arguments->At(0)); 346 GET_NATIVE_ARGUMENT(Smi, length, arguments->At(0));
338 if (length.Value() < 0) { 347 intptr_t len = length.Value();
348 if (len < 0 || len > Uint16Array::kMaxElements) {
349 const String& error = String::Handle(String::NewFormatted(
350 "length (%ld) must be in the range [0..%ld]",
351 len, Uint16Array::kMaxElements));
339 GrowableArray<const Object*> args; 352 GrowableArray<const Object*> args;
340 args.Add(&length); 353 args.Add(&error);
341 Exceptions::ThrowByType(Exceptions::kIllegalArgument, args); 354 Exceptions::ThrowByType(Exceptions::kIllegalArgument, args);
342 } 355 }
343 const Uint16Array& new_array = 356 const Uint16Array& new_array = Uint16Array::Handle(Uint16Array::New(len));
344 Uint16Array::Handle(Uint16Array::New(length.Value()));
345 arguments->SetReturn(new_array); 357 arguments->SetReturn(new_array);
346 } 358 }
347 359
348 360
349 DEFINE_NATIVE_ENTRY(Uint16Array_getIndexed, 2) { 361 DEFINE_NATIVE_ENTRY(Uint16Array_getIndexed, 2) {
350 GETTER(Uint16Array, Smi, uint16_t); 362 GETTER(Uint16Array, Smi, uint16_t);
351 } 363 }
352 364
353 365
354 DEFINE_NATIVE_ENTRY(Uint16Array_setIndexed, 3) { 366 DEFINE_NATIVE_ENTRY(Uint16Array_setIndexed, 3) {
355 SETTER(Uint16Array, Smi, Value, uint16_t); 367 SETTER(Uint16Array, Smi, Value, uint16_t);
356 } 368 }
357 369
358 370
359 // Int32Array 371 // Int32Array
360 372
361 DEFINE_NATIVE_ENTRY(Int32Array_new, 1) { 373 DEFINE_NATIVE_ENTRY(Int32Array_new, 1) {
362 GET_NATIVE_ARGUMENT(Smi, length, arguments->At(0)); 374 GET_NATIVE_ARGUMENT(Smi, length, arguments->At(0));
363 if (length.Value() < 0) { 375 intptr_t len = length.Value();
376 if (len < 0 || len > Int32Array::kMaxElements) {
377 const String& error = String::Handle(String::NewFormatted(
378 "length (%ld) must be in the range [0..%ld]",
379 len, Int32Array::kMaxElements));
364 GrowableArray<const Object*> args; 380 GrowableArray<const Object*> args;
365 args.Add(&length); 381 args.Add(&error);
366 Exceptions::ThrowByType(Exceptions::kIllegalArgument, args); 382 Exceptions::ThrowByType(Exceptions::kIllegalArgument, args);
367 } 383 }
368 const Int32Array& new_array = 384 const Int32Array& new_array = Int32Array::Handle(Int32Array::New(len));
369 Int32Array::Handle(Int32Array::New(length.Value()));
370 arguments->SetReturn(new_array); 385 arguments->SetReturn(new_array);
371 } 386 }
372 387
373 388
374 DEFINE_NATIVE_ENTRY(Int32Array_getIndexed, 2) { 389 DEFINE_NATIVE_ENTRY(Int32Array_getIndexed, 2) {
375 GETTER(Int32Array, Integer, int32_t); 390 GETTER(Int32Array, Integer, int32_t);
376 } 391 }
377 392
378 393
379 DEFINE_NATIVE_ENTRY(Int32Array_setIndexed, 3) { 394 DEFINE_NATIVE_ENTRY(Int32Array_setIndexed, 3) {
380 SETTER(Int32Array, Integer, AsInt64Value, int32_t); 395 SETTER(Int32Array, Integer, AsInt64Value, int32_t);
381 } 396 }
382 397
383 398
384 // Uint32Array 399 // Uint32Array
385 400
386 DEFINE_NATIVE_ENTRY(Uint32Array_new, 1) { 401 DEFINE_NATIVE_ENTRY(Uint32Array_new, 1) {
387 GET_NATIVE_ARGUMENT(Smi, length, arguments->At(0)); 402 GET_NATIVE_ARGUMENT(Smi, length, arguments->At(0));
388 if (length.Value() < 0) { 403 intptr_t len = length.Value();
404 if (len < 0 || len > Uint32Array::kMaxElements) {
405 const String& error = String::Handle(String::NewFormatted(
406 "length (%ld) must be in the range [0..%ld]",
407 len, Uint32Array::kMaxElements));
389 GrowableArray<const Object*> args; 408 GrowableArray<const Object*> args;
390 args.Add(&length); 409 args.Add(&error);
391 Exceptions::ThrowByType(Exceptions::kIllegalArgument, args); 410 Exceptions::ThrowByType(Exceptions::kIllegalArgument, args);
392 } 411 }
393 const Uint32Array& new_array = 412 const Uint32Array& new_array = Uint32Array::Handle(Uint32Array::New(len));
394 Uint32Array::Handle(Uint32Array::New(length.Value()));
395 arguments->SetReturn(new_array); 413 arguments->SetReturn(new_array);
396 } 414 }
397 415
398 416
399 DEFINE_NATIVE_ENTRY(Uint32Array_getIndexed, 2) { 417 DEFINE_NATIVE_ENTRY(Uint32Array_getIndexed, 2) {
400 GETTER(Uint32Array, Integer, uint32_t); 418 GETTER(Uint32Array, Integer, uint32_t);
401 } 419 }
402 420
403 421
404 DEFINE_NATIVE_ENTRY(Uint32Array_setIndexed, 3) { 422 DEFINE_NATIVE_ENTRY(Uint32Array_setIndexed, 3) {
405 SETTER(Uint32Array, Integer, AsInt64Value, uint32_t); 423 SETTER(Uint32Array, Integer, AsInt64Value, uint32_t);
406 } 424 }
407 425
408 426
409 // Int64Array 427 // Int64Array
410 428
411 DEFINE_NATIVE_ENTRY(Int64Array_new, 1) { 429 DEFINE_NATIVE_ENTRY(Int64Array_new, 1) {
412 GET_NATIVE_ARGUMENT(Smi, length, arguments->At(0)); 430 GET_NATIVE_ARGUMENT(Smi, length, arguments->At(0));
413 if (length.Value() < 0) { 431 intptr_t len = length.Value();
432 if (len < 0 || len > Int64Array::kMaxElements) {
433 const String& error = String::Handle(String::NewFormatted(
434 "length (%ld) must be in the range [0..%ld]",
435 len, Int64Array::kMaxElements));
414 GrowableArray<const Object*> args; 436 GrowableArray<const Object*> args;
415 args.Add(&length); 437 args.Add(&error);
416 Exceptions::ThrowByType(Exceptions::kIllegalArgument, args); 438 Exceptions::ThrowByType(Exceptions::kIllegalArgument, args);
417 } 439 }
418 const Int64Array& new_array = 440 const Int64Array& new_array = Int64Array::Handle(Int64Array::New(len));
419 Int64Array::Handle(Int64Array::New(length.Value()));
420 arguments->SetReturn(new_array); 441 arguments->SetReturn(new_array);
421 } 442 }
422 443
423 444
424 DEFINE_NATIVE_ENTRY(Int64Array_getIndexed, 2) { 445 DEFINE_NATIVE_ENTRY(Int64Array_getIndexed, 2) {
425 GETTER(Int64Array, Integer, int64_t); 446 GETTER(Int64Array, Integer, int64_t);
426 } 447 }
427 448
428 449
429 DEFINE_NATIVE_ENTRY(Int64Array_setIndexed, 3) { 450 DEFINE_NATIVE_ENTRY(Int64Array_setIndexed, 3) {
430 SETTER(Int64Array, Integer, AsInt64Value, int64_t); 451 SETTER(Int64Array, Integer, AsInt64Value, int64_t);
431 } 452 }
432 453
433 454
434 // Uint64Array 455 // Uint64Array
435 456
436 DEFINE_NATIVE_ENTRY(Uint64Array_new, 1) { 457 DEFINE_NATIVE_ENTRY(Uint64Array_new, 1) {
437 GET_NATIVE_ARGUMENT(Smi, length, arguments->At(0)); 458 GET_NATIVE_ARGUMENT(Smi, length, arguments->At(0));
438 if (length.Value() < 0) { 459 intptr_t len = length.Value();
460 if (len < 0 || len > Uint64Array::kMaxElements) {
461 const String& error = String::Handle(String::NewFormatted(
462 "length (%ld) must be in the range [0..%ld]",
463 len, Uint64Array::kMaxElements));
439 GrowableArray<const Object*> args; 464 GrowableArray<const Object*> args;
440 args.Add(&length); 465 args.Add(&error);
441 Exceptions::ThrowByType(Exceptions::kIllegalArgument, args); 466 Exceptions::ThrowByType(Exceptions::kIllegalArgument, args);
442 } 467 }
443 const Uint64Array& new_array = 468 const Uint64Array& new_array = Uint64Array::Handle(Uint64Array::New(len));
444 Uint64Array::Handle(Uint64Array::New(length.Value()));
445 arguments->SetReturn(new_array); 469 arguments->SetReturn(new_array);
446 } 470 }
447 471
448 472
449 DEFINE_NATIVE_ENTRY(Uint64Array_getIndexed, 2) { 473 DEFINE_NATIVE_ENTRY(Uint64Array_getIndexed, 2) {
450 GETTER_UINT64(Uint64Array); 474 GETTER_UINT64(Uint64Array);
451 } 475 }
452 476
453 477
454 DEFINE_NATIVE_ENTRY(Uint64Array_setIndexed, 3) { 478 DEFINE_NATIVE_ENTRY(Uint64Array_setIndexed, 3) {
455 SETTER_UINT64(Uint64Array); 479 SETTER_UINT64(Uint64Array);
456 } 480 }
457 481
458 482
459 // Float32Array 483 // Float32Array
460 484
461 DEFINE_NATIVE_ENTRY(Float32Array_new, 1) { 485 DEFINE_NATIVE_ENTRY(Float32Array_new, 1) {
462 GET_NATIVE_ARGUMENT(Smi, length, arguments->At(0)); 486 GET_NATIVE_ARGUMENT(Smi, length, arguments->At(0));
463 if (length.Value() < 0) { 487 intptr_t len = length.Value();
488 if (len < 0 || len > Float32Array::kMaxElements) {
489 const String& error = String::Handle(String::NewFormatted(
490 "length (%ld) must be in the range [0..%ld]",
491 len, Float32Array::kMaxElements));
464 GrowableArray<const Object*> args; 492 GrowableArray<const Object*> args;
465 args.Add(&length); 493 args.Add(&error);
466 Exceptions::ThrowByType(Exceptions::kIllegalArgument, args); 494 Exceptions::ThrowByType(Exceptions::kIllegalArgument, args);
467 } 495 }
468 const Float32Array& new_array = 496 const Float32Array& new_array = Float32Array::Handle(Float32Array::New(len));
469 Float32Array::Handle(Float32Array::New(length.Value()));
470 arguments->SetReturn(new_array); 497 arguments->SetReturn(new_array);
471 } 498 }
472 499
473 500
474 DEFINE_NATIVE_ENTRY(Float32Array_getIndexed, 2) { 501 DEFINE_NATIVE_ENTRY(Float32Array_getIndexed, 2) {
475 GETTER(Float32Array, Double, float); 502 GETTER(Float32Array, Double, float);
476 } 503 }
477 504
478 505
479 DEFINE_NATIVE_ENTRY(Float32Array_setIndexed, 3) { 506 DEFINE_NATIVE_ENTRY(Float32Array_setIndexed, 3) {
480 SETTER(Float32Array, Double, value, float); 507 SETTER(Float32Array, Double, value, float);
481 } 508 }
482 509
483 510
484 // Float64Array 511 // Float64Array
485 512
486 DEFINE_NATIVE_ENTRY(Float64Array_new, 1) { 513 DEFINE_NATIVE_ENTRY(Float64Array_new, 1) {
487 GET_NATIVE_ARGUMENT(Smi, length, arguments->At(0)); 514 GET_NATIVE_ARGUMENT(Smi, length, arguments->At(0));
488 if (length.Value() < 0) { 515 intptr_t len = length.Value();
516 if (len < 0 || len > Float64Array::kMaxElements) {
517 const String& error = String::Handle(String::NewFormatted(
518 "length (%ld) must be in the range [0..%ld]",
519 len, Float64Array::kMaxElements));
489 GrowableArray<const Object*> args; 520 GrowableArray<const Object*> args;
490 args.Add(&length); 521 args.Add(&error);
491 Exceptions::ThrowByType(Exceptions::kIllegalArgument, args); 522 Exceptions::ThrowByType(Exceptions::kIllegalArgument, args);
492 } 523 }
493 const Float64Array& new_array = 524 const Float64Array& new_array = Float64Array::Handle(Float64Array::New(len));
494 Float64Array::Handle(Float64Array::New(length.Value()));
495 arguments->SetReturn(new_array); 525 arguments->SetReturn(new_array);
496 } 526 }
497 527
498 528
499 DEFINE_NATIVE_ENTRY(Float64Array_getIndexed, 2) { 529 DEFINE_NATIVE_ENTRY(Float64Array_getIndexed, 2) {
500 GETTER(Float64Array, Double, double); 530 GETTER(Float64Array, Double, double);
501 } 531 }
502 532
503 533
504 DEFINE_NATIVE_ENTRY(Float64Array_setIndexed, 3) { 534 DEFINE_NATIVE_ENTRY(Float64Array_setIndexed, 3) {
(...skipping 114 matching lines...) Expand 10 before | Expand all | Expand 10 after
619 DEFINE_NATIVE_ENTRY(ExternalFloat64Array_getIndexed, 2) { 649 DEFINE_NATIVE_ENTRY(ExternalFloat64Array_getIndexed, 2) {
620 GETTER(ExternalFloat64Array, Double, double); 650 GETTER(ExternalFloat64Array, Double, double);
621 } 651 }
622 652
623 653
624 DEFINE_NATIVE_ENTRY(ExternalFloat64Array_setIndexed, 3) { 654 DEFINE_NATIVE_ENTRY(ExternalFloat64Array_setIndexed, 3) {
625 SETTER(ExternalFloat64Array, Double, value, double); 655 SETTER(ExternalFloat64Array, Double, value, double);
626 } 656 }
627 657
628 } // namespace dart 658 } // namespace dart
OLDNEW
« no previous file with comments | « lib/array.cc ('k') | lib/string.cc » ('j') | lib/string.cc » ('J')

Powered by Google App Engine
This is Rietveld 408576698