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

Side by Side Diff: runtime/vm/parser.cc

Issue 1310383002: Minor cleanup in preparation for removal of allocation in new space, and removal of an 'optimizatio… (Closed) Base URL: https://github.com/dart-lang/sdk.git@master
Patch Set: Created 5 years, 4 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
« no previous file with comments | « runtime/vm/parser.h ('k') | no next file » | 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 #include "vm/parser.h" 5 #include "vm/parser.h"
6 6
7 #include "lib/invocation_mirror.h" 7 #include "lib/invocation_mirror.h"
8 #include "platform/utils.h" 8 #include "platform/utils.h"
9 #include "vm/ast_transformer.h" 9 #include "vm/ast_transformer.h"
10 #include "vm/bootstrap.h" 10 #include "vm/bootstrap.h"
(...skipping 497 matching lines...) Expand 10 before | Expand all | Expand 10 after
508 name(NULL), 508 name(NULL),
509 default_value(NULL), 509 default_value(NULL),
510 metadata(NULL), 510 metadata(NULL),
511 var(NULL), 511 var(NULL),
512 is_final(false), 512 is_final(false),
513 is_field_initializer(false), 513 is_field_initializer(false),
514 has_explicit_type(false) { } 514 has_explicit_type(false) { }
515 const AbstractType* type; 515 const AbstractType* type;
516 intptr_t name_pos; 516 intptr_t name_pos;
517 const String* name; 517 const String* name;
518 const Object* default_value; // NULL if not an optional parameter. 518 const Instance* default_value; // NULL if not an optional parameter.
519 const Object* metadata; // NULL if no metadata or metadata not evaluated. 519 const Object* metadata; // NULL if no metadata or metadata not evaluated.
520 LocalVariable* var; // Scope variable allocated for this parameter. 520 LocalVariable* var; // Scope variable allocated for this parameter.
521 bool is_final; 521 bool is_final;
522 bool is_field_initializer; 522 bool is_field_initializer;
523 bool has_explicit_type; 523 bool has_explicit_type;
524 }; 524 };
525 525
526 526
527 struct ParamList { 527 struct ParamList {
528 ParamList() { 528 ParamList() {
(...skipping 821 matching lines...) Expand 10 before | Expand all | Expand 10 after
1350 params.AddFinalParameter(token_pos, 1350 params.AddFinalParameter(token_pos,
1351 &Symbols::ClosureParameter(), 1351 &Symbols::ClosureParameter(),
1352 &Type::ZoneHandle(Z, Type::DynamicType())); 1352 &Type::ZoneHandle(Z, Type::DynamicType()));
1353 bool params_ok = ParseFormalParameters(constructor, &params); 1353 bool params_ok = ParseFormalParameters(constructor, &params);
1354 USE(params_ok); 1354 USE(params_ok);
1355 ASSERT(params_ok); 1355 ASSERT(params_ok);
1356 // Per language spec, the type of the closure parameters is dynamic. 1356 // Per language spec, the type of the closure parameters is dynamic.
1357 // Replace the types parsed from the constructor. 1357 // Replace the types parsed from the constructor.
1358 params.EraseParameterTypes(); 1358 params.EraseParameterTypes();
1359 1359
1360 SetupDefaultsForOptionalParams(&params, default_values); 1360 SetupDefaultsForOptionalParams(params, default_values);
1361 ASSERT(func.num_fixed_parameters() == params.num_fixed_parameters); 1361 ASSERT(func.num_fixed_parameters() == params.num_fixed_parameters);
1362 ASSERT(func.NumOptionalParameters() == params.num_optional_parameters); 1362 ASSERT(func.NumOptionalParameters() == params.num_optional_parameters);
1363 1363
1364 OpenFunctionBlock(func); 1364 OpenFunctionBlock(func);
1365 LocalScope* scope = current_block_->scope; 1365 LocalScope* scope = current_block_->scope;
1366 AddFormalParamsToScope(&params, scope); 1366 AddFormalParamsToScope(&params, scope);
1367 1367
1368 ArgumentListNode* ctor_args = new ArgumentListNode(token_pos); 1368 ArgumentListNode* ctor_args = new ArgumentListNode(token_pos);
1369 // Skip implicit closure parameter at 0. 1369 // Skip implicit closure parameter at 0.
1370 for (intptr_t i = 1; i < func.NumParameters(); i++) { 1370 for (intptr_t i = 1; i < func.NumParameters(); i++) {
(...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after
1412 Field::ZoneHandle(Z, field_class.LookupInstanceField(field_name)); 1412 Field::ZoneHandle(Z, field_class.LookupInstanceField(field_name));
1413 const AbstractType& field_type = AbstractType::ZoneHandle(Z, field.type()); 1413 const AbstractType& field_type = AbstractType::ZoneHandle(Z, field.type());
1414 params.AddFinalParameter(ident_pos, 1414 params.AddFinalParameter(ident_pos,
1415 &Symbols::Value(), 1415 &Symbols::Value(),
1416 &field_type); 1416 &field_type);
1417 ASSERT(func.num_fixed_parameters() == 2); // closure, value. 1417 ASSERT(func.num_fixed_parameters() == 2); // closure, value.
1418 } else if (!parent.IsGetterFunction() && !parent.IsImplicitGetterFunction()) { 1418 } else if (!parent.IsGetterFunction() && !parent.IsImplicitGetterFunction()) {
1419 const bool allow_explicit_default_values = true; 1419 const bool allow_explicit_default_values = true;
1420 SkipFunctionPreamble(); 1420 SkipFunctionPreamble();
1421 ParseFormalParameterList(allow_explicit_default_values, false, &params); 1421 ParseFormalParameterList(allow_explicit_default_values, false, &params);
1422 SetupDefaultsForOptionalParams(&params, default_values); 1422 SetupDefaultsForOptionalParams(params, default_values);
1423 } 1423 }
1424 1424
1425 // Populate function scope with the formal parameters. 1425 // Populate function scope with the formal parameters.
1426 LocalScope* scope = current_block_->scope; 1426 LocalScope* scope = current_block_->scope;
1427 AddFormalParamsToScope(&params, scope); 1427 AddFormalParamsToScope(&params, scope);
1428 1428
1429 ArgumentListNode* func_args = new ArgumentListNode(token_pos); 1429 ArgumentListNode* func_args = new ArgumentListNode(token_pos);
1430 if (!func.is_static()) { 1430 if (!func.is_static()) {
1431 func_args->Add(LoadReceiver(token_pos)); 1431 func_args->Add(LoadReceiver(token_pos));
1432 } 1432 }
(...skipping 70 matching lines...) Expand 10 before | Expand all | Expand 10 after
1503 params.num_fixed_parameters++; 1503 params.num_fixed_parameters++;
1504 } 1504 }
1505 ASSERT(desc.PositionalCount() == params.num_fixed_parameters); 1505 ASSERT(desc.PositionalCount() == params.num_fixed_parameters);
1506 1506
1507 // Named parameters. 1507 // Named parameters.
1508 for (; i < desc.Count(); ++i) { 1508 for (; i < desc.Count(); ++i) {
1509 ParamDesc p; 1509 ParamDesc p;
1510 intptr_t index = i - desc.PositionalCount(); 1510 intptr_t index = i - desc.PositionalCount();
1511 p.name = &String::ZoneHandle(Z, desc.NameAt(index)); 1511 p.name = &String::ZoneHandle(Z, desc.NameAt(index));
1512 p.type = &Type::ZoneHandle(Z, Type::DynamicType()); 1512 p.type = &Type::ZoneHandle(Z, Type::DynamicType());
1513 p.default_value = &Object::null_object(); 1513 p.default_value = &Object::null_instance();
1514 params.parameters->Add(p); 1514 params.parameters->Add(p);
1515 params.num_optional_parameters++; 1515 params.num_optional_parameters++;
1516 params.has_optional_named_parameters = true; 1516 params.has_optional_named_parameters = true;
1517 } 1517 }
1518 ASSERT(desc.NamedCount() == params.num_optional_parameters); 1518 ASSERT(desc.NamedCount() == params.num_optional_parameters);
1519 1519
1520 SetupDefaultsForOptionalParams(&params, default_values); 1520 SetupDefaultsForOptionalParams(params, default_values);
1521 1521
1522 // Build local scope for function and populate with the formal parameters. 1522 // Build local scope for function and populate with the formal parameters.
1523 OpenFunctionBlock(func); 1523 OpenFunctionBlock(func);
1524 AddFormalParamsToScope(&params, current_block_->scope); 1524 AddFormalParamsToScope(&params, current_block_->scope);
1525 } 1525 }
1526 1526
1527 SequenceNode* Parser::ParseNoSuchMethodDispatcher(const Function& func, 1527 SequenceNode* Parser::ParseNoSuchMethodDispatcher(const Function& func,
1528 Array* default_values) { 1528 Array* default_values) {
1529 TRACE_PARSER("ParseNoSuchMethodDispatcher"); 1529 TRACE_PARSER("ParseNoSuchMethodDispatcher");
1530 ASSERT(FLAG_lazy_dispatchers); 1530 ASSERT(FLAG_lazy_dispatchers);
(...skipping 386 matching lines...) Expand 10 before | Expand all | Expand 10 after
1917 ExpectToken(Token::kASSIGN); 1917 ExpectToken(Token::kASSIGN);
1918 } else { 1918 } else {
1919 ExpectToken(Token::kCOLON); 1919 ExpectToken(Token::kCOLON);
1920 } 1920 }
1921 params->num_optional_parameters++; 1921 params->num_optional_parameters++;
1922 params->has_explicit_default_values = true; // Also if explicitly NULL. 1922 params->has_explicit_default_values = true; // Also if explicitly NULL.
1923 if (is_top_level_) { 1923 if (is_top_level_) {
1924 // Skip default value parsing. 1924 // Skip default value parsing.
1925 SkipExpr(); 1925 SkipExpr();
1926 } else { 1926 } else {
1927 const Object& const_value = ParseConstExpr()->literal(); 1927 const Instance& const_value = ParseConstExpr()->literal();
1928 parameter.default_value = &const_value; 1928 parameter.default_value = &const_value;
1929 } 1929 }
1930 } else { 1930 } else {
1931 if (params->has_optional_positional_parameters || 1931 if (params->has_optional_positional_parameters ||
1932 params->has_optional_named_parameters) { 1932 params->has_optional_named_parameters) {
1933 // Implicit default value is null. 1933 // Implicit default value is null.
1934 params->num_optional_parameters++; 1934 params->num_optional_parameters++;
1935 parameter.default_value = &Object::null_object(); 1935 parameter.default_value = &Object::null_instance();
1936 } else { 1936 } else {
1937 params->num_fixed_parameters++; 1937 params->num_fixed_parameters++;
1938 ASSERT(params->num_optional_parameters == 0); 1938 ASSERT(params->num_optional_parameters == 0);
1939 } 1939 }
1940 } 1940 }
1941 if (parameter.type->IsVoidType()) { 1941 if (parameter.type->IsVoidType()) {
1942 ReportError("parameter '%s' may not be 'void'", 1942 ReportError("parameter '%s' may not be 'void'",
1943 parameter.name->ToCString()); 1943 parameter.name->ToCString());
1944 } 1944 }
1945 if (params->implicitly_final) { 1945 if (params->implicitly_final) {
(...skipping 995 matching lines...) Expand 10 before | Expand all | Expand 10 after
2941 params.AddFinalParameter( 2941 params.AddFinalParameter(
2942 TokenPos(), 2942 TokenPos(),
2943 &Symbols::PhaseParameter(), 2943 &Symbols::PhaseParameter(),
2944 &Type::ZoneHandle(Z, Type::SmiType())); 2944 &Type::ZoneHandle(Z, Type::SmiType()));
2945 2945
2946 if (func.is_const()) { 2946 if (func.is_const()) {
2947 params.SetImplicitlyFinal(); 2947 params.SetImplicitlyFinal();
2948 } 2948 }
2949 ParseFormalParameterList(allow_explicit_default_values, false, &params); 2949 ParseFormalParameterList(allow_explicit_default_values, false, &params);
2950 2950
2951 SetupDefaultsForOptionalParams(&params, default_parameter_values); 2951 SetupDefaultsForOptionalParams(params, default_parameter_values);
2952 ASSERT(AbstractType::Handle(Z, func.result_type()).IsResolved()); 2952 ASSERT(AbstractType::Handle(Z, func.result_type()).IsResolved());
2953 ASSERT(func.NumParameters() == params.parameters->length()); 2953 ASSERT(func.NumParameters() == params.parameters->length());
2954 2954
2955 // Now populate function scope with the formal parameters. 2955 // Now populate function scope with the formal parameters.
2956 AddFormalParamsToScope(&params, current_block_->scope); 2956 AddFormalParamsToScope(&params, current_block_->scope);
2957 2957
2958 const bool is_redirecting_constructor = 2958 const bool is_redirecting_constructor =
2959 (CurrentToken() == Token::kCOLON) && 2959 (CurrentToken() == Token::kCOLON) &&
2960 ((LookaheadToken(1) == Token::kTHIS) && 2960 ((LookaheadToken(1) == Token::kTHIS) &&
2961 ((LookaheadToken(2) == Token::kLPAREN) || 2961 ((LookaheadToken(2) == Token::kLPAREN) ||
(...skipping 305 matching lines...) Expand 10 before | Expand all | Expand 10 after
3267 func.IsGetterFunction() || 3267 func.IsGetterFunction() ||
3268 (func.is_generated_body() && 3268 (func.is_generated_body() &&
3269 Function::Handle(func.parent_function()).IsGetterFunction())); 3269 Function::Handle(func.parent_function()).IsGetterFunction()));
3270 const bool allow_explicit_default_values = true; 3270 const bool allow_explicit_default_values = true;
3271 if (func.IsGetterFunction()) { 3271 if (func.IsGetterFunction()) {
3272 // Populate function scope with the formal parameters. Since in this case 3272 // Populate function scope with the formal parameters. Since in this case
3273 // we are compiling a getter this will at most populate the receiver. 3273 // we are compiling a getter this will at most populate the receiver.
3274 AddFormalParamsToScope(&params, current_block_->scope); 3274 AddFormalParamsToScope(&params, current_block_->scope);
3275 } else if (func.IsAsyncClosure()) { 3275 } else if (func.IsAsyncClosure()) {
3276 AddAsyncClosureParameters(&params); 3276 AddAsyncClosureParameters(&params);
3277 SetupDefaultsForOptionalParams(&params, default_parameter_values); 3277 SetupDefaultsForOptionalParams(params, default_parameter_values);
3278 AddFormalParamsToScope(&params, current_block_->scope); 3278 AddFormalParamsToScope(&params, current_block_->scope);
3279 ASSERT(AbstractType::Handle(Z, func.result_type()).IsResolved()); 3279 ASSERT(AbstractType::Handle(Z, func.result_type()).IsResolved());
3280 ASSERT(func.NumParameters() == params.parameters->length()); 3280 ASSERT(func.NumParameters() == params.parameters->length());
3281 if (!Function::Handle(func.parent_function()).IsGetterFunction()) { 3281 if (!Function::Handle(func.parent_function()).IsGetterFunction()) {
3282 // Parse and discard any formal parameters. They are accessed as 3282 // Parse and discard any formal parameters. They are accessed as
3283 // context variables. 3283 // context variables.
3284 ParamList discarded_params; 3284 ParamList discarded_params;
3285 ParseFormalParameterList(allow_explicit_default_values, 3285 ParseFormalParameterList(allow_explicit_default_values,
3286 false, 3286 false,
3287 &discarded_params); 3287 &discarded_params);
3288 } 3288 }
3289 } else if (func.IsSyncGenClosure()) { 3289 } else if (func.IsSyncGenClosure()) {
3290 AddSyncGenClosureParameters(&params); 3290 AddSyncGenClosureParameters(&params);
3291 SetupDefaultsForOptionalParams(&params, default_parameter_values); 3291 SetupDefaultsForOptionalParams(params, default_parameter_values);
3292 AddFormalParamsToScope(&params, current_block_->scope); 3292 AddFormalParamsToScope(&params, current_block_->scope);
3293 ASSERT(AbstractType::Handle(Z, func.result_type()).IsResolved()); 3293 ASSERT(AbstractType::Handle(Z, func.result_type()).IsResolved());
3294 if (!Function::Handle(func.parent_function()).IsGetterFunction()) { 3294 if (!Function::Handle(func.parent_function()).IsGetterFunction()) {
3295 // Parse and discard any formal parameters. They are accessed as 3295 // Parse and discard any formal parameters. They are accessed as
3296 // context variables. 3296 // context variables.
3297 ParamList discarded_params; 3297 ParamList discarded_params;
3298 ParseFormalParameterList(allow_explicit_default_values, 3298 ParseFormalParameterList(allow_explicit_default_values,
3299 false, 3299 false,
3300 &discarded_params); 3300 &discarded_params);
3301 } 3301 }
3302 } else if (func.IsAsyncGenClosure()) { 3302 } else if (func.IsAsyncGenClosure()) {
3303 AddAsyncGenClosureParameters(&params); 3303 AddAsyncGenClosureParameters(&params);
3304 SetupDefaultsForOptionalParams(&params, default_parameter_values); 3304 SetupDefaultsForOptionalParams(params, default_parameter_values);
3305 AddFormalParamsToScope(&params, current_block_->scope); 3305 AddFormalParamsToScope(&params, current_block_->scope);
3306 ASSERT(AbstractType::Handle(Z, func.result_type()).IsResolved()); 3306 ASSERT(AbstractType::Handle(Z, func.result_type()).IsResolved());
3307 ASSERT(func.NumParameters() == params.parameters->length()); 3307 ASSERT(func.NumParameters() == params.parameters->length());
3308 if (!Function::Handle(func.parent_function()).IsGetterFunction()) { 3308 if (!Function::Handle(func.parent_function()).IsGetterFunction()) {
3309 // Parse and discard any formal parameters. They are accessed as 3309 // Parse and discard any formal parameters. They are accessed as
3310 // context variables. 3310 // context variables.
3311 ParamList discarded_params; 3311 ParamList discarded_params;
3312 ParseFormalParameterList(allow_explicit_default_values, 3312 ParseFormalParameterList(allow_explicit_default_values,
3313 false, 3313 false,
3314 &discarded_params); 3314 &discarded_params);
3315 } 3315 }
3316 } else { 3316 } else {
3317 ParseFormalParameterList(allow_explicit_default_values, false, &params); 3317 ParseFormalParameterList(allow_explicit_default_values, false, &params);
3318 3318
3319 // The number of parameters and their type are not yet set in local 3319 // The number of parameters and their type are not yet set in local
3320 // functions, since they are not 'top-level' parsed. 3320 // functions, since they are not 'top-level' parsed.
3321 if (func.IsLocalFunction()) { 3321 if (func.IsLocalFunction()) {
3322 AddFormalParamsToFunction(&params, func); 3322 AddFormalParamsToFunction(&params, func);
3323 } 3323 }
3324 SetupDefaultsForOptionalParams(&params, default_parameter_values); 3324 SetupDefaultsForOptionalParams(params, default_parameter_values);
3325 ASSERT(AbstractType::Handle(Z, func.result_type()).IsResolved()); 3325 ASSERT(AbstractType::Handle(Z, func.result_type()).IsResolved());
3326 ASSERT(func.NumParameters() == params.parameters->length()); 3326 ASSERT(func.NumParameters() == params.parameters->length());
3327 3327
3328 // Check whether the function has any field initializer formal parameters, 3328 // Check whether the function has any field initializer formal parameters,
3329 // which are not allowed in non-constructor functions. 3329 // which are not allowed in non-constructor functions.
3330 if (params.has_field_initializer) { 3330 if (params.has_field_initializer) {
3331 for (int i = 0; i < params.parameters->length(); i++) { 3331 for (int i = 0; i < params.parameters->length(); i++) {
3332 ParamDesc& param = (*params.parameters)[i]; 3332 ParamDesc& param = (*params.parameters)[i];
3333 if (param.is_field_initializer) { 3333 if (param.is_field_initializer) {
3334 ReportError(param.name_pos, 3334 ReportError(param.name_pos,
(...skipping 3881 matching lines...) Expand 10 before | Expand all | Expand 10 after
7216 ASSERT(new_body->scope() != NULL); 7216 ASSERT(new_body->scope() != NULL);
7217 new_body->scope()->LookupVariable(Symbols::AwaitJumpVar(), false); 7217 new_body->scope()->LookupVariable(Symbols::AwaitJumpVar(), false);
7218 new_body->scope()->LookupVariable(Symbols::AwaitContextVar(), false); 7218 new_body->scope()->LookupVariable(Symbols::AwaitContextVar(), false);
7219 new_body->scope()->LookupVariable(Symbols::AsyncCompleter(), false); 7219 new_body->scope()->LookupVariable(Symbols::AsyncCompleter(), false);
7220 new_body->scope()->RecursivelyCaptureAllVariables(); 7220 new_body->scope()->RecursivelyCaptureAllVariables();
7221 return new_body; 7221 return new_body;
7222 } 7222 }
7223 7223
7224 7224
7225 // Set up default values for all optional parameters to the function. 7225 // Set up default values for all optional parameters to the function.
7226 void Parser::SetupDefaultsForOptionalParams(const ParamList* params, 7226 void Parser::SetupDefaultsForOptionalParams(const ParamList& params,
7227 Array* default_values) { 7227 Array* default_values) {
7228 if (params->num_optional_parameters > 0) { 7228 if (params.num_optional_parameters > 0) {
7229 // Build array of default parameter values. 7229 // Build array of default parameter values.
7230 ParamDesc* param = 7230 *default_values = Array::New(params.num_optional_parameters);
7231 params->parameters->data() + params->num_fixed_parameters; 7231 const ZoneGrowableArray<ParamDesc>& parameters = *params.parameters;
7232 *default_values = Array::New(params->num_optional_parameters); 7232 for (int i = 0; i < params.num_optional_parameters; i++) {
7233 for (int i = 0; i < params->num_optional_parameters; i++) { 7233 const Object* default_value =
7234 ASSERT(param->default_value != NULL); 7234 parameters[i + params.num_fixed_parameters].default_value;
hausner 2015/08/24 20:13:18 Maybe give a symbolic name to the offset in the pa
srdjan 2015/08/24 20:21:56 Done.
7235 default_values->SetAt(i, *param->default_value); 7235 default_values->SetAt(i, *default_value);
7236 param++;
7237 } 7236 }
7238 } 7237 }
7239 } 7238 }
7240 7239
7241 7240
7242 // Populate the parameter type array and parameter name array of the function 7241 // Populate the parameter type array and parameter name array of the function
7243 // with the formal parameter types and names. 7242 // with the formal parameter types and names.
7244 void Parser::AddFormalParamsToFunction(const ParamList* params, 7243 void Parser::AddFormalParamsToFunction(const ParamList* params,
7245 const Function& func) { 7244 const Function& func) {
7246 ASSERT((params != NULL) && (params->parameters != NULL)); 7245 ASSERT((params != NULL) && (params->parameters != NULL));
(...skipping 6942 matching lines...) Expand 10 before | Expand all | Expand 10 after
14189 void Parser::SkipQualIdent() { 14188 void Parser::SkipQualIdent() {
14190 ASSERT(IsIdentifier()); 14189 ASSERT(IsIdentifier());
14191 ConsumeToken(); 14190 ConsumeToken();
14192 if (CurrentToken() == Token::kPERIOD) { 14191 if (CurrentToken() == Token::kPERIOD) {
14193 ConsumeToken(); // Consume the kPERIOD token. 14192 ConsumeToken(); // Consume the kPERIOD token.
14194 ExpectIdentifier("identifier expected after '.'"); 14193 ExpectIdentifier("identifier expected after '.'");
14195 } 14194 }
14196 } 14195 }
14197 14196
14198 } // namespace dart 14197 } // namespace dart
OLDNEW
« no previous file with comments | « runtime/vm/parser.h ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698