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

Unified Diff: packages/analyzer/lib/src/context/context.dart

Issue 1400473008: Roll Observatory packages and add a roll script (Closed) Base URL: git@github.com:dart-lang/observatory_pub_packages.git@master
Patch Set: Created 5 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 side-by-side diff with in-line comments
Download patch
« no previous file with comments | « packages/analyzer/lib/src/context/cache.dart ('k') | packages/analyzer/lib/src/error.dart » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: packages/analyzer/lib/src/context/context.dart
diff --git a/analyzer/lib/src/context/context.dart b/packages/analyzer/lib/src/context/context.dart
similarity index 91%
rename from analyzer/lib/src/context/context.dart
rename to packages/analyzer/lib/src/context/context.dart
index 9c0c9808356e71d72075280b178eba1551f8733c..893ae9f5a51642d64268b47f3040c7614057614a 100644
--- a/analyzer/lib/src/context/context.dart
+++ b/packages/analyzer/lib/src/context/context.dart
@@ -8,6 +8,7 @@ import 'dart:async';
import 'dart:collection';
import 'package:analyzer/instrumentation/instrumentation.dart';
+import 'package:analyzer/plugin/task.dart';
import 'package:analyzer/src/cancelable_future.dart';
import 'package:analyzer/src/context/cache.dart';
import 'package:analyzer/src/generated/ast.dart';
@@ -26,15 +27,12 @@ import 'package:analyzer/src/generated/incremental_resolver.dart';
import 'package:analyzer/src/generated/java_core.dart';
import 'package:analyzer/src/generated/java_engine.dart';
import 'package:analyzer/src/generated/resolver.dart';
-import 'package:analyzer/src/generated/scanner.dart';
import 'package:analyzer/src/generated/sdk.dart' show DartSdk;
import 'package:analyzer/src/generated/source.dart';
import 'package:analyzer/src/generated/utilities_collection.dart';
import 'package:analyzer/src/task/dart.dart';
import 'package:analyzer/src/task/dart_work_manager.dart';
import 'package:analyzer/src/task/driver.dart';
-import 'package:analyzer/src/task/html.dart';
-import 'package:analyzer/src/task/html_work_manager.dart';
import 'package:analyzer/src/task/incremental_element_builder.dart';
import 'package:analyzer/src/task/manager.dart';
import 'package:analyzer/task/dart.dart';
@@ -122,14 +120,14 @@ class AnalysisContextImpl implements InternalAnalysisContext {
TaskManager _taskManager;
/**
- * The [DartWorkManager] instance that performs Dart specific scheduling.
+ * A list of all [WorkManager]s used by this context.
*/
- DartWorkManager dartWorkManager;
+ final List<WorkManager> workManagers = <WorkManager>[];
/**
- * The work manager that performs HTML specific scheduling.
+ * The [DartWorkManager] instance that performs Dart specific scheduling.
*/
- HtmlWorkManager htmlWorkManager;
+ DartWorkManager dartWorkManager;
/**
* The analysis driver used to perform analysis.
@@ -162,11 +160,22 @@ class AnalysisContextImpl implements InternalAnalysisContext {
TypeProvider _typeProvider;
/**
+ * The [TypeSystem] for this context, `null` if not yet created.
+ */
+ TypeSystem _typeSystem;
+
+ /**
* The controller for sending [SourcesChangedEvent]s.
*/
StreamController<SourcesChangedEvent> _onSourcesChangedController;
/**
+ * A subscription for a stream of events indicating when files are (and are
+ * not) being implicitly analyzed.
+ */
+ StreamController<ImplicitAnalysisEvent> _implicitAnalysisEventsController;
+
+ /**
* The listeners that are to be notified when various analysis results are
* produced in this context.
*/
@@ -213,13 +222,21 @@ class AnalysisContextImpl implements InternalAnalysisContext {
_privatePartition = new UniversalCachePartition(this);
_cache = createCacheFromSourceFactory(null);
_taskManager = AnalysisEngine.instance.taskManager;
- // TODO(scheglov) Get WorkManager(Factory)(s) from plugins.
- dartWorkManager = new DartWorkManager(this);
- htmlWorkManager = new HtmlWorkManager(this);
- driver = new AnalysisDriver(
- _taskManager, <WorkManager>[dartWorkManager, htmlWorkManager], this);
+ for (WorkManagerFactory factory
+ in AnalysisEngine.instance.enginePlugin.workManagerFactories) {
+ WorkManager workManager = factory(this);
+ if (workManager != null) {
+ workManagers.add(workManager);
+ if (workManager is DartWorkManager) {
+ dartWorkManager = workManager;
+ }
+ }
+ }
+ driver = new AnalysisDriver(_taskManager, workManagers, this);
_onSourcesChangedController =
new StreamController<SourcesChangedEvent>.broadcast();
+ _implicitAnalysisEventsController =
+ new StreamController<ImplicitAnalysisEvent>.broadcast();
}
@override
@@ -239,7 +256,10 @@ class AnalysisContextImpl implements InternalAnalysisContext {
(this._options.hint && !options.hint) ||
(this._options.lint && !options.lint) ||
this._options.preserveComments != options.preserveComments ||
- this._options.enableStrictCallChecks != options.enableStrictCallChecks;
+ this._options.strongMode != options.strongMode ||
+ this._options.enableStrictCallChecks !=
+ options.enableStrictCallChecks ||
+ this._options.enableSuperMixins != options.enableSuperMixins;
int cacheSize = options.cacheSize;
if (this._options.cacheSize != cacheSize) {
this._options.cacheSize = cacheSize;
@@ -250,15 +270,18 @@ class AnalysisContextImpl implements InternalAnalysisContext {
this._options.generateSdkErrors = options.generateSdkErrors;
this._options.dart2jsHint = options.dart2jsHint;
this._options.enableStrictCallChecks = options.enableStrictCallChecks;
+ this._options.enableSuperMixins = options.enableSuperMixins;
this._options.hint = options.hint;
this._options.incremental = options.incremental;
this._options.incrementalApi = options.incrementalApi;
this._options.incrementalValidation = options.incrementalValidation;
this._options.lint = options.lint;
this._options.preserveComments = options.preserveComments;
+ this._options.strongMode = options.strongMode;
if (needsRecompute) {
- dartWorkManager.onAnalysisOptionsChanged();
- htmlWorkManager.onAnalysisOptionsChanged();
+ for (WorkManager workManager in workManagers) {
+ workManager.onAnalysisOptionsChanged();
+ }
}
}
@@ -276,8 +299,10 @@ class AnalysisContextImpl implements InternalAnalysisContext {
_priorityOrder = sources;
}
}
- dartWorkManager.applyPriorityTargets(_priorityOrder);
- htmlWorkManager.applyPriorityTargets(_priorityOrder);
+ for (WorkManager workManager in workManagers) {
+ workManager.applyPriorityTargets(_priorityOrder);
+ }
+ driver.reset();
}
@override
@@ -304,6 +329,10 @@ class AnalysisContextImpl implements InternalAnalysisContext {
List<Source> get htmlSources => _getSources(SourceKind.HTML);
@override
+ Stream<ImplicitAnalysisEvent> get implicitAnalysisEvents =>
+ _implicitAnalysisEventsController.stream;
+
+ @override
bool get isDisposed => _disposed;
@override
@@ -345,7 +374,8 @@ class AnalysisContextImpl implements InternalAnalysisContext {
/**
* Make _pendingFutureSources available to unit tests.
*/
- HashMap<AnalysisTarget, List<PendingFuture>> get pendingFutureSources_forTesting =>
+ HashMap<AnalysisTarget,
+ List<PendingFuture>> get pendingFutureSources_forTesting =>
_pendingFutureTargets;
@override
@@ -374,8 +404,9 @@ class AnalysisContextImpl implements InternalAnalysisContext {
factory.context = this;
_sourceFactory = factory;
_cache = createCacheFromSourceFactory(factory);
- dartWorkManager.onSourceFactoryChanged();
- htmlWorkManager.onSourceFactoryChanged();
+ for (WorkManager workManager in workManagers) {
+ workManager.onSourceFactoryChanged();
+ }
}
@override
@@ -455,6 +486,14 @@ class AnalysisContextImpl implements InternalAnalysisContext {
}
@override
+ TypeSystem get typeSystem {
+ if (_typeSystem == null) {
+ _typeSystem = TypeSystem.create(this);
+ }
+ return _typeSystem;
+ }
+
+ @override
void addListener(AnalysisListener listener) {
if (!_listeners.contains(listener)) {
_listeners.add(listener);
@@ -516,10 +555,10 @@ class AnalysisContextImpl implements InternalAnalysisContext {
for (Source source in removedSources) {
_sourceRemoved(source);
}
- dartWorkManager.applyChange(
- changeSet.addedSources, changeSet.changedSources, removedSources);
- htmlWorkManager.applyChange(
- changeSet.addedSources, changeSet.changedSources, removedSources);
+ for (WorkManager workManager in workManagers) {
+ workManager.applyChange(
+ changeSet.addedSources, changeSet.changedSources, removedSources);
+ }
_onSourcesChangedController.add(new SourcesChangedEvent(changeSet));
}
@@ -532,31 +571,13 @@ class AnalysisContextImpl implements InternalAnalysisContext {
if (source == null) {
return null;
}
- CompilationUnit unit = parseCompilationUnit(source);
- if (unit == null) {
+ SourceRange docRange = element.docRange;
+ if (docRange == null) {
return null;
}
- NodeLocator locator = new NodeLocator(element.nameOffset);
- AstNode nameNode = locator.searchWithin(unit);
- while (nameNode != null) {
- if (nameNode is AnnotatedNode) {
- Comment comment = nameNode.documentationComment;
- if (comment == null) {
- return null;
- }
- StringBuffer buffer = new StringBuffer();
- List<Token> tokens = comment.tokens;
- for (int i = 0; i < tokens.length; i++) {
- if (i > 0) {
- buffer.write("\n");
- }
- buffer.write(tokens[i].lexeme);
- }
- return buffer.toString();
- }
- nameNode = nameNode.parent;
- }
- return null;
+ String code = getContents(source).data;
+ String comment = code.substring(docRange.offset, docRange.end);
+ return comment.replaceAll('\r\n', '\n');
}
@override
@@ -620,8 +641,8 @@ class AnalysisContextImpl implements InternalAnalysisContext {
return new CancelableFuture.error(new AnalysisNotScheduledError());
}
var unitTarget = new LibrarySpecificUnit(librarySource, unitSource);
- return new _AnalysisFutureHelper<CompilationUnit>(this).computeAsync(
- unitTarget, (CacheEntry entry) {
+ return new _AnalysisFutureHelper<CompilationUnit>(this)
+ .computeAsync(unitTarget, (CacheEntry entry) {
CacheState state = entry.getState(RESOLVED_UNIT);
if (state == CacheState.ERROR) {
throw entry.exception;
@@ -634,6 +655,7 @@ class AnalysisContextImpl implements InternalAnalysisContext {
});
}
+ @override
Object /*V*/ computeResult(
AnalysisTarget target, ResultDescriptor /*<V>*/ descriptor) {
CacheEntry entry = getCacheEntry(target);
@@ -686,7 +708,7 @@ class AnalysisContextImpl implements InternalAnalysisContext {
for (Source librarySource in containingLibraries) {
LibrarySpecificUnit target =
new LibrarySpecificUnit(librarySource, unitSource);
- CompilationUnit unit = _cache.getValue(target, RESOLVED_UNIT);
+ CompilationUnit unit = getResult(target, RESOLVED_UNIT);
if (unit == null) {
units = null;
break;
@@ -728,6 +750,10 @@ class AnalysisContextImpl implements InternalAnalysisContext {
entry.modificationTime = getModificationStamp(target);
}
_cache.put(entry);
+ if (target is Source) {
+ _implicitAnalysisEventsController
+ .add(new ImplicitAnalysisEvent(target, true));
+ }
}
return entry;
}
@@ -736,7 +762,7 @@ class AnalysisContextImpl implements InternalAnalysisContext {
CompilationUnitElement getCompilationUnitElement(
Source unitSource, Source librarySource) {
AnalysisTarget target = new LibrarySpecificUnit(librarySource, unitSource);
- return _cache.getValue(target, COMPILATION_UNIT_ELEMENT);
+ return getResult(target, COMPILATION_UNIT_ELEMENT);
}
@override
@@ -782,13 +808,13 @@ class AnalysisContextImpl implements InternalAnalysisContext {
@override
AnalysisErrorInfo getErrors(Source source) {
- String name = source.shortName;
- if (AnalysisEngine.isDartFileName(name) || source is DartScript) {
- return dartWorkManager.getErrors(source);
- } else if (AnalysisEngine.isHtmlFileName(name)) {
- return htmlWorkManager.getErrors(source);
+ List<AnalysisError> allErrors = <AnalysisError>[];
+ for (WorkManager workManager in workManagers) {
+ List<AnalysisError> errors = workManager.getErrors(source);
+ allErrors.addAll(errors);
}
- return new AnalysisErrorInfoImpl(AnalysisError.NO_ERRORS, null);
+ LineInfo lineInfo = getLineInfo(source);
+ return new AnalysisErrorInfoImpl(allErrors, lineInfo);
}
@override
@@ -809,7 +835,7 @@ class AnalysisContextImpl implements InternalAnalysisContext {
for (Source source in _cache.sources) {
if (AnalysisEngine.isHtmlFileName(source.shortName)) {
List<Source> referencedLibraries =
- analysisCache.getValue(source, REFERENCED_LIBRARIES);
+ getResult(source, REFERENCED_LIBRARIES);
if (_containsAny(referencedLibraries, librarySources)) {
htmlSources.add(source);
}
@@ -825,7 +851,7 @@ class AnalysisContextImpl implements InternalAnalysisContext {
SourceKind getKindOf(Source source) {
String name = source.shortName;
if (AnalysisEngine.isDartFileName(name)) {
- return _cache.getValue(source, SOURCE_KIND);
+ return getResult(source, SOURCE_KIND);
} else if (AnalysisEngine.isHtmlFileName(name)) {
return SourceKind.HTML;
}
@@ -872,10 +898,10 @@ class AnalysisContextImpl implements InternalAnalysisContext {
@override
LibraryElement getLibraryElement(Source source) =>
- _cache.getValue(source, LIBRARY_ELEMENT);
+ getResult(source, LIBRARY_ELEMENT);
@override
- LineInfo getLineInfo(Source source) => _cache.getValue(source, LINE_INFO);
+ LineInfo getLineInfo(Source source) => getResult(source, LINE_INFO);
@override
int getModificationStamp(Source source) {
@@ -926,7 +952,7 @@ class AnalysisContextImpl implements InternalAnalysisContext {
!AnalysisEngine.isDartFileName(librarySource.shortName)) {
return null;
}
- return _cache.getValue(
+ return getResult(
new LibrarySpecificUnit(librarySource, unitSource), RESOLVED_UNIT);
}
@@ -939,6 +965,11 @@ class AnalysisContextImpl implements InternalAnalysisContext {
}
@override
+ Object getResult(AnalysisTarget target, ResultDescriptor result) {
+ return _cache.getValue(target, result);
+ }
+
+ @override
List<Source> getSourcesWithFullName(String path) {
return analysisCache.getSourcesWithFullName(path);
}
@@ -992,7 +1023,7 @@ class AnalysisContextImpl implements InternalAnalysisContext {
@override
void invalidateLibraryHints(Source librarySource) {
- List<Source> sources = _cache.getValue(librarySource, UNITS);
+ List<Source> sources = getResult(librarySource, UNITS);
if (sources != null) {
for (Source source in sources) {
getCacheEntry(source).setState(HINTS, CacheState.INVALID);
@@ -1107,16 +1138,23 @@ class AnalysisContextImpl implements InternalAnalysisContext {
new LibrarySpecificUnit(librarySource, librarySource);
entry = getCacheEntry(unit);
setValue(HINTS, AnalysisError.NO_ERRORS);
- // dartEntry.setValue(LINTS, AnalysisError.NO_ERRORS);
- entry.setState(RESOLVE_REFERENCES_ERRORS, CacheState.FLUSHED);
+ setValue(LINTS, AnalysisError.NO_ERRORS);
+ setValue(LIBRARY_UNIT_ERRORS, AnalysisError.NO_ERRORS);
+ setValue(RESOLVE_TYPE_NAMES_ERRORS, AnalysisError.NO_ERRORS);
+ setValue(RESOLVE_UNIT_ERRORS, AnalysisError.NO_ERRORS);
entry.setState(RESOLVED_UNIT, CacheState.FLUSHED);
entry.setState(RESOLVED_UNIT1, CacheState.FLUSHED);
entry.setState(RESOLVED_UNIT2, CacheState.FLUSHED);
entry.setState(RESOLVED_UNIT3, CacheState.FLUSHED);
entry.setState(RESOLVED_UNIT4, CacheState.FLUSHED);
entry.setState(RESOLVED_UNIT5, CacheState.FLUSHED);
+ entry.setState(RESOLVED_UNIT6, CacheState.FLUSHED);
+ entry.setState(RESOLVED_UNIT7, CacheState.FLUSHED);
+ entry.setState(RESOLVED_UNIT8, CacheState.FLUSHED);
+ entry.setState(RESOLVED_UNIT9, CacheState.FLUSHED);
// USED_IMPORTED_ELEMENTS
// USED_LOCAL_ELEMENTS
+ setValue(VARIABLE_REFERENCE_ERRORS, AnalysisError.NO_ERRORS);
setValue(VERIFY_ERRORS, AnalysisError.NO_ERRORS);
});
@@ -1141,10 +1179,6 @@ class AnalysisContextImpl implements InternalAnalysisContext {
@override
CompilationUnit resolveCompilationUnit2(
Source unitSource, Source librarySource) {
- if (!AnalysisEngine.isDartFileName(unitSource.shortName) ||
- !AnalysisEngine.isDartFileName(librarySource.shortName)) {
- return null;
- }
return computeResult(
new LibrarySpecificUnit(librarySource, unitSource), RESOLVED_UNIT);
}
@@ -1192,6 +1226,10 @@ class AnalysisContextImpl implements InternalAnalysisContext {
entry.setState(RESOLVED_UNIT3, CacheState.FLUSHED);
entry.setState(RESOLVED_UNIT4, CacheState.FLUSHED);
entry.setState(RESOLVED_UNIT5, CacheState.FLUSHED);
+ entry.setState(RESOLVED_UNIT6, CacheState.FLUSHED);
+ entry.setState(RESOLVED_UNIT7, CacheState.FLUSHED);
+ entry.setState(RESOLVED_UNIT8, CacheState.FLUSHED);
+ entry.setState(RESOLVED_UNIT9, CacheState.FLUSHED);
entry.setState(RESOLVED_UNIT, CacheState.FLUSHED);
}
@@ -1219,7 +1257,7 @@ class AnalysisContextImpl implements InternalAnalysisContext {
for (Source source in missingSources) {
if (getLibrariesContaining(source).isEmpty &&
getLibrariesDependingOn(source).isEmpty) {
- _cache.remove(source);
+ _removeFromCache(source);
removalCount++;
}
}
@@ -1427,6 +1465,10 @@ class AnalysisContextImpl implements InternalAnalysisContext {
entry.modificationTime = getModificationStamp(source);
entry.explicitlyAdded = explicitlyAdded;
_cache.put(entry);
+ if (!explicitlyAdded) {
+ _implicitAnalysisEventsController
+ .add(new ImplicitAnalysisEvent(source, true));
+ }
return entry;
}
@@ -1507,8 +1549,12 @@ class AnalysisContextImpl implements InternalAnalysisContext {
* related to it. If so, add the source to the set of sources that need to be
* processed. This method is intended to be used for testing purposes only.
*/
- void _getSourcesNeedingProcessing(Source source, CacheEntry entry,
- bool isPriority, bool hintsEnabled, bool lintsEnabled,
+ void _getSourcesNeedingProcessing(
+ Source source,
+ CacheEntry entry,
+ bool isPriority,
+ bool hintsEnabled,
+ bool lintsEnabled,
HashSet<Source> sources) {
CacheState state = entry.getState(CONTENT);
if (state == CacheState.INVALID ||
@@ -1589,16 +1635,16 @@ class AnalysisContextImpl implements InternalAnalysisContext {
return;
}
}
-// if (lintsEnabled) {
-// state = unitEntry.getState(LINTS);
-// if (state == CacheState.INVALID ||
-// (isPriority && state == CacheState.FLUSHED)) {
-// sources.add(source);
-// return;
-// } else if (state == CacheState.ERROR) {
-// return;
-// }
-// }
+ if (lintsEnabled) {
+ state = unitEntry.getState(LINTS);
+ if (state == CacheState.INVALID ||
+ (isPriority && state == CacheState.FLUSHED)) {
+ sources.add(source);
+ return;
+ } else if (state == CacheState.ERROR) {
+ return;
+ }
+ }
}
}
// } else if (kind == SourceKind.HTML) {
@@ -1637,6 +1683,14 @@ class AnalysisContextImpl implements InternalAnalysisContext {
}
}
+ void _removeFromCache(Source source) {
+ CacheEntry entry = _cache.remove(source);
+ if (entry != null && !entry.explicitlyAdded) {
+ _implicitAnalysisEventsController
+ .add(new ImplicitAnalysisEvent(source, false));
+ }
+ }
+
/**
* Remove the given [source] from the priority order if it is in the list.
*/
@@ -1658,6 +1712,10 @@ class AnalysisContextImpl implements InternalAnalysisContext {
* that referenced the source before it existed.
*/
void _sourceAvailable(Source source) {
+ // TODO(brianwilkerson) This method needs to check whether the source was
+ // previously being implicitly analyzed. If so, the cache entry needs to be
+ // update to reflect the new status and an event needs to be generated to
+ // inform clients that it is no longer being implicitly analyzed.
CacheEntry entry = _cache.get(source);
if (entry == null) {
_createCacheEntry(source, true);
@@ -1691,7 +1749,9 @@ class AnalysisContextImpl implements InternalAnalysisContext {
}
return;
}
- } catch (e) {}
+ } catch (e) {
+ entry.modificationTime = -1;
+ }
}
// We need to invalidate the cache.
{
@@ -1728,17 +1788,17 @@ class AnalysisContextImpl implements InternalAnalysisContext {
}
entry.setState(CONTENT, CacheState.INVALID);
}
- dartWorkManager.applyChange(
- Source.EMPTY_LIST, <Source>[source], Source.EMPTY_LIST);
- htmlWorkManager.applyChange(
- Source.EMPTY_LIST, <Source>[source], Source.EMPTY_LIST);
+ for (WorkManager workManager in workManagers) {
+ workManager.applyChange(
+ Source.EMPTY_LIST, <Source>[source], Source.EMPTY_LIST);
+ }
}
/**
* Record that the give [source] has been deleted.
*/
void _sourceDeleted(Source source) {
- // TODO(brianwilkerson) Implement this.
+ // TODO(brianwilkerson) Implement or remove this.
// SourceEntry sourceEntry = _cache.get(source);
// if (sourceEntry is HtmlEntry) {
// HtmlEntry htmlEntry = sourceEntry;
@@ -1769,7 +1829,7 @@ class AnalysisContextImpl implements InternalAnalysisContext {
* Record that the given [source] has been removed.
*/
void _sourceRemoved(Source source) {
- _cache.remove(source);
+ _removeFromCache(source);
_removeFromPriorityOrder(source);
}
@@ -1811,11 +1871,17 @@ class AnalysisContextImpl implements InternalAnalysisContext {
// do resolution
Stopwatch perfCounter = new Stopwatch()..start();
PoorMansIncrementalResolver resolver = new PoorMansIncrementalResolver(
- typeProvider, unitSource, null, sourceEntry, unitEntry, oldUnit,
- analysisOptions.incrementalApi, analysisOptions);
+ typeProvider,
+ unitSource,
+ null,
+ sourceEntry,
+ unitEntry,
+ oldUnit,
+ analysisOptions.incrementalApi);
bool success = resolver.resolve(newCode);
AnalysisEngine.instance.instrumentationService.logPerformance(
- AnalysisPerformanceKind.INCREMENTAL, perfCounter,
+ AnalysisPerformanceKind.INCREMENTAL,
+ perfCounter,
'success=$success,context_id=$_id,code_length=${newCode.length}');
if (!success) {
return false;
« no previous file with comments | « packages/analyzer/lib/src/context/cache.dart ('k') | packages/analyzer/lib/src/error.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698