Skip to content

Commit 825737d

Browse files
author
John Messerly
committed
fuse some null checks with type checks, introduce a special bool variant
fixes #536 [email protected] Review URL: https://codereview.chromium.org/1964263002 .
1 parent 97ec1f7 commit 825737d

File tree

4 files changed

+85
-66
lines changed

4 files changed

+85
-66
lines changed

pkg/dev_compiler/lib/runtime/dart_sdk.js

Lines changed: 29 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -456,22 +456,24 @@ dart_library.library('dart_sdk', null, /* Imports */[
456456
if (obj == null) return obj;
457457
let result = dart.strongInstanceOf(obj, type, true);
458458
if (result) return obj;
459+
dart._throwCastError(obj, type, result);
460+
};
461+
dart.test = function(obj) {
462+
if (typeof obj == "boolean") return obj;
463+
dart.throwCastError(dart.getReifiedType(obj), core.bool);
464+
};
465+
dart._throwCastError = function(obj, type, result) {
459466
let actual = dart.getReifiedType(obj);
460-
if (result === false) dart.throwCastError(actual, type);
461-
dart.throwStrongModeError('Strong mode cast failure from ' + dart.typeName(actual) + ' to ' + dart.typeName(type));
467+
if (result == false) dart.throwCastError(actual, type);
468+
dart.throwStrongModeError('Strong mode cast failure from ' + dart.notNull(dart.as(dart.typeName(actual), core.String)) + ' to ' + dart.notNull(dart.as(dart.typeName(type), core.String)));
462469
};
463470
dart.asInt = function(obj) {
464-
if (obj == null) {
465-
return null;
466-
}
471+
if (obj == null) return null;
467472
if (Math.floor(obj) != obj) {
468473
dart.throwCastError(dart.getReifiedType(obj), core.int);
469474
}
470475
return obj;
471476
};
472-
dart.arity = function(f) {
473-
return {min: f.length, max: f.length};
474-
};
475477
dart.equals = function(x, y) {
476478
if (x == null || y == null) return x == y;
477479
let eq = x['=='];
@@ -6976,7 +6978,7 @@ dart_library.library('dart_sdk', null, /* Imports */[
69766978
if (this.doneHandlers == null) {
69776979
this.doneHandlers = [];
69786980
}
6979-
if (dart.notNull(dart.as(dart.dsend(this.doneHandlers, 'contains', responsePort), core.bool))) return;
6981+
if (dart.test(dart.dsend(this.doneHandlers, 'contains', responsePort))) return;
69806982
dart.dsend(this.doneHandlers, 'add', responsePort);
69816983
}
69826984
removeDoneListener(responsePort) {
@@ -7070,7 +7072,7 @@ dart_library.library('dart_sdk', null, /* Imports */[
70707072
_isolate_helper._globalState.currentContext = old;
70717073
if (old != null) old[_setGlobals]();
70727074
if (this[_scheduledControlEvents] != null) {
7073-
while (dart.notNull(dart.as(dart.dload(this[_scheduledControlEvents], 'isNotEmpty'), core.bool))) {
7075+
while (dart.test(dart.dload(this[_scheduledControlEvents], 'isNotEmpty'))) {
70747076
dart.dcall(dart.dsend(this[_scheduledControlEvents], 'removeFirst'));
70757077
}
70767078
}
@@ -9289,20 +9291,20 @@ dart_library.library('dart_sdk', null, /* Imports */[
92899291
_js_helper.checkBool(isUtc);
92909292
let jsMonth = dart.dsend(month, '-', 1);
92919293
let value = null;
9292-
if (dart.notNull(dart.as(isUtc, core.bool))) {
9294+
if (dart.test(isUtc)) {
92939295
value = Date.UTC(years, jsMonth, day, hours, minutes, seconds, milliseconds);
92949296
} else {
92959297
value = new Date(years, jsMonth, day, hours, minutes, seconds, milliseconds).valueOf();
92969298
}
9297-
if (dart.notNull(dart.as(dart.dload(value, 'isNaN'), core.bool)) || dart.notNull(dart.as(dart.dsend(value, '<', -MAX_MILLISECONDS_SINCE_EPOCH), core.bool)) || dart.notNull(dart.as(dart.dsend(value, '>', MAX_MILLISECONDS_SINCE_EPOCH), core.bool))) {
9299+
if (dart.test(dart.dload(value, 'isNaN')) || dart.test(dart.dsend(value, '<', -MAX_MILLISECONDS_SINCE_EPOCH)) || dart.test(dart.dsend(value, '>', MAX_MILLISECONDS_SINCE_EPOCH))) {
92989300
return null;
92999301
}
9300-
if (dart.notNull(dart.as(dart.dsend(years, '<=', 0), core.bool)) || dart.notNull(dart.as(dart.dsend(years, '<', 100), core.bool))) return _js_helper.Primitives.patchUpY2K(value, years, isUtc);
9302+
if (dart.test(dart.dsend(years, '<=', 0)) || dart.test(dart.dsend(years, '<', 100))) return _js_helper.Primitives.patchUpY2K(value, years, isUtc);
93019303
return value;
93029304
}
93039305
static patchUpY2K(value, years, isUtc) {
93049306
let date = new Date(value);
9305-
if (dart.notNull(dart.as(isUtc, core.bool))) {
9307+
if (dart.test(isUtc)) {
93069308
date.setUTCFullYear(years);
93079309
} else {
93089310
date.setFullYear(years);
@@ -9410,7 +9412,7 @@ dart_library.library('dart_sdk', null, /* Imports */[
94109412
_js_helper.diagnoseIndexError = function(indexable, index) {
94119413
if (!(typeof index == 'number')) return new core.ArgumentError.value(index, 'index');
94129414
let length = dart.as(dart.dload(indexable, 'length'), core.int);
9413-
if (dart.notNull(dart.as(dart.dsend(index, '<', 0), core.bool)) || dart.notNull(dart.as(dart.dsend(index, '>=', length), core.bool))) {
9415+
if (dart.test(dart.dsend(index, '<', 0)) || dart.test(dart.dsend(index, '>=', length))) {
94149416
return core.RangeError.index(dart.as(index, core.int), indexable, 'index', null, length);
94159417
}
94169418
return new core.RangeError.value(dart.as(index, core.num), 'index');
@@ -9420,14 +9422,14 @@ dart_library.library('dart_sdk', null, /* Imports */[
94209422
if (!(typeof start == 'number')) {
94219423
return new core.ArgumentError.value(start, 'start');
94229424
}
9423-
if (dart.notNull(dart.as(dart.dsend(start, '<', 0), core.bool)) || dart.notNull(dart.as(dart.dsend(start, '>', length), core.bool))) {
9425+
if (dart.test(dart.dsend(start, '<', 0)) || dart.test(dart.dsend(start, '>', length))) {
94249426
return new core.RangeError.range(dart.as(start, core.num), 0, dart.as(length, core.int), 'start');
94259427
}
94269428
if (end != null) {
94279429
if (!(typeof end == 'number')) {
94289430
return new core.ArgumentError.value(end, 'end');
94299431
}
9430-
if (dart.notNull(dart.as(dart.dsend(end, '<', start), core.bool)) || dart.notNull(dart.as(dart.dsend(end, '>', length), core.bool))) {
9432+
if (dart.test(dart.dsend(end, '<', start)) || dart.test(dart.dsend(end, '>', length))) {
94319433
return new core.RangeError.range(dart.as(end, core.num), dart.as(start, core.int), dart.as(length, core.int), 'end');
94329434
}
94339435
}
@@ -28799,7 +28801,7 @@ dart_library.library('dart_sdk', null, /* Imports */[
2879928801
}
2880028802
get [_errorExplanation]() {
2880128803
dart.assert(this[_hasValue]);
28802-
if (dart.notNull(dart.as(dart.dsend(this.invalidValue, '<', 0), core.bool))) {
28804+
if (dart.test(dart.dsend(this.invalidValue, '<', 0))) {
2880328805
return ": index must not be negative";
2880428806
}
2880528807
if (this.length == 0) {
@@ -30361,9 +30363,9 @@ dart_library.library('dart_sdk', null, /* Imports */[
3036130363
} else {
3036230364
result = pathSegments[dartx.map](core.String)(dart.fn(s => core.Uri._uriEncode(core.Uri._pathCharTable, s, convert.UTF8, false), core.String, [core.String]))[dartx.join]("/");
3036330365
}
30364-
if (dart.notNull(dart.as(dart.dload(result, 'isEmpty'), core.bool))) {
30366+
if (dart.test(dart.dload(result, 'isEmpty'))) {
3036530367
if (isFile) return "/";
30366-
} else if (ensureLeadingSlash && !dart.notNull(dart.as(dart.dsend(result, 'startsWith', '/'), core.bool))) {
30368+
} else if (ensureLeadingSlash && !dart.test(dart.dsend(result, 'startsWith', '/'))) {
3036730369
result = "/" + dart.notNull(dart.as(result, core.String));
3036830370
}
3036930371
result = core.Uri._normalizePath(dart.as(result, core.String), scheme, hasAuthority);
@@ -31250,7 +31252,7 @@ dart_library.library('dart_sdk', null, /* Imports */[
3125031252
let indices = dart.list([core.UriData._noScheme], core.int);
3125131253
core.UriData._writeUri(dart.as(mimeType, core.String), null, parameters, buffer, indices);
3125231254
indices[dartx.add](buffer.length);
31253-
if (dart.notNull(dart.as(percentEncoded, core.bool))) {
31255+
if (dart.test(percentEncoded)) {
3125431256
buffer.write(',');
3125531257
core.UriData._uriEncodeBytes(core.UriData._uricTable, bytes, buffer);
3125631258
} else {
@@ -33225,13 +33227,13 @@ dart_library.library('dart_sdk', null, /* Imports */[
3322533227
if (dart.notNull(html_common.isJavaScriptDate(object))) return true;
3322633228
if (dart.is(object, core.List)) {
3322733229
for (let i = 0; i < dart.notNull(object[dartx.length]); i++) {
33228-
if (dart.notNull(dart.as(containsDate(object[dartx.get](i)), core.bool))) return true;
33230+
if (dart.test(containsDate(object[dartx.get](i)))) return true;
3322933231
}
3323033232
}
3323133233
return false;
3323233234
}
3323333235
dart.fn(containsDate);
33234-
if (dart.notNull(dart.as(containsDate(nativeKey), core.bool))) {
33236+
if (dart.test(containsDate(nativeKey))) {
3323533237
dart.throw(new core.UnimplementedError('Key containing DateTime'));
3323633238
}
3323733239
return nativeKey;
@@ -69613,21 +69615,21 @@ dart_library.library('dart_sdk', null, /* Imports */[
6961369615
}
6961469616
set height(newHeight) {
6961569617
if (dart.is(newHeight, html$.Dimension)) {
69616-
if (dart.notNull(dart.as(dart.dsend(dart.dload(newHeight, 'value'), '<', 0), core.bool))) newHeight = new html$.Dimension.px(0);
69618+
if (dart.test(dart.dsend(dart.dload(newHeight, 'value'), '<', 0))) newHeight = new html$.Dimension.px(0);
6961769619
this[_element$][dartx.style][dartx.height] = dart.toString(newHeight);
6961869620
} else if (typeof newHeight == 'number') {
69619-
if (dart.notNull(dart.as(dart.dsend(newHeight, '<', 0), core.bool))) newHeight = 0;
69621+
if (dart.test(dart.dsend(newHeight, '<', 0))) newHeight = 0;
6962069622
this[_element$][dartx.style][dartx.height] = `${newHeight}px`;
6962169623
} else {
6962269624
dart.throw(new core.ArgumentError("newHeight is not a Dimension or num"));
6962369625
}
6962469626
}
6962569627
set width(newWidth) {
6962669628
if (dart.is(newWidth, html$.Dimension)) {
69627-
if (dart.notNull(dart.as(dart.dsend(dart.dload(newWidth, 'value'), '<', 0), core.bool))) newWidth = new html$.Dimension.px(0);
69629+
if (dart.test(dart.dsend(dart.dload(newWidth, 'value'), '<', 0))) newWidth = new html$.Dimension.px(0);
6962869630
this[_element$][dartx.style][dartx.width] = dart.toString(newWidth);
6962969631
} else if (typeof newWidth == 'number') {
69630-
if (dart.notNull(dart.as(dart.dsend(newWidth, '<', 0), core.bool))) newWidth = 0;
69632+
if (dart.test(dart.dsend(newWidth, '<', 0))) newWidth = 0;
6963169633
this[_element$][dartx.style][dartx.width] = `${newWidth}px`;
6963269634
} else {
6963369635
dart.throw(new core.ArgumentError("newWidth is not a Dimension or num"));

pkg/dev_compiler/lib/src/compiler/code_generator.dart

Lines changed: 30 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -446,27 +446,44 @@ class CodeGenerator extends GeneralizingAstVisitor
446446
}
447447

448448
@override
449-
visitAsExpression(AsExpression node) {
450-
var from = getStaticType(node.expression);
451-
var to = node.type.type;
452-
453-
var fromExpr = _visit(node.expression);
449+
visitAsExpression(AsExpression node) =>
450+
_emitCast(node.expression, to: node.type.type);
451+
452+
/// Emits a cast and/or a null check (i.e. a cast to a non-null type).
453+
JS.Expression _emitCast(Expression fromExpr,
454+
{DartType to, bool checkNull: false}) {
455+
var jsFrom = _visit(fromExpr);
456+
var from = getStaticType(fromExpr);
457+
458+
JS.Expression maybeCheckNull(JS.Expression jsExpr) {
459+
if (checkNull && isNullable(fromExpr)) {
460+
return js.call('dart.notNull(#)', jsExpr);
461+
}
462+
return jsExpr;
463+
}
454464

455465
// Skip the cast if it's not needed.
456-
if (rules.isSubtypeOf(from, to)) return fromExpr;
466+
if (to == null || rules.isSubtypeOf(from, to)) {
467+
return maybeCheckNull(jsFrom);
468+
}
457469

458470
// All Dart number types map to a JS double.
459471
if (_isNumberInJS(from) && _isNumberInJS(to)) {
460472
// Make sure to check when converting to int.
461473
if (from != types.intType && to == types.intType) {
462-
return js.call('dart.asInt(#)', [fromExpr]);
474+
// TODO(jmesserly): fuse this with notNull check.
475+
return maybeCheckNull(js.call('dart.asInt(#)', [jsFrom]));
463476
}
464477

465478
// A no-op in JavaScript.
466-
return fromExpr;
479+
return maybeCheckNull(jsFrom);
467480
}
468481

469-
return js.call('dart.as(#, #)', [fromExpr, _emitType(to)]);
482+
if (to == types.boolType && checkNull) {
483+
return js.call('dart.test(#)', _visit(fromExpr));
484+
}
485+
486+
return maybeCheckNull(js.call('dart.as(#, #)', [jsFrom, _emitType(to)]));
470487
}
471488

472489
@override
@@ -3061,9 +3078,10 @@ class CodeGenerator extends GeneralizingAstVisitor
30613078

30623079
JS.Expression notNull(Expression expr) {
30633080
if (expr == null) return null;
3064-
var jsExpr = _visit(expr);
3065-
if (!isNullable(expr)) return jsExpr;
3066-
return js.call('dart.notNull(#)', jsExpr);
3081+
if (expr is AsExpression) {
3082+
return _emitCast(expr.expression, to: expr.type.type, checkNull: true);
3083+
}
3084+
return _emitCast(expr, checkNull: true);
30673085
}
30683086

30693087
@override

pkg/dev_compiler/test/codegen/expect/notnull.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -156,10 +156,10 @@ dart_library.library('notnull', null, /* Imports */[
156156
}
157157
f(o) {
158158
core.print(1 + dart.notNull(dart.as(this.varField, core.num)) + 2);
159-
while (dart.notNull(dart.as(dart.dsend(this.varField, '<', 10), core.bool))) {
159+
while (dart.test(dart.dsend(this.varField, '<', 10))) {
160160
this.varField = dart.dsend(this.varField, '+', 1);
161161
}
162-
while (dart.notNull(dart.as(dart.dsend(this.varField, '<', 10), core.bool)))
162+
while (dart.test(dart.dsend(this.varField, '<', 10)))
163163
this.varField = dart.dsend(this.varField, '+', 1);
164164
core.print(1 + dart.notNull(this.intField) + 2);
165165
while (dart.notNull(this.intField) < 10) {

pkg/dev_compiler/tool/input_sdk/private/ddc_runtime/operations.dart

Lines changed: 24 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -221,7 +221,7 @@ final _ignoreTypeFailure = JS('', '''(() => {
221221
/// and strong mode
222222
/// Returns null if [obj] is not an instance of [type] in strong mode
223223
/// but might be in spec mode
224-
strongInstanceOf(obj, type, ignoreFromWhiteList) => JS('', '''(() => {
224+
bool strongInstanceOf(obj, type, ignoreFromWhiteList) => JS('', '''(() => {
225225
let actual = $getReifiedType($obj);
226226
let result = $isSubtype(actual, $type);
227227
if (result || actual == $jsobject ||
@@ -252,36 +252,35 @@ instanceOf(obj, type) => JS('', '''(() => {
252252
})()''');
253253

254254
@JSExportName('as')
255-
cast(obj, type) => JS('', '''(() => {
256-
if ($obj == null) return $obj;
255+
cast(obj, type) {
256+
if (obj == null) return obj;
257257

258-
let result = $strongInstanceOf($obj, $type, true);
259-
if (result) return $obj;
258+
bool result = strongInstanceOf(obj, type, true);
259+
if (JS('bool', '#', result)) return obj;
260+
_throwCastError(obj, type, result);
261+
}
260262

261-
let actual = $getReifiedType($obj);
263+
bool test(obj) {
264+
if (JS('bool', 'typeof # == "boolean"', obj)) return JS('bool', '#', obj);
265+
throwCastError(getReifiedType(obj), JS('', '#', bool));
266+
}
262267

263-
if (result === false) $throwCastError(actual, $type);
268+
void _throwCastError(obj, type, bool result) {
269+
var actual = getReifiedType(obj);
270+
if (result == false) throwCastError(actual, type);
264271

265-
$throwStrongModeError('Strong mode cast failure from ' +
266-
$typeName(actual) + ' to ' + $typeName($type));
267-
})()''');
272+
throwStrongModeError('Strong mode cast failure from ' +
273+
typeName(actual) + ' to ' + typeName(type));
274+
}
268275

269-
asInt(obj) => JS('', '''(() => {
270-
if ($obj == null) {
271-
return null;
272-
}
273-
if (Math.floor($obj) != $obj) {
274-
// Note: null will also be caught by this check
275-
$throwCastError($getReifiedType($obj), $int);
276-
}
277-
return $obj;
278-
})()''');
276+
asInt(obj) {
277+
if (obj == null) return null;
279278

280-
arity(f) => JS('', '''(() => {
281-
// TODO(jmesserly): need to parse optional params.
282-
// In ES6, length is the number of required arguments.
283-
return { min: $f.length, max: $f.length };
284-
})()''');
279+
if (JS('bool', 'Math.floor(#) != #', obj, obj)) {
280+
throwCastError(getReifiedType(obj), JS('', '#', int));
281+
}
282+
return obj;
283+
}
285284

286285
equals(x, y) => JS('', '''(() => {
287286
if ($x == null || $y == null) return $x == $y;

0 commit comments

Comments
 (0)