| /* |
| * 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/CurrentTime.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; |
| 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. |
| // TODO(jacobr): this will also pose a problem for JavaScript instanceof Blob checks. |
| " Blob = function(array, options) {" |
| " if (arguments.length < 2) return new blob(makeSafeArg(array));" |
| " else return new blob(makeSafeArg(array), options);" |
| " };" |
| " Blob.prototype = blob.prototype;" |
| "" |
| |
| // 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(v8::Isolate*); |
| static v8::Local<v8::FunctionTemplate> dartObjectTemplate(v8::Isolate*); |
| static v8::Local<v8::FunctionTemplate> dartListTemplate(v8::Isolate*); |
| |
| // 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) |
| { |
| auto v8Isolate = domData->v8Isolate(); |
| if (Dart_IsError(result)) { |
| 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)), v8Isolate); |
| return; |
| } |
| v8SetReturnValue(info, ret); |
| } |
| } |
| |
| template<typename CallbackInfo> |
| void setJsReturnValueTyped(DartDOMData* domData, CallbackInfo info, Dart_Handle result) |
| { |
| auto v8Isolate = domData->v8Isolate(); |
| if (Dart_IsError(result)) { |
| V8ThrowException::throwException(v8::String::NewFromUtf8(v8Isolate, Dart_GetError(result)), v8Isolate); |
| } else { |
| Dart_Handle exception = 0; |
| v8::Local<v8::Value> ret = JsInterop::fromDartTyped(domData, result, exception); |
| if (exception) { |
| V8ThrowException::throwException(V8Converter::stringToV8(Dart_ToString(exception)), v8Isolate); |
| return; |
| } |
| v8SetReturnValue(info, ret); |
| } |
| } |
| |
| // Function invocation callback that exactly matches the old dart:js interop semantics. |
| 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], domData, true)); |
| } |
| |
| 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), domData, true)); |
| } |
| |
| setJsReturnValue(domData, args, Dart_InvokeClosure(handle, dartFunctionArgs.size(), dartFunctionArgs.data())); |
| } |
| |
| // Function invocation callback that matches the package:js typed interop semantics |
| // and dart:html callback passing semantics. |
| static void typedFunctionInvocationCallback(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::toDartTyped(args[0], domData)); |
| } |
| |
| 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::toDartTyped(argsList->Get(i), domData)); |
| } |
| |
| setJsReturnValueTyped(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(v8::Isolate* v8Isolate) |
| { |
| DEFINE_STATIC_LOCAL(v8::Persistent<v8::FunctionTemplate>, proxyTemplate, ()); |
| v8::Local<v8::FunctionTemplate> proxyTemplateLocal; |
| if (proxyTemplate.IsEmpty()) { |
| proxyTemplate.Reset(v8Isolate, 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> typedDartFunctionTemplate(v8::Isolate* v8Isolate) |
| { |
| DEFINE_STATIC_LOCAL(v8::Persistent<v8::FunctionTemplate>, proxyTemplate, ()); |
| v8::Local<v8::FunctionTemplate> proxyTemplateLocal; |
| if (proxyTemplate.IsEmpty()) { |
| proxyTemplate.Reset(v8Isolate, v8::FunctionTemplate::New(v8Isolate)); |
| proxyTemplateLocal = v8::Local<v8::FunctionTemplate>::New(v8Isolate, proxyTemplate); |
| v8::Local<v8::ObjectTemplate> instanceTemplate = setupInstanceTemplate(proxyTemplateLocal); |
| |
| instanceTemplate->SetCallAsFunctionHandler(&typedFunctionInvocationCallback); |
| } 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, v8::Local<v8::Context> context) |
| : args(args) |
| , v8Scope(context) |
| { |
| 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); |
| } |
| |
| // Here and elsewhere, legacy indicates that we need to match the painful |
| // to support legacy dart:js semantics where EventTarget and a few other |
| // classes are passed as dart:html objects while other classes are passed |
| // as JsObject, JsArray, etc. |
| void setReturnValue(v8::Local<v8::Value> ret, DartDOMData* data, bool legacy) |
| { |
| ASSERT(!tryCatch.HasCaught()); |
| Dart_SetReturnValue(args, JsInterop::toDart(ret, data, legacy)); |
| ASSERT(!tryCatch.HasCaught()); |
| } |
| |
| void setReturnValueTyped(v8::Local<v8::Object> ret, InterceptorData* interceptorData, DartDOMData* data) |
| { |
| ASSERT(!tryCatch.HasCaught()); |
| Dart_SetReturnValue(args, JsObject::toDartTyped(ret, data, interceptorData)); |
| ASSERT(!tryCatch.HasCaught()); |
| } |
| |
| void setReturnValueTyped(v8::Local<v8::Value> ret, DartDOMData* data) |
| { |
| ASSERT(!tryCatch.HasCaught()); |
| Dart_SetReturnValue(args, JsInterop::toDartTyped(ret, data)); |
| ASSERT(!tryCatch.HasCaught()); |
| } |
| |
| void setReturnValueProbablyNative(v8::Local<v8::Object> ret) |
| { |
| ASSERT(!tryCatch.HasCaught()); |
| Dart_SetReturnValue(args, JsInterop::toDartProbablyNative(ret)); |
| ASSERT(!tryCatch.HasCaught()); |
| } |
| |
| void setReturnValueInteger(int64_t ret) |
| { |
| ASSERT(!tryCatch.HasCaught()); |
| Dart_SetIntegerReturnValue(args, ret); |
| } |
| |
| |
| void setReturnValueString(const AtomicString& ret) |
| { |
| ASSERT(!tryCatch.HasCaught()); |
| DartUtilities::setDartStringReturnValue(args, ret); |
| } |
| }; |
| |
| PassRefPtr<JsObject> JsObject::create(v8::Local<v8::Object> v8Handle, v8::Isolate* v8Isolate) |
| { |
| return adoptRef(new JsObject(v8Handle, v8Isolate)); |
| } |
| |
| v8::Local<v8::Function> toTypedDartFunction(Dart_Handle function, DartDOMData* domData, v8::Isolate* v8Isolate) { |
| ASSERT(DartUtilities::isFunction(domData, function)); |
| void* peer = 0; |
| Dart_Handle result = Dart_GetPeer(function, &peer); |
| ALLOW_UNUSED_LOCAL(result); |
| ASSERT(!Dart_IsError(result)); |
| v8::Persistent<v8::Function>* existingFunction = static_cast<v8::Persistent<v8::Function>*>(peer); |
| if (existingFunction) { |
| return v8::Local<v8::Function>::New(v8Isolate, *existingFunction); |
| } |
| |
| v8::Local<v8::Object> functionProxy = dartFunctionTemplate(v8Isolate)->InstanceTemplate()->NewInstance(); |
| DartHandleProxy::writePointerToProxy(functionProxy, function); |
| // The raw functionProxy doesn't behave enough like a true JS function |
| // so we wrap it in a true JS function. |
| v8::Local<v8::Function> object = domData->jsInteropData()->wrapDartFunction(v8Isolate)->Call(functionProxy, 0, 0).As<v8::Function>(); |
| |
| // TODO(jacobr): it is really ugly but required as we can't use the DartHandleProxy directly. |
| Dart_PersistentHandle persistent = Dart_NewPersistentHandle(function); |
| InteropDartHandles* cache = domData->m_dartHandles.get(object, v8Isolate); |
| cache->m_typedInterop = persistent; |
| cache->m_rawInterop = persistent; |
| cache->m_legacyInteropMode = TypedInterop; // Doesn't matter. |
| |
| v8::Local<v8::Object> proxy = typedDartFunctionTemplate(v8Isolate)->InstanceTemplate()->NewInstance(); |
| DartHandleProxy::writePointerToProxy(proxy, function); |
| |
| v8::Local<v8::Function> ret = v8::Local<v8::Function>::Cast(domData->jsInteropData()->wrapDartFunction(v8Isolate)->Call(proxy, 0, 0)); |
| existingFunction = new v8::Persistent<v8::Function>(); |
| existingFunction->Reset(v8Isolate, ret); |
| Dart_SetPeer(function, (void*)existingFunction); |
| return ret; |
| } |
| |
| v8::Local<v8::Value> fromDartHelper(DartDOMData* domData, Dart_Handle handle, Dart_Handle& exception, bool typed) |
| { |
| auto v8Isolate = domData->v8Isolate(); |
| v8::Handle<v8::Value> value = V8Converter::toV8IfPrimitive(domData, handle, exception); |
| if (!value.IsEmpty() || exception) |
| return value; |
| |
| value = V8Converter::toV8IfBrowserNative(domData, handle, exception); |
| if (!value.IsEmpty() || exception) |
| return value; |
| |
| if (DartDOMWrapper::subtypeOf(handle, JsObject::dartClassId)) { |
| JsObject* object = DartDOMWrapper::unwrapDartWrapper<JsObject>(domData, handle, exception); |
| if (exception) |
| return v8::Local<v8::Value>(); |
| return object->localV8Object(v8Isolate); |
| } |
| |
| if (DartUtilities::isFunction(domData, handle)) { |
| if (typed) { |
| return toTypedDartFunction(handle, domData, v8Isolate); |
| } else { |
| v8::Local<v8::Object> functionProxy = dartFunctionTemplate(v8Isolate)->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. |
| v8::Local<v8::Function> object = domData->jsInteropData()->wrapDartFunction(v8Isolate)->Call(functionProxy, 0, 0).As<v8::Function>(); |
| |
| // TODO(jacobr): it is really ugly but required as we can't use the DartHandleProxy directly. |
| InteropDartHandles* cache = domData->m_dartHandles.get(object, v8Isolate); |
| Dart_PersistentHandle persistent = Dart_NewPersistentHandle(handle); |
| cache->m_typedInterop = persistent; |
| cache->m_rawInterop = persistent; |
| cache->m_legacyInteropMode = RawInterop; |
| return object; |
| } |
| } |
| |
| 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(v8Isolate)->InstanceTemplate()->NewInstance(); |
| } else { |
| proxy = dartObjectTemplate(v8Isolate)->InstanceTemplate()->NewInstance(); |
| } |
| DartHandleProxy::writePointerToProxy(proxy, handle); |
| proxy->SetHiddenValue(v8::String::NewFromUtf8(v8Isolate, "dartProxy"), v8::Boolean::New(v8Isolate, true)); |
| |
| return proxy; |
| } |
| |
| v8::Local<v8::Value> JsInterop::fromDartTyped(DartDOMData* domData, Dart_Handle handle, Dart_Handle& exception) |
| { |
| return fromDartHelper(domData, handle, exception, true); |
| } |
| |
| v8::Local<v8::Value> JsInterop::fromDart(DartDOMData* domData, Dart_Handle handle, Dart_Handle& exception) |
| { |
| return fromDartHelper(domData, handle, exception, false); |
| } |
| |
| JsObject::JsObject(v8::Local<v8::Object> v8Handle, v8::Isolate* isolate) |
| { |
| v8::Persistent<v8::Object> persistentHandle; |
| v8Object.Reset(isolate, v8Handle); |
| m_cachedNode = 0; |
| } |
| |
| v8::Local<v8::Object> JsObject::localV8Object(v8::Isolate* v8Isolate) |
| { |
| return v8::Local<v8::Object>::New(v8Isolate, v8Object); |
| } |
| |
| Node* JsObject::toNode(v8::Isolate* v8Isolate) { |
| if (m_cachedNode) return m_cachedNode; |
| m_cachedNode = V8Node::toImplWithTypeCheck(v8Isolate, localV8Object(v8Isolate)); |
| // TODO(jacobr): we don't optimize for cases where the type check fails. |
| return m_cachedNode; |
| } |
| |
| Element* JsObject::toElement(v8::Isolate* v8Isolate) { |
| Node* node = toNode(v8Isolate); |
| return node && node->isElementNode() ? (Element*)m_cachedNode : 0; |
| } |
| |
| Dart_Handle JsInterop::toDart(v8::Local<v8::Value> v8Handle, DartDOMData* domData, bool legacy) |
| { |
| 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, domData, legacy); |
| } |
| |
| Dart_Handle JsInterop::toDartTyped(v8::Local<v8::Value> v8Handle, DartDOMData* domData) |
| { |
| Dart_Handle handle = V8Converter::toDartIfPrimitive(v8Handle); |
| if (handle) |
| return handle; |
| |
| ASSERT(v8Handle->IsObject()); |
| v8::Handle<v8::Object> object = v8Handle.As<v8::Object>(); |
| Dart_Handle exception = 0; |
| handle = V8Converter::toDartIfBrowserNative(object, domData->v8Isolate(), exception); |
| ASSERT(!exception); |
| if (handle) |
| return handle; |
| // 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::toDartTyped(object, domData); |
| } |
| |
| Dart_Handle JsObject::toDart(v8::Local<v8::Object> object, DartDOMData* domData, bool legacy) |
| { |
| return toDart(object, domData, domData->m_dartHandles.get(object, domData->v8Isolate()), legacy); |
| } |
| |
| // Contract for this method is it assumes any implicit conversions such as DateTime have already been performed. |
| Dart_Handle JsObject::toDart(v8::Local<v8::Object> object, DartDOMData* domData, InteropDartHandles* cache, bool legacy) |
| { |
| Dart_Handle wrapper; |
| auto v8Isolate = domData->v8Isolate(); |
| if (object->IsFunction()) { |
| if (cache->m_rawInterop) return cache->m_rawInterop; |
| RefPtr<JsFunction> jsFunction = JsFunction::create(object.As<v8::Function>(), v8Isolate); |
| wrapper = JsFunction::toDart(jsFunction); |
| cache->m_legacyInteropMode = RawInterop; |
| } else if (object->IsArray() |
| // Check for Dart List objects from different Dart isolates. |
| // In dart2js the List from a different isolate would just be a regular |
| // JS Array so it can be treated as a JS Array. |
| || dartListTemplate(v8Isolate)->HasInstance(object)) { |
| |
| if (cache->m_rawInterop) return cache->m_rawInterop; |
| RefPtr<JsArray> jsArray = JsArray::create(object, v8Isolate); |
| wrapper = JsArray::toDart(jsArray); |
| cache->m_legacyInteropMode = RawInterop; |
| } else { |
| if (legacy) { |
| InterceptorData* interceptor = 0; |
| if (cache->m_legacyInteropMode == UnknownLegacyMode) { |
| interceptor = computeInterceptor(object, domData, cache); |
| } |
| if (cache->m_legacyInteropMode == TypedInterop) { |
| return toDartTyped(object, domData, interceptor, cache); |
| } |
| } |
| if (cache->m_rawInterop) return cache->m_rawInterop; |
| RefPtr<JsObject> jsObject = JsObject::create(object, v8Isolate); |
| wrapper = JsObject::toDart(jsObject, domData); |
| } |
| |
| v8::Local<v8::Object> proxy; |
| |
| ASSERT(Dart_IsInstance(wrapper)); |
| // 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. |
| cache->m_rawInterop = Dart_NewPersistentHandle(wrapper); |
| return wrapper; |
| } |
| |
| Dart_Handle JsObject::toDartTyped(v8::Local<v8::Object> object, DartDOMData* domData) |
| { |
| InteropDartHandles* cache = domData->m_dartHandles.get(object, domData->v8Isolate()); |
| return toDartTyped(object, domData, cache); |
| } |
| |
| Dart_Handle JsObject::toDartTyped(v8::Local<v8::Object> object, DartDOMData* domData, InteropDartHandles* cache) { |
| return toDartTyped(object, domData, 0, cache); |
| } |
| |
| InterceptorData* JsObject::computeInterceptor(v8::Local<v8::Object> object, DartDOMData* domData, InteropDartHandles* cache) { |
| v8::Local<v8::Value> proto = object->GetPrototype(); |
| auto v8Isolate = domData->v8Isolate(); |
| bool cachedInterceptor = proto->IsObject(); |
| InterceptorData* interceptor = cachedInterceptor ? |
| domData->m_interceptors.get(proto.As<v8::Object>(), v8Isolate) : |
| // We can't actually cache the interceptor for this case. |
| // We currently cache on the instance but that is pretty pointless. |
| // TODO(jacobr): we should track to make sure this case isn't common |
| domData->m_interceptors.get(object, v8Isolate); |
| |
| if (interceptor->isNotEmpty()) { |
| cache->m_legacyInteropMode = interceptor->m_legacyInteropMode; |
| return interceptor; |
| } |
| |
| if (object->IsFunction() || object->IsArray() || dartListTemplate(v8Isolate)->HasInstance(object)) { |
| cache->m_legacyInteropMode = RawInterop; |
| interceptor->m_legacyInteropMode = RawInterop; |
| } else { |
| // Need to determine the Dart type before we can create the right wrapper. |
| // This is really inefficient.... We should really just pass the proto or something |
| // and for this case we really don't need to be leaking tons of memory... oh well. |
| Dart_Handle rawWrapper = JsObject::toDart(object, domData, cache, false); |
| bool isCrossFrame = !object->CreationContext()->Global()->StrictEquals(domData->v8Global()); |
| Element* element = V8Element::toImplWithTypeCheck(v8Isolate, object); |
| bool isElement = element && element->executionContext(); |
| Dart_Handle args[3] = { rawWrapper, Dart_NewBoolean(isCrossFrame), Dart_NewBoolean(isElement) }; |
| Dart_Handle ret = invokeTopLevelJsInteropMethod(DartDOMData::current(), "_lookupType", 3, args); |
| ASSERT(Dart_IsList(ret)); |
| Dart_Handle type = Dart_ListGetAt(ret, 0); |
| ASSERT(Dart_IsType(type)); |
| ASSERT(!Dart_IsError(type)); |
| Dart_Handle legacyInteropConvertToNativeHandle = Dart_ListGetAt(ret, 1); |
| ASSERT(Dart_IsBoolean(legacyInteropConvertToNativeHandle)); |
| bool legacyInteropConvertToNative = false; |
| Dart_BooleanValue(legacyInteropConvertToNativeHandle, &legacyInteropConvertToNative); |
| interceptor->m_legacyInteropMode = legacyInteropConvertToNative ? TypedInterop : RawInterop; |
| cache->m_legacyInteropMode = interceptor->m_legacyInteropMode; |
| interceptor->m_type = Dart_NewPersistentHandle(type); |
| } |
| return interceptor; |
| } |
| |
| // Only call this method if you are confident that the v8 Object does not have an existing |
| // Dart wrapper. For example, the result of calling document.createElement, etc. |
| Dart_Handle JsObject::toDartTyped(v8::Local<v8::Object> object, DartDOMData* domData, InterceptorData* interceptor, InteropDartHandles* cache) |
| { |
| Dart_Handle wrapper; |
| if (cache->m_typedInterop) return cache->m_typedInterop; |
| |
| // Depending on the frequency of Function and Array we should check them |
| // first. |
| auto v8Isolate = domData->v8Isolate(); |
| if (!interceptor) { |
| interceptor = computeInterceptor(object, domData, cache); |
| } |
| |
| if (interceptor->m_type) { |
| return createTyped(object, domData, interceptor, cache); |
| } |
| |
| // TODO(jacobr): these checks for IsFunction and IsArray are really unfortunate. |
| // figure out how to remove them. |
| if (object->IsFunction()) { |
| cache->m_legacyInteropMode = RawInterop; |
| RefPtr<JSFunction> jsFunction = JSFunction::create(object.As<v8::Function>(), v8Isolate); |
| wrapper = JSFunction::toDart(jsFunction); |
| } else { |
| ASSERT(object->IsArray() |
| // Check for Dart List objects from different Dart isolates. |
| // In dart2js the List from a different isolate would just be a regular |
| // JS Array so it can be treated as a JS Array. |
| || dartListTemplate(v8Isolate)->HasInstance(object)); |
| cache->m_legacyInteropMode = RawInterop; |
| RefPtr<JSArray> jsArray = JSArray::create(object, v8Isolate); |
| wrapper = JSArray::toDart(jsArray); |
| } |
| |
| ASSERT(Dart_IsInstance(wrapper)); |
| // 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. |
| |
| cache->m_typedInterop = Dart_NewPersistentHandle(wrapper); |
| return wrapper; |
| } |
| |
| Dart_Handle JsObject::toDartTyped(v8::Local<v8::Object> object, DartDOMData* domData, InterceptorData* interceptorData) |
| { |
| ASSERT(interceptorData && interceptorData->m_type); |
| auto v8Isolate = domData->v8Isolate(); |
| InteropDartHandles* cache = domData->m_dartHandles.get(object, v8Isolate); |
| |
| if (cache->m_typedInterop) return cache->m_typedInterop; |
| return JsObject::createTyped(object, domData, interceptorData, cache); |
| } |
| |
| Dart_Handle JsObject::createTyped(v8::Local<v8::Object> object, DartDOMData* domData, InterceptorData* interceptorData, InteropDartHandles* cache) |
| { |
| auto v8Isolate = domData->v8Isolate(); |
| RefPtr<JsObject> jsObject = JsObject::create(object, v8Isolate); |
| ASSERT(interceptorData->m_type); |
| Dart_Handle wrapper = DartDOMWrapper::createWrapperForType<JsObject>(domData, jsObject.get(), JsObject::dartClassId, interceptorData->m_type); |
| |
| if (interceptorData->m_isCustomElement) { |
| Dart_Handle ret = Dart_InvokeConstructor(wrapper, Dart_NewStringFromCString("created"), 0, 0); |
| if (Dart_IsError(ret)) { |
| DartUtilities::reportProblem(domData->scriptExecutionContext(), ret); |
| |
| if (!domData->htmlElementType()) { |
| Dart_Handle htmlElementType = Dart_GetType(domData->htmlLibrary(), Dart_NewStringFromCString("HtmlElementImpl"), 0, 0); |
| RELEASE_ASSERT(!Dart_IsError(htmlElementType)); |
| domData->setHtmlElementType(Dart_NewPersistentHandle(htmlElementType)); |
| } |
| |
| // We need a fresh JsObject to create a new DOM wrapper. |
| jsObject = JsObject::create(object, v8Isolate); |
| wrapper = DartDOMWrapper::createWrapperForType<JsObject>(domData, jsObject.get(), JsObject::dartClassId, domData->htmlElementType()); |
| } |
| } |
| |
| ASSERT(Dart_IsInstance(wrapper)); |
| // 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. |
| cache->m_typedInterop = Dart_NewPersistentHandle(wrapper); |
| return wrapper; |
| } |
| |
| void JsInterop::buildInteropPatchFiles(DartDOMData* domData, Vector<InteropPatchFile>* patches, Dart_Handle& exception) |
| { |
| // TODO(terry): startTime used for elapsed time perf debugging. |
| double startTime = currentTimeMS(); |
| ALLOW_UNUSED_LOCAL(startTime); |
| |
| Dart_Handle libraries = Dart_NewList(0); |
| |
| // Build patch files implementing all external methods specified with new |
| // style JS interop and JsObjectImpl, JsFunctionImpl, and JsArrayImpl |
| // classes that implement all Dart types annoted with @Js. |
| // 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 externals = invokeTopLevelJsInteropMethod(domData, "_generateInteropPatchFiles", 1, &libraries); |
| if (Dart_IsError(externals)) { |
| exception = externals; |
| return; |
| } |
| |
| // TODO(terry): Elapsed time perf debugging. |
| double endTime = currentTimeMS(); |
| ALLOW_UNUSED_LOCAL(endTime); |
| |
| ASSERT(Dart_IsList(externals)); |
| intptr_t externalsLength = 0; |
| Dart_ListLength(externals, &externalsLength); |
| ASSERT(externalsLength % 3 == 0); |
| |
| #if 0 |
| // TODO(terry): Debug logging reports speed of patch files generation. |
| // Log speed of generating the patch files. |
| intptr_t librariesLength = 0; |
| |
| // Number of total libraries handled statistics. |
| libraries = Dart_GetLibraryIds(); |
| ASSERT(Dart_IsList(libraries)); |
| Dart_ListLength(libraries, &librariesLength); |
| |
| fprintf(stderr, "\nGenerated %li interop patch files from %li libraries in %f ms.\n\n", |
| externalsLength / 3, librariesLength, endTime - startTime); |
| #endif |
| |
| for (intptr_t i = 0; i < externalsLength; i += 3) { |
| InteropPatchFile patch; |
| |
| Dart_Handle libraryUri = Dart_ListGetAt(externals, i); |
| Dart_Handle patchFileUri = Dart_ListGetAt(externals, i + 1); |
| Dart_Handle source = Dart_ListGetAt(externals, i + 2); |
| ASSERT(Dart_IsString(libraryUri)); |
| ASSERT(Dart_IsString(patchFileUri)); |
| ASSERT(Dart_IsString(source)); |
| patch.libraryUri = DartUtilities::toString(libraryUri); |
| patch.patchFileUri = DartUtilities::toString(patchFileUri); |
| patch.source = DartUtilities::toString(source); |
| patches->append(patch); |
| |
| #if 0 |
| // TODO(terry): Debug logging to see each patch file generated. |
| // Log the patch files. |
| CString libraryName = CString(reinterpret_cast<const char*>(patch.libraryUri.characters8()), patch.libraryUri.length()); |
| fprintf(stderr, " Patch %li %s\n", i / 3, libraryName.data()); |
| #endif |
| } |
| } |
| |
| void JsInterop::initializeJsInterop(DartDOMData* domData, const Vector<InteropPatchFile>& patches, Dart_Handle& exception) |
| { |
| 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 ret; |
| for (size_t i = 0; i < patches.size(); ++i) { |
| const InteropPatchFile& patch = patches[i]; |
| Dart_Handle library = Dart_LookupLibrary(DartUtilities::safeStringToDartString(patch.libraryUri)); |
| ASSERT(Dart_IsLibrary(library)); |
| Dart_Handle patchFileUri = DartUtilities::safeStringToDartString(patch.patchFileUri); |
| Dart_Handle source = DartUtilities::safeStringToDartString(patch.source); |
| ret = Dart_LibraryLoadPatch(library, patchFileUri, source); |
| if (Dart_IsError(ret)) { |
| exception = ret; |
| return; |
| } |
| } |
| ret = Dart_FinalizeLoading(false); |
| if (Dart_IsError(ret)) { |
| exception = ret; |
| return; |
| } |
| |
| interopData->setJsObjectImplDefined(); |
| |
| if (domData->isDOMEnabled()) { |
| // 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. |
| auto v8Isolate = domData->v8Isolate(); |
| |
| v8::Local<v8::Function> dartArrayConstructor = dartListTemplate(v8Isolate)->GetFunction(); |
| |
| domData->v8Global()->Set(v8::String::NewFromUtf8(v8Isolate, "$DartList"), |
| dartArrayConstructor); |
| V8ScriptRunner::compileAndRunInternalScript(v8::String::NewFromUtf8(v8Isolate, dartArrayPolyfill), v8Isolate); |
| |
| ret = Dart_Invoke(domData->jsLibrary(), Dart_NewStringFromCString("_registerAllJsInterfaces"), 0, 0); |
| if (Dart_IsError(ret)) { |
| exception = ret; |
| return; |
| } |
| } |
| } |
| |
| Dart_Handle JsObject::toDart(PassRefPtr<JsObject> jsObject, DartDOMData* domData) |
| { |
| return DartDOMWrapper::createWrapper<JsObject>(domData, jsObject.get(), JsObject::dartClassId); |
| } |
| |
| JsObject::~JsObject() |
| { |
| v8Object.Reset(); |
| } |
| |
| // JsFunction |
| Dart_Handle JsFunction::toDart(PassRefPtr<JsFunction> jsFunction) |
| { |
| DartDOMData* domData = DartDOMData::current(); |
| return DartDOMWrapper::createWrapper<JsFunction>(domData, jsFunction.get(), JsFunction::dartClassId); |
| } |
| |
| JsFunction::JsFunction(v8::Local<v8::Function> v8Handle, v8::Isolate* v8Isolate) : JsObject(v8Handle, v8Isolate) { } |
| |
| PassRefPtr<JsFunction> JsFunction::create(v8::Local<v8::Function> v8Handle, v8::Isolate* v8Isolate) |
| { |
| return adoptRef(new JsFunction(v8Handle, v8Isolate)); |
| } |
| |
| // JSFunction |
| Dart_Handle JSFunction::toDart(PassRefPtr<JSFunction> jsFunction) |
| { |
| DartDOMData* domData = DartDOMData::current(); |
| return DartDOMWrapper::createWrapper<JSFunction>(domData, jsFunction.get(), JSFunction::dartClassId); |
| } |
| |
| JSFunction::JSFunction(v8::Local<v8::Function> v8Handle, v8::Isolate* v8Isolate) : JsObject(v8Handle, v8Isolate) { } |
| |
| PassRefPtr<JSFunction> JSFunction::create(v8::Local<v8::Function> v8Handle, v8::Isolate* v8Isolate) |
| { |
| return adoptRef(new JSFunction(v8Handle, v8Isolate)); |
| } |
| |
| // JsArray |
| Dart_Handle JsArray::toDart(PassRefPtr<JsArray> jsArray) |
| { |
| DartDOMData* domData = DartDOMData::current(); |
| return DartDOMWrapper::createWrapper<JsArray>(domData, jsArray.get(), JsArray::dartClassId); |
| } |
| |
| JsArray::JsArray(v8::Local<v8::Object> v8Handle, v8::Isolate* v8Isolate) : JsObject(v8Handle, v8Isolate) { } |
| |
| PassRefPtr<JsArray> JsArray::create(v8::Local<v8::Object> v8Handle, v8::Isolate* v8Isolate) |
| { |
| return adoptRef(new JsArray(v8Handle, v8Isolate)); |
| } |
| |
| // JSArray |
| Dart_Handle JSArray::toDart(PassRefPtr<JSArray> jsArray) |
| { |
| DartDOMData* domData = DartDOMData::current(); |
| return DartDOMWrapper::createWrapper<JSArray>(domData, jsArray.get(), JSArray::dartClassId); |
| } |
| |
| JSArray::JSArray(v8::Local<v8::Object> v8Handle, v8::Isolate* v8Isolate) : JsObject(v8Handle, v8Isolate) { } |
| |
| PassRefPtr<JSArray> JSArray::create(v8::Local<v8::Object> v8Handle, v8::Isolate* v8Isolate) |
| { |
| return adoptRef(new JSArray(v8Handle, v8Isolate)); |
| } |
| |
| namespace JsInteropInternal { |
| |
| JsObject* toJsObject(Dart_NativeArguments args, int index, Dart_Handle& exception) |
| { |
| JsObject* result = DartDOMWrapper::unwrapDartWrapper<JsObject>(args, index, exception); |
| if (result) return result; |
| exception = Dart_NewStringFromCString("Not a valid JS object"); |
| return 0; |
| } |
| |
| typedef HashMap<Dart_Handle, v8::Handle<v8::Value> > DartHandleToV8Map; |
| v8::Handle<v8::Value> jsifyHelper(DartDOMData*, Dart_Handle value, DartHandleToV8Map&, Dart_Handle& exception, bool typed); |
| |
| 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 argsListToV8Typed(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::fromDartTyped(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; |
| { |
| DartDOMData* domData = static_cast<DartDOMData*>(Dart_GetNativeIsolateData(args)); |
| JsInteropScopes scopes(args, domData->v8Context()); |
| 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. |
| scopes.setReturnValue(JsObject::toDart(ret.As<v8::Object>(), domData, false)); |
| return; |
| } |
| |
| fail: |
| Dart_ThrowException(exception); |
| ASSERT_NOT_REACHED(); |
| } |
| |
| static void identityEqualityCallback(Dart_NativeArguments args) |
| { |
| Dart_Handle exception = 0; |
| { |
| DartDOMData* domData = static_cast<DartDOMData*>(Dart_GetNativeIsolateData(args)); |
| JsInteropScopes scopes(args, domData->v8Context()); |
| 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 getterCallbackHelper(Dart_NativeArguments args, bool legacy) |
| { |
| Dart_Handle exception = 0; |
| { |
| DartDOMData* domData = static_cast<DartDOMData*>(Dart_GetNativeIsolateData(args)); |
| JsInteropScopes scopes(args, domData->v8Context()); |
| JsObject* receiver = DartDOMWrapper::receiver<JsObject>(args); |
| auto v8Isolate = domData->v8Isolate(); |
| v8::Local<v8::Object> v8Receiver = receiver->localV8Object(v8Isolate); |
| 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 result = Dart_IntegerToUint64(index, &intIndex); |
| ALLOW_UNUSED_LOCAL(result); |
| 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, domData, legacy); |
| return; |
| } |
| |
| fail: |
| Dart_ThrowException(exception); |
| ASSERT_NOT_REACHED(); |
| } |
| |
| static void getterCallback(Dart_NativeArguments args) |
| { |
| return getterCallbackHelper(args, false); |
| } |
| |
| static void getterLegacyCallback(Dart_NativeArguments args) |
| { |
| return getterCallbackHelper(args, true); |
| } |
| |
| static void typedGetterCallback(Dart_NativeArguments args) |
| { |
| Dart_Handle exception = 0; |
| { |
| DartDOMData* domData = static_cast<DartDOMData*>(Dart_GetNativeIsolateData(args)); |
| JsInteropScopes scopes(args, domData->v8Context()); |
| JsObject* receiver = DartDOMWrapper::receiver<JsObject>(args); |
| auto v8Isolate = domData->v8Isolate(); |
| v8::Local<v8::Object> v8Receiver = receiver->localV8Object(v8Isolate); |
| Dart_Handle index = Dart_GetNativeArgument(args, 1); |
| v8::Local<v8::Value> ret; |
| |
| if (!Dart_IsString(index)) goto fail; |
| |
| ret = v8Receiver->Get(V8Converter::stringToV8(index)); |
| |
| if (scopes.handleJsException(&exception)) |
| goto fail; |
| scopes.setReturnValueTyped(ret, domData); |
| return; |
| } |
| |
| fail: |
| Dart_ThrowException(exception); |
| ASSERT_NOT_REACHED(); |
| } |
| |
| static void typedIndexGetter(Dart_NativeArguments args) |
| { |
| Dart_Handle exception = 0; |
| |
| { |
| DartDOMData* domData = static_cast<DartDOMData*>(Dart_GetNativeIsolateData(args)); |
| JsInteropScopes scopes(args, domData->v8Context()); |
| JsObject* receiver = DartDOMWrapper::receiver<JsObject>(args); |
| auto v8Isolate = domData->v8Isolate(); |
| v8::Local<v8::Object> v8Receiver = receiver->localV8Object(v8Isolate); |
| Dart_Handle index = Dart_GetNativeArgument(args, 1); |
| v8::Local<v8::Value> ret; |
| |
| if (!Dart_IsNumber(index)) goto fail; |
| |
| ret = v8Receiver->Get(V8Converter::numberToV8(index)); |
| |
| if (scopes.handleJsException(&exception)) |
| goto fail; |
| scopes.setReturnValueTyped(ret, domData); |
| return; |
| } |
| |
| fail: |
| Dart_ThrowException(exception); |
| ASSERT_NOT_REACHED(); |
| } |
| |
| static void hasPropertyCallback(Dart_NativeArguments args) |
| { |
| Dart_Handle exception = 0; |
| { |
| DartDOMData* domData = static_cast<DartDOMData*>(Dart_GetNativeIsolateData(args)); |
| JsInteropScopes scopes(args, domData->v8Context()); |
| auto v8Isolate = domData->v8Isolate(); |
| JsObject* receiver = DartDOMWrapper::receiver<JsObject>(args); |
| v8::Local<v8::Object> v8Receiver = receiver->localV8Object(v8Isolate); |
| 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; |
| { |
| DartDOMData* domData = static_cast<DartDOMData*>(Dart_GetNativeIsolateData(args)); |
| JsInteropScopes scopes(args, domData->v8Context()); |
| auto v8Isolate = domData->v8Isolate(); |
| JsObject* receiver = DartDOMWrapper::receiver<JsObject>(args); |
| v8::Local<v8::Object> v8Receiver = receiver->localV8Object(v8Isolate); |
| 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; |
| { |
| DartDOMData* domData = static_cast<DartDOMData*>(Dart_GetNativeIsolateData(args)); |
| JsInteropScopes scopes(args, domData->v8Context()); |
| auto v8Isolate = domData->v8Isolate(); |
| JsObject* receiver = DartDOMWrapper::receiver<JsObject>(args); |
| v8::Local<v8::Object> v8Receiver = receiver->localV8Object(v8Isolate); |
| v8::Local<v8::Value> type = JsInterop::fromDart(domData, Dart_GetNativeArgument(args, 1), exception); |
| |
| v8::Local<v8::Value> ret = domData->jsInteropData()->instanceofFunction(v8Isolate)->Call(v8Receiver, 1, &type); |
| if (scopes.handleJsException(&exception)) |
| goto fail; |
| |
| ASSERT(ret->IsBoolean()); |
| Dart_SetBooleanReturnValue(args, ret->IsTrue()); |
| return; |
| } |
| |
| fail: |
| Dart_ThrowException(exception); |
| ASSERT_NOT_REACHED(); |
| } |
| |
| static void setterCallbackHelper(Dart_NativeArguments args, bool legacy) |
| { |
| Dart_Handle exception = 0; |
| { |
| DartDOMData* domData = static_cast<DartDOMData*>(Dart_GetNativeIsolateData(args)); |
| JsInteropScopes scopes(args, domData->v8Context()); |
| auto v8Isolate = domData->v8Isolate(); |
| JsObject* receiver = DartDOMWrapper::receiver<JsObject>(args); |
| v8::Local<v8::Object> v8Receiver = receiver->localV8Object(v8Isolate); |
| 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 result = Dart_IntegerToUint64(index, &intIndex); |
| ALLOW_UNUSED_LOCAL(result); |
| 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 setterCallback(Dart_NativeArguments args) |
| { |
| return setterCallbackHelper(args, false); |
| } |
| |
| static void setterLegacyCallback(Dart_NativeArguments args) |
| { |
| return setterCallbackHelper(args, true); |
| } |
| |
| static void typedSetterCallback(Dart_NativeArguments args) |
| { |
| Dart_Handle exception = 0; |
| { |
| DartDOMData* domData = static_cast<DartDOMData*>(Dart_GetNativeIsolateData(args)); |
| JsInteropScopes scopes(args, domData->v8Context()); |
| auto v8Isolate = domData->v8Isolate(); |
| JsObject* receiver = DartDOMWrapper::receiver<JsObject>(args); |
| v8::Local<v8::Object> v8Receiver = receiver->localV8Object(v8Isolate); |
| Dart_Handle index = Dart_GetNativeArgument(args, 1); |
| v8::Local<v8::Value> value = JsInterop::fromDartTyped(domData, Dart_GetNativeArgument(args, 2), exception); |
| if (exception) |
| goto fail; |
| bool ret = false; |
| if (!Dart_IsString(index)) goto fail; |
| ret = v8Receiver->Set(V8Converter::stringToV8(index), value); |
| |
| if (scopes.handleJsException(&exception)) |
| goto fail; |
| scopes.setReturnValue(DartUtilities::boolToDart(ret)); |
| return; |
| } |
| |
| fail: |
| Dart_ThrowException(exception); |
| ASSERT_NOT_REACHED(); |
| } |
| |
| static void typedIndexSetter(Dart_NativeArguments args) |
| { |
| Dart_Handle exception = 0; |
| { |
| DartDOMData* domData = static_cast<DartDOMData*>(Dart_GetNativeIsolateData(args)); |
| JsInteropScopes scopes(args, domData->v8Context()); |
| auto v8Isolate = domData->v8Isolate(); |
| JsObject* receiver = DartDOMWrapper::receiver<JsObject>(args); |
| v8::Local<v8::Object> v8Receiver = receiver->localV8Object(v8Isolate); |
| Dart_Handle index = Dart_GetNativeArgument(args, 1); |
| v8::Local<v8::Value> value = JsInterop::fromDartTyped(domData, Dart_GetNativeArgument(args, 2), exception); |
| if (exception) |
| goto fail; |
| bool ret = false; |
| if (!Dart_IsNumber(index)) goto fail; |
| ret = v8Receiver->Set(V8Converter::numberToV8(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; |
| { |
| DartDOMData* domData = static_cast<DartDOMData*>(Dart_GetNativeIsolateData(args)); |
| auto v8Isolate = domData->v8Isolate(); |
| JsInteropScopes scopes(args, domData->v8Context()); |
| JsObject* receiver = DartDOMWrapper::receiver<JsObject>(args); |
| int hashCode = receiver->localV8Object(v8Isolate)->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(); |
| } |
| |
| // TODO(jacobr): is there any reason to keep this method separate from hashCodeCallback? |
| static void typedHashCodeCallback(Dart_NativeArguments args) |
| { |
| Dart_Handle exception = 0; |
| { |
| DartDOMData* domData = static_cast<DartDOMData*>(Dart_GetNativeIsolateData(args)); |
| auto v8Isolate = domData->v8Isolate(); |
| JsInteropScopes scopes(args, domData->v8Context()); |
| JsObject* receiver = DartDOMWrapper::receiver<JsObject>(args); |
| int hashCode = receiver->localV8Object(v8Isolate)->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 defineInterceptorCustomElementCallback(Dart_NativeArguments args) |
| { |
| Dart_Handle exception = 0; |
| { |
| DartDOMData* domData = static_cast<DartDOMData*>(Dart_GetNativeIsolateData(args)); |
| auto v8Isolate = domData->v8Isolate(); |
| JsObject* jsObject = toJsObject(args, 0, exception); |
| if (exception) |
| goto fail; |
| |
| v8::Local<v8::Object> v8Object = jsObject->localV8Object(v8Isolate); |
| |
| Dart_Handle type = Dart_GetNativeArgument(args, 1); |
| ASSERT(Dart_IsType(type)); |
| |
| // v8::Local<v8::Value> proto = v8Object->GetPrototype(); |
| InterceptorData* interceptor; |
| if (v8Object->IsObject()) { |
| interceptor = domData->m_interceptors.get(v8Object, v8Isolate); |
| interceptor->m_type = Dart_NewPersistentHandle(type); |
| // ASSUME this is only required for element upgrades so this is correct by default |
| interceptor->m_legacyInteropMode = TypedInterop; |
| interceptor->m_isCustomElement = true; |
| } else { |
| exception = Dart_NewStringFromCString("Internal Error. No Prototype to attach interceptor to"); |
| goto fail; |
| } |
| return; |
| } |
| fail: |
| Dart_ThrowException(exception); |
| ASSERT_NOT_REACHED(); |
| } |
| |
| static void callConstructor0Callback(Dart_NativeArguments args) |
| { |
| Dart_Handle exception = 0; |
| { |
| DartDOMData* domData = static_cast<DartDOMData*>(Dart_GetNativeIsolateData(args)); |
| auto v8Isolate = domData->v8Isolate(); |
| JsInteropScopes scopes(args, domData->v8Context()); |
| JsObject* jsConstructor = toJsObject(args, 0, exception); |
| if (exception) |
| goto fail; |
| v8::Local<v8::Object> constructorArg = jsConstructor->localV8Object(v8Isolate); |
| |
| if (!constructorArg->IsFunction()) { |
| exception = Dart_NewStringFromCString("constructor not a function"); |
| goto fail; |
| } |
| |
| v8::Local<v8::Value> ret = constructorArg->CallAsConstructor(0, 0); |
| crashIfV8IsDead(); |
| |
| if (scopes.handleJsException(&exception)) |
| goto fail; |
| |
| scopes.setReturnValueTyped(ret, domData); |
| return; |
| } |
| |
| fail: |
| Dart_ThrowException(exception); |
| ASSERT_NOT_REACHED(); |
| } |
| |
| static void callConstructorCallback(Dart_NativeArguments args) |
| { |
| Dart_Handle exception = 0; |
| { |
| DartDOMData* domData = static_cast<DartDOMData*>(Dart_GetNativeIsolateData(args)); |
| auto v8Isolate = domData->v8Isolate(); |
| JsInteropScopes scopes(args, domData->v8Context()); |
| JsObject* jsConstructor = toJsObject(args, 0, exception); |
| if (exception) |
| goto fail; |
| v8::Local<v8::Object> constructorArg = jsConstructor->localV8Object(v8Isolate); |
| |
| if (!constructorArg->IsFunction()) { |
| exception = Dart_NewStringFromCString("constructor not a function"); |
| goto fail; |
| } |
| |
| Vector<v8::Local<v8::Value> > v8Args; |
| argsListToV8Typed(domData, Dart_GetNativeArgument(args, 1), &v8Args, exception); |
| |
| v8::Local<v8::Value> ret = constructorArg->CallAsConstructor(v8Args.size(), v8Args.data()); |
| crashIfV8IsDead(); |
| |
| if (scopes.handleJsException(&exception)) |
| goto fail; |
| |
| scopes.setReturnValueTyped(ret, domData); |
| return; |
| } |
| |
| fail: |
| Dart_ThrowException(exception); |
| ASSERT_NOT_REACHED();} |
| |
| static void toTypedObjectCallback(Dart_NativeArguments args) |
| { |
| Dart_Handle exception = 0; |
| { |
| DartDOMData* domData = static_cast<DartDOMData*>(Dart_GetNativeIsolateData(args)); |
| auto v8Isolate = domData->v8Isolate(); |
| v8::Local<v8::Context> context = domData->v8Context(); |
| JsInteropScopes scopes(args, context); |
| JsObject* jsObject = toJsObject(args, 0, exception); |
| if (exception) |
| goto fail; |
| v8::Local<v8::Object> v8Object = jsObject->localV8Object(v8Isolate); |
| if (scopes.handleJsException(&exception)) |
| goto fail; |
| scopes.setReturnValueTyped(v8Object, domData); |
| return; |
| } |
| |
| fail: |
| Dart_ThrowException(exception); |
| ASSERT_NOT_REACHED(); |
| } |
| |
| // Callback to force an instance to adopt a specific interceptor. |
| static void setInstanceInterceptorCallback(Dart_NativeArguments args) |
| { |
| Dart_Handle exception = 0; |
| { |
| DartDOMData* domData = static_cast<DartDOMData*>(Dart_GetNativeIsolateData(args)); |
| auto v8Isolate = domData->v8Isolate(); |
| v8::Local<v8::Context> context = domData->v8Context(); |
| JsInteropScopes scopes(args, context); |
| JsObject* jsObject = toJsObject(args, 0, exception); |
| Dart_Handle type = Dart_GetNativeArgument(args, 1); |
| bool isCustomElement = DartUtilities::dartToBool(args, 2, exception); |
| if (exception) |
| goto fail; |
| v8::Local<v8::Object> v8Object = jsObject->localV8Object(v8Isolate); |
| if (scopes.handleJsException(&exception)) |
| goto fail; |
| |
| // Define a temporary interceptor record. |
| InterceptorData tempInterceptor; |
| tempInterceptor.m_type = Dart_PersistentHandle(type); |
| tempInterceptor.m_isCustomElement = isCustomElement; |
| auto cache = domData->m_dartHandles.get(v8Object, v8Isolate); |
| cache->m_typedInterop = 0; // Clear existing handle. |
| scopes.setReturnValue(JsObject::createTyped(v8Object, domData, &tempInterceptor, cache)); |
| return; |
| } |
| |
| fail: |
| Dart_ThrowException(exception); |
| ASSERT_NOT_REACHED(); |
| } |
| |
| static void callMethodCallbackHelper(Dart_NativeArguments args, bool legacy) |
| { |
| Dart_Handle exception = 0; |
| { |
| DartDOMData* domData = static_cast<DartDOMData*>(Dart_GetNativeIsolateData(args)); |
| JsInteropScopes scopes(args, domData->v8Context()); |
| auto v8Isolate = domData->v8Isolate(); |
| JsObject* receiver = DartDOMWrapper::receiver<JsObject>(args); |
| v8::Local<v8::Object> v8Receiver = receiver->localV8Object(v8Isolate); |
| 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()) { |
| |
| v8::Local<v8::Value> result; |
| if (!V8ScriptRunner::callFunction(value.As<v8::Function>(), |
| domData->scriptExecutionContext(), receiver->localV8Object(v8Isolate), v8Args.size(), v8Args.data(), v8Isolate).ToLocal(&ret)) { |
| if (!scopes.handleJsException(&exception)) |
| exception = Dart_NewStringFromCString("V8 Exception"); |
| goto fail; |
| } |
| } 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, domData, legacy); |
| return; |
| } |
| |
| fail: |
| Dart_ThrowException(exception); |
| ASSERT_NOT_REACHED(); |
| } |
| |
| static void callMethodCallback(Dart_NativeArguments args) { |
| callMethodCallbackHelper(args, false); |
| } |
| |
| static void callMethodLegacyCallback(Dart_NativeArguments args) { |
| callMethodCallbackHelper(args, true); |
| } |
| |
| static void typedCallMethodCallback(Dart_NativeArguments args) |
| { |
| Dart_Handle exception = 0; |
| { |
| DartDOMData* domData = static_cast<DartDOMData*>(Dart_GetNativeIsolateData(args)); |
| JsInteropScopes scopes(args, domData->v8Context()); |
| auto v8Isolate = domData->v8Isolate(); |
| JsObject* receiver = DartDOMWrapper::receiver<JsObject>(args); |
| v8::Local<v8::Object> v8Receiver = receiver->localV8Object(v8Isolate); |
| Dart_Handle name = Dart_GetNativeArgument(args, 1); |
| |
| Vector<v8::Local<v8::Value> > v8Args; |
| argsListToV8Typed(domData, Dart_GetNativeArgument(args, 2), &v8Args, exception); |
| if (exception || !Dart_IsString(name)) |
| goto fail; |
| |
| v8::Local<v8::Value> value = v8Receiver->Get(V8Converter::stringToV8(name)); |
| v8::Local<v8::Value> ret; |
| |
| if (value->IsFunction()) { |
| |
| v8::Local<v8::Value> result; |
| if (!V8ScriptRunner::callFunction(value.As<v8::Function>(), |
| domData->scriptExecutionContext(), receiver->localV8Object(v8Isolate), v8Args.size(), v8Args.data(), v8Isolate).ToLocal(&ret)) { |
| if (!scopes.handleJsException(&exception)) |
| exception = Dart_NewStringFromCString("V8 Exception"); |
| goto fail; |
| } |
| } 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.setReturnValueTyped(ret, domData); |
| return; |
| } |
| |
| fail: |
| Dart_ThrowException(exception); |
| ASSERT_NOT_REACHED(); |
| } |
| |
| static void newJsArrayCallback(Dart_NativeArguments args) |
| { |
| DartDOMData* domData = static_cast<DartDOMData*>(Dart_GetNativeIsolateData(args)); |
| auto v8Isolate = domData->v8Isolate(); |
| JsInteropScopes scopes(args, domData->v8Context()); |
| // We don't need to use legacy mode for this return value as this is known |
| // to be an Array not a DOM type. |
| scopes.setReturnValue(JsObject::toDart(v8::Array::New(v8Isolate), domData, false)); |
| return; |
| } |
| |
| static void newJsArrayFromSafeListCallback(Dart_NativeArguments args) |
| { |
| Dart_Handle exception = 0; |
| { |
| DartDOMData* domData = static_cast<DartDOMData*>(Dart_GetNativeIsolateData(args)); |
| JsInteropScopes scopes(args, domData->v8Context()); |
| auto v8Isolate = domData->v8Isolate(); |
| 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(v8Isolate, 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, domData, false); |
| return; |
| } |
| |
| fail: |
| Dart_ThrowException(exception); |
| ASSERT_NOT_REACHED(); |
| } |
| |
| |
| static void jsArrayLengthCallback(Dart_NativeArguments args) |
| { |
| Dart_Handle exception = 0; |
| { |
| DartDOMData* domData = static_cast<DartDOMData*>(Dart_GetNativeIsolateData(args)); |
| JsInteropScopes scopes(args, domData->v8Context()); |
| auto v8Isolate = domData->v8Isolate(); |
| JsObject* receiver = DartDOMWrapper::receiver<JsObject>(args); |
| v8::Local<v8::Value> length = receiver->localV8Object(v8Isolate)->Get(v8::String::NewFromUtf8(v8Isolate, "length")); |
| if (scopes.handleJsException(&exception)) |
| goto fail; |
| scopes.setReturnValueInteger(length->IntegerValue()); |
| return; |
| } |
| |
| fail: |
| Dart_ThrowException(exception); |
| ASSERT_NOT_REACHED(); |
| } |
| |
| static void fromBrowserObjectCallback(Dart_NativeArguments args) |
| { |
| Dart_Handle exception = 0; |
| { |
| DartDOMData* domData = static_cast<DartDOMData*>(Dart_GetNativeIsolateData(args)); |
| auto v8Isolate = domData->v8Isolate(); |
| v8::Local<v8::Context> context = domData->v8Context(); |
| JsInteropScopes scopes(args, context); |
| JsObject* jsObject = toJsObject(args, 0, exception); |
| if (exception) |
| goto fail; |
| v8::Local<v8::Object> v8Object = jsObject->localV8Object(v8Isolate); |
| if (scopes.handleJsException(&exception)) |
| goto fail; |
| scopes.setReturnValue(v8Object, domData, false); |
| return; |
| } |
| |
| fail: |
| Dart_ThrowException(exception); |
| ASSERT_NOT_REACHED(); |
| } |
| |
| static void applyCallback(Dart_NativeArguments args) |
| { |
| Dart_Handle exception = 0; |
| { |
| DartDOMData* domData = static_cast<DartDOMData*>(Dart_GetNativeIsolateData(args)); |
| JsInteropScopes scopes(args, domData->v8Context()); |
| auto v8Isolate = domData->v8Isolate(); |
| JsObject* receiver = DartDOMWrapper::receiver<JsObject>(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 = domData->v8Global(); |
| } 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; |
| if (!V8ScriptRunner::callFunction(receiver->localV8Object(v8Isolate).As<v8::Function>(), |
| domData->scriptExecutionContext(), |
| thisArg.As<v8::Object>(), |
| v8Args.size(), |
| v8Args.data(), |
| v8Isolate).ToLocal(&ret)) { |
| exception = Dart_NewStringFromCString("V8 Exception"); |
| goto fail; |
| } |
| if (scopes.handleJsException(&exception)) |
| goto fail; |
| scopes.setReturnValue(ret, domData, true); |
| return; |
| } |
| |
| fail: |
| Dart_ThrowException(exception); |
| ASSERT_NOT_REACHED(); |
| } |
| |
| static void typedApplyCallback(Dart_NativeArguments args) |
| { |
| Dart_Handle exception = 0; |
| { |
| // TODO(jacobr): this is crazily inefficient if the JSFunction happens |
| // to really be a Dart function. Consider optimizing that case. |
| DartDOMData* domData = static_cast<DartDOMData*>(Dart_GetNativeIsolateData(args)); |
| JsInteropScopes scopes(args, domData->v8Context()); |
| auto v8Isolate = domData->v8Isolate(); |
| JsObject* receiver = DartDOMWrapper::receiver<JsObject>(args); |
| |
| Vector<v8::Local<v8::Value> > v8Args; |
| argsListToV8Typed(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 = domData->v8Global(); |
| } else { |
| thisArg = JsInterop::fromDartTyped(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; |
| if (!V8ScriptRunner::callFunction(receiver->localV8Object(v8Isolate).As<v8::Function>(), |
| domData->scriptExecutionContext(), |
| thisArg.As<v8::Object>(), |
| v8Args.size(), |
| v8Args.data(), |
| v8Isolate).ToLocal(&ret)) { |
| exception = Dart_NewStringFromCString("V8 Exception"); |
| goto fail; |
| } |
| if (scopes.handleJsException(&exception)) |
| goto fail; |
| scopes.setReturnValueTyped(ret, domData); |
| return; |
| } |
| |
| fail: |
| Dart_ThrowException(exception); |
| ASSERT_NOT_REACHED(); |
| } |
| |
| static void applyDebuggerOnlyCallback(Dart_NativeArguments args) |
| { |
| Dart_Handle exception = 0; |
| { |
| DartDOMData* domData = static_cast<DartDOMData*>(Dart_GetNativeIsolateData(args)); |
| JsInteropScopes scopes(args, domData->v8Context()); |
| auto v8Isolate = domData->v8Isolate(); |
| JsObject* receiver = DartDOMWrapper::receiver<JsObject>(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 = domData->v8Global(); |
| } else { |
| thisArg = JsInterop::fromDartTyped(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; |
| if (!V8ScriptRunner::callFunction(receiver->localV8Object(v8Isolate).As<v8::Function>(), |
| domData->scriptExecutionContext(), |
| thisArg.As<v8::Object>(), |
| v8Args.size(), |
| v8Args.data(), |
| v8Isolate).ToLocal(&ret)) { |
| exception = Dart_NewStringFromCString("V8 Exception"); |
| goto fail; |
| } |
| if (scopes.handleJsException(&exception)) |
| goto fail; |
| scopes.setReturnValueTyped(ret, domData); |
| return; |
| } |
| |
| fail: |
| Dart_ThrowException(exception); |
| ASSERT_NOT_REACHED(); |
| } |
| |
| static void toStringCallback(Dart_NativeArguments args) |
| { |
| Dart_Handle exception = 0; |
| { |
| DartDOMData* domData = static_cast<DartDOMData*>(Dart_GetNativeIsolateData(args)); |
| JsInteropScopes scopes(args, domData->v8Context()); |
| auto v8Isolate = domData->v8Isolate(); |
| JsObject* receiver = DartDOMWrapper::receiver<JsObject>(args); |
| v8::Local<v8::Object> v8Object = receiver->localV8Object(v8Isolate); |
| 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(V8Converter::stringToDart(v8String)); |
| return; |
| } |
| fail: |
| Dart_ThrowException(exception); |
| ASSERT_NOT_REACHED(); |
| } |
| |
| static void contextCallback(Dart_NativeArguments args) |
| { |
| DartDOMData* domData = static_cast<DartDOMData*>(Dart_GetNativeIsolateData(args)); |
| v8::Context::Scope scope(domData->v8Context()); |
| Dart_SetReturnValue(args, JsObject::toDart(domData->v8Global(), domData, false)); |
| } |
| |
| static void finalizeJsInterfacesCallback(Dart_NativeArguments args) |
| { |
| // Obsolete. |
| } |
| |
| 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, bool typed) |
| { |
| auto v8Isolate = domData->v8Isolate(); |
| 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(v8Isolate); |
| map.set(value, object); |
| |
| // We converted to internal Dart list, methods shouldn't throw exceptions now. |
| intptr_t length = 0; |
| Dart_Handle result = Dart_ListLength(asList, &length); |
| ALLOW_UNUSED_LOCAL(result); |
| 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, typed); |
| if (exception) |
| return v8::Handle<v8::Value>(); |
| v8::Handle<v8::Value> value = jsifyHelper(domData, Dart_ListGetAt(asList, i + 1), map, exception, typed); |
| 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, bool typed) |
| { |
| auto v8Isolate = domData->v8Isolate(); |
| 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(v8Isolate, 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, typed); |
| 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, bool typed) |
| { |
| DartHandleToV8Map::iterator iter = map.find(value); |
| if (iter != map.end()) |
| return iter->value; |
| |
| if (Dart_IsList(value)) |
| return listToV8(domData, value, map, exception, typed); |
| |
| bool isMap = DartUtilities::dartToBool(DartUtilities::invokeUtilsMethod("isMap", 1, &value), exception); |
| ASSERT(!exception); |
| if (isMap) |
| return mapToV8(domData, value, map, exception, typed); |
| |
| Dart_Handle maybeList = DartUtilities::invokeUtilsMethod("toListIfIterable", 1, &value); |
| if (Dart_IsList(maybeList)) |
| return listToV8(domData, maybeList, map, exception, typed); |
| |
| v8::Handle<v8::Value> ret = typed ? JsInterop::fromDartTyped(domData, value, exception) : JsInterop::fromDart(domData, value, exception); |
| map.set(value, ret); |
| return ret; |
| } |
| |
| static void jsifyCallback(Dart_NativeArguments args) |
| { |
| Dart_Handle exception = 0; |
| { |
| DartDOMData* domData = static_cast<DartDOMData*>(Dart_GetNativeIsolateData(args)); |
| JsInteropScopes scopes(args, domData->v8Context()); |
| Dart_Handle value = Dart_GetNativeArgument(args, 0); |
| DartHandleToV8Map map; |
| v8::Local<v8::Value> ret = jsifyHelper(domData, value, map, exception, false); |
| if (exception) |
| goto fail; |
| |
| if (scopes.handleJsException(&exception)) |
| goto fail; |
| // Intentionally skip legacy auto-conversion in this case as the user |
| // expects a JsObject. |
| scopes.setReturnValue(ret, domData, false); |
| return; |
| } |
| fail: |
| Dart_ThrowException(exception); |
| ASSERT_NOT_REACHED(); |
| } |
| |
| static void typedJsifyCallback(Dart_NativeArguments args) |
| { |
| Dart_Handle exception = 0; |
| { |
| DartDOMData* domData = static_cast<DartDOMData*>(Dart_GetNativeIsolateData(args)); |
| JsInteropScopes scopes(args, domData->v8Context()); |
| Dart_Handle value = Dart_GetNativeArgument(args, 0); |
| DartHandleToV8Map map; |
| v8::Local<v8::Value> ret = jsifyHelper(domData, value, map, exception, true); |
| if (exception) |
| goto fail; |
| |
| if (scopes.handleJsException(&exception)) |
| goto fail; |
| scopes.setReturnValueTyped(ret, domData); |
| return; |
| } |
| fail: |
| Dart_ThrowException(exception); |
| ASSERT_NOT_REACHED(); |
| } |
| |
| static void newObjectCallback(Dart_NativeArguments args) |
| { |
| Dart_Handle exception = 0; |
| { |
| DartDOMData* domData = static_cast<DartDOMData*>(Dart_GetNativeIsolateData(args)); |
| auto v8Isolate = domData->v8Isolate(); |
| JsInteropScopes scopes(args, domData->v8Context()); // TODO(jacobr): this line isn't needed. |
| v8::Local<v8::Object> ret = v8::Object::New(v8Isolate); |
| if (exception) |
| goto fail; |
| |
| if (scopes.handleJsException(&exception)) |
| goto fail; |
| // TODO(jacobr): we could hard code the interceptor in this case. |
| scopes.setReturnValueTyped(ret, domData); |
| return; |
| } |
| fail: |
| Dart_ThrowException(exception); |
| ASSERT_NOT_REACHED(); |
| } |
| |
| static void newArrayCallback(Dart_NativeArguments args) |
| { |
| Dart_Handle exception = 0; |
| { |
| DartDOMData* domData = static_cast<DartDOMData*>(Dart_GetNativeIsolateData(args)); |
| auto v8Isolate = domData->v8Isolate(); |
| JsInteropScopes scopes(args, domData->v8Context()); // TODO(jacobr): this line isn't needed. |
| v8::Local<v8::Object> ret = v8::Array::New(v8Isolate); |
| if (exception) |
| goto fail; |
| |
| if (scopes.handleJsException(&exception)) |
| goto fail; |
| // TODO(jacobr): we could hard code the interceptor in this case. |
| scopes.setReturnValueTyped(ret, domData); |
| return; |
| } |
| fail: |
| Dart_ThrowException(exception); |
| ASSERT_NOT_REACHED(); |
| } |
| |
| static void withThisCallback(Dart_NativeArguments args) |
| { |
| Dart_Handle exception = 0; |
| { |
| DartDOMData* domData = static_cast<DartDOMData*>(Dart_GetNativeIsolateData(args)); |
| auto v8Isolate = domData->v8Isolate(); |
| JsInteropScopes scopes(args, domData->v8Context()); |
| Dart_Handle function = Dart_GetNativeArgument(args, 0); |
| ASSERT(DartUtilities::isFunction(domData, function)); |
| |
| v8::Local<v8::Object> proxy = dartFunctionTemplate(v8Isolate)->InstanceTemplate()->NewInstance(); |
| DartHandleProxy::writePointerToProxy(proxy, function); |
| |
| v8::Local<v8::Function> ret = v8::Local<v8::Function>::Cast(domData->jsInteropData()->captureThisFunction(v8Isolate)->Call(proxy, 0, 0)); |
| |
| if (scopes.handleJsException(&exception)) |
| goto fail; |
| scopes.setReturnValue(ret, domData, true); |
| return; |
| } |
| fail: |
| Dart_ThrowException(exception); |
| ASSERT_NOT_REACHED(); |
| } |
| |
| static void typedFunctionCreateWithThisCallback(Dart_NativeArguments args) |
| { |
| Dart_Handle exception = 0; |
| { |
| DartDOMData* domData = static_cast<DartDOMData*>(Dart_GetNativeIsolateData(args)); |
| auto v8Isolate = domData->v8Isolate(); |
| JsInteropScopes scopes(args, domData->v8Context()); |
| Dart_Handle function = Dart_GetNativeArgument(args, 0); |
| ASSERT(DartUtilities::isFunction(domData, function)); |
| |
| v8::Local<v8::Object> proxy = typedDartFunctionTemplate(v8Isolate)->InstanceTemplate()->NewInstance(); |
| DartHandleProxy::writePointerToProxy(proxy, function); |
| |
| v8::Local<v8::Function> ret = v8::Local<v8::Function>::Cast(domData->jsInteropData()->captureThisFunction(v8Isolate)->Call(proxy, 0, 0)); |
| |
| if (scopes.handleJsException(&exception)) |
| goto fail; |
| scopes.setReturnValueTyped(ret, domData); |
| return; |
| } |
| fail: |
| Dart_ThrowException(exception); |
| ASSERT_NOT_REACHED(); |
| } |
| |
| static void typedFunctionCreateCallback(Dart_NativeArguments args) |
| { |
| Dart_Handle exception = 0; |
| { |
| DartDOMData* domData = static_cast<DartDOMData*>(Dart_GetNativeIsolateData(args)); |
| auto v8Isolate = domData->v8Isolate(); |
| JsInteropScopes scopes(args, domData->v8Context()); |
| Dart_Handle function = Dart_GetNativeArgument(args, 0); |
| ASSERT(DartUtilities::isFunction(domData, function)); |
| |
| v8::Local<v8::Function> ret = toTypedDartFunction(function, domData, v8Isolate); |
| |
| if (scopes.handleJsException(&exception)) |
| goto fail; |
| scopes.setReturnValueTyped(ret, domData); |
| return; |
| } |
| fail: |
| Dart_ThrowException(exception); |
| ASSERT_NOT_REACHED(); |
| } |
| |
| void initializeCustomElement(Dart_NativeArguments args) |
| { |
| Dart_Handle exception = 0; |
| { |
| Dart_Handle elementWrapper = Dart_GetNativeArgument(args, 0); |
| if (!DartDOMWrapper::subtypeOf(elementWrapper, JsObject::dartClassId)) { |
| exception = Dart_NewStringFromCString("created called outside of custom element creation."); |
| goto fail; |
| } |
| // We don't actually perform any real initialization work. |
| return; |
| } |
| |
| fail: |
| Dart_ThrowException(exception); |
| ASSERT_NOT_REACHED(); |
| } |
| |
| } |
| |
| static DartNativeEntry nativeEntries[] = { |
| { JsInteropInternal::jsObjectConstructorCallback, 2, "JsObject_constructorCallback" }, |
| { JsInteropInternal::contextCallback, 0, "Js_context_Callback" }, |
| { JsInteropInternal::finalizeJsInterfacesCallback, 0, "Js_finalizeJsInterfaces" }, |
| { JsInteropInternal::interfacesFinalizedCallback, 0, "Js_interfacesFinalized_Callback" }, |
| { JsInteropInternal::jsifyCallback, 1, "JsObject_jsify" }, |
| { JsInteropInternal::typedJsifyCallback, 1, "JSObject_jsify" }, |
| { JsInteropInternal::newObjectCallback, 0, "JSObject_newObject" }, |
| { JsInteropInternal::newArrayCallback, 0, "JSObject_newArray" }, |
| { JsInteropInternal::withThisCallback, 1, "JsFunction_withThis" }, |
| { JsInteropInternal::getterCallback, 2, "JsObject_[]" }, |
| { JsInteropInternal::getterLegacyCallback, 2, "JsObject_[]Legacy" }, |
| { JsInteropInternal::setterCallback, 3, "JsObject_[]=" }, |
| { JsInteropInternal::setterLegacyCallback, 3, "JsObject_[]=Legacy" }, |
| { JsInteropInternal::hashCodeCallback, 1, "JsObject_hashCode" }, |
| { JsInteropInternal::callMethodCallback, 3, "JsObject_callMethod" }, |
| { JsInteropInternal::callMethodLegacyCallback, 3, "JsObject_callMethodLegacy" }, |
| |
| // Begin new interop methods |
| { JsInteropInternal::typedGetterCallback, 2, "JSObject_[]" }, |
| { JsInteropInternal::typedSetterCallback, 3, "JSObject_[]=" }, |
| |
| { JsInteropInternal::typedHashCodeCallback, 1, "JSObject_hashCode" }, |
| { JsInteropInternal::typedCallMethodCallback, 3, "JSObject_callMethod" }, |
| { JsInteropInternal::typedIndexGetter, 2, "JSArray_indexed_getter" }, |
| { JsInteropInternal::typedIndexSetter, 3, "JSArray_indexed_setter" }, |
| { JsInteropInternal::callConstructor0Callback, 1, "JSNative_callConstructor0" }, |
| { JsInteropInternal::callConstructorCallback, 2, "JSNative_callConstructor" }, |
| { JsInteropInternal::defineInterceptorCustomElementCallback, 2, "Utils_defineInterceptorCustomElement" }, |
| { JsInteropInternal::setInstanceInterceptorCallback, 3, "Utils_setInstanceInterceptor" }, |
| { JsInteropInternal::initializeCustomElement, 1, "Utils_initializeCustomElement" }, |
| |
| { JsInteropInternal::toTypedObjectCallback, 1, "JSNative_toTypedObject" }, |
| |
| { JsInteropInternal::typedApplyCallback, 3, "JSFunction_apply" }, |
| { JsInteropInternal::typedFunctionCreateWithThisCallback, 1, "JSFunction_createWithThis" }, |
| { JsInteropInternal::typedFunctionCreateCallback, 1, "JSFunction_create" }, |
| |
| // TODO(jacobr): do we want to do anything differently for JSObject |
| // toString relative to JsObject.toString? |
| { JsInteropInternal::toStringCallback, 1, "JSObject_toString" }, |
| |
| // End new interop methods |
| { 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; |
| } |
| setJsReturnValueTyped(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. |
| } |
| setJsReturnValueTyped(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)); |
| auto v8Isolate = domData->v8Isolate(); |
| |
| 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(v8Isolate, 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); |
| setJsReturnValueTyped(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::toDartTyped(info[0], domData); |
| } |
| Dart_Handle args[2] = { handle, e }; |
| Dart_Handle ret = Dart_Invoke(domData->jsLibrary(), Dart_NewStringFromCString(methodName), 2, args); |
| setJsReturnValueTyped(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::toDartTyped(info[i], domData)); |
| } |
| // Note: this is also just info.Holder(). |
| Dart_Handle args[2] = { handle, argsList }; |
| setJsReturnValueTyped(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::toDartTyped(value, domData) }; |
| Dart_Handle ret = Dart_Invoke(domData->jsLibrary(), Dart_NewStringFromCString("_setListLength"), 2, args); |
| setJsReturnValueTyped(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(v8::Isolate* v8Isolate) |
| { |
| DEFINE_STATIC_LOCAL(v8::Persistent<v8::FunctionTemplate>, proxyTemplate, ()); |
| v8::Local<v8::FunctionTemplate> proxyTemplateLocal; |
| if (proxyTemplate.IsEmpty()) { |
| proxyTemplate.Reset(v8Isolate, 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 dartObjectToStringCallback(const v8::FunctionCallbackInfo<v8::Value>& info) |
| { |
| if (handleNonDartProxyThis(info, "toString")) { |
| return; |
| } |
| DartScopes scopes(info.Holder()); |
| DartDOMData* domData = DartDOMData::current(); |
| Dart_Handle handle = scopes.handle; |
| setJsReturnValueTyped(domData, info, Dart_ToString(handle)); |
| } |
| |
| static v8::Local<v8::FunctionTemplate> dartObjectTemplate(v8::Isolate* v8Isolate) |
| { |
| DEFINE_STATIC_LOCAL(v8::Persistent<v8::FunctionTemplate>, proxyTemplate, ()); |
| v8::Local<v8::FunctionTemplate> proxyTemplateLocal; |
| if (proxyTemplate.IsEmpty()) { |
| proxyTemplate.Reset(v8Isolate, v8::FunctionTemplate::New(v8Isolate)); |
| proxyTemplateLocal = v8::Local<v8::FunctionTemplate>::New(v8Isolate, proxyTemplate); |
| v8::Local<v8::ObjectTemplate> protoTemplate = proxyTemplateLocal->PrototypeTemplate(); |
| protoTemplate->Set(v8::String::NewFromUtf8(v8Isolate, "toString"), v8::FunctionTemplate::New(v8Isolate, dartObjectToStringCallback)); |
| |
| proxyTemplateLocal->SetClassName(v8::String::NewFromUtf8(v8Isolate, "DartObject")); |
| setupInstanceTemplate(proxyTemplateLocal); |
| } else { |
| proxyTemplateLocal = v8::Local<v8::FunctionTemplate>::New(v8Isolate, proxyTemplate); |
| } |
| return proxyTemplateLocal; |
| } |
| |
| } |