Search code examples
v8

Adding a new Object to V8 using Torque


TL;DR: I tried adding a new Object defined exclusively in Torque and use it as a new member for the Name class. This resulted in compilation failures due to function used but never defined errors, pertaining to a function that IS defined in torque-generated files, however for some reason not properly included.

I am unsure how to proceed and trying to just include different combinations of the torque-generated files in the appropriate locations simply led to redefinition errors.

The question: Is it possible to define a new object and use it in the way I intend to, without adding C++ class definitions (that inherit from the torque-generated classes and so forth) and if yes, where do I seem to be going wrong. In the following I will describe the steps I took so far:

  • Add a myClass.tq/ file to src/objects that includes a definition of my class along the lines of:
@export
@generateBodyDescriptors
class MyClass extends HeapObject {
    macro SomeMacro(): Boolean {...}
    macro Set(): void { this.i = 1 }
    i: uint32;
}
  • Add this file to BUILD.gn

I then tried to use this new type to add a member to an existing type (the abstract Name type):

  • Added a new member in src/objects/name.tq
  • Included torque-generated/src/objects/myClass.tq in src/objects/name.h
  • String inherits from Name so i had to modify two "constructors" methods in src/objects/string.tq to include the new member
  • Changed the expected size of String objects in include/v8-interal.h to account for the new member
  • Added some macros to Name that actually use the new member (check if its internal uint is non-zero and set the value to one)

And lastly I added two methods to the String prototype that I defined as javascript builtins, so that I can verify my changes work.

Concretely, the resulting errors complained about the TorqueGeneratedMyClass::cast(Object) method being used but never defined. This error results from the myClass-tq.inc file. However, the method is defined, in the corresponding myClass-tq-inl.inc file.

I'm not sure if I described the issue concretely enough, but I'm happy to clarify uncertainties and appreciate every help.

Edit:

Complete Changes:

// src/objects/myClass.tq
@export
@generateBodyDescriptors
class MyClass extends HeapObject {
    macro IsSet(): Boolean {
        return this.i == 0 ? False : True;
    }
    macro Set(): void { this.i = 1 }
    i: uint32;
}

// src/objects/name.h

@@ -16,6 +17,7 @@
 namespace v8 {
 namespace internal {

+#include "torque-generated/src/objects/taint-tq.inc"
 #include "torque-generated/src/objects/name-tq.inc"

// src/objects/name.tq
@@ -4,7 +4,18 @@

 @abstract
 extern class Name extends PrimitiveHeapObject {
+  macro IsSet(): Boolean {
+    return this.test.IsSet();
+  }
+
+  macro Set(): void {
+    this.test.Set();
+  }
+
+  test: MyClass;
   raw_hash_field: NameHash;

}

// src/objects/string.tq
@@ -135,7 +135,10 @@ macro AllocateNonEmptySeqOneByteString<Iterator: type>(
   dcheck(length != 0 && length <= kStringMaxLength);
   return new SeqOneByteString{
     map: kOneByteStringMap,
+    test: new StringTaint{tainted: 0},
     raw_hash_field: kNameEmptyHashField,
     length: Signed(length),
     chars: ...content
   };
@@ -146,7 +149,10 @@ macro AllocateNonEmptySeqTwoByteString<Iterator: type>(
   dcheck(length > 0 && length <= kStringMaxLength);
   return new SeqTwoByteString{
     map: kStringMap,
+    test: new StringTaint{tainted: 0},
     raw_hash_field: kNameEmptyHashField,
     length: Signed(length),
     chars: ...content
   };

// src/builtins/string-test.tq

namespace string {
    transitioning javascript builtin StringPrototypeIsSet(js-implicit context: NativeContext, receiver: JSAny)(): Boolean {
        const string: String = ToThisString(receiver, 'String.prototype.testIsSet');
        return string.IsSet();
    }

    transitioning javascript builtin StringPrototypeSet(js-implicit context: NativeContext, receiver: JSAny)(): JSAny {
        const string: String = ToThisString(receiver, 'String.prototype.testSet');
        string.Set();

        return Undefined;
    }
}

// src/init/bootstrapper.cc

@@ -2078,6 +2078,10 @@ void Genesis::InitializeGlobal(Handle<JSGlobalObject> global_object,
                           Builtin::kStringPrototypeSup, 0, false);
     SimpleInstallFunction(isolate_, prototype, "startsWith",
                           Builtin::kStringPrototypeStartsWith, 1, false);
+    SimpleInstallFunction(isolate_, prototype, "testSet",
+                          Builtin::kStringPrototypeTaint, 0, true);
+    SimpleInstallFunction(isolate_, prototype, "testIsSet",
+                          Builtin::kStringPrototypeIsTainted, 0, true);
     SimpleInstallFunction(isolate_, prototype, "toString",
                           Builtin::kStringPrototypeToString, 0, true);
     SimpleInstallFunction(isolate_, prototype, "trim",

// BUILD.gn

@@ -1698,6 +1698,7 @@ torque_files = [
   "src/builtins/string-startswith.tq",
   "src/builtins/string-substr.tq",
   "src/builtins/string-substring.tq",
+  "src/builtins/string-test.tq",
   "src/builtins/string-trim.tq",
   "src/builtins/symbol.tq",
   "src/builtins/torque-internal.tq",
@@ -1781,6 +1782,7 @@ torque_files = [
   "src/objects/swiss-hash-table-helpers.tq",
   "src/objects/swiss-name-dictionary.tq",
   "src/objects/synthetic-module.tq",
+  "src/objects/myClass.tq",
   "src/objects/template-objects.tq",
   "src/objects/templates.tq",
   "src/objects/torque-defined-classes.tq",

Error:

In file included from ../deps/v8/src/objects/name.h:20,
                 from ../deps/v8/src/objects/string.h:15,
                 from ../deps/v8/src/heap/factory.h:25,
                 from ../deps/v8/src/execution/isolate.h:33,
                 from ../deps/v8/src/logging/log.h:16,
                 from ../deps/v8/src/heap/base-space.h:12,
                 from ../deps/v8/src/heap/spaces.h:16,
                 from ../deps/v8/src/heap/marking-visitor.h:13,
                 from ../deps/v8/src/heap/concurrent-marking.h:14,
                 from ../deps/v8/src/heap/concurrent-marking.cc:5:
/home/ccloud/sap_node/out/Release/obj/gen/torque-generated/src/objects/myClass-tq.inc:26:22: warning: inline function ‘static D v8::internal::TorqueGeneratedMyClass<D, P>::cast(v8::internal::Object) [with D = v8::internal::MyClass; P = v8::internal::HeapObject]’ used but never defined
   26 |   V8_INLINE static D cast(Object object);
      |                      ^~~~
/home/ccloud/sap_node/out/Release/obj/gen/torque-generated/src/objects/myClass-tq.inc: In static member function ‘static T v8::internal::ConcurrentMarkingVisitor::Cast(v8::internal::HeapObject) [with T = v8::internal::MyClass]’:
/home/ccloud/sap_node/out/Release/obj/gen/torque-generated/src/objects/myClass-tq.inc:26:22: error: inlining failed in call to always_inline ‘static D v8::internal::TorqueGeneratedMyClass<D, P>::cast(v8::internal::Object) [with D = v8::internal::MyClass; P = v8::internal::HeapObject]’: function body not available
../deps/v8/src/heap/concurrent-marking.cc:103:19: note: called from here
  103 |     return T::cast(object);
      |            ~~~~~~~^~~~~~~~
make[1]: *** [tools/v8_gypfiles/v8_base_without_compiler.target.mk:1000: /home/ccloud/sap_node/out/Release/obj.target/v8_base_without_compiler/deps/v8/src/heap/concurrent-marking.o] Error 1
make[1]: *** Waiting for unfinished jobs....
In file included from ../deps/v8/src/objects/name.h:20,
                 from ../deps/v8/src/objects/string.h:15,
                 from ../deps/v8/src/heap/factory.h:25,
                 from ../deps/v8/src/execution/isolate.h:33,
                 from ../deps/v8/src/common/ptr-compr-inl.h:10,
                 from ../deps/v8/src/execution/isolate-utils-inl.h:8,
                 from ../deps/v8/src/diagnostics/objects-printer.cc:11:
/home/ccloud/sap_node/out/Release/obj/gen/torque-generated/src/objects/myClass-tq.inc:26:22: warning: inline function ‘static D v8::internal::TorqueGeneratedMyClass<D, P>::cast(v8::internal::Object) [with D = v8::internal::MyClass; P = v8::internal::HeapObject]’ used but never defined
   26 |   V8_INLINE static D cast(Object object);
      |                      ^~~~
/home/ccloud/sap_node/out/Release/obj/gen/torque-generated/src/objects/myClass-tq.inc: In member function ‘void v8::internal::HeapObject::HeapObjectPrint(std::ostream&)’:
/home/ccloud/sap_node/out/Release/obj/gen/torque-generated/src/objects/myClass-tq.inc:26:22: error: inlining failed in call to always_inline ‘static D v8::internal::TorqueGeneratedMyClass<D, P>::cast(v8::internal::Object) [with D = v8::internal::MyClass; P = v8::internal::HeapObject]’: function body not available
../deps/v8/src/diagnostics/objects-printer.cc:216:21: note: called from here
  216 |     Name::cast(*this).Name##Print(os); \
      |                     ^
/home/ccloud/sap_node/out/Release/obj/gen/torque-generated/instance-types.h:637:3: note: in expansion of macro ‘MAKE_TORQUE_CASE’
  637 |   V(MyClass, MY_CLASS_TYPE) /* src/objects/myClass.tq?l=1&c=1 */ \
      |   ^
../deps/v8/src/diagnostics/objects-printer.cc:220:7: note: in expansion of macro ‘TORQUE_INSTANCE_CHECKERS_SINGLE_FULLY_DEFINED’
  220 |       TORQUE_INSTANCE_CHECKERS_SINGLE_FULLY_DEFINED(MAKE_TORQUE_CASE)
      |       ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
In file included from ../deps/v8/src/objects/name.h:20,
                 from ../deps/v8/src/objects/string.h:15,
                 from ../deps/v8/src/heap/factory.h:25,
                 from ../deps/v8/src/execution/isolate.h:33,
                 from ../deps/v8/src/common/ptr-compr-inl.h:10,
                 from ../deps/v8/src/execution/isolate-utils-inl.h:8,
                 from ../deps/v8/src/diagnostics/objects-printer.cc:11:
/home/ccloud/sap_node/out/Release/obj/gen/torque-generated/src/objects/myClass-tq.inc:26:22: error: inlining failed in call to always_inline ‘static D v8::internal::TorqueGeneratedMyClass<D, P>::cast(v8::internal::Object) [with D = v8::internal::MyClass; P = v8::internal::HeapObject]’: function body not available
   26 |   V8_INLINE static D cast(Object object);
      |                      ^~~~
../deps/v8/src/diagnostics/objects-printer.cc:216:21: note: called from here
  216 |     Name::cast(*this).Name##Print(os); \
      |                     ^
/home/ccloud/sap_node/out/Release/obj/gen/torque-generated/instance-types.h:637:3: note: in expansion of macro ‘MAKE_TORQUE_CASE’
  637 |   V(MyClass, MY_CLASS_TYPE) /* src/objects/myClass.tq?l=1&c=1 */ \
      |   ^
../deps/v8/src/diagnostics/objects-printer.cc:220:7: note: in expansion of macro ‘TORQUE_INSTANCE_CHECKERS_SINGLE_FULLY_DEFINED’
  220 |       TORQUE_INSTANCE_CHECKERS_SINGLE_FULLY_DEFINED(MAKE_TORQUE_CASE)
      |       ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Solution

  • Is it possible to define a new object and use it in the way I intend to, without adding C++ class definitions

    Yes.

    where do I seem to be going wrong

    This part seems suspicious:

    the method is defined, in the corresponding myClass-tq-inl.inc file

    All generated *.inc files have to be #included somewhere. There's likely some *.cc file that needs it (you didn't provide enough details, so I can't tell for sure).


    A couple of more general points:

    • when asking for programming help (anywhere!), provide reproducible code (in this case: your complete patch, not just a prose description of it).
    • when asking about compiler errors, provide the complete error message, not just a vague summary like "the errors complained about ...".
    • while you are of course free to fork V8 and muck with its internals, a word of caution: such modifications are likely to be very labor-intensive to maintain as you update to newer V8 versions (and you will want to update to newer V8 versions, if this isn't just some throwaway experiment). To make your work more future-proof, either attempt to upstream it (which might be difficult in this case, as increasing the size of Name objects will be an unpopular proposition, I expect), or use only the public API (which isn't perfectly stable, but much more stable than random internals). Of course, the latter won't allow you to modify internally used objects; since you didn't describe your higher-level goal I can't tell whether that's a dealbreaker or whether there could be alternative approaches.