Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

__extends not working as expected for non native inheritance #1181

Closed
farfromrefug opened this issue Sep 30, 2018 · 11 comments
Closed

__extends not working as expected for non native inheritance #1181

farfromrefug opened this issue Sep 30, 2018 · 11 comments
Assignees
Milestone

Comments

@farfromrefug
Copy link
Contributor

farfromrefug commented Sep 30, 2018

I am trying to do this:

import Vue from "nativescript-vue";
class BaseVueComponent extends Vue {
}
class Home extends BaseVueComponent {
}

I end up with this error on android:

Can not extend an already extended native object

This comes from the custom __extends . I understand this error but it should only happen if parent class is actually a native class.

This is really important as it breaks any deep inheritance (thus good practice) use.

@Logikgate
Copy link

@farfromrefug We are experiencing the exact same issue. I've been trying to run down the issue for two days now and haven't made any progress. Hopefully someone chimes in soon since our entire application architecture relies on base components being extended.

@farfromrefug
Copy link
Contributor Author

I actually need to bump that issue. This is starting to be a very big issue in Vue projects extending native classes.

I have dev project for one my plugin where I dev in Vue.
I can't factorize my code because of that issue.

But now I am starting to think that it is related to nativescript-vue and the android-runtime.
I only have that issue for classes extending Vue from nativescript-vue

@farfromrefug
Copy link
Contributor Author

I found the reason and i fixed it.
Vue prototype also define an extend method
the android runtime ts_helper test for that method existance to see if that a native class.
That's not enough and will make the runtime think the Vue class is a native class.
Testing for /native/.test(Parent.extend.toString()) makes it work as a native function description contains the word native (always).

But that's enough, the __extends_ts from the android runtime does not seems to be good in the case of a Vue class, I end up with errors like that Cannot create property '_Ctor' on string 'Map'

So what i did is use the original tslib extend method if the class is not native, otherwise use the current existing one.

Here is the resulting ts_helper.js

Please integrate that fix as soon as possible! Really needed!

(function() {
	var __extends_ns = function (d, b) {
		if (!b.extend) {
			for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
		}

		function __() { this.constructor = d; }
		__.prototype = b.prototype;
		d.prototype = new __();
	};

	var extendStatics = Object.setPrototypeOf ||
        ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
        function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };

	var __extends_ts = function (d, b) {
        extendStatics(d, b);
        function __() { this.constructor = d; }
        d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
    };


	var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
		var c = arguments.length;
		var r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;

		if (typeof global.Reflect === "object" && typeof global.Reflect.decorate === "function") {
			r = global.Reflect.decorate(decorators, target, key, desc);
		}
		else {
			for (var i = decorators.length - 1; i >= 0; i--) {
				if (d = decorators[i]) {
					r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
				}
			}
		}
		return c > 3 && r && Object.defineProperty(target, key, r), r;
	};

	// For backward compatibility.
	var __native = function(thiz) {
	    // we are setting the __container__ property to the base class when the super method is called
    	// if the constructor returns the __native(this) call we will use the old implementation
    	// copying all the properties to the result
    	// otherwise if we are using the result from the super() method call we won't need such logic
    	// as thiz already contains the parent properties
    	// this way we now support both implementations in typescript generated constructors:
    	// 1: super(); return __native(this);
    	// 2: return super() || this;
	    if(thiz.__container__) {
            var result = thiz.__proto__;

            for (var prop in thiz)
            {
                if (thiz.hasOwnProperty(prop))
                {
                    thiz.__proto__[prop] = thiz[prop];
                    delete thiz[prop];
                }
            }

            thiz.constructor = undefined;
            thiz.__proto__ = undefined;
            Object.freeze(thiz);
            Object.preventExtensions(thiz);
            return result;
		} else {
            return thiz;
		}
	};

	var __extends = function(Child, Parent) {
		var extendNativeClass = !!Parent.extend && /native/.test(Parent.extend.toString());
		if (!extendNativeClass)	{
			__extends_ts(Child, Parent);
			return;
		}
		if	(Parent.__isPrototypeImplementationObject) {
			throw new Error("Can not extend an already extended native object.");
		}

		function extend(thiz) {
			var child = thiz.__proto__.__child;
			if (!child.__extended) {
				var parent = thiz.__proto__.__parent;
				child.__extended = parent.extend(child.name, child.prototype, true);
				// This will deal with "i instanceof child"
				child[Symbol.hasInstance] = function(instance) {
					return instance instanceof this.__extended;
				}
			}
			return child.__extended;
		};

		Parent.__activityExtend = function(parent, name, implementationObject) {
			__log("__activityExtend called");
			return parent.extend(name, implementationObject);
		};

		Parent.call = function(thiz) {
			var Extended = extend(thiz);
			thiz.__container__ = true;
			if (arguments.length > 1)
			{
				thiz.__proto__ = new (Function.prototype.bind.apply(Extended, [null].concat(Array.prototype.slice.call(arguments, 1))));
			}
			else
			{
				thiz.__proto__ = new Extended()
			}
			return thiz.__proto__;
		};

		Parent.apply = function(thiz, args) {
			var Extended = extend(thiz);
			thiz.__container__ = true;
			if (args && args.length > 0)
			{
				thiz.__proto__ = new (Function.prototype.bind.apply(Extended, [null].concat(args)));
			}
			else
			{
				thiz.__proto__ = new Extended();
			}
			return thiz.__proto__;
		};
		__extends_ns(Child, Parent);
		Child.__isPrototypeImplementationObject = true;
		Child.__proto__ = Parent;
		Child.prototype.__parent = Parent;
		Child.prototype.__child = Child;
	}

	function JavaProxy(className) {
		return function (target) {
			var extended = target.extend(className, target.prototype)
			extended.name = className;
			return extended;
		};
	}

	function Interfaces(interfacesArr) {
		return function (target) {
			if(interfacesArr instanceof Array) {
				// attach interfaces: [] to the object
				target.prototype.interfaces = interfacesArr;
			}
		}
	}

	Object.defineProperty(global, "__native", { value: __native });
	Object.defineProperty(global, "__extends", { value: __extends });
	Object.defineProperty(global, "__decorate", { value: __decorate });

	global.JavaProxy = JavaProxy;
	global.Interfaces = Interfaces;
})()

@farfromrefug
Copy link
Contributor Author

@vtrifonov could somehone look at this? This is a really important bug and fix for people working with Vue. It would be really nice to see it released with 5.0.0.
Thanks

@vtrifonov
Copy link
Contributor

@farfromrefug can you please provide a sample project with the issue above?

@vtrifonov
Copy link
Contributor

never-mind, I've managed to reproduce it!

@farfromrefug
Copy link
Contributor Author

farfromrefug commented Nov 6, 2018

@vtrifonov sorry i should have sent a sample. Pretty easy to reproduce. Thanks for testing this!

@vmutafov
Copy link
Contributor

vmutafov commented Nov 7, 2018

Hi @farfromrefug,

Could you provide a sample where the "Cannot create property '_Ctor' on string 'Map'" message is displayed as we couldn't reproduce this specific issue?

Thanks ;)

@farfromrefug
Copy link
Contributor Author

@vmutafov i will prepare one.

@farfromrefug
Copy link
Contributor Author

Here is a sample. as it is you will get the Can not extend an already extended native object

Now to get the _Ctor error:

  • build the app
  • go and edit ts_helpers.js in platforms/android/app/src/main/assets/internal:
    • change the test for Parent.extend to a test for !!Parent.extend && /native/.test(Parent.extend.toString())

There you will get the _Ctor error using the current __extends_ts

If you now change ts_helpers.js to the one i submitted, it will work.

vue_issue.zip

@vmutafov
Copy link
Contributor

vmutafov commented Nov 7, 2018

Thank you for the sample and the effort ;) I'll test it and update you as soon as possible :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants