OLD | NEW |
---|---|
1 /* | 1 /* |
2 * Copyright (C) 2013 Google Inc. All rights reserved. | 2 * Copyright (C) 2013 Google Inc. All rights reserved. |
3 * | 3 * |
4 * Redistribution and use in source and binary forms, with or without | 4 * Redistribution and use in source and binary forms, with or without |
5 * modification, are permitted provided that the following conditions are | 5 * modification, are permitted provided that the following conditions are |
6 * met: | 6 * met: |
7 * | 7 * |
8 * * Redistributions of source code must retain the above copyright | 8 * * Redistributions of source code must retain the above copyright |
9 * notice, this list of conditions and the following disclaimer. | 9 * notice, this list of conditions and the following disclaimer. |
10 * * Redistributions in binary form must reproduce the above | 10 * * Redistributions in binary form must reproduce the above |
(...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
45 | 45 |
46 #include <dart_api.h> | 46 #include <dart_api.h> |
47 #include <limits> | 47 #include <limits> |
48 | 48 |
49 namespace blink { | 49 namespace blink { |
50 | 50 |
51 const int JsObject::dartClassId = _JsObjectClassId; | 51 const int JsObject::dartClassId = _JsObjectClassId; |
52 const int JsFunction::dartClassId = _JsFunctionClassId; | 52 const int JsFunction::dartClassId = _JsFunctionClassId; |
53 const int JsArray::dartClassId = _JsArrayClassId; | 53 const int JsArray::dartClassId = _JsArrayClassId; |
54 | 54 |
55 // TODO(jacobr): v8::String::NewFromUtf8 usages should be cached for constant | |
56 // strings to improve performance. | |
57 /** | |
58 * Polyfill script to make Dart List objects look like JavaScript arrays when | |
59 * passed to JavaScript via JavaScript interop. Handling the edge cases | |
60 * requires patching the JavaScript Array prototype for a couple methods | |
61 * and implementing all of the JavaScript Array methods on the JavaScript | |
62 * proxy object for the Dart List. Some of the JavaScript methods are | |
63 * implemented in Dart and C++ code, some are implemented in this JavaScript | |
64 * polyfill script. | |
65 */ | |
66 const char* dartArrayPolyfill = | |
67 "(function() {" | |
68 // If filter has already been defined on the DartList prototype, | |
69 // another DOM isolate has beaten us to polyfilling. | |
70 " if ($DartList.prototype.hasOwnProperty('filter')) return;" | |
71 " var isArray = Array.isArray;" | |
72 " var concat = Array.prototype.concat;" | |
73 " function makeSafeArgs(args) {" | |
74 " var len = args.length;" | |
75 " for (var i = 0; i < len; ++i) {" | |
76 " var arg = args[i];" | |
77 " if (arg instanceof $DartList) {" | |
78 " args[i] = arg.$toJsArray();" | |
79 " }" | |
80 " }" | |
81 " };" | |
82 | |
83 // If performance becomes an issue, we could implement these | |
84 // methods in Dart instead of creating a shallow copy JavaScript | |
85 // array containing the elements of the Dart List and calling | |
86 // the JavaScript method. | |
87 | |
88 // Handle methods that take a callback with value, index, and thisArg | |
89 // parameters. The trick is we need to make thisArg reference the | |
90 // underlying Dart List rather than the JavaScript copy we create for | |
91 // implementation convenience. | |
92 " ['filter', 'forEach', 'some', 'every', 'map'].forEach(function(name) {" | |
93 " Object.defineProperty($DartList.prototype, name, {enumerable: false, va lue: function(callback, thisArg) {" | |
94 " var dartList = this;" | |
95 " return this.$toJsArray()[name](function(value, index) {" | |
96 " return callback.call(thisArg, value, index, dartList);" | |
97 " });" | |
98 " }});" | |
99 " });" | |
100 | |
101 " ['slice', 'indexOf', 'lastIndexOf'].forEach(function(name) {" | |
102 " Object.defineProperty($DartList.prototype, name, {enumerable: false, va lue: function() {" | |
103 " var jsArray = this.$toJsArray();" | |
104 " return jsArray[name].apply(jsArray, arguments);" | |
105 " }});" | |
106 " });" | |
107 | |
108 " ['reduce', 'reduceRight'].forEach(function(name) {" | |
109 " Object.defineProperty($DartList.prototype, name, {enumerable: false, va lue: function(callback, thisArg) {" | |
110 " var dartList = this;" | |
111 " return this.$toJsArray()[name](function(previousValue, currentValue, index) {" | |
112 " return callback.call(thisArg, previousValue, currentValue, index, dartList);" | |
113 " });" | |
114 " }});" | |
115 " });" | |
116 | |
117 // Arguments to concat that are Arrays are treated differently. | |
118 // Warning: this will slow down general JavaScript array concat performance in Dartium. | |
119 " Array.prototype.concat = function() {" | |
120 " makeSafeArgs(arguments);" | |
121 " return concat.apply(this, arguments);" | |
122 " };" | |
123 | |
124 // Need to make sure that Array.isArray returns true for Dart lists. | |
125 " Array.isArray = function(arr) {" | |
126 " return isArray(arr) || (arr instanceof $DartList);" | |
127 " };" | |
128 "})();"; | |
129 | |
55 static v8::Local<v8::FunctionTemplate> dartFunctionTemplate(); | 130 static v8::Local<v8::FunctionTemplate> dartFunctionTemplate(); |
56 static v8::Local<v8::FunctionTemplate> dartObjectTemplate(); | 131 static v8::Local<v8::FunctionTemplate> dartObjectTemplate(); |
132 static v8::Local<v8::FunctionTemplate> dartListTemplate(); | |
133 | |
134 // TODO(jacobr): we should really be using this method everywhere instead of | |
135 // sticking interop methods in the dart:html _Utils class. | |
136 static Dart_Handle invokeTopLevelJsInteropMethod(DartDOMData* domData, const cha r* methodName, int argCount, Dart_Handle* args) | |
137 { | |
138 Dart_PersistentHandle library = domData->jsLibrary(); | |
139 ASSERT(!Dart_IsError(library)); | |
140 return Dart_Invoke(library, Dart_NewStringFromCString(methodName), argCount, args); | |
141 } | |
57 | 142 |
58 template<typename CallbackInfo> | 143 template<typename CallbackInfo> |
59 void setJsReturnValue(DartDOMData* domData, CallbackInfo info, Dart_Handle resul t) | 144 void setJsReturnValue(DartDOMData* domData, CallbackInfo info, Dart_Handle resul t) |
60 { | 145 { |
61 if (Dart_IsError(result)) { | 146 if (Dart_IsError(result)) { |
62 v8::Isolate* v8Isolate = v8::Isolate::GetCurrent(); | 147 v8::Isolate* v8Isolate = v8::Isolate::GetCurrent(); |
63 V8ThrowException::throwException(v8::String::NewFromUtf8(v8Isolate, Dart _GetError(result)), v8Isolate); | 148 V8ThrowException::throwException(v8::String::NewFromUtf8(v8Isolate, Dart _GetError(result)), v8Isolate); |
64 } else { | 149 } else { |
65 Dart_Handle exception = 0; | 150 Dart_Handle exception = 0; |
66 v8::Local<v8::Value> ret = JsInterop::fromDart(domData, result, exceptio n); | 151 v8::Local<v8::Value> ret = JsInterop::fromDart(domData, result, exceptio n); |
(...skipping 156 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
223 if (DartUtilities::isFunction(domData, handle)) { | 308 if (DartUtilities::isFunction(domData, handle)) { |
224 v8::Local<v8::Object> functionProxy = dartFunctionTemplate()->InstanceTe mplate()->NewInstance(); | 309 v8::Local<v8::Object> functionProxy = dartFunctionTemplate()->InstanceTe mplate()->NewInstance(); |
225 DartHandleProxy::writePointerToProxy(functionProxy, handle); | 310 DartHandleProxy::writePointerToProxy(functionProxy, handle); |
226 // The raw functionProxy doesn't behave enough like a true JS function | 311 // The raw functionProxy doesn't behave enough like a true JS function |
227 // so we wrap it in a true JS function. | 312 // so we wrap it in a true JS function. |
228 return domData->jsInteropData()->wrapDartFunction()->Call(functionProxy, 0, 0); | 313 return domData->jsInteropData()->wrapDartFunction()->Call(functionProxy, 0, 0); |
229 } | 314 } |
230 | 315 |
231 v8::Local<v8::Object> proxy; | 316 v8::Local<v8::Object> proxy; |
232 ASSERT(Dart_IsInstance(handle)); | 317 ASSERT(Dart_IsInstance(handle)); |
233 proxy = dartObjectTemplate()->InstanceTemplate()->NewInstance(); | 318 // Simulate the behavior of the Dart dev compiler where new List() is |
319 // equivalent to a JavaScript array. We accomplish this by creating a | |
320 // JavaScript object that fakes that it is a JavaScript array but is | |
321 // actually backed by a Dart list. This is not a breaking change as | |
322 // existing Dart-JS interop passed arrays as opaque Dart handles. | |
323 // The jsify method can still be called if you wish to create a copy | |
324 // of a json like Dart data structure. | |
325 if (Dart_IsList(handle)) { | |
326 proxy = dartListTemplate()->InstanceTemplate()->NewInstance(); | |
327 } else { | |
328 proxy = dartObjectTemplate()->InstanceTemplate()->NewInstance(); | |
329 } | |
234 DartHandleProxy::writePointerToProxy(proxy, handle); | 330 DartHandleProxy::writePointerToProxy(proxy, handle); |
235 v8::Isolate* v8Isolate = v8::Isolate::GetCurrent(); | 331 v8::Isolate* v8Isolate = v8::Isolate::GetCurrent(); |
236 proxy->SetHiddenValue(v8::String::NewFromUtf8(v8Isolate, "dartProxy"), v8::B oolean::New(v8Isolate, true)); | 332 proxy->SetHiddenValue(v8::String::NewFromUtf8(v8Isolate, "dartProxy"), v8::B oolean::New(v8Isolate, true)); |
237 | 333 |
238 return proxy; | 334 return proxy; |
239 } | 335 } |
240 | 336 |
241 JsObject::JsObject(v8::Local<v8::Object> v8Handle) | 337 JsObject::JsObject(v8::Local<v8::Object> v8Handle) |
242 { | 338 { |
243 v8::Isolate* isolate = v8::Isolate::GetCurrent(); | 339 v8::Isolate* isolate = v8::Isolate::GetCurrent(); |
(...skipping 49 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
293 | 389 |
294 if (object->IsArray()) { | 390 if (object->IsArray()) { |
295 RefPtr<JsArray> jsArray = JsArray::create(object.As<v8::Array>()); | 391 RefPtr<JsArray> jsArray = JsArray::create(object.As<v8::Array>()); |
296 return JsArray::toDart(jsArray); | 392 return JsArray::toDart(jsArray); |
297 } | 393 } |
298 | 394 |
299 RefPtr<JsObject> jsObject = JsObject::create(object); | 395 RefPtr<JsObject> jsObject = JsObject::create(object); |
300 return JsObject::toDart(jsObject); | 396 return JsObject::toDart(jsObject); |
301 } | 397 } |
302 | 398 |
399 static void maybeCreateJsObjectImplClass(DartDOMData* domData) | |
400 { | |
401 DartJsInteropData* interopData = domData->jsInteropData(); | |
402 // Skip if the JSObjectImpl class has already been defined. | |
403 if (interopData->jsObjectImplDefined()) { | |
404 return; | |
405 } | |
406 // Helper method that generates boilerplate source code for | |
407 // JsObjectImpl, JsFunctionImpl, and JsArrayImpl classes that implement | |
408 // all Dart types that have been passed to the dart:js registerJsInterfaces | |
409 // method. The sole purpose of these classes is to ensure that checked mode | |
410 // allows casting a JsObject to all types implemented by a JsObject. | |
411 Dart_Handle source = invokeTopLevelJsInteropMethod(domData, "_generateJsObje ctImplPart", 0, 0); | |
412 ASSERT(Dart_IsString(source)); | |
413 | |
414 Dart_Handle ALLOW_UNUSED ret = Dart_LibraryLoadPatch(domData->jsLibrary(), D art_NewStringFromCString("JsInteropImpl.dart"), source); | |
415 ASSERT(!Dart_IsError(ret)); | |
416 ret = Dart_FinalizeLoading(false); | |
417 ASSERT(!Dart_IsError(ret)); | |
418 | |
419 interopData->setJsObjectImplDefined(); | |
420 | |
421 // Start of polyfill work to make Dart List proxies behave like JavaScript | |
422 // Arrays by monkey patching JavaScript Array and the List JavaScript | |
423 // proxies as needed. | |
424 v8::Context::Scope scope(DartUtilities::currentV8Context()); | |
425 | |
426 v8::Isolate* v8Isolate = v8::Isolate::GetCurrent(); | |
427 | |
428 v8::Local<v8::Function> dartArrayConstructor = dartListTemplate()->GetFuncti on(); | |
429 | |
430 DartUtilities::currentV8Context()->Global()->Set(v8::String::NewFromUtf8(v8I solate, "$DartList"), | |
431 dartArrayConstructor); | |
432 V8ScriptRunner::compileAndRunInternalScript(v8::String::NewFromUtf8(v8Isolat e, dartArrayPolyfill), v8Isolate); | |
433 } | |
434 | |
303 Dart_Handle JsObject::toDart(PassRefPtr<JsObject> jsObject) | 435 Dart_Handle JsObject::toDart(PassRefPtr<JsObject> jsObject) |
304 { | 436 { |
305 return DartDOMWrapper::createWrapper<JsObject>(DartDOMData::current(), jsObj ect.get(), JsObject::dartClassId); | 437 DartDOMData* domData = DartDOMData::current(); |
438 // We need to ensure JsObjectImpl exists before creating the wrapper. | |
439 maybeCreateJsObjectImplClass(domData); | |
440 return DartDOMWrapper::createWrapper<JsObject>(domData, jsObject.get(), JsOb ject::dartClassId); | |
306 } | 441 } |
307 | 442 |
308 JsObject::~JsObject() | 443 JsObject::~JsObject() |
309 { | 444 { |
310 v8Object.Reset(); | 445 v8Object.Reset(); |
311 } | 446 } |
312 | 447 |
313 Dart_Handle JsFunction::toDart(PassRefPtr<JsFunction> jsFunction) | 448 Dart_Handle JsFunction::toDart(PassRefPtr<JsFunction> jsFunction) |
314 { | 449 { |
315 return DartDOMWrapper::createWrapper<JsFunction>(DartDOMData::current(), jsF unction.get(), JsFunction::dartClassId); | 450 DartDOMData* domData = DartDOMData::current(); |
451 // We need to ensure JsObjectImpl exists before creating the wrapper. | |
452 maybeCreateJsObjectImplClass(domData); | |
453 return DartDOMWrapper::createWrapper<JsFunction>(domData, jsFunction.get(), JsFunction::dartClassId); | |
316 } | 454 } |
317 | 455 |
318 JsFunction::JsFunction(v8::Local<v8::Function> v8Handle) : JsObject(v8Handle) { } | 456 JsFunction::JsFunction(v8::Local<v8::Function> v8Handle) : JsObject(v8Handle) { } |
319 | 457 |
320 PassRefPtr<JsFunction> JsFunction::create(v8::Local<v8::Function> v8Handle) | 458 PassRefPtr<JsFunction> JsFunction::create(v8::Local<v8::Function> v8Handle) |
321 { | 459 { |
322 return adoptRef(new JsFunction(v8Handle)); | 460 return adoptRef(new JsFunction(v8Handle)); |
323 } | 461 } |
324 | 462 |
325 v8::Local<v8::Function> JsFunction::localV8Function() | 463 v8::Local<v8::Function> JsFunction::localV8Function() |
326 { | 464 { |
327 return localV8Object().As<v8::Function>(); | 465 return localV8Object().As<v8::Function>(); |
328 } | 466 } |
329 | 467 |
330 Dart_Handle JsArray::toDart(PassRefPtr<JsArray> jsArray) | 468 Dart_Handle JsArray::toDart(PassRefPtr<JsArray> jsArray) |
331 { | 469 { |
332 return DartDOMWrapper::createWrapper<JsArray>(DartDOMData::current(), jsArra y.get(), JsArray::dartClassId); | 470 DartDOMData* domData = DartDOMData::current(); |
471 // We need to ensure JsArrayImpl exists before creating the wrapper. | |
472 maybeCreateJsObjectImplClass(domData); | |
473 return DartDOMWrapper::createWrapper<JsArray>(domData, jsArray.get(), JsArra y::dartClassId); | |
333 } | 474 } |
334 | 475 |
335 JsArray::JsArray(v8::Local<v8::Array> v8Handle) : JsObject(v8Handle) { } | 476 JsArray::JsArray(v8::Local<v8::Array> v8Handle) : JsObject(v8Handle) { } |
336 | 477 |
337 PassRefPtr<JsArray> JsArray::create(v8::Local<v8::Array> v8Handle) | 478 PassRefPtr<JsArray> JsArray::create(v8::Local<v8::Array> v8Handle) |
338 { | 479 { |
339 return adoptRef(new JsArray(v8Handle)); | 480 return adoptRef(new JsArray(v8Handle)); |
340 } | 481 } |
341 | 482 |
342 v8::Local<v8::Array> JsArray::localV8Array() | 483 v8::Local<v8::Array> JsArray::localV8Array() |
(...skipping 522 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
865 ASSERT_NOT_REACHED(); | 1006 ASSERT_NOT_REACHED(); |
866 } | 1007 } |
867 | 1008 |
868 static void contextCallback(Dart_NativeArguments args) | 1009 static void contextCallback(Dart_NativeArguments args) |
869 { | 1010 { |
870 v8::Local<v8::Context> v8Context = DartUtilities::currentV8Context(); | 1011 v8::Local<v8::Context> v8Context = DartUtilities::currentV8Context(); |
871 v8::Context::Scope scope(v8Context); | 1012 v8::Context::Scope scope(v8Context); |
872 Dart_SetReturnValue(args, JsObject::toDart(v8Context->Global())); | 1013 Dart_SetReturnValue(args, JsObject::toDart(v8Context->Global())); |
873 } | 1014 } |
874 | 1015 |
1016 static void finalizeJsInterfacesCallback(Dart_NativeArguments args) | |
1017 { | |
1018 DartDOMData* domData = static_cast<DartDOMData*>(Dart_GetNativeIsolateData(a rgs)); | |
1019 maybeCreateJsObjectImplClass(domData); | |
1020 } | |
1021 | |
1022 static void interfacesFinalizedCallback(Dart_NativeArguments args) | |
1023 { | |
1024 DartDOMData* domData = static_cast<DartDOMData*>(Dart_GetNativeIsolateData(a rgs)); | |
1025 DartJsInteropData* interopData = domData->jsInteropData(); | |
1026 // Skip if the JSObjectImpl class has already been defined. | |
1027 Dart_SetBooleanReturnValue(args, interopData->jsObjectImplDefined()); | |
1028 } | |
1029 | |
875 v8::Handle<v8::Value> mapToV8(DartDOMData* domData, Dart_Handle value, DartHandl eToV8Map& map, Dart_Handle& exception) | 1030 v8::Handle<v8::Value> mapToV8(DartDOMData* domData, Dart_Handle value, DartHandl eToV8Map& map, Dart_Handle& exception) |
876 { | 1031 { |
877 Dart_Handle asList = DartUtilities::invokeUtilsMethod("convertMapToList", 1, &value); | 1032 Dart_Handle asList = DartUtilities::invokeUtilsMethod("convertMapToList", 1, &value); |
878 if (!DartUtilities::checkResult(asList, exception)) | 1033 if (!DartUtilities::checkResult(asList, exception)) |
879 return v8::Handle<v8::Value>(); | 1034 return v8::Handle<v8::Value>(); |
880 ASSERT(Dart_IsList(asList)); | 1035 ASSERT(Dart_IsList(asList)); |
881 | 1036 |
882 // Now we have a list [key, value, key, value, ....], create a v8 object and set necesary | 1037 // Now we have a list [key, value, key, value, ....], create a v8 object and set necesary |
883 // properties on it. | 1038 // properties on it. |
884 v8::Handle<v8::Object> object = v8::Object::New(v8::Isolate::GetCurrent()); | 1039 v8::Handle<v8::Object> object = v8::Object::New(v8::Isolate::GetCurrent()); |
(...skipping 118 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1003 fail: | 1158 fail: |
1004 Dart_ThrowException(exception); | 1159 Dart_ThrowException(exception); |
1005 ASSERT_NOT_REACHED(); | 1160 ASSERT_NOT_REACHED(); |
1006 } | 1161 } |
1007 | 1162 |
1008 } | 1163 } |
1009 | 1164 |
1010 static DartNativeEntry nativeEntries[] = { | 1165 static DartNativeEntry nativeEntries[] = { |
1011 { JsInteropInternal::jsObjectConstructorCallback, 2, "JsObject_constructorCa llback" }, | 1166 { JsInteropInternal::jsObjectConstructorCallback, 2, "JsObject_constructorCa llback" }, |
1012 { JsInteropInternal::contextCallback, 0, "Js_context_Callback" }, | 1167 { JsInteropInternal::contextCallback, 0, "Js_context_Callback" }, |
1168 { JsInteropInternal::finalizeJsInterfacesCallback, 0, "Js_finalizeJsInterfac es" }, | |
1169 { JsInteropInternal::interfacesFinalizedCallback, 0, "Js_interfacesFinalized _Callback" }, | |
1013 { JsInteropInternal::jsifyCallback, 1, "JsObject_jsify" }, | 1170 { JsInteropInternal::jsifyCallback, 1, "JsObject_jsify" }, |
1014 { JsInteropInternal::withThisCallback, 1, "JsFunction_withThis" }, | 1171 { JsInteropInternal::withThisCallback, 1, "JsFunction_withThis" }, |
1015 { JsInteropInternal::getterCallback, 2, "JsObject_[]" }, | 1172 { JsInteropInternal::getterCallback, 2, "JsObject_[]" }, |
1016 { JsInteropInternal::setterCallback, 3, "JsObject_[]=" }, | 1173 { JsInteropInternal::setterCallback, 3, "JsObject_[]=" }, |
1017 { JsInteropInternal::hashCodeCallback, 1, "JsObject_hashCode" }, | 1174 { JsInteropInternal::hashCodeCallback, 1, "JsObject_hashCode" }, |
1018 { JsInteropInternal::callMethodCallback, 3, "JsObject_callMethod" }, | 1175 { JsInteropInternal::callMethodCallback, 3, "JsObject_callMethod" }, |
1019 { JsInteropInternal::toStringCallback, 1, "JsObject_toString" }, | 1176 { JsInteropInternal::toStringCallback, 1, "JsObject_toString" }, |
1020 { JsInteropInternal::identityEqualityCallback, 2, "JsObject_identityEquality " }, | 1177 { JsInteropInternal::identityEqualityCallback, 2, "JsObject_identityEquality " }, |
1021 { JsInteropInternal::hasPropertyCallback, 2, "JsObject_hasProperty" }, | 1178 { JsInteropInternal::hasPropertyCallback, 2, "JsObject_hasProperty" }, |
1022 { JsInteropInternal::deletePropertyCallback, 2, "JsObject_deleteProperty" }, | 1179 { JsInteropInternal::deletePropertyCallback, 2, "JsObject_deleteProperty" }, |
(...skipping 25 matching lines...) Expand all Loading... | |
1048 const uint8_t* JsInterop::symbolizer(Dart_NativeFunction nf) | 1205 const uint8_t* JsInterop::symbolizer(Dart_NativeFunction nf) |
1049 { | 1206 { |
1050 for (intptr_t i = 0; nativeEntries[i].nativeFunction != 0; i++) { | 1207 for (intptr_t i = 0; nativeEntries[i].nativeFunction != 0; i++) { |
1051 if (nf == nativeEntries[i].nativeFunction) { | 1208 if (nf == nativeEntries[i].nativeFunction) { |
1052 return reinterpret_cast<const uint8_t*>(nativeEntries[i].name); | 1209 return reinterpret_cast<const uint8_t*>(nativeEntries[i].name); |
1053 } | 1210 } |
1054 } | 1211 } |
1055 return 0; | 1212 return 0; |
1056 } | 1213 } |
1057 | 1214 |
1058 } | 1215 // Methods enabling a Dart List to emulate a JS Array. |
1216 static void indexedGetterArray(uint32_t index, const v8::PropertyCallbackInfo<v8 ::Value>& info) | |
1217 { | |
1218 DartScopes scopes(info.Holder()); | |
1219 Dart_Handle handle = scopes.handle; | |
1220 DartDOMData* domData = DartDOMData::current(); | |
1221 Dart_Handle ret = 0; | |
1222 ASSERT(Dart_IsList(handle)); | |
1223 ret = Dart_ListGetAt(handle, index); | |
1224 if (Dart_IsError(ret)) { | |
1225 // Return undefined if the index is invalid to match JS semantics. | |
1226 // TODO(jacobr): we should log to the console warning of bad use of a | |
1227 // Dart List. | |
1228 return; | |
1229 } | |
1230 setJsReturnValue(domData, info, ret); | |
1231 } | |
1232 | |
1233 static void indexedSetterArray(uint32_t index, v8::Local<v8::Value> value, const v8::PropertyCallbackInfo<v8::Value>& info) | |
1234 { | |
1235 DartScopes scopes(info.Holder()); | |
1236 Dart_Handle handle = scopes.handle; | |
1237 DartDOMData* domData = DartDOMData::current(); | |
1238 | |
1239 Dart_Handle ret = 0; | |
1240 ASSERT(Dart_IsList(handle)); | |
1241 intptr_t length = 0; | |
1242 ret = Dart_ListLength(handle, &length); | |
1243 ASSERT(!Dart_IsError(ret)); | |
1244 if (index >= length) { | |
1245 // Approximate JS semantics by filling the list with nulls if we | |
1246 // attempt to add an element past the end of the list. | |
1247 // TODO(jacobr): ideally we would match JS semantics exactly and fill | |
1248 // the list with undefined. | |
1249 Dart_Handle args[2] = { handle, DartUtilities::unsignedToDart(index + 1) }; | |
1250 ret = Dart_Invoke(domData->jsLibrary(), Dart_NewStringFromCString("_arra yExtend"), 2, args); | |
1251 ASSERT(!Dart_IsError(ret)); | |
1252 } | |
1253 ret = Dart_ListSetAt(handle, index, DartHandleProxy::unwrapValue(value)); | |
1254 if (Dart_IsError(ret)) { | |
1255 // Return undefined if the index is invalid to match JS semantics. | |
1256 // TODO(jacobr): we should log to the console warning of bad use of a | |
1257 // Dart list or add a JS expando to the object ala JS. | |
1258 } | |
1259 setJsReturnValue(domData, info, ret); | |
1260 } | |
1261 | |
1262 static void indexedEnumeratorArray(const v8::PropertyCallbackInfo<v8::Array>& in fo) | |
1263 { | |
1264 DartScopes scopes(info.Holder()); | |
1265 Dart_Handle handle = scopes.handle; | |
1266 | |
1267 intptr_t length = 0; | |
1268 ASSERT(Dart_IsList(handle)); | |
1269 Dart_ListLength(handle, &length); | |
1270 | |
1271 v8::Isolate* v8Isolate = v8::Isolate::GetCurrent(); | |
1272 v8::Local<v8::Array> indexes = v8::Array::New(v8Isolate, length); | |
1273 for (int i = 0; i < length; i++) | |
1274 indexes->Set(i, v8::Integer::New(v8Isolate, i)); | |
1275 | |
1276 v8SetReturnValue(info, indexes); | |
1277 } | |
1278 | |
1279 v8::Handle<v8::Array> shallowListToV8(Dart_Handle value, DartDOMData* domData, D art_Handle& exception) | |
1280 { | |
1281 ASSERT(Dart_IsList(value)); | |
1282 | |
1283 intptr_t length = 0; | |
1284 Dart_Handle result = Dart_ListLength(value, &length); | |
1285 if (!DartUtilities::checkResult(result, exception)) | |
1286 return v8::Handle<v8::Array>(); | |
1287 | |
1288 v8::Local<v8::Array> array = v8::Array::New(v8::Isolate::GetCurrent(), lengt h); | |
1289 | |
1290 for (intptr_t i = 0; i < length; ++i) { | |
1291 result = Dart_ListGetAt(value, i); | |
1292 v8::Handle<v8::Value> v8value = JsInterop::fromDart(domData, result, exc eption); | |
1293 // TODO(jacobr): is there a better way to handle this error case? | |
1294 if (exception) | |
1295 return v8::Handle<v8::Array>(); | |
1296 array->Set(i, v8value); | |
1297 } | |
1298 return array; | |
1299 } | |
1300 | |
1301 void arrayHelper(const v8::FunctionCallbackInfo<v8::Value>& info, const char* me thodName) | |
1302 { | |
1303 // We need to bail out if "this" is not a Dart proxy or we will crash the VM . | |
1304 // TODO(jacob): we could generate a user readable error message instead of | |
1305 // just returning. | |
1306 if (!DartHandleProxy::isDartProxy(info.Holder())) { | |
1307 return; | |
1308 } | |
1309 DartScopes scopes(info.Holder()); | |
1310 DartDOMData* domData = DartDOMData::current(); | |
1311 Dart_Handle handle = scopes.handle; | |
1312 Dart_Handle ret = Dart_Invoke(domData->jsLibrary(), Dart_NewStringFromCStrin g(methodName), 1, &handle); | |
1313 setJsReturnValue(domData, info, ret); | |
1314 } | |
1315 | |
1316 void arrayHelper1Arg(const v8::FunctionCallbackInfo<v8::Value>& info, const char * methodName) | |
1317 { | |
1318 // We need to bail out if "this" is not a Dart proxy or we will crash the VM . | |
1319 // TODO(jacob): we could generate a user readable error message instead of | |
1320 // just returning. | |
vsm
2015/06/26 14:46:33
Throw an exception? Should be able to steal the b
Jacob
2015/06/26 20:53:30
I shouldn't throw an exception here. Added some so
| |
1321 if (!DartHandleProxy::isDartProxy(info.Holder())) { | |
1322 return; | |
1323 } | |
1324 DartScopes scopes(info.Holder()); | |
1325 DartDOMData* domData = DartDOMData::current(); | |
1326 Dart_Handle handle = scopes.handle; | |
1327 Dart_Handle e; | |
1328 if (info.Length() == 0) { | |
1329 e = Dart_Null(); | |
1330 } else { | |
1331 e = JsInterop::toDart(info[0]); | |
1332 } | |
1333 Dart_Handle args[2] = { handle, e }; | |
1334 Dart_Handle ret = Dart_Invoke(domData->jsLibrary(), Dart_NewStringFromCStrin g(methodName), 2, args); | |
1335 setJsReturnValue(domData, info, ret); | |
1336 } | |
1337 | |
1338 static void arrayNamedPropertyGetter(v8::Local<v8::String> name, const v8::Prope rtyCallbackInfo<v8::Value>& info) | |
1339 { | |
1340 if (!DartHandleProxy::isDartProxy(info.Holder())) { | |
1341 return; | |
1342 } | |
1343 | |
1344 v8::Isolate* v8Isolate = v8::Isolate::GetCurrent(); | |
1345 if (!name->Equals(v8::String::NewFromUtf8(v8Isolate, "length"))) | |
1346 return; | |
1347 | |
1348 DartScopes scopes(info.Holder()); | |
1349 Dart_Handle handle = scopes.handle; | |
1350 | |
1351 intptr_t length = 0; | |
1352 Dart_ListLength(handle, &length); | |
1353 v8SetReturnValueInt(info, length); | |
1354 } | |
1355 | |
1356 static void arrayNamedPropertySetter(v8::Local<v8::String> property, v8::Local<v 8::Value> value, const v8::PropertyCallbackInfo<v8::Value>& info) | |
1357 { | |
1358 if (!DartHandleProxy::isDartProxy(info.Holder())) { | |
1359 return; | |
1360 } | |
1361 | |
1362 v8::Isolate* v8Isolate = v8::Isolate::GetCurrent(); | |
1363 if (!property->Equals(v8::String::NewFromUtf8(v8Isolate, "length"))) { | |
1364 return; | |
1365 } | |
1366 | |
1367 DartScopes scopes(info.Holder()); | |
1368 Dart_Handle handle = scopes.handle; | |
1369 DartDOMData* domData = DartDOMData::current(); | |
1370 Dart_Handle args[2] = { handle, JsInterop::toDart(value) }; | |
1371 Dart_Handle ret = Dart_Invoke(domData->jsLibrary(), Dart_NewStringFromCStrin g("_setListLength"), 2, args); | |
1372 setJsReturnValue(domData, info, ret); | |
1373 } | |
1374 | |
1375 static void arrayQueryProperty(v8::Local<v8::String> name, const v8::PropertyCal lbackInfo<v8::Integer>& info) | |
1376 { | |
1377 v8::Isolate* v8Isolate = v8::Isolate::GetCurrent(); | |
1378 if (name->Equals(v8::String::NewFromUtf8(v8Isolate, "length"))) { | |
1379 v8SetReturnValueInt(info, v8::DontEnum | v8::DontDelete); | |
1380 } | |
1381 } | |
1382 | |
1383 static void arrayDeleteProperty(v8::Local<v8::String> name, const v8::PropertyCa llbackInfo<v8::Boolean>& info) | |
1384 { | |
1385 v8::Isolate* v8Isolate = v8::Isolate::GetCurrent(); | |
1386 if (name->Equals(v8::String::NewFromUtf8(v8Isolate, "length"))) { | |
1387 v8SetReturnValueBool(info, false); | |
1388 } | |
1389 } | |
1390 | |
1391 void arrayToStringCallback(const v8::FunctionCallbackInfo<v8::Value>& info) | |
1392 { | |
1393 arrayHelper(info, "_arrayToString"); | |
1394 } | |
1395 | |
1396 | |
1397 void arrayJoinCallback(const v8::FunctionCallbackInfo<v8::Value>& info) | |
1398 { | |
1399 arrayHelper1Arg(info, "_arrayJoin"); | |
1400 } | |
1401 | |
1402 void arrayPushCallback(const v8::FunctionCallbackInfo<v8::Value>& info) | |
1403 { | |
1404 arrayHelper1Arg(info, "_arrayPush"); | |
1405 } | |
1406 | |
1407 void arrayPopCallback(const v8::FunctionCallbackInfo<v8::Value>& info) | |
1408 { | |
1409 arrayHelper(info, "_arrayPop"); | |
1410 } | |
1411 | |
1412 void arrayHelperWithArgsAsList(const v8::FunctionCallbackInfo<v8::Value>& info, const char* methodName) | |
1413 { | |
1414 // We need to bail out if "this" is not a Dart proxy or we will crash the VM . | |
1415 // TODO(jacob): we could generate a user readable error message instead of | |
1416 // just returning. | |
1417 if (!DartHandleProxy::isDartProxy(info.Holder())) { | |
1418 return; | |
1419 } | |
1420 DartScopes scopes(info.Holder()); | |
1421 DartDOMData* domData = DartDOMData::current(); | |
1422 Dart_Handle handle = scopes.handle; | |
1423 int length = info.Length(); | |
1424 Dart_Handle argsList = Dart_NewList(length); | |
1425 for (int i = 0; i < length; ++i) { | |
1426 Dart_ListSetAt(argsList, i, JsInterop::toDart(info[i])); | |
1427 } | |
1428 // Note: this is also just info.Holder(). | |
1429 Dart_Handle args[2] = { handle, argsList }; | |
1430 setJsReturnValue(domData, info, Dart_Invoke(domData->jsLibrary(), Dart_NewSt ringFromCString(methodName), 2, args)); | |
1431 } | |
1432 | |
1433 void concatCallback(const v8::FunctionCallbackInfo<v8::Value>& info) | |
1434 { | |
1435 arrayHelperWithArgsAsList(info, "_arrayConcat"); | |
1436 } | |
1437 | |
1438 void arrayReverseCallback(const v8::FunctionCallbackInfo<v8::Value>& info) | |
1439 { | |
1440 arrayHelper(info, "_arrayReverse"); | |
1441 } | |
1442 | |
1443 void arrayShiftCallback(const v8::FunctionCallbackInfo<v8::Value>& info) | |
1444 { | |
1445 arrayHelper(info, "_arrayShift"); | |
1446 } | |
1447 | |
1448 void arrayUnshiftCallback(const v8::FunctionCallbackInfo<v8::Value>& info) | |
1449 { | |
1450 arrayHelperWithArgsAsList(info, "_arrayUnshift"); | |
1451 } | |
1452 | |
1453 void arraySpliceCallback(const v8::FunctionCallbackInfo<v8::Value>& info) | |
1454 { | |
1455 arrayHelperWithArgsAsList(info, "_arraySplice"); | |
1456 } | |
1457 | |
1458 void toJsArrayCallback(const v8::FunctionCallbackInfo<v8::Value>& info) | |
1459 { | |
1460 if (!DartHandleProxy::isDartProxy(info.Holder())) { | |
1461 return; | |
1462 } | |
1463 DartScopes scopes(info.Holder()); | |
1464 DartDOMData* domData = DartDOMData::current(); | |
1465 Dart_Handle handle = scopes.handle; | |
1466 Dart_Handle exception = 0; | |
1467 v8::Local<v8::Array> v8Array = shallowListToV8(handle, domData, exception); | |
1468 ASSERT(!exception); | |
1469 v8SetReturnValue(info, v8Array); | |
1470 } | |
1471 | |
1472 void arraySortCallback(const v8::FunctionCallbackInfo<v8::Value>& info) | |
1473 { | |
1474 // TODO(jacobr): consider using the JavaScript sort method instead. | |
1475 arrayHelper1Arg(info, "_arraySort"); | |
1476 } | |
1477 | |
1478 v8::Local<v8::FunctionTemplate> dartListTemplate() | |
1479 { | |
1480 DEFINE_STATIC_LOCAL(v8::Persistent<v8::FunctionTemplate>, proxyTemplate, ()) ; | |
1481 v8::Local<v8::FunctionTemplate> proxyTemplateLocal; | |
1482 v8::Isolate* v8Isolate = v8::Isolate::GetCurrent(); | |
1483 if (proxyTemplate.IsEmpty()) { | |
1484 proxyTemplate.Reset(v8::Isolate::GetCurrent(), v8::FunctionTemplate::New (v8Isolate)); | |
1485 proxyTemplateLocal = v8::Local<v8::FunctionTemplate>::New(v8Isolate, pro xyTemplate); | |
1486 // Set to Array because we want these instances to appear to be | |
1487 // JavaScript arrays as far as user code is concerned. | |
1488 proxyTemplateLocal->SetClassName(v8::String::NewFromUtf8(v8Isolate, "Arr ay")); | |
1489 // Hack to set the prototype to be the prototype of an actual JavaScript Array. | |
1490 | |
1491 v8::Local<v8::ObjectTemplate> protoTemplate = proxyTemplateLocal->Proto typeTemplate(); | |
1492 | |
1493 protoTemplate ->Set(v8::String::NewFromUtf8(v8Isolate, "toString"), v8:: FunctionTemplate::New(v8Isolate, arrayToStringCallback), v8::DontEnum); | |
1494 protoTemplate ->Set(v8::String::NewFromUtf8(v8Isolate, "join"), v8::Func tionTemplate::New(v8Isolate, arrayJoinCallback), v8::DontEnum); | |
1495 protoTemplate ->Set(v8::String::NewFromUtf8(v8Isolate, "push"), v8::Func tionTemplate::New(v8Isolate, arrayPushCallback), v8::DontEnum); | |
1496 protoTemplate ->Set(v8::String::NewFromUtf8(v8Isolate, "pop"), v8::Funct ionTemplate::New(v8Isolate, arrayPopCallback), v8::DontEnum); | |
1497 protoTemplate ->Set(v8::String::NewFromUtf8(v8Isolate, "concat"), v8::Fu nctionTemplate::New(v8Isolate, concatCallback), v8::DontEnum); | |
1498 protoTemplate ->Set(v8::String::NewFromUtf8(v8Isolate, "reverse"), v8::F unctionTemplate::New(v8Isolate, arrayReverseCallback), v8::DontEnum); | |
1499 | |
1500 protoTemplate ->Set(v8::String::NewFromUtf8(v8Isolate, "shift"), v8::Fun ctionTemplate::New(v8Isolate, arrayShiftCallback), v8::DontEnum); | |
1501 protoTemplate ->Set(v8::String::NewFromUtf8(v8Isolate, "unshift"), v8::F unctionTemplate::New(v8Isolate, arrayUnshiftCallback), v8::DontEnum); | |
1502 protoTemplate ->Set(v8::String::NewFromUtf8(v8Isolate, "splice"), v8::Fu nctionTemplate::New(v8Isolate, arraySpliceCallback), v8::DontEnum); | |
1503 protoTemplate ->Set(v8::String::NewFromUtf8(v8Isolate, "sort"), v8::Func tionTemplate::New(v8Isolate, arraySortCallback), v8::DontEnum); | |
1504 protoTemplate ->Set(v8::String::NewFromUtf8(v8Isolate, "$toJsArray"), v8 ::FunctionTemplate::New(v8Isolate, toJsArrayCallback), v8::DontEnum); | |
1505 | |
1506 // ES6 experimental properties not currently supported that we could sup port if needed. | |
1507 // These would require building separate live proxy objects. | |
1508 // "entries", | |
1509 // "values", | |
1510 // "keys" | |
1511 | |
1512 v8::Local<v8::ObjectTemplate> instanceTemplate = setupInstanceTemplate(p roxyTemplateLocal); | |
1513 instanceTemplate->SetIndexedPropertyHandler(&indexedGetterArray, &indexe dSetterArray, 0, 0, &indexedEnumeratorArray); | |
1514 instanceTemplate->SetNamedPropertyHandler(&arrayNamedPropertyGetter, &ar rayNamedPropertySetter, &arrayQueryProperty, &arrayDeleteProperty, 0); | |
1515 } else { | |
1516 proxyTemplateLocal = v8::Local<v8::FunctionTemplate>::New(v8Isolate, pro xyTemplate); | |
1517 } | |
1518 return proxyTemplateLocal; | |
1519 } | |
1520 | |
1521 } | |
OLD | NEW |