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

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

Issue 11086045: Revert "Allow closures inside lazy initializers." (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 8 years, 2 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 | lib/compiler/implementation/resolver.dart » ('j') | no next file with comments »
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 #library("closureToClassMapper"); 5 #library("closureToClassMapper");
6 6
7 #import("elements/elements.dart"); 7 #import("elements/elements.dart");
8 #import("leg.dart"); 8 #import("leg.dart");
9 #import("scanner/scannerlib.dart"); 9 #import("scanner/scannerlib.dart");
10 #import("tree/tree.dart"); 10 #import("tree/tree.dart");
11 #import("util/util.dart"); 11 #import("util/util.dart");
12 12
13 class ClosureTask extends CompilerTask { 13 class ClosureTask extends CompilerTask {
14 Map<Node, ClosureClassMap> closureMappingCache; 14 Map<Node, ClosureClassMap> closureMappingCache;
15 ClosureTask(Compiler compiler) 15 ClosureTask(Compiler compiler)
16 : closureMappingCache = new Map<Node, ClosureClassMap>(), 16 : closureMappingCache = new Map<Node, ClosureClassMap>(),
17 super(compiler); 17 super(compiler);
18 18
19 String get name => "Closure Simplifier"; 19 String get name => "Closure Simplifier";
20 20
21 ClosureClassMap computeClosureToClassMapping(Element element, 21 ClosureClassMap computeClosureToClassMapping(FunctionExpression node,
22 Expression node,
23 TreeElements elements) { 22 TreeElements elements) {
24 return measure(() { 23 return measure(() {
25 ClosureClassMap cached = closureMappingCache[node]; 24 ClosureClassMap cached = closureMappingCache[node];
26 if (cached !== null) return cached; 25 if (cached !== null) return cached;
27 26
28 ClosureTranslator translator = 27 ClosureTranslator translator =
29 new ClosureTranslator(compiler, elements, closureMappingCache); 28 new ClosureTranslator(compiler, elements, closureMappingCache);
30
31 // The translator will store the computed closure-mappings inside the 29 // The translator will store the computed closure-mappings inside the
32 // cache. One for given node and one for each nested closure. 30 // cache. One for given method and one for each nested closure.
33 if (node is FunctionExpression) { 31 translator.translate(node);
34 translator.translateFunction(element, node);
35 } else {
36 // Must be the lazy initializer of a static.
37 assert(node is SendSet);
38 translator.translateLazyInitializer(element, node);
39 }
40 assert(closureMappingCache[node] != null); 32 assert(closureMappingCache[node] != null);
41 return closureMappingCache[node]; 33 return closureMappingCache[node];
42 }); 34 });
43 } 35 }
44 36
45 ClosureClassMap getMappingForNestedFunction(FunctionExpression node) { 37 ClosureClassMap getMappingForNestedFunction(FunctionExpression node) {
46 return measure(() { 38 return measure(() {
47 ClosureClassMap nestedClosureData = closureMappingCache[node]; 39 ClosureClassMap nestedClosureData = closureMappingCache[node];
48 if (nestedClosureData === null) { 40 if (nestedClosureData === null) {
49 // TODO(floitsch): we can only assume that the reason for not having a 41 // TODO(floitsch): we can only assume that the reason for not having a
(...skipping 64 matching lines...) Expand 10 before | Expand all | Expand 10 after
114 List<Element> boxedLoopVariables; 106 List<Element> boxedLoopVariables;
115 107
116 ClosureScope(this.boxElement, this.capturedVariableMapping) 108 ClosureScope(this.boxElement, this.capturedVariableMapping)
117 : boxedLoopVariables = const <Element>[]; 109 : boxedLoopVariables = const <Element>[];
118 110
119 bool hasBoxedLoopVariables() => !boxedLoopVariables.isEmpty(); 111 bool hasBoxedLoopVariables() => !boxedLoopVariables.isEmpty();
120 } 112 }
121 113
122 class ClosureClassMap { 114 class ClosureClassMap {
123 // The closure's element before any translation. Will be null for methods. 115 // The closure's element before any translation. Will be null for methods.
124 final Element closureElement; 116 final FunctionElement closureElement;
125 // The closureClassElement will be null for methods that are not local 117 // The closureClassElement will be null for methods that are not local
126 // closures. 118 // closures.
127 final ClassElement closureClassElement; 119 final ClassElement closureClassElement;
128 // The callElement will be null for methods that are not local closures. 120 // The callElement will be null for methods that are not local closures.
129 final FunctionElement callElement; 121 final FunctionElement callElement;
130 // The [thisElement] makes handling 'this' easier by treating it like any 122 // The [thisElement] makes handling 'this' easier by treating it like any
131 // other argument. It is only set for instance-members. 123 // other argument. It is only set for instance-members.
132 final ThisElement thisElement; 124 final ThisElement thisElement;
133 125
134 // Maps free locals, arguments and function elements to their captured 126 // Maps free locals, arguments and function elements to their captured
(...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after
171 final TreeElements elements; 163 final TreeElements elements;
172 int closureFieldCounter = 0; 164 int closureFieldCounter = 0;
173 bool inTryStatement = false; 165 bool inTryStatement = false;
174 final Map<Node, ClosureClassMap> closureMappingCache; 166 final Map<Node, ClosureClassMap> closureMappingCache;
175 167
176 // Map of captured variables. Initially they will map to themselves. If 168 // Map of captured variables. Initially they will map to themselves. If
177 // a variable needs to be boxed then the scope declaring the variable 169 // a variable needs to be boxed then the scope declaring the variable
178 // will update this mapping. 170 // will update this mapping.
179 Map<Element, Element> capturedVariableMapping; 171 Map<Element, Element> capturedVariableMapping;
180 // List of encountered closures. 172 // List of encountered closures.
181 List<Expression> closures; 173 List<FunctionExpression> closures;
182 174
183 // The variables that have been declared in the current scope. 175 // The variables that have been declared in the current scope.
184 List<Element> scopeVariables; 176 List<Element> scopeVariables;
185 177
186 // Keep track of the mutated variables so that we don't need to box 178 // Keep track of the mutated variables so that we don't need to box
187 // non-mutated variables. 179 // non-mutated variables.
188 Set<Element> mutatedVariables; 180 Set<Element> mutatedVariables;
189 181
190 Element outermostElement; 182 FunctionElement outermostFunctionElement;
191 Element currentElement; 183 FunctionElement currentFunctionElement;
192 184
193 // The closureData of the currentFunctionElement. 185 // The closureData of the currentFunctionElement.
194 ClosureClassMap closureData; 186 ClosureClassMap closureData;
195 187
196 bool insideClosure = false; 188 bool insideClosure = false;
197 189
198 ClosureTranslator(this.compiler, this.elements, this.closureMappingCache) 190 ClosureTranslator(this.compiler, this.elements, this.closureMappingCache)
199 : capturedVariableMapping = new Map<Element, Element>(), 191 : capturedVariableMapping = new Map<Element, Element>(),
200 closures = <Expression>[], 192 closures = <FunctionExpression>[],
201 mutatedVariables = new Set<Element>(); 193 mutatedVariables = new Set<Element>();
202 194
203 void translateFunction(Element element, FunctionExpression node) { 195 void translate(Node node) {
204 assert(elements[node] == element); 196 visit(node);
205 visit(node); // [visitFunctionExpression] will call [visitInvokable].
206 // When variables need to be boxed their [capturedVariableMapping] is 197 // When variables need to be boxed their [capturedVariableMapping] is
207 // updated, but we delay updating the similar freeVariableMapping in the 198 // updated, but we delay updating the similar freeVariableMapping in the
208 // closure datas that capture these variables. 199 // closure datas that capture these variables.
209 // The closures don't have their fields (in the closure class) set, either. 200 // The closures don't have their fields (in the closure class) set, either.
210 updateClosures(); 201 updateClosures();
211 } 202 }
212 203
213 void translateLazyInitializer(Element element, SendSet node) {
214 assert(node.assignmentOperator.source == const SourceString("="));
215 Expression initialValue = node.argumentsNode.nodes.head;
216 visitInvokable(element, node, () { visit(initialValue); });
217 updateClosures();
218 }
219
220 // This function runs through all of the existing closures and updates their 204 // This function runs through all of the existing closures and updates their
221 // free variables to the boxed value. It also adds the field-elements to the 205 // free variables to the boxed value. It also adds the field-elements to the
222 // class representing the closure. At the same time it fills the 206 // class representing the closure. At the same time it fills the
223 // [capturedFieldMapping]. 207 // [capturedFieldMapping].
224 void updateClosures() { 208 void updateClosures() {
225 for (Expression closure in closures) { 209 for (FunctionExpression closure in closures) {
226 // The captured variables that need to be stored in a field of the closure 210 // The captured variables that need to be stored in a field of the closure
227 // class. 211 // class.
228 Set<Element> fieldCaptures = new Set<Element>(); 212 Set<Element> fieldCaptures = new Set<Element>();
229 ClosureClassMap data = closureMappingCache[closure]; 213 ClosureClassMap data = closureMappingCache[closure];
230 Map<Element, Element> freeVariableMapping = data.freeVariableMapping; 214 Map<Element, Element> freeVariableMapping = data.freeVariableMapping;
231 // We get a copy of the keys and iterate over it, to avoid modifications 215 // We get a copy of the keys and iterate over it, to avoid modifications
232 // to the map while iterating over it. 216 // to the map while iterating over it.
233 freeVariableMapping.getKeys().forEach((Element fromElement) { 217 freeVariableMapping.getKeys().forEach((Element fromElement) {
234 assert(fromElement == freeVariableMapping[fromElement]); 218 assert(fromElement == freeVariableMapping[fromElement]);
235 Element updatedElement = capturedVariableMapping[fromElement]; 219 Element updatedElement = capturedVariableMapping[fromElement];
(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after
269 } 253 }
270 254
271 void useLocal(Element element) { 255 void useLocal(Element element) {
272 // If the element is not declared in the current function and the element 256 // If the element is not declared in the current function and the element
273 // is not the closure itself we need to mark the element as free variable. 257 // is not the closure itself we need to mark the element as free variable.
274 // Note that the check on [insideClosure] is not just an 258 // Note that the check on [insideClosure] is not just an
275 // optimization: factories have type parameters as function 259 // optimization: factories have type parameters as function
276 // parameters, and type parameters are declared in the class, not 260 // parameters, and type parameters are declared in the class, not
277 // the factory. 261 // the factory.
278 if (insideClosure && 262 if (insideClosure &&
279 element.enclosingElement != currentElement && 263 element.enclosingElement != currentFunctionElement &&
280 element != currentElement) { 264 element != currentFunctionElement) {
281 assert(closureData.freeVariableMapping[element] == null || 265 assert(closureData.freeVariableMapping[element] == null ||
282 closureData.freeVariableMapping[element] == element); 266 closureData.freeVariableMapping[element] == element);
283 closureData.freeVariableMapping[element] = element; 267 closureData.freeVariableMapping[element] = element;
284 } else if (inTryStatement) { 268 } else if (inTryStatement) {
285 // Don't mark the this-element. This would complicate things in the 269 // Don't mark the this-element. This would complicate things in the
286 // builder. 270 // builder.
287 if (element != closureData.thisElement) { 271 if (element != closureData.thisElement) {
288 // TODO(ngeoffray): only do this if the variable is mutated. 272 // TODO(ngeoffray): only do this if the variable is mutated.
289 closureData.usedVariablesInTry.add(element); 273 closureData.usedVariablesInTry.add(element);
290 } 274 }
(...skipping 91 matching lines...) Expand 10 before | Expand all | Expand 10 after
382 void analyzeTypeVariables(DartType type) { 366 void analyzeTypeVariables(DartType type) {
383 if (type is TypeVariableType) { 367 if (type is TypeVariableType) {
384 useLocal(type.element); 368 useLocal(type.element);
385 } else if (type is InterfaceType) { 369 } else if (type is InterfaceType) {
386 InterfaceType ifcType = type; 370 InterfaceType ifcType = type;
387 for (DartType argument in ifcType.arguments) { 371 for (DartType argument in ifcType.arguments) {
388 analyzeTypeVariables(argument); 372 analyzeTypeVariables(argument);
389 } 373 }
390 } 374 }
391 } 375 }
392 if (outermostElement.isMember() && 376 if (outermostFunctionElement.isMember() &&
393 compiler.world.needsRti(outermostElement.getEnclosingClass())) { 377 compiler.world.needsRti(outermostFunctionElement.getEnclosingClass())) {
394 if (outermostElement.isInstanceMember() 378 if (outermostFunctionElement.isInstanceMember()
395 || outermostElement.isGenerativeConstructor()) { 379 || outermostFunctionElement.isGenerativeConstructor()) {
396 if (hasTypeVariable(type)) useLocal(closureData.thisElement); 380 if (hasTypeVariable(type)) useLocal(closureData.thisElement);
397 } else if (outermostElement.isFactoryConstructor()) { 381 } else if (outermostFunctionElement.isFactoryConstructor()) {
398 analyzeTypeVariables(type); 382 analyzeTypeVariables(type);
399 } 383 }
400 } 384 }
401 385
402 node.visitChildren(this); 386 node.visitChildren(this);
403 } 387 }
404 388
405 // If variables that are declared in the [node] scope are captured and need 389 // If variables that are declared in the [node] scope are captured and need
406 // to be boxed create a box-element and update the [capturingScopes] in the 390 // to be boxed create a box-element and update the [capturingScopes] in the
407 // current [closureData]. 391 // current [closureData].
408 // The boxed variables are updated in the [capturedVariableMapping]. 392 // The boxed variables are updated in the [capturedVariableMapping].
409 void attachCapturedScopeVariables(Node node) { 393 void attachCapturedScopeVariables(Node node) {
410 Element box = null; 394 Element box = null;
411 Map<Element, Element> scopeMapping = new Map<Element, Element>(); 395 Map<Element, Element> scopeMapping = new Map<Element, Element>();
412 for (Element element in scopeVariables) { 396 for (Element element in scopeVariables) {
413 // No need to box non-assignable elements. 397 // No need to box non-assignable elements.
414 if (!element.isAssignable()) continue; 398 if (!element.isAssignable()) continue;
415 if (!mutatedVariables.contains(element)) continue; 399 if (!mutatedVariables.contains(element)) continue;
416 if (capturedVariableMapping.containsKey(element)) { 400 if (capturedVariableMapping.containsKey(element)) {
417 if (box == null) { 401 if (box == null) {
418 // TODO(floitsch): construct better box names. 402 // TODO(floitsch): construct better box names.
419 SourceString boxName = 403 SourceString boxName =
420 new SourceString("box_${closureFieldCounter++}"); 404 new SourceString("box_${closureFieldCounter++}");
421 box = new BoxElement(boxName, currentElement); 405 box = new BoxElement(boxName, currentFunctionElement);
422 } 406 }
423 // TODO(floitsch): construct better boxed names. 407 // TODO(floitsch): construct better boxed names.
424 String elementName = element.name.slowToString(); 408 String elementName = element.name.slowToString();
425 // We are currently using the name in an HForeign which could replace 409 // We are currently using the name in an HForeign which could replace
426 // "$X" with something else. 410 // "$X" with something else.
427 String escaped = elementName.replaceAll("\$", "_"); 411 String escaped = elementName.replaceAll("\$", "_");
428 SourceString boxedName = 412 SourceString boxedName =
429 new SourceString("${escaped}_${closureFieldCounter++}"); 413 new SourceString("${escaped}_${closureFieldCounter++}");
430 Element boxed = new Element(boxedName, ElementKind.FIELD, box); 414 Element boxed = new Element(boxedName, ElementKind.FIELD, box);
431 scopeMapping[element] = boxed; 415 scopeMapping[element] = boxed;
(...skipping 86 matching lines...) Expand 10 before | Expand all | Expand 10 after
518 globalizedElement); 502 globalizedElement);
519 globalizedElement.backendMembers = 503 globalizedElement.backendMembers =
520 const EmptyLink<Element>().prepend(callElement); 504 const EmptyLink<Element>().prepend(callElement);
521 // The nested function's 'this' is the same as the one for the outer 505 // The nested function's 'this' is the same as the one for the outer
522 // function. It could be [null] if we are inside a static method. 506 // function. It could be [null] if we are inside a static method.
523 Element thisElement = closureData.thisElement; 507 Element thisElement = closureData.thisElement;
524 return new ClosureClassMap(element, globalizedElement, 508 return new ClosureClassMap(element, globalizedElement,
525 callElement, thisElement); 509 callElement, thisElement);
526 } 510 }
527 511
528 void visitInvokable(Element element, Expression node, void visitChildren()) { 512 visitFunctionExpression(FunctionExpression node) {
513 Element element = elements[node];
514 if (element.isParameter()) {
515 // TODO(ahe): This is a hack. This method should *not* call
516 // visitChildren.
517 return node.name.accept(this);
518 }
519
529 bool oldInsideClosure = insideClosure; 520 bool oldInsideClosure = insideClosure;
530 Element oldFunctionElement = currentElement; 521 FunctionElement oldFunctionElement = currentFunctionElement;
531 ClosureClassMap oldClosureData = closureData; 522 ClosureClassMap oldClosureData = closureData;
532 523
533 insideClosure = outermostElement != null; 524 insideClosure = outermostFunctionElement != null;
534 currentElement = element; 525 currentFunctionElement = element;
535 if (insideClosure) { 526 if (insideClosure) {
536 closures.add(node); 527 closures.add(node);
537 closureData = globalizeClosure(node, element); 528 closureData = globalizeClosure(node, element);
538 } else { 529 } else {
539 outermostElement = element; 530 outermostFunctionElement = element;
540 Element thisElement = null; 531 Element thisElement = null;
541 if (element.isInstanceMember() || element.isGenerativeConstructor()) { 532 if (element.isInstanceMember() || element.isGenerativeConstructor()) {
542 thisElement = new ThisElement(element); 533 thisElement = new ThisElement(element);
543 } 534 }
544 closureData = new ClosureClassMap(null, null, null, thisElement); 535 closureData = new ClosureClassMap(null, null, null, thisElement);
545 } 536 }
546 closureMappingCache[node] = closureData; 537 closureMappingCache[node] = closureData;
547 538
548 inNewScope(node, () { 539 inNewScope(node, () {
549 // We have to declare the implicit 'this' parameter. 540 // We have to declare the implicit 'this' parameter.
550 if (!insideClosure && closureData.thisElement !== null) { 541 if (!insideClosure && closureData.thisElement !== null) {
551 declareLocal(closureData.thisElement); 542 declareLocal(closureData.thisElement);
552 } 543 }
553 // If we are inside a named closure we have to declare ourselve. For 544 // If we are inside a named closure we have to declare ourselve. For
554 // simplicity we declare the local even if the closure does not have a 545 // simplicity we declare the local even if the closure does not have a
555 // name. 546 // name.
556 // It will simply not be used. 547 // It will simply not be used.
557 if (insideClosure) { 548 if (insideClosure) {
558 declareLocal(element); 549 declareLocal(element);
559 } 550 }
560 551
561 if (currentElement.isFactoryConstructor() 552 if (currentFunctionElement.isFactoryConstructor()
562 && compiler.world.needsRti(currentElement.enclosingElement)) { 553 && compiler.world.needsRti(currentFunctionElement.enclosingElement)) {
563 // Declare the type parameters in the scope. Generative 554 // Declare the type parameters in the scope. Generative
564 // constructors just use 'this'. 555 // constructors just use 'this'.
565 ClassElement cls = currentElement.enclosingElement; 556 ClassElement cls = currentFunctionElement.enclosingElement;
566 cls.typeVariables.forEach((TypeVariableType typeVariable) { 557 cls.typeVariables.forEach((TypeVariableType typeVariable) {
567 declareLocal(typeVariable.element); 558 declareLocal(typeVariable.element);
568 }); 559 });
569 } 560 }
570 561
571 visitChildren(); 562 // TODO(ahe): This is problematic. The backend should not repeat
563 // the work of the resolver. It is the resolver's job to create
564 // parameters, etc. Other phases should only visit statements.
565 // TODO(floitsch): we avoid visiting the initializers on purpose so that
566 // we get an error-message later in the builder.
567 if (node.parameters !== null) node.parameters.accept(this);
568 if (node.body !== null) node.body.accept(this);
572 }); 569 });
573 570
574 571
575 ClosureClassMap savedClosureData = closureData; 572 ClosureClassMap savedClosureData = closureData;
576 bool savedInsideClosure = insideClosure; 573 bool savedInsideClosure = insideClosure;
577 574
578 // Restore old values. 575 // Restore old values.
579 insideClosure = oldInsideClosure; 576 insideClosure = oldInsideClosure;
580 closureData = oldClosureData; 577 closureData = oldClosureData;
581 currentElement = oldFunctionElement; 578 currentFunctionElement = oldFunctionElement;
582 579
583 // Mark all free variables as captured and use them in the outer function. 580 // Mark all free variables as captured and use them in the outer function.
584 List<Element> freeVariables = 581 List<Element> freeVariables =
585 savedClosureData.freeVariableMapping.getKeys(); 582 savedClosureData.freeVariableMapping.getKeys();
586 assert(freeVariables.isEmpty() || savedInsideClosure); 583 assert(freeVariables.isEmpty() || savedInsideClosure);
587 for (Element freeElement in freeVariables) { 584 for (Element freeElement in freeVariables) {
588 if (capturedVariableMapping[freeElement] != null && 585 if (capturedVariableMapping[freeElement] != null &&
589 capturedVariableMapping[freeElement] != freeElement) { 586 capturedVariableMapping[freeElement] != freeElement) {
590 compiler.internalError('In closure analyzer', node: node); 587 compiler.internalError('In closure analyzer', node: node);
591 } 588 }
592 capturedVariableMapping[freeElement] = freeElement; 589 capturedVariableMapping[freeElement] = freeElement;
593 useLocal(freeElement); 590 useLocal(freeElement);
594 } 591 }
595 } 592 }
596 593
597 visitFunctionExpression(FunctionExpression node) {
598 Element element = elements[node];
599
600 if (element.isParameter()) {
601 // TODO(ahe): This is a hack. This method should *not* call
602 // visitChildren.
603 return node.name.accept(this);
604 }
605
606 visitInvokable(element, node, () {
607 // TODO(ahe): This is problematic. The backend should not repeat
608 // the work of the resolver. It is the resolver's job to create
609 // parameters, etc. Other phases should only visit statements.
610 // TODO(floitsch): we avoid visiting the initializers on purpose so that
611 // we get an error-message later in the builder.
612 if (node.parameters !== null) node.parameters.accept(this);
613 if (node.body !== null) node.body.accept(this);
614 });
615 }
616
617 visitFunctionDeclaration(FunctionDeclaration node) { 594 visitFunctionDeclaration(FunctionDeclaration node) {
618 node.visitChildren(this); 595 node.visitChildren(this);
619 declareLocal(elements[node]); 596 declareLocal(elements[node]);
620 } 597 }
621 598
622 visitTryStatement(TryStatement node) { 599 visitTryStatement(TryStatement node) {
623 // TODO(ngeoffray): implement finer grain state. 600 // TODO(ngeoffray): implement finer grain state.
624 bool oldInTryStatement = inTryStatement; 601 bool oldInTryStatement = inTryStatement;
625 inTryStatement = true; 602 inTryStatement = true;
626 node.visitChildren(this); 603 node.visitChildren(this);
627 inTryStatement = oldInTryStatement; 604 inTryStatement = oldInTryStatement;
628 } 605 }
629 } 606 }
OLDNEW
« no previous file with comments | « no previous file | lib/compiler/implementation/resolver.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698