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

Side by Side Diff: sdk/lib/_internal/compiler/implementation/compile_time_constants.dart

Issue 221873002: Compute frontend/backend specific constants. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 6 years, 8 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 part of dart2js; 5 part of dart2js;
6 6
7 abstract class ConstantHandler {
floitsch 2014/04/02 16:50:02 The name "ConstantHandler" is now used with three
Johnni Winther 2014/04/07 11:42:27 Done.
8 Constant getConstantForVariable(VariableElement element);
9 }
10
11 abstract class ConstantCompiler extends ConstantHandler {
12 /**
13 * Returns a compile-time constant, or reports an error if the element is not
14 * a compile-time constant.
15 */
16 Constant compileConstant(VariableElement element);
17
18 Constant compileNode(Node node, TreeElements elements);
karlklose 2014/04/02 13:04:44 Please ad comments to these methods, too. And perh
Johnni Winther 2014/04/07 11:42:27 Done.
19
20 Constant compileMetadata(MetadataAnnotation metadata,
21 Node node, TreeElements elements);
22 }
23
24 abstract class BackendConstantHandler extends ConstantHandler {
25 /**
26 * Returns the a compile-time constant if the variable could be compiled
27 * eagerly. Otherwise returns `null`.
28 */
29 void compileVariable(VariableElement element);
30
31 Constant getConstantForNode(Node node, TreeElements elements);
32
33 Constant getConstantForMetadata(MetadataAnnotation metadata);
34 }
35
36 class DartConstantHandler extends CompilerTask
37 implements ConstantCompiler, BackendConstantHandler {
38 final DartConstantCompiler constantCompiler;
39
40 DartConstantHandler(Compiler compiler)
41 : this.constantCompiler = new DartConstantCompiler(compiler),
42 super(compiler);
43
44 String get name => 'ConstantHandler';
45
46 Constant getConstantForVariable(VariableElement element) {
47 return constantCompiler.getConstantForVariable(element);
48 }
49
50 Constant getConstantForNode(Node node, TreeElements elements) {
51 return constantCompiler.getConstantForNode(node, elements);
52 }
53
54 Constant getConstantForMetadata(MetadataAnnotation metadata) {
55 return metadata.value;
56 }
57
58 Constant compileConstant(VariableElement element) {
59 return measure(() {
60 return constantCompiler.compileConstant(element);
61 });
62 }
63
64 Constant compileVariable(VariableElement element) {
65 return measure(() {
66 return constantCompiler.compileVariable(element);
67 });
68 }
69
70 Constant compileNode(Node node, TreeElements elements) {
71 return measure(() {
72 return constantCompiler.compileNodeWithDefinitions(node, elements);
73 });
74 }
75
76 Constant compileMetadata(MetadataAnnotation metadata,
77 Node node,
78 TreeElements elements) {
79 return measure(() {
80 return constantCompiler.compileMetadata(metadata, node, elements);
81 });
82 }
83 }
84
7 /** 85 /**
8 * The [ConstantHandler] keeps track of compile-time constants, 86 * The [ConstantCompilerBase] is used to keeps track of compile-time constants,
9 * initializations of global and static fields, and default values of 87 * initializations of global and static fields, and default values of
10 * optional parameters. 88 * optional parameters.
11 */ 89 */
12 class ConstantHandler extends CompilerTask { 90 abstract class ConstantCompilerBase implements ConstantCompiler {
91 final Compiler compiler;
13 final ConstantSystem constantSystem; 92 final ConstantSystem constantSystem;
14 final bool isMetadata;
15 93
16 /** 94 /**
17 * Contains the initial value of fields. Must contain all static and global 95 * Contains the initial value of fields. Must contain all static and global
18 * initializations of const fields. May contain eagerly compiled values for 96 * initializations of const fields. May contain eagerly compiled values for
19 * statics and instance fields. 97 * statics and instance fields.
20 * 98 *
21 * Invariant: The keys in this map are declarations. 99 * Invariant: The keys in this map are declarations.
22 */ 100 */
23 final Map<VariableElement, Constant> initialVariableValues; 101 final Map<VariableElement, Constant> initialVariableValues =
24 102 new Map<VariableElement, Constant>();
25 /** Set of all registered compiled constants. */
26 final Set<Constant> compiledConstants;
27 103
28 /** The set of variable elements that are in the process of being computed. */ 104 /** The set of variable elements that are in the process of being computed. */
29 final Set<VariableElement> pendingVariables; 105 final Set<VariableElement> pendingVariables = new Set<VariableElement>();
30 106
31 /** Caches the statics where the initial value cannot be eagerly compiled. */ 107 ConstantCompilerBase(this.compiler, this.constantSystem);
32 final Set<VariableElement> lazyStatics;
33
34 ConstantHandler(Compiler compiler, this.constantSystem,
35 { bool this.isMetadata: false })
36 : initialVariableValues = new Map<VariableElement, dynamic>(),
37 compiledConstants = new Set<Constant>(),
38 pendingVariables = new Set<VariableElement>(),
39 lazyStatics = new Set<VariableElement>(),
40 super(compiler);
41
42 String get name => 'ConstantHandler';
43
44 void addCompileTimeConstantForEmission(Constant constant) {
45 compiledConstants.add(constant);
46 }
47 108
48 Constant getConstantForVariable(VariableElement element) { 109 Constant getConstantForVariable(VariableElement element) {
49 return initialVariableValues[element.declaration]; 110 return initialVariableValues[element.declaration];
50 } 111 }
51 112
52 /**
53 * Returns a compile-time constant, or reports an error if the element is not
54 * a compile-time constant.
55 */
56 Constant compileConstant(VariableElement element) { 113 Constant compileConstant(VariableElement element) {
57 return compileVariable(element, isConst: true); 114 return compileVariable(element, isConst: true);
58 } 115 }
59 116
60 /**
61 * Returns the a compile-time constant if the variable could be compiled
62 * eagerly. Otherwise returns `null`.
63 */
64 Constant compileVariable(VariableElement element, {bool isConst: false}) { 117 Constant compileVariable(VariableElement element, {bool isConst: false}) {
65 return measure(() { 118
66 if (initialVariableValues.containsKey(element.declaration)) { 119 if (initialVariableValues.containsKey(element.declaration)) {
67 Constant result = initialVariableValues[element.declaration]; 120 Constant result = initialVariableValues[element.declaration];
68 return result; 121 return result;
69 } 122 }
70 Element currentElement = element; 123 Element currentElement = element;
71 if (element.isParameter() 124 if (element.isParameter() ||
72 || element.isFieldParameter() 125 element.isFieldParameter() ||
73 || element.isVariable()) { 126 element.isVariable()) {
74 currentElement = element.enclosingElement; 127 currentElement = element.enclosingElement;
75 } 128 }
76 return compiler.withCurrentElement(currentElement, () { 129 return compiler.withCurrentElement(currentElement, () {
77 TreeElements definitions = 130 TreeElements definitions =
78 compiler.analyzeElement(currentElement.declaration); 131 compiler.analyzeElement(currentElement.declaration);
79 Constant constant = compileVariableWithDefinitions( 132 Constant constant = compileVariableWithDefinitions(
80 element, definitions, isConst: isConst); 133 element, definitions, isConst: isConst);
81 return constant; 134 return constant;
82 });
83 }); 135 });
84 } 136 }
85 137
86 /** 138 /**
87 * Returns the a compile-time constant if the variable could be compiled 139 * Returns the a compile-time constant if the variable could be compiled
88 * eagerly. If the variable needs to be initialized lazily returns `null`. 140 * eagerly. If the variable needs to be initialized lazily returns `null`.
89 * If the variable is `const` but cannot be compiled eagerly reports an 141 * If the variable is `const` but cannot be compiled eagerly reports an
90 * error. 142 * error.
91 */ 143 */
92 Constant compileVariableWithDefinitions(VariableElement element, 144 Constant compileVariableWithDefinitions(VariableElement element,
93 TreeElements definitions, 145 TreeElements definitions,
94 {bool isConst: false}) { 146 {bool isConst: false}) {
95 return measure(() { 147 Node node = element.parseNode(compiler);
96 if (!isConst && lazyStatics.contains(element)) return null; 148 if (pendingVariables.contains(element)) {
149 if (isConst) {
150 compiler.reportFatalError(
151 node, MessageKind.CYCLIC_COMPILE_TIME_CONSTANTS);
152 }
153 return null;
154 }
155 pendingVariables.add(element);
97 156
98 Node node = element.parseNode(compiler); 157 Expression initializer = element.initializer;
99 if (pendingVariables.contains(element)) { 158 Constant value;
100 if (isConst) { 159 if (initializer == null) {
101 compiler.reportFatalError( 160 // No initial value.
102 node, MessageKind.CYCLIC_COMPILE_TIME_CONSTANTS); 161 value = new NullConstant();
162 } else {
163 value = compileNodeWithDefinitions(
164 initializer, definitions, isConst: isConst);
165 if (compiler.enableTypeAssertions &&
166 value != null &&
167 element.isField()) {
168 DartType elementType = element.type;
169 if (elementType.kind == TypeKind.MALFORMED_TYPE && !value.isNull) {
170 if (isConst) {
171 ErroneousElement element = elementType.element;
172 compiler.reportFatalError(
173 node, element.messageKind, element.messageArguments);
174 } else {
175 // We need to throw an exception at runtime.
176 value = null;
177 }
103 } else { 178 } else {
104 lazyStatics.add(element); 179 DartType constantType = value.computeType(compiler);
105 return null; 180 if (!constantSystem.isSubtype(compiler,
106 } 181 constantType, elementType)) {
107 }
108 pendingVariables.add(element);
109
110 Expression initializer = element.initializer;
111 Constant value;
112 if (initializer == null) {
113 // No initial value.
114 value = new NullConstant();
115 } else {
116 value = compileNodeWithDefinitions(
117 initializer, definitions, isConst: isConst);
118 if (compiler.enableTypeAssertions &&
119 value != null &&
120 element.isField()) {
121 DartType elementType = element.type;
122 if (elementType.kind == TypeKind.MALFORMED_TYPE && !value.isNull) {
123 if (isConst) { 182 if (isConst) {
124 ErroneousElement element = elementType.element;
125 compiler.reportFatalError( 183 compiler.reportFatalError(
126 node, element.messageKind, element.messageArguments); 184 node, MessageKind.NOT_ASSIGNABLE,
185 {'fromType': constantType, 'toType': elementType});
127 } else { 186 } else {
128 // We need to throw an exception at runtime. 187 // If the field cannot be lazily initialized, we will throw
188 // the exception at runtime.
129 value = null; 189 value = null;
130 } 190 }
131 } else {
132 DartType constantType = value.computeType(compiler);
133 if (!constantSystem.isSubtype(compiler,
134 constantType, elementType)) {
135 if (isConst) {
136 compiler.reportFatalError(
137 node, MessageKind.NOT_ASSIGNABLE,
138 {'fromType': constantType, 'toType': elementType});
139 } else {
140 // If the field cannot be lazily initialized, we will throw
141 // the exception at runtime.
142 value = null;
143 }
144 }
145 } 191 }
146 } 192 }
147 } 193 }
148 if (value != null) { 194 }
149 initialVariableValues[element.declaration] = value; 195 if (value != null) {
150 } else { 196 initialVariableValues[element.declaration] = value;
151 assert(!isConst); 197 } else {
152 lazyStatics.add(element); 198 assert(!isConst);
153 } 199 }
154 pendingVariables.remove(element); 200 pendingVariables.remove(element);
155 return value; 201 return value;
156 });
157 } 202 }
158 203
159 Constant compileNodeWithDefinitions(Node node, 204 Constant compileNodeWithDefinitions(Node node,
160 TreeElements definitions, 205 TreeElements definitions,
161 {bool isConst: false}) { 206 {bool isConst: true}) {
162 return measure(() { 207 assert(node != null);
163 assert(node != null); 208 CompileTimeConstantEvaluator evaluator = new CompileTimeConstantEvaluator(
164 Constant constant = definitions.getConstant(node); 209 this, definitions, compiler, isConst: isConst);
165 if (constant != null) { 210 return evaluator.evaluate(node);
166 return constant;
167 }
168 CompileTimeConstantEvaluator evaluator = new CompileTimeConstantEvaluator(
169 this, definitions, compiler, isConst: isConst);
170 constant = evaluator.evaluate(node);
171 if (constant != null) {
172 definitions.setConstant(node, constant);
173 }
174 return constant;
175 });
176 } 211 }
177 212
178 /** 213 Constant compileNode(Node node, TreeElements elements) {
179 * Returns an [Iterable] of static non final fields that need to be 214 return compileNodeWithDefinitions(node, elements);
180 * initialized. The fields list must be evaluated in order since they might
181 * depend on each other.
182 */
183 Iterable<VariableElement> getStaticNonFinalFieldsForEmission() {
184 return initialVariableValues.keys.where((element) {
185 return element.kind == ElementKind.FIELD
186 && !element.isInstanceMember()
187 && !element.modifiers.isFinal()
188 // The const fields are all either emitted elsewhere or inlined.
189 && !element.modifiers.isConst();
190 });
191 } 215 }
192 216
193 List<VariableElement> getLazilyInitializedFieldsForEmission() { 217 Constant compileMetadata(MetadataAnnotation metadata,
194 return new List<VariableElement>.from(lazyStatics); 218 Node node,
219 TreeElements elements) {
220 return compileNodeWithDefinitions(node, elements);
221 }
222 }
223
224 class DartConstantCompiler extends ConstantCompilerBase {
225 DartConstantCompiler(Compiler compiler)
226 : super(compiler, const DartConstantSystem());
227
228 Constant getConstantForNode(Node node, TreeElements definitions) {
229 return definitions.getConstant(node);
195 } 230 }
196 231
197 /** 232 Constant getConstantForMetadata(MetadataAnnotation metadata) {
198 * Returns a list of constants topologically sorted so that dependencies 233 return metadata.value;
199 * appear before the dependent constant. [preSortCompare] is a comparator
200 * function that gives the constants a consistent order prior to the
201 * topological sort which gives the constants an ordering that is less
202 * sensitive to perturbations in the source code.
203 */
204 List<Constant> getConstantsForEmission([preSortCompare]) {
205 // We must emit dependencies before their uses.
206 Set<Constant> seenConstants = new Set<Constant>();
207 List<Constant> result = new List<Constant>();
208
209 void addConstant(Constant constant) {
210 if (!seenConstants.contains(constant)) {
211 constant.getDependencies().forEach(addConstant);
212 assert(!seenConstants.contains(constant));
213 result.add(constant);
214 seenConstants.add(constant);
215 }
216 }
217
218 List<Constant> sorted = compiledConstants.toList();
219 if (preSortCompare != null) {
220 sorted.sort(preSortCompare);
221 }
222 sorted.forEach(addConstant);
223 return result;
224 } 234 }
225 235
226 Constant getInitialValueFor(VariableElement element) { 236 Constant compileNodeWithDefinitions(Node node,
227 Constant initialValue = initialVariableValues[element.declaration]; 237 TreeElements definitions,
228 if (initialValue == null) { 238 {bool isConst: true}) {
229 compiler.internalError(element, "No initial value for given element."); 239 Constant constant = definitions.getConstant(node);
240 if (constant != null) {
241 return constant;
230 } 242 }
231 return initialValue; 243 constant =
244 super.compileNodeWithDefinitions(node, definitions, isConst: isConst);
245 if (constant != null) {
246 definitions.setConstant(node, constant);
247 }
248 return constant;
232 } 249 }
233 } 250 }
234 251
235 class CompileTimeConstantEvaluator extends Visitor { 252 class CompileTimeConstantEvaluator extends Visitor {
236 bool isEvaluatingConstant; 253 bool isEvaluatingConstant;
237 final ConstantHandler handler; 254 final ConstantCompilerBase handler;
238 final TreeElements elements; 255 final TreeElements elements;
239 final Compiler compiler; 256 final Compiler compiler;
240 257
241 CompileTimeConstantEvaluator(this.handler, 258 CompileTimeConstantEvaluator(this.handler,
242 this.elements, 259 this.elements,
243 this.compiler, 260 this.compiler,
244 {bool isConst: false}) 261 {bool isConst: false})
245 : this.isEvaluatingConstant = isConst; 262 : this.isEvaluatingConstant = isConst;
246 263
247 ConstantSystem get constantSystem => handler.constantSystem; 264 ConstantSystem get constantSystem => handler.constantSystem;
(...skipping 185 matching lines...) Expand 10 before | Expand all | Expand 10 after
433 if (prefixNode != null) { 450 if (prefixNode != null) {
434 Element maybePrefix = elements[prefixNode.asIdentifier()]; 451 Element maybePrefix = elements[prefixNode.asIdentifier()];
435 if (maybePrefix != null && maybePrefix.isPrefix() && 452 if (maybePrefix != null && maybePrefix.isPrefix() &&
436 (maybePrefix as PrefixElement).isDeferred) { 453 (maybePrefix as PrefixElement).isDeferred) {
437 return true; 454 return true;
438 } 455 }
439 } 456 }
440 return false; 457 return false;
441 } 458 }
442 459
460 Constant visitIdentifier(Identifier node) {
461 Element element = elements[node];
462 if (Elements.isClass(element) || Elements.isTypedef(element)) {
463 return makeTypeConstant(element);
464 }
465 return signalNotCompileTimeConstant(node);
466 }
467
443 // TODO(floitsch): provide better error-messages. 468 // TODO(floitsch): provide better error-messages.
444 Constant visitSend(Send send) { 469 Constant visitSend(Send send) {
445 Element element = elements[send]; 470 Element element = elements[send];
446 if (send.isPropertyAccess) { 471 if (send.isPropertyAccess) {
447 if (isDeferredUse(send)) { 472 if (isDeferredUse(send)) {
448 return signalNotCompileTimeConstant(send, 473 return signalNotCompileTimeConstant(send,
449 message: MessageKind.DEFERRED_COMPILE_TIME_CONSTANT); 474 message: MessageKind.DEFERRED_COMPILE_TIME_CONSTANT);
450 } 475 }
451 if (Elements.isStaticOrTopLevelFunction(element)) { 476 if (Elements.isStaticOrTopLevelFunction(element)) {
452 return new FunctionConstant(element); 477 return new FunctionConstant(element);
(...skipping 17 matching lines...) Expand all
470 if (result != null) return result; 495 if (result != null) return result;
471 } 496 }
472 return signalNotCompileTimeConstant(send); 497 return signalNotCompileTimeConstant(send);
473 } else if (send.isCall) { 498 } else if (send.isCall) {
474 if (identical(element, compiler.identicalFunction) 499 if (identical(element, compiler.identicalFunction)
475 && send.argumentCount() == 2) { 500 && send.argumentCount() == 2) {
476 Constant left = evaluate(send.argumentsNode.nodes.head); 501 Constant left = evaluate(send.argumentsNode.nodes.head);
477 Constant right = evaluate(send.argumentsNode.nodes.tail.head); 502 Constant right = evaluate(send.argumentsNode.nodes.tail.head);
478 Constant result = constantSystem.identity.fold(left, right); 503 Constant result = constantSystem.identity.fold(left, right);
479 if (result != null) return result; 504 if (result != null) return result;
480 } else if (Elements.isClass(element) || Elements.isTypedef(element)) {
481 // The node itself is not a constant but we register the selector (the
482 // identifier that refers to the class/typedef) as a constant.
483 Constant typeConstant = makeTypeConstant(element);
484 elements.setConstant(send.selector, typeConstant);
485 } 505 }
486 return signalNotCompileTimeConstant(send); 506 return signalNotCompileTimeConstant(send);
487 } else if (send.isPrefix) { 507 } else if (send.isPrefix) {
488 assert(send.isOperator); 508 assert(send.isOperator);
489 Constant receiverConstant = evaluate(send.receiver); 509 Constant receiverConstant = evaluate(send.receiver);
490 if (receiverConstant == null) return null; 510 if (receiverConstant == null) return null;
491 Operator op = send.selector; 511 Operator op = send.selector;
492 Constant folded; 512 Constant folded;
493 switch (op.source) { 513 switch (op.source) {
494 case "!": 514 case "!":
(...skipping 308 matching lines...) Expand 10 before | Expand all | Expand 10 after
803 final FunctionElement constructor; 823 final FunctionElement constructor;
804 final Map<Element, Constant> definitions; 824 final Map<Element, Constant> definitions;
805 final Map<Element, Constant> fieldValues; 825 final Map<Element, Constant> fieldValues;
806 826
807 /** 827 /**
808 * Documentation wanted -- johnniwinther 828 * Documentation wanted -- johnniwinther
809 * 829 *
810 * Invariant: [constructor] must be an implementation element. 830 * Invariant: [constructor] must be an implementation element.
811 */ 831 */
812 ConstructorEvaluator(FunctionElement constructor, 832 ConstructorEvaluator(FunctionElement constructor,
813 ConstantHandler handler, 833 ConstantCompiler handler,
814 Compiler compiler) 834 Compiler compiler)
815 : this.constructor = constructor, 835 : this.constructor = constructor,
816 this.definitions = new Map<Element, Constant>(), 836 this.definitions = new Map<Element, Constant>(),
817 this.fieldValues = new Map<Element, Constant>(), 837 this.fieldValues = new Map<Element, Constant>(),
818 super(handler, 838 super(handler,
819 compiler.resolver.resolveMethodElement(constructor.declaration), 839 compiler.resolver.resolveMethodElement(constructor.declaration),
820 compiler, 840 compiler,
821 isConst: true) { 841 isConst: true) {
822 assert(invariant(constructor, constructor.isImplementation)); 842 assert(invariant(constructor, constructor.isImplementation));
823 } 843 }
(...skipping 12 matching lines...) Expand all
836 856
837 void potentiallyCheckType(Node node, 857 void potentiallyCheckType(Node node,
838 TypedElement element, 858 TypedElement element,
839 Constant constant) { 859 Constant constant) {
840 if (compiler.enableTypeAssertions) { 860 if (compiler.enableTypeAssertions) {
841 DartType elementType = element.type; 861 DartType elementType = element.type;
842 DartType constantType = constant.computeType(compiler); 862 DartType constantType = constant.computeType(compiler);
843 // TODO(ngeoffray): Handle type parameters. 863 // TODO(ngeoffray): Handle type parameters.
844 if (elementType.element.isTypeVariable()) return; 864 if (elementType.element.isTypeVariable()) return;
845 if (!constantSystem.isSubtype(compiler, constantType, elementType)) { 865 if (!constantSystem.isSubtype(compiler, constantType, elementType)) {
866 // TODO(johnniwinther): Provide better [node] values that point to the
867 // origin of the constant and not (just) the assignment.
846 compiler.reportFatalError( 868 compiler.reportFatalError(
847 node, MessageKind.NOT_ASSIGNABLE, 869 node, MessageKind.NOT_ASSIGNABLE,
848 {'fromType': elementType, 'toType': constantType}); 870 {'fromType': elementType, 'toType': constantType});
849 } 871 }
850 } 872 }
851 } 873 }
852 874
853 void updateFieldValue(Node node, TypedElement element, Constant constant) { 875 void updateFieldValue(Node node, TypedElement element, Constant constant) {
854 potentiallyCheckType(node, element, constant); 876 potentiallyCheckType(node, element, constant);
855 fieldValues[element] = constant; 877 fieldValues[element] = constant;
(...skipping 125 matching lines...) Expand 10 before | Expand all | Expand 10 after
981 if (fieldValue == null) { 1003 if (fieldValue == null) {
982 // Use the default value. 1004 // Use the default value.
983 fieldValue = handler.compileConstant(field); 1005 fieldValue = handler.compileConstant(field);
984 } 1006 }
985 jsNewArguments.add(fieldValue); 1007 jsNewArguments.add(fieldValue);
986 }, 1008 },
987 includeSuperAndInjectedMembers: true); 1009 includeSuperAndInjectedMembers: true);
988 return jsNewArguments; 1010 return jsNewArguments;
989 } 1011 }
990 } 1012 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698