Classes: Add support for arguments in default constructor
authorarv@chromium.org <arv@chromium.org>
Tue, 11 Nov 2014 21:35:27 +0000 (21:35 +0000)
committerarv@chromium.org <arv@chromium.org>
Tue, 11 Nov 2014 21:36:16 +0000 (21:36 +0000)
This is currently done by generating a default constructor that looks
like this:

  constructor() {
    %DefaultConstructorSuperCall();
  }

The a runtime function implements the logic which is pretty similar to
Runtime_Apply except that it uses the [[Prototype]] of the current
function.

This is the second try. The first failed because the test was using a
array that was too large for Function.prototype.apply.

Revert "Revert "Classes: Add support for arguments in default constructor""

This reverts commit 43aa7e541df56a132608b8b4217e9da84575e4f8.

BUG=v8:3672
LOG=Y
TBR=dslomov@chromium.org

Review URL: https://codereview.chromium.org/716853003

Cr-Commit-Position: refs/heads/master@{#25272}
git-svn-id: https://v8.googlecode.com/svn/branches/bleeding_edge@25272 ce2b1a6d-e550-0410-aec6-3dcde31c8c00

src/parser.cc
src/runtime/runtime-classes.cc
src/runtime/runtime-object.cc
src/runtime/runtime.h
test/mjsunit/harmony/classes.js

index e34854f..4c81355 100644 (file)
@@ -302,10 +302,12 @@ FunctionLiteral* Parser::DefaultConstructor(bool call_super, Scope* scope,
 
     body = new (zone()) ZoneList<Statement*>(1, zone());
     if (call_super) {
-      Expression* prop = SuperReference(function_scope, factory(), pos);
       ZoneList<Expression*>* args =
           new (zone()) ZoneList<Expression*>(0, zone());
-      Call* call = factory()->NewCall(prop, args, pos);
+      CallRuntime* call = factory()->NewCallRuntime(
+          ast_value_factory()->empty_string(),
+          Runtime::FunctionForId(Runtime::kDefaultConstructorSuperCall), args,
+          pos);
       body->Add(factory()->NewExpressionStatement(call, pos), zone());
     }
 
index d1dbe21..75e7974 100644 (file)
@@ -436,5 +436,52 @@ RUNTIME_FUNCTION(Runtime_StoreKeyedToSuper_Sloppy) {
 
   return StoreKeyedToSuper(isolate, home_object, receiver, key, value, SLOPPY);
 }
+
+
+RUNTIME_FUNCTION(Runtime_DefaultConstructorSuperCall) {
+  HandleScope scope(isolate);
+  DCHECK(args.length() == 0);
+
+  // Compute the frame holding the arguments.
+  JavaScriptFrameIterator it(isolate);
+  it.AdvanceToArgumentsFrame();
+  JavaScriptFrame* frame = it.frame();
+
+  Handle<JSFunction> function(frame->function(), isolate);
+  Handle<Object> receiver(frame->receiver(), isolate);
+
+  Handle<Object> proto_function;
+  ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, proto_function,
+                                     Runtime::GetPrototype(isolate, function));
+
+  // Get the actual number of provided arguments.
+  const int argc = frame->ComputeParametersCount();
+
+  // Loose upper bound to allow fuzzing. We'll most likely run out of
+  // stack space before hitting this limit.
+  static int kMaxArgc = 1000000;
+  RUNTIME_ASSERT(argc >= 0 && argc <= kMaxArgc);
+
+  // If there are too many arguments, allocate argv via malloc.
+  const int argv_small_size = 10;
+  Handle<Object> argv_small_buffer[argv_small_size];
+  SmartArrayPointer<Handle<Object> > argv_large_buffer;
+  Handle<Object>* argv = argv_small_buffer;
+  if (argc > argv_small_size) {
+    argv = new Handle<Object>[argc];
+    if (argv == NULL) return isolate->StackOverflow();
+    argv_large_buffer = SmartArrayPointer<Handle<Object> >(argv);
+  }
+
+  for (int i = 0; i < argc; ++i) {
+    argv[i] = handle(frame->GetParameter(i), isolate);
+  }
+
+  Handle<Object> result;
+  ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
+      isolate, result,
+      Execution::Call(isolate, proto_function, receiver, argc, argv, false));
+  return *result;
+}
 }
 }  // namespace v8::internal
index fc97d6b..3b302d1 100644 (file)
@@ -242,10 +242,8 @@ MaybeHandle<Object> Runtime::DefineObjectProperty(Handle<JSObject> js_object,
 }
 
 
-RUNTIME_FUNCTION(Runtime_GetPrototype) {
-  HandleScope scope(isolate);
-  DCHECK(args.length() == 1);
-  CONVERT_ARG_HANDLE_CHECKED(Object, obj, 0);
+MaybeHandle<Object> Runtime::GetPrototype(Isolate* isolate,
+                                          Handle<Object> obj) {
   // We don't expect access checks to be needed on JSProxy objects.
   DCHECK(!obj->IsAccessCheckNeeded() || obj->IsJSObject());
   PrototypeIterator iter(isolate, obj, PrototypeIterator::START_AT_RECEIVER);
@@ -257,15 +255,26 @@ RUNTIME_FUNCTION(Runtime_GetPrototype) {
       isolate->ReportFailedAccessCheck(
           Handle<JSObject>::cast(PrototypeIterator::GetCurrent(iter)),
           v8::ACCESS_GET);
-      RETURN_FAILURE_IF_SCHEDULED_EXCEPTION(isolate);
-      return isolate->heap()->undefined_value();
+      RETURN_EXCEPTION_IF_SCHEDULED_EXCEPTION(isolate, Object);
+      return isolate->factory()->undefined_value();
     }
     iter.AdvanceIgnoringProxies();
     if (PrototypeIterator::GetCurrent(iter)->IsJSProxy()) {
-      return *PrototypeIterator::GetCurrent(iter);
+      return PrototypeIterator::GetCurrent(iter);
     }
   } while (!iter.IsAtEnd(PrototypeIterator::END_AT_NON_HIDDEN));
-  return *PrototypeIterator::GetCurrent(iter);
+  return PrototypeIterator::GetCurrent(iter);
+}
+
+
+RUNTIME_FUNCTION(Runtime_GetPrototype) {
+  HandleScope scope(isolate);
+  DCHECK(args.length() == 1);
+  CONVERT_ARG_HANDLE_CHECKED(Object, obj, 0);
+  Handle<Object> result;
+  ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, result,
+                                     Runtime::GetPrototype(isolate, obj));
+  return *result;
 }
 
 
index ea21faf..749c15f 100644 (file)
@@ -199,7 +199,8 @@ namespace internal {
   F(StoreToSuper_Strict, 4, 1)                             \
   F(StoreToSuper_Sloppy, 4, 1)                             \
   F(StoreKeyedToSuper_Strict, 4, 1)                        \
-  F(StoreKeyedToSuper_Sloppy, 4, 1)
+  F(StoreKeyedToSuper_Sloppy, 4, 1)                        \
+  F(DefaultConstructorSuperCall, 0, 1)
 
 
 #define RUNTIME_FUNCTION_LIST_ALWAYS_2(F)              \
@@ -828,6 +829,9 @@ class Runtime : public AllStatic {
   MUST_USE_RESULT static MaybeHandle<Object> GetObjectProperty(
       Isolate* isolate, Handle<Object> object, Handle<Object> key);
 
+  MUST_USE_RESULT static MaybeHandle<Object> GetPrototype(
+      Isolate* isolate, Handle<Object> object);
+
   MUST_USE_RESULT static MaybeHandle<Name> ToName(Isolate* isolate,
                                                   Handle<Object> key);
 
index 8748f62..1c08e92 100644 (file)
@@ -624,6 +624,55 @@ function assertAccessorDescriptor(object, name) {
 })();
 
 
+(function TestDefaultConstructorArguments() {
+  var args, self;
+  class Base {
+    constructor() {
+      self = this;
+      args = arguments;
+    }
+  }
+  class Derived extends Base {}
+
+  new Derived;
+  assertEquals(0, args.length);
+
+  new Derived(0, 1, 2);
+  assertEquals(3, args.length);
+  assertTrue(self instanceof Derived);
+
+  var arr = new Array(100);
+  var obj = {};
+  Derived.apply(obj, arr);
+  assertEquals(100, args.length);
+  assertEquals(obj, self);
+})();
+
+
+(function TestDefaultConstructorArguments2() {
+  var args;
+  class Base {
+    constructor(x, y) {
+      args = arguments;
+    }
+  }
+  class Derived extends Base {}
+
+  new Derived;
+  assertEquals(0, args.length);
+
+  new Derived(1);
+  assertEquals(1, args.length);
+  assertEquals(1, args[0]);
+
+  new Derived(1, 2, 3);
+  assertEquals(3, args.length);
+  assertEquals(1, args[0]);
+  assertEquals(2, args[1]);
+  assertEquals(3, args[2]);
+})();
+
+
 /* TODO(arv): Implement
 (function TestNameBindingInConstructor() {
   class C {