Index: Source/bindings/core/dart/DartJsInterop.cpp |
diff --git a/Source/bindings/core/dart/DartJsInterop.cpp b/Source/bindings/core/dart/DartJsInterop.cpp |
new file mode 100644 |
index 0000000000000000000000000000000000000000..0a2e39f1db8636c2e98069fd23acbed3d7688bd6 |
--- /dev/null |
+++ b/Source/bindings/core/dart/DartJsInterop.cpp |
@@ -0,0 +1,1623 @@ |
+/* |
+ * Copyright (C) 2013 Google Inc. All rights reserved. |
+ * |
+ * Redistribution and use in source and binary forms, with or without |
+ * modification, are permitted provided that the following conditions are |
+ * met: |
+ * |
+ * * Redistributions of source code must retain the above copyright |
+ * notice, this list of conditions and the following disclaimer. |
+ * * Redistributions in binary form must reproduce the above |
+ * copyright notice, this list of conditions and the following disclaimer |
+ * in the documentation and/or other materials provided with the |
+ * distribution. |
+ * * Neither the name of Google Inc. nor the names of its |
+ * contributors may be used to endorse or promote products derived from |
+ * this software without specific prior written permission. |
+ * |
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
+ */ |
+#include "config.h" |
+ |
+#include "bindings/core/dart/DartJsInterop.h" |
+ |
+#include "bindings/core/dart/DartDOMWrapper.h" |
+#include "bindings/core/dart/DartHandleProxy.h" |
+#include "bindings/core/dart/DartJsInteropData.h" |
+#include "bindings/core/dart/DartPersistentValue.h" |
+#include "bindings/core/dart/DartUtilities.h" |
+#include "bindings/core/dart/V8Converter.h" |
+#include "bindings/core/v8/V8Binding.h" |
+#include "bindings/core/v8/V8Element.h" |
+#include "bindings/core/v8/V8RecursionScope.h" |
+#include "bindings/core/v8/V8ScriptRunner.h" |
+#include "core/dom/Element.h" |
+ |
+#include "wtf/StdLibExtras.h" |
+ |
+#include <dart_api.h> |
+#include <limits> |
+ |
+namespace blink { |
+ |
+const int JsObject::dartClassId = _JsObjectClassId; |
+const int JsFunction::dartClassId = _JsFunctionClassId; |
+const int JsArray::dartClassId = _JsArrayClassId; |
+ |
+// TODO(jacobr): v8::String::NewFromUtf8 usages should be cached for constant |
+// strings to improve performance. |
+/** |
+ * Polyfill script to make Dart List objects look like JavaScript arrays when |
+ * passed to JavaScript via JavaScript interop. Handling the edge cases |
+ * requires patching the JavaScript Array prototype for a couple methods |
+ * and implementing all of the JavaScript Array methods on the JavaScript |
+ * proxy object for the Dart List. Some of the JavaScript methods are |
+ * implemented in Dart and C++ code, some are implemented in this JavaScript |
+ * polyfill script. |
+ */ |
+const char* dartArrayPolyfill = |
+ "(function() {" |
+ // If filter has already been defined on the DartList prototype, |
+ // another DOM isolate has beaten us to polyfilling. |
+ " if ($DartList.prototype.hasOwnProperty('filter')) return;" |
+ // This hack is required so that someDartList instanceof Array == true. |
+ " $DartList.prototype.__proto__ = Array.prototype;" |
+ " $DartList.__proto__ = Array;" |
+ // This hack is required for libraries such as jQuery that detect arrays |
+ // by looking at the constructor property treat $DartList as Array. |
+ " $DartList.prototype.constructor = Array;" |
+ " var isArray = Array.isArray;" |
+ " var concat = Array.prototype.concat;" |
+ " var blob = Blob;" |
+ " function makeSafeArg(arg) {" |
+ " return (arg instanceof $DartList) ? arg.$toJsArray() : arg;" |
+ " };" |
+ " function makeSafeArgs(args) {" |
+ " var len = args.length;" |
+ " for (var i = 0; i < len; ++i) {" |
+ " var arg = args[i];" |
+ " if (arg instanceof $DartList) {" |
+ " args[i] = arg.$toJsArray();" |
+ " }" |
+ " }" |
+ " };" |
+ |
+ // If performance becomes an issue, we could implement these |
+ // methods in Dart instead of creating a shallow copy JavaScript |
+ // array containing the elements of the Dart List and calling |
+ // the JavaScript method. |
+ |
+ // Handle methods that take a callback with value, index, and thisArg |
+ // parameters. The trick is we need to make thisArg reference the |
+ // underlying Dart List rather than the JavaScript copy we create for |
+ // implementation convenience. |
+ " ['filter', 'forEach', 'some', 'every', 'map'].forEach(function(name) {" |
+ " Object.defineProperty($DartList.prototype, name, {enumerable: false, value: function(callback, thisArg) {" |
+ " var dartList = this;" |
+ " return this.$toJsArray()[name](function(value, index) {" |
+ " return callback.call(thisArg, value, index, dartList);" |
+ " });" |
+ " }});" |
+ " });" |
+ |
+ " ['slice', 'indexOf', 'lastIndexOf'].forEach(function(name) {" |
+ " Object.defineProperty($DartList.prototype, name, {enumerable: false, value: function() {" |
+ " var jsArray = this.$toJsArray();" |
+ " return jsArray[name].apply(jsArray, arguments);" |
+ " }});" |
+ " });" |
+ |
+ " ['reduce', 'reduceRight'].forEach(function(name) {" |
+ " Object.defineProperty($DartList.prototype, name, {enumerable: false, value: function(callback, thisArg) {" |
+ " var dartList = this;" |
+ " return this.$toJsArray()[name](function(previousValue, currentValue, index) {" |
+ " return callback.call(thisArg, previousValue, currentValue, index, dartList);" |
+ " });" |
+ " }});" |
+ " });" |
+ |
+ // Arguments to concat that are Arrays are treated differently. |
+ // Warning: this will slow down general JavaScript array concat performance in Dartium. |
+ " Array.prototype.concat = function() {" |
+ " makeSafeArgs(arguments);" |
+ " return concat.apply(this, arguments);" |
+ " };" |
+ |
+ // The Blob constructor cannot handle our psuedo Arrays. |
+ // Warning: this will slow down general Blob constructor performance in Dartium. |
+ " Blob = function(array, options) {" |
+ " if (arguments.length < 2) return new blob(makeSafeArg(array));" |
+ " else return new blob(makeSafeArg(array), options);" |
+ " };" |
+ |
+ // Need to make sure that Array.isArray returns true for Dart lists. |
+ " Array.isArray = function(arr) {" |
+ " return isArray(arr) || (arr instanceof $DartList);" |
+ " };" |
+ "})();"; |
+ |
+static v8::Local<v8::FunctionTemplate> dartFunctionTemplate(); |
+static v8::Local<v8::FunctionTemplate> dartObjectTemplate(); |
+static v8::Local<v8::FunctionTemplate> dartListTemplate(); |
+ |
+// TODO(jacobr): we should really be using this method everywhere instead of |
+// sticking interop methods in the dart:html _Utils class. |
+static Dart_Handle invokeTopLevelJsInteropMethod(DartDOMData* domData, const char* methodName, int argCount, Dart_Handle* args) |
+{ |
+ Dart_PersistentHandle library = domData->jsLibrary(); |
+ ASSERT(!Dart_IsError(library)); |
+ return Dart_Invoke(library, Dart_NewStringFromCString(methodName), argCount, args); |
+} |
+ |
+template<typename CallbackInfo> |
+void setJsReturnValue(DartDOMData* domData, CallbackInfo info, Dart_Handle result) |
+{ |
+ if (Dart_IsError(result)) { |
+ v8::Isolate* v8Isolate = v8::Isolate::GetCurrent(); |
+ V8ThrowException::throwException(v8::String::NewFromUtf8(v8Isolate, Dart_GetError(result)), v8Isolate); |
+ } else { |
+ Dart_Handle exception = 0; |
+ v8::Local<v8::Value> ret = JsInterop::fromDart(domData, result, exception); |
+ if (exception) { |
+ V8ThrowException::throwException(V8Converter::stringToV8(Dart_ToString(exception)), v8::Isolate::GetCurrent()); |
+ return; |
+ } |
+ v8SetReturnValue(info, ret); |
+ } |
+} |
+ |
+static void functionInvocationCallback(const v8::FunctionCallbackInfo<v8::Value>& args) |
+{ |
+ DartScopes scopes(args.Holder()); |
+ Dart_Handle handle = scopes.handle; |
+ DartDOMData* domData = DartDOMData::current(); |
+ ASSERT(domData); |
+ ASSERT(DartUtilities::isFunction(domData, handle)); |
+ |
+ Vector<Dart_Handle> dartFunctionArgs; |
+ ASSERT(args.Length() == 1 || args.Length() == 2); |
+ // If there is 1 argument, we assume it is a v8:Array or arguments, if |
+ // there are 2 arguments, the first argument is "this" and the second |
+ // argument is an array of arguments. |
+ if (args.Length() > 1) { |
+ dartFunctionArgs.append(JsInterop::toDart(args[0])); |
+ } |
+ |
+ v8::Local<v8::Array> argsList = args[args.Length()-1].As<v8::Array>(); |
+ uint32_t argsListLength = argsList->Length(); |
+ for (uint32_t i = 0; i < argsListLength; i++) { |
+ dartFunctionArgs.append(JsInterop::toDart(argsList->Get(i))); |
+ } |
+ |
+ setJsReturnValue(domData, args, Dart_InvokeClosure(handle, dartFunctionArgs.size(), dartFunctionArgs.data())); |
+} |
+ |
+static v8::Local<v8::ObjectTemplate> setupInstanceTemplate(v8::Local<v8::FunctionTemplate> proxyTemplate) |
+{ |
+ v8::Local<v8::ObjectTemplate> instanceTemplate = proxyTemplate->InstanceTemplate(); |
+ instanceTemplate->SetInternalFieldCount(1); |
+ return instanceTemplate; |
+} |
+ |
+static v8::Local<v8::FunctionTemplate> dartFunctionTemplate() |
+{ |
+ DEFINE_STATIC_LOCAL(v8::Persistent<v8::FunctionTemplate>, proxyTemplate, ()); |
+ v8::Local<v8::FunctionTemplate> proxyTemplateLocal; |
+ v8::Isolate* v8Isolate = v8::Isolate::GetCurrent(); |
+ if (proxyTemplate.IsEmpty()) { |
+ proxyTemplate.Reset(v8::Isolate::GetCurrent(), v8::FunctionTemplate::New(v8Isolate)); |
+ proxyTemplateLocal = v8::Local<v8::FunctionTemplate>::New(v8Isolate, proxyTemplate); |
+ v8::Local<v8::ObjectTemplate> instanceTemplate = setupInstanceTemplate(proxyTemplateLocal); |
+ |
+ instanceTemplate->SetCallAsFunctionHandler(&functionInvocationCallback); |
+ } else { |
+ proxyTemplateLocal = v8::Local<v8::FunctionTemplate>::New(v8Isolate, proxyTemplate); |
+ } |
+ return proxyTemplateLocal; |
+} |
+ |
+static v8::Local<v8::FunctionTemplate> dartObjectTemplate() |
+{ |
+ DEFINE_STATIC_LOCAL(v8::Persistent<v8::FunctionTemplate>, proxyTemplate, ()); |
+ v8::Local<v8::FunctionTemplate> proxyTemplateLocal; |
+ v8::Isolate* v8Isolate = v8::Isolate::GetCurrent(); |
+ if (proxyTemplate.IsEmpty()) { |
+ proxyTemplate.Reset(v8Isolate, v8::FunctionTemplate::New(v8Isolate)); |
+ proxyTemplateLocal = v8::Local<v8::FunctionTemplate>::New(v8Isolate, proxyTemplate); |
+ proxyTemplateLocal->SetClassName(v8::String::NewFromUtf8(v8Isolate, "DartObject")); |
+ setupInstanceTemplate(proxyTemplateLocal); |
+ } else { |
+ proxyTemplateLocal = v8::Local<v8::FunctionTemplate>::New(v8Isolate, proxyTemplate); |
+ } |
+ return proxyTemplateLocal; |
+} |
+ |
+/** |
+ * Helper class to manage scopes needed for JSInterop code. |
+ */ |
+class JsInteropScopes { |
+public: |
+ Dart_NativeArguments args; |
+ v8::Context::Scope v8Scope; |
+ v8::TryCatch tryCatch; |
+ |
+ JsInteropScopes(Dart_NativeArguments args) |
+ : args(args) |
+ , v8Scope(DartUtilities::currentV8Context()) |
+ { |
+ ASSERT(v8::Isolate::GetCurrent()); |
+ } |
+ |
+ ~JsInteropScopes() |
+ { |
+ // The user is expected to call handleJsException before the scope is |
+ // closed so that V8 exceptions are properly sent back to Dart. |
+ ASSERT(!tryCatch.HasCaught()); |
+ } |
+ |
+ bool handleJsException(Dart_Handle* exception) |
+ { |
+ if (!tryCatch.HasCaught()) |
+ return false; |
+ // FIXME: terminate v8 if tryCatch.CanContinue() is false. |
+ ASSERT(tryCatch.CanContinue()); |
+ ASSERT(exception); |
+ v8::Handle<v8::Value> ex(tryCatch.Exception()->ToString()); |
+ if (ex.IsEmpty()) { |
+ *exception = Dart_NewStringFromCString("Empty JavaScript exception"); |
+ } else { |
+ *exception = V8Converter::stringToDart(ex); |
+ } |
+ tryCatch.Reset(); |
+ return true; |
+ } |
+ |
+ void setReturnValue(Dart_Handle ret) |
+ { |
+ ASSERT(!tryCatch.HasCaught()); |
+ Dart_SetReturnValue(args, ret); |
+ } |
+ |
+ void setReturnValue(v8::Local<v8::Value> ret) |
+ { |
+ ASSERT(!tryCatch.HasCaught()); |
+ Dart_SetReturnValue(args, JsInterop::toDart(ret)); |
+ ASSERT(!tryCatch.HasCaught()); |
+ } |
+ |
+ void setReturnValueInteger(int64_t ret) |
+ { |
+ ASSERT(!tryCatch.HasCaught()); |
+ Dart_SetIntegerReturnValue(args, ret); |
+ } |
+}; |
+ |
+PassRefPtr<JsObject> JsObject::create(v8::Local<v8::Object> v8Handle) |
+{ |
+ return adoptRef(new JsObject(v8Handle)); |
+} |
+ |
+v8::Local<v8::Value> JsInterop::fromDart(DartDOMData* domData, Dart_Handle handle, Dart_Handle& exception) |
+{ |
+ v8::Handle<v8::Value> value = V8Converter::toV8IfPrimitive(domData, handle, exception); |
+ if (!value.IsEmpty() || exception) |
+ return value; |
+ // TODO(terry): START of uncommented block by Jacob, I've re-enabled for clamped arrays... |
+ value = V8Converter::toV8IfBrowserNative(domData, handle, exception); |
+ if (!value.IsEmpty() || exception) |
+ return value; |
+ // TODO(terry): END of uncommented block by Jacob. |
+ if (DartDOMWrapper::subtypeOf(handle, JsObject::dartClassId)) { |
+ JsObject* object = DartDOMWrapper::unwrapDartWrapper<JsObject>(domData, handle, exception); |
+ if (exception) |
+ return v8::Local<v8::Value>(); |
+ return object->localV8Object(); |
+ } |
+ |
+ if (DartUtilities::isFunction(domData, handle)) { |
+ v8::Local<v8::Object> functionProxy = dartFunctionTemplate()->InstanceTemplate()->NewInstance(); |
+ DartHandleProxy::writePointerToProxy(functionProxy, handle); |
+ // The raw functionProxy doesn't behave enough like a true JS function |
+ // so we wrap it in a true JS function. |
+ return domData->jsInteropData()->wrapDartFunction()->Call(functionProxy, 0, 0); |
+ } |
+ |
+ v8::Local<v8::Object> proxy; |
+ ASSERT(Dart_IsInstance(handle)); |
+ // Simulate the behavior of the Dart dev compiler where new List() is |
+ // equivalent to a JavaScript array. We accomplish this by creating a |
+ // JavaScript object that fakes that it is a JavaScript array but is |
+ // actually backed by a Dart list. This is not a breaking change as |
+ // existing Dart-JS interop passed arrays as opaque Dart handles. |
+ // The jsify method can still be called if you wish to create a copy |
+ // of a json like Dart data structure. |
+ if (Dart_IsList(handle)) { |
+ proxy = dartListTemplate()->InstanceTemplate()->NewInstance(); |
+ } else { |
+ proxy = dartObjectTemplate()->InstanceTemplate()->NewInstance(); |
+ } |
+ DartHandleProxy::writePointerToProxy(proxy, handle); |
+ v8::Isolate* v8Isolate = v8::Isolate::GetCurrent(); |
+ proxy->SetHiddenValue(v8::String::NewFromUtf8(v8Isolate, "dartProxy"), v8::Boolean::New(v8Isolate, true)); |
+ |
+ return proxy; |
+} |
+ |
+// Given a Blink Element instance, make it a V8Object that can then be returned |
+// as a Dart JsObject that holds the Javascript object (V8Object). |
+void JsInterop::returnBlinkElement(Element* value, Dart_NativeArguments args, Dart_Handle& exception) |
+{ |
+ JsInteropScopes scopes(args); |
+ |
+ V8ScriptState* state = DartUtilities::v8ScriptStateForCurrentIsolate(); |
+ |
+ // This is calling blink::Element::toV8(Element*,...) to return a V8Object from |
+ // the Blink Element. |
+ v8::Handle<v8::Value> v8Value = toV8(value, state->context()->Global(), state->isolate()); |
+ v8::Local<v8::Object> v8Handle = v8Value.As<v8::Object>(); |
+ |
+ // Return a Dart JsObject that wraps Element * back to the Dart return value. |
+ scopes.setReturnValue(v8Handle); |
+} |
+ |
+// Given a Blink Element instance, make it a V8Object that can then be returned |
+// as a Dart JsObject that holds the Javascript object (V8Object). |
+v8::Local<v8::Object> JsInterop::blinkElement(Element* value, Dart_Handle& exception) |
+{ |
+ V8ScriptState* state = DartUtilities::v8ScriptStateForCurrentIsolate(); |
+ |
+ // This is calling blink::Element::toV8(Element*,...) to return a V8Object from |
+ // the Blink Element. |
+ v8::Handle<v8::Value> v8Value = toV8(value, state->context()->Global(), state->isolate()); |
+ v8::Local<v8::Object> v8Handle = v8Value.As<v8::Object>(); |
+ return v8Handle; |
+} |
+ |
+JsObject::JsObject(v8::Local<v8::Object> v8Handle) |
+{ |
+ v8::Isolate* isolate = v8::Isolate::GetCurrent(); |
+ v8::Persistent<v8::Object> persistentHandle; |
+ v8Object.Reset(isolate, v8Handle); |
+} |
+ |
+v8::Local<v8::Object> JsObject::localV8Object() |
+{ |
+ return v8::Local<v8::Object>::New(v8::Isolate::GetCurrent(), v8Object); |
+} |
+ |
+Dart_Handle JsInterop::toDart(v8::Local<v8::Value> v8Handle) |
+{ |
+ Dart_Handle handle = V8Converter::toDartIfPrimitive(v8Handle); |
+ if (handle) |
+ return handle; |
+ |
+ ASSERT(v8Handle->IsObject()); |
+ v8::Handle<v8::Object> object = v8Handle.As<v8::Object>(); |
+ // TODO(terry): START of uncommented block by Jacob, I've re-enabled for clamped arrays... |
+ Dart_Handle exception = 0; |
+ handle = V8Converter::toDartIfBrowserNative(object, object->CreationContext()->GetIsolate(), exception); |
+ ASSERT(!exception); |
+ if (handle) |
+ return handle; |
+ // TODO(terry): END of uncommented block by Jacob. |
+ // Unwrap objects passed from Dart to JS that are being passed back to |
+ // Dart. FIXME: we do not yet handle unwrapping JS functions passed |
+ // from Dart to JS as we have to wrap them with true JS Function objects. |
+ // If this use case is important we can support it at the cost of hanging |
+ // an extra expando off the JS function wrapping the Dart function. |
+ if (DartHandleProxy::isDartProxy(v8Handle)) { |
+ DartPersistentValue* scriptValue = DartHandleProxy::readPointerFromProxy(v8Handle); |
+ ASSERT(scriptValue->isIsolateAlive()); |
+ // If the isolate does not match we fall back to using the existing JS |
+ // wrapper for the Dart object so that simple cases that would work in |
+ // Dart2Js work. We could alternately throw an exception here. |
+ if (scriptValue->isolate() == Dart_CurrentIsolate()) { |
+ return scriptValue->value(); |
+ } |
+ } |
+ |
+ return JsObject::toDart(object); |
+} |
+ |
+Dart_Handle JsObject::toDart(v8::Local<v8::Object> object) |
+{ |
+ // FIXME: perform caching so that === can be used. |
+ if (object->IsFunction()) { |
+ RefPtr<JsFunction> jsFunction = JsFunction::create(object.As<v8::Function>()); |
+ return JsFunction::toDart(jsFunction); |
+ } |
+ |
+ if (object->IsArray()) { |
+ RefPtr<JsArray> jsArray = JsArray::create(object.As<v8::Array>()); |
+ return JsArray::toDart(jsArray); |
+ } |
+ |
+ RefPtr<JsObject> jsObject = JsObject::create(object); |
+ return JsObject::toDart(jsObject); |
+} |
+ |
+static void maybeCreateJsObjectImplClass(DartDOMData* domData) |
+{ |
+ DartJsInteropData* interopData = domData->jsInteropData(); |
+ // Skip if the JSObjectImpl class has already been defined. |
+ if (interopData->jsObjectImplDefined()) { |
+ return; |
+ } |
+ // Helper method that generates boilerplate source code for |
+ // JsObjectImpl, JsFunctionImpl, and JsArrayImpl classes that implement |
+ // all Dart types that have been passed to the dart:js registerJsInterfaces |
+ // method. The sole purpose of these classes is to ensure that checked mode |
+ // allows casting a JsObject to all types implemented by a JsObject. |
+ Dart_Handle source = invokeTopLevelJsInteropMethod(domData, "_generateJsObjectImplPart", 0, 0); |
+ ASSERT(Dart_IsString(source)); |
+ |
+ Dart_Handle ALLOW_UNUSED ret = Dart_LibraryLoadPatch(domData->jsLibrary(), Dart_NewStringFromCString("JsInteropImpl.dart"), source); |
+ ASSERT(!Dart_IsError(ret)); |
+ ret = Dart_FinalizeLoading(false); |
+ ASSERT(!Dart_IsError(ret)); |
+ |
+ interopData->setJsObjectImplDefined(); |
+ |
+ // Start of polyfill work to make Dart List proxies behave like JavaScript |
+ // Arrays by monkey patching JavaScript Array and the List JavaScript |
+ // proxies as needed. |
+ v8::Context::Scope scope(DartUtilities::currentV8Context()); |
+ |
+ v8::Isolate* v8Isolate = v8::Isolate::GetCurrent(); |
+ |
+ v8::Local<v8::Function> dartArrayConstructor = dartListTemplate()->GetFunction(); |
+ |
+ DartUtilities::currentV8Context()->Global()->Set(v8::String::NewFromUtf8(v8Isolate, "$DartList"), |
+ dartArrayConstructor); |
+ V8ScriptRunner::compileAndRunInternalScript(v8::String::NewFromUtf8(v8Isolate, dartArrayPolyfill), v8Isolate); |
+} |
+ |
+Dart_Handle JsObject::toDart(PassRefPtr<JsObject> jsObject) |
+{ |
+ DartDOMData* domData = DartDOMData::current(); |
+ // We need to ensure JsObjectImpl exists before creating the wrapper. |
+ maybeCreateJsObjectImplClass(domData); |
+ return DartDOMWrapper::createWrapper<JsObject>(domData, jsObject.get(), JsObject::dartClassId); |
+} |
+ |
+JsObject::~JsObject() |
+{ |
+ v8Object.Reset(); |
+} |
+ |
+Dart_Handle JsFunction::toDart(PassRefPtr<JsFunction> jsFunction) |
+{ |
+ DartDOMData* domData = DartDOMData::current(); |
+ // We need to ensure JsObjectImpl exists before creating the wrapper. |
+ maybeCreateJsObjectImplClass(domData); |
+ return DartDOMWrapper::createWrapper<JsFunction>(domData, jsFunction.get(), JsFunction::dartClassId); |
+} |
+ |
+JsFunction::JsFunction(v8::Local<v8::Function> v8Handle) : JsObject(v8Handle) { } |
+ |
+PassRefPtr<JsFunction> JsFunction::create(v8::Local<v8::Function> v8Handle) |
+{ |
+ return adoptRef(new JsFunction(v8Handle)); |
+} |
+ |
+v8::Local<v8::Function> JsFunction::localV8Function() |
+{ |
+ return localV8Object().As<v8::Function>(); |
+} |
+ |
+Dart_Handle JsArray::toDart(PassRefPtr<JsArray> jsArray) |
+{ |
+ DartDOMData* domData = DartDOMData::current(); |
+ // We need to ensure JsArrayImpl exists before creating the wrapper. |
+ maybeCreateJsObjectImplClass(domData); |
+ return DartDOMWrapper::createWrapper<JsArray>(domData, jsArray.get(), JsArray::dartClassId); |
+} |
+ |
+JsArray::JsArray(v8::Local<v8::Array> v8Handle) : JsObject(v8Handle) { } |
+ |
+PassRefPtr<JsArray> JsArray::create(v8::Local<v8::Array> v8Handle) |
+{ |
+ return adoptRef(new JsArray(v8Handle)); |
+} |
+ |
+v8::Local<v8::Array> JsArray::localV8Array() |
+{ |
+ return localV8Object().As<v8::Array>(); |
+} |
+ |
+namespace JsInteropInternal { |
+ |
+typedef HashMap<Dart_Handle, v8::Handle<v8::Value> > DartHandleToV8Map; |
+v8::Handle<v8::Value> jsifyHelper(DartDOMData*, Dart_Handle value, DartHandleToV8Map&, Dart_Handle& exception); |
+ |
+void argsListToV8(DartDOMData* domData, Dart_Handle args, Vector<v8::Local<v8::Value> >* v8Args, Dart_Handle& exception) |
+{ |
+ if (Dart_IsNull(args)) |
+ return; |
+ |
+ if (!Dart_IsList(args)) { |
+ exception = Dart_NewStringFromCString("args not type list"); |
+ return; |
+ } |
+ |
+ intptr_t argsLength = 0; |
+ Dart_ListLength(args, &argsLength); |
+ for (intptr_t i = 0; i < argsLength; i++) { |
+ v8Args->append(JsInterop::fromDart(domData, Dart_ListGetAt(args, i), exception)); |
+ if (exception) |
+ return; |
+ } |
+} |
+ |
+void argsListToV8DebuggerOnly(DartDOMData* domData, Dart_Handle args, Vector<v8::Local<v8::Value> >* v8Args, Dart_Handle& exception) |
+{ |
+ if (Dart_IsNull(args)) |
+ return; |
+ |
+ if (!Dart_IsList(args)) { |
+ exception = Dart_NewStringFromCString("args not type list"); |
+ return; |
+ } |
+ |
+ intptr_t argsLength = 0; |
+ Dart_ListLength(args, &argsLength); |
+ for (intptr_t i = 0; i < argsLength; i++) { |
+ v8Args->append(DartHandleProxy::create(Dart_ListGetAt(args, i))); |
+ } |
+} |
+ |
+static void jsObjectConstructorCallback(Dart_NativeArguments args) |
+{ |
+ Dart_Handle exception = 0; |
+ { |
+ JsInteropScopes scopes(args); |
+ DartDOMData* domData = static_cast<DartDOMData*>(Dart_GetNativeIsolateData(args)); |
+ v8::Local<v8::Value> constructorArg = JsInterop::fromDart(domData, Dart_GetNativeArgument(args, 0), exception); |
+ if (exception) |
+ goto fail; |
+ |
+ if (!constructorArg->IsFunction()) { |
+ exception = Dart_NewStringFromCString("constructor not a function"); |
+ goto fail; |
+ } |
+ |
+ Vector<v8::Local<v8::Value> > v8Args; |
+ argsListToV8(domData, Dart_GetNativeArgument(args, 1), &v8Args, exception); |
+ |
+ v8::Local<v8::Value> ret = constructorArg.As<v8::Function>()->CallAsConstructor(v8Args.size(), v8Args.data()); |
+ crashIfV8IsDead(); |
+ |
+ if (scopes.handleJsException(&exception)) |
+ goto fail; |
+ |
+ // Intentionally skip auto-conversion in this case as the user expects |
+ // a JSObject. FIXME: evaluate if this is the right solution. |
+ // Alternately, we could throw an exception. |
+ if (ret->IsObject()) { |
+ scopes.setReturnValue(JsObject::toDart(ret.As<v8::Object>())); |
+ } else { |
+ // This will throw an exception in Dart checked mode. |
+ scopes.setReturnValue(ret); |
+ } |
+ return; |
+ } |
+ |
+fail: |
+ Dart_ThrowException(exception); |
+ ASSERT_NOT_REACHED(); |
+} |
+ |
+static void identityEqualityCallback(Dart_NativeArguments args) |
+{ |
+ Dart_Handle exception = 0; |
+ { |
+ JsInteropScopes scopes(args); |
+ DartDOMData* domData = static_cast<DartDOMData*>(Dart_GetNativeIsolateData(args)); |
+ v8::Local<v8::Value> a = JsInterop::fromDart(domData, Dart_GetNativeArgument(args, 0), exception); |
+ if (exception) |
+ goto fail; |
+ v8::Local<v8::Value> b = JsInterop::fromDart(domData, Dart_GetNativeArgument(args, 1), exception); |
+ if (exception) |
+ goto fail; |
+ |
+ bool strictEquals = a->StrictEquals(b); |
+ |
+ if (scopes.handleJsException(&exception)) |
+ goto fail; |
+ scopes.setReturnValue(DartUtilities::boolToDart(strictEquals)); |
+ return; |
+ } |
+ |
+fail: |
+ Dart_ThrowException(exception); |
+ ASSERT_NOT_REACHED(); |
+} |
+ |
+static void getterCallback(Dart_NativeArguments args) |
+{ |
+ Dart_Handle exception = 0; |
+ { |
+ JsInteropScopes scopes(args); |
+ JsObject* receiver = DartDOMWrapper::receiver<JsObject>(args); |
+ v8::Local<v8::Object> v8Receiver = receiver->localV8Object(); |
+ Dart_Handle index = Dart_GetNativeArgument(args, 1); |
+ uint64_t intIndex = 0; |
+ v8::Local<v8::Value> ret; |
+ |
+ if (Dart_IsInteger(index)) { |
+ bool isUint64 = false; |
+ Dart_IntegerFitsIntoUint64(index, &isUint64); |
+ if (isUint64) { |
+ Dart_Handle ALLOW_UNUSED result = Dart_IntegerToUint64(index, &intIndex); |
+ if (intIndex <= std::numeric_limits<uint32_t>::max()) { |
+ ASSERT(!Dart_IsError(result)); |
+ ret = v8Receiver->Get((uint32_t)intIndex); |
+ } else { |
+ ret = v8Receiver->Get(V8Converter::numberToV8(index)); |
+ } |
+ } else { |
+ ret = v8Receiver->Get(V8Converter::numberToV8(index)); |
+ } |
+ } else if (Dart_IsString(index)) { |
+ ret = v8Receiver->Get(V8Converter::stringToV8(index)); |
+ } else if (Dart_IsNumber(index)) { |
+ ret = v8Receiver->Get(V8Converter::numberToV8(index)); |
+ } else { |
+ ret = v8Receiver->Get(V8Converter::stringToV8(Dart_ToString(index))); |
+ } |
+ |
+ if (scopes.handleJsException(&exception)) |
+ goto fail; |
+ scopes.setReturnValue(ret); |
+ return; |
+ } |
+ |
+fail: |
+ Dart_ThrowException(exception); |
+ ASSERT_NOT_REACHED(); |
+} |
+ |
+static void hasPropertyCallback(Dart_NativeArguments args) |
+{ |
+ Dart_Handle exception = 0; |
+ { |
+ JsInteropScopes scopes(args); |
+ JsObject* receiver = DartDOMWrapper::receiver<JsObject>(args); |
+ v8::Local<v8::Object> v8Receiver = receiver->localV8Object(); |
+ Dart_Handle property = Dart_GetNativeArgument(args, 1); |
+ |
+ if (!Dart_IsString(property)) |
+ property = Dart_ToString(property); |
+ |
+ bool hasProperty = v8Receiver->Has(V8Converter::stringToV8(property)); |
+ if (scopes.handleJsException(&exception)) |
+ goto fail; |
+ scopes.setReturnValue(DartUtilities::boolToDart(hasProperty)); |
+ return; |
+ } |
+ |
+fail: |
+ Dart_ThrowException(exception); |
+ ASSERT_NOT_REACHED(); |
+} |
+ |
+static void deletePropertyCallback(Dart_NativeArguments args) |
+{ |
+ Dart_Handle exception = 0; |
+ { |
+ JsInteropScopes scopes(args); |
+ JsObject* receiver = DartDOMWrapper::receiver<JsObject>(args); |
+ v8::Local<v8::Object> v8Receiver = receiver->localV8Object(); |
+ Dart_Handle property = Dart_GetNativeArgument(args, 1); |
+ if (!Dart_IsString(property)) |
+ property = Dart_ToString(property); |
+ |
+ v8Receiver->Delete(V8Converter::stringToV8(property)); |
+ if (scopes.handleJsException(&exception)) |
+ goto fail; |
+ return; |
+ } |
+ |
+fail: |
+ Dart_ThrowException(exception); |
+ ASSERT_NOT_REACHED(); |
+} |
+ |
+static void instanceofCallback(Dart_NativeArguments args) |
+{ |
+ Dart_Handle exception = 0; |
+ { |
+ JsInteropScopes scopes(args); |
+ DartDOMData* domData = static_cast<DartDOMData*>(Dart_GetNativeIsolateData(args)); |
+ JsObject* receiver = DartDOMWrapper::receiver<JsObject>(args); |
+ v8::Local<v8::Object> v8Receiver = receiver->localV8Object(); |
+ v8::Local<v8::Value> type = JsInterop::fromDart(domData, Dart_GetNativeArgument(args, 1), exception); |
+ |
+ // FIXME: we could optimize the following lines slightly as the return |
+ // type is bool. |
+ v8::Local<v8::Value> ret = domData->jsInteropData()->instanceofFunction()->Call(v8Receiver, 1, &type); |
+ if (scopes.handleJsException(&exception)) |
+ goto fail; |
+ scopes.setReturnValue(ret); |
+ return; |
+ } |
+ |
+fail: |
+ Dart_ThrowException(exception); |
+ ASSERT_NOT_REACHED(); |
+} |
+ |
+static void setterCallback(Dart_NativeArguments args) |
+{ |
+ Dart_Handle exception = 0; |
+ { |
+ JsInteropScopes scopes(args); |
+ DartDOMData* domData = static_cast<DartDOMData*>(Dart_GetNativeIsolateData(args)); |
+ JsObject* receiver = DartDOMWrapper::receiver<JsObject>(args); |
+ v8::Local<v8::Object> v8Receiver = receiver->localV8Object(); |
+ Dart_Handle index = Dart_GetNativeArgument(args, 1); |
+ v8::Local<v8::Value> value = JsInterop::fromDart(domData, Dart_GetNativeArgument(args, 2), exception); |
+ if (exception) |
+ goto fail; |
+ uint64_t intIndex = 0; |
+ bool ret = false; |
+ if (Dart_IsInteger(index)) { |
+ bool isUint64 = false; |
+ Dart_IntegerFitsIntoUint64(index, &isUint64); |
+ if (isUint64) { |
+ Dart_Handle ALLOW_UNUSED result = Dart_IntegerToUint64(index, &intIndex); |
+ if (intIndex <= std::numeric_limits<uint32_t>::max()) { |
+ ASSERT(!Dart_IsError(result)); |
+ ret = v8Receiver->Set((uint32_t)intIndex, value); |
+ } else { |
+ ret = v8Receiver->Set(V8Converter::numberToV8(index), value); |
+ } |
+ } else { |
+ ret = v8Receiver->Set(V8Converter::numberToV8(index), value); |
+ } |
+ } else if (Dart_IsString(index)) { |
+ ret = v8Receiver->Set(V8Converter::stringToV8(index), value); |
+ } else if (Dart_IsNumber(index)) { |
+ ret = v8Receiver->Set(V8Converter::numberToV8(index), value); |
+ } else { |
+ ret = v8Receiver->Set(V8Converter::stringToV8(Dart_ToString(index)), value); |
+ } |
+ |
+ if (scopes.handleJsException(&exception)) |
+ goto fail; |
+ scopes.setReturnValue(DartUtilities::boolToDart(ret)); |
+ return; |
+ } |
+ |
+fail: |
+ Dart_ThrowException(exception); |
+ ASSERT_NOT_REACHED(); |
+} |
+ |
+static void hashCodeCallback(Dart_NativeArguments args) |
+{ |
+ Dart_Handle exception = 0; |
+ { |
+ JsInteropScopes scopes(args); |
+ JsObject* receiver = DartDOMWrapper::receiver<JsObject>(args); |
+ int hashCode = receiver->localV8Object()->GetIdentityHash(); |
+ // FIXME: salt the v8 hashcode so we don't leak information about v8 |
+ // memory allocation. |
+ if (scopes.handleJsException(&exception)) |
+ goto fail; |
+ scopes.setReturnValueInteger(hashCode); |
+ return; |
+ } |
+ |
+fail: |
+ Dart_ThrowException(exception); |
+ ASSERT_NOT_REACHED(); |
+} |
+ |
+static void callMethodCallback(Dart_NativeArguments args) |
+{ |
+ Dart_Handle exception = 0; |
+ { |
+ JsInteropScopes scopes(args); |
+ DartDOMData* domData = static_cast<DartDOMData*>(Dart_GetNativeIsolateData(args)); |
+ JsObject* receiver = DartDOMWrapper::receiver<JsObject>(args); |
+ v8::Local<v8::Object> v8Receiver = receiver->localV8Object(); |
+ |
+ Dart_Handle name = Dart_GetNativeArgument(args, 1); |
+ |
+ Vector<v8::Local<v8::Value> > v8Args; |
+ argsListToV8(domData, Dart_GetNativeArgument(args, 2), &v8Args, exception); |
+ if (exception) |
+ goto fail; |
+ if (!Dart_IsString(name)) |
+ name = Dart_ToString(name); |
+ |
+ v8::Local<v8::Value> value = v8Receiver->Get(V8Converter::stringToV8(name)); |
+ v8::Local<v8::Value> ret; |
+ if (value->IsFunction()) { |
+ ret = V8ScriptRunner::callFunction(value.As<v8::Function>(), DartUtilities::scriptExecutionContext(), receiver->localV8Object(), v8Args.size(), v8Args.data(), v8::Isolate::GetCurrent()); |
+ } else if (value->IsObject()) { |
+ ret = V8ScriptRunner::callAsFunction(v8::Isolate::GetCurrent(), value.As<v8::Object>(), receiver->localV8Object(), v8Args.size(), v8Args.data()); |
+ } else { |
+ // FIXME: we currently convert this exception to a NoSuchMethod |
+ // exception in the Dart code that wraps this native method. |
+ // Consider throwing a NoSuchMethod exception directly instead. |
+ exception = Dart_NewStringFromCString("property is not a function"); |
+ goto fail; |
+ } |
+ |
+ if (scopes.handleJsException(&exception)) |
+ goto fail; |
+ scopes.setReturnValue(ret); |
+ return; |
+ } |
+ |
+fail: |
+ Dart_ThrowException(exception); |
+ ASSERT_NOT_REACHED(); |
+} |
+ |
+static void newJsArrayCallback(Dart_NativeArguments args) |
+{ |
+ JsInteropScopes scopes(args); |
+ scopes.setReturnValue(JsObject::toDart(v8::Array::New(v8::Isolate::GetCurrent()))); |
+ return; |
+} |
+ |
+static void newJsArrayFromSafeListCallback(Dart_NativeArguments args) |
+{ |
+ Dart_Handle exception = 0; |
+ { |
+ JsInteropScopes scopes(args); |
+ DartDOMData* domData = static_cast<DartDOMData*>(Dart_GetNativeIsolateData(args)); |
+ Dart_Handle list = Dart_GetNativeArgument(args, 0); |
+ // Code on the Dart side insures this arg is a native Dart list. |
+ ASSERT(Dart_IsList(list)); |
+ |
+ intptr_t length = 0; |
+ Dart_Handle result = Dart_ListLength(list, &length); |
+ ASSERT(!Dart_IsError(result)); |
+ v8::Local<v8::Array> array = v8::Array::New(v8::Isolate::GetCurrent(), length); |
+ |
+ for (intptr_t i = 0; i < length; ++i) { |
+ result = Dart_ListGetAt(list, i); |
+ ASSERT(!Dart_IsError(result)); |
+ v8::Handle<v8::Value> v8value = JsInterop::fromDart(domData, result, exception); |
+ if (exception) |
+ goto fail; |
+ |
+ array->Set(i, v8value); |
+ } |
+ scopes.setReturnValue(array); |
+ return; |
+ } |
+ |
+fail: |
+ Dart_ThrowException(exception); |
+ ASSERT_NOT_REACHED(); |
+} |
+ |
+ |
+static void jsArrayLengthCallback(Dart_NativeArguments args) |
+{ |
+ Dart_Handle exception = 0; |
+ { |
+ JsInteropScopes scopes(args); |
+ JsArray* receiver = DartDOMWrapper::receiver<JsArray>(args); |
+ uint32_t length = receiver->localV8Array()->Length(); |
+ if (scopes.handleJsException(&exception)) |
+ goto fail; |
+ scopes.setReturnValueInteger(length); |
+ return; |
+ } |
+ |
+fail: |
+ Dart_ThrowException(exception); |
+ ASSERT_NOT_REACHED(); |
+} |
+ |
+static void fromBrowserObjectCallback(Dart_NativeArguments args) |
+{ |
+ Dart_Handle exception = 0; |
+ { |
+ JsInteropScopes scopes(args); |
+ DartDOMData* domData = static_cast<DartDOMData*>(Dart_GetNativeIsolateData(args)); |
+ |
+ v8::Local<v8::Value> ret = V8Converter::toV8IfBrowserNative(domData, Dart_GetNativeArgument(args, 0), exception); |
+ if (ret.IsEmpty()) { |
+ exception = Dart_NewStringFromCString("object must be an Node, ArrayBuffer, Blob, ImageData, or IDBKeyRange"); |
+ goto fail; |
+ } |
+ if (exception) |
+ goto fail; |
+ ASSERT(ret->IsObject()); |
+ if (scopes.handleJsException(&exception)) |
+ goto fail; |
+ scopes.setReturnValue(JsObject::toDart(ret.As<v8::Object>())); |
+ return; |
+ } |
+ |
+fail: |
+ Dart_ThrowException(exception); |
+ ASSERT_NOT_REACHED(); |
+} |
+ |
+static void applyCallback(Dart_NativeArguments args) |
+{ |
+ Dart_Handle exception = 0; |
+ { |
+ JsInteropScopes scopes(args); |
+ DartDOMData* domData = static_cast<DartDOMData*>(Dart_GetNativeIsolateData(args)); |
+ JsFunction* receiver = DartDOMWrapper::receiver<JsFunction>(args); |
+ |
+ Vector<v8::Local<v8::Value> > v8Args; |
+ argsListToV8(domData, Dart_GetNativeArgument(args, 1), &v8Args, exception); |
+ if (exception) |
+ goto fail; |
+ |
+ v8::Local<v8::Value> thisArg; |
+ Dart_Handle thisArgDart = Dart_GetNativeArgument(args, 2); |
+ if (Dart_IsNull(thisArgDart)) { |
+ // Use the global v8 object if no Dart thisArg was passed in. |
+ thisArg = DartUtilities::currentV8Context()->Global(); |
+ } else { |
+ Dart_Handle jso = Dart_GetField(thisArgDart, Dart_NewStringFromCString("blink_jsObject")); |
+ if (!Dart_IsError(jso) && DartDOMWrapper::subtypeOf(jso, JsObject::dartClassId)) { |
+ // Use the blink JS Interop object. |
+ JsObject* object = DartDOMWrapper::unwrapDartWrapper<JsObject>(domData, jso, exception); |
+ if (exception) |
+ thisArg = v8::Local<v8::Value>(); |
+ thisArg = object->localV8Object(); |
+ } else { |
+ thisArg = JsInterop::fromDart(domData, thisArgDart, exception); |
+ if (exception) |
+ goto fail; |
+ } |
+ if (!thisArg->IsObject()) { |
+ exception = Dart_NewStringFromCString("thisArg is not an object"); |
+ goto fail; |
+ } |
+ } |
+ |
+ v8::Local<v8::Value> ret = V8ScriptRunner::callFunction(receiver->localV8Function(), DartUtilities::scriptExecutionContext(), thisArg.As<v8::Object>(), v8Args.size(), v8Args.data(), v8::Isolate::GetCurrent()); |
+ if (scopes.handleJsException(&exception)) |
+ goto fail; |
+ scopes.setReturnValue(ret); |
+ return; |
+ } |
+ |
+fail: |
+ Dart_ThrowException(exception); |
+ ASSERT_NOT_REACHED(); |
+} |
+ |
+static void applyDebuggerOnlyCallback(Dart_NativeArguments args) |
+{ |
+ Dart_Handle exception = 0; |
+ { |
+ JsInteropScopes scopes(args); |
+ DartDOMData* domData = static_cast<DartDOMData*>(Dart_GetNativeIsolateData(args)); |
+ JsFunction* receiver = DartDOMWrapper::receiver<JsFunction>(args); |
+ |
+ Vector<v8::Local<v8::Value> > v8Args; |
+ argsListToV8DebuggerOnly(domData, Dart_GetNativeArgument(args, 1), &v8Args, exception); |
+ if (exception) |
+ goto fail; |
+ |
+ v8::Local<v8::Value> thisArg; |
+ Dart_Handle thisArgDart = Dart_GetNativeArgument(args, 2); |
+ if (Dart_IsNull(thisArgDart)) { |
+ // Use the global v8 object if no Dart thisArg was passed in. |
+ thisArg = DartUtilities::currentV8Context()->Global(); |
+ } else { |
+ thisArg = JsInterop::fromDart(domData, thisArgDart, exception); |
+ if (exception) |
+ goto fail; |
+ if (!thisArg->IsObject()) { |
+ exception = Dart_NewStringFromCString("thisArg is not an object"); |
+ goto fail; |
+ } |
+ } |
+ |
+ v8::Local<v8::Value> ret = V8ScriptRunner::callFunction(receiver->localV8Function(), DartUtilities::scriptExecutionContext(), thisArg.As<v8::Object>(), v8Args.size(), v8Args.data(), v8::Isolate::GetCurrent()); |
+ if (scopes.handleJsException(&exception)) |
+ goto fail; |
+ scopes.setReturnValue(ret); |
+ return; |
+ } |
+ |
+fail: |
+ Dart_ThrowException(exception); |
+ ASSERT_NOT_REACHED(); |
+} |
+ |
+static void toStringCallback(Dart_NativeArguments args) |
+{ |
+ Dart_Handle exception = 0; |
+ { |
+ JsInteropScopes scopes(args); |
+ JsObject* receiver = DartDOMWrapper::receiver<JsObject>(args); |
+ v8::Local<v8::Object> v8Object = receiver->localV8Object(); |
+ if (scopes.handleJsException(&exception)) |
+ goto fail; |
+ if (v8Object.IsEmpty()) { |
+ exception = Dart_NewStringFromCString("Invalid v8 handle"); |
+ goto fail; |
+ } |
+ |
+ v8::Local<v8::String> v8String = v8Object->ToString(); |
+ if (scopes.handleJsException(&exception)) |
+ goto fail; |
+ scopes.setReturnValue(v8String); |
+ return; |
+ } |
+fail: |
+ Dart_ThrowException(exception); |
+ ASSERT_NOT_REACHED(); |
+} |
+ |
+static void contextCallback(Dart_NativeArguments args) |
+{ |
+ v8::Local<v8::Context> v8Context = DartUtilities::currentV8Context(); |
+ v8::Context::Scope scope(v8Context); |
+ Dart_SetReturnValue(args, JsObject::toDart(v8Context->Global())); |
+} |
+ |
+static void finalizeJsInterfacesCallback(Dart_NativeArguments args) |
+{ |
+ DartDOMData* domData = static_cast<DartDOMData*>(Dart_GetNativeIsolateData(args)); |
+ maybeCreateJsObjectImplClass(domData); |
+} |
+ |
+static void interfacesFinalizedCallback(Dart_NativeArguments args) |
+{ |
+ DartDOMData* domData = static_cast<DartDOMData*>(Dart_GetNativeIsolateData(args)); |
+ DartJsInteropData* interopData = domData->jsInteropData(); |
+ // Skip if the JSObjectImpl class has already been defined. |
+ Dart_SetBooleanReturnValue(args, interopData->jsObjectImplDefined()); |
+} |
+ |
+v8::Handle<v8::Value> mapToV8(DartDOMData* domData, Dart_Handle value, DartHandleToV8Map& map, Dart_Handle& exception) |
+{ |
+ Dart_Handle asList = DartUtilities::invokeUtilsMethod("convertMapToList", 1, &value); |
+ if (!DartUtilities::checkResult(asList, exception)) |
+ return v8::Handle<v8::Value>(); |
+ ASSERT(Dart_IsList(asList)); |
+ |
+ // Now we have a list [key, value, key, value, ....], create a v8 object and set necesary |
+ // properties on it. |
+ v8::Handle<v8::Object> object = v8::Object::New(v8::Isolate::GetCurrent()); |
+ map.set(value, object); |
+ |
+ // We converted to internal Dart list, methods shouldn't throw exceptions now. |
+ intptr_t length = 0; |
+ Dart_Handle ALLOW_UNUSED result = Dart_ListLength(asList, &length); |
+ ASSERT(!Dart_IsError(result)); |
+ ASSERT(!(length % 2)); |
+ for (intptr_t i = 0; i < length; i += 2) { |
+ v8::Handle<v8::Value> key = jsifyHelper(domData, Dart_ListGetAt(asList, i), map, exception); |
+ if (exception) |
+ return v8::Handle<v8::Value>(); |
+ v8::Handle<v8::Value> value = jsifyHelper(domData, Dart_ListGetAt(asList, i + 1), map, exception); |
+ if (exception) |
+ return v8::Handle<v8::Value>(); |
+ |
+ object->Set(key, value); |
+ } |
+ |
+ return object; |
+} |
+ |
+v8::Handle<v8::Value> listToV8(DartDOMData* domData, Dart_Handle value, DartHandleToV8Map& map, Dart_Handle& exception) |
+{ |
+ ASSERT(Dart_IsList(value)); |
+ |
+ intptr_t length = 0; |
+ Dart_Handle result = Dart_ListLength(value, &length); |
+ if (!DartUtilities::checkResult(result, exception)) |
+ return v8::Handle<v8::Value>(); |
+ |
+ v8::Local<v8::Array> array = v8::Array::New(v8::Isolate::GetCurrent(), length); |
+ map.set(value, array); |
+ |
+ for (intptr_t i = 0; i < length; ++i) { |
+ result = Dart_ListGetAt(value, i); |
+ if (!DartUtilities::checkResult(result, exception)) |
+ return v8::Handle<v8::Value>(); |
+ v8::Handle<v8::Value> v8value = jsifyHelper(domData, result, map, exception); |
+ if (exception) |
+ return v8::Handle<v8::Value>(); |
+ array->Set(i, v8value); |
+ } |
+ |
+ return array; |
+} |
+ |
+v8::Handle<v8::Value> jsifyHelper(DartDOMData* domData, Dart_Handle value, DartHandleToV8Map& map, Dart_Handle& exception) |
+{ |
+ DartHandleToV8Map::iterator iter = map.find(value); |
+ if (iter != map.end()) |
+ return iter->value; |
+ |
+ if (Dart_IsList(value)) |
+ return listToV8(domData, value, map, exception); |
+ |
+ bool isMap = DartUtilities::dartToBool(DartUtilities::invokeUtilsMethod("isMap", 1, &value), exception); |
+ ASSERT(!exception); |
+ if (isMap) |
+ return mapToV8(domData, value, map, exception); |
+ |
+ Dart_Handle maybeList = DartUtilities::invokeUtilsMethod("toListIfIterable", 1, &value); |
+ if (Dart_IsList(maybeList)) |
+ return listToV8(domData, maybeList, map, exception); |
+ |
+ v8::Handle<v8::Value> ret = JsInterop::fromDart(domData, value, exception); |
+ map.set(value, ret); |
+ return ret; |
+} |
+ |
+static void jsifyCallback(Dart_NativeArguments args) |
+{ |
+ Dart_Handle exception = 0; |
+ { |
+ JsInteropScopes scopes(args); |
+ Dart_Handle value = Dart_GetNativeArgument(args, 0); |
+ DartDOMData* domData = static_cast<DartDOMData*>(Dart_GetNativeIsolateData(args)); |
+ DartHandleToV8Map map; |
+ v8::Local<v8::Value> ret = jsifyHelper(domData, value, map, exception); |
+ if (exception) |
+ goto fail; |
+ |
+ if (scopes.handleJsException(&exception)) |
+ goto fail; |
+ // Intentionally skip auto-conversion in this case as the user expects |
+ // a JSObject. FIXME: evaluate if this is the right solution. |
+ // Alternately, we could throw an exception. |
+ if (ret->IsObject()) { |
+ scopes.setReturnValue(JsObject::toDart(ret.As<v8::Object>())); |
+ } else { |
+ // This will throw an exception in Dart checked mode. |
+ scopes.setReturnValue(ret); |
+ } |
+ return; |
+ } |
+fail: |
+ Dart_ThrowException(exception); |
+ ASSERT_NOT_REACHED(); |
+} |
+ |
+static void withThisCallback(Dart_NativeArguments args) |
+{ |
+ Dart_Handle exception = 0; |
+ { |
+ JsInteropScopes scopes(args); |
+ Dart_Handle function = Dart_GetNativeArgument(args, 0); |
+ DartDOMData* domData = static_cast<DartDOMData*>(Dart_GetNativeIsolateData(args)); |
+ ASSERT(DartUtilities::isFunction(domData, function)); |
+ |
+ v8::Local<v8::Object> proxy = dartFunctionTemplate()->InstanceTemplate()->NewInstance(); |
+ DartHandleProxy::writePointerToProxy(proxy, function); |
+ |
+ v8::Local<v8::Function> ret = v8::Local<v8::Function>::Cast(domData->jsInteropData()->captureThisFunction()->Call(proxy, 0, 0)); |
+ |
+ if (scopes.handleJsException(&exception)) |
+ goto fail; |
+ scopes.setReturnValue(ret); |
+ return; |
+ } |
+fail: |
+ Dart_ThrowException(exception); |
+ ASSERT_NOT_REACHED(); |
+} |
+ |
+} |
+ |
+static DartNativeEntry nativeEntries[] = { |
+ { JsInteropInternal::jsObjectConstructorCallback, 2, "JsObject_constructorCallback" }, |
+ { JsInterop::jsInteropContextCallback, 0, "Js_context_Callback" }, |
+ { JsInteropInternal::finalizeJsInterfacesCallback, 0, "Js_finalizeJsInterfaces" }, |
+ { JsInteropInternal::interfacesFinalizedCallback, 0, "Js_interfacesFinalized_Callback" }, |
+ { JsInteropInternal::jsifyCallback, 1, "JsObject_jsify" }, |
+ { JsInteropInternal::withThisCallback, 1, "JsFunction_withThis" }, |
+ { JsInterop::jsInteropGetterCallback, 2, "JsObject_[]" }, |
+ { JsInteropInternal::setterCallback, 3, "JsObject_[]=" }, |
+ { JsInteropInternal::hashCodeCallback, 1, "JsObject_hashCode" }, |
+ { JsInterop::jsInteropCallMethodCallback, 3, "JsObject_callMethod" }, |
+ { JsInteropInternal::toStringCallback, 1, "JsObject_toString" }, |
+ { JsInteropInternal::identityEqualityCallback, 2, "JsObject_identityEquality" }, |
+ { JsInteropInternal::hasPropertyCallback, 2, "JsObject_hasProperty" }, |
+ { JsInteropInternal::deletePropertyCallback, 2, "JsObject_deleteProperty" }, |
+ { JsInteropInternal::instanceofCallback, 2, "JsObject_instanceof" }, |
+ { JsInteropInternal::applyCallback, 3, "JsFunction_apply" }, |
+ { JsInteropInternal::applyDebuggerOnlyCallback, 3, "JsFunction_applyDebuggerOnly" }, |
+ { JsInteropInternal::newJsArrayCallback, 0, "JsArray_newJsArray" }, |
+ { JsInteropInternal::newJsArrayFromSafeListCallback, 1, "JsArray_newJsArrayFromSafeList" }, |
+ { JsInteropInternal::jsArrayLengthCallback, 1, "JsArray_length" }, |
+ { JsInteropInternal::fromBrowserObjectCallback, 1, "JsObject_fromBrowserObject" }, |
+ { 0, 0, 0 }, |
+}; |
+ |
+Dart_NativeFunction JsInterop::resolver(Dart_Handle nameHandle, int argumentCount, bool* autoSetupScope) |
+{ |
+ ASSERT(autoSetupScope); |
+ *autoSetupScope = true; |
+ String name = DartUtilities::toString(nameHandle); |
+ |
+ for (intptr_t i = 0; nativeEntries[i].nativeFunction != 0; i++) { |
+ if (argumentCount == nativeEntries[i].argumentCount && name == nativeEntries[i].name) { |
+ return nativeEntries[i].nativeFunction; |
+ } |
+ } |
+ |
+ return 0; |
+} |
+ |
+const uint8_t* JsInterop::symbolizer(Dart_NativeFunction nf) |
+{ |
+ for (intptr_t i = 0; nativeEntries[i].nativeFunction != 0; i++) { |
+ if (nf == nativeEntries[i].nativeFunction) { |
+ return reinterpret_cast<const uint8_t*>(nativeEntries[i].name); |
+ } |
+ } |
+ return 0; |
+} |
+ |
+// Methods enabling a Dart List to emulate a JS Array. |
+static void indexedGetterArray(uint32_t index, const v8::PropertyCallbackInfo<v8::Value>& info) |
+{ |
+ DartScopes scopes(info.Holder()); |
+ Dart_Handle handle = scopes.handle; |
+ DartDOMData* domData = DartDOMData::current(); |
+ Dart_Handle ret = 0; |
+ ASSERT(Dart_IsList(handle)); |
+ ret = Dart_ListGetAt(handle, index); |
+ if (Dart_IsError(ret)) { |
+ // Return undefined if the index is invalid to match JS semantics. |
+ // TODO(jacobr): we should log to the console warning of bad use of a |
+ // Dart List. |
+ return; |
+ } |
+ setJsReturnValue(domData, info, ret); |
+} |
+ |
+static void indexedSetterArray(uint32_t index, v8::Local<v8::Value> value, const v8::PropertyCallbackInfo<v8::Value>& info) |
+{ |
+ DartScopes scopes(info.Holder()); |
+ Dart_Handle handle = scopes.handle; |
+ DartDOMData* domData = DartDOMData::current(); |
+ |
+ Dart_Handle ret = 0; |
+ ASSERT(Dart_IsList(handle)); |
+ intptr_t length = 0; |
+ ret = Dart_ListLength(handle, &length); |
+ ASSERT(!Dart_IsError(ret)); |
+ if (index >= static_cast<uint32_t>(length)) { |
+ // Approximate JS semantics by filling the list with nulls if we |
+ // attempt to add an element past the end of the list. |
+ // TODO(jacobr): ideally we would match JS semantics exactly and fill |
+ // the list with undefined. |
+ Dart_Handle args[2] = { handle, DartUtilities::unsignedToDart(index + 1)}; |
+ ret = Dart_Invoke(domData->jsLibrary(), Dart_NewStringFromCString("_arrayExtend"), 2, args); |
+ ASSERT(!Dart_IsError(ret)); |
+ } |
+ ret = Dart_ListSetAt(handle, index, DartHandleProxy::unwrapValue(value)); |
+ if (Dart_IsError(ret)) { |
+ // Return undefined if the index is invalid to match JS semantics. |
+ // TODO(jacobr): we should log to the console warning of bad use of a |
+ // Dart list or add a JS expando to the object ala JS. |
+ } |
+ setJsReturnValue(domData, info, ret); |
+} |
+ |
+static void indexedEnumeratorArray(const v8::PropertyCallbackInfo<v8::Array>& info) |
+{ |
+ DartScopes scopes(info.Holder()); |
+ Dart_Handle handle = scopes.handle; |
+ |
+ intptr_t length = 0; |
+ ASSERT(Dart_IsList(handle)); |
+ Dart_ListLength(handle, &length); |
+ |
+ v8::Isolate* v8Isolate = v8::Isolate::GetCurrent(); |
+ v8::Local<v8::Array> indexes = v8::Array::New(v8Isolate, length); |
+ for (int i = 0; i < length; i++) |
+ indexes->Set(i, v8::Integer::New(v8Isolate, i)); |
+ |
+ v8SetReturnValue(info, indexes); |
+} |
+ |
+v8::Handle<v8::Array> shallowListToV8(Dart_Handle value, DartDOMData* domData, Dart_Handle& exception) |
+{ |
+ ASSERT(Dart_IsList(value)); |
+ |
+ intptr_t length = 0; |
+ Dart_Handle result = Dart_ListLength(value, &length); |
+ if (!DartUtilities::checkResult(result, exception)) |
+ return v8::Handle<v8::Array>(); |
+ |
+ v8::Local<v8::Array> array = v8::Array::New(v8::Isolate::GetCurrent(), length); |
+ |
+ for (intptr_t i = 0; i < length; ++i) { |
+ result = Dart_ListGetAt(value, i); |
+ v8::Handle<v8::Value> v8value = JsInterop::fromDart(domData, result, exception); |
+ // TODO(jacobr): is there a better way to handle this error case? |
+ if (exception) |
+ return v8::Handle<v8::Array>(); |
+ array->Set(i, v8value); |
+ } |
+ return array; |
+} |
+ |
+// If one of the methods we added to the Dart List proxy to make it mascarade |
+// as JavaScript Array happens to get called on a JavaScript object instead of |
+// a Dart List proxy object we use the corresponding regular JavaScript Array |
+// method which will do something plausible in most cases even if the |
+// JavaScript object isn't actually a JavaScript array. The chrome devtools |
+// appear to go down exactly this code path as I got crashes in the devtools |
+// due to info.Holder not being a DartHandleProxy. I haven't investigated |
+// whether devtools would still function properly if we threw an exception |
+// which would match the behavior of DOM classes when a method is called on the |
+// wrong object. The current behavior while strange matches the behavior |
+// observed when an method from Array is called on an arbitrary JS object. |
+bool handleNonDartProxyThis(const v8::FunctionCallbackInfo<v8::Value>& info, const char* jsMethodName) |
+{ |
+ if (DartHandleProxy::isDartProxy(info.Holder())) { |
+ return false; |
+ } |
+ v8::Isolate* v8Isolate = v8::Isolate::GetCurrent(); |
+ // Get the method on JS array in an inefficient way. |
+ v8::Local<v8::Function> method = v8::Array::New(v8Isolate)->Get(v8::String::NewFromUtf8(v8Isolate, jsMethodName)).As<v8::Function>(); |
+ ASSERT(!method.IsEmpty()); |
+ int length = info.Length(); |
+ Vector<v8::Local<v8::Value> > v8Args(length); |
+ for (int i = 0; i < length; ++i) { |
+ v8Args[i] = info[i]; |
+ } |
+ v8SetReturnValue(info, method->Call(info.Holder(), length, v8Args.data())); |
+ return true; |
+} |
+ |
+void arrayHelper(const v8::FunctionCallbackInfo<v8::Value>& info, const char* methodName, const char* jsMethodName) |
+{ |
+ if (handleNonDartProxyThis(info, jsMethodName)) { |
+ return; |
+ } |
+ DartScopes scopes(info.Holder()); |
+ DartDOMData* domData = DartDOMData::current(); |
+ Dart_Handle handle = scopes.handle; |
+ Dart_Handle ret = Dart_Invoke(domData->jsLibrary(), Dart_NewStringFromCString(methodName), 1, &handle); |
+ setJsReturnValue(domData, info, ret); |
+} |
+ |
+void arrayHelper1Arg(const v8::FunctionCallbackInfo<v8::Value>& info, const char* methodName, const char* jsMethodName) |
+{ |
+ if (handleNonDartProxyThis(info, jsMethodName)) { |
+ return; |
+ } |
+ DartScopes scopes(info.Holder()); |
+ DartDOMData* domData = DartDOMData::current(); |
+ Dart_Handle handle = scopes.handle; |
+ Dart_Handle e; |
+ if (info.Length() == 0) { |
+ e = Dart_Null(); |
+ } else { |
+ e = JsInterop::toDart(info[0]); |
+ } |
+ Dart_Handle args[2] = { handle, e }; |
+ Dart_Handle ret = Dart_Invoke(domData->jsLibrary(), Dart_NewStringFromCString(methodName), 2, args); |
+ setJsReturnValue(domData, info, ret); |
+} |
+ |
+void arrayHelperWithArgsAsList(const v8::FunctionCallbackInfo<v8::Value>& info, const char* methodName, const char* jsMethodName) |
+{ |
+ if (handleNonDartProxyThis(info, jsMethodName)) { |
+ return; |
+ } |
+ DartScopes scopes(info.Holder()); |
+ DartDOMData* domData = DartDOMData::current(); |
+ Dart_Handle handle = scopes.handle; |
+ int length = info.Length(); |
+ Dart_Handle argsList = Dart_NewList(length); |
+ for (int i = 0; i < length; ++i) { |
+ Dart_ListSetAt(argsList, i, JsInterop::toDart(info[i])); |
+ } |
+ // Note: this is also just info.Holder(). |
+ Dart_Handle args[2] = { handle, argsList }; |
+ setJsReturnValue(domData, info, Dart_Invoke(domData->jsLibrary(), Dart_NewStringFromCString(methodName), 2, args)); |
+} |
+ |
+static void arrayNamedPropertyGetter(v8::Local<v8::String> name, const v8::PropertyCallbackInfo<v8::Value>& info) |
+{ |
+ if (!DartHandleProxy::isDartProxy(info.Holder())) { |
+ // I don't think this case can occur but avoid crashing if there is an |
+ // exotic way to trigger it. |
+ return; |
+ } |
+ |
+ v8::Isolate* v8Isolate = v8::Isolate::GetCurrent(); |
+ if (!name->Equals(v8::String::NewFromUtf8(v8Isolate, "length"))) |
+ return; |
+ |
+ DartScopes scopes(info.Holder()); |
+ Dart_Handle handle = scopes.handle; |
+ |
+ intptr_t length = 0; |
+ Dart_ListLength(handle, &length); |
+ v8SetReturnValueInt(info, length); |
+} |
+ |
+static void arrayNamedPropertySetter(v8::Local<v8::String> property, v8::Local<v8::Value> value, const v8::PropertyCallbackInfo<v8::Value>& info) |
+{ |
+ if (!DartHandleProxy::isDartProxy(info.Holder())) { |
+ // I don't think this case can occur but avoid crashing if there is an |
+ // exotic way to trigger it. |
+ return; |
+ } |
+ |
+ v8::Isolate* v8Isolate = v8::Isolate::GetCurrent(); |
+ if (!property->Equals(v8::String::NewFromUtf8(v8Isolate, "length"))) { |
+ return; |
+ } |
+ |
+ DartScopes scopes(info.Holder()); |
+ Dart_Handle handle = scopes.handle; |
+ DartDOMData* domData = DartDOMData::current(); |
+ Dart_Handle args[2] = { handle, JsInterop::toDart(value) }; |
+ Dart_Handle ret = Dart_Invoke(domData->jsLibrary(), Dart_NewStringFromCString("_setListLength"), 2, args); |
+ setJsReturnValue(domData, info, ret); |
+} |
+ |
+static void arrayQueryProperty(v8::Local<v8::String> name, const v8::PropertyCallbackInfo<v8::Integer>& info) |
+{ |
+ v8::Isolate* v8Isolate = v8::Isolate::GetCurrent(); |
+ if (name->Equals(v8::String::NewFromUtf8(v8Isolate, "length"))) { |
+ v8SetReturnValueInt(info, v8::DontEnum | v8::DontDelete); |
+ } |
+} |
+ |
+static void arrayDeleteProperty(v8::Local<v8::String> name, const v8::PropertyCallbackInfo<v8::Boolean>& info) |
+{ |
+ v8::Isolate* v8Isolate = v8::Isolate::GetCurrent(); |
+ if (name->Equals(v8::String::NewFromUtf8(v8Isolate, "length"))) { |
+ v8SetReturnValueBool(info, false); |
+ } |
+} |
+ |
+void arrayToStringCallback(const v8::FunctionCallbackInfo<v8::Value>& info) |
+{ |
+ arrayHelper(info, "_arrayToString", "toString"); |
+} |
+ |
+ |
+void arrayJoinCallback(const v8::FunctionCallbackInfo<v8::Value>& info) |
+{ |
+ arrayHelper1Arg(info, "_arrayJoin", "join"); |
+} |
+ |
+void arrayPushCallback(const v8::FunctionCallbackInfo<v8::Value>& info) |
+{ |
+ arrayHelperWithArgsAsList(info, "_arrayPush", "push"); |
+} |
+ |
+void arrayPopCallback(const v8::FunctionCallbackInfo<v8::Value>& info) |
+{ |
+ arrayHelper(info, "_arrayPop", "pop"); |
+} |
+ |
+void concatCallback(const v8::FunctionCallbackInfo<v8::Value>& info) |
+{ |
+ arrayHelperWithArgsAsList(info, "_arrayConcat", "concat"); |
+} |
+ |
+void arrayReverseCallback(const v8::FunctionCallbackInfo<v8::Value>& info) |
+{ |
+ arrayHelper(info, "_arrayReverse", "reverse"); |
+} |
+ |
+void arrayShiftCallback(const v8::FunctionCallbackInfo<v8::Value>& info) |
+{ |
+ arrayHelper(info, "_arrayShift", "shift"); |
+} |
+ |
+void arrayUnshiftCallback(const v8::FunctionCallbackInfo<v8::Value>& info) |
+{ |
+ arrayHelperWithArgsAsList(info, "_arrayUnshift", "unshift"); |
+} |
+ |
+void arraySpliceCallback(const v8::FunctionCallbackInfo<v8::Value>& info) |
+{ |
+ arrayHelperWithArgsAsList(info, "_arraySplice", "splice"); |
+} |
+ |
+void toJsArrayCallback(const v8::FunctionCallbackInfo<v8::Value>& info) |
+{ |
+ // This isn't a method on the JavaScript Array class so it is fine to |
+ // just return the existing hopefully Array like JS object if not called on |
+ // a Dart List. |
+ if (!DartHandleProxy::isDartProxy(info.Holder())) { |
+ v8SetReturnValue(info, info.Holder()); |
+ } |
+ DartScopes scopes(info.Holder()); |
+ DartDOMData* domData = DartDOMData::current(); |
+ Dart_Handle handle = scopes.handle; |
+ Dart_Handle exception = 0; |
+ v8::Local<v8::Array> v8Array = shallowListToV8(handle, domData, exception); |
+ ASSERT(!exception); |
+ v8SetReturnValue(info, v8Array); |
+} |
+ |
+void arraySortCallback(const v8::FunctionCallbackInfo<v8::Value>& info) |
+{ |
+ // TODO(jacobr): consider using the JavaScript sort method instead. |
+ arrayHelper1Arg(info, "_arraySort", "sort"); |
+} |
+ |
+v8::Local<v8::FunctionTemplate> dartListTemplate() |
+{ |
+ DEFINE_STATIC_LOCAL(v8::Persistent<v8::FunctionTemplate>, proxyTemplate, ()); |
+ v8::Local<v8::FunctionTemplate> proxyTemplateLocal; |
+ v8::Isolate* v8Isolate = v8::Isolate::GetCurrent(); |
+ if (proxyTemplate.IsEmpty()) { |
+ proxyTemplate.Reset(v8::Isolate::GetCurrent(), v8::FunctionTemplate::New(v8Isolate)); |
+ proxyTemplateLocal = v8::Local<v8::FunctionTemplate>::New(v8Isolate, proxyTemplate); |
+ // Set to Array because we want these instances to appear to be |
+ // JavaScript arrays as far as user code is concerned. |
+ proxyTemplateLocal->SetClassName(v8::String::NewFromUtf8(v8Isolate, "Array")); |
+ // Hack to set the prototype to be the prototype of an actual JavaScript Array. |
+ |
+ v8::Local<v8::ObjectTemplate> protoTemplate = proxyTemplateLocal->PrototypeTemplate(); |
+ |
+ protoTemplate ->Set(v8::String::NewFromUtf8(v8Isolate, "toString"), v8::FunctionTemplate::New(v8Isolate, arrayToStringCallback), v8::DontEnum); |
+ protoTemplate ->Set(v8::String::NewFromUtf8(v8Isolate, "join"), v8::FunctionTemplate::New(v8Isolate, arrayJoinCallback), v8::DontEnum); |
+ protoTemplate ->Set(v8::String::NewFromUtf8(v8Isolate, "push"), v8::FunctionTemplate::New(v8Isolate, arrayPushCallback), v8::DontEnum); |
+ protoTemplate ->Set(v8::String::NewFromUtf8(v8Isolate, "pop"), v8::FunctionTemplate::New(v8Isolate, arrayPopCallback), v8::DontEnum); |
+ protoTemplate ->Set(v8::String::NewFromUtf8(v8Isolate, "concat"), v8::FunctionTemplate::New(v8Isolate, concatCallback), v8::DontEnum); |
+ protoTemplate ->Set(v8::String::NewFromUtf8(v8Isolate, "reverse"), v8::FunctionTemplate::New(v8Isolate, arrayReverseCallback), v8::DontEnum); |
+ |
+ protoTemplate ->Set(v8::String::NewFromUtf8(v8Isolate, "shift"), v8::FunctionTemplate::New(v8Isolate, arrayShiftCallback), v8::DontEnum); |
+ protoTemplate ->Set(v8::String::NewFromUtf8(v8Isolate, "unshift"), v8::FunctionTemplate::New(v8Isolate, arrayUnshiftCallback), v8::DontEnum); |
+ protoTemplate ->Set(v8::String::NewFromUtf8(v8Isolate, "splice"), v8::FunctionTemplate::New(v8Isolate, arraySpliceCallback), v8::DontEnum); |
+ protoTemplate ->Set(v8::String::NewFromUtf8(v8Isolate, "sort"), v8::FunctionTemplate::New(v8Isolate, arraySortCallback), v8::DontEnum); |
+ protoTemplate ->Set(v8::String::NewFromUtf8(v8Isolate, "$toJsArray"), v8::FunctionTemplate::New(v8Isolate, toJsArrayCallback), v8::DontEnum); |
+ |
+ // ES6 experimental properties not currently supported that we could support if needed. |
+ // These would require building separate live proxy objects. |
+ // "entries", |
+ // "values", |
+ // "keys" |
+ |
+ v8::Local<v8::ObjectTemplate> instanceTemplate = setupInstanceTemplate(proxyTemplateLocal); |
+ instanceTemplate->SetIndexedPropertyHandler(&indexedGetterArray, &indexedSetterArray, 0, 0, &indexedEnumeratorArray); |
+ instanceTemplate->SetNamedPropertyHandler(&arrayNamedPropertyGetter, &arrayNamedPropertySetter, &arrayQueryProperty, &arrayDeleteProperty, 0); |
+ } else { |
+ proxyTemplateLocal = v8::Local<v8::FunctionTemplate>::New(v8Isolate, proxyTemplate); |
+ } |
+ return proxyTemplateLocal; |
+} |
+ |
+void JsInterop::jsInteropGetterCallback(Dart_NativeArguments args) |
+{ |
+ JsInteropInternal::getterCallback(args); |
+} |
+ |
+void JsInterop::jsInteropCallMethodCallback(Dart_NativeArguments args) |
+{ |
+ JsInteropInternal::callMethodCallback(args); |
+} |
+ |
+void JsInterop::jsInteropContextCallback(Dart_NativeArguments args) |
+{ |
+ return JsInteropInternal::contextCallback(args); |
+} |
+ |
+} |