-
Notifications
You must be signed in to change notification settings - Fork 1.6k
/
Copy pathnative_generator.dart
214 lines (195 loc) · 8.28 KB
/
native_generator.dart
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
// Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
library dart2js.js_emitter.native_generator;
import 'package:js_runtime/shared/embedded_names.dart' as embeddedNames;
import '../js/js.dart' as jsAst;
import '../js/js.dart' show js;
import '../js_backend/backend_usage.dart' show BackendUsage;
import 'model.dart';
class NativeGenerator {
static bool needsIsolateAffinityTagInitialization(BackendUsage backendUsage) {
return backendUsage.needToInitializeIsolateAffinityTag;
}
/// Generates the code for isolate affinity tags.
///
/// Independently Dart programs on the same page must not interfere and
/// this code sets up the variables needed to guarantee that behavior.
static jsAst.Statement generateIsolateAffinityTagInitialization(
BackendUsage backendUsage,
jsAst.Expression generateEmbeddedGlobalAccess(String global),
jsAst.Expression internStringFunction) {
assert(backendUsage.needToInitializeIsolateAffinityTag);
jsAst.Expression getIsolateTagAccess =
generateEmbeddedGlobalAccess(embeddedNames.GET_ISOLATE_TAG);
jsAst.Expression isolateTagAccess =
generateEmbeddedGlobalAccess(embeddedNames.ISOLATE_TAG);
jsAst.Expression dispatchPropertyNameAccess =
generateEmbeddedGlobalAccess(embeddedNames.DISPATCH_PROPERTY_NAME);
return js.statement('''
!function() {
var intern = #internStringFunction;
#getIsolateTag = function(name) {
return intern("___dart_" + name + #isolateTag);
};
// To ensure that different programs loaded into the same context (page)
// use distinct dispatch properies, we place an object on `Object` to
// contain the names already in use.
var tableProperty = "___dart_isolate_tags_";
var usedProperties = Object[tableProperty] ||
(Object[tableProperty] = Object.create(null));
var rootProperty = "_${generateIsolateTagRoot()}";
for (var i = 0; ; i++) {
var property = intern(rootProperty + "_" + i + "_");
if (!(property in usedProperties)) {
usedProperties[property] = 1;
#isolateTag = property;
break;
}
}
if (#initializeDispatchProperty) {
#dispatchPropertyName = #getIsolateTag("dispatch_record");
}
}();
''', {
'initializeDispatchProperty':
backendUsage.needToInitializeDispatchProperty,
'internStringFunction': internStringFunction,
'getIsolateTag': getIsolateTagAccess,
'isolateTag': isolateTagAccess,
'dispatchPropertyName': dispatchPropertyNameAccess
});
}
static String generateIsolateTagRoot() {
// TODO(sra): MD5 of contributing source code or URIs?
return 'ZxYxX';
}
/// Encodes the collected native information so that it can be treated by
/// the native info-handler below.
///
/// The encoded information has the form:
///
// "%": "leafTag1|leafTag2|...;nonleafTag1|...;Class1|Class2|...",
//
// If there is no data following a semicolon, the semicolon can be omitted.
static jsAst.Expression encodeNativeInfo(Class cls) {
List<String> leafTags = cls.nativeLeafTags;
List<String> nonLeafTags = cls.nativeNonLeafTags;
List<Class> extensions = cls.nativeExtensions;
String formatTags(Iterable<String> tags) {
if (tags == null) return '';
return (tags.toList()..sort()).join('|');
}
String leafStr = formatTags(leafTags);
String nonLeafStr = formatTags(nonLeafTags);
StringBuffer sb = new StringBuffer(leafStr);
if (nonLeafStr != '') {
sb..write(';')..write(nonLeafStr);
}
String encoding = sb.toString();
if (cls.isNative || encoding != '' || extensions != null) {
List<jsAst.Literal> parts = <jsAst.Literal>[js.stringPart(encoding)];
if (extensions != null) {
parts
..add(js.stringPart(';'))
..addAll(js.joinLiterals(
extensions.map((Class cls) => cls.name), js.stringPart('|')));
}
return jsAst.concatenateStrings(parts, addQuotes: true);
}
return null;
}
/// Returns a JavaScript template that fills the embedded globals referenced
/// by [interceptorsByTagAccess] and [leafTagsAccess].
///
/// This code must be invoked for every class that has a native info before
/// the program starts.
///
/// The [infoAccess] parameter must evaluate to an expression that contains
/// the info (as a JavaScript string).
///
/// The [constructorAccess] parameter must evaluate to an expression that
/// contains the constructor of the class. The constructor's prototype must
/// be set up.
///
/// The [subclassReadGenerator] function must evaluate to a JS expression
/// that returns a reference to the constructor (with evaluated prototype)
/// of the given JS expression.
///
/// The [interceptorsByTagAccess] must point to the embedded global
/// [embeddedNames.INTERCEPTORS_BY_TAG] and must be initialized with an empty
/// JS Object (used as a map).
///
/// Similarly, the [leafTagsAccess] must point to the embedded global
/// [embeddedNames.LEAF_TAGS] and must be initialized with an empty JS Object
/// (used as a map).
///
/// Both variables are passed in (instead of creating the access here) to
/// make sure the caller is aware of these globals.
static jsAst.Statement buildNativeInfoHandler(
jsAst.Expression infoAccess,
jsAst.Expression constructorAccess,
jsAst.Expression subclassReadGenerator(jsAst.Expression subclass),
jsAst.Expression interceptorsByTagAccess,
jsAst.Expression leafTagsAccess) {
jsAst.Expression subclassRead =
subclassReadGenerator(js('subclasses[i]', []));
return js.statement('''
// The native info looks like this:
//
// HtmlElement: {
// "%": "HTMLDivElement|HTMLAnchorElement;HTMLElement;FancyButton"
//
// The first two semicolon-separated parts contain dispatch tags, the
// third contains the JavaScript names for classes.
//
// The tags indicate that JavaScript objects with the dispatch tags
// (usually constructor names) HTMLDivElement, HTMLAnchorElement and
// HTMLElement all map to the Dart native class named HtmlElement.
// The first set is for effective leaf nodes in the hierarchy, the
// second set is non-leaf nodes.
//
// The third part contains the JavaScript names of Dart classes that
// extend the native class. Here, FancyButton extends HtmlElement, so
// the runtime needs to know that window.HTMLElement.prototype is the
// prototype that needs to be extended in creating the custom element.
//
// The information is used to build tables referenced by
// getNativeInterceptor and custom element support.
{
var nativeSpec = #info.split(";");
if (nativeSpec[0]) {
var tags = nativeSpec[0].split("|");
for (var i = 0; i < tags.length; i++) {
#interceptorsByTagAccess[tags[i]] = #constructor;
#leafTagsAccess[tags[i]] = true;
}
}
if (nativeSpec[1]) {
tags = nativeSpec[1].split("|");
if (#allowNativesSubclassing) {
if (nativeSpec[2]) {
var subclasses = nativeSpec[2].split("|");
for (var i = 0; i < subclasses.length; i++) {
var subclass = #subclassRead;
subclass.#nativeSuperclassTagName = tags[0];
}
}
for (i = 0; i < tags.length; i++) {
#interceptorsByTagAccess[tags[i]] = #constructor;
#leafTagsAccess[tags[i]] = false;
}
}
}
}
''', {
'info': infoAccess,
'constructor': constructorAccess,
'subclassRead': subclassRead,
'interceptorsByTagAccess': interceptorsByTagAccess,
'leafTagsAccess': leafTagsAccess,
'nativeSuperclassTagName': embeddedNames.NATIVE_SUPERCLASS_TAG_NAME,
'allowNativesSubclassing': true
});
}
}