Skip to content

Commit

Permalink
POC: Wrap bridged types to throw when called as a function
Browse files Browse the repository at this point in the history
For Khan#25: This shows how we might wrap the constructors so that they throw when called without `new`. In this example, I've hardcoded `TextLayer` as the only wrapped type, but we'd presumably want to wrap every instantiable type. When you call a type without `new`, you now get a protonope, as you might expect. (It always says line 5 though since that's where the `new Error()` call is in the wrap script -- not sure how to fix that easily.)
  • Loading branch information
sophiebits committed Mar 7, 2015
1 parent 5300f79 commit 3e390a5
Showing 1 changed file with 30 additions and 1 deletion.
31 changes: 30 additions & 1 deletion PrototopeJSBridge/Context.swift
Original file line number Diff line number Diff line change
Expand Up @@ -82,5 +82,34 @@ public class Context {
TextAlignmentBridge.addToContext(context)
CameraLayerBridge.addToContext(context)
CameraPositionBridge.addToContext(context)

// TODO: Wrap all of these types
wrapBridgedType("TextLayer")
}

/// Wraps the global context variable `jsName` in a guard that throws a JS error if the constructor is called as a function, to prevent JavaScriptCore from crashing hard. See https://github.com/Khan/Prototope/issues/25.
private func wrapBridgedType(jsName: String) {
let originalType = context.objectForKeyedSubscript(jsName)
let wrappedType = bridgedTypeWrapper.callWithArguments([originalType])
context.setObject(wrappedType, forKeyedSubscript: jsName)
}

private var bridgedTypeWrapper: JSValue {
let script = [
"(function wrap(OriginalConstructor) {",
"function Constructor() {",
"if (!(this instanceof Constructor)) {",
"// TODO: This uses the Swift class name, not the JavaScript name",
"throw new Error('Use `new` to instantiate ' + OriginalConstructor.name);",
"}",
"return new (Function.prototype.bind.apply(OriginalConstructor, arguments));",
"}",
"Constructor.prototype = OriginalConstructor.prototype;",
"// This is sketchy and mutates the original prototype, but seems to work.",
"Constructor.prototype.constructor = Constructor;",
"return Constructor;",
"})"
]
return context.evaluateScript("\n".join(script))
}
}
}

0 comments on commit 3e390a5

Please sign in to comment.