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

Commit 8dc1eee

Browse files
author
John Messerly
committed
fixes #173, ability to use native JS indexers
dom_experimental could opt-in to use this Review URL: https://codereview.chromium.org/1145833004
1 parent c416826 commit 8dc1eee

File tree

7 files changed

+96
-9
lines changed

7 files changed

+96
-9
lines changed

lib/src/codegen/js_codegen.dart

+23-6
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,7 @@ class JSCodegenVisitor extends GeneralizingAstVisitor with ConversionVisitor {
110110
if (unit.directives.isNotEmpty) {
111111
var libraryDir = unit.directives.first;
112112
if (libraryDir is LibraryDirective) {
113-
var jsName = getAnnotationValue(libraryDir, _isJsNameAnnotation);
113+
var jsName = findNodeAnnotation(libraryDir, _isJsNameAnnotation);
114114
jsDefaultValue = getConstantField(jsName, 'name', types.stringType);
115115
}
116116
}
@@ -329,7 +329,7 @@ class JSCodegenVisitor extends GeneralizingAstVisitor with ConversionVisitor {
329329
// If we've already emitted this class, skip it.
330330
var classElem = node.element;
331331
var type = classElem.type;
332-
var jsName = getAnnotationValue(node, _isJsNameAnnotation);
332+
var jsName = findNodeAnnotation(node, _isJsNameAnnotation);
333333

334334
if (jsName != null) return _emitJsType(node.name.name, jsName);
335335

@@ -350,7 +350,7 @@ class JSCodegenVisitor extends GeneralizingAstVisitor with ConversionVisitor {
350350
_classHeritage(classElem), _emitClassMethods(node, ctors, fields));
351351

352352
String jsPeerName;
353-
var jsPeer = getAnnotationValue(node, _isJsPeerInterface);
353+
var jsPeer = findNodeAnnotation(node, _isJsPeerInterface);
354354
if (jsPeer != null) {
355355
jsPeerName = getConstantField(jsPeer, 'name', types.stringType);
356356
}
@@ -475,7 +475,7 @@ class JSCodegenVisitor extends GeneralizingAstVisitor with ConversionVisitor {
475475
jsMethods.add(_emitImplicitConstructor(node, fields));
476476
}
477477

478-
bool hasJsPeer = getAnnotationValue(node, _isJsPeerInterface) != null;
478+
bool hasJsPeer = findNodeAnnotation(node, _isJsPeerInterface) != null;
479479

480480
bool hasIterator = false;
481481
for (var m in node.members) {
@@ -1349,7 +1349,12 @@ class JSCodegenVisitor extends GeneralizingAstVisitor with ConversionVisitor {
13491349

13501350
JS.Expression _emitSet(Expression lhs, Expression rhs) {
13511351
if (lhs is IndexExpression) {
1352-
return _emitSend(_getTarget(lhs), '[]=', [lhs.index, rhs]);
1352+
var target = _getTarget(lhs);
1353+
if (_useNativeJsIndexer(target.staticType)) {
1354+
return js.call(
1355+
'#[#] = #', [_visit(target), _visit(lhs.index), _visit(rhs)]);
1356+
}
1357+
return _emitSend(target, '[]=', [lhs.index, rhs]);
13531358
}
13541359

13551360
Expression target = null;
@@ -2116,9 +2121,18 @@ class JSCodegenVisitor extends GeneralizingAstVisitor with ConversionVisitor {
21162121

21172122
@override
21182123
visitIndexExpression(IndexExpression node) {
2119-
return _emitSend(_getTarget(node), '[]', [node.index]);
2124+
var target = _getTarget(node);
2125+
if (_useNativeJsIndexer(target.staticType)) {
2126+
return new JS.PropertyAccess(_visit(target), _visit(node.index));
2127+
}
2128+
return _emitSend(target, '[]', [node.index]);
21202129
}
21212130

2131+
// TODO(jmesserly): ideally we'd check the method and see if it is marked
2132+
// `external`, but that doesn't work because it isn't in the element model.
2133+
bool _useNativeJsIndexer(DartType type) =>
2134+
findElementAnnotation(type.element, _isJsIndexerAnnotation) != null;
2135+
21222136
/// Gets the target of a [PropertyAccess] or [IndexExpression].
21232137
/// Those two nodes are special because they're both allowed on left side of
21242138
/// an assignment expression and cascades.
@@ -2636,6 +2650,9 @@ String jsOutputPath(LibraryInfo info, Uri root) {
26362650
// TODO(jmesserly): validate the library. See issue #135.
26372651
bool _isJsNameAnnotation(DartObjectImpl value) => value.type.name == 'JsName';
26382652

2653+
bool _isJsIndexerAnnotation(DartObjectImpl value) =>
2654+
value.type.name == 'JsIndexer';
2655+
26392656
bool _isJsPeerInterface(DartObjectImpl value) =>
26402657
value.type.name == 'JsPeerInterface';
26412658

lib/src/utils.dart

+14-1
Original file line numberDiff line numberDiff line change
@@ -349,7 +349,7 @@ String resourceOutputPath(Uri resourceUri, Uri entryUri) {
349349
///
350350
/// (v) => v.type.name == 'Deprecated' && v.type.element.library.isDartCore
351351
///
352-
DartObjectImpl getAnnotationValue(
352+
DartObjectImpl findNodeAnnotation(
353353
AnnotatedNode node, bool test(DartObjectImpl value)) {
354354
for (var metadata in node.metadata) {
355355
ElementAnnotationImpl element = metadata.elementAnnotation;
@@ -364,6 +364,19 @@ DartObjectImpl getAnnotationValue(
364364
return null;
365365
}
366366

367+
/// Similar to [findNodeAnnotation] but starts from any element.
368+
DartObjectImpl findElementAnnotation(
369+
Element element, bool test(DartObjectImpl value)) {
370+
for (var metadata in element.metadata) {
371+
var evalResult = metadata.evaluationResult;
372+
if (evalResult == null) continue;
373+
374+
var value = evalResult.value;
375+
if (value != null && test(value)) return value;
376+
}
377+
return null;
378+
}
379+
367380
/// Given a constant [value], a [fieldName], and an [expectedType], returns the
368381
/// value of that field.
369382
///

test/codegen/domtest.dart

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
// Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file
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.
4+
5+
library domtest;
6+
7+
import 'sunflower/dom.dart';
8+
9+
// https://github.com/dart-lang/dev_compiler/issues/173
10+
testNativeIndexers() {
11+
var nodes = document.querySelector('body').childNodes;
12+
for (int i = 0; i < nodes.length; i++) {
13+
print(nodes[i]);
14+
print(nodes[i] = null);
15+
}
16+
}

test/codegen/expect/domtest.js

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
var domtest = dart.defineLibrary(domtest, {});
2+
var dom = dart.import(dom);
3+
var core = dart.import(core);
4+
(function(exports, dom, core) {
5+
'use strict';
6+
// Function testNativeIndexers: () → dynamic
7+
function testNativeIndexers() {
8+
let nodes = dom.document.querySelector('body').childNodes;
9+
for (let i = 0; dart.notNull(i) < dart.notNull(nodes.length); i = dart.notNull(i) + 1) {
10+
core.print(nodes[i]);
11+
core.print(nodes[i] = null);
12+
}
13+
}
14+
// Exports:
15+
exports.testNativeIndexers = testNativeIndexers;
16+
})(domtest, dom, core);

test/codegen/expect/domtest.txt

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
// Messages from compiling domtest.dart

test/codegen/expect/sunflower/dom.js

+5-2
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,11 @@ var core = dart.import(core);
1919
constructors: () => ({Overload: [Overload, []]})
2020
});
2121
let overload = dart.const(new Overload());
22+
class JsIndexer extends core.Object {
23+
JsIndexer() {
24+
}
25+
}
2226
let EventListener = dart.typedef('EventListener', () => dart.functionType(dart.void, [Event]));
23-
class Event extends core.Object {}
2427
let InputElement = HTMLInputElement;
2528
let CanvasElement = HTMLCanvasElement;
2629
class RenderingContext extends core.Object {}
@@ -41,8 +44,8 @@ var core = dart.import(core);
4144
exports.JsName = JsName;
4245
exports.Overload = Overload;
4346
exports.overload = overload;
47+
exports.JsIndexer = JsIndexer;
4448
exports.EventListener = EventListener;
45-
exports.Event = Event;
4649
exports.InputElement = InputElement;
4750
exports.CanvasElement = CanvasElement;
4851
exports.RenderingContext = RenderingContext;

test/codegen/sunflower/dom.dart

+21
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,10 @@ class Overload {
1919
}
2020
const overload = const Overload();
2121

22+
class JsIndexer {
23+
const JsIndexer();
24+
}
25+
2226
external Document get document;
2327

2428
@JsName(name: 'Document')
@@ -30,10 +34,27 @@ abstract class Document {
3034
abstract class Element {
3135
void addEventListener(String type, EventListener callback, [bool capture]);
3236
String textContent;
37+
NodeList get childNodes;
38+
}
39+
40+
@JsName()
41+
class Node {}
42+
43+
@JsName()
44+
@JsIndexer()
45+
class NodeList {
46+
external NodeList();
47+
external num get length;
48+
external set length(num _);
49+
external Node item(num index);
50+
51+
external Node operator [](num index);
52+
external void operator []=(num index, Node);
3353
}
3454

3555
typedef void EventListener(Event e);
3656

57+
@JsName()
3758
abstract class Event {}
3859

3960
@JsName(name: 'HTMLInputElement')

0 commit comments

Comments
 (0)