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