Skip to content
This repository was archived by the owner on Feb 22, 2018. It is now read-only.

Commit 9d61df7

Browse files
author
John Messerly
committed
support the JS builtin
* fix a few cases where we regressed js_ast parser interpolations * infer correct static type for JS call expressions [email protected] Review URL: https://codereview.chromium.org/962083002
1 parent 1e62196 commit 9d61df7

File tree

14 files changed

+1124
-892
lines changed

14 files changed

+1124
-892
lines changed

lib/src/checker/resolver.dart

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -363,7 +363,26 @@ class RestrictedStaticTypeAnalyzer extends StaticTypeAnalyzer {
363363
visitMethodInvocation(MethodInvocation node) {
364364
// TODO(sigmund): follow up with analyzer team - why is this needed?
365365
visitSimpleIdentifier(node.methodName);
366-
return super.visitMethodInvocation(node);
366+
super.visitMethodInvocation(node);
367+
368+
var e = node.methodName.staticElement;
369+
if (e is FunctionElement &&
370+
e.library.name == '_foreign_helper' &&
371+
e.name == 'JS') {
372+
// Fix types for JS builtin calls.
373+
//
374+
// This code was taken from analyzer. It's not super sophisticated:
375+
// only looks for the type name in dart:core, so we just copy it here.
376+
//
377+
// TODO(jmesserly): we'll likely need something that can handle a wider
378+
// variety of types, especially when we get to JS interop.
379+
var args = node.argumentList.arguments;
380+
if (args.isNotEmpty && args.first is SimpleStringLiteral) {
381+
var coreLib = _typeProvider.objectType.element.library;
382+
var classElem = coreLib.getType(args.first.stringValue);
383+
if (classElem != null) node.staticType = classElem.type;
384+
}
385+
}
367386
}
368387

369388
// Review note: no longer need to override visitFunctionExpression, this is

lib/src/codegen/js_codegen.dart

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -757,9 +757,12 @@ class JSCodegenVisitor extends GeneralizingAstVisitor with ConversionVisitor {
757757
JS.Block visitBlock(Block node) => new JS.Block(_visitList(node.statements));
758758

759759
@override
760-
JS.Expression visitMethodInvocation(MethodInvocation node) {
760+
visitMethodInvocation(MethodInvocation node) {
761761
var target = node.isCascaded ? _cascadeTarget : node.target;
762762

763+
var result = _emitForeignJS(node);
764+
if (result != null) return result;
765+
763766
if (rules.isDynamicCall(node.methodName)) {
764767
var args = node.argumentList.accept(this);
765768
if (target != null) {
@@ -787,6 +790,25 @@ class JSCodegenVisitor extends GeneralizingAstVisitor with ConversionVisitor {
787790
return js.call('#(#)', [targetJs, node.argumentList.accept(this)]);
788791
}
789792

793+
/// Emits code for the `JS(...)` builtin.
794+
_emitForeignJS(MethodInvocation node) {
795+
var e = node.methodName.staticElement;
796+
if (e is FunctionElement &&
797+
e.library.name == '_foreign_helper' &&
798+
e.name == 'JS') {
799+
var args = node.argumentList.arguments;
800+
// arg[0] is static return type, used in `RestrictedStaticTypeAnalyzer`
801+
var code = args[1] as StringLiteral;
802+
803+
var template = js.parseForeignJS(code.stringValue);
804+
var result = template.instantiate(_visitList(args.skip(2)));
805+
// `throw` is emitted as a statement by `parseForeignJS`.
806+
assert(result is JS.Expression || node.parent is ExpressionStatement);
807+
return result;
808+
}
809+
return null;
810+
}
811+
790812
@override
791813
JS.Expression visitFunctionExpressionInvocation(
792814
FunctionExpressionInvocation node) {

lib/src/js/builder.dart

Lines changed: 56 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -894,26 +894,10 @@ class MiniJsParser {
894894
// PropertyDefinition :
895895
// PropertyName : AssignmentExpression
896896
// MethodDefinition
897+
properties.add(parseMethodOrProperty());
897898

898-
if (acceptCategory(HASH)) {
899-
properties.add(parseInterpolatedMember());
900-
} else {
901-
bool isGetter = acceptString('get');
902-
bool isSetter = isGetter ? false : acceptString('set');
903-
Expression name = parsePropertyName();
904-
905-
if (lastCategory == LPAREN) {
906-
Fun fun = parseFun();
907-
properties.add(
908-
new Method(name, fun, isGetter: isGetter, isSetter: isSetter));
909-
} else {
910-
expectCategory(COLON);
911-
Expression value = parseAssignment();
912-
properties.add(new Property(name, value));
913-
}
914-
if (acceptCategory(RBRACE)) break;
915-
expectCategory(COMMA);
916-
}
899+
if (acceptCategory(RBRACE)) break;
900+
expectCategory(COMMA);
917901
}
918902
return new ObjectInitializer(properties);
919903
}
@@ -1467,28 +1451,59 @@ class MiniJsParser {
14671451
expectCategory(LBRACE);
14681452
var methods = new List<Method>();
14691453
while (lastCategory != RBRACE) {
1470-
methods.add(parseMethod());
1454+
methods.add(parseMethodOrProperty(onlyMethods: true));
14711455
}
14721456
expectCategory(RBRACE);
14731457
return new ClassExpression(name, heritage, methods);
14741458
}
14751459

1476-
Method parseMethod() {
1477-
if (acceptCategory(HASH)) return parseInterpolatedMember();
1478-
1460+
/**
1461+
* Parses a [Method] or a [Property].
1462+
*
1463+
* Most of the complexity is from supporting interpolation. Several forms
1464+
* are supported:
1465+
*
1466+
* - getter/setter names: `get #() { ... }`
1467+
* - method names: `#() { ... }`
1468+
* - property names: `#: ...`
1469+
* - entire methods: `#`
1470+
*/
1471+
Property parseMethodOrProperty({bool onlyMethods: false}) {
14791472
bool isStatic = acceptString('static');
1480-
bool isGetter = acceptString('get');
1481-
bool isSetter = isGetter ? false : acceptString('set');
1482-
var name = parsePropertyName();
1483-
var fun = parseFun();
1484-
return new Method(name, fun,
1485-
isGetter: isGetter, isSetter: isSetter, isStatic: isStatic);
1486-
}
14871473

1488-
InterpolatedMethod parseInterpolatedMember() {
1489-
var member = new InterpolatedMethod(parseHash());
1490-
interpolatedValues.add(member);
1491-
return member;
1474+
bool isGetter = false;
1475+
bool isSetter = false;
1476+
Expression name = null;
1477+
if (acceptCategory(HASH)) {
1478+
if (lastCategory != LPAREN && (onlyMethods || lastCategory != COLON)) {
1479+
// Interpolated method
1480+
var member = new InterpolatedMethod(parseHash());
1481+
interpolatedValues.add(member);
1482+
return member;
1483+
}
1484+
name = parseInterpolatedExpression();
1485+
} else {
1486+
name = parsePropertyName();
1487+
}
1488+
1489+
// Allow get or set to be followed by another property name.
1490+
if (lastCategory == ALPHA && name is PropertyName) {
1491+
PropertyName p = name;
1492+
isGetter = p.name == 'get';
1493+
isSetter = p.name == 'set';
1494+
if (isGetter || isSetter) {
1495+
name = parsePropertyName();
1496+
}
1497+
}
1498+
1499+
if (!onlyMethods && acceptCategory(COLON)) {
1500+
Expression value = parseAssignment();
1501+
return new Property(name, value);
1502+
} else {
1503+
var fun = parseFun();
1504+
return new Method(name, fun,
1505+
isGetter: isGetter, isSetter: isSetter, isStatic: isStatic);
1506+
}
14921507
}
14931508

14941509
Expression parsePropertyName() {
@@ -1505,13 +1520,16 @@ class MiniJsParser {
15051520
expectCategory(RSQUARE);
15061521
return expr;
15071522
} else if (acceptCategory(HASH)) {
1508-
var nameOrPosition = parseHash();
1509-
var interpolatedLiteral = new InterpolatedLiteral(nameOrPosition);
1510-
interpolatedValues.add(interpolatedLiteral);
1511-
return interpolatedLiteral;
1523+
return parseInterpolatedExpression();
15121524
} else {
15131525
error('Expected property name');
15141526
return null;
15151527
}
15161528
}
1529+
1530+
InterpolatedExpression parseInterpolatedExpression() {
1531+
var interpolated = new InterpolatedExpression(parseHash());
1532+
interpolatedValues.add(interpolated);
1533+
return interpolated;
1534+
}
15171535
}

test/codegen/expect/_foreign_helper/_foreign_helper.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,7 @@ var _foreign_helper;
119119
}
120120
// Function JS_STRING_CONCAT: (String, String) → String
121121
function JS_STRING_CONCAT(a, b) {
122-
return dart.as(JS('String', '# + #', a, b), core.String);
122+
return a + b;
123123
}
124124
// Exports:
125125
_foreign_helper.JS = JS;

0 commit comments

Comments
 (0)