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