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

Side by Side Diff: lib/compiler/implementation/emitter.dart

Issue 10696194: Introduce CodeBuffer as StringBuffer replacement in compiler. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
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
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 /** 5 /**
6 * A function element that represents a closure call. The signature is copied 6 * A function element that represents a closure call. The signature is copied
7 * from the given element. 7 * from the given element.
8 */ 8 */
9 class ClosureInvocationElement extends FunctionElement { 9 class ClosureInvocationElement extends FunctionElement {
10 ClosureInvocationElement(SourceString name, 10 ClosureInvocationElement(SourceString name,
11 FunctionElement other) 11 FunctionElement other)
12 : super.from(name, other, other.enclosingElement); 12 : super.from(name, other, other.enclosingElement);
13 13
14 isInstanceMember() => true; 14 isInstanceMember() => true;
15 } 15 }
16 16
17 /** 17 /**
18 * Generates the code for all used classes in the program. Static fields (even 18 * Generates the code for all used classes in the program. Static fields (even
19 * in classes) are ignored, since they can be treated as non-class elements. 19 * in classes) are ignored, since they can be treated as non-class elements.
20 * 20 *
21 * The code for the containing (used) methods must exist in the [:universe:]. 21 * The code for the containing (used) methods must exist in the [:universe:].
22 */ 22 */
23 class CodeEmitterTask extends CompilerTask { 23 class CodeEmitterTask extends CompilerTask {
24 bool needsInheritFunction = false; 24 bool needsInheritFunction = false;
25 bool needsDefineClass = false; 25 bool needsDefineClass = false;
26 bool needsClosureClass = false; 26 bool needsClosureClass = false;
27 final Namer namer; 27 final Namer namer;
28 NativeEmitter nativeEmitter; 28 NativeEmitter nativeEmitter;
29 StringBuffer boundClosureBuffer; 29 CodeBuffer boundClosureBuffer;
30 StringBuffer mainBuffer; 30 CodeBuffer mainBuffer;
31 /** Shorter access to [isolatePropertiesName]. Both here in the code, as 31 /** Shorter access to [isolatePropertiesName]. Both here in the code, as
32 well as in the generated code. */ 32 well as in the generated code. */
33 String isolateProperties; 33 String isolateProperties;
34 String classesCollector; 34 String classesCollector;
35 final Map<int, String> boundClosureCache; 35 final Map<int, String> boundClosureCache;
36 36
37 final bool generateSourceMap; 37 final bool generateSourceMap;
38 final SourceMapBuilder sourceMapBuilder; 38 final SourceMapBuilder sourceMapBuilder;
39 39
40 CodeEmitterTask(Compiler compiler, [bool generateSourceMap = false]) 40 CodeEmitterTask(Compiler compiler, [bool generateSourceMap = false])
41 : namer = compiler.namer, 41 : namer = compiler.namer,
42 boundClosureBuffer = new StringBuffer(), 42 boundClosureBuffer = new CodeBuffer(),
43 mainBuffer = new StringBuffer(), 43 mainBuffer = new CodeBuffer(),
44 boundClosureCache = new Map<int, String>(), 44 boundClosureCache = new Map<int, String>(),
45 generateSourceMap = generateSourceMap, 45 generateSourceMap = generateSourceMap,
46 sourceMapBuilder = new SourceMapBuilder(), 46 sourceMapBuilder = new SourceMapBuilder(),
47 super(compiler) { 47 super(compiler) {
48 nativeEmitter = new NativeEmitter(this); 48 nativeEmitter = new NativeEmitter(this);
49 } 49 }
50 50
51 String get name() => 'CodeEmitter'; 51 String get name() => 'CodeEmitter';
52 52
53 String get defineClassName() 53 String get defineClassName()
(...skipping 172 matching lines...) Expand 10 before | Expand all | Expand 10 after
226 } 226 }
227 str += "}\\n"; 227 str += "}\\n";
228 var newIsolate = new Function(str); 228 var newIsolate = new Function(str);
229 newIsolate.prototype = isolatePrototype; 229 newIsolate.prototype = isolatePrototype;
230 isolatePrototype.constructor = newIsolate; 230 isolatePrototype.constructor = newIsolate;
231 newIsolate.${namer.ISOLATE_PROPERTIES} = isolateProperties; 231 newIsolate.${namer.ISOLATE_PROPERTIES} = isolateProperties;
232 return newIsolate; 232 return newIsolate;
233 }"""; 233 }""";
234 } 234 }
235 235
236 void addDefineClassAndFinishClassFunctionsIfNecessary(StringBuffer buffer) { 236 void addDefineClassAndFinishClassFunctionsIfNecessary(CodeBuffer buffer) {
237 if (needsDefineClass) { 237 if (needsDefineClass) {
238 String isolate = namer.ISOLATE; 238 String isolate = namer.ISOLATE;
239 buffer.add("$defineClassName = $defineClassFunction;\n"); 239 buffer.add("$defineClassName = $defineClassFunction;\n");
240 buffer.add("$pendingClassesName = {};\n"); 240 buffer.add("$pendingClassesName = {};\n");
241 buffer.add("$finishClassesName = $finishClassesFunction;\n"); 241 buffer.add("$finishClassesName = $finishClassesFunction;\n");
242 } 242 }
243 } 243 }
244 244
245 void emitFinishIsolateConstructor(StringBuffer buffer) { 245 void emitFinishIsolateConstructor(CodeBuffer buffer) {
246 String name = finishIsolateConstructorName; 246 String name = finishIsolateConstructorName;
247 String value = finishIsolateConstructorFunction; 247 String value = finishIsolateConstructorFunction;
248 buffer.add("$name = $value;\n"); 248 buffer.add("$name = $value;\n");
249 } 249 }
250 250
251 void emitFinishIsolateConstructorInvocation(StringBuffer buffer) { 251 void emitFinishIsolateConstructorInvocation(CodeBuffer buffer) {
252 String isolate = namer.ISOLATE; 252 String isolate = namer.ISOLATE;
253 buffer.add("$isolate = $finishIsolateConstructorName($isolate);\n"); 253 buffer.add("$isolate = $finishIsolateConstructorName($isolate);\n");
254 } 254 }
255 255
256 void addParameterStub(FunctionElement member, 256 void addParameterStub(FunctionElement member,
257 Selector selector, 257 Selector selector,
258 DefineMemberFunction defineInstanceMember) { 258 DefineMemberFunction defineInstanceMember) {
259 FunctionSignature parameters = member.computeSignature(compiler); 259 FunctionSignature parameters = member.computeSignature(compiler);
260 int positionalArgumentCount = selector.positionalArgumentCount; 260 int positionalArgumentCount = selector.positionalArgumentCount;
261 if (positionalArgumentCount == parameters.parameterCount) { 261 if (positionalArgumentCount == parameters.parameterCount) {
262 assert(selector.namedArgumentCount == 0); 262 assert(selector.namedArgumentCount == 0);
263 return; 263 return;
264 } 264 }
265 ConstantHandler handler = compiler.constantHandler; 265 ConstantHandler handler = compiler.constantHandler;
266 List<SourceString> names = selector.getOrderedNamedArguments(); 266 List<SourceString> names = selector.getOrderedNamedArguments();
267 267
268 String invocationName = 268 String invocationName =
269 namer.instanceMethodInvocationName(member.getLibrary(), member.name, 269 namer.instanceMethodInvocationName(member.getLibrary(), member.name,
270 selector); 270 selector);
271 StringBuffer buffer = new StringBuffer(); 271 CodeBuffer buffer = new CodeBuffer();
272 buffer.add('function('); 272 buffer.add('function(');
273 273
274 // The parameters that this stub takes. 274 // The parameters that this stub takes.
275 List<String> parametersBuffer = new List<String>(selector.argumentCount); 275 List<String> parametersBuffer = new List<String>(selector.argumentCount);
276 // The arguments that will be passed to the real method. 276 // The arguments that will be passed to the real method.
277 List<String> argumentsBuffer = new List<String>(parameters.parameterCount); 277 List<String> argumentsBuffer = new List<String>(parameters.parameterCount);
278 278
279 // We fill the lists depending on the selector. For example, 279 // We fill the lists depending on the selector. For example,
280 // take method foo: 280 // take method foo:
281 // foo(a, b, [c, d]); 281 // foo(a, b, [c, d]);
(...skipping 46 matching lines...) Expand 10 before | Expand all | Expand 10 after
328 } else { 328 } else {
329 Constant value = handler.initialVariableValues[element]; 329 Constant value = handler.initialVariableValues[element];
330 if (value == null) { 330 if (value == null) {
331 argumentsBuffer[count] = NullConstant.JsNull; 331 argumentsBuffer[count] = NullConstant.JsNull;
332 } else { 332 } else {
333 if (!value.isNull()) { 333 if (!value.isNull()) {
334 // If the value is the null constant, we should not pass it 334 // If the value is the null constant, we should not pass it
335 // down to the native method. 335 // down to the native method.
336 indexOfLastOptionalArgumentInParameters = count; 336 indexOfLastOptionalArgumentInParameters = count;
337 } 337 }
338 StringBuffer argumentBuffer = new StringBuffer(); 338 CodeBuffer argumentBuffer = new CodeBuffer();
339 handler.writeConstant(argumentBuffer, value); 339 handler.writeConstant(argumentBuffer, value);
340 argumentsBuffer[count] = argumentBuffer.toString(); 340 argumentsBuffer[count] = argumentBuffer.toString();
341 } 341 }
342 } 342 }
343 } 343 }
344 count++; 344 count++;
345 }); 345 });
346 String parametersString = Strings.join(parametersBuffer, ","); 346 String parametersString = Strings.join(parametersBuffer, ",");
347 buffer.add('$parametersString) {\n'); 347 buffer.add('$parametersString) {\n');
348 348
349 if (member.isNative()) { 349 if (member.isNative()) {
350 nativeEmitter.generateParameterStub( 350 nativeEmitter.generateParameterStub(
351 member, invocationName, parametersString, argumentsBuffer, 351 member, invocationName, parametersString, argumentsBuffer,
352 indexOfLastOptionalArgumentInParameters, buffer); 352 indexOfLastOptionalArgumentInParameters, buffer);
353 } else { 353 } else {
354 String arguments = Strings.join(argumentsBuffer, ","); 354 String arguments = Strings.join(argumentsBuffer, ",");
355 buffer.add(' return this.${namer.getName(member)}($arguments)'); 355 buffer.add(' return this.${namer.getName(member)}($arguments)');
356 } 356 }
357 buffer.add('\n}'); 357 buffer.add('\n}');
358 defineInstanceMember(invocationName, buffer.toString()); 358 defineInstanceMember(invocationName, buffer);
359 } 359 }
360 360
361 void addParameterStubs(FunctionElement member, 361 void addParameterStubs(FunctionElement member,
362 DefineMemberFunction defineInstanceMember) { 362 DefineMemberFunction defineInstanceMember) {
363 Set<Selector> selectors = compiler.codegenWorld.invokedNames[member.name]; 363 Set<Selector> selectors = compiler.codegenWorld.invokedNames[member.name];
364 if (selectors == null) return; 364 if (selectors == null) return;
365 for (Selector selector in selectors) { 365 for (Selector selector in selectors) {
366 if (!selector.applies(member, compiler)) continue; 366 if (!selector.applies(member, compiler)) continue;
367 addParameterStub(member, selector, defineInstanceMember); 367 addParameterStub(member, selector, defineInstanceMember);
368 } 368 }
(...skipping 20 matching lines...) Expand all
389 void addInstanceMember(Element member, 389 void addInstanceMember(Element member,
390 DefineMemberFunction defineInstanceMember) { 390 DefineMemberFunction defineInstanceMember) {
391 // TODO(floitsch): we don't need to deal with members of 391 // TODO(floitsch): we don't need to deal with members of
392 // uninstantiated classes, that have been overwritten by subclasses. 392 // uninstantiated classes, that have been overwritten by subclasses.
393 393
394 if (member.kind === ElementKind.FUNCTION 394 if (member.kind === ElementKind.FUNCTION
395 || member.kind === ElementKind.GENERATIVE_CONSTRUCTOR_BODY 395 || member.kind === ElementKind.GENERATIVE_CONSTRUCTOR_BODY
396 || member.kind === ElementKind.GETTER 396 || member.kind === ElementKind.GETTER
397 || member.kind === ElementKind.SETTER) { 397 || member.kind === ElementKind.SETTER) {
398 if (member.modifiers !== null && member.modifiers.isAbstract()) return; 398 if (member.modifiers !== null && member.modifiers.isAbstract()) return;
399 CodeBlock codeBlock = compiler.codegenWorld.generatedCode[member]; 399 CodeBuffer codeBuffer = compiler.codegenWorld.generatedCode[member];
400 if (codeBlock == null) return; 400 if (codeBuffer == null) return;
401 defineInstanceMember(namer.getName(member), 401 defineInstanceMember(namer.getName(member), codeBuffer);
402 codeBlock.code, 402 codeBuffer = compiler.codegenWorld.generatedBailoutCode[member];
403 codeBlock.sourceMappings); 403 if (codeBuffer !== null) {
404 codeBlock = compiler.codegenWorld.generatedBailoutCode[member]; 404 defineInstanceMember(compiler.namer.getBailoutName(member), codeBuffer);
405 if (codeBlock !== null) {
406 defineInstanceMember(compiler.namer.getBailoutName(member),
407 codeBlock.code,
408 codeBlock.sourceMappings);
409 } 405 }
410 FunctionElement function = member; 406 FunctionElement function = member;
411 FunctionSignature parameters = function.computeSignature(compiler); 407 FunctionSignature parameters = function.computeSignature(compiler);
412 if (!parameters.optionalParameters.isEmpty()) { 408 if (!parameters.optionalParameters.isEmpty()) {
413 addParameterStubs(member, defineInstanceMember); 409 addParameterStubs(member, defineInstanceMember);
414 } 410 }
415 } else if (member.kind === ElementKind.FIELD) { 411 } else if (member.kind === ElementKind.FIELD) {
416 SourceString name = member.name; 412 SourceString name = member.name;
417 ClassElement cls = member.getEnclosingClass(); 413 ClassElement cls = member.getEnclosingClass();
418 if (cls.lookupSuperMember(name) !== null) { 414 if (cls.lookupSuperMember(name) !== null) {
419 String fieldName = namer.instanceFieldName(cls, name); 415 String fieldName = namer.instanceFieldName(cls, name);
416 CodeBuffer getterBuffer = new CodeBuffer();
417 getterBuffer.add('function() {\n return this.$fieldName;\n }');
420 defineInstanceMember(namer.getterName(cls.getLibrary(), name), 418 defineInstanceMember(namer.getterName(cls.getLibrary(), name),
421 'function() {\n return this.$fieldName;\n }'); 419 getterBuffer);
420 CodeBuffer setterBuffer = new CodeBuffer();
421 setterBuffer.add('function(x) {\n this.$fieldName = x;\n }');
422 defineInstanceMember(namer.setterName(cls.getLibrary(), name), 422 defineInstanceMember(namer.setterName(cls.getLibrary(), name),
423 'function(x) {\n this.$fieldName = x;\n }'); 423 setterBuffer);
424 } 424 }
425 } else { 425 } else {
426 compiler.internalError('unexpected kind: "${member.kind}"', 426 compiler.internalError('unexpected kind: "${member.kind}"',
427 element: member); 427 element: member);
428 } 428 }
429 emitExtraAccessors(member, defineInstanceMember); 429 emitExtraAccessors(member, defineInstanceMember);
430 } 430 }
431 431
432 Set<Element> emitClassFields(ClassElement classElement, StringBuffer buffer) { 432 Set<Element> emitClassFields(ClassElement classElement, CodeBuffer buffer) {
433 // If the class is never instantiated we still need to set it up for 433 // If the class is never instantiated we still need to set it up for
434 // inheritance purposes, but we can simplify its JavaScript constructor. 434 // inheritance purposes, but we can simplify its JavaScript constructor.
435 bool isInstantiated = 435 bool isInstantiated =
436 compiler.codegenWorld.instantiatedClasses.contains(classElement); 436 compiler.codegenWorld.instantiatedClasses.contains(classElement);
437 437
438 bool isFirstField = true; 438 bool isFirstField = true;
439 void addField(ClassElement enclosingClass, Element member) { 439 void addField(ClassElement enclosingClass, Element member) {
440 assert(!member.isNative()); 440 assert(!member.isNative());
441 // See if we can dynamically create getters and setters. 441 // See if we can dynamically create getters and setters.
442 // We can only generate getters and setters for [classElement] since 442 // We can only generate getters and setters for [classElement] since
(...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after
479 // generate the field getter/setter dynamically. Since this is only 479 // generate the field getter/setter dynamically. Since this is only
480 // allowed on fields that are in [classElement] we don't need to visit 480 // allowed on fields that are in [classElement] we don't need to visit
481 // superclasses for non-instantiated classes. 481 // superclasses for non-instantiated classes.
482 classElement.forEachInstanceField( 482 classElement.forEachInstanceField(
483 addField, 483 addField,
484 includeBackendMembers: true, 484 includeBackendMembers: true,
485 includeSuperMembers: isInstantiated && !classElement.isNative()); 485 includeSuperMembers: isInstantiated && !classElement.isNative());
486 } 486 }
487 487
488 void emitInstanceMembers(ClassElement classElement, 488 void emitInstanceMembers(ClassElement classElement,
489 StringBuffer buffer, 489 CodeBuffer buffer,
490 bool needsLeadingComma) { 490 bool needsLeadingComma) {
491 bool needsComma = needsLeadingComma; 491 bool needsComma = needsLeadingComma;
492 void defineInstanceMember(String name, 492 void defineInstanceMember(String name, CodeBuffer memberBuffer) {
493 String value,
494 [List<SourceMappingEntry> sourceMappings]) {
495 if (needsComma) buffer.add(','); 493 if (needsComma) buffer.add(',');
496 needsComma = true; 494 needsComma = true;
497 buffer.add('\n'); 495 buffer.add('\n');
498 buffer.add(' $name: '); 496 buffer.add(' $name: ');
499 sourceMapBuilder.addCodeBlock(sourceMappings, buffer.length); 497 addMappings(memberBuffer, buffer.length);
500 buffer.add('$value'); 498 buffer.add(memberBuffer);
501 } 499 }
502 500
503 classElement.forEachMember(includeBackendMembers: true, 501 classElement.forEachMember(includeBackendMembers: true,
504 f: (ClassElement enclosing, Element member) { 502 f: (ClassElement enclosing, Element member) {
505 if (member.isInstanceMember()) { 503 if (member.isInstanceMember()) {
506 addInstanceMember(member, defineInstanceMember); 504 addInstanceMember(member, defineInstanceMember);
507 } 505 }
508 }); 506 });
509 507
510 generateTypeTests(classElement, (Element other) { 508 generateTypeTests(classElement, (Element other) {
509 String code;
511 if (nativeEmitter.requiresNativeIsCheck(other)) { 510 if (nativeEmitter.requiresNativeIsCheck(other)) {
512 defineInstanceMember(namer.operatorIs(other), 511 code = 'function() { return true; }';
513 'function() { return true; }');
514 } else { 512 } else {
515 defineInstanceMember(namer.operatorIs(other), 'true'); 513 code = 'true';
516 } 514 }
515 CodeBuffer buffer = new CodeBuffer();
516 buffer.add(code);
517 defineInstanceMember(namer.operatorIs(other), buffer);
517 }); 518 });
518 519
519 if (classElement === compiler.objectClass && compiler.enabledNoSuchMethod) { 520 if (classElement === compiler.objectClass && compiler.enabledNoSuchMethod) {
520 // Emit the noSuchMethods on the Object prototype now, so that 521 // Emit the noSuchMethods on the Object prototype now, so that
521 // the code in the dynamicMethod can find them. Note that the 522 // the code in the dynamicMethod can find them. Note that the
522 // code in dynamicMethod is invoked before analyzing the full JS 523 // code in dynamicMethod is invoked before analyzing the full JS
523 // script. 524 // script.
524 emitNoSuchMethodCalls(defineInstanceMember); 525 emitNoSuchMethodCalls(defineInstanceMember);
525 } 526 }
526 } 527 }
527 528
528 void generateClass(ClassElement classElement, StringBuffer buffer) { 529 void generateClass(ClassElement classElement, CodeBuffer buffer) {
529 if (classElement.isNative()) { 530 if (classElement.isNative()) {
530 nativeEmitter.generateNativeClass(classElement); 531 nativeEmitter.generateNativeClass(classElement);
531 return; 532 return;
532 } else { 533 } else {
533 // TODO(ngeoffray): Instead of switching between buffer, we 534 // TODO(ngeoffray): Instead of switching between buffer, we
534 // should create code sections, and decide where to emit them at 535 // should create code sections, and decide where to emit them at
535 // the end. 536 // the end.
536 buffer = mainBuffer; 537 buffer = mainBuffer;
537 } 538 }
538 539
(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after
572 Element element = interfaceType.element; 573 Element element = interfaceType.element;
573 if (!alreadyGenerated.contains(element) && 574 if (!alreadyGenerated.contains(element) &&
574 compiler.codegenWorld.isChecks.contains(element)) { 575 compiler.codegenWorld.isChecks.contains(element)) {
575 alreadyGenerated.add(element); 576 alreadyGenerated.add(element);
576 generateTypeTest(element); 577 generateTypeTest(element);
577 } 578 }
578 generateInterfacesIsTests(element, generateTypeTest, alreadyGenerated); 579 generateInterfacesIsTests(element, generateTypeTest, alreadyGenerated);
579 } 580 }
580 } 581 }
581 582
582 void emitClasses(StringBuffer buffer) { 583 void emitClasses(CodeBuffer buffer) {
583 Set<ClassElement> instantiatedClasses = 584 Set<ClassElement> instantiatedClasses =
584 compiler.codegenWorld.instantiatedClasses; 585 compiler.codegenWorld.instantiatedClasses;
585 Set<ClassElement> neededClasses = 586 Set<ClassElement> neededClasses =
586 new Set<ClassElement>.from(instantiatedClasses); 587 new Set<ClassElement>.from(instantiatedClasses);
587 for (ClassElement element in instantiatedClasses) { 588 for (ClassElement element in instantiatedClasses) {
588 for (ClassElement superclass = element.superclass; 589 for (ClassElement superclass = element.superclass;
589 superclass !== null; 590 superclass !== null;
590 superclass = superclass.superclass) { 591 superclass = superclass.superclass) {
591 if (neededClasses.contains(superclass)) break; 592 if (neededClasses.contains(superclass)) break;
592 neededClasses.add(superclass); 593 neededClasses.add(superclass);
(...skipping 12 matching lines...) Expand all
605 } 606 }
606 607
607 // The closure class could have become necessary because of the generation 608 // The closure class could have become necessary because of the generation
608 // of stubs. 609 // of stubs.
609 ClassElement closureClass = compiler.closureClass; 610 ClassElement closureClass = compiler.closureClass;
610 if (needsClosureClass && !instantiatedClasses.contains(closureClass)) { 611 if (needsClosureClass && !instantiatedClasses.contains(closureClass)) {
611 generateClass(closureClass, buffer); 612 generateClass(closureClass, buffer);
612 } 613 }
613 } 614 }
614 615
615 void emitFinishClassesInvocationIfNecessary(StringBuffer buffer) { 616 void emitFinishClassesInvocationIfNecessary(CodeBuffer buffer) {
616 if (needsDefineClass) { 617 if (needsDefineClass) {
617 buffer.add("$finishClassesName($classesCollector);\n"); 618 buffer.add("$finishClassesName($classesCollector);\n");
618 // Reset the map. 619 // Reset the map.
619 buffer.add("$classesCollector = {};\n"); 620 buffer.add("$classesCollector = {};\n");
620 } 621 }
621 } 622 }
622 623
623 void emitStaticFunctionsWithNamer(StringBuffer buffer, 624 void emitStaticFunctionsWithNamer(CodeBuffer buffer,
624 Map<Element, CodeBlock> generatedCode, 625 Map<Element, CodeBuffer> generatedCode,
625 String functionNamer(Element element)) { 626 String functionNamer(Element element)) {
626 generatedCode.forEach((Element element, CodeBlock codeBlock) { 627 generatedCode.forEach((Element element, CodeBuffer functionBuffer) {
627 if (!element.isInstanceMember()) { 628 if (!element.isInstanceMember()) {
628 String functionName = functionNamer(element); 629 String functionName = functionNamer(element);
629 buffer.add('$isolateProperties.$functionName = '); 630 buffer.add('$isolateProperties.$functionName = ');
630 sourceMapBuilder.addCodeBlock(codeBlock.sourceMappings, buffer.length); 631 addMappings(functionBuffer, buffer.length);
631 buffer.add('${codeBlock.code};\n\n'); 632 buffer.add(functionBuffer);
633 buffer.add(';\n\n');
632 } 634 }
633 }); 635 });
634 } 636 }
635 637
636 void emitStaticFunctions(StringBuffer buffer) { 638 void emitStaticFunctions(CodeBuffer buffer) {
637 emitStaticFunctionsWithNamer(buffer, 639 emitStaticFunctionsWithNamer(buffer,
638 compiler.codegenWorld.generatedCode, 640 compiler.codegenWorld.generatedCode,
639 namer.getName); 641 namer.getName);
640 emitStaticFunctionsWithNamer(buffer, 642 emitStaticFunctionsWithNamer(buffer,
641 compiler.codegenWorld.generatedBailoutCode, 643 compiler.codegenWorld.generatedBailoutCode,
642 namer.getBailoutName); 644 namer.getBailoutName);
643 } 645 }
644 646
645 void emitStaticFunctionGetters(StringBuffer buffer) { 647 void emitStaticFunctionGetters(CodeBuffer buffer) {
646 Set<FunctionElement> functionsNeedingGetter = 648 Set<FunctionElement> functionsNeedingGetter =
647 compiler.codegenWorld.staticFunctionsNeedingGetter; 649 compiler.codegenWorld.staticFunctionsNeedingGetter;
648 for (FunctionElement element in functionsNeedingGetter) { 650 for (FunctionElement element in functionsNeedingGetter) {
649 // The static function does not have the correct name. Since 651 // The static function does not have the correct name. Since
650 // [addParameterStubs] use the name to create its stubs we simply 652 // [addParameterStubs] use the name to create its stubs we simply
651 // create a fake element with the correct name. 653 // create a fake element with the correct name.
652 // Note: the callElement will not have any enclosingElement. 654 // Note: the callElement will not have any enclosingElement.
653 FunctionElement callElement = 655 FunctionElement callElement =
654 new ClosureInvocationElement(namer.CLOSURE_INVOCATION_NAME, element); 656 new ClosureInvocationElement(namer.CLOSURE_INVOCATION_NAME, element);
655 String staticName = namer.getName(element); 657 String staticName = namer.getName(element);
656 int parameterCount = element.parameterCount(compiler); 658 int parameterCount = element.parameterCount(compiler);
657 String invocationName = 659 String invocationName =
658 namer.instanceMethodName(element.getLibrary(), callElement.name, 660 namer.instanceMethodName(element.getLibrary(), callElement.name,
659 parameterCount); 661 parameterCount);
660 String fieldAccess = '$isolateProperties.$staticName'; 662 String fieldAccess = '$isolateProperties.$staticName';
661 buffer.add("$fieldAccess.$invocationName = $fieldAccess;\n"); 663 buffer.add("$fieldAccess.$invocationName = $fieldAccess;\n");
662 addParameterStubs(callElement, ( 664 addParameterStubs(callElement, (String name, CodeBuffer value) {
663 String name, String value,
664 [List<SourceMappingEntry> sourceMappings]) {
665 buffer.add('$fieldAccess.$name = $value;\n'); 665 buffer.add('$fieldAccess.$name = $value;\n');
666 }); 666 });
667 // If a static function is used as a closure we need to add its name 667 // If a static function is used as a closure we need to add its name
668 // in case it is used in spawnFunction. 668 // in case it is used in spawnFunction.
669 String fieldName = namer.STATIC_CLOSURE_NAME_NAME; 669 String fieldName = namer.STATIC_CLOSURE_NAME_NAME;
670 buffer.add('$fieldAccess.$fieldName = "$staticName";\n'); 670 buffer.add('$fieldAccess.$fieldName = "$staticName";\n');
671 } 671 }
672 } 672 }
673 673
674 void emitDynamicFunctionGetter(FunctionElement member, 674 void emitDynamicFunctionGetter(FunctionElement member,
(...skipping 51 matching lines...) Expand 10 before | Expand all | Expand 10 after
726 callElement.name, parameterCount); 726 callElement.name, parameterCount);
727 List<String> arguments = new List<String>(parameterCount); 727 List<String> arguments = new List<String>(parameterCount);
728 for (int i = 0; i < parameterCount; i++) { 728 for (int i = 0; i < parameterCount; i++) {
729 arguments[i] = "p$i"; 729 arguments[i] = "p$i";
730 } 730 }
731 String joinedArgs = Strings.join(arguments, ", "); 731 String joinedArgs = Strings.join(arguments, ", ");
732 boundClosureBuffer.add( 732 boundClosureBuffer.add(
733 "$invocationName: function($joinedArgs) {"); 733 "$invocationName: function($joinedArgs) {");
734 boundClosureBuffer.add(" return this.self[this.target]($joinedArgs);"); 734 boundClosureBuffer.add(" return this.self[this.target]($joinedArgs);");
735 boundClosureBuffer.add(" }"); 735 boundClosureBuffer.add(" }");
736 addParameterStubs(callElement, ( 736 addParameterStubs(callElement, (String stubName, CodeBuffer memberValue) {
737 String stubName, String memberValue,
738 [List<SourceMappingEntry> sourceMappings]) {
739 boundClosureBuffer.add(',\n $stubName: $memberValue'); 737 boundClosureBuffer.add(',\n $stubName: $memberValue');
740 }); 738 });
741 boundClosureBuffer.add("\n});\n"); 739 boundClosureBuffer.add("\n});\n");
742 740
743 closureClass = namer.isolateAccess(closureClassElement); 741 closureClass = namer.isolateAccess(closureClassElement);
744 742
745 // Cache it. 743 // Cache it.
746 if (!hasOptionalParameters) { 744 if (!hasOptionalParameters) {
747 boundClosureCache[parameterCount] = closureClass; 745 boundClosureCache[parameterCount] = closureClass;
748 } 746 }
749 } 747 }
750 748
751 // And finally the getter. 749 // And finally the getter.
752 String getterName = namer.getterName(member.getLibrary(), member.name); 750 String getterName = namer.getterName(member.getLibrary(), member.name);
753 String targetName = namer.instanceMethodName(member.getLibrary(), 751 String targetName = namer.instanceMethodName(member.getLibrary(),
754 member.name, parameterCount); 752 member.name, parameterCount);
755 defineInstanceMember( 753 CodeBuffer getterBuffer = new CodeBuffer();
756 getterName, 754 getterBuffer.add(
757 "function() { return new $closureClass(this, '$targetName'); }"); 755 "function() { return new $closureClass(this, '$targetName'); }");
756 defineInstanceMember(getterName, getterBuffer);
758 } 757 }
759 758
760 void emitCallStubForGetter(Element member, 759 void emitCallStubForGetter(Element member,
761 Set<Selector> selectors, 760 Set<Selector> selectors,
762 DefineMemberFunction defineInstanceMember) { 761 DefineMemberFunction defineInstanceMember) {
763 String getter; 762 String getter;
764 if (member.kind == ElementKind.GETTER) { 763 if (member.kind == ElementKind.GETTER) {
765 getter = "this.${namer.getterName(member.getLibrary(), member.name)}()"; 764 getter = "this.${namer.getterName(member.getLibrary(), member.name)}()";
766 } else { 765 } else {
767 String name = namer.instanceFieldName(member.getEnclosingClass(), 766 String name = namer.instanceFieldName(member.getEnclosingClass(),
768 member.name); 767 member.name);
769 getter = "this.$name"; 768 getter = "this.$name";
770 } 769 }
771 for (Selector selector in selectors) { 770 for (Selector selector in selectors) {
772 if (selector.applies(member, compiler)) { 771 if (selector.applies(member, compiler)) {
773 String invocationName = 772 String invocationName =
774 namer.instanceMethodInvocationName(member.getLibrary(), member.name, 773 namer.instanceMethodInvocationName(member.getLibrary(), member.name,
775 selector); 774 selector);
776 SourceString callName = namer.CLOSURE_INVOCATION_NAME; 775 SourceString callName = namer.CLOSURE_INVOCATION_NAME;
777 String closureCallName = 776 String closureCallName =
778 namer.instanceMethodInvocationName(member.getLibrary(), callName, 777 namer.instanceMethodInvocationName(member.getLibrary(), callName,
779 selector); 778 selector);
780 List<String> arguments = <String>[]; 779 List<String> arguments = <String>[];
781 for (int i = 0; i < selector.argumentCount; i++) { 780 for (int i = 0; i < selector.argumentCount; i++) {
782 arguments.add("arg$i"); 781 arguments.add("arg$i");
783 } 782 }
784 String joined = Strings.join(arguments, ", "); 783 String joined = Strings.join(arguments, ", ");
785 defineInstanceMember( 784 CodeBuffer getterBuffer = new CodeBuffer();
786 invocationName, 785 getterBuffer.add(
787 "function($joined) { return $getter.$closureCallName($joined); }"); 786 "function($joined) { return $getter.$closureCallName($joined); }");
787 defineInstanceMember(invocationName, getterBuffer);
788 } 788 }
789 } 789 }
790 } 790 }
791 791
792 void emitStaticNonFinalFieldInitializations(StringBuffer buffer) { 792 void emitStaticNonFinalFieldInitializations(CodeBuffer buffer) {
793 ConstantHandler handler = compiler.constantHandler; 793 ConstantHandler handler = compiler.constantHandler;
794 List<VariableElement> staticNonFinalFields = 794 List<VariableElement> staticNonFinalFields =
795 handler.getStaticNonFinalFieldsForEmission(); 795 handler.getStaticNonFinalFieldsForEmission();
796 for (Element element in staticNonFinalFields) { 796 for (Element element in staticNonFinalFields) {
797 buffer.add('$isolateProperties.${namer.getName(element)} = '); 797 buffer.add('$isolateProperties.${namer.getName(element)} = ');
798 compiler.withCurrentElement(element, () { 798 compiler.withCurrentElement(element, () {
799 handler.writeJsCodeForVariable(buffer, element); 799 handler.writeJsCodeForVariable(buffer, element);
800 }); 800 });
801 buffer.add(';\n'); 801 buffer.add(';\n');
802 } 802 }
803 } 803 }
804 804
805 void emitCompileTimeConstants(StringBuffer buffer) { 805 void emitCompileTimeConstants(CodeBuffer buffer) {
806 ConstantHandler handler = compiler.constantHandler; 806 ConstantHandler handler = compiler.constantHandler;
807 List<Constant> constants = handler.getConstantsForEmission(); 807 List<Constant> constants = handler.getConstantsForEmission();
808 bool addedMakeConstantList = false; 808 bool addedMakeConstantList = false;
809 for (Constant constant in constants) { 809 for (Constant constant in constants) {
810 String name = handler.getNameForConstant(constant); 810 String name = handler.getNameForConstant(constant);
811 // The name is null when the constant is already a JS constant. 811 // The name is null when the constant is already a JS constant.
812 // TODO(floitsch): every constant should be registered, so that we can 812 // TODO(floitsch): every constant should be registered, so that we can
813 // share the ones that take up too much space (like some strings). 813 // share the ones that take up too much space (like some strings).
814 if (name === null) continue; 814 if (name === null) continue;
815 if (!addedMakeConstantList && constant.isList()) { 815 if (!addedMakeConstantList && constant.isList()) {
816 addedMakeConstantList = true; 816 addedMakeConstantList = true;
817 emitMakeConstantList(buffer); 817 emitMakeConstantList(buffer);
818 } 818 }
819 buffer.add('$isolateProperties.$name = '); 819 buffer.add('$isolateProperties.$name = ');
820 handler.writeJsCode(buffer, constant); 820 handler.writeJsCode(buffer, constant);
821 buffer.add(';\n'); 821 buffer.add(';\n');
822 } 822 }
823 } 823 }
824 824
825 void emitMakeConstantList(StringBuffer buffer) { 825 void emitMakeConstantList(CodeBuffer buffer) {
826 buffer.add(namer.ISOLATE); 826 buffer.add(namer.ISOLATE);
827 buffer.add(@'''.makeConstantList = function(list) { 827 buffer.add(@'''.makeConstantList = function(list) {
828 list.immutable$list = true; 828 list.immutable$list = true;
829 list.fixed$length = true; 829 list.fixed$length = true;
830 return list; 830 return list;
831 }; 831 };
832 '''); 832 ''');
833 } 833 }
834 834
835 void emitExtraAccessors(Element member, 835 void emitExtraAccessors(Element member,
(...skipping 15 matching lines...) Expand all
851 if (compiler.codegenWorld.instantiatedClasses.isEmpty()) return; 851 if (compiler.codegenWorld.instantiatedClasses.isEmpty()) return;
852 852
853 ClassElement objectClass = 853 ClassElement objectClass =
854 compiler.coreLibrary.find(const SourceString('Object')); 854 compiler.coreLibrary.find(const SourceString('Object'));
855 String runtimeObjectPrototype = 855 String runtimeObjectPrototype =
856 '${namer.isolateAccess(objectClass)}.prototype'; 856 '${namer.isolateAccess(objectClass)}.prototype';
857 String noSuchMethodName = 857 String noSuchMethodName =
858 namer.instanceMethodName(null, Compiler.NO_SUCH_METHOD, 2); 858 namer.instanceMethodName(null, Compiler.NO_SUCH_METHOD, 2);
859 Collection<LibraryElement> libraries = compiler.libraries.getValues(); 859 Collection<LibraryElement> libraries = compiler.libraries.getValues();
860 860
861 String generateMethod(String methodName, Selector selector) { 861 CodeBuffer generateMethod(String methodName, Selector selector) {
862 StringBuffer buffer = new StringBuffer(); 862 CodeBuffer buffer = new CodeBuffer();
863 buffer.add('function'); 863 buffer.add('function');
864 StringBuffer args = new StringBuffer(); 864 CodeBuffer args = new CodeBuffer();
865 for (int i = 0; i < selector.argumentCount; i++) { 865 for (int i = 0; i < selector.argumentCount; i++) {
866 if (i != 0) args.add(', '); 866 if (i != 0) args.add(', ');
867 args.add('arg$i'); 867 args.add('arg$i');
868 } 868 }
869 // We need to check if the object has a noSuchMethod. If not, it 869 // We need to check if the object has a noSuchMethod. If not, it
870 // means the object is a native object, and we can just call our 870 // means the object is a native object, and we can just call our
871 // generic noSuchMethod. Note that when calling this method, the 871 // generic noSuchMethod. Note that when calling this method, the
872 // 'this' object is not a Dart object. 872 // 'this' object is not a Dart object.
873 buffer.add(' ($args) {\n'); 873 buffer.add(' ($args) {\n');
874 buffer.add(' return this.$noSuchMethodName\n'); 874 buffer.add(' return this.$noSuchMethodName\n');
875 buffer.add(" ? this.$noSuchMethodName('$methodName', [$args])\n"); 875 buffer.add(" ? this.$noSuchMethodName('$methodName', [$args])\n");
876 buffer.add(" : $runtimeObjectPrototype.$noSuchMethodName.call("); 876 buffer.add(" : $runtimeObjectPrototype.$noSuchMethodName.call(");
877 buffer.add("this, '$methodName', [$args])\n"); 877 buffer.add("this, '$methodName', [$args])\n");
878 buffer.add('}'); 878 buffer.add('}');
879 return buffer.toString(); 879 return buffer;
880 } 880 }
881 881
882 compiler.codegenWorld.invokedNames.forEach((SourceString methodName, 882 compiler.codegenWorld.invokedNames.forEach((SourceString methodName,
883 Set<Selector> selectors) { 883 Set<Selector> selectors) {
884 if (objectClass.lookupLocalMember(methodName) === null 884 if (objectClass.lookupLocalMember(methodName) === null
885 && methodName != Elements.OPERATOR_EQUALS) { 885 && methodName != Elements.OPERATOR_EQUALS) {
886 for (Selector selector in selectors) { 886 for (Selector selector in selectors) {
887 if (methodName.isPrivate()) { 887 if (methodName.isPrivate()) {
888 for (LibraryElement lib in libraries) { 888 for (LibraryElement lib in libraries) {
889 String jsName = 889 String jsName =
890 namer.instanceMethodInvocationName(lib, methodName, selector); 890 namer.instanceMethodInvocationName(lib, methodName, selector);
891 String method = 891 CodeBuffer method =
892 generateMethod(methodName.slowToString(), selector); 892 generateMethod(methodName.slowToString(), selector);
893 defineInstanceMember(jsName, method); 893 defineInstanceMember(jsName, method);
894 } 894 }
895 } else { 895 } else {
896 String jsName = 896 String jsName =
897 namer.instanceMethodInvocationName(null, methodName, selector); 897 namer.instanceMethodInvocationName(null, methodName, selector);
898 String method = generateMethod(methodName.slowToString(), selector); 898 CodeBuffer method = generateMethod(methodName.slowToString(),
899 selector);
899 defineInstanceMember(jsName, method); 900 defineInstanceMember(jsName, method);
900 } 901 }
901 } 902 }
902 } 903 }
903 }); 904 });
904 905
905 compiler.codegenWorld.invokedGetters.forEach((SourceString getterName, 906 compiler.codegenWorld.invokedGetters.forEach((SourceString getterName,
906 Set<Selector> selectors) { 907 Set<Selector> selectors) {
907 if (getterName.isPrivate()) { 908 if (getterName.isPrivate()) {
908 for (LibraryElement lib in libraries) { 909 for (LibraryElement lib in libraries) {
909 String jsName = namer.getterName(lib, getterName); 910 String jsName = namer.getterName(lib, getterName);
910 String method = generateMethod('get ${getterName.slowToString()}', 911 CodeBuffer method = generateMethod('get ${getterName.slowToString()}',
911 Selector.GETTER); 912 Selector.GETTER);
912 defineInstanceMember(jsName, method); 913 defineInstanceMember(jsName, method);
913 } 914 }
914 } else { 915 } else {
915 String jsName = namer.getterName(null, getterName); 916 String jsName = namer.getterName(null, getterName);
916 String method = generateMethod('get ${getterName.slowToString()}', 917 CodeBuffer method = generateMethod('get ${getterName.slowToString()}',
917 Selector.GETTER); 918 Selector.GETTER);
918 defineInstanceMember(jsName, method); 919 defineInstanceMember(jsName, method);
919 } 920 }
920 }); 921 });
921 922
922 compiler.codegenWorld.invokedSetters.forEach((SourceString setterName, 923 compiler.codegenWorld.invokedSetters.forEach((SourceString setterName,
923 Set<Selector> selectors) { 924 Set<Selector> selectors) {
924 if (setterName.isPrivate()) { 925 if (setterName.isPrivate()) {
925 for (LibraryElement lib in libraries) { 926 for (LibraryElement lib in libraries) {
926 String jsName = namer.setterName(lib, setterName); 927 String jsName = namer.setterName(lib, setterName);
927 String method = generateMethod('set ${setterName.slowToString()}', 928 CodeBuffer method = generateMethod('set ${setterName.slowToString()}',
928 Selector.SETTER); 929 Selector.SETTER);
929 defineInstanceMember(jsName, method); 930 defineInstanceMember(jsName, method);
930 } 931 }
931 } else { 932 } else {
932 String jsName = namer.setterName(null, setterName); 933 String jsName = namer.setterName(null, setterName);
933 String method = generateMethod('set ${setterName.slowToString()}', 934 CodeBuffer method = generateMethod('set ${setterName.slowToString()}',
934 Selector.SETTER); 935 Selector.SETTER);
935 defineInstanceMember(jsName, method); 936 defineInstanceMember(jsName, method);
936 } 937 }
937 }); 938 });
938 } 939 }
939 940
940 String buildIsolateSetup(StringBuffer buffer, 941 String buildIsolateSetup(CodeBuffer buffer,
941 Element appMain, 942 Element appMain,
942 Element isolateMain) { 943 Element isolateMain) {
943 String mainAccess = "${namer.isolateAccess(appMain)}"; 944 String mainAccess = "${namer.isolateAccess(appMain)}";
944 String currentIsolate = "${namer.CURRENT_ISOLATE}"; 945 String currentIsolate = "${namer.CURRENT_ISOLATE}";
945 String mainEnsureGetter = ''; 946 String mainEnsureGetter = '';
946 // Since we pass the closurized version of the main method to 947 // Since we pass the closurized version of the main method to
947 // the isolate method, we must make sure that it exists. 948 // the isolate method, we must make sure that it exists.
948 if (!compiler.codegenWorld.staticFunctionsNeedingGetter.contains(appMain)) { 949 if (!compiler.codegenWorld.staticFunctionsNeedingGetter.contains(appMain)) {
949 String invocationName = 950 String invocationName =
950 "${namer.closureInvocationName(Selector.INVOCATION_0)}"; 951 "${namer.closureInvocationName(Selector.INVOCATION_0)}";
(...skipping 17 matching lines...) Expand all
968 } 969 }
969 function \$setGlobals(context) { 970 function \$setGlobals(context) {
970 $currentIsolate = context.isolateStatics; 971 $currentIsolate = context.isolateStatics;
971 \$globalThis = $currentIsolate; 972 \$globalThis = $currentIsolate;
972 } 973 }
973 $mainEnsureGetter 974 $mainEnsureGetter
974 """); 975 """);
975 return "${namer.isolateAccess(isolateMain)}($mainAccess)"; 976 return "${namer.isolateAccess(isolateMain)}($mainAccess)";
976 } 977 }
977 978
978 emitMain(StringBuffer buffer) { 979 emitMain(CodeBuffer buffer) {
979 if (compiler.isMockCompilation) return; 980 if (compiler.isMockCompilation) return;
980 Element main = compiler.mainApp.find(Compiler.MAIN); 981 Element main = compiler.mainApp.find(Compiler.MAIN);
981 String mainCall = null; 982 String mainCall = null;
982 if (compiler.isolateLibrary != null) { 983 if (compiler.isolateLibrary != null) {
983 Element isolateMain = 984 Element isolateMain =
984 compiler.isolateLibrary.find(Compiler.START_ROOT_ISOLATE); 985 compiler.isolateLibrary.find(Compiler.START_ROOT_ISOLATE);
985 mainCall = buildIsolateSetup(buffer, main, isolateMain); 986 mainCall = buildIsolateSetup(buffer, main, isolateMain);
986 } else { 987 } else {
987 mainCall = '${namer.isolateAccess(main)}()'; 988 mainCall = '${namer.isolateAccess(main)}()';
988 } 989 }
(...skipping 61 matching lines...) Expand 10 before | Expand all | Expand 10 after
1050 String sourceMap = sourceMapBuilder.build(compiledFile); 1051 String sourceMap = sourceMapBuilder.build(compiledFile);
1051 // TODO(podivilov): We should find a better way to return source maps to 1052 // TODO(podivilov): We should find a better way to return source maps to
1052 // compiler. Using diagnostic handler for that purpose is a temporary 1053 // compiler. Using diagnostic handler for that purpose is a temporary
1053 // hack. 1054 // hack.
1054 compiler.reportDiagnostic( 1055 compiler.reportDiagnostic(
1055 null, sourceMap, new api.Diagnostic(-1, 'source map')); 1056 null, sourceMap, new api.Diagnostic(-1, 'source map'));
1056 } 1057 }
1057 }); 1058 });
1058 return compiler.assembledCode; 1059 return compiler.assembledCode;
1059 } 1060 }
1061
1062 void addMappings(CodeBuffer buffer, int bufferOffset) {
1063 buffer.forEachSourceLocation((Element element, Token token, int offset) {
1064 SourceFile sourceFile = element.getCompilationUnit().script.file;
1065 String sourceName = null;
1066 if (token.kind === IDENTIFIER_TOKEN) {
1067 sourceName = token.slowToString();
1068 }
1069 int totalOffset = bufferOffset + offset;
1070 sourceMapBuilder.addMapping(
1071 sourceFile, token.charOffset, sourceName, totalOffset);
1072 });
1073 }
1060 } 1074 }
1061 1075
1062 typedef void DefineMemberFunction(String invocationName, 1076 typedef void DefineMemberFunction(String invocationName, CodeBuffer definition);
1063 String definition,
1064 [List<SourceMappingEntry> sourceMappings]);
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698