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

Side by Side Diff: frog/leg/native_handler.dart

Issue 9873021: Move frog/leg to lib/compiler/implementation. (Closed) Base URL: http://dart.googlecode.com/svn/branches/bleeding_edge/dart/
Patch Set: Created 8 years, 8 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 | Annotate | Revision Log
« no previous file with comments | « frog/leg/native_emitter.dart ('k') | frog/leg/operations.dart » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
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
3 // BSD-style license that can be found in the LICENSE file.
4
5 #library('native');
6 #import('../../lib/uri/uri.dart');
7 #import('leg.dart');
8 #import('elements/elements.dart');
9 #import('scanner/scannerlib.dart');
10 #import('ssa/ssa.dart');
11 #import('tree/tree.dart');
12 #import('util/util.dart');
13
14 void processNativeClasses(Compiler compiler,
15 Collection<LibraryElement> libraries) {
16 for (LibraryElement library in libraries) {
17 processNativeClassesInLibrary(compiler, library);
18 }
19 }
20
21 void addSubtypes(ClassElement cls,
22 NativeEmitter emitter) {
23 for (Type type in cls.allSupertypes) {
24 List<Element> subtypes = emitter.subtypes.putIfAbsent(
25 type.element,
26 () => <ClassElement>[]);
27 subtypes.add(cls);
28 }
29 }
30
31 void processNativeClassesInLibrary(Compiler compiler,
32 LibraryElement library) {
33 bool hasNativeClass = false;
34 for (Link<Element> link = library.topLevelElements;
35 !link.isEmpty(); link = link.tail) {
36 Element element = link.head;
37 if (element.kind == ElementKind.CLASS) {
38 ClassElement classElement = element;
39 if (classElement.isNative()) {
40 hasNativeClass = true;
41 compiler.registerInstantiatedClass(classElement);
42 // Also parse the node to know all its methods because
43 // otherwise it will only be parsed if there is a call to
44 // one of its constructor.
45 classElement.parseNode(compiler);
46 // Resolve to setup the inheritance.
47 classElement.ensureResolved(compiler);
48 // Add the information that this class is a subtype of
49 // its supertypes. The code emitter and the ssa builder use that
50 // information.
51 NativeEmitter emitter = compiler.emitter.nativeEmitter;
52 addSubtypes(classElement, emitter);
53 }
54 }
55 }
56 if (hasNativeClass) {
57 compiler.registerStaticUse(compiler.findHelper(
58 const SourceString('dynamicFunction')));
59 compiler.registerStaticUse(compiler.findHelper(
60 const SourceString('dynamicSetMetadata')));
61 compiler.registerStaticUse(compiler.findHelper(
62 const SourceString('defineProperty')));
63 compiler.registerStaticUse(compiler.findHelper(
64 const SourceString('toStringForNativeObject')));
65 }
66 }
67
68 void maybeEnableNative(Compiler compiler,
69 LibraryElement library,
70 Uri uri) {
71 String libraryName = uri.toString();
72 if (library.script.name.contains('dart/frog/tests/native/src')
73 || libraryName == 'dart:dom'
74 || libraryName == 'dart:isolate'
75 || libraryName == 'dart:html') {
76 library.define(new ForeignElement(
77 const SourceString('native'), library), compiler);
78 library.canUseNative = true;
79 }
80
81 // Additionaly, if this is a test, we allow access to foreign functions.
82 if (library.script.name.contains('dart/frog/tests/native/src')) {
83 library.define(compiler.findHelper(const SourceString('JS')), compiler);
84 }
85 }
86
87 void checkAllowedLibrary(ElementListener listener, Token token) {
88 LibraryElement currentLibrary = listener.compilationUnitElement.getLibrary();
89 if (!currentLibrary.canUseNative) {
90 listener.recoverableError("Unexpected token", token: token);
91 }
92 }
93
94 Token handleNativeBlockToSkip(Listener listener, Token token) {
95 checkAllowedLibrary(listener, token);
96 token = token.next;
97 if (token.kind === STRING_TOKEN) {
98 token = token.next;
99 }
100 if (token.stringValue === '{') {
101 BeginGroupToken beginGroupToken = token;
102 token = beginGroupToken.endGroup;
103 }
104 return token;
105 }
106
107 Token handleNativeClassBodyToSkip(Listener listener, Token token) {
108 checkAllowedLibrary(listener, token);
109 listener.handleIdentifier(token);
110 token = token.next;
111 if (token.kind !== STRING_TOKEN) {
112 return listener.unexpected(token);
113 }
114 token = token.next;
115 if (token.stringValue !== '{') {
116 return listener.unexpected(token);
117 }
118 BeginGroupToken beginGroupToken = token;
119 token = beginGroupToken.endGroup;
120 return token;
121 }
122
123 Token handleNativeClassBody(Listener listener, Token token) {
124 checkAllowedLibrary(listener, token);
125 token = token.next;
126 if (token.kind !== STRING_TOKEN) {
127 listener.unexpected(token);
128 } else {
129 token = token.next;
130 }
131 return token;
132 }
133
134 Token handleNativeFunctionBody(ElementListener listener, Token token) {
135 checkAllowedLibrary(listener, token);
136 Token begin = token;
137 listener.beginExpressionStatement(token);
138 listener.handleIdentifier(token);
139 token = token.next;
140 if (token.kind === STRING_TOKEN) {
141 listener.beginLiteralString(token);
142 listener.endLiteralString(0);
143 listener.pushNode(new NodeList.singleton(listener.popNode()));
144 listener.endSend(token);
145 token = token.next;
146 listener.endExpressionStatement(token);
147 } else {
148 listener.pushNode(new NodeList.empty());
149 listener.endSend(token);
150 listener.endReturnStatement(true, begin, token);
151 }
152 listener.endFunctionBody(1, begin, token);
153 // TODO(ngeoffray): expect a ';'.
154 return token.next;
155 }
156
157 SourceString checkForNativeClass(ElementListener listener) {
158 SourceString nativeName;
159 Node node = listener.nodes.head;
160 if (node != null
161 && node.asIdentifier() != null
162 && node.asIdentifier().source.stringValue == 'native') {
163 nativeName = node.asIdentifier().token.next.value;
164 listener.popNode();
165 }
166 return nativeName;
167 }
168
169 bool isOverriddenMethod(FunctionElement element,
170 ClassElement cls,
171 NativeEmitter nativeEmitter) {
172 List<ClassElement> subtypes = nativeEmitter.subtypes[cls];
173 if (subtypes == null) return false;
174 for (ClassElement subtype in subtypes) {
175 if (subtype.lookupLocalMember(element.name) != null) return true;
176 }
177 return false;
178 }
179
180 void handleSsaNative(SsaBuilder builder, Send node) {
181 // Register NoSuchMethodException and captureStackTrace in the compiler
182 // because the dynamic dispatch for native classes may use them.
183 Compiler compiler = builder.compiler;
184 ClassElement cls = compiler.coreLibrary.find(
185 Compiler.NO_SUCH_METHOD_EXCEPTION);
186 cls.ensureResolved(compiler);
187 compiler.addToWorkList(cls.lookupConstructor(cls.name));
188 compiler.registerStaticUse(
189 compiler.findHelper(new SourceString('captureStackTrace')));
190
191 FunctionElement element = builder.work.element;
192 element.setNative();
193 NativeEmitter nativeEmitter = compiler.emitter.nativeEmitter;
194 // If what we're compiling is a getter named 'typeName' and the native
195 // class is named 'DOMType', we generate a call to the typeNameOf
196 // function attached on the isolate.
197 // The DOM classes assume that their 'typeName' property, which is
198 // not a JS property on the DOM types, returns the type name.
199 if (element.name == const SourceString('typeName')
200 && element.isGetter()
201 && nativeEmitter.toNativeName(element.enclosingElement) == 'DOMType') {
202 DartString jsCode = new DartString.literal(
203 '${nativeEmitter.typeNameOfName}(#)');
204 List<HInstruction> inputs =
205 <HInstruction>[builder.localsHandler.readThis()];
206 builder.push(new HForeign(
207 jsCode, const LiteralDartString('String'), inputs));
208 return;
209 }
210
211 HInstruction convertDartClosure(Element parameter) {
212 HInstruction local = builder.localsHandler.readLocal(parameter);
213 // TODO(ngeoffray): by better analyzing the function type and
214 // its formal parameters, we could just pass, eg closure.$call$0.
215 builder.push(new HStatic(builder.interceptors.getClosureConverter()));
216 List<HInstruction> callInputs = <HInstruction>[builder.pop(), local];
217 HInstruction closure = new HInvokeStatic(Selector.INVOCATION_1, callInputs);
218 builder.add(closure);
219 return closure;
220 }
221
222 FunctionParameters parameters = element.computeParameters(builder.compiler);
223 if (node.arguments.isEmpty()) {
224 List<String> arguments = <String>[];
225 List<HInstruction> inputs = <HInstruction>[];
226 String receiver = '';
227 if (element.isInstanceMember()) {
228 receiver = '#.';
229 inputs.add(builder.localsHandler.readThis());
230 }
231 parameters.forEachParameter((Element parameter) {
232 Type type = parameter.computeType(compiler);
233 HInstruction input = builder.localsHandler.readLocal(parameter);
234 if (type is FunctionType) input = convertDartClosure(parameter);
235 inputs.add(input);
236 arguments.add('#');
237 });
238 String foreignParameters = Strings.join(arguments, ',');
239
240 String dartMethodName;
241 String nativeMethodName = element.name.slowToString();
242 String nativeMethodCall;
243
244 if (element.kind == ElementKind.FUNCTION) {
245 dartMethodName = builder.compiler.namer.instanceMethodName(
246 element.getLibrary(), element.name, parameters.parameterCount);
247 nativeMethodCall = '$receiver$nativeMethodName($foreignParameters)';
248 } else if (element.kind == ElementKind.GETTER) {
249 dartMethodName = builder.compiler.namer.getterName(
250 element.getLibrary(), element.name);
251 nativeMethodCall = '$receiver$nativeMethodName';
252 } else if (element.kind == ElementKind.SETTER) {
253 dartMethodName = builder.compiler.namer.setterName(
254 element.getLibrary(), element.name);
255 nativeMethodCall = '$receiver$nativeMethodName = $foreignParameters';
256 } else {
257 builder.compiler.internalError('unexpected kind: "${element.kind}"',
258 element: element);
259 }
260
261 HInstruction thenInstruction;
262 void visitThen() {
263 DartString jsCode = new DartString.literal(nativeMethodCall);
264 thenInstruction =
265 new HForeign(jsCode, const LiteralDartString('Object'), inputs);
266 builder.add(thenInstruction);
267 }
268
269 bool isNativeLiteral = false;
270 bool isOverridden = false;
271 NativeEmitter nativeEmitter = builder.compiler.emitter.nativeEmitter;
272 if (element.enclosingElement.kind == ElementKind.CLASS) {
273 ClassElement classElement = element.enclosingElement;
274 String nativeName = classElement.nativeName.slowToString();
275 isNativeLiteral = nativeEmitter.isNativeLiteral(nativeName);
276 isOverridden = isOverriddenMethod(element, classElement, nativeEmitter);
277 }
278 if (!element.isInstanceMember() || isNativeLiteral || !isOverridden) {
279 // We generate a direct call to the native method.
280 visitThen();
281 builder.stack.add(thenInstruction);
282 } else {
283 // Record that this method is overridden. In case of optional
284 // arguments, the emitter will generate stubs to handle them,
285 // and needs to know if the method is overridden.
286 nativeEmitter.overriddenMethods.add(element);
287
288 // If the method is an instance method that is overridden, we
289 // generate the following code:
290 // function(params) {
291 // return Object.getPrototypeOf(this).hasOwnProperty(dartMethodName))
292 // ? this.methodName(params)
293 // : Object.prototype.methodName.call(this, params);
294 // }
295 //
296 // The property check at the beginning is to make sure we won't
297 // call the method from the super class, in case the prototype of
298 // 'this' does not have the method yet.
299 HInstruction elseInstruction;
300 void visitElse() {
301 String params = arguments.isEmpty() ? '' : ', $foreignParameters';
302 DartString jsCode = new DartString.literal(
303 'Object.prototype.$dartMethodName.call(#$params)');
304 elseInstruction =
305 new HForeign(jsCode, const LiteralDartString('Object'), inputs);
306 builder.add(elseInstruction);
307 }
308
309 HConstant constant = builder.graph.addConstantString(
310 new DartString.literal('$dartMethodName'));
311 DartString jsCode = new DartString.literal(
312 'Object.getPrototypeOf(#).hasOwnProperty(#)');
313 builder.push(new HForeign(
314 jsCode, const LiteralDartString('Object'),
315 <HInstruction>[builder.localsHandler.readThis(), constant]));
316
317 builder.handleIf(visitThen, visitElse);
318
319 HPhi phi = new HPhi.manyInputs(
320 null, <HInstruction>[thenInstruction, elseInstruction]);
321 builder.current.addPhi(phi);
322 builder.stack.add(phi);
323 }
324
325 } else if (!node.arguments.tail.isEmpty()) {
326 builder.compiler.cancel('More than one argument to native');
327 } else {
328 // This is JS code written in a Dart file with the construct
329 // native """ ... """;. It does not work well with mangling,
330 // but there should currently be no clash between leg mangling
331 // and the library where this construct is being used. This
332 // mangling problem will go away once we switch these libraries
333 // to use Leg's 'JS' function.
334 parameters.forEachParameter((Element parameter) {
335 Type type = parameter.computeType(compiler);
336 if (type is FunctionType) {
337 HInstruction jsClosure = convertDartClosure(parameter);
338 // Because the JS code references the argument name directly,
339 // we must keep the name and assign the JS closure to it.
340 builder.add(new HForeign(
341 new DartString.literal('${parameter.name.slowToString()} = #'),
342 const LiteralDartString('void'),
343 <HInstruction>[jsClosure]));
344 }
345 });
346 LiteralString jsCode = node.arguments.head;
347 builder.push(new HForeign(jsCode.dartString,
348 const LiteralDartString('Object'),
349 <HInstruction>[]));
350 }
351 }
OLDNEW
« no previous file with comments | « frog/leg/native_emitter.dart ('k') | frog/leg/operations.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698