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

Side by Side Diff: runtime/vm/kernel_binary_flowgraph.h

Issue 2854393002: [kernel] [partial] Streaming of kernel binary without AST nodes (Closed)
Patch Set: Created 3 years, 7 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
OLDNEW
1 // Copyright (c) 2017, the Dart project authors. Please see the AUTHORS file 1 // Copyright (c) 2017, 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 #ifndef RUNTIME_VM_KERNEL_BINARY_FLOWGRAPH_H_ 5 #ifndef RUNTIME_VM_KERNEL_BINARY_FLOWGRAPH_H_
6 #define RUNTIME_VM_KERNEL_BINARY_FLOWGRAPH_H_ 6 #define RUNTIME_VM_KERNEL_BINARY_FLOWGRAPH_H_
7 7
8 #if !defined(DART_PRECOMPILED_RUNTIME) 8 #if !defined(DART_PRECOMPILED_RUNTIME)
9 9
10 #include <map> 10 #include <map>
11 11
12 #include "vm/kernel.h" 12 #include "vm/kernel.h"
13 #include "vm/kernel_binary.h" 13 #include "vm/kernel_binary.h"
14 #include "vm/kernel_to_il.h" 14 #include "vm/kernel_to_il.h"
15 #include "vm/object.h" 15 #include "vm/object.h"
16 16
17 namespace dart { 17 namespace dart {
18 namespace kernel { 18 namespace kernel {
19 19
20 enum LogicalOperator { kAnd, kOr };
Kevin Millikin (Google) 2017/05/11 10:38:36 You should be able to use LogicalExpression::Opera
jensj 2017/05/11 12:59:25 Done.
21 enum VariableDeclarationFlags {
Kevin Millikin (Google) 2017/05/11 10:38:36 Similarly use VariableDeclaration::Flags instead.
jensj 2017/05/11 12:59:25 Done.
22 kFlagFinal = 1 << 0,
23 kFlagConst = 1 << 1,
24 };
25 enum YieldFlags {
Kevin Millikin (Google) 2017/05/11 10:38:36 Similarly use YieldStatement::Flags instead.
jensj 2017/05/11 12:59:25 Done.
26 kFlagYieldStar = 1 << 0,
27 kFlagNative = 1 << 1,
28 };
29
30 class StreamingDartTypeTranslator {
31 public:
32 StreamingDartTypeTranslator(StreamingFlowGraphBuilder* builder,
Kevin Millikin (Google) 2017/05/11 10:38:36 Unless we actually want to support the behavior of
jensj 2017/05/11 12:59:25 Done.
33 TranslationHelper* helper,
34 ActiveClass* active_class,
35 bool finalize = false)
36 : builder_(builder),
37 translation_helper_(*helper),
38 active_class_(active_class),
39 type_parameter_scope_(NULL),
40 zone_(helper->zone()),
41 result_(AbstractType::Handle(helper->zone())),
42 finalize_(finalize) {}
43
44 // Can return a malformed type.
45 AbstractType& BuildType();
46 // Will return `TypeArguments::null()` in case any of the arguments are
47 // malformed.
48 const TypeArguments& BuildTypeArguments(intptr_t length);
49
50 // Will return `TypeArguments::null()` in case any of the arguments are
51 // malformed.
52 const TypeArguments& BuildInstantiatedTypeArguments(
53 const dart::Class& receiver_class,
54 intptr_t length);
55
56 const Type& ReceiverType(const dart::Class& klass);
57
58 private:
59 // Can return a malformed type.
Kevin Millikin (Google) 2017/05/11 10:38:36 Technically, it can't return anything.
jensj 2017/05/11 12:59:25 Done.
60 void BuildTypeInternal();
61 void BuildInterfaceType(bool simple);
62 void BuildFunctionType();
63 void BuildSimpleFunctionType();
64 void BuildTypeParameterType();
65
66 class TypeParameterScope {
67 public:
68 TypeParameterScope(StreamingDartTypeTranslator* translator,
69 intptr_t* parameters,
70 intptr_t parameters_count)
71 : parameters_(parameters),
72 parameters_count_(parameters_count),
73 outer_(translator->type_parameter_scope_),
74 translator_(translator) {
75 translator_->type_parameter_scope_ = this;
76 }
77 ~TypeParameterScope() {
78 delete[] parameters_;
79 translator_->type_parameter_scope_ = outer_;
80 }
81
82 TypeParameterScope* outer() const { return outer_; }
83 intptr_t* parameters() const { return parameters_; }
84 intptr_t parameters_count() const { return parameters_count_; }
85
86 private:
87 intptr_t* parameters_;
88 intptr_t parameters_count_;
89 TypeParameterScope* outer_;
90 StreamingDartTypeTranslator* translator_;
91 };
92
93 StreamingFlowGraphBuilder* builder_;
94 TranslationHelper& translation_helper_;
95 ActiveClass* active_class_;
96 TypeParameterScope* type_parameter_scope_;
97 Zone* zone_;
98 AbstractType& result_;
99 bool finalize_;
100 };
101
102
20 class StreamingConstantEvaluator { 103 class StreamingConstantEvaluator {
21 public: 104 public:
22 StreamingConstantEvaluator(StreamingFlowGraphBuilder* builder, 105 StreamingConstantEvaluator(StreamingFlowGraphBuilder* builder,
Kevin Millikin (Google) 2017/05/11 10:38:36 Same here, don't pass the zone, TranslationHelper,
jensj 2017/05/11 12:59:25 Done.
23 Zone* zone, 106 Zone* zone,
24 TranslationHelper* h, 107 TranslationHelper* helper,
25 DartTypeTranslator* type_translator); 108 StreamingDartTypeTranslator* type_translator);
26 109
27 virtual ~StreamingConstantEvaluator() {} 110 virtual ~StreamingConstantEvaluator() {}
28 111
29 Instance& EvaluateExpression(); 112 Instance& EvaluateExpression(intptr_t offset, bool reset_position = true);
30 113 Instance& EvaluateListLiteral(intptr_t offset, bool reset_position = true);
31 void EvaluateStaticGet(); 114 Instance& EvaluateMapLiteral(intptr_t offset, bool reset_position = true);
32 void EvaluateSymbolLiteral(); 115 Instance& EvaluateConstructorInvocation(intptr_t offset,
33 void EvaluateDoubleLiteral(); 116 bool reset_position = true);
117 Object& EvaluateExpressionSafe(intptr_t offset);
34 118
35 private: 119 private:
120 void EvaluateVariableGet();
121 void EvaluateVariableGet(uint8_t payload);
122 void EvaluatePropertyGet();
123 void EvaluateStaticGet();
124 void EvaluateMethodInvocation();
125 void EvaluateStaticInvocation();
126 void EvaluateConstructorInvocationInternal();
127 void EvaluateNot();
128 void EvaluateLogicalExpression();
129 void EvaluateConditionalExpression();
130 void EvaluateStringConcatenation();
131 void EvaluateSymbolLiteral();
132 void EvaluateTypeLiteral();
133 void EvaluateListLiteralInternal();
134 void EvaluateMapLiteralInternal();
135 void EvaluateLet();
136 void EvaluateBigIntLiteral();
137 void EvaluateStringLiteral();
138 void EvaluateIntLiteral(uint8_t payload);
139 void EvaluateIntLiteral(bool is_negative);
140 void EvaluateDoubleLiteral();
141 void EvaluateBoolLiteral(bool value);
142 void EvaluateNullLiteral();
143
144 const Object& RunFunction(const Function& function,
145 intptr_t argument_count,
146 const Instance* receiver,
147 const TypeArguments* type_args);
148
149 const Object& RunFunction(const Function& function,
150 const Array& arguments,
151 const Array& names);
152
36 RawObject* EvaluateConstConstructorCall(const dart::Class& type_class, 153 RawObject* EvaluateConstConstructorCall(const dart::Class& type_class,
37 const TypeArguments& type_arguments, 154 const TypeArguments& type_arguments,
38 const Function& constructor, 155 const Function& constructor,
39 const Object& argument); 156 const Object& argument);
40 157
158 const TypeArguments* TranslateTypeArguments(const Function& target,
159 dart::Class* target_klass);
160
161 void AssertBoolInCheckedMode() {
162 if (isolate_->type_checks() && !result_.IsBool()) {
163 translation_helper_.ReportError("Expected boolean expression.");
164 }
165 }
166
167 bool EvaluateBooleanExpressionHere();
168
41 bool GetCachedConstant(intptr_t kernel_offset, Instance* value); 169 bool GetCachedConstant(intptr_t kernel_offset, Instance* value);
42 void CacheConstantValue(intptr_t kernel_offset, const Instance& value); 170 void CacheConstantValue(intptr_t kernel_offset, const Instance& value);
43 171
44 StreamingFlowGraphBuilder* builder_; 172 StreamingFlowGraphBuilder* builder_;
45 Isolate* isolate_; 173 Isolate* isolate_;
46 Zone* zone_; 174 Zone* zone_;
47 TranslationHelper& translation_helper_; 175 TranslationHelper& translation_helper_;
48 // DartTypeTranslator& type_translator_; 176 StreamingDartTypeTranslator& type_translator_;
49 177
50 Script& script_; 178 Script& script_;
51 Instance& result_; 179 Instance& result_;
52 }; 180 };
53 181
54 182
55 class StreamingFlowGraphBuilder { 183 class StreamingFlowGraphBuilder {
56 public: 184 public:
57 StreamingFlowGraphBuilder(FlowGraphBuilder* flow_graph_builder, 185 StreamingFlowGraphBuilder(FlowGraphBuilder* flow_graph_builder,
58 const uint8_t* buffer, 186 const uint8_t* buffer,
59 intptr_t buffer_length) 187 intptr_t buffer_length)
60 : flow_graph_builder_(flow_graph_builder), 188 : flow_graph_builder_(flow_graph_builder),
61 translation_helper_(flow_graph_builder->translation_helper_), 189 translation_helper_(flow_graph_builder->translation_helper_),
62 zone_(flow_graph_builder->zone_), 190 zone_(flow_graph_builder->zone_),
63 reader_(new kernel::Reader(buffer, buffer_length)), 191 reader_(new kernel::Reader(buffer, buffer_length)),
64 constant_evaluator_(this, 192 constant_evaluator_(this,
65 flow_graph_builder->zone_, 193 zone_,
66 &flow_graph_builder->translation_helper_, 194 &translation_helper_,
67 &flow_graph_builder->type_translator_), 195 &type_translator_),
196 type_translator_(this,
197 &translation_helper_,
198 &flow_graph_builder->active_class_,
199 /* finalize= */ true),
68 canonical_names_(NULL), 200 canonical_names_(NULL),
69 canonical_names_size_(-1), 201 canonical_names_size_(-1),
70 canonical_names_entries_read_(0), 202 canonical_names_entries_read_(0),
71 canonical_names_next_offset_(-1) {} 203 canonical_names_next_offset_(-1),
204 direct_descendant_position_(TokenPosition::kLast) {}
72 205
73 virtual ~StreamingFlowGraphBuilder() { 206 virtual ~StreamingFlowGraphBuilder() {
74 delete reader_; 207 delete reader_;
75 // The canonical names themselves are not (yet) deallocated. 208 // The canonical names themselves are not (yet) deallocated.
76 delete[] canonical_names_; 209 delete[] canonical_names_;
77 } 210 }
78 211
79 Fragment BuildAt(intptr_t kernel_offset); 212 Fragment BuildExpressionAt(intptr_t kernel_offset);
213 Fragment BuildExpression();
214 Fragment BuildStatementAt(intptr_t kernel_offset);
215 Fragment BuildStatement();
80 216
81 private: 217 private:
82 CanonicalName* GetCanonicalName(intptr_t index); 218 CanonicalName* GetCanonicalName(intptr_t index);
219 intptr_t ReadNameAsStringIndex();
220 const dart::String& ReadNameAsMethodName();
221 const dart::String& ReadNameAsGetterName();
222 const dart::String& ReadNameAsSetterName();
223
224 void UpdateDirectDescendantPosition(TokenPosition position);
225 void ResetDirectDescendantPosition();
83 226
84 intptr_t ReaderOffset(); 227 intptr_t ReaderOffset();
85 void SetOffset(intptr_t offset); 228 void SetOffset(intptr_t offset);
86 void SkipBytes(intptr_t skip); 229 void SkipBytes(intptr_t skip);
230 bool ReadBool();
231 uint8_t ReadByte();
87 uint32_t ReadUInt(); 232 uint32_t ReadUInt();
233 uint32_t PeekUInt();
88 intptr_t ReadListLength(); 234 intptr_t ReadListLength();
235 void SkipDartType();
236 void SkipOptionalDartType();
237 void SkipInterfaceType(bool simple);
238 void SkipFunctionType(bool simple);
239 void SkipExpression();
240 void SkipStatement();
241 void SkipName();
242 void SkipArguments();
243 void SkipVariableDeclaration();
89 TokenPosition ReadPosition(bool record = true); 244 TokenPosition ReadPosition(bool record = true);
90 Tag ReadTag(uint8_t* payload = NULL); 245 Tag ReadTag(uint8_t* payload = NULL);
246 Tag PeekTag(uint8_t* payload = NULL);
247 word ReadFlags();
91 248
249 void loop_depth_inc();
250 void loop_depth_dec();
251 intptr_t for_in_depth();
252 void for_in_depth_inc();
253 void for_in_depth_dec();
254 void catch_depth_inc();
255 void catch_depth_dec();
256 void try_depth_inc();
257 void try_depth_dec();
258 intptr_t CurrentTryIndex();
259 intptr_t AllocateTryIndex();
260 LocalVariable* CurrentException();
261 LocalVariable* CurrentStackTrace();
92 CatchBlock* catch_block(); 262 CatchBlock* catch_block();
263 ActiveClass* active_class();
93 ScopeBuildingResult* scopes(); 264 ScopeBuildingResult* scopes();
94 ParsedFunction* parsed_function(); 265 ParsedFunction* parsed_function();
266 TryFinallyBlock* try_finally_block();
267 SwitchBlock* switch_block();
268 BreakableBlock* breakable_block();
269 GrowableArray<YieldContinuation>& yield_continuations();
270 Value* stack();
271 Value* Pop();
95 272
273 Tag PeekArgumentsFirstPositionalTag();
274 const TypeArguments& PeekArgumentsInstantiatedType(const dart::Class& klass);
275 intptr_t PeekArgumentsCount();
276 intptr_t PeekArgumentsTypeCount();
277 void SkipArgumentsBeforeActualArguments();
278
279 LocalVariable* LookupVariable(intptr_t kernel_offset);
280 LocalVariable* MakeTemporary();
281 Token::Kind MethodKind(const dart::String& name);
282 dart::RawFunction* LookupMethodByMember(CanonicalName* target,
283 const dart::String& method_name);
284
285 bool NeedsDebugStepCheck(const Function& function, TokenPosition position);
286 bool NeedsDebugStepCheck(Value* value, TokenPosition position);
287
288 void InlineBailout(const char* reason);
96 Fragment DebugStepCheck(TokenPosition position); 289 Fragment DebugStepCheck(TokenPosition position);
97 Fragment LoadLocal(LocalVariable* variable); 290 Fragment LoadLocal(LocalVariable* variable);
291 Fragment Return(TokenPosition position);
98 Fragment PushArgument(); 292 Fragment PushArgument();
293 Fragment EvaluateAssertion();
99 Fragment RethrowException(TokenPosition position, int catch_try_index); 294 Fragment RethrowException(TokenPosition position, int catch_try_index);
100 Fragment ThrowNoSuchMethodError(); 295 Fragment ThrowNoSuchMethodError();
101 Fragment Constant(const Object& value); 296 Fragment Constant(const Object& value);
102 Fragment IntConstant(int64_t value); 297 Fragment IntConstant(int64_t value);
103 Fragment LoadStaticField(); 298 Fragment LoadStaticField();
104 Fragment StaticCall(TokenPosition position, 299 Fragment StaticCall(TokenPosition position,
105 const Function& target, 300 const Function& target,
106 intptr_t argument_count); 301 intptr_t argument_count);
302 Fragment StaticCall(TokenPosition position,
303 const Function& target,
304 intptr_t argument_count,
305 const Array& argument_names);
306 Fragment InstanceCall(TokenPosition position,
307 const dart::String& name,
308 Token::Kind kind,
309 intptr_t argument_count,
310 intptr_t num_args_checked = 1);
311 Fragment InstanceCall(TokenPosition position,
312 const dart::String& name,
313 Token::Kind kind,
314 intptr_t argument_count,
315 const Array& argument_names,
316 intptr_t num_args_checked);
317 Fragment ThrowException(TokenPosition position);
318 Fragment BooleanNegate();
319 Fragment TranslateInstantiatedTypeArguments(
320 const TypeArguments& type_arguments);
321 Fragment StrictCompare(Token::Kind kind, bool number_check = false);
322 Fragment AllocateObject(const dart::Class& klass, intptr_t argument_count);
323 Fragment StoreLocal(TokenPosition position, LocalVariable* variable);
324 Fragment StoreStaticField(TokenPosition position, const dart::Field& field);
325 Fragment StringInterpolate(TokenPosition position);
326 Fragment StringInterpolateSingle(TokenPosition position);
327 Fragment ThrowTypeError();
328 Fragment LoadInstantiatorTypeArguments();
329 Fragment LoadFunctionTypeArguments();
330 Fragment InstantiateType(const AbstractType& type);
331 Fragment CreateArray();
332 Fragment StoreIndexed(intptr_t class_id);
333 Fragment CheckStackOverflow();
334 Fragment CloneContext();
335 Fragment TranslateFinallyFinalizers(TryFinallyBlock* outer_finally,
336 intptr_t target_context_depth);
337 Fragment BranchIfTrue(TargetEntryInstr** then_entry,
338 TargetEntryInstr** otherwise_entry,
339 bool negate);
340 Fragment BranchIfEqual(TargetEntryInstr** then_entry,
341 TargetEntryInstr** otherwise_entry,
342 bool negate);
343 Fragment BranchIfNull(TargetEntryInstr** then_entry,
344 TargetEntryInstr** otherwise_entry,
345 bool negate = false);
346 Fragment CatchBlockEntry(const Array& handler_types,
347 intptr_t handler_index,
348 bool needs_stacktrace);
349 Fragment TryCatch(int try_handler_index);
350 Fragment Drop();
351 Fragment NullConstant();
352 JoinEntryInstr* BuildJoinEntry();
353 JoinEntryInstr* BuildJoinEntry(intptr_t try_index);
354 Fragment Goto(JoinEntryInstr* destination);
355 Fragment BuildImplicitClosureCreation(const Function& target);
356 Fragment CheckBooleanInCheckedMode();
357 Fragment CheckAssignableInCheckedMode(const dart::AbstractType& dst_type,
358 const dart::String& dst_name);
359 Fragment CheckVariableTypeInCheckedMode(intptr_t variable_kernel_position);
360 Fragment CheckVariableTypeInCheckedMode(const AbstractType& dst_type,
361 const dart::String& name_symbol);
362 Fragment EnterScope(intptr_t kernel_offset, bool* new_context = NULL);
363 Fragment ExitScope(intptr_t kernel_offset);
364
365 Fragment TranslateCondition(bool* negate);
366 const TypeArguments& BuildTypeArguments();
367 Fragment BuildArguments(Array* argument_names,
368 intptr_t* argument_count,
369 bool skip_push_arguments = false,
370 bool do_drop = false);
371 Fragment BuildArgumentsFromActualArguments(Array* argument_names,
372 bool skip_push_arguments = false,
373 bool do_drop = false);
107 374
108 Fragment BuildInvalidExpression(); 375 Fragment BuildInvalidExpression();
376 Fragment BuildVariableGet();
377 Fragment BuildVariableGet(uint8_t payload);
378 Fragment BuildVariableSet();
379 Fragment BuildVariableSet(uint8_t payload);
380 Fragment BuildPropertyGet();
381 Fragment BuildPropertySet();
382 Fragment BuildDirectPropertyGet();
383 Fragment BuildDirectPropertySet();
109 Fragment BuildStaticGet(); 384 Fragment BuildStaticGet();
385 Fragment BuildStaticSet();
386 Fragment BuildMethodInvocation();
387 Fragment BuildDirectMethodInvocation();
388 Fragment BuildStaticInvocation(bool is_const);
389 Fragment BuildConstructorInvocation(bool is_const);
390 Fragment BuildNot();
391 Fragment BuildLogicalExpression();
392 Fragment BuildConditionalExpression();
393 Fragment BuildStringConcatenation();
394 Fragment BuildIsExpression();
395 Fragment BuildAsExpression();
110 Fragment BuildSymbolLiteral(); 396 Fragment BuildSymbolLiteral();
397 Fragment BuildTypeLiteral();
111 Fragment BuildThisExpression(); 398 Fragment BuildThisExpression();
112 Fragment BuildRethrow(); 399 Fragment BuildRethrow();
400 Fragment BuildThrow();
401 Fragment BuildListLiteral(bool is_const);
402 Fragment BuildMapLiteral(bool is_const);
403 Fragment BuildLet();
113 Fragment BuildBigIntLiteral(); 404 Fragment BuildBigIntLiteral();
114 Fragment BuildStringLiteral(); 405 Fragment BuildStringLiteral();
115 Fragment BuildIntLiteral(uint8_t payload); 406 Fragment BuildIntLiteral(uint8_t payload);
116 Fragment BuildIntLiteral(bool is_negative); 407 Fragment BuildIntLiteral(bool is_negative);
117 Fragment BuildDoubleLiteral(); 408 Fragment BuildDoubleLiteral();
118 Fragment BuildBoolLiteral(bool value); 409 Fragment BuildBoolLiteral(bool value);
119 Fragment BuildNullLiteral(); 410 Fragment BuildNullLiteral();
120 411
412 Fragment BuildInvalidStatement();
413 Fragment BuildExpressionStatement();
414 Fragment BuildBlock();
415 Fragment BuildEmptyStatement();
416 Fragment BuildAssertStatement();
417 Fragment BuildLabeledStatement();
418 Fragment BuildBreakStatement();
419 Fragment BuildWhileStatement();
420 Fragment BuildDoStatement();
421 Fragment BuildForStatement();
422 Fragment BuildForInStatement(bool async);
423 Fragment BuildSwitchStatement();
424 Fragment BuildContinueSwitchStatement();
425 Fragment BuildIfStatement();
426 Fragment BuildReturnStatement();
427 Fragment BuildTryCatch();
428 Fragment BuildTryFinally();
429 Fragment BuildYieldStatement();
430 Fragment BuildVariableDeclaration(bool has_tag);
431
121 FlowGraphBuilder* flow_graph_builder_; 432 FlowGraphBuilder* flow_graph_builder_;
122 TranslationHelper& translation_helper_; 433 TranslationHelper& translation_helper_;
123 Zone* zone_; 434 Zone* zone_;
124 kernel::Reader* reader_; 435 kernel::Reader* reader_;
125 StreamingConstantEvaluator constant_evaluator_; 436 StreamingConstantEvaluator constant_evaluator_;
437 StreamingDartTypeTranslator type_translator_;
126 438
127 CanonicalName** canonical_names_; 439 CanonicalName** canonical_names_;
128 intptr_t canonical_names_size_; 440 intptr_t canonical_names_size_;
129 intptr_t canonical_names_entries_read_; 441 intptr_t canonical_names_entries_read_;
130 intptr_t canonical_names_next_offset_; 442 intptr_t canonical_names_next_offset_;
131 443
444 // Keep track of direct-descendant Expressions token position.
445 // Note: This is set to TokenPosition::kLast to indicate 'not set'.
446 TokenPosition direct_descendant_position_;
447
132 friend class StreamingConstantEvaluator; 448 friend class StreamingConstantEvaluator;
449 friend class StreamingDartTypeTranslator;
133 }; 450 };
134 451
135 452
136 } // namespace kernel 453 } // namespace kernel
137 } // namespace dart 454 } // namespace dart
138 455
139 #endif // !defined(DART_PRECOMPILED_RUNTIME) 456 #endif // !defined(DART_PRECOMPILED_RUNTIME)
140 #endif // RUNTIME_VM_KERNEL_BINARY_FLOWGRAPH_H_ 457 #endif // RUNTIME_VM_KERNEL_BINARY_FLOWGRAPH_H_
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698